iOS逆向之旅(进阶篇) — HOOK(FishHook)
官方简介
fishhook is a very simple library that enables dynamically rebinding symbols in Mach-O binaries running on iOS in the simulator and on device. This provides functionality that is similar to using DYLD_INTERPOSE
on OS X. At Facebook, we've found it useful as a way to hook calls in libSystem for debugging/tracing purposes (for example, auditing for double-close issues with file descriptors).
【简而言之就是一个很轻量级的库(就两个文件),可以动态的去修改macho可执行文件的属性】
项目地址:FishHook 。
案例一【HOOK 系统函数】
- 直接把FishHook的两个文件直接拖进项目,并引入头文件
image.png
- 申明一个函数指针用于保存原NSLog的真实函数地址
static void(*origNSLog)(NSString *format, ...);
- 自定义一个新的函数用于取代NSLog
void newNSLog(NSString *format, ...){
//再调用系统的nslog
origNSLog(@"就不打印");
}
- 调用fishhook的rebind_symbols实现hook
struct rebinding bind;
bind.name = "NSLog";//要HOOK系统函数的函数名称
bind.replacement = newNSLog;//新的函数去替换系统的NSLog
bind.replaced = (void *)&origNSLog;//把真正的NSLog地址保存到origNSLog
struct rebinding rebs[] = {bind};
rebind_symbols(rebs, 1);
上面这种事传统的写法,我们也可以简写成
rebind_symbols((struct rebinding [1]){{"NSLog",newNSLog,(void*)&origNSLog}}, 1);
- 这样就hook成功了,直接看效果
image.png
HOOK成功~~~~~~~~~
案例二【HOOK 自定义函数】
- 在案例一的基础上继续加,首先声明一个自定义函数func
void func(){
NSLog(@"123");
}
- 申明一个函数指针用于保存原func的真实函数地址
static void(*origFunc)();
- 自定义一个新的函数用于取代func
void newFunc(){
NSLog(@"456");
}
- 调用fishhook的rebind_symbols实现hook
rebind_symbols((struct rebinding [1]){{"func",newFunc,(void*)&origFunc}}, 1);
- 查看hook结果
image.png
不难看出hook失败了,hook成功的话,应该打印456才对
FishHook的原理
通过以上两个案例为何FishHook能hook系统函数,却hook不了我们自己的函数,接下来我们对其原理进行分析
科普一下
ASLR技术:是一种针对缓冲区溢出的安全保护技术,通过对堆、栈、共享库映射等线性区布局的随机化,通过增加攻击者预测目的地址的难度。对于我们APP而言,它保证每次MachO文件加载的时候是随机地址【这个我们可以通过LLDB指令的image list去查看】
image.png
根据苹果pic技术【位置代码独立】,当我们Macho需要调用系统库函数的时候,会在_DATA段中建立一个指针。DYLD【动态库加载】会进行动态的绑定,会将这个指针指向外部函数
回过头我们在分析我们之前的两个案例
根据PIC技术,我们在调用NSLog的时候,系统会现在_Data段建立一个指针,这个指针在DYLD动态加载Foundation框架时,把这个指针指向NSLog的的真实地址。
fishhook他实际就是在改这个指针,让这个指针向我们本地函数的地址。
所以fishhook的函数名字就叫rebind_symbols(重新绑定这个符号【指针】),很贴切。而我们本身自己的函数,不存在这个DYLD动态加载的过程,自然无法HOOK的了。