很多网上资料讲解fishhook原理,要么泛泛而谈,要么大而全,要么直接代码太高深。本文深入底层汇编,由浅入深,通过一条主脉络娓娓道来,剖析fishhook,玩转fishhook!
fishhook是什么?有何用
hook简介
fishhook
是facebook
开源的第三方框架。从名称上看含有**hook
**这个词,其本意就是"钩子",钩子就是用来勾住某一种事物,比如fish
鱼。在计算机中就可以勾住某一个程序(就是挂钩程序)或者某一个函数,从而扩展程序功能或者改变程序运行的流程,比如在iOS开发中经常会被用来做”逆行开发“。
hook使用场景
埋点
拦截用户手势交互或者进入某个指定页面等应用接口,进行用户行为统计分析。
应用加固
防止黑客通过Hook
技术破解/攻破你的应用,来加固应用,比如ptrace
调试防护、隐藏防护、注入对抗。
应用隔离
说白了就是隔离应用在安全区域,比如移动办公应用,就可以hook
拦截网络、截屏、剪贴板等接口,来防止数据泄露。
hook技术
method swizzle
原理很简单就是利用OC
的runtime
特性来动态修改objc_msgSend
函数中的id/selector
参数,来改变id
与selector
之间的对应关系,以交换id
对应的method
,从而实现hook
,这也是为啥OC
是一门动态语言。比如经常这样使用:
fishhook
众所周知,C语言是一门静态语言,而静态语言在编译完成后变量、函数及其参数就已经确定,无法修改。但fishhook
就能在程序运行时动态修改C
函数,比如微信内存监控就使用fishhook
来hook
malloc/free
函数监控堆内存分配。
Cydia
...
这里不做阐述,重点阐述
fishhook
;
fishhook实战 如何使用?
既然fishhok
能hook
C
函数,就来个需求:就hook
经常使用的Foundation.framework
中的NSLog
来实战演练下具体使用,直接代码如下:
//原函数指针变量
static void (*sys_nslog)(NSString *format, ...);
//hook新函数
void hook_nslog(NSString *format, ...) {
format = [format stringByAppendingString:@"❤️ ( ⚫︎ー⚫︎ ) balalala~"];
sys_nslog(format);
}
- (void)viewDidLoad {
[super viewDidLoad];
//提出需求->分解需求->完成需求->测试验收
//【需求】hook NSLog
//1. 点击屏幕输出日志;
//2.fishhook hook NSLog让日志内容发生点变化!!!
//定义hook的函数的结构体变量
struct rebinding nslog_reb;
nslog_reb.name = "NSLog";
nslog_reb.replacement = hook_nslog;
nslog_reb.replaced = (void *)&sys_nslog;
//定义需要hook的函数的结构体数组变量
struct rebinding rebs[] = {nslog_reb};
//很简单,传递结构体数组地址及其成员变量数目
rebind_symbols(rebs, 1);
}
//点击屏幕输出日志
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"click!!!");
}
输出结果如下:
结果说明fishhook
能够hook
动态链接库Foundation.framework
中的NSLog
,**那fishhook
能不能hook所有的C函数???**这里通过实例自定义函数来验证下,代码如下:
//原函数
static void func(void) {
printf("%s \n", __func__);
}
//新函数
static void hook_func(void) {
printf("%s \n", __func__);
}
- (void)viewDidLoad {
[super viewDidLoad];
//提出需求->分解需求->完成需求->测试验收
//【需求】hook NSLog
//1. 点击屏幕输出日志;
//2. fishhook hook NSLog让日志内容发生点变化!!!
//3. hook 自定义函数func 是否函数名称发送变化!!!
func();
//hook NSLog
struct rebinding nslog_reb;
nslog_reb.name = "NSLog";
nslog_reb.replacement = hook_nslog;
nslog_reb.replaced = (void *)&sys_nslog;
//hook func
struct rebinding func_reb;
func_reb.name = "func";
func_reb.replacement = hook_func;
func_reb.replaced = nil;
struct rebinding rebs[] = {nslog_reb, func_reb};
rebind_symbols(rebs, 2);
func();
}
结果如下:
说明fishhook无法hook自定义函数!!!why????
fishhook原理探索 为何这么用
通过底层汇编来揭秘fishhook
为何只能hook
动态链接库函数,不能hook
自定义函数!!!使用Hopper disassembler
反汇编上面的代码可执行文件,自定义函数func
汇编结果如下:
说明func
函数没有发生变化,即未被fishhook
hook
,why?? 别着急,往下看,看下func
函数是在代码段还是数据段?
使用MachOView
来看下func
函数是否在代码段还是数据段,如图:
自定义函数在代码段,代码段具有只读可执行权限,因此fishhook
无法hook
自定义函数!那NSLog
在代码段还是数据段呢???
使用MachOView
来看下具体的段,如下图;
NSLog
函数符号位于数据段,因此fishhook
能够hook
动态链接库Foundation.framework
中的NSLog
,重要原因之一就是:函数符号位于数据段,只有数据段内容才能被修改!!!
函数符号为何在数据段?
一图读懂,如下图:
一句话简述:真正的函数符号NSLog
位于外部动态链接库Foundation.framework
,属于外部符号,由于ASLR
偏移量动态链接库每次加载到内存的地址不定的,就需要可执行文件加载到内存时_NSLog_ptr
重新进行”符号重绑定“以修正其地址内容,来执行真正的NSLog
实现。
继续看下函数符号NSLog
位于具体的哪个段,如下图:
位于_la_symbol_ptr
段,就是”懒加载符号“,即使用时再去加载!!!相对的就是”非懒加载符号“,如下图:
同样位于数据段,内容为空值,即程序启动后就去进行”符号重绑定“。
懒加载符号如何被加载?
懒加载符号NSLog
跳转到0x100002394
,0x100002394
又跳转到0x100002384
,进而跳转到dyld_stub_binder
,字面意思就是dyld
进行桩绑定,即dyld
符号重绑定!!!
dyld与fishhook有何关系?
一句话概述:就是dyld
动态加载器提供了获取镜像数据的接口,比如Mach-O header
、ASLR
,进而就可以通过Mach-O
中的信息获取函数符号的所有相关数据信息,比如懒加载符号、非懒加载符号等;
如何确定函数符号地址?
这就是fishhook
的重要流程,就是符号查找过程,很简单,一张图就可以明白,如下图:
流程如下:
- 懒加载符号表
Lazy Symbol Pointer Table
与间接符号表Indirect Symbol Table
中的符号一一对应; - 间接符号表保存了函数符号在符号表
Symbol Table
中的偏移量; - 符号表中每项为
struct nlist
结构体,其中保存了函数符号在字符串表String Table
中的偏移量; - 通过字符串表的偏移量就可以找到最终的函数符号对于的函数名称
通过遍历以上流程就可以建立函数符号与函数名称的对应关系,就可以通过函数名称来找到最终的函数符号,进而就可以修改函数符号的指向,来指向自己的实现。因此,fishhook
提供的函数接口rebind_symbols
中的结构体struct rebinding
需要提供函数名称,如下:
struct rebinding {
const char *name;//函数名称
void *replacement;//新函数指针
void **replaced;//原函数地址的指针
};
总结
缕一缕fishhook
能够hook
C
函数的原因不外乎这几点:
- 函数符号属于外部符号,位于动态链接库,进而位于数据段,只有数据段的内容才能被修改;
dyld
提供了获取镜像信息的接口,如获取Mach-O header
、ASLR
等,进而就可以获取懒加载符号表、非懒加载符号表、间接符号表、符号表、字符串表等符号相关的所有信息;- 通过遍历函数符号建立函数符号与函数名称的对应关系,就可以通过函数名称就可以找到最终的函数符号地址,就可以修改函数符号的内容来指向自己的实现。
思考与探索
fishhook
能够hook
C
函数,能不能hook
C++
函数?Linux
系统能不能hook
C/C++
函数?
让学习成为一种乐趣!!!