iOS中 Hook系统方法的方式 runtime、FishHook、Cydia Substrate的原理?

在 iOS 开发中,Hook(钩子)技术是一种修改或扩展系统方法或函数行为的技术,常用于动态调试、性能监控、热修复等场景。常见的 Hook 方式包括 Runtime APIFishHookCydia Substrate(第三方框架),它们的原理和适用场景有所不同。


1. Runtime API(Objective-C 方法 Hook)

适用场景:Hook Objective-C 方法(基于 objc_msgSend 的消息机制)。
原理
Objective-C 的运行时(Runtime)是动态的,允许在运行时修改类的方法实现。主要通过以下 API 实现 Hook:

  • class_replaceMethod:替换某个类的方法实现。
  • method_exchangeImplementations:交换两个方法的实现(Method Swizzling)。
  • method_setImplementation:直接设置某个方法的实现。

示例代码(Method Swizzling)

#import <objc/runtime.h>

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class cls = [UIViewController class];
        SEL originalSel = @selector(viewDidLoad);
        SEL swizzledSel = @selector(swizzled_viewDidLoad);
        
        Method originalMethod = class_getInstanceMethod(cls, originalSel);
        Method swizzledMethod = class_getInstanceMethod(cls, swizzledSel);
        
        // 尝试添加方法(防止原方法未实现)
        BOOL didAddMethod = class_addMethod(cls, originalSel, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
        
        if (didAddMethod) {
            // 添加成功,说明原方法不存在,直接替换
            class_replaceMethod(cls, swizzledSel, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        } else {
            // 交换实现
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

- (void)swizzled_viewDidLoad {
    [self swizzled_viewDidLoad]; // 实际调用原 viewDidLoad
    NSLog(@"Hook: viewDidLoad called");
}

特点

  • 仅适用于 Objective-C 方法(基于 objc_msgSend 动态派发)。
  • 不能 Hook C 函数或系统库的底层函数(如 mallocopen 等)。
  • 依赖 Objective-C Runtime,安全性较高,不易崩溃。

2. FishHook(C 函数 Hook)

适用场景:Hook C 函数(如系统库函数 printfopen 等)。
原理
FishHook 是 Facebook 开源的库,基于 Mach-O 文件格式dyld 动态链接机制 实现 Hook。
Mach-O 文件中的 符号表(Symbol Table)懒加载/非懒加载指针表(Lazy/Non-Lazy Binding) 存储了外部函数的地址。FishHook 通过修改这些指针表,替换目标函数的调用地址。

示例代码(Hook printf

#import "fishhook.h"

static int (*orig_printf)(const char *, ...);

int my_printf(const char *format, ...) {
    orig_printf("Hooked: ");
    va_list args;
    va_start(args, format);
    int ret = orig_printf(format, args);
    va_end(args);
    return ret;
}

+ (void)load {
    rebind_symbols((struct rebinding[1]){{"printf", my_printf, (void **)&orig_printf}}, 1);
}

特点

  • 适用于 C 函数(如 libcFoundation 中的底层函数)。
  • 不能 Hook Objective-C 方法(由于 Objective-C 方法调用不通过符号表)。
  • 依赖于 Mach-O 符号表,可能受系统保护机制影响(如 iOS 15+ 的 PAC 保护)。

3. Cydia Substrate(第三方 Hook 框架)

适用场景:越|狱环境下 Hook 任意函数(C/Objective-C)。
原理
Cydia Substrate(原 MobileSubstrate)是越|狱社区的经典 Hook 框架,提供更强劲的 Hook 能力:

  • MSHookMessageEx:Hook Objective-C 方法(类似 Runtime,但更稳定)。
  • MSHookFunction:Hook C 函数(类似 FishHook,但支持更多场景)。
  • 通过 代码注入(DYLIB 注入)函数地址替换 实现 Hook。

示例代码(Hook objc_msgSend

#import <substrate.h>

void (*orig_objc_msgSend)(id, SEL, ...);

void my_objc_msgSend(id self, SEL _cmd, ...) {
    NSLog(@"Hook: %@ called %@", self, NSStringFromSelector(_cmd));
    orig_objc_msgSend(self, _cmd, ...);
}

__attribute__((constructor)) static void init() {
    MSHookFunction(&objc_msgSend, &my_objc_msgSend, &orig_objc_msgSend);
}

特点

  • 支持 Hook 几乎所有函数(包括系统私有 API)。
  • 需要越|狱环境(依赖动态库注入)。
  • 功能强劲但风险较高,可能被苹果检测到。

对比总结

方式 适用场景 原理 依赖环境 安全性
Runtime API Objective-C 方法 交换 objc_method 的实现 iOS Runtime 高(官方支持)
FishHook C 函数(如 printf 修改 Mach-O 符号表绑定 dyld 动态链接 中(受 PAC 影响)
Cydia Substrate 越yu环境下的任意函数 动态库注入 + 函数替换 越yu 低(易被检测)

其他高级 Hook 技术

  1. libffi(动态调用)
    用于动态调用任意函数,可用于复杂 Hook 场景(如 Block Hook)。
  2. Ptrace/DYLD_INSERT_LIBRARIES
    通过 ptrace 或环境变量注入动态库(需越|狱)。
  3. Kernel-Level Hook(如 TrustCache Bypass)
    修改内核权限,实现更深层的 Hook(仅限越|狱研究)。

总结:

  • 普通 App:用 Runtime(OC 方法) + FishHook(C 函数)。
  • 越|狱插件:用 Cydia Substrate 或 libffi、libhooker(可以绕过PAC)。
  • 系统级 Hook:需结合 Mach-O 修改或内核级技术(一般需越|狱)。

注意:非越|狱环境下,Apple 对动态修改代码有严格限制(如 App Store 审核会拒绝 FishHook 某些系统 API)。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容