跳到主要内容

fishhook

· 阅读需 4 分钟
Quany
软件工程师

fishhook 是 Facebook 开源的一个轻量级库,它允许你在 iOS 和 macOS 平台上动态地重绑定 Mach-O 二进制文件中的符号。这意味着你可以在运行时“钩取”(Hook)系统级的 C 函数调用,将其替换为你自己的实现,而无需修改原始的二进制文件或重新启动应用。

🔧 核心原理

fishhook 的实现非常巧妙,它利用了 Mach-O 文件格式的动态链接机制。其核心原理可以概括为以下几点:

  1. 目标区域fishhook 修改的并非只读的代码段(__TEXT),而是可写的数据段(__DATA)中的符号指针表(具体是 __la_symbol_ptr__nl_symbol_ptr 节)。这些指针在程序启动时由动态链接器(dyld)填充为外部函数(如 printf)的真实地址。
  2. 操作机制:通过 vm_protect 这个底层系统调用,fishhook 临时改变了存储符号指针的内存页面的保护属性,使其从只读变为可写。
  3. 写时复制(Copy-on-Write):这是关键的一步。当使用 VM_PROT_COPY 参数时,系统会为当前进程创建一个该内存页的私有副本。这样,修改只会影响当前进程,而不会破坏共享的系统库。
  4. 地址替换:最后,fishhook 将符号指针表中目标函数的地址替换为你提供的自定义函数的地址。此后,所有通过该符号指针表发起的调用都会转向你的函数。

简单来说,fishhook 就像是修改了一个函数地址的“通讯录”,让程序在不知情的情况下调用你指定的新函数。

📝 使用方法

使用 fishhook 的步骤非常直接,通常包含以下三个环节:

  1. 引入库文件:将 fishhook.hfishhook.c 两个文件添加到你的项目中。
  2. 准备替换函数:定义一个与你想要钩取的函数签名完全一致的函数。在这个函数里,你可以添加自己的逻辑,并选择是否调用原始函数。
  3. 执行重绑定:调用 rebind_symbols 函数,告诉 fishhook 要替换哪个符号。

下面是一个钩取 printf 函数的经典示例:

#include <stdio.h>
#include "fishhook.h"

// 用于保存原始printf函数指针的变量
static int (*orig_printf)(const char * restrict format, ...);

// 自定义的printf函数
int my_printf(const char * restrict format, ...) {
// 添加钩取逻辑,例如在输出前加上一个标记
orig_printf("[Hook] ");

// 继续执行原始printf的功能
va_list args;
va_start(args, format);
int result = orig_printf(format, args);
va_end(args);

return result;
}

int main() {
// 构造一个rebinding结构体数组
struct rebinding rebs[1] = {
{"printf", (void *)my_printf, (void **)&orig_printf}
};

// 执行重绑定
rebind_symbols(rebs, 1);

// 此时调用printf,实际执行的是my_printf
printf("Hello, Fishhook!\n"); // 输出: [Hook] Hello, Fishhook!

return 0;
}

代码示例整合自和

⚠️ 特点与局限性

了解 fishhook 的能力边界非常重要:

  • 主要优势
    • 针对系统 C 函数:非常适合钩取来自 libSystem(如 malloc, open, printf)等动态库的函数。
    • 无需越狱:可在非越狱环境的 App Store 应用中使用。
  • 主要局限性
    • 无法钩取静态函数:如果函数的实现在编译时已经静态链接到最终的可执行文件中(地址在编译期就已确定),fishhook 无法修改它。
    • 无法钩取内联函数:编译器内联展开的函数没有独立的符号地址,因此无法被钩取。
    • 对 Swift 函数效果有限:由于 Swift 复杂的名称修饰(Name Mangling)机制,直接钩取 Swift 函数比较困难。

🛡️ 应用场景与防护

fishhook 的强大功能使其在多种场景下发挥作用:

  • 调试与日志记录:跟踪特定的系统调用,如记录所有文件操作(open)或内存分配(malloc)。
  • 性能监控:测量关键函数的执行时间。
  • 安全研究:分析应用的行为或进行逆向工程。
  • 功能扩展:在不拥有源码的情况下为现有函数添加新功能。

正因为 fishhook 可能被用于恶意目的,也催生了相应的防护技术。例如,AntiFishhook 就是一个专门设计来实时检测和防御 fishhook 攻击的库。

💎 总结

总而言之,fishhook 是一个通过动态修改 Mach-O 文件数据段中的符号绑定来实现 C 函数钩取的强大工具。它深刻体现了 macOS/iOS 系统底层动态链接的灵活性。

希望这些信息能帮助你全面理解 fishhook。如果你对某个特定细节或应用场景有进一步的疑问,我很乐意提供更多解答。

微信公众号

微信公众号