substitute vs Substrate vs fishhook:iOS/Mac Hook工具终极选型指南
【免费下载链接】substituteA free runtime modification library.项目地址: https://gitcode.com/gh_mirrors/su/substitute
如果你在 iOS 或 Mac 上开发,需要"劫持"系统函数或第三方库的方法,substitute、Substrate 和 fishhook是最常被拿来对比的三款 Hook 工具。其中substitute是一个免费的运行时修改库(libsubstitute),核心能力是在运行时改写函数机器码并安全地重定向调用;Substrate是越狱生态里元老级的代码注入框架;fishhook则是专注于重新绑定动态库符号的轻量工具。这篇文章用大白话讲清三者的原理差异、能力边界和适用场景,帮你 3 分钟做出靠谱的选型决策。
一、三分钟认识这三款 Hook 工具 🧰
substitute:灵活的运行时函数劫持库
substitute(即 libsubstitute)主打"想 hook 谁就 hook 谁"。它的做法是:先反汇编目标函数的开头几字节,把被覆盖的指令搬到一块**跳板(trampoline)**里,再在原函数入口处写入跳转指令,把调用重定向到你提供的替换函数;同时回传一个"旧函数指针",方便你在钩子里继续调用原实现。
除了函数级钩子,它还提供两种更轻量的拦截方式——GOT/PLT 符号拦截和ObjC 消息拦截,全部公共接口都集中在lib/substitute.h中,核心改写逻辑在lib/hook-functions.c,各架构(ARM / ARM64 / x86)的汇编与反汇编实现分别位于lib/arm/、lib/arm64/、lib/x86/目录。
Substrate:越狱生态的元老级框架
Substrate(Cydia Substrate)是越狱 tweak 的"地基",绝大多数 Cydia 插件都建立在它之上。它提供MSHookFunction钩 C 函数、MSHookMessageEx钩 ObjC 方法,并配套一批便捷宏和实例变量访问工具。值得一提的是,substitute 内置了一个兼容层substrate/substrate.h,让你原本写 Substrate 的代码几乎不改就能跑在 substitute 上,迁移成本极低。
fishhook:轻量级动态符号重绑定工具
fishhook 只做一件事:把某个动态库引用的符号,重新绑定到你的实现(例如把malloc、read替换成自定义版本)。它只改 GOT/PLT 表、不碰函数代码,因此体积小巧、上手极快,特别适合做"底层函数透明替换"。
二、原理对比:它们到底"劫持"了什么 🎯
三款工具的差异,根源在于"劫持点"不同。理解了这一点,选型就不再靠感觉:
| 机制 | 代表 | 工作方式 | 副作用 |
|---|---|---|---|
| 内联代码补丁 | substitute、Substrate | 改写函数入口指令 + 跳板转发 | 需要可写可执行内存 |
| GOT/PLT 重绑定 | fishhook、substitute 的 interpose | 修改符号表指向 | 不改代码,但只覆盖动态符号 |
| ObjC 运行时 | Substrate、substitute | 替换方法 IMP | 仅限 ObjC 方法,不碰 C 函数 |
简单说:内联补丁能力最强但"动刀"最深;符号重绑定最温和、可沙盒化;ObjC 运行时最省心,但只能钩到 Objective-C 方法。
三、关键维度横向对比 📊
| 维度 | substitute | Substrate | fishhook |
|---|---|---|---|
| 可劫持范围 | 任意 C/C++ 函数 + ObjC 方法 | C 函数 + ObjC 方法 + 实例变量 | 动态库引用的 C 符号 |
| 是否改写可执行代码 | 是(interpose 除外) | 是 | 否 |
| 沙盒 / 非越狱 iOS | interpose 技术可用 | 需越狱 | 受只读 GOT 限制 |
| 线程安全 | 强(主线程 + 暂停线程) | 较弱 | 较强 |
| 架构支持 | ARM / ARM64 / x86 | 多架构 | ARM / x86_64 为主 |
| 是否依赖越狱 | 否 | 是 | 否 |
| 上手难度 | 中等 | 低(宏丰富) | 低 |
| 维护状态 | 已停更(建议用 fork) | 官方停更 | 稳定但较老 |
四、按场景选型:我该用哪一个?✅
- 想 hook 任意 C/C++ 函数,且要线程安全→ 选substitute,它的
substitute_hook_functions会暂停其他线程、原子地完成补丁,还能回传旧实现指针,是最"全能"的方案。 - 维护老项目 / 越狱 tweak,代码已经用
MSHookFunction→ 选Substrate,或直接用 substitute 的兼容层substrate/substrate.h平滑替换底层。 - 只想替换少数几个 C 函数(如日志、内存分配)→ 选fishhook,代码量最小、最不易出错。
- 钩 Objective-C 方法 / 重写类行为→ 三者皆可,substitute 的
substitute_hook_objc_message和 Substrate 的MSHookMessageEx都能胜任,且都比手写 ObjC 运行时代码干净。 - 要在沙盒(App Store 合规)内做拦截→ 优先看 substitute 的
substitute_interpose_imports或 fishhook,二者都不依赖改写可执行内存。
五、新手避坑指南 ⚠️
- 注意维护状态:substitute 上游已标注"Unmaintained",生产使用建议寻找活跃维护的 fork 版本;Substrate 官方也已停更。选型时把"谁在维护"也纳入考量。
- 主线程限制:substitute 的函数钩子要求在主线程调用(它会暂停其他线程以避免竞态),非主线程调用会直接返回错误码,可用
substitute_strerror把错误码转成可读字符串定位问题。 - 别 hook 太短的函数:当函数开头的可补丁区域内就出现无条件返回等"危险指令"时,内联补丁会失败——这正是 substitute 提供
substitute_interpose_imports这类替代方案的价值所在。 - fishhook 的边界:它只重绑定动态符号,改不了内联调用、静态链接代码,以及在较新 iOS 上只读的 dyld shared cache 里的系统符号。
- 别混用非原子运行时修改:substitute 的文档明确提示,它与 Substrate 这类"非原子替换方法"的实现并存时可能产生竞态,同一进程内尽量统一一套 Hook 方案。
六、substitute 核心 API 速查 🔧
全部接口定义在lib/substitute.h,最常用的几个:
substitute_hook_functions— 内联函数钩子(主线程调用,线程安全,返回旧实现指针)substitute_interpose_imports— GOT/PLT 符号拦截(不改代码、原子操作,技术层面可沙盒)substitute_hook_objc_message— ObjC 方法钩子(基于原子的class_replaceMethod)substitute_open_image/substitute_find_private_syms— 查找已加载镜像中的(私有)符号地址substitute_strerror— 把SUBSTITUTE_ERR_*错误码转成可读字符串
结语
一句话总结:要全能和线程安全选 substitute,要越狱生态与低迁移成本选 Substrate,要轻量替换少数 C 符号选 fishhook。三者并非互斥——理解"内联补丁 / 符号重绑定 / ObjC 运行时"这三种劫持机制,你就能根据目标函数的类型、运行环境(是否越狱、是否沙盒)和线程要求,做出最合适的选择。
【免费下载链接】substituteA free runtime modification library.项目地址: https://gitcode.com/gh_mirrors/su/substitute
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考