# JavaScript API 手册

译者：Ivory\
邮箱：<aztiwe0@gmail.com>\
时间：2023.5.16（始）-

学习自用，如有错误，烦请指正，不胜感激。


# 目录

## 1. 运行时

&#x20;\[toc]


# 1. 运行时信息


# 1.1 Frida

* `Frida.version`: 属性，当前 Frida 版本信息，类型为字符串。
* `Frida.heapSize`: 动态属性，包含 Frida 当前私有堆内存的大小。该私有堆内存由所有脚本和Frida运行时共享。这个属性可以帮助你留意注入消耗的内存。


# 1.2 Script

* `Script.runtime`: 字符串属性，表示当前使用的运行时实现。`QJS` 或 `V8` 。
* `Script.pin()`: 暂时阻止卸载当前脚本。这个行为会被引用计数，因此后面某个地方一定要有个匹配的`unpin()`。通常在你需要手动控制另一个线程的清理工作时，被用在`bindWeak()`的回调中。
* `Script.unpin()`: `pin()` 的逆操作，使当前的脚本可以被卸载。
* `Script.bindWeak(value, fn)`: 监视变量`value`，当`value`被垃圾回收后，或脚本将要被卸载时调用fn函数。该API返回一个`ID`，你可以将该ID传递给`Script.unbindWeak()`，进行显示的清理。当你构造语言绑定时，这个API会非常有用：当一个JS变量不再被需要时，脚本会自动释放Native资源。
* `Script.unbindWeak(id)`: 停止对`Script.bindWeak(value, fn)`监视的变量的监视，并立刻调用`fn`。
* `Script.setGlobalAccessHandler(handler | null)`: 安装或卸载一些处理函数，这些函数用来处理尝试访问不存在的全局变量时发生的异常。这个API在实现一个REPL时非常有用，比如尝试惰性取回数据库id时。\
  其中，`handler`是一个普通的JavaScript对象，该对象需要包含两个属性：\
  \* `enumerate()`: 用于查询那些全局变量存在，必须返回一个字符串数组。\
  \* `get(property)`: 用于取回指定property的值。


# 2. 进程，线程，模块和内存


# 2.1 线程 (Thread)

* `Thread.backtrace([context, backtracer])`: 生成当前线程的回溯栈，返回形式为一个`NativePointer`数组。如果你想在`Interceptor`的`onEnter`或`onLeave`回调中使用该函数，你需要提供`this.contex`t给`context`参数（不是必须），这样该函数生成的回溯栈会更加准确。不提供`context`参数时，该函数会从当前栈位置自动生成一个`context`，不过由于JavaScript虚拟机栈帧的问题，结果不会很准确。另一个可选参数`backtracer`用于指明使用何种追踪器，其值只能为`Backtracer.FUZZY`或`Backtracer.ACCURATE`，后者是默认值。追踪器的准确度取决于被追踪程序对调试的友好程度以及调试信息，而fuzzy追踪器会对栈进行鉴别，并尝试猜出返回地址，也就是说你会得到一些假阳性错误，不过好处是任何二进制文件都可以使用。生成的回溯栈目前被限制在16个栈帧，如果你想调整，那你必须自己重新编译Frida。

{% code lineNumbers="true" %}

```javascript
const f = Module.getExportByName('libcommonCrypto.dylib', 'CCCryptorCreate');
Interceptor.attach(f, {
  onEnter(args) {
    console.log('CCCryptorCreate called from: \n' + 
        Thread.backtrace(this.context, Backtracer.ACCURATE)
        .map(DebugSymbol.fromAddress).join('\n') + '\n');
  }
});
```

{% endcode %}

* `Thread.sleep(delay)`: 挂起当前线程`delay`秒。比如`delay`为0.05表示休眠50毫秒。


# 2.2 进程 (Process)

* `Process.id`: 进程id，数字。
* `Process.arch`: 架构，字符串：`ia32`, `x64`, `arm`, `arm64`
* `Process.platform`: 系统，字符串：`windows`, `darwin`, `linux`, `qnx`
* `Process.pageSize`: 虚拟内存页大小（单位为字节），数字。这个属性可以帮助你写出移植性更好的脚本。
* `Process.pointerSize`: 指针大小（单位为字节），数字。同样可以提高你脚本的可移植性。
* `Process.codeSigningPolicy`:&#x20;
* `Process.isDebuggerAttached()`: 指明当前是否有调试器调试，布尔值。
* `Process.getCurrentThreadId()`: 后去当前线程的系统级别的ID，数字。
* `Process.enumerateThreads()`: 枚举所有的线程，并返回一个对象数组，数组中每个对象都包含以下几个属性:\
  \* `id`: 数字，系统层级的id\
  \* `state`: 字符串，表示该线程的状态：`running`, `stopped`, `waitting`, `uninterruptible`, `halted`\
  \* `context`: 对象，包含 `pc`， `sp`属性，这两个属性为`NativePointer`对象，用于指明 `EIP`/`RIP`/`PC`和`ESP`/`RSP`/`SP`（分别对应`ia32`/`x64`/`arm`）。其他处理器也适用，比如`eax`, `rax`, `r0`, `x0`, 等等。
* `Process.findModuleByAddress(address)`, `Process.getModuleByAddress(address)`, `Process.findModuleByName(name)`, `Process.getModuleByName(name)`: 返回一个`Module`，该模块的地址/名称匹配搜索串。若没有找到目标模块，find类函数会返回`null`，而get类函数会抛出异常。
* `Process.enumerateModules()`: 枚举当前已加载的模块，返回值为模块对象列表。
* `Process.findRangeByAddress(address)`, `Process.getRangeByAddress(address)`: 返回一个对象，该对象描述了包含目标地址的内存块的详细信息。若没有找到这样的内存范围，前者会返回`null`，而后者会抛出异常。列举的详细信息，详情查看`Process.enumerateRanges()`。
* `Process.enumerateRanges(protection|specifier)`: 枚举内存中满足`protection`条件的内存范围。`protection`参数应当为这样形式的字符串: `rwx`, 而`rw-`表示必须至少可写可读。或者可以传入`specifier`对象，该对象包含`protection`和`coalesce`两个键。`protection`键同上，`coalesce`键表示附件的range是否也是同样的保护机制（默认为`false`，也就是将range分离开）。该函数返回一个对象数组，其中每个对象包含以下属性：\
  \* `base`: `NativePointer`，表示基地址\
  \* `size`: 大小，以字节为单位\
  \* `protection`: 保护属性\
  \* `file`: 对象，文件映射细节，包含以下属性:\
  &#x20; \* `path`: 文件的系统地址\
  &#x20; \* `offset`: 偏移量，单位字节\
  &#x20; \* `size`: 文件大小，单位字节
* `Process.enumerateMallocRanges()`: 和`enumerateRanges()`类似，但是返回系统堆部分的内存。
* `Process.setExceptionHandler(callback)`: 安装一个进程级的异常处理回调。该回调会在原进程处理函数调用前调用。该回调只有一个参数，`details`，该参数为为一个对象，包含以下属性:\
  \* `type`: 字符串: `abort`, `access-violation`, `guard-page`, `illegal-instruction`, `stack-overflow`, `arithmetic`, `breakpoint`, `single-step`, `system`\
  \* `address`: `NativePointer`，发生异常的地址\
  \* `memory`: 包含以下属性的对象:\
  &#x20; \* `operation`: 触发异常的操作，字符串: `read`, `write`, `execute`\
  &#x20; \* `address`: `NativePointer`，异常发生时，存取的内存地址\
  \* `context`: 对象，包含`pc`属性和`sp`属性，分别为指明 `EIP`/`RIP`/`PC` 和 `ESP`/`RSP`/`SP` 的`NativePointer`（对应 `ia32`/`x64`/`arm`）。其他处理器也可以使用，比如`eax`，`rax`，`r0`，`x0`等等。你也可以通过给这些属性赋值来更新寄存器的值。\
  \* `nativeContext`: `NativePointer`，包含操作系统的地址，以及架构特异的CPU上下文结构。这个参数是对`context`参数的补充，防止`context`提供的信息不足；不过我们不推荐使用这个参数，如果出错了也不要给我们提交pr。\
  你的回调函数将会决定如何处理异常。你可以单纯记录，并通过`send`函数和阻塞的`recv`函数来和你的app通信，也可以修改寄存器和内存，以恢复异常。如果你真的处理了该异常，那么该回调应该返回`true`，之后frida会立刻恢复该线程。如果你不返回`true`，frida会将该异常传递给进程的异常处理器，或者让操作系统终止该进程。


# 2.3 模块 (Module)

&#x20;由`Module.load()`和`Process.enumerateModules()`返回的对象。

* `name`: 字符串，模块的标准名
* `base`: `NativePointer`，基地址
* `size`: 数字，大小，单位字节
* `path`: 字符串，完整的系统路径
* `enumerateImports()`: 枚举导入模块，返回一个对象数组，其中每个对象包含以下属性:\
  \* `type`: 字符串，`function` 或 `variable`\
  \* `name`: 字符串，导入名称\
  \* `module`: 字符串，模块名\
  \* `address`: `NativePointer`，模块的绝对地址\
  \* `slot`: `NativePointer`，导入模块的内存位置\
  对于所有的导入，只有`name`属性是保证一定存在的。其他的属性会尽可能的解析，但不保证一定成功。
* `enumerateExports()`: 枚举导出模块，返回一个对象数组，其中每个对象包含以下属性:\
  \* `type`: 字符串，`function` 或 `variable`\
  \* `name`: 字符串，导出名\
  \* `address`: `NativePointer`，绝对地址
* `enumerateSymbols()`: 枚举模块中的所有符号，返回一个对象数组，其中每个对象包含以下属性:\
  \* `isGloable`: 布尔值，指明该符号是否全局可见\
  \* `type`: 字符串: `unknown`, `section`, `undefined(Mach-O)`, `absolute(Mach-O)`, `prebound-undefined(Mach-O)`, `indirect(Mach-O)`, `object(ELF)`, `function(ELF)`, `file(ELF)`, `common(ELF)`, `tls(ELF)`\
  \* `section`: 如果存在，则包含:\
  &#x20; \* `id`: 字符串，包含节索引，段名，节名\
  &#x20; \* `protection`: 保护策略，和`Process.enumerateRanges()`里类似\
  \* `name`: 字符串，符号名\
  \* `address`: `NativePointer`，绝对地址\
  \* `size`: 数字，符号的大小，单位字节
* `enumerateRanges(protection)`: 和`Process.enuerateRanges`一样，不过范围是模块
* `findExportByName(exportName)`, `getExportByName(exportName)`: 返回导出的绝对地址，其中该导出的名称和`exportName`一致。若没有该导出，find类函数会返回`null`，get类函数会抛出异常
* `Module.load(path)`: 从文件系统中加载指定模块，并返回一个模块对象。若目标对象无法加载，则会抛出异常
* `Module.ensureInitialized(name)`: 确保指定模块的初始化部分被运行了。在早期注入中非常重要，这里的早期是指进程生命周期的早期。一个典型案例是与指定模块中ObjC类进行交互时使用。
* `Module.findBaseAddress(name)`, `Module.getBaseAddress(name)`: 返回指定名称的模块的基地址。若没有找到指定模块，find类函数会返回`null`，get类函数会抛出异常
* `Module.findExportByName(moduleName|null, exportName)`, `Module.getExportByName(moduleName|null, exportName)`: 返回模块`ModuleName`中`exportName`的导出的绝对地址。若你不知道具体是哪个模块，你可以传入`null`，不过这样搜索的开销会非常大，尽量不要这么做。若没有目标模块，find类函数会返回`null`，get类函数会抛出异常<br>


# 2.4 模块映射 (ModuleMap)


# 2.5 内存 (Memory)


# 2.6 内存读写监视器 (MemoryAccessMonitor)


# 2.7 C模块 (CModule)


# 2.8 Api解析器 (ApiResolver)

Api解析器


# 2.9 调试符号 (DebugSymbol)


# 2.10 内核 (Kernel)


# 3. 数据类型，函数和回调


# 3.1 64位整型 (Int64)


# 3.2 无符号64位整型 (UInt64)


# 3.3 Native指针 (NativePointer)


# 3.4 数组 (ArrayBuffer)


# 3.5 Native函数 (NativeFunction)


# 3.6 Native回调 (NativeCallback)


# 3.7 系统函数 (SystemFunction)


# 4. 网络


# 4.1 套接字 (Socket)


# 4.2 套接字监听器 (SocketListener)


# 4.3 套接字连接 (SocketConnection)


# 5. 文件和数据流


# 5.1 文件 (File)


# 5.2 读写流 (IOStream)


# 5.3 输入流 (InputStream)


# 5.4 输出流 (OutputStream)


# 5.5 Unix输入流 (UnixInputStream)


# 5.6 Unix输出流 (UnixOutputStream)


# 5.7 Win32输入流 (Win32InputStream)


# 5.7 Win32输出流 (Win32OutputStream)


# 6. 数据库


# 6.1 Sqlite数据库


# 6.2 Sqlite语句


# 7. 注入


# 7.1 Interceptor

Interceptor


# 7.2 Stalker

Stalker


# 7.3 ObjC


# 7.4 Java


# 8. CPU指令

CPU


# 8.1 指令集


# 8.2 X86写入


# 8.3 X86重定位


# 8.4 x86枚举类型


# 8.5 Arm写入

Arm


# 8.6 Arm重定位

Arm


# 8.7 Thumb写入

Thumb


# 8.8 Thumb重定位


# 8.9 ARM 枚举类型

ARM


# 8.10 ARM64 写入


# 8.11 ARM64 重定位


# 8.12 AARCH64 枚举类型


# 8.13 Mips写入


# 8.14 Mips重定位


# 8.15 Mips枚举类型

Mips


# 9. 其他


# 9.1 终端


# 9.2 转储


# 9.3 缩写


# 9.4 宿主与被注入程序之间的通信


# 9.5 时间相关


# 9.6 垃圾回收


