Hook
1. 概述:Hook,中文译为“挂钩”或“钩子”。在iOS逆向中是指改变程序运行流程的一种技术。通过hook可以让别人的程序执行自己所写的代码。在逆向中经常使用这种技术。所以在学习过程中,我们重点要了解其原理,这样能够对恶意代码进行有效的防护。
2. 示意图:
3. iOS中几种hook的方式
<1>Method swizzle:利用OC的Runtime特性,动态改变SEL(方法编号)和IMP(方法实现)的对应关系,达到OC方法调用流程改变的目的。主要用于OC方法。
<2>fishHook:它是Facebook提供的一个动态修改链接mach-O文件的工具。利用MachO文件加载原理,通过修改懒加载和非懒加载两个表的指针达到C函数HOOK的目的。
<3>Cydia Substrate: 原名为 Mobile Substrate ,它的主要作用是针对OC方法、C函数以及函数地址进行HOOK操作。当然它并不是仅仅针对iOS而设计的,安卓一样可以用。官方地址:http://www.cydiasubstrate.com/。
Method Swizzle
在OC中 SEL和IMP之间的关系,就好像一本书的“目录”。SEL是方法编号,就像“标题”一样。IMP是方法的实现的真是地址,就像“页码”一样,他们是对应的关系。
runtime提供了交换两个SEL和IMP的对应关系的函数
通过这个函数交换两个SEL和IMP对应关系的技术,我们称之为Method Swizzle(方法欺骗)。
Cydia Substrate
MobileHooker:顾名思义用于HOOK。它定义一系列的宏和函数,底层调用objc的runtime和fishhook来替换系统或者目标应用的函数.其中有两个函数:
<1>MSHookMessageEx:主要作用于Objective-C方法 void MSHookMessageEx(Class class, SEL selector, IMP replacement, IMP result)
<2>MSHookFunction 主要作用于C和C++函数 void MSHookFunction(voidfunction,void*replacement,void** p_original), Logos语法的%hook就是对此函数做了一层封装。
MobileLoader:用于加载第三方dylib在运行的应用程序中。启动时MobileLoader会根据规则把指定目录的第三方的动态库加载进去,第三方的动态库也就是我们写的破解程序.
Safe Mode:破解程序本质是dylib,寄生在别人进程里。系统进程一旦出错,可能导致整个进程崩溃,崩溃后就会造成iOS瘫痪。所以CydiaSubstrate引入了安全模式,在安全模式下所有基于CydiaSubstratede 的三方dylib都会被禁用,便于查错与修复。
fishHook
描述:它是Facebook提供的一个动态修改链接mach-O文件的工具。利用MachO文件加载原理,通过修改懒加载和非懒加载两个表的指针达到C函数HOOK的目的。
获取地址:https://github.com/facebook/fishhook
关键函数:
参数1:存放rebinding结构体的数组,可以同时交换多个函数
参数2:rebindings数组的长度
rebinding数组结构体:
fishHOOK可以HOOK我们的C函数,但是我们知道函数是静态的,也就是说在编译的时候,编译器就知道了它的实现地址,这也是为什么C函数只写函数声明调用时会报错。那么为什么fishhook还能够改变C函数的调用呢?难道函数也有动态的特性存在?我们一起来探究它的原理…
fishhook是怎么工作的?
dyld binds lazy and non-lazy symbols by updating pointers in particular sections of the __DATAsegment of a Mach-O binary. fishhook re-binds these symbols by determining the locations to update for each of the symbol names passed to rebind_symbols and then writing out the corresponding replacements.For a given image, the __DATA segment may contain two sections that are relevant for dynamic symbol bindings: __nl_symbol_ptr and __la_symbol_ptr. __nl_symbol_ptr is an array of pointers to non-lazily bound data (these are bound at the time a library is loaded) and __la_symbol_ptr is an array of pointers to imported functions that is generally filled by a routine called dyld_stub_binder during the first call to that symbol (it's also possible to tell dyld to bind these at launch). In order to find the name of the symbol that corresponds to a particular location in one of these sections, we have to jump through several layers of indirection. For the two relevant sections, the section headers (struct sections from <mach-o/loader.h>) provide an offset (in the reserved1 field) into what is known as the indirect symbol table. The indirect symbol table, which is located in the __LINKEDIT segment of the binary, is just an array of indexes into the symbol table (also in __LINKEDIT) whose order is identical to that of the pointers in the non-lazy and lazy symbol sections. So, given struct section nl_symbol_ptr, the corresponding index in the symbol table of the first address in that section is indirect_symbol_table[nl_symbol_ptr->reserved1]. The symbol table itself is an array of struct nlists (see <mach-o/nlist.h>), and each nlist contains an index into the string table in __LINKEDIT which where the actual symbol names are stored. So, for each pointer __nl_symbol_ptrand __la_symbol_ptr, we are able to find the corresponding symbol and then the corresponding string to compare against the requested symbol names, and if there is a match, we replace the pointer in the section with the replacement.
我们用fishhook来hook个系统的NSLog函数
打印结果为:我点击了一下屏幕___hook住了。
然后我们在看一个例子,这个例子是hook我们自己写的C函数
打印:我点击了一下屏幕
这是为什么呢?fishHook 无法交换自定义函数。原因是C是静态的在编译时期就确定了函数的地址。我们无法更改它,那么为啥fishhook能交换IOS系统的C函数呢?因为系统的C函数存在着动态的部分“共享缓存”。共享缓存(dyld shared cache,动态库共享缓存)。由于iOS系统为了节约内存的空间,他将一些共用的系统库(UIKit,Foundation)等都放在共享缓存里面。我们自定义的函数的实现地址在MachO本地文件里面,编译之后真实地址已经确定。而NSLog在编译的时候,并没有确定真是地址,只确定了虚拟地址(符号地址)。 那么MachO中的符号地址是怎么跟绑定的呢?苹果运用了一种PIC(Position Independent Code,位置代码独立)技术。当dyld把MachO加载到内存里面的时候然后dyld通过PIC将MachO里面的符号地址跟在共享缓存里面的真实地址绑定,当我们调用系统的方法(NSLog)的时候,先找到符号地址然后在根据符号地址来找到真实地址。在来看fishhook,其函数名称rebind_symbols(重新绑定符号),而源码都是在操作MachO文件里面的符号。所以fishHook的原理就是改变系统方法的符号绑定,将系统的函数符号指向自己的函数地址,将系统的函数地址赋值到我们申明的变量中(重绑定符号)来实现hook的。