学习 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_enter 和 listener->on_leave 指向你自己的 C 函数。
设置 user_data:把你的 上下文 (Context) 地址赋值给 listener->user_data。这是连接回调函数和你的数据的桥梁。
调用 gum_interceptor_attach:传入目标函数和配置好的监听器,执行 Hook。
处理结果:
可以注意到,其中 on_enter 和 on_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_thunk 和 leave_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_thunk 和 leave_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_value 和 calling_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_level。gum_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 是一个布尔值(true 或 false),它的意思是“是否要在离开时设置陷阱?”
所以,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_id 和 ignore_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_invocation 是 Interceptor 的“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代码中 X17 在 enter_thunk 中被用作第一个参数)。
LDR X16, [enter_thunk_addr]:JIT 一条指令,把我们 预先造好 的 enter_thunk(还记得吗?那个 C 语言桥梁)的地址加载到 X16。
BR X16:JIT 一条指令,JUMP 到 enter_thunk。

实际调试演示:
特征检测相关文章:
onLeave 部分:
ctx->on_leave_trampoline = ...:“标记 onLeave 的入口!”
下面 JIT 了 一模一样 的代码,只是它加载的是 leave_thunk 的地址。(然后回顾之前分析的 第 2 部分(最底层):gum_emit_leave_thunk代码中 X17 在 leave_thunk 中被用作第一个参数)。
至此,“总调度中心” (Dispatcher) 建造完毕。 它包含两个部分,分别用于 onEnter 和 onLeave。
这就是我们所有分析的“风暴中心”! 这就是对你最初 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。它只是一个“外貌特征”,这个“外貌”足以**“解锁”**那个真正会去检查 LR 的 while 循环。
问题 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)
{
const gsize code_size_in_pages = 1;
gsize code_size;
GumEmitLrFuncContext ctx;
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.func = NULL;
ctx.caller_lr = 0;
gum_memory_patch_code (ctx.code, code_size, gum_emit_lr_func, &ctx);
TESTCASE (attach_to_function_reading_lr)
{
const gsize code_size_in_pages = 1;
gsize code_size;
GumEmitLrFuncContext ctx;
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.func = NULL;
ctx.caller_lr = 0;
gum_memory_patch_code (ctx.code, code_size, gum_emit_lr_func, &ctx);
g_assert_cmphex (ctx.run (), ==, ctx.caller_lr);
g_assert_cmphex (ctx.run (), ==, ctx.caller_lr);
interceptor_fixture_attach (fixture, 0, ctx.func, '>', '<');
interceptor_fixture_attach (fixture, 0, ctx.func, '>', '<');
g_assert_cmphex (ctx.run (), !=, ctx.caller_lr);
g_assert_cmpstr (fixture->result->str, ==, "><");
g_assert_cmphex (ctx.run (), !=, ctx.caller_lr);
g_assert_cmpstr (fixture->result->str, ==, "><");
interceptor_fixture_detach (fixture, 0);
gum_free_pages (ctx.code);
}
interceptor_fixture_detach (fixture, 0);
gum_free_pages (ctx.code);
}
TESTCASE (attach_to_thunk_reading_lr)
{
const gsize code_size_in_pages = 1;
gsize code_size;
GumEmitLrThunkContext ctx;
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;
gum_memory_patch_code (ctx.code, code_size, gum_emit_lr_thunk, &ctx);
TESTCASE (attach_to_thunk_reading_lr)
{
const gsize code_size_in_pages = 1;
gsize code_size;
GumEmitLrThunkContext ctx;
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;
gum_memory_patch_code (ctx.code, code_size, gum_emit_lr_thunk, &ctx);
g_assert_cmphex (ctx.run (), ==, ctx.expected_lr);
g_assert_cmphex (ctx.run (), ==, ctx.expected_lr);
interceptor_fixture_attach (fixture, 0, ctx.thunk, '>', '<');
interceptor_fixture_attach (fixture, 0, ctx.thunk, '>', '<');
g_assert_cmphex (ctx.run (), ==, ctx.expected_lr);
g_assert_cmpstr (fixture->result->str, ==, "><");
g_assert_cmphex (ctx.run (), ==, ctx.expected_lr);
g_assert_cmpstr (fixture->result->str, ==, "><");
interceptor_fixture_detach (fixture, 0);
gum_free_pages (ctx.code);
}
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;
ctx = h->listener_context[listener_index];
if (ctx != NULL)
{
arm64_listener_context_free (ctx);
h->listener_context[listener_index] = NULL;
}
ctx = h->listener_context[listener_index];
if (ctx != NULL)
{
arm64_listener_context_free (ctx);
h->listener_context[listener_index] = NULL;
}
ctx = g_slice_new0 (Arm64ListenerContext);
ctx = g_slice_new0 (Arm64ListenerContext);
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;
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;
ctx->fixture = h;
ctx->enter_char = enter_char;
ctx->leave_char = leave_char;
ctx->fixture = h;
ctx->enter_char = enter_char;
ctx->leave_char = leave_char;
result = gum_interceptor_attach (h->interceptor, test_func,
GUM_INVOCATION_LISTENER (ctx->listener), NULL,
GUM_ATTACH_FLAGS_NONE);
result = gum_interceptor_attach (h->interceptor, test_func,
GUM_INVOCATION_LISTENER (ctx->listener), NULL,
GUM_ATTACH_FLAGS_NONE);
if (result == GUM_ATTACH_OK)
{
h->listener_context[listener_index] = ctx;
}
else
{
arm64_listener_context_free (ctx);
}
return result;
}
if (result == GUM_ATTACH_OK)
{
h->listener_context[listener_index] = ctx;
}
else
{
arm64_listener_context_free (ctx);
}
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;
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;
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:
{
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:
{
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);
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);
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);
}
if (self->backend == NULL)
{
self->backend = _gum_interceptor_backend_create(&self->mutex, &self->allocator);
}
ctx = gum_function_context_new (self, function_address, type);
if (gum_process_get_code_signing_policy () == GUM_CODE_SIGNING_REQUIRED)
{
if (!_gum_interceptor_backend_claim_grafted_trampoline (self->backend, ctx))
goto policy_violation;
}
else
{
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)
{
if (!_gum_interceptor_backend_claim_grafted_trampoline (self->backend, ctx))
goto policy_violation;
}
else
{
if (!_gum_interceptor_backend_create_trampoline (self->backend, ctx))
goto wrong_signature;
}
g_hash_table_insert (self->function_by_address, function_address, ctx);
gum_interceptor_transaction_schedule_update (&self->current_transaction, ctx,gum_interceptor_activate);
return ctx;
g_hash_table_insert (self->function_by_address, function_address, ctx);
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->mutex = mutex;
backend->allocator = allocator;
GumInterceptorBackend *
_gum_interceptor_backend_create (GRecMutex * mutex,
GumCodeAllocator * allocator)
{
GumInterceptorBackend * backend;
backend = g_slice_new0 (GumInterceptorBackend);
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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_enter_thunk (GumArm64Writer * aw)
{
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);
}
; 步骤 C: 保存所有寄存器(包括 X17)到堆栈
; (gum_emit_prolog)
; 步骤 D: 准备 C 函数的第 2、3、4 个参数 (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 函数的第 2、3、4 个参数 (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)
{
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_leave_thunk (GumArm64Writer * aw)
{
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_thunks (gpointer mem,
GumInterceptorBackend * self)
{
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_thunks (gpointer mem,
GumInterceptorBackend * self)
{
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_thunks (gpointer mem,
GumInterceptorBackend * self)
{
GumArm64Writer * aw = &self->writer;
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_thunks (gpointer mem,
GumInterceptorBackend * self)
{
GumArm64Writer * aw = &self->writer;
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_interceptor_backend_create_thunks (GumInterceptorBackend * self)
{
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_create_thunks (GumInterceptorBackend * self)
{
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);
}
<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);
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)
{
invocation_ctx = &stack_entry->invocation_context;
}
will_trap_on_leave = function_ctx->replacement_function != NULL || (invoke_listeners && function_ctx->has_on_leave_listener);
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)
{
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++)
{
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++)
{
if (listener_entry->listener_interface->on_enter != NULL)
{
listener_entry->listener_interface->on_enter (
listener_entry->listener_instance, invocation_ctx);
}
}
}
ctx->listener->on_enter = (TestCallbackListenerFunc) arm64_listener_context_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)
{
*next_hop = function_ctx->replacement_function;
}
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)
{
*next_hop = function_ctx->replacement_function;
}
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;
invocation_ctx->backend = &interceptor_ctx->listener_backend;
invocation_ctx = &stack_entry->invocation_context;
invocation_ctx->cpu_context = cpu_context;
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++)
{
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++)
{
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;
gum_arm64_writer_reset (aw, ctx->trampoline_slice->data);
if (ctx->type == GUM_INTERCEPTOR_TYPE_FAST)
{
deflector_target = ctx->replacement_function;
}
else
{
ctx->on_enter_trampoline = gum_sign_code_pointer (gum_arm64_writer_cur (aw));
deflector_target = ctx->on_enter_trampoline;
}
if (need_deflector)
{
}
if (!gum_interceptor_backend_prepare_trampoline (self, ctx, &need_deflector))
return FALSE;
gum_arm64_writer_reset (aw, ctx->trampoline_slice->data);
if (ctx->type == GUM_INTERCEPTOR_TYPE_FAST)
{
deflector_target = ctx->replacement_function;
}
else
{
ctx->on_enter_trampoline = gum_sign_code_pointer (gum_arm64_writer_cur (aw));
deflector_target = ctx->on_enter_trampoline;
}
if (need_deflector)
{
}
if (ctx->type != GUM_INTERCEPTOR_TYPE_FAST)
{
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);
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)
{
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);
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);
}
static void
gum_emit_enter_thunk (GumArm64Writer * aw)
{
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_enter_thunk (GumArm64Writer * aw)
{
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)
{
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_leave_thunk (GumArm64Writer * aw)
{
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);
}
ctx->on_invoke_trampoline = gum_sign_code_pointer (gum_arm64_writer_cur (aw));
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));
gum_arm64_relocator_reset (ar, function_address, aw);
signature = g_string_sized_new (16);
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!
最后于 2025-11-12 09:11
被教教我吧~编辑
,原因: