上一篇整理了一下MachO文件的基本结构,但是对其主要的工作原理并没有深究,fishhook是iOS安全中常见的hook框架,可以通过对其原理探究更加深入的了解MachO的工作原理。
fishhook
fishHook是Facebook提供的一个动态修改链接mach-O文件的工具。利用MachO文件加载原理,通过修改懒加载表(Lazy Symbol Pointers)和非懒加载表(Non-Lazy Symbol Pointers)这两个表的指针达到C函数HOOK的目的。
首先需要下载fishhook源码,新建项目,将其中的fishhook.c和fishhook.h文件放入目录下:
fishhook简单利用
fishhook.h内容并不多,只有两个接口函数和一个结构体,具体如下:
|
|
rebind_symbols函数的第一个参数传入rebinding结构体数组,hook一个函数只需要传入一个rebinding结构体,hook多个函数则传入多个rebinding结构体。
rebinding结构体参数意义如下:
demo的代码如下:
运行点击屏幕,就可以Hook NSLog函数输出我们自己定义的字符串。
fishhook原理分析
首先有个问题需要思考,fishhook为什么可以Hook系统的C函数,只有动态才有可能被Hook,C函数是静态的,OC是动态的,这是因为系统的C函数有动态的部分,也就是符号绑定的过程。
PIC技术(位置代码独立):在文件内部生成一个表, 函数名称:函数地址,这玩意就是符号表
当MachO由dyld加载到内存中时,在调用NSLOg时,dyld将该函数地址赋值到符号表函数地址,这个过程就叫做符号绑定。
我们在之前的代码基础上添加两句NSLog输出
前面的NSLog是为了提前将该函数进行绑定,也即时懒加载符号表中,非懒加载符号在dyld加载时就会绑定真实的地址值,而懒加载不会,只有第一次去调用才会绑定真实地址,在第二次调用时直接使用真实地址。
编译demo,使用MachOView查看编译生成的MachO文件,可以看到NSLog是位于懒加载符号表的第一个,offset为0x8020,这里说一下为什么要在最上面的代码写一个NSLog,是为了先完成绑定真实地址的过程,下次调用就不用再去绑定了。
在rebind_symbols调用处下断点,调试输入命令image list查看demo运行时的基址。
用获取到的基址加上NSLog函数偏移0x8020,memory read查看内存,查看对应内存地址的汇编代码dis -s,发现是NSLog的函数真实地址
单步步过rebind_symbols(rebs, 1),再次查看NSLog偏移位置的内存,发现里面写入的地址有变化,查看对应的汇编代码,已经替换为我们自己写的myNSLog的真实地址。
通过上述的调试过程,可以看出fishhook实现原理其实就是重新绑定了符号表,将原本NSLog的地址替换为myNSLog的地址。至于如何重新绑定这个过程需要深入分析fishhook源码,之后单独开一篇进行分析。
class-dump原理
class-dump可以将应用的头文件导出,头文件包含了应用的类名和方法名对应关系,通过MachO文件格式来分析其原理。
同样以上述demo为例来探寻class-dump原理。
首先在数据段中寻找_objc_classlist节,该节中保存了程序中每个类的地址,下图可以看出0x8158地址上保存着数据0x100008E18,改地址保存的是类AppDelegate的信息
32 位类信息的结构体定义如下:
|
|
64 位的类信息的结构体定义如下:
0x100008E18属于数据段的_objc_data节
该类的Data数据是0x100008D30,其中保存类的数据信息
32位的结构体定义如下:
64 位的结构体定义如下:
在数据段的_objc_const节中查看0x100008D30, 该节保存是objc常量
Name中数据地址为0x100007711,保存的字符串位于__objc_classname节中
_objc_const节中Base Methods地址的数据是0x100008BD0,在_objc_const节寻找该地址,该类相关的函数都在其中。
其中方法名对应的字符串位于代码段的__objc_methname节中。
总结
通过fishhook和class-dump原理来熟悉MachO文件格式,反之通过MachO文件格式去解析各种工具实现原理,收获良多