首页
社区
课程
招聘
[原创]frida-gum 源码解读
发表于: 2025-11-5 20:01 559

[原创]frida-gum 源码解读

2025-11-5 20:01
559

学习 frida 源码,先从 test 案例入口开始分析,以 https://bbs.kanxue.com/thread-278423.htm 作为指导进行学习,有理解不对的地方欢迎指正,谢谢。

测试文件 frida\subprojects\frida-gum\tests\core\arch-arm64\interceptor-arm64.c

你看到的这段代码是一个 单元测试(Unit Test)

目标模块: Interceptor (拦截器),这是 Frida 用来实现 Inline Hook 的核心组件。

目标架构: interceptor_arm64,这特指是为 ARM64 架构(目前绝大多数 64 位智能手机的 CPU 架构)编写的测试。

核心概念:lr 寄存器

测试的真正目的:一个棘手的边界情况

这部分很简单,只是一个目录,告诉测试框架:“嘿,我们要跑这两个测试。”

这个测试用例测试的是:Hook 一个“会读取 lr 寄存器”的*普通函数*

这个测试用例测试的是:Hook 一个“会读取 lr 寄存器”的*Thunk 函数*

这两个测试非常精彩,它们共同验证了 Interceptor 的一个核心能力:

这就是 Frida 强大的 稳定性(Robustness) 的体现。它不仅能 Hook 函数,还能在 Hook 的同时,保证原函数的(尤其是依赖 lr 这种易变状态的)逻辑不被破坏。

下面从“它做了什么” (What it does) 转向了“它是怎么做的” (How it does it)。

看上面两个 TESTCASE 中的公共函数 interceptor_fixture_attach 是 Frida 测试框架中用于“挂钩” (Hook) 的辅助函数 (Helper Functions)。让我们来解剖它们。

interceptor_fixture_attach 是一个 “必须成功”的包装器

它存在的意义就是为了让 TESTCASE 里的代码更简洁。在测试中,我们 期望 Hook 总是成功的。所以我们用这个函数:它去尝试 Hook,如果失败了,就直接让测试失败。如果成功了,它就安静地返回。

这是真正的主菜。这个函数负责 创建并注册一个监听器 (Listener),然后把它附加 (attach) 到目标函数上。

我们把测试需要的数据存放到“工具箱” ctx 中。

ctx->fixture = h;:把测试“固定装置”h 存进去。

ctx->enter_char = enter_char;:把 '>' 存进去。

ctx->leave_char = leave_char;:把 '<' 存进去。

回顾一下 Hooked 函数调用 的工作流:

目标函数被调用,Hook 触发。

Frida 调用 on_enter(即 arm64_listener_context_on_enter)。

Frida 把 user_data(即 ctx)传给 on_enter

on_enter 函数内部:

(1). 通过 ctx 拿到 ctx->fixture(即 h)。
(2). 通过 ctx 拿到 ctx->enter_char(即 '>')。
(3). 把 '>' 追加到 h->result->str 字符串的末尾。

Frida 调用原始的目标函数(ctx.run() 内部的原始逻辑)。

原始函数执行完毕,准备返回。Frida 在它真正返回 之前,调用 on_leave (即 arm64_listener_context_on_leave)。

on_leave 函数内部:

(1). 通过 ctx 拿到 ctx->fixture(即 h)。
(2). 通过 ctx 拿到 ctx->leave_char(即 '<')。
(3) 把 '<' 追加到 h->result->str 字符串的末尾。

(此时,h->result->str 的内容变成了 "><")

on_leave 返回,Frida 将原始函数的返回值(如果有的话)交还给调用者。

这就是 fixture->result->str == "><" 这个断言的由来!

通过分析这两个函数,你实际上已经学通了 Frida Interceptor (C API 层面) 的标准工作流程:

分配上下文 (Context):创建一个结构体(如 Arm64ListenerContext)来存放你需要的所有信息(回调函数、用户数据等)。

创建监听器 (Listener):创建一个 GumInvocationListener 对象。

配置回调 (Callbacks):设置 listener->on_enterlistener->on_leave 指向你自己的 C 函数。

设置 user_data:把你的 上下文 (Context) 地址赋值给 listener->user_data。这是连接回调函数和你的数据的桥梁。

调用 gum_interceptor_attach:传入目标函数和配置好的监听器,执行 Hook。

处理结果

可以注意到,其中 on_enteron_leave 是可以由用户自行重载的。然后再从 gum_interceptor_attach 进入,该函数包括了布置 hook 并启动 hook 的任务:

interceptor_fixture_try_attach 只是一个测试辅助函数,而你现在看到的 gum_interceptor_attach 则是 Frida Interceptor 模块的公开 C API 核心。所有语言(JavaScript, Python, Swift...)的 Interceptor.attach() 最终都会调用到这个 C 函数。

让我们来一步步解剖这个“引擎”。

这个函数的 真正工作 可以分为两个阶段:

这是在执行“高危操作”(修改正在运行的程序的代码)之前的标准 安全措施

精妙的技巧! 这行代码告诉 Interceptor:“嘿,接下来我(指当前这个线程)要执行一些内部操作了。如果我不小心调用了 其他 已经被 Hook 的函数,请不要 触发它们的 onEnter/onLeave 回调。”

为什么这么做?

在 Frida Hook 框架中,当我们对一个函数(如 open, recv, malloc)安装拦截(intercept)后,所有线程执行该函数时都会跳转到我们注入的 trampoline / replacement 函数。但有时,我们自己当前线程的代码 也会调用被 Hook 的函数(比如 Hook malloc 时,我们自己的日志模块也会用到 malloc),

这就可能导致:

当前线程陷入递归 hook → 死循环 → 崩溃。

为了避免这种情况,Frida 提供了:

最后,我们来看看那些 goto 语句。在 C 语言中,goto 经常被(正确地)用于 集中的错误处理和资源释放。这是一种非常标准的模式。

gum_interceptor_attach 是一个 非常健壮 的函数:

Inline Hook(也称内联钩子)是一种低级的函数钩子技术,它通过直接修改目标函数的机器码(通常是函数开头的几条指令)来实现钩子。具体来说,它会用跳转指令(如 jmp)替换函数的起始代码,将执行流重定向到自定义的钩子函数中,执行完钩子后再跳回原函数继续运行。这种方式效率高,但实现复杂,需要处理架构差异(如 ARM、x86)和指令对齐问题。

Inline Hook 可以看作是 Frida 等工具可能采用的一种底层技术,但 Frida 提供了更全面的生态和易用性。如果你是在开发钩子工具,Frida 更推荐入门;如果追求低级控制,则需掌握 Inline Hook。

当一个被 Hook 的函数(TargetFunction)被调用时,它首先会跳转(JUMP)到 Frida 的“总调度中心”(Dispatcher)。这个Dispatcher会先保存现场,然后循环调用所有已注册的 onEnter 监听器(Listener)。接着,它会通过“跳板机”(Trampoline)来执行被覆盖的几条原始指令,并跳回到原函数的“身体”部分继续执行。当原函数执行完毕返回时,Dispatcher会再次捕获返回,循环调用所有 onLeave 监听器,最后才恢复现场,将控制权和(可能被修改过的)返回值交还给最初的调用者。

关于 lr 寄存器、Thunk、Trampoline 的 所有 汇编层面的“脏活累活”,几乎都发生在这个函数调用的深处

gum_interceptor_attach (我们上一步看的) 只是一个“前台接待”,它负责处理锁、事务和 Listener 列表。

gum_interceptor_instrument 则是“后台的建筑工头”。它的工作是:“去,给我把 function_address 这个地方的‘舞台’(function_ctx)建好!”

让我们逐段解剖这个“工头”的施工流程。

这是 整个 Interceptor 中最重要的优化

self->function_by_address 是一个 哈希表(HashTable),你可以把它想象成一个“全局登记册”。

g_hash_table_lookup(..., function_address) 的意思是:“去登记册里查一下,我们是不是已经为 function_address 这个地址建过‘舞台’(function_ctx)了?”

if (ctx != NULL)

查到了! 这意味着这是对该函数的 第二次(或第 N 次)attach

“工头”一看:“哦,舞台(ctx)已经在了。”

它什么也不用做,直接返回之前建好的那个 ctx

这就是为什么我们说“插桩 (Instrument) 只会发生一次”!

if (ctx->type != type)

type (类型) 这个参数,代表你 这次 想用 哪种方式 来 Hook 这个函数。

这个 if (ctx->type != type) 检查就是在说:

“OK,我发现这个函数(function_address已经被 Hook 过了ctx != NULL)。

“我检查一下当初 Hook 它的类型(ctx->type),是不是和你 这次 想用的类型(type)一样?

如果不一样(比如你之前用 attach Hook 了它,现在又想用 replace 去 Hook 它),那对不起,这两种操作是 互斥 的!我不能让你这么做。”

这是一个 安全锁,它确保你 不能对同一个函数混用不同类型的 Hook(比如在 attach 之后又去 replace 它)。如果 Frida 发现你试图这么做,它会返回 GUM_INSTRUMENTATION_ERROR_WRONG_TYPE(类型错误)来拒绝这次操作。

if (ctx == NULL)

_gum_interceptor_backend_create 关键函数

这是 最核心的施工步骤

gum_interceptor_instrument 是 Interceptor 的“建築工頭”,它是一個 效率與安全並重 的函數,其核心職責是 “建造”(插樁)一個函數的“舞台”(ctx),並確保這個建造過程只發生一次。

先看【Interceptor的核心——gum_interceptor_instrument 】代码中的第 2 阶段:【准备施工队】—— “我的汇编工具在哪?”的_gum_interceptor_backend_create 函数

因此我们跟到 _gum_interceptor_backend_create 里看看它是如何实现的。该函数是平台相关的具体函数,由于打算分析 arm64 下的实现,因此这里的源代码应为 frida-gum/gum/backend-arm64/guminterceptor-arm64.c

你現在看到的 _gum_interceptor_backend_create 函數,就是 gum_interceptor_instrument (建築工頭) 在找不到“施工隊”(self->backend == NULL)時,調用的“HR/工具房管理員”。

它的職責是:創建並初始化一個特定於 CPU 架構(在這裡是 ARM64)的 backend(“施工隊”),並為其配備所有必需的工具。

_gum_interceptor_backend_create 是 Interceptor 的“军械库/工具房”。

gum_interceptor_backend_create_thunks 及其调用的所有函数,其唯一目的就是预先 JIT(即时编译) (创建)两个可重用的汇编代码块enter_thunkleave_thunk

这正是我们之前所说的“性能优化”。Frida 无需在 每次 attach 时都重新生成这些通用的“桥接”代码,而是在启动时就将它们一次性造好。

让我们像搭乐高一样,从最小的零件(enter/leave thunk)开始,逐步拼装出完整的逻辑。

从底层向上层看代码

这是“进入 Thunk”(enter_thunk)的汇编代码 生成器

enter_thunk 是一个标准化的汇编“零件”,它是在 Dispatcher(总调度中心)内部被调用的。它的工作是 从纯汇编的世界“桥接”到 C 语言的世界,去 调用那个真正执行 onEnter 循环的 C 函数

这是“离开 Thunk”(leave_thunk)的汇编代码 生成器

它几乎和 enter_thunk 一模一样,但它调用的是 C 函数 _gum_function_context_end_invocation

这是“Thunk 布局引擎”。它调用上面的两个“生成器”,把 enter_thunkleave_thunk 一前一后 地写(JIT, 即时编译)到一块内存里。

我们来详细分解 gum_emit_thunks 这个“布局引擎”的工作流程:

总结一下:

“一前一后”的实现非常简单:

这就好像 用笔在纸上写字,enter_thunk 写完了不换行,紧接着写 leave_thunk

这是“Thunk 生产线经理”。它负责申请内存、“隐藏”内存,并启动“布局引擎”(gum_emit_thunks)。

gum_interceptor_backend_create_thunks 是一个 性能优化。它在 Interceptor 启动时,预先 JIT 编译 了两个高度可重用的汇编“零件”:

Dispatcher(总调度中心)在 JIT 编译时,就不再需要自己生成这些复杂的“调用 C”的代码了,它只需要 JIT 编译两条简单的 BL(跳转)指令,分别跳转到这两个 已经预制好thunk 地址即可。这就是“搭积木”的原理。

这个函数是真正执行 onEnter 逻辑的地方。它在纯 C 语言环境中运行,负责处理所有复杂的逻辑:比如递归保护、线程过滤、调用 onEnter 循环,以及决定下一步该做什么(是去执行原函数?还是去执行替换函数?)。

让我们来详细拆解这个“大脑”的思维过程。

_gum_function_context_begin_invocation

_gum_function_context_end_invocation

在深入代码之前,我们必须先理解它的参数,尤其是最后两个,它们是 “出参”(out-parameters),这个函数通过 修改它们告诉 外层(即 enter_thunk 汇编代码)接下来该跳到哪里去。

问题:之前的【第 4 部分(最上层):gum_interceptor_backend_create_thunks】中的gum_cloak_add_range 已经“隐藏”了内存,避免无限递归,为什么还要做下面的保护措施呢?

gum_cloak_add_range 确实是“隐藏”了内存,但让我们搞清楚它们各自的 保护对象 是谁。

_gum_function_context_begin_invocation 里面的两个“保镖”(gum_tls_key_get_valuecalling_replacement不是 用来防 Stalker 的。

你设想的场景(onEnter(A) 调用 function_B)还不是最直接的递归。

让我们用一个更常见、更危险的真实场景来解释:onEnter 内部调用了 console.log

想象一下,console.log 最终会调用底层的 write 函数来向屏幕输出。

你,作为用户,Hook 了底层的 write 函数:

JavaScript

现在,你的程序调用了 write(比如,printf("Hello"))。

下面是“无限递归”的开始(假设没有“守卫”):

现在,我们看看 有了“守卫”,Frida 是如何优雅地处理这个灾难的:

“守卫”(gum_tls_key_get_value)的用途是:防止一个 onEnter 回调(或 replace 实现)不小心调用了 另一个(甚至是 同一个 )被 Hook 的函数,从而导致 Interceptor 逻辑“重入”(Re-entrancy)而引发无限递归。

它就像一个“请勿打扰”的门牌:Frida 在进入 C 语言总控室时挂上门牌,如果它在挂着门牌时又有人来敲门(递归调用),它就知道这是“自己人”在办事,于是直接让他们“走旁门”(goto bypass)去执行原始函数,而不是再走一遍“总控室”逻辑。

我们来详细解释一下这个“干扰”是怎么发生的。

errno (在 Windows 上是 GetLastError())并不是一个普通的变量,它是一个 保存在线程局部存储(TLS)中的“最后错误码”

C 语言中很多函数的错误处理都依赖它。一个非常经典的“错误”流程是这样的:

现在,假设我们 Hook 了另一个函数,比如 write,而程序是这样执行的:

结果就是: 你的 onEnter 回调(通过 console.log)意外地““治愈”了”原始程序的错误状态,导致原始程序的错误处理逻辑(if (errno == 2) { ... }失效 了,从而引发了 Bug。

逻辑: 这一长串 if 语句在做一个决定:gboolean invoke_listeners = ?(我们到底要不要运行 onEnter?)

检查 1: selected_thread_id。你是否 attach 时只指定了某个特定线程?如果是,我们现在是那个线程吗?

检查 2: ignore_levelgum_interceptor_ignore_current_thread()attach 里的那个)就是增加了这个 ignore_level。如果 > 0,就 调用。

invoke_listeners 目前是 true

interceptor_ctx->ignore_level 是什么?

(interceptor_ctx->ignore_level <= 0) 的判断

这行代码是 ignoreCurrentThread() 功能的“执行者”。它确保了:

这和我们之前讨论的 “递归保镖”gum_tls_key_get_value)是两个 不同 的机制:

“递归保镖”gum_tls_key_get_value):是 自动的,防止 onEnter 调用 console.log 这种 意外 的无限递归。

“忽略计数器”ignore_level):是 手动的,让你(用户)可以 主动地 在一段时间内关闭当前线程的所有 Hook。

检查 3: unignorable_listener(不可忽略的 Listener)。这是一个例外:即使上面两项检查都说“不要调用”,但如果存在一个“不可忽略”的 Listener,我们 必须 调用它。

逻辑: 这是在为 onLeave 做准备。

will_trap_on_leave = ?(我们需要 onLeave 吗?)

如果这是一个 replace 操作(replacement_function != NULL),

如果我们要调用 Listener(invoke_listeners并且 它们中有任何一个注册了 onLeave

否则,

解释说明

这行代码是在决定:“Frida 是否需要在目标函数执行完毕、即将返回时,再次‘劫持’控制权?

will_trap_on_leave 是一个布尔值(truefalse),它的意思是“是否要在离开时设置陷阱?

所以,Frida 必须在 onEnter 阶段就决定是否需要 onLeave。它只在以下两种情况下才 必须||,或)去劫持返回:

if (will_trap_on_leave)(如果需要 onLeave):

else if (invoke_listeners)(如果只需要 onEnter,不需要 onLeave):

gum_function_context_fixup_cpu_context(...)

if (invoke_listeners)

invocation_ctx->cpu_context = cpu_context;

关键! 这行代码把“存包小票”(invocation_ctx)和“CPU 状态”(cpu_context)关联 起来。

invocation_ctx 的真正意思是“调用上下文”(Invo cation C on t e x t),它是一个 “句柄” (Handle),代表了 “这一次” 函数调用。

想象一下你去一个需要“存包”和“取包”的地方(比如游乐园):

现在,当 JS 回调中执行 this.context.x0 = 0x123 时,它修改的就是 cpu_context->x0,也就是 堆栈上真实的 X0

for (i = 0; ...)

这就是 循环,遍历所有 listener

listener_entry->listener_interface->on_enter(...)

执行! 这就是调用 C 回调(arm64_listener_context_on_enter)的地方,C 回调再去调用 JS 的 onEnter

我们最开始分析的测试代码 interceptor_fixture_try_attach ,在那里面做了这件事:

现在,for 循环里的 listener_entry->listener_interface->on_enter(...) 调用的,正是我们当初注册的回调函数 arm64_listener_context_on_enter

从而达成 完整的调用链

_gum_function_context_begin_invocation 是 Interceptor 的 “C 语言总控室”

它在 enter_thunk(汇编桥梁)之后、onEnter(JS 回调)之前运行。它负责:

通过 gum_tls_key_get_value 防止无限递归

通过 selected_thread_idignore_level 过滤 掉不应触发的调用。

通过 gum_invocation_stack_push 保存 onLeave 所需的状态(“存包”)。

通过 invocation_ctx->cpu_context = cpu_context 将 JS 的 this.context 链接 到真实的 CPU 寄存器,从而可以修改读取寄存器值。

循环调用 所有 onEnter 回调。

决定 enter_thunk“下一跳”*next_hop):是去原函数还是替换函数?

劫持 CPU 的 “返回地址”*caller_ret_addr):是返回给原调用者,还是返回给 on_leave_trampoline

在以下两种情况下,will_trap_on_leave 会为 true,Frida 覆写返回地址:

在这两种情况下,Frida 都 必须 在函数返回时重新获得控制权(去执行 onLeave 循环或清理堆栈),因此它 必须 劫持返回地址。

返回给“原调用者”

在以下情况下,will_trap_on_leave 会为 false,Frida 不会 触碰返回地址:

在这种情况下,Frida 知道 onLeave 阶段无事可做,它会选择性能最高的路径:在 onEnter 执行完毕后,让原函数直接 RET(返回)给它的“原调用者”,Frida 不再介入。

分析完onEnter,我们再看onLeave: 第2部分gum_emit_leave_thunk(我们之前分析的), 调用的是这个 _gum_function_context_end_invocation C 函数。

gum_emit_leave_thunk(汇编桥梁)的工作就是调用这个 C 函数。

这个函数是 “C 语言总控室”的下半场onEnter 阶段(begin_invocation)负责了 “存包”、决策、和“劫持”返回地址。而这个 end_invocation 函数(onLeave 阶段)则负责:“取包”、执行 onLeave 循环、清理、并最终“放行”。

_gum_function_context_end_invocationInterceptor 的“C 语言收尾室”

它在 on_leave_trampoline 劫持了函数返回后被调用。它负责:

现在我在返回【Interceptor的核心——gum_interceptor_instrument 】的代码中去分析 第 3 阶段:【施工!】—— “开工!建 Trampoline!”中的 _gum_interceptor_backend_create_trampoline 函数

我们终于抵达了 Interceptor 的 “施工现场”

gum_interceptor_instrument(“工头”)在检查了“缓存”后,如果发现是第一次 Hook,就会调用这个 _gum_interceptor_backend_create_trampoline 函数。

这就是 “施工队” (backend) 实际在“建造” Trampoline(跳板机)和 Dispatcher(总调度中心)的地方

我们最初关于 lr 寄存器的所有问题,都将在这个函数里找到最终的答案。

让我们开始“施工”!

gum_interceptor_backend_prepare_trampoline(...)

gum_arm64_writer_reset (aw, ctx->trampoline_slice->data);

ctx->on_enter_trampoline = gum_sign_code_pointer (gum_arm64_writer_cur (aw));

这行代码的 核心目的 是:JIT 编译(即时编译)Dispatcher(总调度中心)的入口点,对其进行“安全签名”,然后把这个入口地址“登记”到“施工蓝图” (ctx) 中。

让我们从右到左,一步一步把它分解开:

gum_arm64_writer_cur (aw)

gum_sign_code_pointer ( ... )

ctx->on_enter_trampoline = ...

这就是 Dispatcher 的“JIT 编译”过程!

onEnter 部分:

LDR X17, [ctx_addr]:JIT 一条指令,把“舞台” (ctx) 的地址加载到 X17 寄存器。(然后回顾之前分析的 第 1 部分(最底层):gum_emit_enter_thunk代码中 X17enter_thunk 中被用作第一个参数)。

LDR X16, [enter_thunk_addr]:JIT 一条指令,把我们 预先造好enter_thunk(还记得吗?那个 C 语言桥梁)的地址加载到 X16

BR X16:JIT 一条指令,JUMPenter_thunk

实际调试演示

特征检测相关文章

onLeave 部分:

ctx->on_leave_trampoline = ...“标记 onLeave 的入口!”

下面 JIT 了 一模一样 的代码,只是它加载的是 leave_thunk 的地址。(然后回顾之前分析的 第 2 部分(最底层):gum_emit_leave_thunk代码中 X17leave_thunk 中被用作第一个参数)。

至此,“总调度中心” (Dispatcher) 建造完毕。 它包含两个部分,分别用于 onEnteronLeave

这就是我们所有分析的“风暴中心”! 这就是对你最初 lr 测试用例的 生产环境 解决方案。

is_eligible_for_lr_rewriting = ...

if (is_eligible_for_lr_rewriting)(如果启用):

关于 Interceptor 如何(以及何时)决定修复 LR(链接寄存器)读取的核心逻辑。

问题 1:“矛盾”:为什么比较的长度不同?(8 字节 vs 16 字节)

答案: 因为 Frida “偷”的字节数(data->redirect_code_size)是 动态的,而不是固定的 8 字节。

第 1 阶段:准备“施工地”和“入口”代码中的 gum_interceptor_backend_prepare_trampoline(“预分析”函数)会智能地分析目标函数,来决定“安全偷取”的最小字节数(决定是“偷”4 字节 or 8 字节 or 16 字节 等等):

结论: 8 字节和 16 字节并不矛盾。它们是 Frida 为了 两种不同情况 而设置的 两种不同长度 的“高风险”签名。

问题 2:为什么是这两个“签名”?(目的)

答案: 这是一个 “启发式检查”(Heuristic Check),是 Frida 最重要的 性能优化 之一。

Frida 不会每个 函数上都执行昂贵的 LR 修复(即逐条检查指令的 while 循环),因为太慢了。相反,它使用这两个“签名”作为 “高风险特征码”(Red Flags)“快速筛选”

总结: 这行代码的 唯一目的,就是 “解锁” 那个昂贵的 LR 修复逻辑(while 循环),只对“高风险分子”进行逐条扫描。

问题 3:假如“被偷”的指令中没有读取 LR,但是后续(函数“身体”里)有读取 LR,还会修复吗?

答案:会!但是通过*另一种*机制。

Frida 有 两套 LR 修复方案,互为补充:

结论:

问题 4:开头‘长得’像 "stp;mov;mov;bl" 的函数,第二个 mov 是不是就是 MOV Xn, LR

答案:不是的。

这是一个非常常见的、也是非常合理的推测,但答案是否定的。

“签名” (signature->str) 是一个“傻瓜式”检查。

这个“签名” ("stp;mov;mov;bl") 只是一个“高风险特征码”。

打个比方:

while 循环peek_next_write_insn)才是真正的“搜身”(Detailed Search)。它逐个检查那 4 条“被偷”的指令的**“操作数”,去真正地寻找**那把“武器”(MOV Xn, LR)。

总结: "stp;mov;mov;bl" 里的 mov 不是 MOV Xn, LR。它只是一个“外貌特征”,这个“外貌”足以**“解锁”**那个真正会去检查 LRwhile 循环。

问题 5:“解锁”那个昂贵的 while 循环,去逐条扫描,扫描多少字节?

答案:它扫描的不是“字节”,而是“被偷走的指令列表”。

while 循环(while ((insn = gum_arm64_relocator_peek_next_write_insn (ar)) != NULL)不会再去内存中读取新的字节。

它扫描(遍历)的是已经被 do...while 循环“偷走”并暂存在 ar(“搬运工”)对象中的那个指令列表

所以,这个 while 循环的迭代次数是固定的,它等于“被偷走”的指令数量:

结论: 这个昂贵的 while 循环,它的扫描范围仅限于do...while 循环(为了腾出空间而)“偷走”的那几条指令。它不会扫描函数“身体”的其余部分(这就是为什么我们还需要“运行时修复” gum_function_context_fixup_cpu_context)。

_gum_interceptor_backend_create_trampoline 是 Interceptor 中 最复杂 的 JIT 编译函数(“施工现场”)。

Stalker 的主要目标是跟踪一个线程的执行流(Code Execution Tracing),而不是监控内存访问。

它的大致原理是这样的:

Stalker 是一种非常精细的执行跟踪技术,它让您几乎可以在每条指令(或每个基本块)执行前后“挂钩”(hook),从而实现代码覆盖率分析、路径跟踪等高级功能。

TESTLIST_BEGIN (interceptor_arm64)
  TESTENTRY (attach_to_thunk_reading_lr)
  TESTENTRY (attach_to_function_reading_lr)
TESTLIST_END ()
TESTLIST_BEGIN (interceptor_arm64)
  TESTENTRY (attach_to_thunk_reading_lr)
  TESTENTRY (attach_to_function_reading_lr)
TESTLIST_END ()
TESTCASE (attach_to_function_reading_lr)
{
  // 1. 准备工作:分配内存
  const gsize code_size_in_pages = 1;
  gsize code_size;
  GumEmitLrFuncContext ctx; // 'ctx' 是一个结构体,像个工具箱,存放这次测试需要的所有变量
 
  code_size = code_size_in_pages * gum_query_page_size (); // 计算1个内存页到底是多少字节
  ctx.code = gum_alloc_n_pages (code_size_in_pages, GUM_PAGE_RW); // 分配1页可读(R)可写(W)的内存
 
  // 2. 初始化工具箱
  ctx.run = NULL;     // C语言的函数指针,指向我们要测试的函数
  ctx.func = NULL;    // 原始函数指针
  ctx.caller_lr = 0;  // 用来存放“期望的”lr值
 
  // 3. 动态生成测试函数(最关键的步骤之一)
  gum_memory_patch_code (ctx.code, code_size, gum_emit_lr_func, &ctx);
TESTCASE (attach_to_function_reading_lr)
{
  // 1. 准备工作:分配内存
  const gsize code_size_in_pages = 1;
  gsize code_size;
  GumEmitLrFuncContext ctx; // 'ctx' 是一个结构体,像个工具箱,存放这次测试需要的所有变量
 
  code_size = code_size_in_pages * gum_query_page_size (); // 计算1个内存页到底是多少字节
  ctx.code = gum_alloc_n_pages (code_size_in_pages, GUM_PAGE_RW); // 分配1页可读(R)可写(W)的内存
 
  // 2. 初始化工具箱
  ctx.run = NULL;     // C语言的函数指针,指向我们要测试的函数
  ctx.func = NULL;    // 原始函数指针
  ctx.caller_lr = 0;  // 用来存放“期望的”lr值
 
  // 3. 动态生成测试函数(最关键的步骤之一)
  gum_memory_patch_code (ctx.code, code_size, gum_emit_lr_func, &ctx);
// 4. 【测试点A】: 验证函数在 Hook 前是否正常
g_assert_cmphex (ctx.run (), ==, ctx.caller_lr);
// 4. 【测试点A】: 验证函数在 Hook 前是否正常
g_assert_cmphex (ctx.run (), ==, ctx.caller_lr);
// 5. 【核心操作】: Attach!开始 Hook
interceptor_fixture_attach (fixture, 0, ctx.func, '>', '<');
// 5. 【核心操作】: Attach!开始 Hook
interceptor_fixture_attach (fixture, 0, ctx.func, '>', '<');
// 6. 【测试点B】: 验证 Hook 后的行为
g_assert_cmphex (ctx.run (), !=, ctx.caller_lr);
g_assert_cmpstr (fixture->result->str, ==, "><");
// 6. 【测试点B】: 验证 Hook 后的行为
g_assert_cmphex (ctx.run (), !=, ctx.caller_lr);
g_assert_cmpstr (fixture->result->str, ==, "><");
  // 7. 清理工作
  interceptor_fixture_detach (fixture, 0); // 卸载 Hook
  gum_free_pages (ctx.code);               // 释放内存
}
  // 7. 清理工作
  interceptor_fixture_detach (fixture, 0); // 卸载 Hook
  gum_free_pages (ctx.code);               // 释放内存
}
TESTCASE (attach_to_thunk_reading_lr)
{
  // 1. 准备工作 (和上一个测试完全一样)
  const gsize code_size_in_pages = 1;
  gsize code_size;
  GumEmitLrThunkContext ctx; // 这次用的是 Thunk 的“工具箱”
 
  code_size = code_size_in_pages * gum_query_page_size ();
  ctx.code = gum_alloc_n_pages (code_size_in_pages, GUM_PAGE_RW);
  ctx.run = NULL;
  ctx.thunk = NULL;
  ctx.expected_lr = 0;
 
  // 2. 动态生成 Thunk 函数
  gum_memory_patch_code (ctx.code, code_size, gum_emit_lr_thunk, &ctx);
TESTCASE (attach_to_thunk_reading_lr)
{
  // 1. 准备工作 (和上一个测试完全一样)
  const gsize code_size_in_pages = 1;
  gsize code_size;
  GumEmitLrThunkContext ctx; // 这次用的是 Thunk 的“工具箱”
 
  code_size = code_size_in_pages * gum_query_page_size ();
  ctx.code = gum_alloc_n_pages (code_size_in_pages, GUM_PAGE_RW);
  ctx.run = NULL;
  ctx.thunk = NULL;
  ctx.expected_lr = 0;
 
  // 2. 动态生成 Thunk 函数
  gum_memory_patch_code (ctx.code, code_size, gum_emit_lr_thunk, &ctx);
// 3. 【测试点A】: 验证 Thunk 在 Hook 前是否正常
g_assert_cmphex (ctx.run (), ==, ctx.expected_lr);
// 3. 【测试点A】: 验证 Thunk 在 Hook 前是否正常
g_assert_cmphex (ctx.run (), ==, ctx.expected_lr);
// 4. 【核心操作】: Attach!Hook 这个 Thunk
interceptor_fixture_attach (fixture, 0, ctx.thunk, '>', '<');
// 4. 【核心操作】: Attach!Hook 这个 Thunk
interceptor_fixture_attach (fixture, 0, ctx.thunk, '>', '<');
// 5. 【测试点B】: 验证 Hook 后的行为(这是最关键的区别!)
g_assert_cmphex (ctx.run (), ==, ctx.expected_lr);
g_assert_cmpstr (fixture->result->str, ==, "><");
// 5. 【测试点B】: 验证 Hook 后的行为(这是最关键的区别!)
g_assert_cmphex (ctx.run (), ==, ctx.expected_lr);
g_assert_cmpstr (fixture->result->str, ==, "><");
  // 6. 清理工作
  interceptor_fixture_detach (fixture, 0);
  gum_free_pages (ctx.code);
}
  // 6. 清理工作
  interceptor_fixture_detach (fixture, 0);
  gum_free_pages (ctx.code);
}
static void
interceptor_fixture_attach (InterceptorFixture * h,
                            guint listener_index,
                            gpointer test_func,
                            gchar enter_char,
                            gchar leave_char)
{
  g_assert_cmpint (interceptor_fixture_try_attach (h, listener_index, test_func,
      enter_char, leave_char), ==, GUM_ATTACH_OK);
}
static void
interceptor_fixture_attach (InterceptorFixture * h,
                            guint listener_index,
                            gpointer test_func,
                            gchar enter_char,
                            gchar leave_char)
{
  g_assert_cmpint (interceptor_fixture_try_attach (h, listener_index, test_func,
      enter_char, leave_char), ==, GUM_ATTACH_OK);
}
static GumAttachReturn
interceptor_fixture_try_attach (InterceptorFixture * h,
                                guint listener_index,
                                gpointer test_func,
                                gchar enter_char,
                                gchar leave_char)
{
  GumAttachReturn result;
  Arm64ListenerContext * ctx;
static GumAttachReturn
interceptor_fixture_try_attach (InterceptorFixture * h,
                                guint listener_index,
                                gpointer test_func,
                                gchar enter_char,
                                gchar leave_char)
{
  GumAttachReturn result;
  Arm64ListenerContext * ctx;
// --- 步骤 1: 清理可能存在的旧监听器 ---
ctx = h->listener_context[listener_index];
if (ctx != NULL)
{
  arm64_listener_context_free (ctx);
  h->listener_context[listener_index] = NULL;
}
// --- 步骤 1: 清理可能存在的旧监听器 ---
ctx = h->listener_context[listener_index];
if (ctx != NULL)
{
  arm64_listener_context_free (ctx);
  h->listener_context[listener_index] = NULL;
}
// --- 步骤 2: 创建新的监听器上下文 (Context) ---
ctx = g_slice_new0 (Arm64ListenerContext);
// --- 步骤 2: 创建新的监听器上下文 (Context) ---
ctx = g_slice_new0 (Arm64ListenerContext);
// --- 步骤 3: 配置监听器 (Listener) ---
ctx->listener = test_callback_listener_new ();
ctx->listener->on_enter =(TestCallbackListenerFunc) arm64_listener_context_on_enter;
ctx->listener->on_leave =(TestCallbackListenerFunc) arm64_listener_context_on_leave;
ctx->listener->user_data = ctx;
// --- 步骤 3: 配置监听器 (Listener) ---
ctx->listener = test_callback_listener_new ();
ctx->listener->on_enter =(TestCallbackListenerFunc) arm64_listener_context_on_enter;
ctx->listener->on_leave =(TestCallbackListenerFunc) arm64_listener_context_on_leave;
ctx->listener->user_data = ctx;
// --- 步骤 4: 填充我们自己的上下文信息 ---
ctx->fixture = h;
ctx->enter_char = enter_char;
ctx->leave_char = leave_char;
// --- 步骤 4: 填充我们自己的上下文信息 ---
ctx->fixture = h;
ctx->enter_char = enter_char;
ctx->leave_char = leave_char;
// --- 步骤 5: 执行真正的附加 (Attach) 操作 ---
result = gum_interceptor_attach (h->interceptor, test_func,
    GUM_INVOCATION_LISTENER (ctx->listener), NULL,
    GUM_ATTACH_FLAGS_NONE);
// --- 步骤 5: 执行真正的附加 (Attach) 操作 ---
result = gum_interceptor_attach (h->interceptor, test_func,
    GUM_INVOCATION_LISTENER (ctx->listener), NULL,
    GUM_ATTACH_FLAGS_NONE);
  // --- 步骤 6: 根据结果进行处理 ---
  if (result == GUM_ATTACH_OK)
  {
    h->listener_context[listener_index] = ctx;
  }
  else
  {
    arm64_listener_context_free (ctx);
  }
 
  return result;
}
  // --- 步骤 6: 根据结果进行处理 ---
  if (result == GUM_ATTACH_OK)
  {
    h->listener_context[listener_index] = ctx;
  }
  else
  {
    arm64_listener_context_free (ctx);
  }
 
  return result;
}
//frida\subprojects\frida-gum\gum\guminterceptor.c
GumAttachReturn
gum_interceptor_attach (GumInterceptor * self,
                        gpointer function_address,
                        GumInvocationListener * listener,
                        gpointer listener_function_data,
                        GumAttachFlags flags)
{
  GumAttachReturn result = GUM_ATTACH_OK;
  GumFunctionContext * function_ctx;
  GumInstrumentationError error;
 
  gum_interceptor_ignore_current_thread (self);
  GUM_INTERCEPTOR_LOCK (self);
  gum_interceptor_transaction_begin (&self->current_transaction);
  self->current_transaction.is_dirty = TRUE;
 
  function_address = gum_interceptor_resolve (self, function_address);
 
  function_ctx = gum_interceptor_instrument (self, GUM_INTERCEPTOR_TYPE_DEFAULT,
      function_address, &error);
 
  if (function_ctx == NULL)
    goto instrumentation_error;
 
  if (gum_function_context_has_listener (function_ctx, listener))
    goto already_attached;
 
  gum_function_context_add_listener (function_ctx, listener,
      listener_function_data, (flags & GUM_ATTACH_FLAGS_UNIGNORABLE) != 0);
 
  goto beach;
 
instrumentation_error:
  {
    switch (error)
    {
      case GUM_INSTRUMENTATION_ERROR_WRONG_SIGNATURE:
        result = GUM_ATTACH_WRONG_SIGNATURE;
        break;
      case GUM_INSTRUMENTATION_ERROR_POLICY_VIOLATION:
        result = GUM_ATTACH_POLICY_VIOLATION;
        break;
      case GUM_INSTRUMENTATION_ERROR_WRONG_TYPE:
        result = GUM_ATTACH_WRONG_TYPE;
        break;
      default:
        g_assert_not_reached ();
    }
    goto beach;
  }
already_attached:
  {
    result = GUM_ATTACH_ALREADY_ATTACHED;
    goto beach;
  }
beach:
  {
    gum_interceptor_transaction_end (&self->current_transaction);
    GUM_INTERCEPTOR_UNLOCK (self);
    gum_interceptor_unignore_current_thread (self);
 
    return result;
  }
}
//frida\subprojects\frida-gum\gum\guminterceptor.c
GumAttachReturn
gum_interceptor_attach (GumInterceptor * self,
                        gpointer function_address,
                        GumInvocationListener * listener,
                        gpointer listener_function_data,
                        GumAttachFlags flags)
{
  GumAttachReturn result = GUM_ATTACH_OK;
  GumFunctionContext * function_ctx;
  GumInstrumentationError error;
 
  gum_interceptor_ignore_current_thread (self);
  GUM_INTERCEPTOR_LOCK (self);
  gum_interceptor_transaction_begin (&self->current_transaction);
  self->current_transaction.is_dirty = TRUE;
 
  function_address = gum_interceptor_resolve (self, function_address);
 
  function_ctx = gum_interceptor_instrument (self, GUM_INTERCEPTOR_TYPE_DEFAULT,
      function_address, &error);
 
  if (function_ctx == NULL)
    goto instrumentation_error;
 
  if (gum_function_context_has_listener (function_ctx, listener))
    goto already_attached;
 
  gum_function_context_add_listener (function_ctx, listener,
      listener_function_data, (flags & GUM_ATTACH_FLAGS_UNIGNORABLE) != 0);
 
  goto beach;
 
instrumentation_error:
  {
    switch (error)
    {
      case GUM_INSTRUMENTATION_ERROR_WRONG_SIGNATURE:
        result = GUM_ATTACH_WRONG_SIGNATURE;
        break;
      case GUM_INSTRUMENTATION_ERROR_POLICY_VIOLATION:
        result = GUM_ATTACH_POLICY_VIOLATION;
        break;
      case GUM_INSTRUMENTATION_ERROR_WRONG_TYPE:
        result = GUM_ATTACH_WRONG_TYPE;
        break;
      default:
        g_assert_not_reached ();
    }
    goto beach;
  }
already_attached:
  {
    result = GUM_ATTACH_ALREADY_ATTACHED;
    goto beach;
  }
beach:
  {
    gum_interceptor_transaction_end (&self->current_transaction);
    GUM_INTERCEPTOR_UNLOCK (self);
    gum_interceptor_unignore_current_thread (self);
 
    return result;
  }
}
GumAttachReturn
gum_interceptor_attach (GumInterceptor * self,
                        gpointer function_address,
                        GumInvocationListener * listener,
                        gpointer listener_function_data, /* 注意这个参数 */
                        GumAttachFlags flags)
{
  GumAttachReturn result = GUM_ATTACH_OK; // 默认返回“成功”
  GumFunctionContext * function_ctx;
  GumInstrumentationError error;
GumAttachReturn
gum_interceptor_attach (GumInterceptor * self,
                        gpointer function_address,
                        GumInvocationListener * listener,
                        gpointer listener_function_data, /* 注意这个参数 */
                        GumAttachFlags flags)
{
  GumAttachReturn result = GUM_ATTACH_OK; // 默认返回“成功”
  GumFunctionContext * function_ctx;
  GumInstrumentationError error;
gum_interceptor_ignore_current_thread (self);
GUM_INTERCEPTOR_LOCK (self);
gum_interceptor_transaction_begin (&self->current_transaction);
self->current_transaction.is_dirty = TRUE;
gum_interceptor_ignore_current_thread (self);
GUM_INTERCEPTOR_LOCK (self);
gum_interceptor_transaction_begin (&self->current_transaction);
self->current_transaction.is_dirty = TRUE;
gum_interceptor_ignore_current_thread(self);
gum_interceptor_ignore_current_thread(self);
它的作用是:
 **让当前线程暂时被标记为“忽略拦截”**
当被 hook 的函数在该线程中执行时,Frida 会自动跳过 hook 路径,直接执行原始函数。
它的作用是:
 **让当前线程暂时被标记为“忽略拦截”**
当被 hook 的函数在该线程中执行时,Frida 会自动跳过 hook 路径,直接执行原始函数。
function_address = gum_interceptor_resolve (self, function_address);
 
function_ctx = gum_interceptor_instrument (self, GUM_INTERCEPTOR_TYPE_DEFAULT,
    function_address, &error);
 
if (function_ctx == NULL)
  goto instrumentation_error;
function_address = gum_interceptor_resolve (self, function_address);
 
function_ctx = gum_interceptor_instrument (self, GUM_INTERCEPTOR_TYPE_DEFAULT,
    function_address, &error);
 
if (function_ctx == NULL)
  goto instrumentation_error;
if (gum_function_context_has_listener (function_ctx, listener))
  goto already_attached;
 
gum_function_context_add_listener (function_ctx, listener,
    listener_function_data, (flags & GUM_ATTACH_FLAGS_UNIGNORABLE) != 0);
 
goto beach;
if (gum_function_context_has_listener (function_ctx, listener))
  goto already_attached;
 
gum_function_context_add_listener (function_ctx, listener,
    listener_function_data, (flags & GUM_ATTACH_FLAGS_UNIGNORABLE) != 0);
 
goto beach;
instrumentation_error:
  {
    // ... 根据 'error' 类型设置 'result' 状态码 ...
    goto beach;
  }
already_attached:
  {
    result = GUM_ATTACH_ALREADY_ATTACHED;
    goto beach;
  }
beach:
  {
    gum_interceptor_transaction_end (&self->current_transaction);
    GUM_INTERCEPTOR_UNLOCK (self);
    gum_interceptor_unignore_current_thread (self);
 
    return result;
  }
instrumentation_error:
  {
    // ... 根据 'error' 类型设置 'result' 状态码 ...
    goto beach;
  }
already_attached:
  {
    result = GUM_ATTACH_ALREADY_ATTACHED;
    goto beach;
  }
beach:
  {
    gum_interceptor_transaction_end (&self->current_transaction);
    GUM_INTERCEPTOR_UNLOCK (self);
    gum_interceptor_unignore_current_thread (self);
 
    return result;
  }
static GumFunctionContext *
gum_interceptor_instrument (/* ... */)
{
  GumFunctionContext * ctx;
 
  *error = GUM_INSTRUMENTATION_ERROR_NONE;
 
  ctx = (GumFunctionContext *) g_hash_table_lookup (self->function_by_address,function_address); //function_by_address“全局登记册”
 
  if (ctx != NULL)
  {
    if (ctx->type != type)
    {
      *error = GUM_INSTRUMENTATION_ERROR_WRONG_TYPE;
      return NULL;
    }
    return ctx;
  }
static GumFunctionContext *
gum_interceptor_instrument (/* ... */)
{
  GumFunctionContext * ctx;
 
  *error = GUM_INSTRUMENTATION_ERROR_NONE;
 
  ctx = (GumFunctionContext *) g_hash_table_lookup (self->function_by_address,function_address); //function_by_address“全局登记册”
 
  if (ctx != NULL)
  {
    if (ctx->type != type)
    {
      *error = GUM_INSTRUMENTATION_ERROR_WRONG_TYPE;
      return NULL;
    }
    return ctx;
  }
if (self->backend == NULL)
{
  self->backend = _gum_interceptor_backend_create(&self->mutex, &self->allocator); //平台相关(当前 CPU 架构)的具体函数
}
if (self->backend == NULL)
{
  self->backend = _gum_interceptor_backend_create(&self->mutex, &self->allocator); //平台相关(当前 CPU 架构)的具体函数
}
ctx = gum_function_context_new (self, function_address, type);
 
if (gum_process_get_code_signing_policy () == GUM_CODE_SIGNING_REQUIRED)
{ //分支 A
  if (!_gum_interceptor_backend_claim_grafted_trampoline (self->backend, ctx))
    goto policy_violation;  //违反政策 杀死APP
}
else
{ //分支 B
  if (!_gum_interceptor_backend_create_trampoline (self->backend, ctx)) //就是我们一直在找的那个函数
    goto wrong_signature;
}
ctx = gum_function_context_new (self, function_address, type);
 
if (gum_process_get_code_signing_policy () == GUM_CODE_SIGNING_REQUIRED)
{ //分支 A
  if (!_gum_interceptor_backend_claim_grafted_trampoline (self->backend, ctx))
    goto policy_violation;  //违反政策 杀死APP
}
else
{ //分支 B
  if (!_gum_interceptor_backend_create_trampoline (self->backend, ctx)) //就是我们一直在找的那个函数
    goto wrong_signature;
}
//登记到function_by_address“全局登记册”,实现“只插桩一次”的优化 
g_hash_table_insert (self->function_by_address, function_address, ctx);
 
//添加任务,设置回调函数 gum_interceptor_activate 用于激活跳板
//gum_interceptor_activate真正是去内存中写入那条 `JUMP` 指令
gum_interceptor_transaction_schedule_update (&self->current_transaction, ctx,gum_interceptor_activate);
 
return ctx;
//登记到function_by_address“全局登记册”,实现“只插桩一次”的优化 
g_hash_table_insert (self->function_by_address, function_address, ctx);
 
//添加任务,设置回调函数 gum_interceptor_activate 用于激活跳板
//gum_interceptor_activate真正是去内存中写入那条 `JUMP` 指令
gum_interceptor_transaction_schedule_update (&self->current_transaction, ctx,gum_interceptor_activate);
 
return ctx;
policy_violation:
  /* ... */
wrong_signature:
  /* ... */
propagate_error:
  {
    gum_function_context_finalize (ctx);
    return NULL;
  }
policy_violation:
  /* ... */
wrong_signature:
  /* ... */
propagate_error:
  {
    gum_function_context_finalize (ctx);
    return NULL;
  }
GumInterceptorBackend *
_gum_interceptor_backend_create (GRecMutex * mutex,
                                 GumCodeAllocator * allocator)
{
  GumInterceptorBackend * backend;
 
  backend = g_slice_new0 (GumInterceptorBackend); //為“施工隊”(`backend`)本身分配內存
  backend->mutex = mutex;
  backend->allocator = allocator;
GumInterceptorBackend *
_gum_interceptor_backend_create (GRecMutex * mutex,
                                 GumCodeAllocator * allocator)
{
  GumInterceptorBackend * backend;
 
  backend = g_slice_new0 (GumInterceptorBackend); //為“施工隊”(`backend`)本身分配內存
  backend->mutex = mutex;
  backend->allocator = allocator;
if (gum_process_get_code_signing_policy () == GUM_CODE_SIGNING_OPTIONAL)
{
if (gum_process_get_code_signing_policy () == GUM_CODE_SIGNING_OPTIONAL)
{
gum_arm64_writer_init (&backend->writer, NULL);
gum_arm64_relocator_init (&backend->relocator, NULL, &backend->writer);
gum_arm64_writer_init (&backend->writer, NULL);
gum_arm64_relocator_init (&backend->relocator, NULL, &backend->writer);
    gum_interceptor_backend_create_thunks (backend);
  }
 
  return backend;
}
    gum_interceptor_backend_create_thunks (backend);
  }
 
  return backend;
}
static void gum_interceptor_backend_create_thunks (GumInterceptorBackend * self) //第4部分:最上层
{
  gsize page_size, code_size;
  GumMemoryRange range;
 
  page_size = gum_query_page_size ();
  code_size = page_size;
 
  self->thunks = gum_memory_allocate (NULL, code_size, page_size, GUM_PAGE_RW);
 
  range.base_address = GUM_ADDRESS (self->thunks);
  range.size = code_size;
  gum_cloak_add_range (&range);
 
  gum_memory_patch_code (self->thunks, 1024,
      (GumMemoryPatchApplyFunc) gum_emit_thunks, self);
}
 
 
static void gum_interceptor_backend_destroy_thunks (GumInterceptorBackend * self)
{
  gum_memory_free (self->thunks, gum_query_page_size ());
}
 
static void gum_emit_thunks (gpointer mem,GumInterceptorBackend * self)//第3部分:中间层
{
  GumArm64Writer * aw = &self->writer;
 
  self->enter_thunk = self->thunks;
  gum_arm64_writer_reset (aw, mem);
  aw->pc = GUM_ADDRESS (self->enter_thunk);
  gum_emit_enter_thunk (aw);
  gum_arm64_writer_flush (aw);
 
  self->leave_thunk = (guint8 *) self->enter_thunk + gum_arm64_writer_offset (aw);
  gum_emit_leave_thunk (aw);
  gum_arm64_writer_flush (aw);
}
 
static void
gum_emit_enter_thunk (GumArm64Writer * aw)  //第1部分:最底层
{
  gum_emit_prolog (aw);
 
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X1, ARM64_REG_SP,
      GUM_FRAME_OFFSET_CPU_CONTEXT);
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X2, ARM64_REG_SP,
      GUM_FRAME_OFFSET_CPU_CONTEXT + G_STRUCT_OFFSET (GumCpuContext, lr));
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X3, ARM64_REG_SP,
      GUM_FRAME_OFFSET_NEXT_HOP);
 
  gum_arm64_writer_put_call_address_with_arguments (aw,
      GUM_ADDRESS (_gum_function_context_begin_invocation), 4,
      GUM_ARG_REGISTER, ARM64_REG_X17,
      GUM_ARG_REGISTER, ARM64_REG_X1,
      GUM_ARG_REGISTER, ARM64_REG_X2,
      GUM_ARG_REGISTER, ARM64_REG_X3);
 
  gum_emit_epilog (aw);
}
 
static void
gum_emit_leave_thunk (GumArm64Writer * aw) //第2部分:最底层
{
  gum_emit_prolog (aw);
 
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X1, ARM64_REG_SP,
      GUM_FRAME_OFFSET_CPU_CONTEXT);
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X2, ARM64_REG_SP,
      GUM_FRAME_OFFSET_NEXT_HOP);
 
  gum_arm64_writer_put_call_address_with_arguments (aw,
      GUM_ADDRESS (_gum_function_context_end_invocation), 3,
      GUM_ARG_REGISTER, ARM64_REG_X17,
      GUM_ARG_REGISTER, ARM64_REG_X1,
      GUM_ARG_REGISTER, ARM64_REG_X2);
 
  gum_emit_epilog (aw);
}
static void gum_interceptor_backend_create_thunks (GumInterceptorBackend * self) //第4部分:最上层
{
  gsize page_size, code_size;
  GumMemoryRange range;
 
  page_size = gum_query_page_size ();
  code_size = page_size;
 
  self->thunks = gum_memory_allocate (NULL, code_size, page_size, GUM_PAGE_RW);
 
  range.base_address = GUM_ADDRESS (self->thunks);
  range.size = code_size;
  gum_cloak_add_range (&range);
 
  gum_memory_patch_code (self->thunks, 1024,
      (GumMemoryPatchApplyFunc) gum_emit_thunks, self);
}
 
 
static void gum_interceptor_backend_destroy_thunks (GumInterceptorBackend * self)
{
  gum_memory_free (self->thunks, gum_query_page_size ());
}
 
static void gum_emit_thunks (gpointer mem,GumInterceptorBackend * self)//第3部分:中间层
{
  GumArm64Writer * aw = &self->writer;
 
  self->enter_thunk = self->thunks;
  gum_arm64_writer_reset (aw, mem);
  aw->pc = GUM_ADDRESS (self->enter_thunk);
  gum_emit_enter_thunk (aw);
  gum_arm64_writer_flush (aw);
 
  self->leave_thunk = (guint8 *) self->enter_thunk + gum_arm64_writer_offset (aw);
  gum_emit_leave_thunk (aw);
  gum_arm64_writer_flush (aw);
}
 
static void
gum_emit_enter_thunk (GumArm64Writer * aw)  //第1部分:最底层
{
  gum_emit_prolog (aw);
 
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X1, ARM64_REG_SP,
      GUM_FRAME_OFFSET_CPU_CONTEXT);
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X2, ARM64_REG_SP,
      GUM_FRAME_OFFSET_CPU_CONTEXT + G_STRUCT_OFFSET (GumCpuContext, lr));
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X3, ARM64_REG_SP,
      GUM_FRAME_OFFSET_NEXT_HOP);
 
  gum_arm64_writer_put_call_address_with_arguments (aw,
      GUM_ADDRESS (_gum_function_context_begin_invocation), 4,
      GUM_ARG_REGISTER, ARM64_REG_X17,
      GUM_ARG_REGISTER, ARM64_REG_X1,
      GUM_ARG_REGISTER, ARM64_REG_X2,
      GUM_ARG_REGISTER, ARM64_REG_X3);
 
  gum_emit_epilog (aw);
}
 
static void
gum_emit_leave_thunk (GumArm64Writer * aw) //第2部分:最底层
{
  gum_emit_prolog (aw);
 
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X1, ARM64_REG_SP,
      GUM_FRAME_OFFSET_CPU_CONTEXT);
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X2, ARM64_REG_SP,
      GUM_FRAME_OFFSET_NEXT_HOP);
 
  gum_arm64_writer_put_call_address_with_arguments (aw,
      GUM_ADDRESS (_gum_function_context_end_invocation), 3,
      GUM_ARG_REGISTER, ARM64_REG_X17,
      GUM_ARG_REGISTER, ARM64_REG_X1,
      GUM_ARG_REGISTER, ARM64_REG_X2);
 
  gum_emit_epilog (aw);
}
static void
gum_emit_enter_thunk (GumArm64Writer * aw)
{
  // 1. 发出“函数序言”(Prolog)
  //    这会 JIT 编译出保存所有 CPU 寄存器(X0-X28, FP, LR)到堆栈上的代码。
  //    这是“保护现场”。
  gum_emit_prolog (aw);
 
  // 2. 准备 C 函数的参数 (X1, X2, X3)
  //    在 ARM64 C 语言调用约定中,参数通过 X0, X1, X2, X3... 传递。
  //    X0 稍后会设置(它来自 X17)。
 
  //    ADD X1, SP, ...CPU_CONTEXT
  //    将“指向堆栈上 GumCpuContext(包含了所有寄存器值) 结构的指针”放入 X1(第二个参数)。
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X1, ARM64_REG_SP, GUM_FRAME_OFFSET_CPU_CONTEXT);
  //    ADD X2, SP, ...CpuContext.lr
  //    将“指向堆栈上 lr 寄存器保存位置的指针”放入 X2(第三个参数)。
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X2, ARM64_REG_SP, GUM_FRAME_OFFSET_CPU_CONTEXT + G_STRUCT_OFFSET (GumCpuContext, lr));
  //    ADD X3, SP, ...NEXT_HOP
  //    将“指向堆栈上 next_hop 字段的指针”放入 X3(第四个参数)。
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X3, ARM64_REG_SP,
      GUM_FRAME_OFFSET_NEXT_HOP);
 
  // 3. JIT 编译一个 C 函数调用
  gum_arm64_writer_put_call_address_with_arguments (aw,
      GUM_ADDRESS (_gum_function_context_begin_invocation), 4, // 目标C函数
      // 这个 C 函数将接收 4 个参数:
      //   - Arg 1 (X0) = X17 (这是由 Dispatcher 提前设置好的 GumFunctionContext*,(包含了 `listener` 列表) )
      GUM_ARG_REGISTER, ARM64_REG_X17,
      //   - Arg 2 (X1) = X1 (我们刚设置的 GumCpuContext*,(包含了所有寄存器值))
      GUM_ARG_REGISTER, ARM64_REG_X1,
      //   - Arg 3 (X2) = X2 (我们刚设置的 saved_lr*)
      GUM_ARG_REGISTER, ARM64_REG_X2,
      //   - Arg 4 (X3) = X3 (我们刚设置的 next_hop*)
      GUM_ARG_REGISTER, ARM64_REG_X3);
 
  // 4. 发出“函数终言”(Epilog)
  //    这会 JIT 编译出从堆栈上恢复所有寄存器的代码,并执行 RET(返回)。
  gum_emit_epilog (aw);
}
static void
gum_emit_enter_thunk (GumArm64Writer * aw)
{
  // 1. 发出“函数序言”(Prolog)
  //    这会 JIT 编译出保存所有 CPU 寄存器(X0-X28, FP, LR)到堆栈上的代码。
  //    这是“保护现场”。
  gum_emit_prolog (aw);
 
  // 2. 准备 C 函数的参数 (X1, X2, X3)
  //    在 ARM64 C 语言调用约定中,参数通过 X0, X1, X2, X3... 传递。
  //    X0 稍后会设置(它来自 X17)。
 
  //    ADD X1, SP, ...CPU_CONTEXT
  //    将“指向堆栈上 GumCpuContext(包含了所有寄存器值) 结构的指针”放入 X1(第二个参数)。
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X1, ARM64_REG_SP, GUM_FRAME_OFFSET_CPU_CONTEXT);
  //    ADD X2, SP, ...CpuContext.lr
  //    将“指向堆栈上 lr 寄存器保存位置的指针”放入 X2(第三个参数)。
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X2, ARM64_REG_SP, GUM_FRAME_OFFSET_CPU_CONTEXT + G_STRUCT_OFFSET (GumCpuContext, lr));
  //    ADD X3, SP, ...NEXT_HOP
  //    将“指向堆栈上 next_hop 字段的指针”放入 X3(第四个参数)。
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X3, ARM64_REG_SP,
      GUM_FRAME_OFFSET_NEXT_HOP);
 
  // 3. JIT 编译一个 C 函数调用
  gum_arm64_writer_put_call_address_with_arguments (aw,
      GUM_ADDRESS (_gum_function_context_begin_invocation), 4, // 目标C函数
      // 这个 C 函数将接收 4 个参数:
      //   - Arg 1 (X0) = X17 (这是由 Dispatcher 提前设置好的 GumFunctionContext*,(包含了 `listener` 列表) )
      GUM_ARG_REGISTER, ARM64_REG_X17,
      //   - Arg 2 (X1) = X1 (我们刚设置的 GumCpuContext*,(包含了所有寄存器值))
      GUM_ARG_REGISTER, ARM64_REG_X1,
      //   - Arg 3 (X2) = X2 (我们刚设置的 saved_lr*)
      GUM_ARG_REGISTER, ARM64_REG_X2,
      //   - Arg 4 (X3) = X3 (我们刚设置的 next_hop*)
      GUM_ARG_REGISTER, ARM64_REG_X3);
 
  // 4. 发出“函数终言”(Epilog)
  //    这会 JIT 编译出从堆栈上恢复所有寄存器的代码,并执行 RET(返回)。
  gum_emit_epilog (aw);
}
; 步骤 C: 保存所有寄存器(包括 X17)到堆栈
; (gum_emit_prolog)
 
; 步骤 D: 准备 C 函数的第 234 个参数 (X1, X2, X3)
ADD X1, SP, ...  ; (GumCpuContext*)
ADD X2, SP, ...  ; (saved_lr*)
ADD X3, SP, ...  ; (next_hop*)
 
; 步骤 E: 准备 C 函数的第 1 个参数 (X0)
; 关键!把“中转”寄存器 X17 里的值,
; “正式”移动到第一个参数寄存器 X0 中!
MOV X0, X17
 
; 步骤 F: 调用 C 语言大脑
BL _gum_function_context_begin_invocation
; 步骤 C: 保存所有寄存器(包括 X17)到堆栈
; (gum_emit_prolog)
 
; 步骤 D: 准备 C 函数的第 234 个参数 (X1, X2, X3)
ADD X1, SP, ...  ; (GumCpuContext*)
ADD X2, SP, ...  ; (saved_lr*)
ADD X3, SP, ...  ; (next_hop*)
 
; 步骤 E: 准备 C 函数的第 1 个参数 (X0)
; 关键!把“中转”寄存器 X17 里的值,
; “正式”移动到第一个参数寄存器 X0 中!
MOV X0, X17
 
; 步骤 F: 调用 C 语言大脑
BL _gum_function_context_begin_invocation
static void
gum_emit_leave_thunk (GumArm64Writer * aw)
{
  // 1. 发出“函数序言”(Prolog) - 保存所有寄存器
  gum_emit_prolog (aw);
 
  // 2. 准备 C 函数的参数 (X1, X2)
  //    ADD X1, SP, ...CPU_CONTEXT
  //    将 GumCpuContext* 放入 X1(第二个参数)。
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X1, ARM64_REG_SP,
      GUM_FRAME_OFFSET_CPU_CONTEXT);
  //    ADD X2, SP, ...NEXT_HOP
  //    将 next_hop* 放入 X2(第三个参数)。
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X2, ARM64_REG_SP,
      GUM_FRAME_OFFSET_NEXT_HOP);
 
  // 3. JIT 编译一个 C 函数调用
  gum_arm64_writer_put_call_address_with_arguments (aw,
      GUM_ADDRESS (_gum_function_context_end_invocation), 3, // 目标C函数
      // 这个 C 函数将接收 3 个参数:
      //   - Arg 1 (X0) = X17 (GumFunctionContext*)
      GUM_ARG_REGISTER, ARM64_REG_X17,
      //   - Arg 2 (X1) = X1 (GumCpuContext*)
      GUM_ARG_REGISTER, ARM64_REG_X1,
      //   - Arg 2 (X2) = X2 (next_hop*)
      GUM_ARG_REGISTER, ARM64_REG_X2);
 
  // 4. 发出“函数终言”(Epilog) - 恢复所有寄存器并 RET
  gum_emit_epilog (aw);
}
static void
gum_emit_leave_thunk (GumArm64Writer * aw)
{
  // 1. 发出“函数序言”(Prolog) - 保存所有寄存器
  gum_emit_prolog (aw);
 
  // 2. 准备 C 函数的参数 (X1, X2)
  //    ADD X1, SP, ...CPU_CONTEXT
  //    将 GumCpuContext* 放入 X1(第二个参数)。
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X1, ARM64_REG_SP,
      GUM_FRAME_OFFSET_CPU_CONTEXT);
  //    ADD X2, SP, ...NEXT_HOP
  //    将 next_hop* 放入 X2(第三个参数)。
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X2, ARM64_REG_SP,
      GUM_FRAME_OFFSET_NEXT_HOP);
 
  // 3. JIT 编译一个 C 函数调用
  gum_arm64_writer_put_call_address_with_arguments (aw,
      GUM_ADDRESS (_gum_function_context_end_invocation), 3, // 目标C函数
      // 这个 C 函数将接收 3 个参数:
      //   - Arg 1 (X0) = X17 (GumFunctionContext*)
      GUM_ARG_REGISTER, ARM64_REG_X17,
      //   - Arg 2 (X1) = X1 (GumCpuContext*)
      GUM_ARG_REGISTER, ARM64_REG_X1,
      //   - Arg 2 (X2) = X2 (next_hop*)
      GUM_ARG_REGISTER, ARM64_REG_X2);
 
  // 4. 发出“函数终言”(Epilog) - 恢复所有寄存器并 RET
  gum_emit_epilog (aw);
}
static void
gum_emit_thunks (gpointer mem,         // 'mem' 是那块新分配的 RW 内存页
                 GumInterceptorBackend * self)
{
  GumArm64Writer * aw = &self->writer; // 拿起“汇编笔”
 
  // 1. 记录 enter_thunk 的起始地址
  self->enter_thunk = self->thunks;
   
  // 2. 将“汇编笔”重置到这块内存的开头
  gum_arm64_writer_reset (aw, mem);
  aw->pc = GUM_ADDRESS (self->enter_thunk); // 告知“笔”当前的绝对地址
 
  // 3. 在内存中 JIT 编译出 enter_thunk 的所有汇编代码
  gum_emit_enter_thunk (aw);
  gum_arm64_writer_flush (aw); // 提交写入
 
  // 4. 记录 leave_thunk 的起始地址
  //    它紧跟在 enter_thunk 的末尾
  self->leave_thunk = (guint8 *) self->enter_thunk + gum_arm64_writer_offset (aw);
   
  // 5. 在内存中 JIT 编译出 leave_thunk 的所有汇编代码
  gum_emit_leave_thunk (aw);
  gum_arm64_writer_flush (aw); // 提交写入
}
static void
gum_emit_thunks (gpointer mem,         // 'mem' 是那块新分配的 RW 内存页
                 GumInterceptorBackend * self)
{
  GumArm64Writer * aw = &self->writer; // 拿起“汇编笔”
 
  // 1. 记录 enter_thunk 的起始地址
  self->enter_thunk = self->thunks;
   
  // 2. 将“汇编笔”重置到这块内存的开头
  gum_arm64_writer_reset (aw, mem);
  aw->pc = GUM_ADDRESS (self->enter_thunk); // 告知“笔”当前的绝对地址
 
  // 3. 在内存中 JIT 编译出 enter_thunk 的所有汇编代码
  gum_emit_enter_thunk (aw);
  gum_arm64_writer_flush (aw); // 提交写入
 
  // 4. 记录 leave_thunk 的起始地址
  //    它紧跟在 enter_thunk 的末尾
  self->leave_thunk = (guint8 *) self->enter_thunk + gum_arm64_writer_offset (aw);
   
  // 5. 在内存中 JIT 编译出 leave_thunk 的所有汇编代码
  gum_emit_leave_thunk (aw);
  gum_arm64_writer_flush (aw); // 提交写入
}
static void
gum_emit_thunks (gpointer mem,         // mem 是一块刚申请的“空白内存页”
                 GumInterceptorBackend * self)
{
  GumArm64Writer * aw = &self->writer; // 拿到“汇编笔”
 
  /* --- 步骤 1: 写入“前面”的 enter_thunk --- */
 
  // 1a. 把“笔尖”复位 (reset) 到“空白内存页”(mem) 的最开头
  gum_arm64_writer_reset (aw, mem);
  // 1b. 告诉“笔”它现在的绝对地址(用于计算跳转)
  aw->pc = GUM_ADDRESS (self->enter_thunk);
 
  // 1c. 开始 JIT 编译!
  //     “笔”开始在内存上写入 enter_thunk 的所有汇编指令
  //     (比如写了 100 字节)
  gum_emit_enter_thunk (aw);
   
  // 1d. “笔”停在了 enter_thunk 的末尾(即 100 字节处)
  gum_arm64_writer_flush (aw);
 
  /* --- 步骤 2: 写入“后面”的 leave_thunk --- */
 
  // 2a. 记录 leave_thunk 的起始地址:
  //     gum_arm64_writer_offset(aw) 会返回“笔”现在的位置(即 100)
  //     所以 leave_thunk 的地址 = 内存页起始地址 + 100
  self->leave_thunk =
      (guint8 *) self->enter_thunk + gum_arm64_writer_offset (aw);
 
  // 2b. 开始 JIT 编译!
  //     “笔”从它当前的位置(100 字节处)继续写入 leave_thunk 的指令
  gum_emit_leave_thunk (aw);
   
  // 2c. “笔”停在了 leave_thunk 的末尾
  gum_arm64_writer_flush (aw);
}
static void
gum_emit_thunks (gpointer mem,         // mem 是一块刚申请的“空白内存页”
                 GumInterceptorBackend * self)
{
  GumArm64Writer * aw = &self->writer; // 拿到“汇编笔”
 
  /* --- 步骤 1: 写入“前面”的 enter_thunk --- */
 
  // 1a. 把“笔尖”复位 (reset) 到“空白内存页”(mem) 的最开头
  gum_arm64_writer_reset (aw, mem);
  // 1b. 告诉“笔”它现在的绝对地址(用于计算跳转)
  aw->pc = GUM_ADDRESS (self->enter_thunk);
 
  // 1c. 开始 JIT 编译!
  //     “笔”开始在内存上写入 enter_thunk 的所有汇编指令
  //     (比如写了 100 字节)
  gum_emit_enter_thunk (aw);
   
  // 1d. “笔”停在了 enter_thunk 的末尾(即 100 字节处)
  gum_arm64_writer_flush (aw);
 
  /* --- 步骤 2: 写入“后面”的 leave_thunk --- */
 
  // 2a. 记录 leave_thunk 的起始地址:
  //     gum_arm64_writer_offset(aw) 会返回“笔”现在的位置(即 100)
  //     所以 leave_thunk 的地址 = 内存页起始地址 + 100
  self->leave_thunk =
      (guint8 *) self->enter_thunk + gum_arm64_writer_offset (aw);
 
  // 2b. 开始 JIT 编译!
  //     “笔”从它当前的位置(100 字节处)继续写入 leave_thunk 的指令
  gum_emit_leave_thunk (aw);
   
  // 2c. “笔”停在了 leave_thunk 的末尾
  gum_arm64_writer_flush (aw);
}
static void
gum_interceptor_backend_create_thunks (GumInterceptorBackend * self)
{
  gsize page_size, code_size;
  GumMemoryRange range;
 
  page_size = gum_query_page_size ();
  code_size = page_size;
 
  // 1. 申请一页 RW(读写)内存
  self->thunks = gum_memory_allocate (NULL, code_size, page_size, GUM_PAGE_RW);
 
  // 2. “隐藏” (Cloak) 这块内存
  //    这是为了防止 Frida 自己的 Stalker 等工具来跟踪 Frida 内部的
  //    thunk 代码,避免无限递归。
  range.base_address = GUM_ADDRESS (self->thunks);
  range.size = code_size;
  gum_cloak_add_range (&range);  //隐藏这块内存
 
  // 3. 调用 gum_emit_thunks(作为回调),
  //    让它在这块内存里 JIT 编译出 enter 和 leave thunks。
  //    (gum_memory_patch_code 稍后会负责将这块内存变为 RX(读+执行))
  gum_memory_patch_code (self->thunks, 1024, (GumMemoryPatchApplyFunc) gum_emit_thunks, self);
}
static void
gum_interceptor_backend_create_thunks (GumInterceptorBackend * self)
{
  gsize page_size, code_size;
  GumMemoryRange range;
 
  page_size = gum_query_page_size ();
  code_size = page_size;
 
  // 1. 申请一页 RW(读写)内存
  self->thunks = gum_memory_allocate (NULL, code_size, page_size, GUM_PAGE_RW);
 
  // 2. “隐藏” (Cloak) 这块内存
  //    这是为了防止 Frida 自己的 Stalker 等工具来跟踪 Frida 内部的
  //    thunk 代码,避免无限递归。
  range.base_address = GUM_ADDRESS (self->thunks);
  range.size = code_size;
  gum_cloak_add_range (&range);  //隐藏这块内存
 
  // 3. 调用 gum_emit_thunks(作为回调),
  //    让它在这块内存里 JIT 编译出 enter 和 leave thunks。
  //    (gum_memory_patch_code 稍后会负责将这块内存变为 RX(读+执行))
  gum_memory_patch_code (self->thunks, 1024, (GumMemoryPatchApplyFunc) gum_emit_thunks, self);
}
<span style="color:red; font-size:26px;">我们先看</span> <span style="color:blue; font-size:26px;">第1部分`gum_emit_enter_thunk`(我们上一步分析的)是一个汇编“桥梁”, </span><span style="color:red; font-size:26px;">它**调用**的就是这个 `_gum_function_context_begin_invocation` C 函数。</span>
<span style="color:red; font-size:26px;">我们先看</span> <span style="color:blue; font-size:26px;">第1部分`gum_emit_enter_thunk`(我们上一步分析的)是一个汇编“桥梁”, </span><span style="color:red; font-size:26px;">它**调用**的就是这个 `_gum_function_context_begin_invocation` C 函数。</span>
gboolean _gum_function_context_begin_invocation (GumFunctionContext * function_ctx,
                                        GumCpuContext * cpu_context,
                                        gpointer * caller_ret_addr,
                                        gpointer * next_hop)
gboolean _gum_function_context_begin_invocation (GumFunctionContext * function_ctx,
                                        GumCpuContext * cpu_context,
                                        gpointer * caller_ret_addr,
                                        gpointer * next_hop)
Interceptor.attach(ptr("write"), {
  onEnter: function(args) {
    // 你的目的是:打印所有被写入的内容
    console.log("WRITING: " + args[1].readCString());
  }
});
Interceptor.attach(ptr("write"), {
  onEnter: function(args) {
    // 你的目的是:打印所有被写入的内容
    console.log("WRITING: " + args[1].readCString());
  }
});
if (gum_tls_key_get_value (gum_interceptor_guard_key) == interceptor)
{
  *next_hop = function_ctx->on_invoke_trampoline;
  goto bypass;
}
gum_tls_key_set_value (gum_interceptor_guard_key, interceptor);
if (gum_tls_key_get_value (gum_interceptor_guard_key) == interceptor)
{
  *next_hop = function_ctx->on_invoke_trampoline;
  goto bypass;
}
gum_tls_key_set_value (gum_interceptor_guard_key, interceptor);
stack_entry = gum_invocation_stack_peek_top (stack);
if (stack_entry != NULL &&
    stack_entry->calling_replacement &&
    /* ... 检查函数地址是否匹配 ... */)
{
  gum_tls_key_set_value (gum_interceptor_guard_key, NULL); // 清除守卫
  *next_hop = function_ctx->on_invoke_trampoline;
  goto bypass;
}
stack_entry = gum_invocation_stack_peek_top (stack);
if (stack_entry != NULL &&
    stack_entry->calling_replacement &&
    /* ... 检查函数地址是否匹配 ... */)
{
  gum_tls_key_set_value (gum_interceptor_guard_key, NULL); // 清除守卫
  *next_hop = function_ctx->on_invoke_trampoline;
  goto bypass;
}
#ifndef HAVE_WINDOWS
  system_error = gum_thread_get_system_error ();
#endif
#ifndef HAVE_WINDOWS
  system_error = gum_thread_get_system_error ();
#endif
if (interceptor->selected_thread_id != 0)
{
  invoke_listeners =
      gum_process_get_current_thread_id () == interceptor->selected_thread_id;
}
 
if (invoke_listeners)
{
  invoke_listeners = (interceptor_ctx->ignore_level <= 0);
}
 
if (!invoke_listeners && function_ctx->has_unignorable_listener)
{
  invoke_listeners = TRUE;
  only_invoke_unignorable_listeners = TRUE;
}
if (interceptor->selected_thread_id != 0)
{
  invoke_listeners =
      gum_process_get_current_thread_id () == interceptor->selected_thread_id;
}
 
if (invoke_listeners)
{
  invoke_listeners = (interceptor_ctx->ignore_level <= 0);
}
 
if (!invoke_listeners && function_ctx->has_unignorable_listener)
{
  invoke_listeners = TRUE;
  only_invoke_unignorable_listeners = TRUE;
}
will_trap_on_leave = function_ctx->replacement_function != NULL || (invoke_listeners && function_ctx->has_on_leave_listener);
 
//Frida 是否需要在目标函数执行完毕、即将返回时,再次‘劫持’控制权?
if (will_trap_on_leave)
{
  stack_entry = gum_invocation_stack_push (stack, function_ctx, *caller_ret_addr, only_invoke_unignorable_listeners);
  invocation_ctx = &stack_entry->invocation_context;
}
else if (invoke_listeners)
{
  /* ... 还是 push,但存入的是假返回地址 ... */
  invocation_ctx = &stack_entry->invocation_context;
}
will_trap_on_leave = function_ctx->replacement_function != NULL || (invoke_listeners && function_ctx->has_on_leave_listener);
 
//Frida 是否需要在目标函数执行完毕、即将返回时,再次‘劫持’控制权?
if (will_trap_on_leave)
{
  stack_entry = gum_invocation_stack_push (stack, function_ctx, *caller_ret_addr, only_invoke_unignorable_listeners);
  invocation_ctx = &stack_entry->invocation_context;
}
else if (invoke_listeners)
{
  /* ... 还是 push,但存入的是假返回地址 ... */
  invocation_ctx = &stack_entry->invocation_context;
}
gum_function_context_fixup_cpu_context (function_ctx, cpu_context);
 
if (invoke_listeners)
{
  /* ... */
  invocation_ctx->cpu_context = cpu_context;
  /* ... */
  listener_entries =
      (GPtrArray *) g_atomic_pointer_get (&function_ctx->listener_entries);
  for (i = 0; i != listener_entries->len; i++)
  {
    /* ... 过滤 unignorable ... */
 
    if (listener_entry->listener_interface->on_enter != NULL)
    {
      listener_entry->listener_interface->on_enter (
          listener_entry->listener_instance, invocation_ctx);
    }
  }
}
gum_function_context_fixup_cpu_context (function_ctx, cpu_context);
 
if (invoke_listeners)
{
  /* ... */
  invocation_ctx->cpu_context = cpu_context;
  /* ... */
  listener_entries =
      (GPtrArray *) g_atomic_pointer_get (&function_ctx->listener_entries);
  for (i = 0; i != listener_entries->len; i++)
  {
    /* ... 过滤 unignorable ... */
 
    if (listener_entry->listener_interface->on_enter != NULL)
    {
      listener_entry->listener_interface->on_enter (
          listener_entry->listener_instance, invocation_ctx);
    }
  }
}
// 我们在“测试代码”里,把 arm64_listener_context_on_enter 这个 C 函数注册为了 on_enter 的回调。
ctx->listener->on_enter = (TestCallbackListenerFunc) arm64_listener_context_on_enter;
// 我们在“测试代码”里,把 arm64_listener_context_on_enter 这个 C 函数注册为了 on_enter 的回调。
ctx->listener->on_enter = (TestCallbackListenerFunc) arm64_listener_context_on_enter;
if (!will_trap_on_leave && invoke_listeners)
{
  gum_invocation_stack_pop (interceptor_ctx->stack);
}
 
gum_tls_key_set_value (gum_interceptor_guard_key, NULL);
 
if (will_trap_on_leave)
{
  *caller_ret_addr = function_ctx->on_leave_trampoline;
}
 
if (function_ctx->replacement_function != NULL)  //使用了frida的  `Interceptor.replace`了
{
  /* ... */
  *next_hop = function_ctx->replacement_function;//设置“下一跳”为 `Interceptor.replace` 的**替换函数**
}
else
{
  *next_hop = function_ctx->on_invoke_trampoline;//设置“下一跳”为**原始函数**
}
if (!will_trap_on_leave && invoke_listeners)
{
  gum_invocation_stack_pop (interceptor_ctx->stack);
}
 
gum_tls_key_set_value (gum_interceptor_guard_key, NULL);
 
if (will_trap_on_leave)
{
  *caller_ret_addr = function_ctx->on_leave_trampoline;
}
 
if (function_ctx->replacement_function != NULL)  //使用了frida的  `Interceptor.replace`了
{
  /* ... */
  *next_hop = function_ctx->replacement_function;//设置“下一跳”为 `Interceptor.replace` 的**替换函数**
}
else
{
  *next_hop = function_ctx->on_invoke_trampoline;//设置“下一跳”为**原始函数**
}
void _gum_function_context_end_invocation (GumFunctionContext * function_ctx,
                                      GumCpuContext * cpu_context,
                                      gpointer * next_hop)
void _gum_function_context_end_invocation (GumFunctionContext * function_ctx,
                                      GumCpuContext * cpu_context,
                                      gpointer * next_hop)
#ifdef HAVE_WINDOWS
  system_error = gum_thread_get_system_error ();
#endif
 
  gum_tls_key_set_value (gum_interceptor_guard_key, function_ctx->interceptor);
 
#ifndef HAVE_WINDOWS
  system_error = gum_thread_get_system_error ();
#endif
#ifdef HAVE_WINDOWS
  system_error = gum_thread_get_system_error ();
#endif
 
  gum_tls_key_set_value (gum_interceptor_guard_key, function_ctx->interceptor);
 
#ifndef HAVE_WINDOWS
  system_error = gum_thread_get_system_error ();
#endif
interceptor_ctx = get_interceptor_thread_context ();
 
stack_entry = gum_invocation_stack_peek_top (interceptor_ctx->stack);
*next_hop = gum_sign_code_pointer (stack_entry->caller_ret_addr);
interceptor_ctx = get_interceptor_thread_context ();
 
stack_entry = gum_invocation_stack_peek_top (interceptor_ctx->stack);
*next_hop = gum_sign_code_pointer (stack_entry->caller_ret_addr);
invocation_ctx = &stack_entry->invocation_context;
invocation_ctx->cpu_context = cpu_context;
/* ... errno 相关的复杂处理 ... */
invocation_ctx->backend = &interceptor_ctx->listener_backend;
invocation_ctx = &stack_entry->invocation_context;
invocation_ctx->cpu_context = cpu_context;
/* ... errno 相关的复杂处理 ... */
invocation_ctx->backend = &interceptor_ctx->listener_backend;
listener_entries = (GPtrArray *) g_atomic_pointer_get (&function_ctx->listener_entries);
only_invoke_unignorable_listeners = stack_entry->only_invoke_unignorable_listeners;
for (i = 0; i != listener_entries->len; i++)
{
  /* ... 过滤 unignorable(不可忽视的) ... */
 
  if (listener_entry->listener_interface->on_leave != NULL)
  {
    listener_entry->listener_interface->on_leave (
        listener_entry->listener_instance, invocation_ctx);
  }
}
listener_entries = (GPtrArray *) g_atomic_pointer_get (&function_ctx->listener_entries);
only_invoke_unignorable_listeners = stack_entry->only_invoke_unignorable_listeners;
for (i = 0; i != listener_entries->len; i++)
{
  /* ... 过滤 unignorable(不可忽视的) ... */
 
  if (listener_entry->listener_interface->on_leave != NULL)
  {
    listener_entry->listener_interface->on_leave (
        listener_entry->listener_instance, invocation_ctx);
  }
}
  gum_thread_set_system_error (invocation_ctx->system_error);
 
  gum_invocation_stack_pop (interceptor_ctx->stack);
 
  gum_tls_key_set_value (gum_interceptor_guard_key, NULL);
 
  g_atomic_int_dec_and_test (&function_ctx->trampoline_usage_counter);
}
  gum_thread_set_system_error (invocation_ctx->system_error);
 
  gum_invocation_stack_pop (interceptor_ctx->stack);
 
  gum_tls_key_set_value (gum_interceptor_guard_key, NULL);
 
  g_atomic_int_dec_and_test (&function_ctx->trampoline_usage_counter);
}
gboolean _gum_interceptor_backend_create_trampoline (GumInterceptorBackend * self,
                                        GumFunctionContext * ctx)
{
  GumArm64Writer * aw = &self->writer;
  GumArm64Relocator * ar = &self->relocator;
  gpointer function_address = ctx->function_address;
  // ...
gboolean _gum_interceptor_backend_create_trampoline (GumInterceptorBackend * self,
                                        GumFunctionContext * ctx)
{
  GumArm64Writer * aw = &self->writer;
  GumArm64Relocator * ar = &self->relocator;
  gpointer function_address = ctx->function_address;
  // ...
if (!gum_interceptor_backend_prepare_trampoline (self, ctx, &need_deflector)) //预分析步骤
  return FALSE;
 
//把“汇编笔” (`aw`) 放在“施工地”的开头,准备 JIT(即时编译)。
gum_arm64_writer_reset (aw, ctx->trampoline_slice->data);
 
if (ctx->type == GUM_INTERCEPTOR_TYPE_FAST)
{
  deflector_target = ctx->replacement_function;
}
else
{
    //JIT 编译出的 `Dispatcher`(总调度中心)的入口地址
    //gum_arm64_writer_cur (aw) 在这里返回的就是这块新内存(“施工地”)的“起始地址”
  ctx->on_enter_trampoline = gum_sign_code_pointer (gum_arm64_writer_cur (aw));
  deflector_target = ctx->on_enter_trampoline;
}
 
if (need_deflector)
{
  // ... (处理 B 指令跳转距离太远的情况) ...
}
if (!gum_interceptor_backend_prepare_trampoline (self, ctx, &need_deflector)) //预分析步骤
  return FALSE;
 
//把“汇编笔” (`aw`) 放在“施工地”的开头,准备 JIT(即时编译)。
gum_arm64_writer_reset (aw, ctx->trampoline_slice->data);
 
if (ctx->type == GUM_INTERCEPTOR_TYPE_FAST)
{
  deflector_target = ctx->replacement_function;
}
else
{
    //JIT 编译出的 `Dispatcher`(总调度中心)的入口地址
    //gum_arm64_writer_cur (aw) 在这里返回的就是这块新内存(“施工地”)的“起始地址”
  ctx->on_enter_trampoline = gum_sign_code_pointer (gum_arm64_writer_cur (aw));
  deflector_target = ctx->on_enter_trampoline;
}
 
if (need_deflector)
{
  // ... (处理 B 指令跳转距离太远的情况) ...
}
if (ctx->type != GUM_INTERCEPTOR_TYPE_FAST)
{
  // JIT 编译“进入 onEnter 逻辑”的部分
  gum_arm64_writer_put_ldr_reg_address (aw, ARM64_REG_X17, GUM_ADDRESS (ctx));
  gum_arm64_writer_put_ldr_reg_address (aw, ARM64_REG_X16, GUM_ADDRESS (gum_sign_code_pointer (self->enter_thunk)));
  gum_arm64_writer_put_br_reg (aw, ARM64_REG_X16);
 
  // JIT 编译“进入 onLeave 逻辑”的部分
  ctx->on_leave_trampoline = gum_arm64_writer_cur (aw);
 
  gum_arm64_writer_put_ldr_reg_address (aw, ARM64_REG_X17, GUM_ADDRESS (ctx));
  gum_arm64_writer_put_ldr_reg_address (aw, ARM64_REG_X16, GUM_ADDRESS (gum_sign_code_pointer (self->leave_thunk)));
  gum_arm64_writer_put_br_reg (aw, ARM64_REG_X16);
 
  gum_arm64_writer_flush (aw);
}
if (ctx->type != GUM_INTERCEPTOR_TYPE_FAST)
{
  // JIT 编译“进入 onEnter 逻辑”的部分
  gum_arm64_writer_put_ldr_reg_address (aw, ARM64_REG_X17, GUM_ADDRESS (ctx));
  gum_arm64_writer_put_ldr_reg_address (aw, ARM64_REG_X16, GUM_ADDRESS (gum_sign_code_pointer (self->enter_thunk)));
  gum_arm64_writer_put_br_reg (aw, ARM64_REG_X16);
 
  // JIT 编译“进入 onLeave 逻辑”的部分
  ctx->on_leave_trampoline = gum_arm64_writer_cur (aw);
 
  gum_arm64_writer_put_ldr_reg_address (aw, ARM64_REG_X17, GUM_ADDRESS (ctx));
  gum_arm64_writer_put_ldr_reg_address (aw, ARM64_REG_X16, GUM_ADDRESS (gum_sign_code_pointer (self->leave_thunk)));
  gum_arm64_writer_put_br_reg (aw, ARM64_REG_X16);
 
  gum_arm64_writer_flush (aw);
}
//回顾之前分析的代码——第 1 部分(最底层):`gum_emit_enter_thunk` 如下:
 
static void
gum_emit_enter_thunk (GumArm64Writer * aw)
{
  // 1. 发出“函数序言”(Prolog)
  //    这会 JIT 编译出保存所有 CPU 寄存器(X0-X28, FP, LR)到堆栈上的代码。
  //    这是“保护现场”。
  gum_emit_prolog (aw);
 
  // 2. 准备 C 函数的参数 (X1, X2, X3)
  //    在 ARM64 C 语言调用约定中,参数通过 X0, X1, X2, X3... 传递。
  //    X0 稍后会设置(它来自 X17)。
 
  //    ADD X1, SP, ...CPU_CONTEXT
  //    将“指向堆栈上 GumCpuContext(包含了所有寄存器值) 结构的指针”放入 X1(第二个参数)。
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X1, ARM64_REG_SP, GUM_FRAME_OFFSET_CPU_CONTEXT);
  //    ADD X2, SP, ...CpuContext.lr
  //    将“指向堆栈上 lr 寄存器保存位置的指针”放入 X2(第三个参数)。
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X2, ARM64_REG_SP, GUM_FRAME_OFFSET_CPU_CONTEXT + G_STRUCT_OFFSET (GumCpuContext, lr));
  //    ADD X3, SP, ...NEXT_HOP
  //    将“指向堆栈上 next_hop 字段的指针”放入 X3(第四个参数)。
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X3, ARM64_REG_SP,
      GUM_FRAME_OFFSET_NEXT_HOP);
 
  // 3. JIT 编译一个 C 函数调用
  gum_arm64_writer_put_call_address_with_arguments (aw,
      GUM_ADDRESS (_gum_function_context_begin_invocation), 4, // 目标C函数
      // 这个 C 函数将接收 4 个参数:
      //   - Arg 1 (X0) = X17 (这是由 Dispatcher 提前设置好的 GumFunctionContext*,(包含了 `listener` 列表) )
      GUM_ARG_REGISTER, ARM64_REG_X17,
      //   - Arg 2 (X1) = X1 (我们刚设置的 GumCpuContext*,(包含了所有寄存器值))
      GUM_ARG_REGISTER, ARM64_REG_X1,
      //   - Arg 3 (X2) = X2 (我们刚设置的 saved_lr*)
      GUM_ARG_REGISTER, ARM64_REG_X2,
      //   - Arg 4 (X3) = X3 (我们刚设置的 next_hop*)
      GUM_ARG_REGISTER, ARM64_REG_X3);
 
  // 4. 发出“函数终言”(Epilog)
  //    这会 JIT 编译出从堆栈上恢复所有寄存器的代码,并执行 RET(返回)。
  gum_emit_epilog (aw);
}
//回顾之前分析的代码——第 1 部分(最底层):`gum_emit_enter_thunk` 如下:
 
static void
gum_emit_enter_thunk (GumArm64Writer * aw)
{
  // 1. 发出“函数序言”(Prolog)
  //    这会 JIT 编译出保存所有 CPU 寄存器(X0-X28, FP, LR)到堆栈上的代码。
  //    这是“保护现场”。
  gum_emit_prolog (aw);
 
  // 2. 准备 C 函数的参数 (X1, X2, X3)
  //    在 ARM64 C 语言调用约定中,参数通过 X0, X1, X2, X3... 传递。
  //    X0 稍后会设置(它来自 X17)。
 
  //    ADD X1, SP, ...CPU_CONTEXT
  //    将“指向堆栈上 GumCpuContext(包含了所有寄存器值) 结构的指针”放入 X1(第二个参数)。
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X1, ARM64_REG_SP, GUM_FRAME_OFFSET_CPU_CONTEXT);
  //    ADD X2, SP, ...CpuContext.lr
  //    将“指向堆栈上 lr 寄存器保存位置的指针”放入 X2(第三个参数)。
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X2, ARM64_REG_SP, GUM_FRAME_OFFSET_CPU_CONTEXT + G_STRUCT_OFFSET (GumCpuContext, lr));
  //    ADD X3, SP, ...NEXT_HOP
  //    将“指向堆栈上 next_hop 字段的指针”放入 X3(第四个参数)。
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X3, ARM64_REG_SP,
      GUM_FRAME_OFFSET_NEXT_HOP);
 
  // 3. JIT 编译一个 C 函数调用
  gum_arm64_writer_put_call_address_with_arguments (aw,
      GUM_ADDRESS (_gum_function_context_begin_invocation), 4, // 目标C函数
      // 这个 C 函数将接收 4 个参数:
      //   - Arg 1 (X0) = X17 (这是由 Dispatcher 提前设置好的 GumFunctionContext*,(包含了 `listener` 列表) )
      GUM_ARG_REGISTER, ARM64_REG_X17,
      //   - Arg 2 (X1) = X1 (我们刚设置的 GumCpuContext*,(包含了所有寄存器值))
      GUM_ARG_REGISTER, ARM64_REG_X1,
      //   - Arg 3 (X2) = X2 (我们刚设置的 saved_lr*)
      GUM_ARG_REGISTER, ARM64_REG_X2,
      //   - Arg 4 (X3) = X3 (我们刚设置的 next_hop*)
      GUM_ARG_REGISTER, ARM64_REG_X3);
 
  // 4. 发出“函数终言”(Epilog)
  //    这会 JIT 编译出从堆栈上恢复所有寄存器的代码,并执行 RET(返回)。
  gum_emit_epilog (aw);
}
//回顾之前分析的代码——第 2 部分(最底层):`gum_emit_leave_thunk`如下:
 
static void
gum_emit_leave_thunk (GumArm64Writer * aw)
{
  // 1. 发出“函数序言”(Prolog) - 保存所有寄存器
  gum_emit_prolog (aw);
 
  // 2. 准备 C 函数的参数 (X1, X2)
  //    ADD X1, SP, ...CPU_CONTEXT
  //    将 GumCpuContext* 放入 X1(第二个参数)。
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X1, ARM64_REG_SP,
      GUM_FRAME_OFFSET_CPU_CONTEXT);
  //    ADD X2, SP, ...NEXT_HOP
  //    将 next_hop* 放入 X2(第三个参数)。
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X2, ARM64_REG_SP,
      GUM_FRAME_OFFSET_NEXT_HOP);
 
  // 3. JIT 编译一个 C 函数调用
  gum_arm64_writer_put_call_address_with_arguments (aw,
      GUM_ADDRESS (_gum_function_context_end_invocation), 3, // 目标C函数
      // 这个 C 函数将接收 3 个参数:
      //   - Arg 1 (X0) = X17 (GumFunctionContext*)
      GUM_ARG_REGISTER, ARM64_REG_X17,
      //   - Arg 2 (X1) = X1 (GumCpuContext*)
      GUM_ARG_REGISTER, ARM64_REG_X1,
      //   - Arg 2 (X2) = X2 (next_hop*)
      GUM_ARG_REGISTER, ARM64_REG_X2);
 
  // 4. 发出“函数终言”(Epilog) - 恢复所有寄存器并 RET
  gum_emit_epilog (aw);
}
//回顾之前分析的代码——第 2 部分(最底层):`gum_emit_leave_thunk`如下:
 
static void
gum_emit_leave_thunk (GumArm64Writer * aw)
{
  // 1. 发出“函数序言”(Prolog) - 保存所有寄存器
  gum_emit_prolog (aw);
 
  // 2. 准备 C 函数的参数 (X1, X2)
  //    ADD X1, SP, ...CPU_CONTEXT
  //    将 GumCpuContext* 放入 X1(第二个参数)。
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X1, ARM64_REG_SP,
      GUM_FRAME_OFFSET_CPU_CONTEXT);
  //    ADD X2, SP, ...NEXT_HOP
  //    将 next_hop* 放入 X2(第三个参数)。
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X2, ARM64_REG_SP,
      GUM_FRAME_OFFSET_NEXT_HOP);
 
  // 3. JIT 编译一个 C 函数调用
  gum_arm64_writer_put_call_address_with_arguments (aw,
      GUM_ADDRESS (_gum_function_context_end_invocation), 3, // 目标C函数
      // 这个 C 函数将接收 3 个参数:
      //   - Arg 1 (X0) = X17 (GumFunctionContext*)
      GUM_ARG_REGISTER, ARM64_REG_X17,
      //   - Arg 2 (X1) = X1 (GumCpuContext*)
      GUM_ARG_REGISTER, ARM64_REG_X1,
      //   - Arg 2 (X2) = X2 (next_hop*)
      GUM_ARG_REGISTER, ARM64_REG_X2);
 
  // 4. 发出“函数终言”(Epilog) - 恢复所有寄存器并 RET
  gum_emit_epilog (aw);
}
ctx->on_invoke_trampoline = gum_sign_code_pointer (gum_arm64_writer_cur (aw));
 
//开始 **读取** `function_address`(原函数)的指令,并把 **搬运/修复** 后的指令,用“汇编笔” (`aw`) JIT 到我们 **当前的位置**。
gum_arm64_relocator_reset (ar, function_address, aw);
 
signature = g_string_sized_new (16);
 
//“偷”指令
do
{
  const cs_insn * insn;
 
  reloc_bytes = gum_arm64_relocator_read_one (ar, &insn);//读取一条指令
  // ...
  g_string_append (signature, insn->mnemonic);
}
while (reloc_bytes < data->redirect_code_size);
ctx->on_invoke_trampoline = gum_sign_code_pointer (gum_arm64_writer_cur (aw));
 
//开始 **读取** `function_address`(原函数)的指令,并把 **搬运/修复** 后的指令,用“汇编笔” (`aw`) JIT 到我们 **当前的位置**。
gum_arm64_relocator_reset (ar, function_address, aw);
 
signature = g_string_sized_new (16);

[培训]科锐软件逆向54期预科班、正式班开始火爆招生报名啦!!!

最后于 1天前 被教教我吧~编辑 ,原因:
收藏
免费 9
支持
分享
最新回复 (5)
雪    币: 104
活跃值: (6838)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
tql 我反手就是一个三连
2025-11-6 09:20
0
雪    币: 1657
活跃值: (7265)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
用了ai吗?
2025-11-6 14:15
0
雪    币: 116
活跃值: (1562)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
值得怀疑 用了ai吗?
用了,gemini,提升效率
2025-11-6 15:47
0
雪    币: 1607
活跃值: (2072)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
教教我吧~ 用了,gemini,提升效率
强啊  ai写这么多 给我网页都搞卡了
2025-11-6 16:23
0
雪    币: 5444
活跃值: (8925)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
牛啊,感谢分享
6天前
0
游客
登录 | 注册 方可回帖
返回