作者:WingKeii- | 来源:互联网 | 2023-09-07 18:58
对于存在别的动态库的函数,程序在运行的时候需要通过动态链接来获取函数的调用地址,在iOS上是通过dyld来实现的,下面来通过离子来做一下梳理-(void)vie
对于存在别的动态库的函数,程序在运行的时候需要通过动态链接来获取函数的调用地址,在iOS上是通过dyld来实现的,下面来通过离子来做一下梳理
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(@"first call");
NSLog(@"second call");
}
然后在两个NSLog都打上断点,运行。
程序断住之后,调节成汇编模式Debug –> Debug Workflow –> Always Show Disassembly,
留意到symbol stub for: NSLog 这句,这句就是跳转到NSLog的stub实现imp___stubs__NSLog
我们先用image list命令来获取模块的基地址
[ 0] 612400A7-6AEC-3714-842C-F83B079272BE 0x0000000100060000 /Users/apple/Library/Developer/Xcode/DerivedData/MachO的动态链接-dyjjhckpseuoonetiqiafxmmlsak/Build/Products/Debug-iphoneos/MachO的动态链接.app/MachO的动态链接
0x0000000100060000 是这次运行时模块的基地址,因为iOS有ASLR,每次都会随机装到内存,随机只是偏移,并不是打乱,所以程序运行时候的及地址为:静态的基地址 + ASLR偏移,静态的基地址可以使用machOview来查看
静态基地址 = 0000000100000000;
所以这次运行的偏移地址ASLR偏移 = 0x0000000100060000 – 0000000100000000 = 0x60000
回到刚刚的bl 0x1000e2560 ; symbol stub for: NSLog,我们可以去看看这个地址里面究竟是什么代码,先求出它对应的 静态地址=运行时地址-ASLR偏移 = 0x100066554 – 0x60000 = 0x100006554。然后用hopper打开,记住不勾选Resolve Lazy Bindings,跳转到该地址,如下
imp___stubs__NSLog:
0000000100006554 nop ; CODE XREF=-[ViewController viewDidLoad]+72, -[ViewController viewDidLoad]+84
0000000100006558 ldr x16, #0x100008008
000000010000655c br x16
这几句代码是取值之后跳转,先看看#0x100008008的值,单击点进去。这里有个坑,如果你用hopper导入的时候选择了Resolve Lazy Binding,那么这个地址会解析成了符号。所以导入的时候不要选,那么可以看到这个地址的值如下:
0000000100008008 dq 0x00000001000065fc ; DATA XREF=imp___stubs__NSLog+4
所以上面的几句汇编的意思是跳转0x00000001000065fc这个地址,然后我们再切到这个地址看看。
00000001000065fc ldr w16, #0x100006604 ; DATA XREF=_NSLog_ptr
0000000100006600 b 0x1000065e4
在这里压入一个参数之后,继续跳转到 0x1000065e4执行,继续跟入到这里,终于找到了stub_helper的调用。
,然后就会进入动态绑定
这个时候在终端si进入单步调试,发现第一次调用时候
NSLog方法跳转的是0x00000001000ce5fc,当第二次执行的时候,发现已经是拿到了真正的函数地址
但是这里其仍然是取的静态地址0x100008008处的值,只不过这个值已经变成了0x000000018248e598
我们可以打印一下静态地址0x1000e2560对应的动态地址0x1000e2560+ 0x60000 = 0x100068008,确实已经变成了0x000000018248e598
(lldb) x 0x100068008
0x100068008: 98 e5 48 82 01 00 00 00 98 e5 47 82 01 00 00 00 ..H.......G.....
0x100068018: 60 70 c2 87 01 00 00 00 14 66 06 00 01 00 00 00 `p.......f......
然后进入0x18248e598这个地址,你会发现这里就是NSLog的函数入口
Foundation`NSLog:
-> 0x18248e598 : sub sp, sp, #0x20 ; =0x20
0x18248e59c : stp x29, x30, [sp, #0x10]
0x18248e5a0 : add x29, sp, #0x10 ; =0x10
0x18248e5a4 : add x8, x29, #0x10 ; =0x10
0x18248e5a8 : str x8, [sp, #0x8]
0x18248e5ac : add x1, x29, #0x10 ; =0x10
0x18248e5b0 : mov x2, x30
0x18248e5b4 : bl 0x1825673c0 ; _NSLogv
0x18248e5b8 : ldp x29, x30, [sp, #0x10]
0x18248e5bc : add sp, sp, #0x20 ; =0x20
0x18248e5c0 : ret
附上刚刚image list的另外一个模块,你会发现0x000000018248e598这个地址已经是另外模块的了。
[ 2] 73FF2B76-D90F-3C90-B010-8F6E36E3B71F 0x000000018247c000 /Users/apple/Library/Developer/Xcode/iOS DeviceSupport/10.3.1 (14E304)/Symbols/System/Library/Frameworks/Foundation.framework/Foundation
这个就是MachO的懒加载符号表的加载过程。第一次调用的时候是指向stub_helper,调用完一次之后,就会修改懒加载符号指针,让其指向真正的函数地址,以后就可以直接跳转。这个可以从第一次调用和第二次调用的MachO二进制文件看到
从上面的图可以知道0x100008008 这个位置就是Lazy Symbol Pointers,并且从MachOView看到最后边的value值,0x100008008对应的就是NSLog,那这个对应关系是怎么来的呢?先看一下Dynamic Symbol Table 下的Indirect Symbols,点击右上角,搜索NSLog
发现这里有两条记录,且两条记录的值是一样的,而且看MachOview解析出来的value,你会发现一个和TEXT段的_stubs 有关系,一个和DATA段的_la_symbol_ptr 有关系,这个关系是从哪里来的呢?先从_la_symbol_ptr的来说明,先找到下面的地方,section header 的_la_symbol_ptr
首先Address指的是这个section的真实地址,这里你会发现Lazy Symbol Pointers的表的起始文件偏移就是这个地址减去基地址,然后是Indirect Sym Index这个字段,在代码里面是Reserved1这个属性,在MachOView里面显示的是十进制13,起始这是一个索引偏移,指的是Lazy Symbol Pointers表中对应元素的第几个,这里是13,然后Indirect Symbols么个元素是4个字节,所以这里距离Indirect Symbols表头是偏移13 * 4 = 52字节 = 0x34,然后Indirect Symbols表头的文件偏移是0xD358,两个之和0xD358 + 0x34 = 0xD38C;
到这里,已经找到了Lazy Symbol Pointers表和Indirect Symbols表的对应关系,至于stubs表和Indirect Symbos表,同理可得。
跟住下来就是Indirect Symbols表是怎么找到”_NSLog” 这个字符的,留意上面的Data值000000D3,转为十进制是211,这个数值对应的就是Symbol Table的元素索引,找到Symbol Table 的第211个元素,如下图:
留意Data值000000CE,这个就是string Table的偏移,继续去到String Table,它的起始文件偏移是0000D3BC
然后找到文件偏移的位置,0000D3BC + 000000CE = 0xD48A