公司动态

substitute_hook_functions实战教程:完整API详解与全部13个错误码速查清单

📅 2026/8/22 14:42:58
substitute_hook_functions实战教程:完整API详解与全部13个错误码速查清单
substitute_hook_functions实战教程完整API详解与全部13个错误码速查清单【免费下载链接】substituteA free runtime modification library.项目地址: https://gitcode.com/gh_mirrors/su/substitutesubstitute是一款免费的运行时修改runtime modification开源库而其核心函数substitute_hook_functions就是整个库的钩子引擎它通过直接修补进程内的机器码把任意 C/C 函数的调用重定向到你自己的替换实现并保留一份蹦床trampoline以便随时回调原始函数。本教程带你完整吃透这个 API 的 4 个参数、1 个结构体以及全部13 个错误码的速查与处理方法零基础也能上手。一、substitute_hook_functions 是做什么的一句话概括改函数开头的几条指令让调用者拐入你的函数被覆盖的原指令搬进蹦床继续执行。它的执行流程分为三步准备阶段检查目标函数能否被钩取指令是否合法、跳转是否在范围内必要时为远程跳转分配 trampoline 内存页分析阶段反汇编函数开头生成被搬走的原指令 跳回原函数的 outro 蹦床提交阶段暂停其他线程默认线程安全模式原子地写入跳转补丁并修正其他线程恰好停在补丁区域内的 PC 值。核心实现在 lib/hook-functions.c对外声明位于 lib/substitute.h。快速开始克隆仓库git clone https://gitcode.com/gh_mirrors/su/substitute cd substitute二、API 完整详解一个函数 一个结构体 1. 函数签名int substitute_hook_functions( const struct substitute_function_hook *hooks, /* 钩子描述数组 */ size_t nhooks, /* 数组元素个数 */ struct substitute_function_hook_record **recordp,/* 撤销记录指针目前传 NULL */ int options); /* 选项0 或见下方 */参数说明hookssubstitute_function_hook结构体数组一次可批量钩多个函数nhooks数组长度recordp预留的撤销钩子接口当前版本未实现传 NULL 即可options0默认线程安全或SUBSTITUTE_NO_THREAD_SAFETY关闭安全检查返回值SUBSTITUTE_OK即 0表示成功非 0 值均为下文 13 个错误码之一。2. 结构体substitute_function_hook的 4 个字段struct substitute_function_hook { void *function; /* 要被钩取的目标函数 */ void *replacement; /* 你的替换函数签名须与目标一致 */ void *old_ptr; /* 可选传出原始函数指针形如 old_foo */ int options; /* 保留字段传 0 即可 */ };old_ptr是精髓钩取成功后库会向它写入一个蹦床地址。通过这个地址调用执行的是被搬走的原函数开头指令 跳回原函数剩余部分等价于调用原始实现——这是实现包装器wrapper模式的关键。3. 最小实战示例下面这个真实用例摘自 test/test-hook-functions.c批量钩取了getpid、hcreate、fwrite和一个自定义函数static pid_t (*old_getpid)(void); static pid_t hook_getpid(void) { return old_getpid() * 2; /* 包装器调用原实现并修改结果 */ } static const struct substitute_function_hook hooks[] { { getpid, hook_getpid, old_getpid }, /* 需要回调原函数传 old_getpid */ { hcreate, hook_hcreate, NULL }, /* 完全替换无需原函数指针 */ }; int main(void) { int ret substitute_hook_functions(hooks, sizeof(hooks) / sizeof(*hooks), NULL, 0); if (ret ! SUBSTITUTE_OK) { fprintf(stderr, hook failed: %s\n, substitute_strerror(ret)); return 1; } printf(getpid() %d\n, getpid()); /* 此时已被替换 */ return 0; }4. options 选项与线程安全模型 选项值含义默认 00线程安全模式必须在主线程调用钩取时逐个暂停其他线程保证原子性SUBSTITUTE_NO_THREAD_SAFETY1关闭主线程检查与所有同步性能更高但调用者需自行保证没有其他线程正在执行目标函数 设计巧思库选择主线程约定而非互斥锁是为了避免与其他同样要做钩子的第三方库互相锁死。如果你的钩取发生在进程启动、尚为单线程阶段两种模式都安全。三、全部 13 个错误码速查清单 以下 13 个常量1 个成功码 12 个错误码定义在 lib/substitute.h 中配套的错误信息转换函数substitute_strerror实现在 lib/strerror.cconst char *substitute_strerror(int err); /* 错误码 - 可读字符串 */#错误码数值触发原因处理建议1SUBSTITUTE_OK0✅ 钩取成功继续执行old_ptr已写入蹦床地址2SUBSTITUTE_ERR_FUNC_TOO_SHORT1函数太短补丁区内出现了非末尾的无条件返回指令该函数无法安全钩取换目标或改用导入重定位方案3SUBSTITUTE_ERR_FUNC_BAD_INSN_AT_START2补丁区开头存在少数几种难以搬移的特殊指令属库的覆盖盲区尝试更新库版本否则更换目标4SUBSTITUTE_ERR_FUNC_CALLS_AT_START3补丁区内末条除外含 call 指令返回地址可能残留在其他线程栈上换目标或确认无并发调用后加SUBSTITUTE_NO_THREAD_SAFETY5SUBSTITUTE_ERR_FUNC_JUMPS_TO_START4跳转分析发现函数后部有跳回补丁区的跳转该函数含自跳转/循环头不适合钩取换目标6SUBSTITUTE_ERR_OOM5内存分配失败out of memory检查进程内存状态减少单次批量钩取数量7SUBSTITUTE_ERR_VM6mmap/mprotect/vm_copy/vm_remap失败常见于内核/沙箱禁止代码页可执行W^X、PaX MPROTECT属环境限制非代码问题8SUBSTITUTE_ERR_NOT_ON_MAIN_THREAD7未在主线程调用且未设SUBSTITUTE_NO_THREAD_SAFETY把钩取调用移到主线程/启动阶段9SUBSTITUTE_ERR_UNEXPECTED_PC_ON_OTHER_THREAD8修复其他线程 PC 时发现其停在补丁区内的非指令边界上⚠️ 钩取本身已完成但被波及的线程可能崩溃建议重启进程重试10SUBSTITUTE_ERR_OUT_OF_RANGE9跳转目标超出直接跳转范围且无法在范围内分配 trampoline地址空间受限如 32 位 ASLR 布局换目标或减小重定位距离11SUBSTITUTE_ERR_UNKNOWN_RELOCATION_TYPE10substitute_interpose_imports未知重定位类型多见于交叉平台/新链接器产物属库覆盖盲区12SUBSTITUTE_ERR_NO_SUCH_SELECTOR11substitute_hook_objc_message类继承链中不存在该 selector检查类名与 SEL 拼写确认方法确实存在13SUBSTITUTE_ERR_ADJUSTING_THREADS12暂停其他线程时发生 OS 错误检查进程状态是否僵死线程、权限不足重试或降级为非线程安全模式 补充内部实现中还有两个非公开错误码SUBSTITUTE_ERR_TASK_FOR_PID1000跨进程注入时task_for_pid失败与SUBSTITUTE_ERR_MISC1001定义在 lib/substitute-internal.h一般 API 调用者不会遇到。错误处理最佳实践int ret substitute_hook_functions(hooks, nhooks, NULL, 0); if (ret ! SUBSTITUTE_OK) { /* 永远别裸打印数字substitute_strerror 给你可读原因 */ fprintf(stderr, substitute failed (%d): %s\n, ret, substitute_strerror(ret)); }四、高频问题排查指南 报SUBSTITUTE_ERR_NOT_ON_MAIN_THREAD→ 把substitute_hook_functions移到main()开头或主线程初始化阶段。报SUBSTITUTE_ERR_VM→ 你的环境沙箱、W^X 策略不允许标记可执行页这不是 bug考虑改用不修改代码的导入重定位 API。报SUBSTITUTE_ERR_FUNC_CALLS_AT_START但我确定没有并发→ 传入SUBSTITUTE_NO_THREAD_SAFETY可跳过该检查。钩取成功但偶发崩溃错误码 8→ 有线程恰好停在被改写区域且 PC 无法修正建议捕获后优雅重启该进程。五、相关文件一览 文件路径作用lib/substitute.h公共 API 声明、结构体、13 个错误码定义lib/hook-functions.csubstitute_hook_functions核心实现三阶段流程lib/strerror.csubstitute_strerror错误码字符串转换lib/substitute-internal.h内部错误码1000/1001与平台宏test/test-hook-functions.c可编译运行的官方实战示例掌握substitute_hook_functions的参数语义和这张 13 项错误码速查表你就能在运行时钩取场景中看码知病快速定位钩取失败原因——这就是它作为免费运行时修改库最值得掌握的 API。【免费下载链接】substituteA free runtime modification library.项目地址: https://gitcode.com/gh_mirrors/su/substitute创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考