diff --git a/_i18n/cn.yml b/_i18n/cn.yml new file mode 100644 index 00000000..ced8c34a --- /dev/null +++ b/_i18n/cn.yml @@ -0,0 +1,54 @@ +- title: 入门指南 + docs: + - home + - quickstart + - installation + - modes + - gadget + - hacking + - presentations + +- title: 教程 + docs: + - functions + - messages + - ios + - android + +- title: 示例 + docs: + - examples/windows + - examples/macos + - examples/linux + - examples/ios + - examples/android + - examples/javascript + +- title: 工具 + docs: + - frida-cli + - frida-ps + - frida-trace + - frida-discover + - frida-ls-devices + - frida-kill + +- title: API 参考 + docs: + - javascript-api + - c-api + - swift-api + +- title: 其他 + docs: + - best-practices + - troubleshooting + - building + - footprint + +- title: 关于 + docs: + - contributing + - gsoc-ideas-2015 + - gsod-ideas-2023 + - history diff --git a/_i18n/cn/_docs/android.md b/_i18n/cn/_docs/android.md new file mode 100644 index 00000000..f249bbef --- /dev/null +++ b/_i18n/cn/_docs/android.md @@ -0,0 +1,93 @@ +在本教程中,我们将展示如何在您的 Android 设备上进行函数跟踪。 + +## 设置您的 Android 设备 + +在开始之前,如果您还没有 root 您的设备,您需要先 root。从技术上讲,不 root 设备也可以使用 Frida,例如通过重新打包应用以包含 frida-gadget,或使用调试器来实现相同的目的。但是,对于本介绍,我们将专注于最简单的情况:已 root 的设备。 + +另请注意,我们最近的大部分测试都是在运行 Android 9 的 Pixel 3 上进行的。较旧的 ROM 可能也可以工作,但是如果您遇到基本问题,例如启动应用程序时 Frida 使系统崩溃,这是由于特定于 ROM 的怪癖。我们无法在所有可能的设备上进行测试,因此我们依靠您的帮助来改进这一点。但是,如果您刚开始使用 Frida,强烈建议使用运行最新官方软件的 Pixel 或 Nexus 设备,或者软件尽可能接近 AOSP 的设备。另一种选择是使用模拟器,最好使用 Google 提供的适用于 arm 或 arm64 的 Android 9 模拟器镜像。(x86 可能也可以工作,但测试明显较少。) + +您还需要 Android SDK 中的 `adb` 工具。 + +首先,从我们的 [releases 页面](https://github.com/frida/frida/releases)下载适用于 Android 的最新 `frida-server` 并解压缩。 + +{% highlight bash %} +$ adb shell getprop ro.product.cpu.abilist # check your device cpu type + +$ unxz frida-server.xz +{% endhighlight %} + +现在,让我们在您的设备上运行它: + +{% highlight bash %} +$ adb root # might be required +$ adb push frida-server /data/local/tmp/ +$ adb shell "chmod 755 /data/local/tmp/frida-server" +$ adb shell "/data/local/tmp/frida-server &" +{% endhighlight %} + +某些应用可能能够检测到 frida-server 的位置。将 frida-server 二进制文件重命名为随机名称,或将其移动到另一个位置(如 /dev)可能会奏效。 + +对于最后一步,请确保以 root 身份启动 frida-server,即如果您在已 root 的设备上执行此操作,您可能需要 *su* 并从该 shell 运行它。 + +
+ 如果在运行 adb root 后收到 adbd cannot run as root in production builds
您需要在每个 shell 命令前加上 su -c。例如:
+ adb shell "su -c chmod 755 /data/local/tmp/frida-server"
+
在跟踪远程设备时,请记住将 适合平台的 frida-server 二进制文件 复制到远程设备。复制后,请务必在开始跟踪会话之前运行 frida-server 二进制文件。
+例如,要跟踪远程 Android 应用程序,您可以将 'frida-server-12.8.0-android-arm' 二进制文件复制到 Android 的 /data/local/tmp 文件夹。使用 adb shell,您可以在后台运行服务器(例如 "frida-server-12.8.0-android-arm &")。
++- MODULE!FUNCTION +- FUNCTION +- !FUNCTION +- MODULE! ++ +以下是一些示例及其说明: + +| 选项值 | 说明 | +| --------------------- | ---------------------------------------------------------------- | +| -i "msvcrt.dll!*cpy*" | 匹配名称中带有 'cpy' 的所有函数,仅在 msvcrt.dll 中 | +| -i "*free*" | 匹配所有模块中名称中带有 'free' 的所有函数 | +| -i "!*free*" | 与 -i "*free*" 相同 | +| -i "gdi32.dll!" | 跟踪 gdi32.dll 中的所有函数(与 -I "gdi32.dll" 相同) | + +
frida-trace 有一个“工作集”的内部概念,即一组“模块:函数”对,其处理程序将在运行时被跟踪。工作集的内容可以通过包含/排除命令行选项 (-I / -X / -i / -x) 进行更改。
+重要的是要理解包含/排除选项的顺序很重要。每个此类选项都在工作集的当前状态上工作,不同的选项顺序可能导致不同的结果。换句话说,包含/排除选项是过程性的(即顺序很重要),而不仅仅是声明性的。
+例如,假设我们要跟踪正在运行的进程中所有模块中的所有 "str*" 和 "mem*" 函数。在我们的示例中,这些函数位于三个模块中:ucrtbase.dll, ntdll.dll, 和 msvcrt.dll。然而,为了减少噪音,我们不想跟踪在 msvcrt.dll 模块中找到的任何函数。
+我们将描述命令行上的三种不同选项顺序,并表明它们产生不同的结果。
++ 在您的会话中,您正在跟踪许多函数。有时您希望所有处理程序打印出它们的进程 ID。使用 `-P` 选项,您可以启用处理程序来决定是否打印进程 ID。 +
+
+ 首先,确定通知处理程序是否应显示进程 ID 的 JSON 对象格式。让我们使用以下格式:
+
+
+
+
{
+ onEnter(log, args, state) {
+ log('memcpy() [msvcrt.dll]');
+ if (parameters.displayPid) {
+ log(`Process ID: ${Process.id}`);
+ }
+ },
+
+ onLeave(log, retval, state) {
+ }
+}
+
+
+ onEnter(log, args, state) {
+ log('memcpy()');
+},
+
+
+缺点是,如果同一函数名存在于多个模块中,则很难区分函数跟踪。`--decorate` 函数指示 `frida-trace` 在默认的 `onEnter` 跟踪指令中插入模块名称:
+
+onEnter(log, args, state) {
+ log('memcpy() [msvcrt.dll]');
+},
+
diff --git a/_i18n/cn/_docs/frida-trace/session-initialization-primer.md b/_i18n/cn/_docs/frida-trace/session-initialization-primer.md
new file mode 100644
index 00000000..b1f73460
--- /dev/null
+++ b/_i18n/cn/_docs/frida-trace/session-initialization-primer.md
@@ -0,0 +1,366 @@
+此页面描述了 frida-trace `--init-session` / `-S` 命令行选项的用途,以及如何在您的工作中使用它。
+
+## 什么是 --init-session 选项?
+
+`--init-session` 选项在 frida-trace 引擎初始化阶段执行任意数量的用户编写的 JavaScript 代码文件。这些文件在调用第一个函数处理程序之前执行。
+
+它的强大之处在于能够定义全局可见的函数并将数据存储在全局 `state` 对象中,该对象作为参数传递给调用的每个处理程序。
+
+`state` 对象允许您在函数调用之间维护信息。存储在 `state` 中的数据可供所有调用的处理程序访问。
+
+## --init-session 选项的用途
+
+`--init-session` / `-S` 选项保证您选择的 JavaScript 源代码在 frida-trace 引擎开始跟踪之前执行。此功能的可能应用包括:
+
+1. 执行自定义代码,创建您选择的代码和数据对象,并在调用第一个函数处理程序之前执行此操作。
+2. 创建共享代码库,允许共享微调和调试过的 JavaScript 代码,这些代码可以由任何处理程序在任何时间全局调用。
+
+frida-trace JavaScript 代码通常编写为一次性的“用完即弃”代码。但是,如果您发现自己经常在处理程序和项目之间复制和粘贴代码,请考虑将代码保存在共享库中。编写并调试后,您可以在未来的项目中重用这些函数和数据。
+
+## 详细示例:创建共享代码库
+
+在此示例中,我们演示使用 `--init-session` / `-S` 选项来增强对 Microsoft Windows `ExtTextOutW()` 函数的跟踪。我们以自上而下的方式描述组件,从 ExtTextOutW.js JavaScript 处理程序函数开始,一直到共享代码文件。
+
+### ExtTextOutW():函数签名
+
+在我的 Windows 系统中,要监视的 [ExtTextOutW()][] 函数位于 *gdi32full.dll* 中。这是该函数的 C 语法:
+
+{% highlight c %}
+BOOL ExtTextOutA(
+ HDC hdc,
+ int x,
+ int y,
+ UINT options,
+ const RECT *lprect,
+ LPCSTR lpString,
+ UINT c,
+ const INT *lpDx
+);
+{% endhighlight %}
+
+利用我们共享代码库中的 JavaScript 代码,我们生成增强的跟踪输出。
+
+### 增强的跟踪输出
+
+在展示处理程序代码和共享代码库之前,这里是增强的跟踪输出本身:
+
+{% highlight console %}
+c:\project> frida-trace -p 6980 --decorate -i "gdi32full.dll!ExtTextOutW" -S core.js -S ms-windows.js
+Instrumenting...
+ExtTextOutW: Loaded handler at "c:\\project\\__handlers__\\gdi32full.dll\\ExtTextOutW.js"
+Started tracing 1 function. Press Ctrl+C to stop.
+ /* TID 0x3ab8 */
+ 2695 ms ---------------------------------------------
+ 2695 ms ExtTextOutW() [gdi32full.dll]
+ 2695 ms x: 0
+ 2695 ms y: 0
+ 2695 ms options: ETO_OPAQUE
+ 2695 ms lprect [20 bytes]
+ 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
+ 00b9c81c 00 00 00 00 01 00 00 00 01 00 00 00 98 02 00 00 ................
+ 00b9c82c 26 90 b2 b3 &...
+ 2695 ms lprect: (left, top, right, bottom) = (0, 1, 1, 664)
+ 2695 ms c: 0
+ 2696 ms x (exit): 0
+ 2696 ms y (exit): 0
+ . . .
+ 2788 ms ---------------------------------------------
+ 2788 ms ExtTextOutW() [gdi32full.dll]
+ 2788 ms x: 1
+ 2788 ms y: 0
+ 2788 ms options: ETO_CLIPPED | ETO_IGNORELANGUAGE
+ 2788 ms lprect [20 bytes]
+ 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
+ 00b9eaac 00 00 00 00 00 00 00 00 4d 00 00 00 0f 00 00 00 ........M.......
+ 00b9eabc 0a 78 7e c1 .x~.
+ 2788 ms lprect: (left, top, right, bottom) = (0, 0, 77, 15)
+ 2788 ms lpString [50 bytes]
+ 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
+ 1aa90148 43 00 61 00 6c 00 69 00 62 00 72 00 69 00 20 00 C.a.l.i.b.r.i. .
+ 1aa90158 28 00 42 00 6f 00 64 00 79 00 29 00 29 00 75 00 (.B.o.d.y.).).u.
+ 1aa90168 20 00 77 00 61 00 6e 00 20 00 20 00 74 00 6f 00 .w.a.n. . .t.o.
+ 1aa90178 20 00 .
+ 2788 ms *lpString: "Calibri (Body))u wan to do"
+ 2788 ms c: 14
+ 2788 ms lpDx [4 bytes]
+ 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
+ 09e71208 08 00 00 00 ....
+ 2788 ms *lpDx: 8
+ 2789 ms x (exit): 0
+ 2789 ms y (exit): 0
+{% endhighlight %}
+
+注意以下跟踪增强:
+
+- `options` 字段转换为文本形式
+- `lprect` 内存指针显示为十六进制内存转储和文本字符串
+- `lpString` 内存指针显示为十六进制内存转储和文本字符串
+- `lpDx` 整数指针显示为十六进制内存转储和整数
+
+文本转换和十六进制内存转储是我们共享代码库中的函数。让我们看看我们的 `ExtTextOutW()` 处理程序 JavaScript 代码,它将使用共享代码。
+
+### 处理程序:ExtTextOutW.js
+
+这是我们的 ExeTextOutW() 处理程序代码。我们通过调用共享代码函数来增强它。这些函数存在于外部作用域中,因为在会话脚本中定义的任何顶级函数对所有处理程序都可见。这些函数是在 frida-trace 通过 `--init-session` / `-S` 命令行选项执行共享代码 JavaScript 文件时定义的。
+
+{% highlight js %}
+/*
+ * Auto-generated by Frida. Please modify to match the signature of ExtTextOutW.
+ * This stub is currently auto-generated from manpages when available.
+ *
+ * For full API reference, see: https://frida.re/docs/javascript-api/
+ */
+
+{
+ onEnter(log, args, state) {
+ /*
+ * C syntax:
+ *
+ * BOOL ExtTextOutW(
+ * HDC hdc,
+ * int x,
+ * int y,
+ * UINT options,
+ * const RECT *lprect,
+ * LPCWSTR lpString,
+ * UINT c,
+ * const INT *lpDx
+ * );
+ */
+
+ log('---------------------------------------------');
+ log('ExtTextOutW() [gdi32full.dll]');
+
+ cloneArgs(args, 8, this);
+ const [, x, y, options, lprect, lpString, c, lpDx] = this.args;
+
+ log(`x: ${x.toInt32()}`);
+ log(`y: ${y.toInt32()}`);
+
+ log(`options: ${decodeExttextoutOptions(options)}`);
+
+ if (!lprect.isNull()) {
+ prettyHexdump(log, 'lprect', lprect, 20);
+ log(`lprect: ${rectStructToString(lprect)}`);
+ }
+
+ if (!lpString.isNull()) {
+ prettyHexdump(log, 'lpString', lpString, 50);
+ log(`*lpString: "${lpString.readUtf16String()}"`);
+ }
+
+ log(`c: ${c.toUInt32()}`);
+
+ if (!lpDx.isNull()) {
+ prettyHexdump(log, 'lpDx', lpDx, 4);
+ log(`*lpDx: ${lpDx.readU32()}`);
+ }
+ },
+
+ onLeave(log, retval, state) {
+ /*
+ * We can access the onEnter arguments using `this.args`,
+ * as cloneArgs() copied them there.
+ */
+ const [, x, y] = this.args;
+ log(`x (exit): ${x.toInt32()}`);
+ log(`y (exit): ${y.toInt32()}`);
+ }
+}
+{% endhighlight %}
+
+请注意,除了调用标准 Frida 函数(例如 `toInt32()`、`isNull()`、`readUtf16String()`)之外,还有对我们共享代码函数的调用(例如 `cloneArgs()`、`decodeExttextoutOptions()`、`prettyHexdump()`、`rectStructToString()`)。共享代码函数已经过调试和完善,随时可以被任何处理程序调用。
+
+### 共享代码:core.js
+
+“core.js”共享代码库包含核心或基本函数,旨在供 frida-trace 处理程序和共享代码库重用。
+
+编写共享代码库很简单:您的共享库源文件定义函数和数据对象,将后者存储在全局 `state` 对象中。一旦存储在那里,任何处理程序都可以通过 `state.propertyName` 访问它们。
+
+{% highlight js %}
+/*
+ * Collection of useful general-purpose Frida handler functions.
+ */
+
+/**
+ * Creates a true JavaScript array as `invCtx.args`. This array can be accessed
+ * in the handler's onLeave().
+ *
+ * @param {NativePointer[]} args - The `args` array as passed to onEnter().
+ * @param {number} numArgs - The number of meaningful arguments in `args`. This
+ * function has no way of determining the number of actual arguments because
+ * `args` is a virtual array.
+ * @param {InvocationContext} invCtx - The `this` object of the calling onEnter().
+ * @returns {NativePointer[]} Copy of `args`.
+ */
+function cloneArgs(args, numArgs, invCtx) {
+ const items = [];
+ for (let i = 0; i !== numArgs; i++)
+ items.push(args[i]);
+ invCtx.args = items;
+}
+
+/**
+ * Returns a string describing the bitflags set in `value`.
+ *
+ * @param {number} value - A value consisting of zero or more bitflags.
+ * @param {Map+ 为了获得更大的控制权,例如自定义身份验证、每节点 ACL 和特定于应用程序的协议消息,您也可以实例化 PortalService 对象,而不是运行 frida-portal CLI 程序。 +
+这些提示和技巧将帮助您成为 Frida 向导!
+这些是理解 Frida 有时需要的额外信息。
+如果您希望避免某些死亡,请注意这些消息。
++ 从 Frida 6.0.9 开始,现在有了 usbmuxd 集成,所以 -U 可以工作。 + 对于较早的 Frida 版本,您可以使用 WiFi 并在两端的 localhost:27042 之间建立 SSH 隧道,然后使用 -R 代替 -U。 +
++ 我们也希望在其他平台上支持这一点,所以如果你觉得这很有用并想提供帮助,请联系我们。 + 根据你的用例,你可能也会发现 **[DebugSymbol](#debugsymbol)** API 就足够了。 +
++ 提供的回调对性能有重大影响。如果你只需要检查参数但不关心返回值,或者反过来, + 请确保省略你不需要的回调;即避免将你的逻辑放在 onEnter 中并将 onLeave 留在那儿作为一个空回调。 +
++ 在 iPhone 5S 上,仅提供 onEnter 时的基本开销可能约为 6 微秒, + 而同时提供 onEnter 和 onLeave 时约为 11 微秒。 +
++ 还要注意拦截对每秒调用无数次的函数的调用;虽然 **[send()](#communication-send)** 是异步的, + 但发送单个消息的总开销并未针对高频进行优化,因此这意味着 Frida 让你根据需要低延迟还是高吞吐量, + 自行决定将多个值批处理到单个 **[send()](#communication-send)** 调用中。 +
++ 但是,当钩住热函数时,你可以将 Interceptor 与 **[CModule](#cmodule)** 结合使用以在 C 中实现回调。 +
++ 提供的回调对性能有重大影响。如果你只需要定期调用摘要但不关心原始事件,或者反过来, + 请确保省略你不需要的回调;即避免将你的逻辑放在 onCallSummary 中并将 + onReceive 留在那儿作为一个空回调。 +
++ 另请注意,Stalker 可以与 **[CModule](#cmodule)** 结合使用,这意味着回调可以用 C 实现。 +
+
+ 从 Frida 17 开始,此runtime bridge不再包含在 Frida 的 GumJS 运行时中,可以通过运行以下命令获取:`npm install frida-objc-bridge`。
+
+ 像这样将其导入到你的代理中:
+ `import ObjC from 'frida-objc-bridge';`
+
+ 目前,在 Frida REPL 加载的脚本以及 frida-trace 中不需要这样做。
+
new ObjC.Object(handle[, protocol]): 给定 `handle` 处的现有对象(一个 **[NativePointer](#nativepointer)**),
+ 创建一个 JavaScript 绑定。如果你想将 `handle` 视为仅实现特定协议的对象,你也可以指定 `protocol` 参数。
+
+{% highlight js %}
+Interceptor.attach(myFunction.implementation, {
+ onEnter(args) {
+ // ObjC: args[0] = self, args[1] = selector, args[2-n] = arguments
+ const myString = new ObjC.Object(args[2]);
+ console.log("String argument: " + myString.toString());
+ }
+});
+{% endhighlight %}
+
+> 此对象具有一些特殊属性:
+>
+> - `$kind`: 指定 `instance`、`class` 或 `meta-class` 的字符串
+> - `$super`: 一个 **[ObjC.Object](#objc-object)** 实例,用于链接到超类方法实现
+> - `$superClass`: 作为 **[ObjC.Object](#objc-object)** 实例的超类
+> - `$class`: 此对象的类作为 **[ObjC.Object](#objc-object)** 实例
+> - `$className`: 包含此对象类名的字符串
+> - `$moduleName`: 包含此对象模块路径的字符串
+> - `$protocols`: 将协议名称映射到此对象符合的每个协议的 [`ObjC.Protocol`](#objc-protocol) 实例的对象
+> - `$methods`: 包含此对象类和父类公开的本机方法名称的数组
+> - `$ownMethods`: 包含此对象类公开的本机方法名称的数组,不包括父类
+> - `$ivars`: 将每个实例变量名称映射到其当前值的对象,允许你通过访问和赋值来读取和写入每个变量
+>
+> 还有一个 `equals(other)` 方法,用于检查两个实例是否引用同一个底层对象。
+>
+> 请注意,所有方法包装器都提供了一个 `clone(options)` API,用于创建具有自定义 **[NativeFunction](#nativefunction)** 选项的新方法包装器。
+
++ `new ObjC.Protocol(handle)`: 给定 `handle` 处的现有协议(一个 **[NativePointer](#nativepointer)**),
+ 创建一个 JavaScript 绑定。
+ {: #objc-protocol}
+
++ `new ObjC.Block(target[, options])`: 给定 `target` 处的现有块(一个 **[NativePointer](#nativepointer)**)创建一个 JavaScript 绑定,
+ 或者,要定义一个新块,`target` 应该是一个指定类型签名和每当块被调用时要调用的 JavaScript 函数的对象。
+ 函数使用 `implementation` 键指定,签名通过 `types` 键或 `retType` 和 `argTypes` 键指定。
+ 有关详细信息,请参阅 [`ObjC.registerClass()`](#objc-registerclass)。
+
+ 请注意,如果现有块缺少签名元数据,你可以调用 `declare(signature)`,其中 `signature` 是一个具有 `types` 键
+ 或 `retType` 和 `argTypes` 键的对象,如上所述。
+
+ 你也可以提供一个 `options` 对象,其中包含 **[NativeFunction](#nativefunction)** 支持的相同选项,
+ 例如传递 `traps: 'all'` 以便在调用块时 [`Stalker.follow()`](#stalker-follow) 执行。
+
+ 最常见的用例是钩住现有块,对于期望两个参数的块,它看起来像这样:
+
+{% highlight js %}
+const pendingBlocks = new Set();
+
+Interceptor.attach(..., {
+ onEnter(args) {
+ const block = new ObjC.Block(args[4]);
+ pendingBlocks.add(block); // Keep it alive
+ const appCallback = block.implementation;
+ block.implementation = (error, value) => {
+ // Do your logging here
+ const result = appCallback(error, value);
+ pendingBlocks.delete(block);
+ return result;
+ };
+ }
+});
+{% endhighlight %}
+
++ `ObjC.implement(method, fn)`: 创建一个与 `method` 签名兼容的 JavaScript 实现,
+ 其中 JavaScript 函数 `fn` 用作实现。返回一个 [`NativeCallback`](#nativecallback),
+ 你可以将其分配给 ObjC 方法的 `implementation` 属性。
+
+{% highlight js %}
+const NSSound = ObjC.classes.NSSound; /* macOS */
+const oldImpl = NSSound.play.implementation;
+NSSound.play.implementation = ObjC.implement(NSSound.play, (handle, selector) => {
+ return oldImpl(handle, selector);
+});
+
+const NSView = ObjC.classes.NSView; /* macOS */
+const drawRect = NSView['- drawRect:'];
+const oldImpl = drawRect.implementation;
+drawRect.implementation = ObjC.implement(drawRect, (handle, selector) => {
+ oldImpl(handle, selector);
+});
+{% endhighlight %}
+
+> 由于 `implementation` 属性是一个 [`NativeFunction`](#nativefunction),因此也是一个 [`NativePointer`](#nativepointer),
+> 你也可以使用 [`Interceptor`](#interceptor) 来钩住函数:
+
+{% highlight js %}
+const { NSSound } = ObjC.classes; /* macOS */
+Interceptor.attach(NSSound.play.implementation, {
+ onEnter() {
+ send("[NSSound play]");
+ }
+});
+{% endhighlight %}
+
++ `ObjC.registerProxy(properties)`: 创建一个旨在充当目标对象代理的新类,其中 `properties` 是一个指定以下内容的对象:
+
+ - `protocols`: (可选) 此类符合的协议数组。
+ - `methods`: (可选) 指定要实现的方法的对象。
+ - `events`: (可选) 指定用于获取有关事件通知的回调的对象:
+ - `dealloc()`: 在对象被释放后立即调用。这是你可以清理任何关联状态的地方。
+ - `forward(name)`: 使用 `name` 调用,指定我们要将调用转发到的方法名称。
+ 这可能是你开始使用仅记录名称的临时回调的地方,以帮助你决定要覆盖哪些方法。
+
+{% highlight js %}
+const MyConnectionDelegateProxy = ObjC.registerProxy({
+ protocols: [ObjC.protocols.NSURLConnectionDataDelegate],
+ methods: {
+ '- connection:didReceiveResponse:': function (conn, resp) {
+ /* fancy logging code here */
+ /* this.data.foo === 1234 */
+ this.data.target
+ .connection_didReceiveResponse_(conn, resp);
+ },
+ '- connection:didReceiveData:': function (conn, data) {
+ /* other logging code here */
+ this.data.target
+ .connection_didReceiveData_(conn, data);
+ }
+ },
+ events: {
+ forward(name) {
+ console.log('*** forwarding: ' + name);
+ }
+ }
+});
+
+const method = ObjC.classes.NSURLConnection[
+ '- initWithRequest:delegate:startImmediately:'];
+Interceptor.attach(method.implementation, {
+ onEnter(args) {
+ args[3] = new MyConnectionDelegateProxy(args[3], {
+ foo: 1234
+ });
+ }
+});
+{% endhighlight %}
+
++ `ObjC.registerClass(properties)`: 创建一个新的 Objective-C 类,其中 `properties` 是一个指定以下内容的对象:
+ {: #objc-registerclass}
+
+ - `name`: (可选) 指定类名的字符串;如果你不关心全局可见的名称并希望运行时为你自动生成一个,请省略此项。
+ - `super`: (可选) 超类,或 *null* 以创建新的根类;省略以继承自 *NSObject*。
+ - `protocols`: (可选) 此类符合的协议数组。
+ - `methods`: (可选) 指定要实现的方法的对象。
+
+{% highlight js %}
+const MyConnectionDelegateProxy = ObjC.registerClass({
+ name: 'MyConnectionDelegateProxy',
+ super: ObjC.classes.NSObject,
+ protocols: [ObjC.protocols.NSURLConnectionDataDelegate],
+ methods: {
+ '- init': function () {
+ const self = this.super.init();
+ if (self !== null) {
+ ObjC.bind(self, {
+ foo: 1234
+ });
+ }
+ return self;
+ },
+ '- dealloc': function () {
+ ObjC.unbind(this.self);
+ this.super.dealloc();
+ },
+ '- connection:didReceiveResponse:': function (conn, resp) {
+ /* this.data.foo === 1234 */
+ },
+ /*
+ * But those previous methods are declared assuming that
+ * either the super-class or a protocol we conform to has
+ * the same method so we can grab its type information.
+ * However, if that's not the case, you would write it
+ * like this:
+ */
+ '- connection:didReceiveResponse:': {
+ retType: 'void',
+ argTypes: ['object', 'object'],
+ implementation(conn, resp) {
+ }
+ },
+ /* Or grab it from an existing class: */
+ '- connection:didReceiveResponse:': {
+ types: ObjC.classes
+ .Foo['- connection:didReceiveResponse:'].types,
+ implementation(conn, resp) {
+ }
+ },
+ /* Or from an existing protocol: */
+ '- connection:didReceiveResponse:': {
+ types: ObjC.protocols.NSURLConnectionDataDelegate
+ .methods['- connection:didReceiveResponse:'].types,
+ implementation(conn, resp) {
+ }
+ },
+ /* Or write the signature by hand if you really want to: */
+ '- connection:didReceiveResponse:': {
+ types: 'v32@0:8@16@24',
+ implementation(conn, resp) {
+ }
+ }
+ }
+});
+
+const proxy = MyConnectionDelegateProxy.alloc().init();
+/* use `proxy`, and later: */
+proxy.release();
+{% endhighlight %}
+
++ `ObjC.registerProtocol(properties)`: 创建一个新的 Objective-C 协议,其中 `properties` 是一个指定以下内容的对象:
+
+ - `name`: (可选) 指定协议名称的字符串;如果你不关心全局可见的名称并希望运行时为你自动生成一个,请省略此项。
+ - `protocols`: (可选) 此协议包含的协议数组。
+ - `methods`: (可选) 指定要声明的方法的对象。
+
+{% highlight js %}
+const MyDataDelegate = ObjC.registerProtocol({
+ name: 'MyDataDelegate',
+ protocols: [ObjC.protocols.NSURLConnectionDataDelegate],
+ methods: {
+ /* You must specify the signature: */
+ '- connection:didStuff:': {
+ retType: 'void',
+ argTypes: ['object', 'object']
+ },
+ /* Or grab it from a method of an existing class: */
+ '- connection:didStuff:': {
+ types: ObjC.classes
+ .Foo['- connection:didReceiveResponse:'].types
+ },
+ /* Or from an existing protocol method: */
+ '- connection:didStuff:': {
+ types: ObjC.protocols.NSURLConnectionDataDelegate
+ .methods['- connection:didReceiveResponse:'].types
+ },
+ /* Or write the signature by hand if you really want to: */
+ '- connection:didStuff:': {
+ types: 'v32@0:8@16@24'
+ },
+ /* You can also make a method optional (default is required): */
+ '- connection:didStuff:': {
+ retType: 'void',
+ argTypes: ['object', 'object'],
+ optional: true
+ }
+ }
+});
+{% endhighlight %}
+
++ `ObjC.bind(obj, data)`: 将一些 JavaScript 数据绑定到 Objective-C 实例;
+ 有关示例,请参阅 [`ObjC.registerClass()`](#objc-registerclass)。
+
++ `ObjC.unbind(obj)`: 从 Objective-C 实例解绑先前关联的 JavaScript 数据;
+ 有关示例,请参阅 [`ObjC.registerClass()`](#objc-registerclass)。
+
++ `ObjC.getBoundData(obj)`: 从 Objective-C 对象中查找先前绑定的数据。
+
++ `ObjC.enumerateLoadedClasses([options, ]callbacks)`: 枚举当前已加载的类,其中 `callbacks` 是一个指定以下内容的对象:
+ {: #objc-enumerateloadedclasses}
+
+ - `onMatch(name, owner)`: 为每个已加载的类调用,`name` 为类名字符串,`owner` 指定加载类的模块路径。
+ 要获取给定类的 JavaScript 包装器,请执行:[`ObjC.classes[name]`](#objc-classes)。
+
+ - `onComplete()`: 当所有类都已枚举时调用。
+
+ 例如:
+
+{% highlight js %}
+ObjC.enumerateLoadedClasses({
+ onMatch(name, owner) {
+ console.log('onMatch:', name, owner);
+ },
+ onComplete() {
+ }
+});
+{% endhighlight %}
+
+可选的 `options` 参数是一个对象,你可以在其中指定 `ownedBy` 属性以将枚举限制为给定 [`ModuleMap`](#modulemap) 中的模块。
+
+例如:
+
+{% highlight js %}
+const appModules = new ModuleMap(isAppModule);
+ObjC.enumerateLoadedClasses({ ownedBy: appModules }, {
+ onMatch(name, owner) {
+ console.log('onMatch:', name, owner);
+ },
+ onComplete() {
+ }
+});
+
+function isAppModule(m) {
+ return !/^\/(usr\/lib|System|Developer)\//.test(m.path);
+}
+{% endhighlight %}
+
++ `ObjC.enumerateLoadedClassesSync([options])`: [`enumerateLoadedClasses()`](#objc-enumerateloadedclasses) 的同步版本,
+ 返回一个将所有者模块映射到类名数组的对象。
+
+ 例如:
+
+{% highlight js %}
+const appModules = new ModuleMap(isAppModule);
+const appClasses = ObjC.enumerateLoadedClassesSync({ ownedBy: appModules });
+console.log('appClasses:', JSON.stringify(appClasses));
+
+function isAppModule(m) {
+ return !/^\/(usr\/lib|System|Developer)\//.test(m.path);
+}
+{% endhighlight %}
+
++ `ObjC.choose(specifier, callbacks)`: 通过扫描堆来枚举与 `specifier` 匹配的类的活动实例。
+ `specifier` 是类选择器或指定类选择器和所需选项的对象。
+ 类选择器是类的 **[ObjC.Object](#objc-object)**,例如 *ObjC.classes.UIButton*。
+ 当传递一个对象作为说明符时,你应该提供带有类选择器的 `class` 字段,以及一个布尔值 `subclasses` 字段,
+ 指示你是否也对匹配给定类选择器的子类感兴趣。默认值是也包括子类。
+ `callbacks` 参数是一个指定以下内容的对象:
+ {: #objc-choose}
+
+ - `onMatch(instance)`: 为找到的每个活动实例调用一次,带有一个即用型 `instance`,
+ 就像你调用了 [`new ObjC.Object(ptr("0x1234"))`](#objc-object) 一样,
+ 知道这个特定的 Objective-C 实例位于 *0x1234*。
+
+ 此函数可能会返回字符串 `stop` 以提前取消枚举。
+
+ - `onComplete()`: 当所有实例都已枚举时调用
+
++ `ObjC.chooseSync(specifier)`: [`choose()`](#objc-choose) 的同步版本,它将实例作为数组返回。
+
++ `ObjC.selector(name)`: 将 JavaScript 字符串 `name` 转换为选择器
+
++ `ObjC.selectorAsString(sel)`: 将选择器 `sel` 转换为 JavaScript 字符串
+
+
+### Java
+
+
+ 从 Frida 17 开始,此runtime bridge不再包含在 Frida 的 GumJS 运行时中,可以通过运行以下命令获取:`npm install frida-java-bridge`。
+
+
+ 像这样将其导入到你的代理中:
+ `import Java from 'frida-java-bridge';`
+
+ 目前,在 Frida REPL 加载的脚本以及 frida-trace 中不需要这样做。
+
Java.cast(handle, klass): 给定 [`Java.use()`](#java-use) 返回的给定类 `klass` 的 `handle` 处的现有实例,
+ 创建一个 JavaScript 包装器。
+ 这样的包装器也有一个 `class` 属性用于获取其类的包装器,以及一个 `$className` 属性用于获取其类名的字符串表示形式。
+
+{% highlight js %}
+const Activity = Java.use('android.app.Activity');
+const activity = Java.cast(ptr('0x1234'), Activity);
+{% endhighlight %}
+
++ Java.array(type, elements): 从 JavaScript 数组 `elements` 创建一个具有指定 `type` 元素的 Java 数组。
+ 生成的 Java 数组的行为类似于 JS 数组,但可以通过引用传递给 Java API,以允许它们修改其内容。
+
+{% highlight js %}
+const values = Java.array('int', [ 1003, 1005, 1007 ]);
+
+const JString = Java.use('java.lang.String');
+const str = JString.$new(Java.array('byte', [ 0x48, 0x65, 0x69 ]));
+{% endhighlight %}
+
++ `Java.backtrace([options])`: 为当前线程生成回溯。
+ {: #java-backtrace}
+
+ 可选的 `options` 参数是一个对象,可能包含以下一些键:
+
+ - `limit`: 向上遍历堆栈的帧数,作为一个数字。
+ 默认为 16。
+
+ 返回一个具有以下属性的对象:
+
+ - `id`: 可用于对相同回溯进行重复数据删除的 ID,作为字符串。
+ - `frames`: 堆栈帧。包含以下属性的对象数组:
+
+ - `signature`: 堆栈帧签名作为字符串,例如
+ `Landroid/os/Looper;,loopOnce,(Landroid/os/Looper;JI)Z`
+ - `origin`: 代码来源,即指定文件系统路径的字符串。在 Android 上,这是 `.dex` 的路径。
+ - `className`: 方法所属的类名,作为字符串,例如
+ `android.os.Looper`
+ - `methodName`: 方法名作为字符串,例如 `loopOnce`
+ - `methodFlags`: 方法标志作为数字,例如
+ `Java.ACC_PUBLIC | Java.ACC_STATIC`
+ - `fileName`: 源文件名作为字符串,例如 `Looper.java`
+ - `lineNumber`: 源行号作为数字,例如 `201`
+
++ `Java.isMainThread()`: 确定调用者是否在主线程上运行。
+
++ `Java.registerClass(spec)`: 创建一个新的 Java 类并返回它的包装器,其中 `spec` 是一个包含以下内容的对象:
+ {: #java-registerclass}
+
+ - `name`: 指定类名的字符串。
+ - `superClass`: (可选) 超类。省略以继承自 `java.lang.Object`。
+ - `implements`: (可选) 此类实现的接口数组。
+ - `fields`: (可选) 指定要公开的每个字段的名称和类型的对象。
+ - `methods`: (可选) 指定要实现的方法的对象。
+
+{% highlight js %}
+const SomeBaseClass = Java.use('com.example.SomeBaseClass');
+const X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
+
+const MyTrustManager = Java.registerClass({
+ name: 'com.example.MyTrustManager',
+ implements: [X509TrustManager],
+ methods: {
+ checkClientTrusted(chain, authType) {
+ },
+ checkServerTrusted(chain, authType) {
+ },
+ getAcceptedIssuers() {
+ return [];
+ },
+ }
+});
+
+const MyWeirdTrustManager = Java.registerClass({
+ name: 'com.example.MyWeirdTrustManager',
+ superClass: SomeBaseClass,
+ implements: [X509TrustManager],
+ fields: {
+ description: 'java.lang.String',
+ limit: 'int',
+ },
+ methods: {
+ $init() {
+ console.log('Constructor called');
+ },
+ checkClientTrusted(chain, authType) {
+ console.log('checkClientTrusted');
+ },
+ checkServerTrusted: [{
+ returnType: 'void',
+ argumentTypes: ['[Ljava.security.cert.X509Certificate;', 'java.lang.String'],
+ implementation(chain, authType) {
+ console.log('checkServerTrusted A');
+ }
+ }, {
+ returnType: 'java.util.List',
+ argumentTypes: ['[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'],
+ implementation(chain, authType, host) {
+ console.log('checkServerTrusted B');
+ return null;
+ }
+ }],
+ getAcceptedIssuers() {
+ console.log('getAcceptedIssuers');
+ return [];
+ },
+ }
+});
+{% endhighlight %}
+
++ `Java.deoptimizeEverything()`: 强制 VM 使用其解释器执行所有操作。
+ 这对于防止优化在某些情况下绕过方法钩子是必要的,并允许使用 ART 的 Instrumentation API 来跟踪运行时。
+
++ `Java.deoptimizeBootImage()`: 类似于 Java.deoptimizeEverything(),但仅取消优化引导映像代码。
+ 与 `dalvik.vm.dex2oat-flags --inline-max-code-units=0` 一起使用以获得最佳效果。
+
++ `Java.vm`: 具有以下方法的对象:
+
+ - `perform(fn)`: 确保当前线程已附加到 VM 并调用 `fn`。(这在来自 Java 的回调中不是必需的。)
+
+ - `getEnv()`: 获取当前线程的 `JNIEnv` 的包装器。如果当前线程未附加到 VM,则抛出异常。
+
+ - `tryGetEnv()`: 尝试获取当前线程的 `JNIEnv` 的包装器。如果当前线程未附加到 VM,则返回 `null`。
+
++ `Java.classFactory`: 用于实现例如 [`Java.use()`](#java-use) 的默认类工厂。使用应用程序的主类加载器。
+
++ `Java.ClassFactory`: 具有以下属性的类:
+
+ + `get(classLoader)`: 获取给定类加载器的类工厂实例。
+ 在幕后使用的默认类工厂仅与应用程序的主类加载器交互。
+ 其他类加载器可以通过 `Java.enumerateClassLoaders()` 发现并通过此 API 进行交互。
+
+ - `loader`: 只读属性,提供当前正在使用的类加载器的包装器。
+ 对于默认类工厂,这是由第一次调用 [`Java.perform()`](#java-perform) 更新的。
+
+ - `cacheDir`: 包含当前正在使用的缓存目录路径的字符串。
+ 对于默认类工厂,这是由第一次调用 [`Java.perform()`](#java-perform) 更新的。
+
+ - `tempFileNaming`: 指定用于临时文件的命名约定的对象。默认为 `{ prefix: 'frida', suffix: 'dat' }`。
+
+ - `use(className)`: 像 [`Java.use()`](#java-use) 但用于特定的类加载器。
+
+ - `openClassFile(filePath)`: 像 [`Java.openClassFile()`](#java-openclassfile) 但用于特定的类加载器。
+
+ - `choose(className, callbacks)`: 像 [`Java.choose()`](#java-choose) 但用于特定的类加载器。
+
+ - `retain(obj)`: 像 [`Java.retain()`](#java-retain) 但用于特定的类加载器。
+
+ - `cast(handle, klass)`: 像 [`Java.cast()`](#java-cast) 但用于特定的类
+ loader.
+
+ - `array(type, elements)`: 像 [`Java.array()`](#java-array) 但用于特定的类加载器。
+
+ - `registerClass(spec)`: 像 [`Java.registerClass()`](#java-registerclass) 但用于特定的类加载器。
+
+---
+
+## CPU 指令
+
+
+### Instruction
+
++ `Instruction.parse(target)`: 解析内存中 `target` 地址处的指令,由 [`NativePointer`](#nativepointer) 表示。
+ 请注意,在 32 位 ARM 上,对于 ARM 函数,此地址的最低有效位必须设置为 0,对于 Thumb 函数,必须设置为 1。
+ 如果你从 Frida API(例如 [`Module#getExportByName()`](#module-getexportbyname))获取地址,Frida 会为你处理此细节。
+
+ 返回的对象具有以下字段:
+
+ - `address`: 此指令的地址 (EIP),作为 [`NativePointer`](#nativepointer)
+ - `next`: 指向下一条指令的指针,因此你可以 `parse()` 它
+ - `size`: 此指令的大小
+ - `mnemonic`: 指令助记符的字符串表示形式
+ - `opStr`: 指令操作数的字符串表示形式
+ - `operands`: 描述每个操作数的对象数组,每个对象至少指定 `type` 和 `value`,
+ 但也可能根据体系结构指定其他属性
+ - `regsRead`: 此指令隐式读取的寄存器名称数组
+ - `regsWritten`: 此指令隐式写入的寄存器名称数组
+ - `groups`: 此指令所属的组名数组
+ - `toString()`: 转换为人类可读的字符串
+
+ 有关 `operands` 和 `groups` 的详细信息,请查阅你的体系结构的 **[Capstone](http://www.capstone-engine.org/)** 文档。
+
+
+### X86Writer
+
++ `new X86Writer(codeAddress[, { pc: ptr('0x1234') }])`: 创建一个新的代码编写器,
+ 用于生成直接写入 `codeAddress` 处内存的 x86 机器代码,`codeAddress` 指定为 **[NativePointer](#nativepointer)**。
+ 第二个参数是一个可选的选项对象,可以在其中指定初始程序计数器,这在生成代码到暂存缓冲区时很有用。
+ 这在 iOS 上使用 [`Memory.patchCode()`](#memory-patchcode) 时至关重要,因为它可能会为你提供一个临时位置,
+ 稍后会将其映射到预期内存位置的内存中。
+
+- `reset(codeAddress[, { pc: ptr('0x1234') }])`: 回收实例
+
+- `dispose()`: 立即清理内存
+
+- `flush()`: 解析标签引用并将挂起的数据写入内存。完成代码生成后,你应该始终调用一次。
+ 通常也希望在不相关的代码片段之间执行此操作,例如一次生成多个函数时。
+
+- `base`: 输出的第一个字节的内存位置,作为 **[NativePointer](#nativepointer)**
+
+- `code`: 输出的下一个字节的内存位置,作为 **[NativePointer](#nativepointer)**
+
+- `pc`: 输出的下一个字节处的程序计数器,作为 **[NativePointer](#nativepointer)**
+
+- `offset`: 当前偏移量作为 JavaScript 数字
+
+- `putLabel(id)`: 在当前位置放置一个标签,其中 `id` 是一个可以在过去和将来的 `put*Label()` 调用中引用的字符串
+ {: #x86writer-putlabel}
+
+- `putCallAddressWithArguments(func, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码,
+ `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或
+ **[NativePointer](#nativepointer)**。
+
+- `putCallAddressWithAlignedArguments(func, args)`: 与上面类似,但也确保参数列表在 16 字节边界上对齐
+
+- `putCallRegWithArguments(reg, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码,
+ `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或
+ **[NativePointer](#nativepointer)**。
+
+- `putCallRegWithAlignedArguments(reg, args)`: 与上面类似,但也确保参数列表在 16 字节边界上对齐
+
+- `putCallRegOffsetPtrWithArguments(reg, offset, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码,
+ `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或
+ **[NativePointer](#nativepointer)**。
+
+- `putCallAddress(address)`: 放置一个 CALL 指令
+
+- `putCallReg(reg)`: 放置一个 CALL 指令
+
+- `putCallRegOffsetPtr(reg, offset)`: 放置一个 CALL 指令
+
+- `putCallIndirect(addr)`: 放置一个 CALL 指令
+
+- `putCallIndirectLabel(labelId)`: 放置一个引用 `labelId` 的 CALL 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#x86writer-putlabel) 定义
+
+- `putCallNearLabel(labelId)`: 放置一个引用 `labelId` 的 CALL 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#x86writer-putlabel) 定义
+
+- `putLeave()`: 放置一个 LEAVE 指令
+
+- `putRet()`: 放置一个 RET 指令
+
+- `putRetImm(immValue)`: 放置一个 RET 指令
+
+- `putJmpAddress(address)`: 放置一个 JMP 指令
+
+- `putJmpShortLabel(labelId)`: 放置一个引用 `labelId` 的 JMP 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#x86writer-putlabel) 定义
+
+- `putJmpNearLabel(labelId)`: 放置一个引用 `labelId` 的 JMP 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#x86writer-putlabel) 定义
+
+- `putJmpReg(reg)`: 放置一个 JMP 指令
+
+- `putJmpRegPtr(reg)`: 放置一个 JMP 指令
+
+- `putJmpRegOffsetPtr(reg, offset)`: 放置一个 JMP 指令
+
+- `putJmpNearPtr(address)`: 放置一个 JMP 指令
+
+- `putJccShort(instructionId, target, hint)`: 放置一个 JCC 指令
+
+- `putJccNear(instructionId, target, hint)`: 放置一个 JCC 指令
+
+- `putJccShortLabel(instructionId, labelId, hint)`: 放置一个引用 `labelId` 的 JCC 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#x86writer-putlabel) 定义
+
+- `putJccNearLabel(instructionId, labelId, hint)`: 放置一个引用 `labelId` 的 JCC 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#x86writer-putlabel) 定义
+
+- `putAddRegImm(reg, immValue)`: 放置一个 ADD 指令
+
+- `putAddRegReg(dstReg, srcReg)`: 放置一个 ADD 指令
+
+- `putAddRegNearPtr(dstReg, srcAddress)`: 放置一个 ADD 指令
+
+- `putSubRegImm(reg, immValue)`: 放置一个 SUB 指令
+
+- `putSubRegReg(dstReg, srcReg)`: 放置一个 SUB 指令
+
+- `putSubRegNearPtr(dstReg, srcAddress)`: 放置一个 SUB 指令
+
+- `putIncReg(reg)`: 放置一个 INC 指令
+
+- `putDecReg(reg)`: 放置一个 DEC 指令
+
+- `putIncRegPtr(target, reg)`: 放置一个 INC 指令
+
+- `putDecRegPtr(target, reg)`: 放置一个 DEC 指令
+
+- `putLockXaddRegPtrReg(dstReg, srcReg)`: 放置一个 LOCK XADD 指令
+
+- `putLockCmpxchgRegPtrReg(dstReg, srcReg)`: 放置一个 LOCK CMPXCHG 指令
+
+- `putLockIncImm32Ptr(target)`: 放置一个 LOCK INC IMM32 指令
+
+- `putLockDecImm32Ptr(target)`: 放置一个 LOCK DEC IMM32 指令
+
+- `putAndRegReg(dstReg, srcReg)`: 放置一个 AND 指令
+
+- `putAndRegU32(reg, immValue)`: 放置一个 AND 指令
+
+- `putShlRegU8(reg, immValue)`: 放置一个 SHL 指令
+
+- `putShrRegU8(reg, immValue)`: 放置一个 SHR 指令
+
+- `putXorRegReg(dstReg, srcReg)`: 放置一个 XOR 指令
+
+- `putMovRegReg(dstReg, srcReg)`: 放置一个 MOV 指令
+
+- `putMovRegU32(dstReg, immValue)`: 放置一个 MOV 指令
+
+- `putMovRegU64(dstReg, immValue)`: 放置一个 MOV 指令
+
+- `putMovRegAddress(dstReg, address)`: 放置一个 MOV 指令
+
+- `putMovRegPtrU32(dstReg, immValue)`: 放置一个 MOV 指令
+
+- `putMovRegOffsetPtrU32(dstReg, dstOffset, immValue)`: 放置一个 MOV 指令
+
+- `putMovRegPtrReg(dstReg, srcReg)`: 放置一个 MOV 指令
+
+- `putMovRegOffsetPtrReg(dstReg, dstOffset, srcReg)`: 放置一个 MOV 指令
+
+- `putMovRegRegPtr(dstReg, srcReg)`: 放置一个 MOV 指令
+
+- `putMovRegRegOffsetPtr(dstReg, srcReg, srcOffset)`: 放置一个 MOV 指令
+
+- `putMovRegBaseIndexScaleOffsetPtr(dstReg, baseReg, indexReg, scale, offset)`: 放置一个 MOV 指令
+
+- `putMovRegNearPtr(dstReg, srcAddress)`: 放置一个 MOV 指令
+
+- `putMovNearPtrReg(dstAddress, srcReg)`: 放置一个 MOV 指令
+
+- `putMovFsU32PtrReg(fsOffset, srcReg)`: 放置一个 MOV FS 指令
+
+- `putMovRegFsU32Ptr(dstReg, fsOffset)`: 放置一个 MOV FS 指令
+
+- `putMovFsRegPtrReg(fsOffset, srcReg)`: 放置一个 MOV FS 指令
+
+- `putMovRegFsRegPtr(dstReg, fsOffset)`: 放置一个 MOV FS 指令
+
+- `putMovGsU32PtrReg(fsOffset, srcReg)`: 放置一个 MOV GS 指令
+
+- `putMovRegGsU32Ptr(dstReg, fsOffset)`: 放置一个 MOV GS 指令
+
+- `putMovGsRegPtrReg(gsOffset, srcReg)`: 放置一个 MOV GS 指令
+
+- `putMovRegGsRegPtr(dstReg, gsOffset)`: 放置一个 MOV GS 指令
+
+- `putMovqXmm0EspOffsetPtr(offset)`: 放置一个 MOVQ XMM0 ESP 指令
+
+- `putMovqEaxOffsetPtrXmm0(offset)`: 放置一个 MOVQ EAX XMM0 指令
+
+- `putMovdquXmm0EspOffsetPtr(offset)`: 放置一个 MOVDQU XMM0 ESP 指令
+
+- `putMovdquEaxOffsetPtrXmm0(offset)`: 放置一个 MOVDQU EAX XMM0 指令
+
+- `putLeaRegRegOffset(dstReg, srcReg, srcOffset)`: 放置一个 LEA 指令
+
+- `putXchgRegRegPtr(leftReg, rightReg)`: 放置一个 XCHG 指令
+
+- `putPushU32(immValue)`: 放置一个 PUSH 指令
+
+- `putPushNearPtr(address)`: 放置一个 PUSH 指令
+
+- `putPushReg(reg)`: 放置一个 PUSH 指令
+
+- `putPopReg(reg)`: 放置一个 POP 指令
+
+- `putPushImmPtr(immPtr)`: 放置一个 PUSH 指令
+
+- `putPushax()`: 放置一个 PUSHAX 指令
+
+- `putPopax()`: 放置一个 POPAX 指令
+
+- `putPushfx()`: 放置一个 PUSHFX 指令
+
+- `putPopfx()`: 放置一个 POPFX 指令
+
+- `putSahf()`: 放置一个 SAHF 指令
+
+- `putLahf()`: 放置一个 LAHF 指令
+
+- `putTestRegReg(regA, regB)`: 放置一个 TEST 指令
+
+- `putTestRegU32(reg, immValue)`: 放置一个 TEST 指令
+
+- `putCmpRegI32(reg, immValue)`: 放置一个 CMP 指令
+
+- `putCmpRegOffsetPtrReg(regA, offset, regB)`: 放置一个 CMP 指令
+
+- `putCmpImmPtrImmU32(immPtr, immValue)`: 放置一个 CMP 指令
+
+- `putCmpRegReg(regA, regB)`: 放置一个 CMP 指令
+
+- `putClc()`: 放置一个 CLC 指令
+
+- `putStc()`: 放置一个 STC 指令
+
+- `putCld()`: 放置一个 CLD 指令
+
+- `putStd()`: 放置一个 STD 指令
+
+- `putCpuid()`: 放置一个 CPUID 指令
+
+- `putLfence()`: 放置一个 LFENCE 指令
+
+- `putRdtsc()`: 放置一个 RDTSC 指令
+
+- `putPause()`: 放置一个 PAUSE 指令
+
+- `putNop()`: 放置一个 NOP 指令
+
+- `putBreakpoint()`: 放置一个特定于操作系统/体系结构的断点指令
+
+- `putPadding(n)`: 放置 `n` 个保护指令
+
+- `putNopPadding(n)`: 放置 `n` 个 NOP 指令
+
+- `putFxsaveRegPtr(reg)`: 放置一个 FXSAVE 指令
+
+- `putFxrstorRegPtr(reg)`: 放置一个 FXRSTOR 指令
+
+- `putU8(value)`: 放置一个 uint8
+
+- `putS8(value)`: 放置一个 int8
+
+- `putBytes(data)`: 放置来自提供的 **[ArrayBuffer](#arraybuffer)** 的原始数据
+
+
+### X86Relocator
+
++ `new X86Relocator(inputCode, output)`: 创建一个新的代码重定位器,用于将 x86 指令从一个内存位置复制到另一个内存位置,
+ 并注意相应地调整位置相关指令。
+ 源地址由 `inputCode` 指定,这是一个 **[NativePointer](#nativepointer)**。
+ 目的地由 `output` 给出,这是一个指向所需目标内存地址的 **[X86Writer](#x86writer)**。
+
+- `reset(inputCode, output)`: 回收实例
+
+- `dispose()`: 立即清理内存
+
+- `input`: 到目前为止读取的最新 **[Instruction](#instruction)**。开始时为 `null`,
+ 并在每次调用 [`readOne()`](#x86relocator-readone) 时更改。
+
+- `eob`: 布尔值,指示是否已到达块的末尾,即我们已到达任何类型的分支,如 CALL、JMP、BL、RET。
+
+- `eoi`: 布尔值,指示是否已到达输入的末尾,例如我们已到达 JMP/B/RET,这是一条指令,其后可能有也可能没有有效代码。
+
+- `readOne()`: 将下一条指令读入重定位器的内部缓冲区,并返回到目前为止读取的字节数,包括以前的调用。
+ 你可以继续调用此方法以继续缓冲,或者立即调用 [`writeOne()`](#x86relocator-writeone) 或 [`skipOne()`](#x86relocator-skipone)。
+ 或者,你可以缓冲直到所需的点,然后调用 [`writeAll()`](#x86relocator-writeall)。
+ 当到达输入末尾时返回零,这意味着 `eoi` 属性现在为 `true`。
+ {: #x86relocator-readone}
+
+- `peekNextWriteInsn()`: 查看要写入或跳过的下一条 **[Instruction](#instruction)**
+
+- `peekNextWriteSource()`: 查看要写入或跳过的下一条指令的地址
+
+- `skipOne()`: 跳过本来要写入的下一条指令
+ {: #x86relocator-skipone}
+
+- `skipOneNoLabel()`: 跳过本来要写入的下一条指令,但不使用标签供内部使用。
+ 这破坏了分支到重定位范围内的位置的重定位,并且是针对所有分支都被重写(例如 Frida 的 **[Stalker](#stalker)**)的用例的优化。
+
+- `writeOne()`: 写入下一条缓冲的指令
+ {: #x86relocator-writeone}
+
+- `writeOneNoLabel()`: 写入下一条缓冲的指令,但不使用标签供内部使用。
+ 这破坏了分支到重定位范围内的位置的重定位,并且是针对所有分支都被重写(例如 Frida 的 **[Stalker](#stalker)**)的用例的优化。
+
+- `writeAll()`: 写入所有缓冲的指令
+ {: #x86relocator-writeall}
+
+
+### x86 enum types
+
+- Register: `xax` `xcx` `xdx` `xbx` `xsp` `xbp` `xsi` `xdi` `eax` `ecx` `edx`
+ `ebx` `esp` `ebp` `esi` `edi` `rax` `rcx` `rdx` `rbx` `rsp` `rbp` `rsi`
+ `rdi` `r8` `r9` `r10` `r11` `r12` `r13` `r14` `r15` `r8d` `r9d` `r10d`
+ `r11d` `r12d` `r13d` `r14d` `r15d` `xip` `eip` `rip`
+- InstructionId: `jo` `jno` `jb` `jae` `je` `jne` `jbe` `ja` `js` `jns` `jp`
+ `jnp` `jl` `jge` `jle` `jg` `jcxz` `jecxz` `jrcxz`
+- BranchHint: `no-hint` `likely` `unlikely`
+- PointerTarget: `byte` `dword` `qword`
+
+
+### ArmWriter
+
++ `new ArmWriter(codeAddress[, { pc: ptr('0x1234') }])`: 创建一个新的代码编写器,
+ 用于生成直接写入 `codeAddress` 处内存的 ARM 机器代码,`codeAddress` 指定为 **[NativePointer](#nativepointer)**。
+ 第二个参数是一个可选的选项对象,可以在其中指定初始程序计数器,这在生成代码到暂存缓冲区时很有用。
+ 这在 iOS 上使用 [`Memory.patchCode()`](#memory-patchcode) 时至关重要,因为它可能会为你提供一个临时位置,
+ 稍后会将其映射到预期内存位置的内存中。
+
+- `reset(codeAddress[, { pc: ptr('0x1234') }])`: 回收实例
+
+- `dispose()`: 立即清理内存
+
+- `flush()`: 解析标签引用并将挂起的数据写入内存。完成代码生成后,你应该始终调用一次。
+ 通常也希望在不相关的代码片段之间执行此操作,例如一次生成多个函数时。
+
+- `base`: 输出的第一个字节的内存位置,作为 **[NativePointer](#nativepointer)**
+
+- `code`: 输出的下一个字节的内存位置,作为 **[NativePointer](#nativepointer)**
+
+- `pc`: 输出的下一个字节处的程序计数器,作为 **[NativePointer](#nativepointer)**
+
+- `offset`: 当前偏移量作为 JavaScript 数字
+
+- `skip(nBytes)`: 跳过 `nBytes`
+
+- `putLabel(id)`: 在当前位置放置一个标签,其中 `id` 是一个可以在过去和将来的 `put*Label()` 调用中引用的字符串
+ {: #armwriter-putlabel}
+
+- `putCallAddressWithArguments(func, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码,
+ `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或
+ **[NativePointer](#nativepointer)**。
+
+- `putCallReg(reg)`: 放置一个 CALL 指令
+
+- `putCallRegWithArguments(reg, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码,
+ `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或
+ NativePointer 指定的立即值。
+
+- `putBranchAddress(address)`: 放置分支/跳转到给定地址所需的代码
+
+- `canBranchDirectlyBetween(from, to)`: 确定两个给定内存位置之间是否可以直接分支
+
+- `putBImm(target)`: 放置一个 B 指令
+
+- `putBCondImm(cc, target)`: 放置一个 B COND 指令
+
+- `putBLabel(labelId)`: 放置一个引用 `labelId` 的 B 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#armwriter-putlabel) 定义
+
+- `putBCondLabel(cc, labelId)`: 放置一个引用 `labelId` 的 B COND 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#armwriter-putlabel) 定义
+
+- `putBlImm(target)`: 放置一个 BL 指令
+
+- `putBlxImm(target)`: 放置一个 BLX 指令
+
+- `putBlLabel(labelId)`: 放置一个引用 `labelId` 的 BL 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#armwriter-putlabel) 定义
+
+- `putBxReg(reg)`: 放置一个 BX 指令
+
+- `putBlReg(reg)`: 放置一个 BL 指令
+
+- `putBlxReg(reg)`: 放置一个 BLX 指令
+
+- `putRet()`: 放置一个 RET 指令
+
+- `putVpushRange(firstReg, lastReg)`: 放置一个 VPUSH RANGE 指令
+
+- `putVpopRange(firstReg, lastReg)`: 放置一个 VPOP RANGE 指令
+
+- `putLdrRegAddress(reg, address)`: 放置一个 LDR 指令
+
+- `putLdrRegU32(reg, val)`: 放置一个 LDR 指令
+
+- `putLdrRegReg(dstReg, srcReg)`: 放置一个 LDR 指令
+
+- `putLdrRegRegOffset(dstReg, srcReg, srcOffset)`: 放置一个 LDR 指令
+
+- `putLdrCondRegRegOffset(cc, dstReg, srcReg, srcOffset)`: 放置一个 LDR COND 指令
+
+- `putLdmiaRegMask(reg, mask)`: 放置一个 LDMIA MASK 指令
+
+- `putLdmiaRegMaskWb(reg, mask)`: 放置一个 LDMIA MASK WB 指令
+
+- `putStrRegReg(srcReg, dstReg)`: 放置一个 STR 指令
+
+- `putStrRegRegOffset(srcReg, dstReg, dstOffset)`: 放置一个 STR 指令
+
+- `putStrCondRegRegOffset(cc, srcReg, dstReg, dstOffset)`: 放置一个 STR COND 指令
+
+- `putMovRegReg(dstReg, srcReg)`: 放置一个 MOV 指令
+
+- `putMovRegRegShift(dstReg, srcReg, shift, shiftValue)`: 放置一个 MOV SHIFT 指令
+
+- `putMovRegCpsr(reg)`: 放置一个 MOV CPSR 指令
+
+- `putMovCpsrReg(reg)`: 放置一个 MOV CPSR 指令
+
+- `putAddRegU16(dstReg, val)`: 放置一个 ADD U16 指令
+
+- `putAddRegU32(dstReg, val)`: 放置一个 ADD 指令
+
+- `putAddRegRegImm(dstReg, srcReg, immVal)`: 放置一个 ADD 指令
+
+- `putAddRegRegReg(dstReg, srcReg1, srcReg2)`: 放置一个 ADD 指令
+
+- `putAddRegRegRegShift(dstReg, srcReg1, srcReg2, shift, shiftValue)`: 放置一个 ADD SHIFT 指令
+
+- `putSubRegU16(dstReg, val)`: 放置一个 SUB U16 指令
+
+- `putSubRegU32(dstReg, val)`: 放置一个 SUB 指令
+
+- `putSubRegRegImm(dstReg, srcReg, immVal)`: 放置一个 SUB 指令
+
+- `putSubRegRegReg(dstReg, srcReg1, srcReg2)`: 放置一个 SUB 指令
+
+- `putRsbRegRegImm(dstReg, srcReg, immVal)`: 放置一个 RSB 指令
+
+- `putAndsRegRegImm(dstReg, srcReg, immVal)`: 放置一个 ANDS 指令
+
+- `putCmpRegImm(dstReg, immVal)`: 放置一个 CMP 指令
+
+- `putNop()`: 放置一个 NOP 指令
+
+- `putBreakpoint()`: 放置一个特定于操作系统/体系结构的断点指令
+
+- `putBrkImm(imm)`: 放置一个 BRK 指令
+
+- `putInstruction(insn)`: 将原始指令作为 JavaScript 数字放置
+
+- `putBytes(data)`: 放置来自提供的 **[ArrayBuffer](#arraybuffer)** 的原始数据
+
+
+### ArmRelocator
+
++ `new ArmRelocator(inputCode, output)`: 创建一个新的代码重定位器,用于将 ARM 指令从一个内存位置复制到另一个内存位置,
+ 并注意相应地调整位置相关指令。
+ 源地址由 `inputCode` 指定,这是一个 **[NativePointer](#nativepointer)**。
+ 目的地由 `output` 给出,这是一个指向所需目标内存地址的 **[ArmWriter](#armwriter)**。
+
+- `reset(inputCode, output)`: 回收实例
+
+- `dispose()`: 立即清理内存
+
+- `input`: 到目前为止读取的最新 **[Instruction](#instruction)**。开始时为 `null`,
+ 并在每次调用 [`readOne()`](#armrelocator-readone) 时更改。
+
+- `eob`: 布尔值,指示是否已到达块的末尾,即我们已到达任何类型的分支,如 CALL、JMP、BL、RET。
+
+- `eoi`: 布尔值,指示是否已到达输入的末尾,例如我们已到达 JMP/B/RET,这是一条指令,其后可能有也可能没有有效代码。
+
+- `readOne()`: 将下一条指令读入重定位器的内部缓冲区,并返回到目前为止读取的字节数,包括以前的调用。
+ 你可以继续调用此方法以继续缓冲,或者立即调用 [`writeOne()`](#armrelocator-writeone) 或 [`skipOne()`](#armrelocator-skipone)。
+ 或者,你可以缓冲直到所需的点,然后调用 [`writeAll()`](#armrelocator-writeall)。
+ 当到达输入末尾时返回零,这意味着 `eoi` 属性现在为 `true`。
+ {: #armrelocator-readone}
+
+- `peekNextWriteInsn()`: 查看要写入或跳过的下一条 **[Instruction](#instruction)**
+
+- `peekNextWriteSource()`: 查看要写入或跳过的下一条指令的地址
+
+- `skipOne()`: 跳过本来要写入的下一条指令
+ {: #armrelocator-skipone}
+
+- `writeOne()`: 写入下一条缓冲的指令
+ {: #armrelocator-writeone}
+
+- `writeAll()`: 写入所有缓冲的指令
+ {: #armrelocator-writeall}
+
+
+### ThumbWriter
+
++ `new ThumbWriter(codeAddress[, { pc: ptr('0x1234') }])`: 创建一个新的代码编写器,
+ 用于生成直接写入 `codeAddress` 处内存的 ARM 机器代码,`codeAddress` 指定为 **[NativePointer](#nativepointer)**。
+ 第二个参数是一个可选的选项对象,可以在其中指定初始程序计数器,这在生成代码到暂存缓冲区时很有用。
+ 这在 iOS 上使用 [`Memory.patchCode()`](#memory-patchcode) 时至关重要,因为它可能会为你提供一个临时位置,
+ 稍后会将其映射到预期内存位置的内存中。
+
+- `reset(codeAddress[, { pc: ptr('0x1234') }])`: 回收实例
+
+- `dispose()`: 立即清理内存
+
+- `flush()`: 解析标签引用并将挂起的数据写入内存。完成代码生成后,你应该始终调用一次。
+ 通常也希望在不相关的代码片段之间执行此操作,例如一次生成多个函数时。
+
+- `base`: 输出的第一个字节的内存位置,作为 **[NativePointer](#nativepointer)**
+
+- `code`: 输出的下一个字节的内存位置,作为 **[NativePointer](#nativepointer)**
+
+- `pc`: 输出的下一个字节处的程序计数器,作为 **[NativePointer](#nativepointer)**
+
+- `offset`: 当前偏移量作为 JavaScript 数字
+
+- `skip(nBytes)`: 跳过 `nBytes`
+
+- `putLabel(id)`: 在当前位置放置一个标签,其中 `id` 是一个可以在过去和将来的 `put*Label()` 调用中引用的字符串
+ {: #thumbwriter-putlabel}
+
+- `commitLabel(id)`: 提交对给定标签的第一个挂起引用,成功时返回 `true`。
+ 如果尚未定义给定标签,或者没有更多对它的挂起引用,则返回 `false`。
+
+- `putCallAddressWithArguments(func, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码,
+ `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或
+ **[NativePointer](#nativepointer)**。
+
+- `putCallRegWithArguments(reg, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码,
+ `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或
+ **[NativePointer](#nativepointer)**。
+
+- `putBranchAddress(address)`: 放置分支/跳转到给定地址所需的代码
+
+- `canBranchDirectlyBetween(from, to)`: 确定两个给定内存位置之间是否可以直接分支
+
+- `putBImm(target)`: 放置一个 B 指令
+
+- `putBLabel(labelId)`: 放置一个引用 `labelId` 的 B 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#thumbwriter-putlabel) 定义
+
+- `putBLabelWide(labelId)`: 放置一个 B WIDE 指令
+
+- `putBxReg(reg)`: 放置一个 BX 指令
+
+- `putBlImm(target)`: 放置一个 BL 指令
+
+- `putBlLabel(labelId)`: 放置一个引用 `labelId` 的 BL 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#thumbwriter-putlabel) 定义
+
+- `putBlxImm(target)`: 放置一个 BLX 指令
+
+- `putBlxReg(reg)`: 放置一个 BLX 指令
+
+- `putCmpRegImm(reg, immValue)`: 放置一个 CMP 指令
+
+- `putBeqLabel(labelId)`: 放置一个引用 `labelId` 的 BEQ 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#thumbwriter-putlabel) 定义
+
+- `putBneLabel(labelId)`: 放置一个引用 `labelId` 的 BNE 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#thumbwriter-putlabel) 定义
+
+- `putBCondLabel(cc, labelId)`: 放置一个引用 `labelId` 的 B COND 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#thumbwriter-putlabel) 定义
+
+- `putBCondLabelWide(cc, labelId)`: 放置一个 B COND WIDE 指令
+
+- `putCbzRegLabel(reg, labelId)`: 放置一个引用 `labelId` 的 CBZ 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#thumbwriter-putlabel) 定义
+
+- `putCbnzRegLabel(reg, labelId)`: 放置一个引用 `labelId` 的 CBNZ 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#thumbwriter-putlabel) 定义
+
+- `putPushRegs(regs)`: 放置一个具有指定寄存器的 PUSH 指令,
+ 指定为 JavaScript 数组,其中每个元素是指定寄存器名称的字符串。
+
+- `putPopRegs(regs)`: 放置一个具有指定寄存器的 POP 指令,
+ 指定为 JavaScript 数组,其中每个元素是指定寄存器名称的字符串。
+
+- `putVpushRange(firstReg, lastReg)`: 放置一个 VPUSH RANGE 指令
+
+- `putVpopRange(firstReg, lastReg)`: 放置一个 VPOP RANGE 指令
+
+- `putLdrRegAddress(reg, address)`: 放置一个 LDR 指令
+
+- `putLdrRegU32(reg, val)`: 放置一个 LDR 指令
+
+- `putLdrRegReg(dstReg, srcReg)`: 放置一个 LDR 指令
+
+- `putLdrRegRegOffset(dstReg, srcReg, srcOffset)`: 放置一个 LDR 指令
+
+- `putLdrbRegReg(dstReg, srcReg)`: 放置一个 LDRB 指令
+
+- `putVldrRegRegOffset(dstReg, srcReg, srcOffset)`: 放置一个 VLDR 指令
+
+- `putLdmiaRegMask(reg, mask)`: 放置一个 LDMIA MASK 指令
+
+- `putStrRegReg(srcReg, dstReg)`: 放置一个 STR 指令
+
+- `putStrRegRegOffset(srcReg, dstReg, dstOffset)`: 放置一个 STR 指令
+
+- `putMovRegReg(dstReg, srcReg)`: 放置一个 MOV 指令
+
+- `putMovRegU8(dstReg, immValue)`: 放置一个 MOV 指令
+
+- `putMovRegCpsr(reg)`: 放置一个 MOV CPSR 指令
+
+- `putMovCpsrReg(reg)`: 放置一个 MOV CPSR 指令
+
+- `putAddRegImm(dstReg, immValue)`: 放置一个 ADD 指令
+
+- `putAddRegReg(dstReg, srcReg)`: 放置一个 ADD 指令
+
+- `putAddRegRegReg(dstReg, leftReg, rightReg)`: 放置一个 ADD 指令
+
+- `putAddRegRegImm(dstReg, leftReg, rightValue)`: 放置一个 ADD 指令
+
+- `putSubRegImm(dstReg, immValue)`: 放置一个 SUB 指令
+
+- `putSubRegReg(dstReg, srcReg)`: 放置一个 SUB 指令
+
+- `putSubRegRegReg(dstReg, leftReg, rightReg)`: 放置一个 SUB 指令
+
+- `putSubRegRegImm(dstReg, leftReg, rightValue)`: 放置一个 SUB 指令
+
+- `putAndRegRegImm(dstReg, leftReg, rightValue)`: 放置一个 AND 指令
+
+- `putOrRegRegImm(dstReg, leftReg, rightValue)`: 放置一个 OR 指令
+
+- `putLslRegRegImm(dstReg, leftReg, rightValue)`: 放置一个 LSL 指令
+
+- `putLslsRegRegImm(dstReg, leftReg, rightValue)`: 放置一个 LSLS 指令
+
+- `putLsrsRegRegImm(dstReg, leftReg, rightValue)`: 放置一个 LSRS 指令
+
+- `putMrsRegReg(dstReg, srcReg)`: 放置一个 MRS 指令
+
+- `putMsrRegReg(dstReg, srcReg)`: 放置一个 MSR 指令
+
+- `putNop()`: 放置一个 NOP 指令
+
+- `putBkptImm(imm)`: 放置一个 BKPT 指令
+
+- `putBreakpoint()`: 放置一个特定于操作系统/体系结构的断点指令
+
+- `putInstruction(insn)`: 将原始指令作为 JavaScript 数字放置
+
+- `putInstructionWide(upper, lower)`: 从两个 JavaScript 数字值放置一个原始 Thumb-2 指令
+
+- `putBytes(data)`: 放置来自提供的 **[ArrayBuffer](#arraybuffer)** 的原始数据
+
+
+### ThumbRelocator
+
++ `new ThumbRelocator(inputCode, output)`: 创建一个新的代码重定位器,用于将 ARM 指令从一个内存位置复制到另一个内存位置,
+ 并注意相应地调整位置相关指令。
+ 源地址由 `inputCode` 指定,这是一个 **[NativePointer](#nativepointer)**。
+ 目的地由 `output` 给出,这是一个指向所需目标内存地址的 **[ThumbWriter](#thumbwriter)**。
+
+- `reset(inputCode, output)`: 回收实例
+
+- `dispose()`: 立即清理内存
+
+- `input`: 到目前为止读取的最新 **[Instruction](#instruction)**。开始时为 `null`,
+ 并在每次调用 [`readOne()`](#thumbrelocator-readone) 时更改。
+
+- `eob`: 布尔值,指示是否已到达块的末尾,即我们已到达任何类型的分支,如 CALL、JMP、BL、RET。
+
+- `eoi`: 布尔值,指示是否已到达输入的末尾,例如我们已到达 JMP/B/RET,这是一条指令,其后可能有也可能没有有效代码。
+
+- `readOne()`: 将下一条指令读入重定位器的内部缓冲区,并返回到目前为止读取的字节数,包括以前的调用。
+ 你可以继续调用此方法以继续缓冲,或者立即调用 [`writeOne()`](#thumbrelocator-writeone) 或 [`skipOne()`](#thumbrelocator-skipone)。
+ 或者,你可以缓冲直到所需的点,然后调用 [`writeAll()`](#thumbrelocator-writeall)。
+ 当到达输入末尾时返回零,这意味着 `eoi` 属性现在为 `true`。
+ {: #thumbrelocator-readone}
+
+- `peekNextWriteInsn()`: 查看要写入或跳过的下一条 **[Instruction](#instruction)**
+
+- `peekNextWriteSource()`: 查看要写入或跳过的下一条指令的地址
+
+- `skipOne()`: 跳过本来要写入的下一条指令
+ {: #thumbrelocator-skipone}
+
+- `writeOne()`: 写入下一条缓冲的指令
+ {: #thumbrelocator-writeone}
+
+- `copyOne()`: 复制下一条缓冲的指令而不推进输出光标,允许将同一条指令写出多次
+
+- `writeAll()`: 写入所有缓冲的指令
+ {: #thumbrelocator-writeall}
+
+
+### ARM enum types
+
+- Register: `r0` `r1` `r2` `r3` `r4` `r5` `r6` `r7` `r8` `r9` `r10` `r11`
+ `r12` `r13` `r14` `r15` `sp` `lr` `sb` `sl` `fp` `ip` `pc` `s0` `s1` `s2`
+ `s3` `s4` `s5` `s6` `s7` `s8` `s9` `s10` `s11` `s12` `s13` `s14` `s15`
+ `s16` `s17` `s18` `s19` `s20` `s21` `s22` `s23` `s24` `s25` `s26` `s27`
+ `s28` `s29` `s30` `s31` `d0` `d1` `d2` `d3` `d4` `d5` `d6` `d7` `d8` `d9`
+ `d10` `d11` `d12` `d13` `d14` `d15` `d16` `d17` `d18` `d19` `d20` `d21`
+ `d22` `d23` `d24` `d25` `d26` `d27` `d28` `d29` `d30` `d31` `q0` `q1` `q2`
+ `q3` `q4` `q5` `q6` `q7` `q8` `q9` `q10` `q11` `q12` `q13` `q14` `q15`
+- SystemRegister: `apsr-nzcvq`
+- ConditionCode: `eq` `ne` `hs` `lo` `mi` `pl` `vs` `vc` `hi` `ls` `ge` `lt`
+ `gt` `le` `al`
+- Shifter: `asr` `lsl` `lsr` `ror` `rrx` `asr-reg` `lsl-reg` `lsr-reg`
+ `ror-reg` `rrx-reg`
+
+
+### Arm64Writer
+
++ `new Arm64Writer(codeAddress[, { pc: ptr('0x1234') }])`: 创建一个新的代码编写器,
+ 用于生成直接写入 `codeAddress` 处内存的 AArch64 机器代码,`codeAddress` 指定为 **[NativePointer](#nativepointer)**。
+ 第二个参数是一个可选的选项对象,可以在其中指定初始程序计数器,这在生成代码到暂存缓冲区时很有用。
+ 这在 iOS 上使用 [`Memory.patchCode()`](#memory-patchcode) 时至关重要,因为它可能会为你提供一个临时位置,
+ 稍后会将其映射到预期内存位置的内存中。
+
+- `reset(codeAddress[, { pc: ptr('0x1234') }])`: 回收实例
+
+- `dispose()`: 立即清理内存
+
+- `flush()`: 解析标签引用并将挂起的数据写入内存。完成代码生成后,你应该始终调用一次。
+ 通常也希望在不相关的代码片段之间执行此操作,例如一次生成多个函数时。
+
+- `base`: 输出的第一个字节的内存位置,作为 NativePointer
+
+- `code`: 输出的下一个字节的内存位置,作为 NativePointer
+
+- `pc`: 输出的下一个字节处的程序计数器,作为 NativePointer
+
+- `offset`: 当前偏移量作为 JavaScript 数字
+
+- `skip(nBytes)`: 跳过 `nBytes`
+
+- `putLabel(id)`: 在当前位置放置一个标签,其中 `id` 是一个可以在过去和将来的 `put*Label()` 调用中引用的字符串
+ {: #arm64writer-putlabel}
+
+- `putCallAddressWithArguments(func, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码,
+ `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或
+ **[NativePointer](#nativepointer)**。
+
+- `putCallRegWithArguments(reg, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码,
+ `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或
+ **[NativePointer](#nativepointer)**。
+
+- `putBranchAddress(address)`: 放置分支/跳转到给定地址所需的代码
+
+- `canBranchDirectlyBetween(from, to)`: 确定两个给定内存位置之间是否可以直接分支
+
+- `putBImm(address)`: 放置一个 B 指令
+
+- `putBLabel(labelId)`: 放置一个引用 `labelId` 的 B 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#arm64writer-putlabel) 定义
+
+- `putBCondLabel(cc, labelId)`: 放置一个引用 `labelId` 的 B COND 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#arm64writer-putlabel) 定义
+
+- `putBlImm(address)`: 放置一个 BL 指令
+
+- `putBlLabel(labelId)`: 放置一个引用 `labelId` 的 BL 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#arm64writer-putlabel) 定义
+
+- `putBrReg(reg)`: 放置一个 BR 指令
+
+- `putBrRegNoAuth(reg)`: 放置一个 BR 指令,期望一个没有任何认证位的原始指针
+
+- `putBlrReg(reg)`: 放置一个 BLR 指令
+
+- `putBlrRegNoAuth(reg)`: 放置一个 BLR 指令,期望一个没有任何认证位的原始指针
+
+- `putRet()`: 放置一个 RET 指令
+
+- `putRetReg(reg)`: 放置一个 RET 指令
+
+- `putCbzRegImm(reg, target)`: 放置一个 CBZ 指令
+
+- `putCbnzRegImm(reg, target)`: 放置一个 CBNZ 指令
+
+- `putCbzRegLabel(reg, labelId)`: 放置一个引用 `labelId` 的 CBZ 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#arm64writer-putlabel) 定义
+
+- `putCbnzRegLabel(reg, labelId)`: 放置一个引用 `labelId` 的 CBNZ 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#arm64writer-putlabel) 定义
+
+- `putTbzRegImmImm(reg, bit, target)`: 放置一个 TBZ 指令
+
+- `putTbnzRegImmImm(reg, bit, target)`: 放置一个 TBNZ 指令
+
+- `putTbzRegImmLabel(reg, bit, labelId)`: 放置一个引用 `labelId` 的 TBZ 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#arm64writer-putlabel) 定义
+
+- `putTbnzRegImmLabel(reg, bit, labelId)`: 放置一个引用 `labelId` 的 TBNZ 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#arm64writer-putlabel) 定义
+
+- `putPushRegReg(regA, regB)`: 放置一个 PUSH 指令
+
+- `putPopRegReg(regA, regB)`: 放置一个 POP 指令
+
+- `putPushAllXRegisters()`: 放置将所有 X 寄存器推入堆栈所需的代码
+
+- `putPopAllXRegisters()`: 放置将所有 X 寄存器从堆栈弹出所需的代码
+
+- `putPushAllQRegisters()`: 放置将所有 Q 寄存器推入堆栈所需的代码
+
+- `putPopAllQRegisters()`: 放置将所有 Q 寄存器从堆栈弹出所需的代码
+
+- `putLdrRegAddress(reg, address)`: 放置一个 LDR 指令
+
+- `putLdrRegU32(reg, val)`: 放置一个 LDR 指令
+
+- `putLdrRegU64(reg, val)`: 放置一个 LDR 指令
+
+- `putLdrRegU32Ptr(reg, srcAddress)`: 放置一个 LDR 指令
+
+- `putLdrRegU64Ptr(reg, srcAddress)`: 放置一个 LDR 指令
+
+- `putLdrRegRef(reg)`: 放置一个带有悬空数据引用的 LDR 指令,
+ 返回一个不透明的 ref 值,该值应传递给所需位置的 [`putLdrRegValue()`](#arm64writer-putldrregvalue)
+ {: #arm64writer-putldrregref}
+
+- `putLdrRegValue(ref, value)`: 从先前的 [`putLdrRegRef()`](#arm64writer-putldrregref) 放置值并更新 LDR 指令
+ {: #arm64writer-putldrregvalue}
+
+- `putLdrRegReg(dstReg, srcReg)`: 放置一个 LDR 指令
+
+- `putLdrRegRegOffset(dstReg, srcReg, srcOffset)`: 放置一个 LDR 指令
+
+- `putLdrRegRegOffsetMode(dstReg, srcReg, srcOffset, mode)`: 放置一个 LDR MODE 指令
+
+- `putLdrswRegRegOffset(dstReg, srcReg, srcOffset)`: 放置一个 LDRSW 指令
+
+- `putAdrpRegAddress(reg, address)`: 放置一个 ADRP 指令
+
+- `putStrRegReg(srcReg, dstReg)`: 放置一个 STR 指令
+
+- `putStrRegRegOffset(srcReg, dstReg, dstOffset)`: 放置一个 STR 指令
+
+- `putStrRegRegOffsetMode(srcReg, dstReg, dstOffset, mode)`: 放置一个 STR MODE 指令
+
+- `putLdpRegRegRegOffset(regA, regB, regSrc, srcOffset, mode)`: 放置一个 LDP 指令
+
+- `putStpRegRegRegOffset(regA, regB, regDst, dstOffset, mode)`: 放置一个 STP 指令
+
+- `putMovRegReg(dstReg, srcReg)`: 放置一个 MOV 指令
+
+- `putMovRegNzcv(reg)`: 放置一个 MOV NZCV 指令
+
+- `putMovNzcvReg(reg)`: 放置一个 MOV NZCV 指令
+
+- `putUxtwRegReg(dstReg, srcReg)`: 放置一个 UXTW 指令
+
+- `putAddRegRegImm(dstReg, leftReg, rightValue)`: 放置一个 ADD 指令
+
+- `putAddRegRegReg(dstReg, leftReg, rightReg)`: 放置一个 ADD 指令
+
+- `putSubRegRegImm(dstReg, leftReg, rightValue)`: 放置一个 SUB 指令
+
+- `putSubRegRegReg(dstReg, leftReg, rightReg)`: 放置一个 SUB 指令
+
+- `putAndRegRegImm(dstReg, leftReg, rightValue)`: 放置一个 AND 指令
+
+- `putEorRegRegReg(dstReg, leftReg, rightReg)`: 放置一个 EOR 指令
+
+- `putUbfm(dstReg, srcReg, imms, immr)`: 放置一个 UBFM 指令
+
+- `putLslRegImm(dstReg, srcReg, shift)`: 放置一个 LSL 指令
+
+- `putLsrRegImm(dstReg, srcReg, shift)`: 放置一个 LSR 指令
+
+- `putTstRegImm(reg, immValue)`: 放置一个 TST 指令
+
+- `putCmpRegReg(regA, regB)`: 放置一个 CMP 指令
+
+- `putXpaciReg(reg)`: 放置一个 XPACI 指令
+
+- `putNop()`: 放置一个 NOP 指令
+
+- `putBrkImm(imm)`: 放置一个 BRK 指令
+
+- `putMrs(dstReg, systemReg)`: 放置一个 MRS 指令
+
+- `putInstruction(insn)`: 将原始指令作为 JavaScript 数字放置
+
+- `putBytes(data)`: 放置来自提供的 **[ArrayBuffer](#arraybuffer)** 的原始数据
+
+- `sign(value)`: 对给定的指针值进行签名
+
+
+### Arm64Relocator
+
++ `new Arm64Relocator(inputCode, output)`: 创建一个新的代码重定位器,用于将 AArch64 指令从一个内存位置复制到另一个内存位置,
+ 并注意相应地调整位置相关指令。
+ 源地址由 `inputCode` 指定,这是一个 **[NativePointer](#nativepointer)**。
+ 目的地由 `output` 给出,这是一个指向所需目标内存地址的 **[Arm64Writer](#arm64writer)**。
+
+- `reset(inputCode, output)`: 回收实例
+
+- `dispose()`: 立即清理内存
+
+- `input`: 到目前为止读取的最新 **[Instruction](#instruction)**。开始时为 `null`,
+ 并在每次调用 [`readOne()`](#arm64relocator-readone) 时更改。
+
+- `eob`: 布尔值,指示是否已到达块的末尾,即我们已到达任何类型的分支,如 CALL、JMP、BL、RET。
+
+- `eoi`: 布尔值,指示是否已到达输入的末尾,例如我们已到达 JMP/B/RET,这是一条指令,其后可能有也可能没有有效代码。
+
+- `readOne()`: 将下一条指令读入重定位器的内部缓冲区,并返回到目前为止读取的字节数,包括以前的调用。
+ 你可以继续调用此方法以继续缓冲,或者立即调用 [`writeOne()`](#arm64relocator-writeone) 或 [`skipOne()`](#arm64relocator-skipone)。
+ 或者,你可以缓冲直到所需的点,然后调用 [`writeAll()`](#arm64relocator-writeall)。
+ 当到达输入末尾时返回零,这意味着 `eoi` 属性现在为 `true`。
+ {: #arm64relocator-readone}
+
+- `peekNextWriteInsn()`: 查看要写入或跳过的下一条 **[Instruction](#instruction)**
+
+- `peekNextWriteSource()`: 查看要写入或跳过的下一条指令的地址
+
+- `skipOne()`: 跳过本来要写入的下一条指令
+ {: #arm64relocator-skipone}
+
+- `writeOne()`: 写入下一条缓冲的指令
+ {: #arm64relocator-writeone}
+
+- `writeAll()`: 写入所有缓冲的指令
+ {: #arm64relocator-writeall}
+
+
+### AArch64 enum types
+
+- Register: `x0` `x1` `x2` `x3` `x4` `x5` `x6` `x7` `x8` `x9` `x10` `x11`
+ `x12` `x13` `x14` `x15` `x16` `x17` `x18` `x19` `x20` `x21` `x22` `x23`
+ `x24` `x25` `x26` `x27` `x28` `x29` `x30` `w0` `w1` `w2` `w3` `w4` `w5`
+ `w6` `w7` `w8` `w9` `w10` `w11` `w12` `w13` `w14` `w15` `w16` `w17` `w18`
+ `w19` `w20` `w21` `w22` `w23` `w24` `w25` `w26` `w27` `w28` `w29` `w30`
+ `sp` `lr` `fp` `wsp` `wzr` `xzr` `nzcv` `ip0` `ip1` `s0` `s1` `s2` `s3`
+ `s4` `s5` `s6` `s7` `s8` `s9` `s10` `s11` `s12` `s13` `s14` `s15` `s16`
+ `s17` `s18` `s19` `s20` `s21` `s22` `s23` `s24` `s25` `s26` `s27` `s28`
+ `s29` `s30` `s31` `d0` `d1` `d2` `d3` `d4` `d5` `d6` `d7` `d8` `d9` `d10`
+ `d11` `d12` `d13` `d14` `d15` `d16` `d17` `d18` `d19` `d20` `d21` `d22`
+ `d23` `d24` `d25` `d26` `d27` `d28` `d29` `d30` `d31` `q0` `q1` `q2` `q3`
+ `q4` `q5` `q6` `q7` `q8` `q9` `q10` `q11` `q12` `q13` `q14` `q15` `q16`
+ `q17` `q18` `q19` `q20` `q21` `q22` `q23` `q24` `q25` `q26` `q27` `q28`
+ `q29` `q30` `q31`
+- ConditionCode: `eq` `ne` `hs` `lo` `mi` `pl` `vs` `vc` `hi` `ls` `ge` `lt`
+ `gt` `le` `al` `nv`
+- IndexMode: `post-adjust` `signed-offset` `pre-adjust`
+
+
+### MipsWriter
+
++ `new MipsWriter(codeAddress[, { pc: ptr('0x1234') }])`: 创建一个新的代码编写器,
+ 用于生成直接写入 `codeAddress` 处内存的 MIPS 机器代码,`codeAddress` 指定为 **[NativePointer](#nativepointer)**。
+ 第二个参数是一个可选的选项对象,可以在其中指定初始程序计数器,这在生成代码到暂存缓冲区时很有用。
+ 这在 iOS 上使用 [`Memory.patchCode()`](#memory-patchcode) 时至关重要,因为它可能会为你提供一个临时位置,
+ 稍后会将其映射到预期内存位置的内存中。
+
+- `reset(codeAddress[, { pc: ptr('0x1234') }])`: 回收实例
+
+- `dispose()`: 立即清理内存
+
+- `flush()`: 解析标签引用并将挂起的数据写入内存。完成代码生成后,你应该始终调用一次。
+ 通常也希望在不相关的代码片段之间执行此操作,例如一次生成多个函数时。
+
+- `base`: 输出的第一个字节的内存位置,作为 **[NativePointer](#nativepointer)**
+
+- `code`: 输出的下一个字节的内存位置,作为 **[NativePointer](#nativepointer)**
+
+- `pc`: 输出的下一个字节处的程序计数器,作为 **[NativePointer](#nativepointer)**
+
+- `offset`: 当前偏移量作为 JavaScript 数字
+
+- `skip(nBytes)`: 跳过 `nBytes`
+
+- `putLabel(id)`: 在当前位置放置一个标签,其中 `id` 是一个可以在过去和将来的 `put*Label()` 调用中引用的字符串
+ {: #mipswriter-putlabel}
+
+- `putCallAddressWithArguments(func, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码,
+ `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或
+ **[NativePointer](#nativepointer)**。
+
+- `putCallRegWithArguments(reg, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码,
+ `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或
+ **[NativePointer](#nativepointer)**。
+
+- `putJAddress(address)`: 放置一个 J 指令
+
+- `putJAddressWithoutNop(address)`: 放置一个 J WITHOUT NOP 指令
+
+- `putJLabel(labelId)`: 放置一个引用 `labelId` 的 J 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#mipswriter-putlabel) 定义
+
+- `putJrReg(reg)`: 放置一个 JR 指令
+
+- `putJalAddress(address)`: 放置一个 JAL 指令
+
+- `putJalrReg(reg)`: 放置一个 JALR 指令
+
+- `putBOffset(offset)`: 放置一个 B 指令
+
+- `putBeqRegRegLabel(rightReg, leftReg, labelId)`: 放置一个引用 `labelId` 的 BEQ 指令,
+ `labelId` 由过去或将来的 [`putLabel()`](#mipswriter-putlabel) 定义
+
+- `putRet()`: 放置一个 RET 指令
+
+- `putLaRegAddress(reg, address)`: 放置一个 LA 指令
+
+- `putLuiRegImm(reg, imm)`: 放置一个 LUI 指令
+
+- `putDsllRegReg(dstReg, srcReg, amount)`: 放置一个 DSLL 指令
+
+- `putOriRegRegImm(rt, rs, imm)`: 放置一个 ORI 指令
+
+- `putLdRegRegOffset(dstReg, srcReg, srcOffset)`: 放置一个 LD 指令
+
+- `putLwRegRegOffset(dstReg, srcReg, srcOffset)`: 放置一个 LW 指令
+
+- `putSwRegRegOffset(srcReg, dstReg, dstOffset)`: 放置一个 SW 指令
+
+- `putMoveRegReg(dstReg, srcReg)`: 放置一个 MOVE 指令
+
+- `putAdduRegRegReg(dstReg, leftReg, rightReg)`: 放置一个 ADDU 指令
+
+- `putAddiRegRegImm(dstReg, leftReg, imm)`: 放置一个 ADDI 指令
+
+- `putAddiRegImm(dstReg, imm)`: 放置一个 ADDI 指令
+
+- `putSubRegRegImm(dstReg, leftReg, imm)`: 放置一个 SUB 指令
+
+- `putPushReg(reg)`: 放置一个 PUSH 指令
+
+- `putPopReg(reg)`: 放置一个 POP 指令
+
+- `putMfhiReg(reg)`: 放置一个 MFHI 指令
+
+- `putMfloReg(reg)`: 放置一个 MFLO 指令
+
+- `putMthiReg(reg)`: 放置一个 MTHI 指令
+
+- `putMtloReg(reg)`: 放置一个 MTLO 指令
+
+- `putNop()`: 放置一个 NOP 指令
+
+- `putBreak()`: 放置一个 BREAK 指令
+
+- `putPrologueTrampoline(reg, address)`: 放置一个最小尺寸的 trampoline 用于跳转到给定地址
+
+- `putInstruction(insn)`: 将原始指令作为 JavaScript 数字放置
+
+- `putBytes(data)`: 放置来自提供的 **[ArrayBuffer](#arraybuffer)** 的原始数据
+
+
+### MipsRelocator
+
++ `new MipsRelocator(inputCode, output)`: 创建一个新的代码重定位器,用于将 MIPS 指令从一个内存位置复制到另一个内存位置,
+ 并注意相应地调整位置相关指令。
+ 源地址由 `inputCode` 指定,这是一个 **[NativePointer](#nativepointer)**。
+ 目的地由 `output` 给出,这是一个指向所需目标内存地址的 **[MipsWriter](#mipswriter)**。
+
+- `reset(inputCode, output)`: 回收实例
+
+- `dispose()`: 立即清理内存
+
+- `input`: 到目前为止读取的最新 **[Instruction](#instruction)**。开始时为 `null`,
+ 并在每次调用 [`readOne()`](#mipsrelocator-readone) 时更改。
+
+- `eob`: 布尔值,指示是否已到达块的末尾,即我们已到达任何类型的分支,如 CALL、JMP、BL、RET。
+
+- `eoi`: 布尔值,指示是否已到达输入的末尾,例如我们已到达 JMP/B/RET,这是一条指令,其后可能有也可能没有有效代码。
+
+- `readOne()`: 将下一条指令读入重定位器的内部缓冲区,并返回到目前为止读取的字节数,包括以前的调用。
+ 你可以继续调用此方法以继续缓冲,或者立即调用 [`writeOne()`](#mipsrelocator-writeone) 或 [`skipOne()`](#mipsrelocator-skipone)。
+ 或者,你可以缓冲直到所需的点,然后调用 [`writeAll()`](#mipsrelocator-writeall)。
+ 当到达输入末尾时返回零,这意味着 `eoi` 属性现在为 `true`。
+ {: #mipsrelocator-readone}
+
+- `peekNextWriteInsn()`: 查看要写入或跳过的下一条 **[Instruction](#instruction)**
+
+- `peekNextWriteSource()`: 查看要写入或跳过的下一条指令的地址
+
+- `skipOne()`: 跳过本来要写入的下一条指令
+ {: #mipsrelocator-skipone}
+
+- `writeOne()`: 写入下一条缓冲的指令
+ {: #mipsrelocator-writeone}
+
+- `writeAll()`: 写入所有缓冲的指令
+ {: #mipsrelocator-writeall}
+
+
+### MIPS enum types
+
+- Register: `v0` `v1` `a0` `a1` `a2` `a3` `t0` `t1` `t2` `t3` `t4` `t5` `t6`
+ `t7` `s0` `s1` `s2` `s3` `s4` `s5` `s6` `s7` `t8` `t9` `k0` `k1` `gp` `sp`
+ `fp` `s8` `ra` `hi` `lo` `zero` `at` `0` `1` `2` `3` `4` `5` `6` `7` `8`
+ `9` `10` `11` `12` `13` `14` `15` `16` `17` `18` `19` `20` `21` `22` `23`
+ `24` `25` `26` `27` `28` `29` `30` `31`
+
+---
+
+## 其他
+
+
+### Console
+
++ `console.log(line)`, `console.warn(line)`, `console.error(line)`:
+ 将 `line` 写入基于 Frida 的应用程序的控制台。确切的行为取决于 [frida-core](https://github.com/frida/frida-core)
+ 集成的位置。
+ 例如,当通过 [frida-python](https://github.com/frida/frida-python) 使用 Frida 时,
+ 此输出将转到 *stdout* 或 *stderr*,当使用 **[frida-qml](https://github.com/frida/frida-qml)** 时,
+ 转到 **[qDebug](https://doc.qt.io/qt-5/qdebug.html)** 等。
+
+ 作为 **[ArrayBuffer](#arraybuffer)** 对象的参数将被替换为具有默认选项的 [`hexdump()`](#hexdump) 的结果。
+
+
+### Hexdump
+
++ `hexdump(target[, options])`: 从提供的 **[ArrayBuffer](#arraybuffer)** 或 [NativePointer](#nativepointer) `target` 生成十六进制转储,
+ 可选地使用 `options` 自定义输出。
+
+ 例如:
+
+{% highlight js %}
+const libc = Process.getModuleByName('libc.so').base;
+console.log(hexdump(libc, {
+ /* address: ptr('0x1000'), -- to override the base address */
+ offset: 0,
+ length: 64,
+ header: true,
+ ansi: true
+}));
+{% endhighlight %}
+
+{% highlight sh %}
+ 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
+00000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 .ELF............
+00000010 03 00 28 00 01 00 00 00 00 00 00 00 34 00 00 00 ..(.........4...
+00000020 34 a8 04 00 00 00 00 05 34 00 20 00 08 00 28 00 4.......4. ...(.
+00000030 1e 00 1d 00 06 00 00 00 34 00 00 00 34 00 00 00 ........4...4...
+{% endhighlight %}
+
+
+### Shorthand
+
++ `int64(v)`: [`new Int64(v)`](#int64) 的简写
+
++ `uint64(v)`: [`new UInt64(v)`](#uint64) 的简写
+
++ `ptr(s)`: [`new NativePointer(s)`](#nativepointer) 的简写
+
++ `NULL`: `ptr("0")` 的简写
+
+
+### 主机和注入进程之间的通信
+
++ `recv([type, ]callback)`: 请求在从基于 Frida 的应用程序收到下一条消息时调用 `callback`。
+ 可选地,可以指定 `type` 以仅接收 `type` 字段设置为 `type` 的消息。
+ {: #communication-recv}
+
+ 消息作为第一个参数传递,如果随消息传递了二进制数据,则第二个参数是 **[ArrayBuffer](#arraybuffer)**,否则为 *null*。
+
+ 这只会给你一条消息,所以你需要再次调用 `recv()` 来接收下一条消息。
+
++ `send(message[, data])`: 将 JavaScript 对象 `message` 发送到基于 Frida 的应用程序(它必须可序列化为 JSON)。
+ 如果你还有一些原始二进制数据想随之发送,例如你使用 [`NativePointer#readByteArray`](#nativepointer-readbytearray)
+ 转储了一些内存,那么你可以通过可选的 `data` 参数传递它。这要求它要么是 **[ArrayBuffer](#arraybuffer)**,
+ 要么是 0 到 255 之间的整数数组。
+ {: #communication-send}
+
+ + 虽然 send() 是异步的,但发送单个消息的总开销并未针对高频进行优化, + 因此这意味着 Frida 让你根据需要低延迟还是高吞吐量,自行决定将多个值批处理到单个 send() 调用中。 +
++ recv() 方法本身是异步的(非阻塞)。注册的回调(onMessage)将恰好接收一条消息。要接收下一条消息,必须使用 recv() 重新注册回调。 +
+