iOS的hook介绍,fishhook的调试理解
全栈老韩
全栈工程师,擅长iOS App开发、前端(vue、react、nuxt、小程序&Taro)开发、Flutter、React Native、后端(midwayjs、golang、express、koa)开发、docker容器、seo优化等。
HOOK
Method Swizzle
利用OC的runtime特性,动态改变SEL(方法编号)和IMP(方法实现)的对应关系,达到OC方法调用流程改变的目的。主要用于OC方法。
fishhook
它是facebook提供的一个动态修改链接mach-O文件的工具。利用MachO文件加载原理,通过修改懒加载和非懒加载两个表的指针达到C函数HOOK的目的。
hook C函数
引入
fishhook.h和fishhook.c
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// HOOK 交换
/**
struct rebinding {
const char *name; // 需要HOOK的函数名称,C字符串
void *replacement; // 新函数的地址
void **replaced; // 用来保存原始函数地址的指针
};
*/
struct rebinding nslog;
nslog.name = "NSLog";
nslog.replaced = (void *) &sys_nslog; // 保存系统函数的地址的指针的指针
nslog.replacement = myNSlog; // 新函数的地址
struct rebinding rebs[1] = {nslog};
/**
用于重新绑定符号
一次性交换多个函数,
arg1:存放rebinding结构体的数组
arg2:数组的长度
*/
rebind_symbols(rebs, 1);
}
// ----更改系统的NSLog函数调用
// 函数指针,用来保存原始的函数地址!
static void(*sys_nslog)(NSString *str, ...);
// 定义一个新的函数
void myNSlog(NSString *str, ...) {
str = [str stringByAppendingString:@"\n钩上了!"];
// 由于系统的内部实现不知道
sys_nslog(str);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"点击了屏幕!");
}
hook 自定义的C函数 (勾不住)
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self funcDemo];
}
// 交换自定义函数
- (void)funcDemo {
rebind_symbols((struct rebinding[1]){{"func", newFunc, (void *)&funcP}}, 1);
}
// 保存原始函数的指针
static void(*funcP)(const char *);
// 新函数
void newFunc(const char * str) {
NSLog(@"勾住了");
funcP(str);
}
// 自定义的函数
void func(const char * str) {
NSLog(@"%s", str);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
func("点击了屏幕");
}
底层汇编解释
- C函数是静态的
- PIC 位置独立代码
- DYLD 将可执行文件加载到内存中,将NSLog的地址绑定到符号symbol,一开始符号symbol都是0x0,当APP被DYLD加载到内存中运行的时候,DYLD会把symbol中的指针指向内存中的UIKit, UIFoundation中的函数地址。
在NSLog函数的hook代码中,在rebind操作时打一个断点,然后在xCode终端看底层汇编的内容:
- 命令
image list可以查看fishhook.app的exec可执行文件在内存中的地址:
image list
[ 0] DC3F7324-1654-3F1B-8A0A-2E8E9B228968 0x0000000108bf5000 /Users/hanweixing/Library/Developer/Xcode/DerivedData/FishHook-egipaudxwxzvacdyvhhoisbqvjio/Build/Products/Debug-iphonesimulator/FishHook.app/FishHook
[ 1] CE635DB2-D47E-3C05-A0A3-6BD982E7E750 0x000000010bbd1000 /usr/lib/dyld
[ 2] 528E1F55-F655-3533-99B9-7EAE1DAE5D07 0x0000000108c01000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/dyld_sim
...
得到可执行文件的内存地址:0x0000000108bf5000
2. 通过MachOView软件,打开fishhook.app的可执行文件exec,在__la_symbol_ptr的 Lazy Symbol Pointers中可以看到NSLog的内存偏移:00004028
- 在Xcode中读取
NSLog对应的symbol地址:
(lldb) x 0x0000000108bf5000+0x4028
0x108bf9028: a0 79 bf 08 01 00 00 00 89 b4 f7 08 01 00 00 00 .y..............
0x108bf9038: 36 0f d8 0c 01 00 00 00 e6 79 bf 08 01 00 00 00 6........y......
- 取前面的8个字节,然后倒序拼接,就可以将
symbol中的值转换成汇编代码:(可以看到这是系统Foundation中的NSLog)
(lldb) dis -s 0x0108bf79a0
Foundation`NSLog`:
0x108bf79a0: pushq $0x0
0x108bf79a5: jmp 0x108bf7990
0x108bf79aa: pushq $0xd
0x108bf79af: jmp 0x108bf7990
0x108bf79b4: pushq $0x118 ; imm = 0x118
0x108bf79b9: jmp 0x108bf7990
- 将断点跳一步,继续在Xcode中进行调试,读取最新的
symbol中的值,可以发现被替换成了我们替换的函数实现。
(lldb) x 0x0000000108bf5000+0x4028
0x108bf9028: e0 6d bf 08 01 00 00 00 89 b4 f7 08 01 00 00 00 .m..............
0x108bf9038: 36 0f d8 0c 01 00 00 00 31 71 91 0b 01 00 00 00 6.......1q......
(lldb) dis -s 0x0108bf6de0
FishHook`myNSlog:
0x108bf6de0 <+0>: pushq %rbp
0x108bf6de1 <+1>: movq %rsp, %rbp
0x108bf6de4 <+4>: subq $0x30, %rsp
0x108bf6de8 <+8>: movq $0x0, -0x8(%rbp)
0x108bf6df0 <+16>: leaq -0x8(%rbp), %rax
0x108bf6df4 <+20>: movq %rdi, -0x10(%rbp)
0x108bf6df8 <+24>: movq %rax, %rdi
0x108bf6dfb <+27>: movq -0x10(%rbp), %rsi
(lldb)
Cydia Substrate (主要用在逆向)
Cydia Substrate原名为Mobile Substrate,它的主要作用是针对OC方法、C函数以及函数地址进行HOOK操作。当然它不仅仅是针对iOS而设计的,安卓一样可以用。
官方地址:http://www.cydiasubstrate.com/
发布于2024-01-31 02:44:48
浏览量52·
暂无评论,快来发表第一条评论吧