欢迎阅读iOS逆向系列(按序阅读食用效果更加)
- iOS逆向 RSA理论
- iOS逆向 hash理论
- iOS逆向 应用重签名+微信重签名实战
- iOS逆向 Shell脚本+脚本重签名
- iOS逆向 代码注入+Hook
- iOS逆向 MachO文件
- iOS逆向 dyld流程
- iOS逆向 HOOK原理之fishhook
之前在iOS逆向 代码注入+Hook篇章讲过Method Swizzling
,这是一种基于Runtime
可以动态改变方法实现的HOOK方案,但Method Swizzling
有自己的局限性,本文就将系统的介绍一下何谓HOOK
和fishhook
HOOK
翻译成中文为“挂钩”、“钩子”,在iOS逆向领域中指的是改变程序运行流程的一种技术,通过HOOK
可以让别人的程序执行自己所写的代码
下列示意图就是对HOOK功能的形象诠释:
在iOS中HOOK技术有以下几种:
SEL(方法编号)
和IMP(方法实现)
的对应关系,达到OC方法调用流程改变的目的Mobile Substrate
,它的主要作用是针对OC方法、C函数以及函数地址进行HOOK操作,且安卓也能使用之前已经介绍过
Method Swizzling
了,OC的Runtime特性让它有了“黑魔法”之称,同时也是局限性所在
三者的区别如下:
Method Swizzling
只适用于动态的OC方法(运行时确定函数实现地址)fishhook
适用于静态的C方法(编译时确定函数实现地址)Cydia Substrate
是一种强大的框架,只需要通过Logos语言(类似于正向开发)就可以进行Hook,适用于OC方法、C函数以及函数地址在github上找到fishhook
的代码,将fishhook.c
、fishhook.h
拖入项目中
整个fishhook就开放了一个结构体rebinding
和两个函数
rebind_symbols
hook项目中的所有函数名称rebind_symbols_image
只hook某一个资源库的函数名称
使用fishhook的步骤:
#import "fishhook.h" 复制代码
rebinding
结构体对象struct rebinding nslog; nslog.name = "NSLog"; nslog.replacement = fxNSlog; nslog.replaced = (void *)&sys_nslog; 复制代码
rebind_symbols
重新绑定符号struct rebinding rebs[] = {nslog}; rebind_symbols(rebs, 1); 复制代码
完整代码如下:
#import "fishhook.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; struct rebinding nslog; nslog.name = "NSLog"; nslog.replacement = fxNSlog; nslog.replaced = (void *)&sys_nslog; struct rebinding rebs[] = {nslog}; rebind_symbols(rebs, 1); } static void(*sys_nslog)(NSString *format, ...); void fxNSlog(NSString *format, ...) { format = [format stringByAppendingFormat:@"已hook"]; sys_nslog(format); } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSLog(@"点击屏幕"); } @end 复制代码
rebinding
来指定交换的函数
name
中指定Hook的函数名称(C字符串)replacement
中指定新的函数地址
replaced
中存放原始函数地址的指针
NSLog
在Foundation库中,在每个手机的内存地址都不一样,不能通过CMD+B
就能确定其内存地址NSLog
的地址,所以这里定义新的函数指针保存函数实现&
修改变量内部的值(void *)
进行类型转换rebind_symbols
中需要两个参数
rebinding
数组rebinding
数组的长度前面介绍了fishhook
是用来hook C函数的,但在此前提下还是有它的局限性——无法交换自定义函数
C函数
是静态的,在编译时就已经确定了函数地址(函数实现地址在MachO本地文件中)系统C函数
则是存在着动态的部分那么为什么系统级别的C函数就可以呢?这就要说到PIC技术了
PIC技术
(Position-independent code),又叫做位置独立代码
,是为了系统C函数
在编译时期能够确认一个地址的一种技术手段
Load Commands
中会依赖Foundation
),在符号表中找到了NSLog
函数,就会进行链接绑定——将Foundation
中NSLog
的真实地址赋值到DATA段
的NSLog
符号上而自定义的C函数不会生成符号表,直接就是一个函数地址,所以fishhook的局限性就在于只有符号表内的符号可以hook(重新绑定符号)
因为NSLog
是个懒加载的符号,所以加了行代言代码
image list
打印所有镜像资源——第一个就是进程的内存首地址
memory read 内存地址
读出符号地址
由于iOS是小端模式,内存地址8位倒着读dis -s 内存地址
查看汇编
此时的符号并没有绑定,因为还没有使用在fishhook的开源网站上有这么一张工作原理图
接下来分析下rebind_symbols
:
prepend_rebindings
将rebindings
数组不断添加到_rebindings_head
链表的头部成为新的头节点_rebindings_head->next
是否为空来判断是否是第一次调用
_dyld_register_func_for_add_image
进行回调监听_rebind_symbols_for_image
接下来是_rebind_symbols_for_image
对MachO文件的操作:按照规则计算各种表的地址和指针在表中的偏移量
最后一步,perform_rebinding_with_section
根据算好的符号表地址和偏移量,找到在符号表中用于指向共享库目标函数的指针,将该指针的值(即目标函数的地址)赋值给rebinding
的*replaced
,最后修改该指针的值为replacement
(新的函数地址)
接下来就根据字符串对应在符号表中的指针,找到其在共享库的函数实现的
Lazy Symbol Pointers
中位列第一个,下标为0
Dynamic System Table->Indirect Symbols
中的value与Lazy Symbol Pointers
一一对应,此表中的Data(0xA9=169)即为另一个表的下标
Symbol Table->Symbols
中找到NSLog,此时的Data对应String Table Index
中的偏移值(0x0000009B)
String Table
中计算表头(0x000061EC)+偏移量(0x0000009B),找到NSLog的函数地址
既然fishhook
可以交换C函数,那么就可以交换method_exchangeImplementations
等函数来防止HOOK,项目本身如果需要使用method_exchangeImplementations
则可以使用新的函数地址来进行操作
在安全攻防领域中,hook原理起到至关重要的作用,而使用fishhook做防护虽然意义不大,但是了解fishhook原理对后续的学习还是挺有帮助的