iOS之fishhook
fishhook
fishHook是Facebook提供的一个动态修改链接mach-O文件的工具。利用MachO文件加载原理,通过重新绑定(rebind_symbols)懒加载表(Lazy Symbol Pointers)和非懒加载表(Non-Lazy Symbol Pointers)这两个表的指针达到C函数HOOK的目的。
dyld加载macho文件和系统框架,对函数地址复制的过程,成为 “符号绑定“
调试分析fishHook
1 |
|
通过machoView查看符号表(Lazy Symbol Pointers)的偏移量(比如NSLog的偏移量是) 0x00005000
在
rebind_symbols(rebs, 1);
下断点,通过image list查看加载的macho文件和所有的框架
1
2
3
4
5
6
7
8
9(lldb) image list
[ 0] E10158F5-413E-35E1-A349-8AFA71FFC955 0x0000000103db1000 /Users/cola/Library/Developer/Xcode/DerivedData/fishhook-dduxiymrdkidvtcxqmuqqwanbexr/Build/Products/Debug-iphonesimulator/fishhook.app/fishhook
[ 1] EEA931D0-403E-3BC8-862A-CBA037DE4A74 0x00000001048e3000 /usr/lib/dyld
[ 2] 75369F31-702D-364A-95C3-8AFA9DD4B3A2 0x0000000103dbf000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/dyld_sim
[ 3] 56E47800-2CCB-3B7D-B94B-CCF5F13D6BCF 0x00007fff256b8000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/Foundation.framework/Foundation
[ 4] 3EC683F6-36EF-33E1-8B98-C95E12BA38D2 0x00007fff513f6000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/libobjc.A.dylib
[ 5] 7881AD7F-524C-3CFA-9595-02ED549166AA 0x00007fff4ff15000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/libSystem.B.dylib
.......macho文件的首地址+第一步的偏移量,就是NSLog的真实地址,读NSLog的符号表地址
memory read 简写 x 0x0000000103db1000+0x00005000
1
2
3(lldb) memory read 0x0000000103db1000+0x00005000
0x103db6000: fa 2d 76 25 ff 7f 00 00 f8 d1 74 25 ff 7f 00 00 .-v%......t%....
0x103db6010: f8 26 09 48 ff 7f 00 00 44 34 db 03 01 00 00 00 .&.H....D4......其中前八个字节为NSLog的地址
查看4中的前八个字节的地址的汇编代码1
2
3
4
5
6
7
8
9(lldb) dis -s 0x7fff25762dfa
Foundation`NSLog:
0x7fff25762dfa <+0>: pushq %rbp
0x7fff25762dfb <+1>: movq %rsp, %rbp
0x7fff25762dfe <+4>: subq $0xd0, %rsp
0x7fff25762e05 <+11>: testb %al, %al
0x7fff25762e07 <+13>: je 0x7fff25762e2f ; <+53>
0x7fff25762e09 <+15>: movaps %xmm0, -0xa0(%rbp)
0x7fff25762e10 <+22>: movaps %xmm1, -0x90(%rbp)从中可以看见NSLog的地址
单步执行,执行fishhook的rebind_symbols方法后查看NSLog的真实地址
和4相比地址已经改变
1
2
3(lldb) memory read 0x000000010f550000+0x00005000
0x10f555000: 50 14 55 0f 01 00 00 00 f8 d1 74 25 ff 7f 00 00 P.U.......t%....
0x10f555010: f8 26 09 48 ff 7f 00 00 54 99 34 52 ff 7f 00 00 .&.H....T.4R....查看前八个字节对应地址的汇编代码,发现已经被替换成我们的myNSlog方法
1
2
3
4
5
6
7
8
9
10
11(lldb) dis -s 010f551450
fishhook`myNSLog:
0x10f551450 <+0>: pushq %rbp
0x10f551451 <+1>: movq %rsp, %rbp
0x10f551454 <+4>: subq $0x30, %rsp
0x10f551458 <+8>: movq $0x0, -0x8(%rbp)
0x10f551460 <+16>: leaq -0x8(%rbp), %rax
0x10f551464 <+20>: movq %rdi, -0x10(%rbp)
0x10f551468 <+24>: movq %rax, %rdi
0x10f55146b <+27>: movq -0x10(%rbp), %rsi
(lldb)调用原方法
因为系统默认绑定的地址被保存到了,自定义的变量中,可以通过地址调用该方法。
1
2//函数指针
static void (*sys_nslog)(NSString * _Nonnull format, ...);调用原方法
1
2
3
4
5//定义一个新的函数
void myNSLog(NSString * _Nonnull format, ...){
format = [format stringByAppendingFormat:@"\n HOOK成功🐂"];
sys_nslog(format);
}
fishHOOK原理
重新绑定符号表
懒加载符号表与indirect symbols表一一对应

indirect symbols表中NSLog的Data值为A6

A6转为10进制为166,其中166对应着Symbols Table下的Symbols

其中 #166在String Table Index表的偏移为D7,符号表基址为83AC,NSLog的地址为 83AC+D7=8483

而fishHook则是逆过程,通过String Table表NSLog的地址,找到Lazy Symbols Pointers(懒加载符号表)中的NSLog符号表地址,获取懒加载符号表的偏移.
fishHOOK能做什么
应用安全防护:
将系统敏感函数,真实地址保存,并将该方法设置为宏定义,以后使用宏定义设置的方法,别人就无法HOOK该方法了,因为你使用的是函数的真实地址,并不是调用符号表中的函数地址,这样别人修改符号表是不影响程序的。
注意:
三方框架有可能会用到该函数,如果fishhook加载的过早会导致三方框架失效。
启动优化、埋点、AOP
- 定位所有方法的调用,hook objc_msgSend函数?
- 二进制重排(layout)的目的在于将hot code聚合在一起,即使得最经常执行的代码或最需要关键执行的代码(如启动阶段的顺序调用)聚合在一起,形成一个更紧凑的__TEXT段。