首页
社区
课程
招聘
[原创]iOS Hook 终极兵器库:九种拦截机制到硬件级 PAC 绕过的硬核指南
发表于: 2天前 1333

[原创]iOS Hook 终极兵器库:九种拦截机制到硬件级 PAC 绕过的硬核指南

2天前
1333

想要掌控 iOS 程序的底层执行流?本文为你深度拆解四层 Hook 机制,从基础的 OC 运行时交换,一路打穿到极度硬核的 ARM64e PAC 绕过与内核级劫持。硬核剖析 fishhook、Dobby 等工具源码与实战陷阱,助你构建真正的终极逆向兵器库!

Hook,中文叫"钩子",本质是一句话:在不修改源码的前提下,拦截并改变程序的执行流程。从 Objective-C 的方法交换,到 Mach-O 的符号表篡改,再到 ARM64 的指令级劫持——iOS 上的 Hook 技术已经形成了四个层级的能力栈。本文将结合 fishhook、Dobby、ElleKit 等开源工具的源码,从底层原理到实战踩坑,完整拆解这四层机制。

iOS 的代码世界由三种语言交织而成:

为什么不能一个方案通吃?

三个方案不是竞争关系,而是覆盖了三个不同的攻击面。而 Substrate、libhooker、ElleKit 这类框架,本质上是对这三层的统一封装

在拆解具体方案前,定义衡量标准:

行话叫"三层穿透"——穿透语言层(OC)、穿透符号层(C 的外部链接)、穿透地址层(只剩下一个无名地址)。

面对一个 hook 需求,按以下顺序做决策:

图片描述

核心取舍

图片描述

iOS 15 分水岭:dyld4 用 LC_DYLD_CHAINED_FIXUPS 彻底替换了 LC_DYLD_INFO。Chained Fixups 改变了符号绑定的方式——把 bind/rebase 信息编码成一个自引用的链表,每个链节点(dyld_chained_ptr_* 结构,具体格式因 pointer format 不同而异,如 DYLD_CHAINED_PTR_64 / ARM64E / ARM64E_USERLAND)包含 next 字段用于指向链中下一个待修复节点。但 __got / __auth_got / __la_symbol_ptr 等 section 仍然存在于 Mach-O 中,只是 dyld 不再通过 LC_DYLD_INFO 的 opcode 来填充它们,而是通过 Chained Fixups 链表逐节点绑定。fishhook 或其他 GOT 解析工具如果只认识 LC_DYLD_INFO,在 iOS 15+ 二进制上将直接解析扑空。现代 fishhook 必须先识别 LC_DYLD_CHAINED_FIXUPS,深入 __LINKEDIT 逐节点遍历链表,才能定位并替换目标符号。

任何 ARM64 C 函数的开头都是标准序言:

Inline Hook 做的事情,是把第一条指令(4 字节)替换成一条无条件跳转:

ARM64 的 b 指令跳转范围是 ±128MB——如果目标函数超出这个范围,就需要用更复杂的间接跳转(adrp + br),这也是 ElleKit 等框架区分"远程 hook"并通过异常处理路径来解决的原因。

关键陷阱method_exchangeImplementations全局交换,影响所有调用者。class_addMethodclass_replaceMethod 同样作用在类对象 (Class) 层面,会波及该类的所有实例。如果真正需要只 hook 单个实例对象,业界做法是 ISA Swizzling(类比系统的 KVO 机制):动态创建一个该类的子类,重写目标方法,然后将那个特定实例的 isa 指针指向这个动态子类。

为什么 fishhook 不能 hook 静态函数? 因为静态函数(static void foo())不经过 GOT——它的地址在编译时就确定了,代码里用 bl #imm26 直接跳转,dyld 不参与绑定。GOT 只存外部符号的地址。

Dobby 最难的不是写跳转指令,而是处理边界条件——被覆盖的指令恰好在调用 mprotect 的范围内?目标函数只有不到 4 字节?ARM64e 的 PAC 要不要一起处理?这些才是 Inline Hook 的真正门槛。

PAC(Pointer Authentication Code) 是 Apple 从 A12(iPhone XS/XR)和 iOS 12 开始引入的硬件级指针完整性保护。它的核心思想很简单:

每一个指针(函数地址、返回地址)在存储时,CPU 自动在指针的高位嵌入一个基于密钥生成的 加密签名(PAC)。当这个指针被用于跳转时,CPU 用同样的密钥重新计算签名并比对——不匹配就触发 PAC/AUTH exception,进程直接崩溃。

PAC 签名长度并不固定:PAC 的可用位数依赖虚拟地址布局(VA_BITS)、TBI(Top Byte Ignore)配置以及具体 PAC 类型(IA/IB/DA/DB/GA),公开研究通常观察到 PAC 占用十几到二十余位高位空间,并非固定的 7 bit。

PAC 使用了 5 个硬件密钥(IA KeyIB KeyDA KeyDB KeyGA Key)。从外部行为观察,同一 Task 内的线程之间可以成功共享 PAC 签名指针——这意味着实际实现中跨线程 PAC 认证必然成立(否则 GCD 等多线程机制将崩溃),通常推测 XNU 在进程创建(exec / spawn)时初始化 PAC 密钥并在 Task 内所有线程间共享。线程上下文切换时,内核将 PAC 密钥重新加载到 APIAKeyLo_EL1 等系统寄存器中——内核(EL1)本身就是密钥的管理者。真正无法读取这些密钥的是用户态程序(EL0),因为 PAC 寄存器是 EL1 特权资源,EL0 访问直接触发异常。注:Apple 未公开完整 PAC 密钥管理实现,以上基于外部行为推断。

为什么不能是线程级密钥? 如果 PAC 密钥是线程专属的,线程 A 签名的函数或数据指针(例如通过 GCD 派发到子线程生成的对象回调)传递给线程 B 时,就会因签名密钥不匹配而直接触发 PAC/AUTH exception——大量跨线程传递的已签名指针将无法通过认证,这与现有系统行为(GCD、block 回调等正常运作)明显不符。

签名算法方面:ARM 架构定义了 PAC 密码学原语(如 QARMA-64),但 Apple 的具体实现细节(密钥派生、算法选择、是否使用自研算法)从未公开。关于"A12 用 QARMA、A14 起切换为自研算法"的说法在公开资料中缺乏直接证据,属于技术社区的推测。

回顾 Inline Hook 的核心操作:在目标函数入口写入 b replacement_addr,把执行流引向自定义函数。在 ARM64 下,这只是一条 4 字节的 b 指令,地址是普通指针,随便写。

但在 ARM64e(PAC 开启)下,问题来了:

用一句话总结:PAC 保护的是"指针"而非"指令"——b(PC-relative 直接编码)天然不受 PAC 影响;真正受 PAC 约束的是通过寄存器携带的间接跳转目标(如 BRAABLRAARETAA)。

关键区分BLR / BR / RET 在 ARM64e 下语义不变,不验证 PAC。只有当编译器/链接器显式使用 BLRAA / BRAA / RETAA 等带认证后缀的指令时,CPU 才会执行 PAC 校验。Inline Hook 如果写入普通 BLR,在 ARM64e 上不会被 PAC 拦截。

这是 ARM64e PAC 设计的关键细节:

PAC 保护的核心对象是指针值,而非跳转指令本身。b 指令的目标地址直接编码在指令字中(PC-relative immediate),不存在"指针"概念,因此 PAC 机制完全不参与。BRAA / BLRAA / RETAA 等带认证后缀的指令才是 PAC 的检查点。

现象:越狱设备装了两个 tweak,都 hook 了 -[UIApplication openURL:]。一个 tweak 工作正常,另一个间歇性失效。两个单独用时都正常,一起装就崩。

排查:两个 tweak 都在 +load 方法里做 Method Swizzling。问题在于:tweak A hook 后,把原始 IMP 保存到 orig_openURL。tweak B 也 hook,但 B 拿到的是 A 已经改过的 IMP。当 A 调用 orig_openURL 时,实际执行的是 B 的 hook 层——如果 B 的实现不是"可重入"的(即不支持被链式调用),就 crash。

修复:ElleKit 在 API 层面做了链式调用兼容——每个 hook 返回的 orig 指针自动指向"前一层",形成调用链。但如果 tweak 自己用 Runtime API 直接交换,链式不会自动成立。

教训Method Swizzling 的 orig 不是你自己的 orig,是你前面的 hook 者的 orig

⚠️ 严禁在 Hook 方法内调用 class_getMethodImplementation 来获取"原始 IMP"——因为交换后 Runtime 返回的正是当前已经被 Hook 的 IMP(即你自身或链顶层)。如果你在 Hook 内部调用它,它会再次跳入你的 Hook,瞬间触发无限递归栈溢出。正确的做法只有两种:

现象:想用 fishhook hook ptrace 来防止 App 被调试,但 rebind_symbols 返回 0(成功),实际调用 ptrace 时却完全没有被拦截。

排查ptrace 的实现在 libsystem_kernel.dylib 中。如果 App 显式声明并调用了 int ptrace(int, pid_t, caddr_t, int),编译器和链接器会正常生成 GOT 表项——不存在"跨模块内联"的可能,因为编译器在编译 App 时根本没有 libsystem_kernel 的源码或中间码。

真正的原因是:安全开发者为了防 fishhook,主动避开了 GOT。最常用的两种手段:

修复:对这类"半内联"的符号,fishhook 无能为力。需要改用 Inline Hook(Dobby)直接 patch ptrace 的函数入口地址。

教训fishhook 只能 hook"老老实实走 GOT"的外部符号,而安全开发者会用 syscall()svc 内联汇编主动绕开 GOT。 对抗 fishhook 比对抗 Inline Hook 容易得多——换一个调用方式就够了。这也是为什么高级反 Hook 必须走到 Inline Hook 或硬件断点层级。

现象:某 App 的私有 C++ 函数,用 Dobby hook 后 App 启动 crash。崩溃日志显示 EXC_BAD_ACCESS,地址落在一个奇怪的只读区域。

排查:> 以下为 Inline Hook 跨页边界问题的示意推演案例。被 hook 的函数入口地址是 0x100013FF0——距离代码段最后一页的边界只有 16 字节。iOS ARM64 的虚拟内存页大小通常为 16KB (0x4000)(但也有 4KB 的情况,应通过 vm_page_size 运行时查询),mprotect 作用于整页(0x100010000-0x100013FFF),但跳转指令(adrp + add + br)占 12 字节,剩余 4 字节不够写完。更致命的是,下一页面(0x100014000)属于 __DATA 段,权限本来就是 rw-——Inline Hook 框架的 mprotect 逻辑若假设目标函数始终落在连续的代码段页内,跨页边界的情况就需要特殊处理。

修复:用一个简单的页对齐前检查:如果函数尾部的所有 hook 指令能落在当前页内,正常 hook;如果跨页,则把 hook 指令写在 trampoline 里,目标函数入口改为 b trampoline

教训写 Inline Hook 框架,最难的不是 ARM64 编码,而是把"你以为连续的东西"和"实际物理布局"之间的差异全部处理掉。

前面 8 节覆盖了主流的 OC Swizzling / GOT / Inline Hook 三条路线。但在极端的对抗场景下——尤其是深入C++ 引擎、Mach 异常调度、或内核级攻防时——还有四种相对小众但杀伤力极大的手段。

在逆向以 C++ 为主的引擎(Unity IL2CPP、Unreal Engine,或系统底层的 WebKit / JavaScriptCore)时,常规的 fishhook 和 OC Swizzling 统统失效——因为它们面向的是 C 符号和 ObjC Runtime,而 C++ 的多态走的是完全不同的路径。

原理:包含虚函数的 C++ 对象,其内存布局的首 8 字节是一个指向**虚函数表(VTable)**的指针 vptr。VTable 是一块连续内存,按声明顺序存储各个虚函数的地址。调用 obj->foo() 的汇编等价于:

攻击手段一:替换单个对象的 vptr

隐蔽性:只修改堆内存(对象的 vptr)或数据段,完全不碰代码段(__TEXT)。传统的代码段哈希校验、sys_icache_invalidate 扫描对它毫无察觉——这是反外挂引擎的盲区。

攻击手段二:直接修改 VTable 内存(影响该类的所有实例)

如果能通过内核漏洞获得对 VTable 所在页的写权限,直接覆盖 VTable 中的函数指针,将对一个类的所有实例生效。这种方式更粗暴,但需要注意:在 ARM64e 二进制中,VTable 中的函数指针也可能启用 PAC 保护(取决于编译器、编译选项和函数类型),直接覆盖可能触发认证失败。

硬件断点 Hook(hwbphook)利用 CPU 调试寄存器,而软件异常同样可以作为无痕 Hook 引擎

原理

隐蔽性优势

代价:每次拦截触发一次 Mach 消息往返(用户态 → 内核 → 用户态),性能开销远大于 Inline Hook。高频调用的函数不适合此方案。

如果想做全局行为打点——比如监控整个 App 内所有 OC 方法的调用,一个个 Swizzle 太慢且容易遗漏。最高效的方式是直接劫持消息发送的入口。

原理:Objective-C 的每一条 [obj method] 调用,编译器最终都会转化为对 objc_msgSend 的调用。objc_msgSend 是纯汇编写的(必须保护所有寄存器状态以平滑传递给实际 IMP)。用 Dobby 拦截 objc_msgSend,在替换函数中保存现场、记录调用、恢复现场后跳回原函数。

开源项目 InspectiveC(David Goldman, 3a0K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6V1j5i4k6A6k6r3N6G2L8r3c8E0j5h3&6Q4x3V1k6u0L8Y4y4H3k6h3y4@1K9i4k6W2b7#2!0q4c8W2!0n7b7#2)9^5z5g2!0q4y4g2!0n7x3q4!0n7x3g2!0q4y4W2)9&6z5q4!0m8c8W2!0q4z5q4!0n7c8W2)9&6z5g2!0q4y4q4!0n7z5q4)9^5x3q4!0q4y4W2)9^5x3q4)9&6c8q4!0q4z5q4!0n7y4#2!0m8c8W2!0q4y4#2)9&6b7g2)9^5y4q4!0q4y4#2!0n7b7W2)9^5c8W2!0q4y4g2)9^5y4g2!0n7z5q4!0q4y4g2!0m8c8g2)9&6c8g2!0q4y4#2)9^5c8g2!0n7x3q4!0q4c8W2!0n7b7#2)9&6b7b7`.`.

注意objc_msgSend 的 hook 性能开销极大(每条 OC 调用都经过拦截点),只适合离线分析、调试和打点,不适合线上持续运行。

当对抗级别上升到最高——攻击者掌握了 tfp0(Task For Pid 0,即内核任务端口)或 Kernel R/W 时,用户态的所有防御(PAC、代码段校验、App 层 syscall 拦截)全部失效。

Syscall Table Patching:直接修改内核的 sysent(系统调用入口表),将 sys_opensys_ptrace 等条目的函数指针替换为内核模块中的自定义函数。此后所有用户态进程的对应系统调用都被拦截,甚至绕过了 __RESTRICT 段和 DYLD_INSERT_LIBRARIES 的所有限制。

MACF(Mandatory Access Control Framework)Hook:XNU 内核内置了 MAC 机制(源自 FreeBSD)。攻击者可以注册恶意的 MAC Policy,在 VFS 层(文件读写)、proc_check 层(进程创建/调试)、Socket 层实现比用户态彻底无数倍的拦截。MACF Hook 的一个关键特征是:它运行在 内核上下文 中,用户态的任何反 Hook 手段(代码段校验、Inspection、sysctl 扫描)都对它完全不可见。

内核级 Hook 需要 tfp0 或等效的 Kernel R/W 原语,通常由越狱工具的内核漏洞利用提供。在非越狱设备上无法实现,但这不意味着它不重要——它定义了 Hook 技术的理论上限。

三个趋势信号:

ARM64e PAC 的适配深化:ElleKit 已在大量 A12+ 设备上稳定运行多年(Dopamine/palera1n 默认使用),但 ARM64e 的 PAC 机制仍然给 Inline Hook 带来额外复杂度——尤其是在涉及 PAC 返回链和远程跳转的场景下

硬件断点成为必备武器:hwbphook 的价值不在于日常 hook,而在于"最后一公里"——当 App 用了代码完整性校验 + PAC + BTI 三重防护,硬件断点仍然是可工作的手段之一(此外还有 Mach Exception Hook、VTable Hook、GOT Hook 等路径)

无需传统越狱的注入能力崛起:TrollStore + Bootstrap 的组合(依赖 CoreTrust 漏洞)让设备无需完整越狱即可获得类似 tweak 的 dylib 注入能力,hook 工具的战场从"越狱环境"扩展到了更广义的系统应用级权限环境

method_exchangeImplementationsDobbyHook,从 GOT 表到硬件断点(DBGBVR/DBGBCR)、Mach Exception Hook——iOS Hook 工具链演进了 15 年,不变的核心只有一句话:改变原本的控制流目标,让它指向你指定的位置。 理解了这一点,所有 Hook 框架都只是这条原理的不同实现。

[1] fishhook — Facebook, 7b0K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6X3j5h3y4W2j5X3!0G2K9#2)9J5c8X3k6A6M7$3S2Z5L8$3!0C8
[2] Dobby — jmpews, 820K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6B7L8i4m8W2N6%4y4Q4x3V1k6p5L8$3u0T1P5b7`.`.
[3] ElleKit — tealbathingsuit, 307K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6@1k6h3q4D9j5X3q4@1K9r3W2F1k6%4y4#2K9i4c8Q4x3V1k6W2L8r3I4W2K9$3W2@1
[4] litehook — opa334, ac1K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6G2M7r3p5K6x3K6c8Q4x3V1k6D9K9i4c8W2K9r3!0G2K9H3`.`.
[5] ElleKit — The Apple Wiki, d18K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6@1K9r3g2S2M7s2m8D9k6i4N6A6K9$3W2Q4x3X3g2U0L8$3#2Q4x3V1k6%4K9h3E0A6i4K6u0r3c8h3I4D9k6f1E0A6N6l9`.`.
[6] Cydia Substrate — Saurik, 4b0K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3y4&6k6r3W2S2M7%4g2T1M7%4c8J5j5i4c8W2i4K6u0W2j5$3!0E0i4K6u0r3
[7] Objective-C Runtime Programming Guide — Apple, 513K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1k6i4k6W2L8r3!0H3k6i4u0Q4x3X3g2S2M7s2m8D9k6g2)9J5k6h3y4G2L8g2)9J5c8X3c8G2j5%4g2E0k6h3&6@1j5i4c8A6L8$3&6Q4x3V1k6G2j5X3A6W2j5%4c8A6N6X3g2U0i4K6u0r3L8$3u0B7k6h3y4@1K9i4k6W2i4K6u0V1j5#2)9#2k6Y4u0#2L8Y4c8A6L8h3f1`.
[8] Mach-O Programming Topics — Apple (Archived)
[9] ARM Architecture Reference Manual — ARM Ltd.
[10] iOS App Hook — Urinx, d05K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6g2M7X3W2F1P5q4)9J5c8X3W2a6f1@1q4H3M7p5S2G2L8$3D9`.
[11] fishhook 原理深度剖析 — 掘金, e96K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6B7N6h3g2B7K9h3&6Q4x3X3g2U0L8W2)9J5c8Y4m8G2M7%4c8Q4x3V1j5%4y4e0V1$3z5e0M7#2x3o6x3#2y4o6x3^5y4K6V1J5y4K6p5K6
[12] Dobby 框架源码学习 — 看雪, https://bbs.kanxue.com/thread-280661.htm
[13] iOS Hook 框架终极对决 — CSDN, 360K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6T1L8r3!0Y4i4K6u0W2j5%4y4V1L8W2)9J5k6h3&6W2N6q4)9J5c8X3N6A6N6r3u0D9L8$3N6Q4y4h3j5H3x3o6x3#2z5q4)9J5c8X3q4J5N6r3W2U0L8r3g2Q4x3V1k6V1k6i4c8S2K9h3I4K6i4K6u0r3x3e0f1J5y4e0l9J5x3U0f1^5

声明:本文仅从技术角度分析 iOS Hook 工具的工作原理,旨在帮助安全研究人员和 iOS 开发者理解运行时修改与代码注入的安全机制。Hook 技术可能违反 App 的使用条款,请确保你的行为符合当地法律法规和软件许可协议。

语言层 绑定方式 典型 Hook 目标 经典工具
Objective-C 动态运行时,SEL → IMP 查表 [UIApplication openURL:][NSFileManager fileExistsAtPath:] Method Swizzling、Substrate
C / C++ 静态链接 + 动态链接,GOT/PLT 符号绑定 open()ptrace()sysctl() fishhook、GOT rebinding
纯地址(无符号) 编译时固定地址,无运行时符号表 私有 C++ 函数、内联函数、strip 后的函数 Inline Hook(Dobby、litehook)
维度 指标 含义
覆盖率 能 hook 的目标类型 OC 方法?C 函数?私有符号?strip 后的函数?
安全性 crash 风险、线程安全性 并发调用会不会崩?改错字节能不能恢复?
穿透性 绕过 hook 检测的能力 越狱检测 / 反 Hook 机制能不能发现?
工具 原理 覆盖层 代表用途
Method Swizzling ObjC Runtime SEL-IMP 交换 OC 层 系统 API 行为拦截
fishhook Mach-O GOT 符号重绑定 C 层(外部符号) Hook 系统 C 函数
Dobby ARM64 Inline Hook 全层(任意地址) 无符号私有函数
Cydia Substrate 统一框架(OC+C+Inline) 全层 tweak 标准 hook 环境
Substitute Substrate 的开源社区替代(Rootful 时代) 全层 Electra/unc0ver/Chimera 基板
libhooker 统一下层 hook 引擎 全层 Taurine/Odyssey/Odysseyra1n
ElleKit SW JIT 汇编器 + 内存 patch 全层 Dopamine/palera1n 基板
litehook 分支跳转 + GOT 重绑定 C 层 + 地址层 轻量级符号查找+hook
hwbphook ARM 硬件断点(watchpoint) 全层(无痕) 绕过内存校验检测
jrmemory VMMap 直接改内存页 全层 绕过代码签名保护
replaceMethod 直接替换 class 的方法列表 OC 层 简单 OC 方法替换
CaptainHook 基于 Substrate 的声明式 hook OC + C 层 Logos 语法的底层引擎
Hook 操作 ARM64(正常) ARM64e(PAC 开启)
写入 b replacement ✅ 直接跳转 b 是 PC-relative 直接编码,不经过指针,PAC 不参与,近程跳转不受影响
构造 trampoline(间接跳转路径) ✅ 复制指令 + b orig ⚠️ 若 trampoline 涉及指针间接跳转(如 blr x16)且指向签名指针,则需 PAC;若走 b 直接跳转则不受影响
从 trampoline 跳回原函数 ✅ 直接跳转 ⚠️ 取决于跳转方式:PC-relative b 安全,通过签名指针间接跳转需要合法 PAC
工具 PAC 支持 原理
ElleKit ✅ 部分支持 近程 hook 仍然用 b(±128MB 范围),因为 b 是 PC-relative 直接编码,不经过指针,不受 PAC 影响。远程 hook(超出 ±128MB 范围)通常借助异常处理机制绕过 PAC 限制。
Dobby ⚠️ 有限支持 对 arm64e 目标的近程 hook 可用,远程 hook 未完整适配。部分 trampoline 实现可能涉及 PAC 返回链(取决于是否走 RETAA/编译器生成代码/LR 签名),需要额外处理。
litehook ⚠️ 支持有限 近程分支跳转在 arm64/arm64e 上可用,但对 ARM64e/PAC 场景支持有限,不同版本差异较大。
fishhook ⚠️ 需手动处理 PAC 签名 在 ARM64e + iOS 14+ 的 __AUTH_CONST.__auth_got 中,GOT 表项存储的函数指针可能受到 PAC 保护。是否需要重新签名取决于 bind 类型、discriminator、key 类型和 dyld 生成方式。一种常见做法是参照 dyld 的签名逻辑:ptrauth_sign_unauthenticated() 配合正确的 discriminator 对替换地址签名后再写入 GOT。但并非所有 __auth_got 条目都需要此操作。
hwbp hook ✅ 不受影响 硬件断点 Hook 利用 CPU 调试寄存器(DBGBVR/DBGBCR)在目标地址设置硬件指令断点,完全不修改内存,因此对 PAC、代码签名校验等对内存敏感的防护手段天然免疫。注意:这与软件断点(brk 指令 + Mach Exception)是两个不同机制。
Substrate / Substitute / libhooker ⚠️ 旧版不支持 这些框架诞生于 A11 时代,原生不支持 PAC。需要通过 ElleKit 这类新引擎间接获得 PAC 兼容性。
工具 架构 代表使用者
Cydia Substrate OC+C+Inline 统一 Cydia 时代所有 tweak
Substitute Substrate 的开源社区替代(Rootful 时代) Electra / unc0ver / Chimera
libhooker 双层 hook 引擎,Swift 基板 Taurine / Odyssey / Odysseyra1n
fishhook GOT 符号重绑定 非越狱 hook(MonkeyDev)
Dobby 多平台 Inline Hook 越狱/安全研究/脱壳
ElleKit SW JIT 汇编 + 远程 hook Dopamine / palera1n
litehook 分支跳转 + GOT 重绑定 轻量级场景
// 一行代码,整个 App 里所有对 [NSDate date] 的调用全部返回 2020-01-01
Method original = class_getClassMethod([NSDate class], @selector(date));
Method fake     = class_getClassMethod([NSDate class], @selector(fakeDate));
method_exchangeImplementations(original, fake);
; 典型的 ARM64 函数序言(函数入口第一条指令)
sub  sp, sp, #0x30     ; 分配栈空间
stp  x29, x30, [sp, #0x20]  ; 保存 frame pointer 和 link register
stp  x20, x19, [sp, #0x10]
...

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 26
打赏
分享
最新回复 (18)
雪    币: 7
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2天前
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
66666
2天前
0
雪    币: 2868
活跃值: (5512)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
666
2天前
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
666
2天前
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
6666
2天前
0
雪    币: 3001
活跃值: (2540)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
7
666
2天前
0
雪    币: 411
活跃值: (161)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
6
2天前
0
雪    币: 4694
活跃值: (5530)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
6
2天前
0
雪    币: 6832
活跃值: (6865)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
66
2天前
0
雪    币: 4974
活跃值: (5682)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
11
感谢分享。
2天前
0
雪    币: 22
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12
感谢分享。
2天前
0
雪    币: 4246
活跃值: (7017)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
13
感谢分享
2天前
0
雪    币: 346
活跃值: (2365)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
学习下
1天前
0
雪    币: 104
活跃值: (8742)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
tql
1天前
0
雪    币: 2469
活跃值: (2890)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
学习一下
1天前
0
雪    币: 38
活跃值: (2999)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
17
学习
1天前
0
雪    币: 200
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
18
666
1天前
0
雪    币: 207
活跃值: (1811)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
tql
21小时前
0
游客
登录 | 注册 方可回帖
返回