在 iOS 开发中,Hook(钩子)技术是一种修改或扩展系统方法或函数行为的技术,常用于动态调试、性能监控、热修复等场景。常见的 Hook 方式包括 Runtime API、FishHook 和 Cydia 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 函数或系统库的底层函数(如
malloc、open等)。 - 依赖 Objective-C Runtime,安全性较高,不易崩溃。
2. FishHook(C 函数 Hook)
适用场景:Hook C 函数(如系统库函数 printf、open 等)。
原理:
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 函数(如
libc、Foundation中的底层函数)。 - 不能 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 技术
-
libffi(动态调用):
用于动态调用任意函数,可用于复杂 Hook 场景(如 Block Hook)。 -
Ptrace/DYLD_INSERT_LIBRARIES:
通过ptrace或环境变量注入动态库(需越|狱)。 -
Kernel-Level Hook(如 TrustCache Bypass):
修改内核权限,实现更深层的 Hook(仅限越|狱研究)。
总结:
- 普通 App:用 Runtime(OC 方法) + FishHook(C 函数)。
- 越|狱插件:用 Cydia Substrate 或 libffi、libhooker(可以绕过PAC)。
- 系统级 Hook:需结合 Mach-O 修改或内核级技术(一般需越|狱)。
注意:非越|狱环境下,Apple 对动态修改代码有严格限制(如 App Store 审核会拒绝 FishHook 某些系统 API)。




















暂无评论内容