0x01.Fishhook
fishhook是Facebook推出的一个轻量级hook框架。
fishhook实现原理:fishhook主要利用了共享缓存功能和PIC技术来实现hook功能。
动态共享缓存
iOS系统为节省内存资源,将系统的动态库资源统一放在了系统的共享区域,该区域其他应用都可以访问。
PIC(位置代码独立)
在MachO文件中会预留出一段空间,这一段空间叫做符号表,在MachO文件的数据段中。dyld在加载MachO文件到内存中后,会将共享区的系统函数地址绑定到对应的符号上,并插入到符号表中。这样在项目中调用相关系统函数时,实际上调用的是对应的符号,通过符号来找到具体的系统函数地址。
其实和安卓,linux这块的.got hook是同个思路。不愧是同源的操作系统。
可以说,通过重绑定就可以将符号所关联的系统函数给替换掉了(注意是系统函数,自定义函数是不行的,具体后面会说),这样就执行时执行的就是我们替换上去的自己的方法了!达成了hook的目的。
Facebook为我们提供了一轻量级的hook框架——fishhook。不知道扎克伯格会不会又说窃取Facebook技术了?(笑)
0x02.关键源码
先看下fishhook两个关键的文件fishhook.h和fishhook.c吧
下面是fishhook.c
这里关键的函数也其实是基于苹果的加载器dyld的,具体的函数在dyld.h中。
0x03.简单实现
又到了小白第一喜欢的依葫芦画瓢时间了。
首先使用fishhook去hook一个懒加载或者非懒加载的函数需要三个步骤:
1、创建一个指针保存原始函数的地址
2、创建一个替代原始函数的新函数
3、使用fishhook的函数来对符号表重绑定(需要一个rebindings的数组和一个数组的长度nel),实现新旧方法的替换,达到hook的目的
下面来实现一下hook NSLog系统函数:
我们先写一个简单的button点击输出log的app
点一下button,就输出一个log
接下来我们来hook NSLog方法,这个是一个系统方法。
直接把fishhook.c和fishhook.h添加到demo中的fishhookdemo目录下(和ViewController.m同目录)
然后开始写代码
这里我把fishhook的执行逻辑放到了我自己定义的控件里面,其实可以人为去控制这个hook执行的时间,反正会回调的。
我hook时替换掉了button点击事件中的NSLog打出log的内容,从fishing替换为了nofishing。
这里有一个问题,为什么我要在viewDidLoad方法也写一个NSlog方法呢?
唔,因为看其他的博客,说是要先执行一次系统函数,共享函数库里才会加载进这个方法。
照这么说,我点击按钮了之后先是输出了一个log,这么说其实已经加载进去了,并不需要在viewDidLoad方法中写一个NSLog方法呢?
试一试吧
再次运行
这里依然是能够hook成功的,那说明共享函数库里确实还是装载了NSLog这个函数
那么我们再把上图第一个红框的NSLog方法删掉试一试
可能是系统共享库里之前已经加载了NSLog这个系统函数,因为我是在之前文件的基础上改的。
不过具体是怎么在更底层一点的空间里实现的,下周调试一下看看吧!这周星期五该放假了哈哈哈
0x04.hook过程分析
使用MachOView打开刚刚的fishhookdemo看一看,NSLog函数是否存在于库资源里
mach-O文件有两个加载表,la_symbol_ptr懒加载表和nl_symbol_ptr非懒加载表。这里可以看见NSLog函数存在于la_symbol_ptr懒加载表里。
这个懒加载表里的函数都是需要通过懒加载的,前面的offset是该函数在内存中的偏移量。NSLog的偏移量为0x00008020。
在Xcode里面对Demo开启调试看看
100f48000+8020 = 100f50020,这应该就是符号表的绝对地址了。
读取之
memory read + 地址读取该地址的内存
dis 命令打印汇编代码
不过没有关系,可以看到这里是没有binding上Nslog的。
都说了是懒绑定,肯定是运行时动态地去找符号绑定啦。运行到NSLog函数实现的时候,这里可以看到NSlog函数。运行后,Foundation中的NSLog加入到符号表中。因此只要该函数被加载过,就可以找到绑定的符号地址。
这里说明你要读取任何系统函数都很简单,读取基地址加偏移即可,感觉上比Android系统的更简单。
这里就重绑定成自己的newNSLog函数了!
现在我们知道,dyld会对系统函数进行符号绑定,符号表在数据段,可读可写,dyld可以绑定,fishhook也可以通过替换系统函数的指针进行重绑定
接下来开始调试看看替换流程吧,从rebind_symbols函数步入,步入prepend_rebindings函数
好吧,有机会再看看汇编咯。