1020 字
5 分钟
Hook
一、常见的Hook技术
- Method Swizzle
- 利用OC的Runtime特性,动态改变SEL和IMP的对应关系,达到OC方法调用流程的改变。主要用于OC方法。
- fishhook
- Facebook提供的一个动态修改链接mach-O文件的工具。利用MachO文件加载原理,通过懒加载和非懒加载两个表的指针达到C函数Hook的目的。
- Cydia Substrate
- 主要针对OC方法、C函数以及对函数地址进行Hook操作。不经针对iOS,安卓也可以用。
二、fishhook
- 首先回顾一下OC的动态特性
- 并不是直接调用方法的实现地址,而是通过一张selector和imp的映射表去找
- 静态语言如C,是直接通过地址访问
- 但外部C函数调用是通过符号绑定来的
2.1 rebind_symbols
/* * For each rebinding in rebindings, rebinds references to external, indirect * symbols with the specified name to instead point at replacement for each * image in the calling process as well as for all future images that are loaded * by the process. If rebind_functions is called more than once, the symbols to * rebind are added to the existing list of rebindings, and if a given symbol * is rebound more than once, the later rebinding will take precedence. */FISHHOOK_VISIBILITYint rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel);参数列表:
- 需要Hook的rebinding数组
- 长度
2.2 rebinding
/* * A structure representing a particular intended rebinding from a symbol * name to its replacement */struct rebinding { const char *name; void *replacement; void **replaced;};name 需要Hook的函数名称,C字符串
replacement 新函数的地址
replaced 原始函数的指针
2.3 使用 rebind_symbols hook NSLog
通过查看接口我们需要创建一些数据。
- (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor blackColor]; // 目标去Hook系统的NSLog函数
struct rebinding nslog; nslog.name = "NSLog"; nslog.replacement = hook_NSLog; // 保存NSLog函数的指针 nslog.replaced = (void *)&sys_NSLog;
struct rebinding bds[] = {nslog};
rebind_symbols(bds, 1);}
/// 旧函数的指针static void (*sys_NSLog)(NSString *format, ...);
// 新函数void hook_NSLog(NSString *format, ...) { format = [format stringByAppendingString:@"\n🌈"]; // 再调用系统的 sys_NSLog(format);}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSLog(@"RyukieLog");}输出结果:
2021-05-05 15:51:44.230666+0800 Hook[1650:239418] RyukieLog🌈2021-05-05 15:51:44.430579+0800 Hook[1650:239418] RyukieLog🌈2021-05-05 15:51:44.679392+0800 Hook[1650:239418] RyukieLog🌈Hook成功!
2.4 试一试hook C函数?
定义一个这样的函数
void logFun(const char * str) { NSLog(@"%s", str);}修改rebinding信息
struct rebinding nslog;nslog.name = "logFun";nslog.replacement = new_logfun;nslog.replaced = (void *)&old_logfun;
static void(*old_logfun)(const char * str);
void new_logfun(const char * str) { str = "🌈"; // 再调用旧的 old_logfun(str);}发现并没有成功!应为这个是通过地址直接访问的。
三、重绑定符号
一个问题:编译的时候,NSLog的真实地址知道么?不知道。
在MachO加载进内存时,dyld会先加载依赖的系统库进共享缓存。
NSLog在Foundation框架中,加载后dyld会将NSLog的真实地址告诉MachO。
验证一下fishhook的实现:

在hook前后分别下断点,打开汇编断点。
第一个断点:
-> 0x10233d234 <+188>: bl 0x10233e320 ; symbol stub for: NSLog
下一步:
-> 0x104cae320 <+0>: nop 0x104cae324 <+4>: ldr x16, #0x5cdc ; (void *)0x0000000104cae464 0x104cae328 <+8>: br x16第二个断点:
-> 0x10233d254 <+220>: bl 0x10233e320 ; symbol stub for: NSLog
下一步:
-> 0x104cae320 <+0>: nop 0x104cae324 <+4>: ldr x16, #0x5cdc ; (void *)0x0000000104cad44c: hook_NSLog at /Users/RyukieW/RyukieSamaProject/Study/逆向/Code/Hook/Hook/Hook/ViewController.m:75 0x104cae328 <+8>: br x16分析:
bl 0x10233e320 ; symbol stub for: NSLog
这一段是到0x10233e320中去寻找符号绑定的地址
第一次的第二步找到了系统原本函数的地址
第二次走到第二步后发现已经是hookNSLog了
四、符号绑定的过程
- 间接符号表
- MachO文件以外的,所有调用系统的都是外部的。
- 本地符号
- 上架去符号,去的是本地符号
- 全局符号
- 可以暴露给外界
4.1 桩
一块代码!
上面一段中看到的一段汇编指令
bl 0x10233e320 ; symbol stub for: NSLog
0x10233e320就是NSLog的桩
bl -> 桩 -> 通过符号表去执行代码
懒加载符号表全部都是通过桩去进行绑定的
五、总结
- 符号绑定过程
- 外部函数调用是执行桩里的代码
- 通过懒加载符号表里的地址去执行
- 懒加载符号表里面默认保存的是binder的代码
- bingder函数在非懒加载符号的表里面(程序运行就绑定好了)
- 外部函数调用是执行桩里的代码