共计 3789 个字符,预计需要花费 10 分钟才能阅读完成。
一、HOOK 概述
1.HOOK 定义
HOOK
翻译成中文为“挂钩”、“钩子”,在 iOS 逆向畛域中指的是扭转程序运行流程的一种技术,通过 HOOK
能够让他人的程序执行本人所写的代码
下列示意图就是对 HOOK 性能的形象诠释:
- 注入恶意代码让用户误以为打卡胜利,理论并没有实现打卡(只批改两头流程)
- 注入恶意代码让用户误以为网络出问题(开拓新的流程分支)
2.HOOK 形式
在 iOS 中 HOOK 技术有以下几种:
- Method Swizzling:利用 OC 的 Runtime 个性,动静扭转
SEL(办法编号)
和IMP(办法实现)
的对应关系,达到 OC 办法调用流程扭转的目标 - fishhook:这是 FaceBook 提供的一个动静批改链接 machO 文件的工具,利用 machO 文件加载原理,通过批改懒加载和非懒加载两个表的指针达到 C 函数 HOOK 的目标
- Cydia Substrate:原名为
Mobile Substrate
,它的次要作用是针对 OC 办法、C 函数以及函数地址进行 HOOK 操作,且安卓也能应用
之前曾经介绍过
Method Swizzling
了,OC 的 Runtime 个性让它有了“黑魔法”之称,同时也是局限性所在
三者的区别如下:
Method Swizzling
只实用于动静的 OC 办法(运行时确定函数实现地址)fishhook
实用于动态的 C 办法(编译时确定函数实现地址)Cydia Substrate
是一种弱小的框架,只须要通过 Logos 语言(相似于正向开发)就能够进行 Hook,实用于 OC 办法、C 函数以及函数地址
二、fishhook
1.fishhook 的应用
整个 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
中指定新的函数地址- c 函数的名称就是函数指针——动态语言编译时就已确定
-
replaced
中寄存原始函数地址的指针- 因为
NSLog
在 Foundation 库中,在每个手机的内存地址都不一样,不能通过CMD+B
就能确定其内存地址 - 这里 fishhook 在运行的时刻会动静获取到
NSLog
的地址,所以这里定义新的函数指针保留函数实现 - 应用
&
批改变量外部的值 - 应用
(void *)
进行类型转换
- 因为
-
rebind_symbols
中须要两个参数- 参数一是
rebinding
数组 - 参数二是
rebinding
数组的长度
- 参数一是
作为一个开发者,有一个学习的气氛跟一个交换圈子特地重要,这有个 iOS 交换群:642363427,不论你是小白还是大牛欢送入驻,分享 BAT, 阿里面试题、面试教训,探讨技术,iOS 开发者一起交流学习成长!
2.fishhook 的局限性
后面介绍了 fishhook
是用来 hook C 函数的,但在此前提下还是有它的局限性——无奈替换自定义函数
C 函数
是动态的,在编译时就曾经确定了函数地址(函数实现地址在 MachO 本地文件中)零碎 C 函数
则是存在着动静的局部
那么为什么零碎级别的 C 函数就能够呢?这就要说到 PIC 技术了
3.PIC 技术
PIC 技术
(Position-independent code),又叫做 地位独立代码
,是为了 零碎 C 函数
在编译期间可能确认一个地址的一种技术手段
- 编译时在 MachO 文件中预留出一段空间——符号表(DATA 段中)
- dyld 把利用加载到内存中时(此时在
Load Commands
中会依赖Foundation
),在符号表中找到了NSLog
函数,就会进行链接绑定——将Foundation
中NSLog
的实在地址赋值到DATA 段
的NSLog
符号上
而自定义的 C 函数不会生成符号表,间接就是一个函数地址,所以 fishhook 的局限性就在于只有符号表内的符号能够 hook(从新绑定符号)
4.LLDB 调试
因为 NSLog
是个懒加载的符号,所以加了行代言代码
- 通过
image list
打印所有镜像资源——第一个就是过程的内存首地址 - 获取到 MachO 中 NSLog 的内存偏移量
应用计算器,内存首地址 + 内存偏移量 = 符号地址
- 通过
memory read 内存地址
读出符号地址
因为 iOS 是小端模式,内存地址 8 位倒着读
- 通过
dis -s 内存地址
查看汇编
此时的符号并没有绑定,因为还没有应用
- 过掉断点(应用 NSLog)再进行查看
此时的 NSLog 符号曾经被绑定
- 再跳到下一步(重绑定符号)进行查看
此时的 NSLog 符号曾经被重绑定了
5.fishhook 原理摸索
接下来剖析下rebind_symbols
:
prepend_rebindings
将rebindings
数组一直增加到_rebindings_head
链表的头部成为新的头节点-
判断链表
_rebindings_head->next
是否为空来判断是否是第一次调用- 若首次调用则应用
_dyld_register_func_for_add_image
进行回调监听 - 若不是则遍历调用
_rebind_symbols_for_image
- 若首次调用则应用
-
这个函数有个 Int 返回值
- 如果重定向胜利则返回 0
- 如果失败则返回 -1
接下来是 _rebind_symbols_for_image
对 MachO 文件的操作:依照规定计算各种表的地址和指针在表中的偏移量
最初一步,perform_rebinding_with_section
依据算好的符号表地址和偏移量,找到在符号表中用于指向共享库指标函数的指针,将该指针的值(即指标函数的地址)赋值给 rebinding
的*replaced
,最初批改该指针的值为replacement
(新的函数地址)
6.fishhook 原理的实际——在 MachO 中查找函数实现
接下来就依据字符串对应在符号表中的指针,找到其在共享库的函数实现的
- NSLog 在
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 的函数地址
7.fishhook 利用场景
既然 fishhook
能够替换 C 函数,那么就能够替换 method_exchangeImplementations
等函数来避免 HOOK,我的项目自身如果须要应用 method_exchangeImplementations
则能够应用新的函数地址来进行操作
- 攻:能够插入新的动静库提前进行 HOOK 办法,此时 fishhook 的防护就不起作用了
- 防:也能够提前插入动静库进行 fishhook 替换(逆向注入的动静库个别排在原我的项目的动静库之后),此时 fishhook 代码先于 hook 代码,所以还是 fishhook 还是失效的
- 攻:釜底抽薪——间接找到 fishhook 的函数地址进行批改,再次运行
- …
写在前面
在平安攻防畛域中,hook 原理起到至关重要的作用,而应用 fishhook 做防护尽管意义不大,然而理解 fishhook 原理对后续的学习还是挺有帮忙的,想理解更多逆向相干常识无妨动动小手,增加一下咱们的交换群 642363427 来为你的技术多添一份荣耀。