dyld 多线程越界

crash堆栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Thread 32 Crashed:
0 _demo_ 0x0000000101a43854 0x1006ac000 + 20543572 +[xxx getImageInfo:path:targetSlide:validate:]
1 _demo_ 0x0000000101a43f2c 0x1006ac000 + 20545324
2 _demo_ 0x0000000101a43f2c 0x1006ac000 + 20545324
3 _demo_ 0x0000000101a35594 0x1006ac000 + 20485524
4 _demo_ 0x0000000101a35528 0x1006ac000 + 20485416
5 libdispatch.dylib 0x00000001b75e7610 0x1b758c000 + 374288
6 libdispatch.dylib 0x00000001b75e8184 0x1b758c000 + 377220
7 libdispatch.dylib 0x00000001b7594464 0x1b758c000 + 33892
8 libdispatch.dylib 0x00000001b7594e58 0x1b758c000 + 36440
9 libdispatch.dylib 0x00000001b759e340 0x1b758c000 + 74560
10 libsystem_pthread.dylib 0x00000001b7637fa4 0x1b762c000 + 49060

Binary Images:
0x1006ac000 - 0x103d33fff +_demo_ arm64 <c7c1a8cc055a37478b7a1b96e5d6fd4f> /private/var/containers/Bundle/Application/E7B81670-67AC-4AD9-9F1B-16A841E459BD/_demo_.app/_demo_

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
+ (NSDictionary *)getImageInfo:(const mach_header_t *)header path:(const char *)path targetSlide:(long)targetSlide validate:(BOOL)validate {
const char* name = path;
const char* tmp = strrchr(name, '/');
if (tmp) {
name = tmp + 1;
}
segImageInfo imageData = {0};
segImageInfo *image = &imageData;
image->cputype = header->cputype;
image->cpusubtype = header->cpusubtype;
long offset = (long)header + sizeof(mach_header_t);
...
}

crash现场还原

1
2
3
4
0   _demo_                        0x0000000101a43854 0x1006ac000 + 20543572 +[xxx getImageInfo:path:targetSlide:validate:]

Binary Images:
0x1006ac000 - 0x103d33fff +_demo_ arm64 <c7c1a8cc055a37478b7a1b96e5d6fd4f> /private/var/containers/Bundle/Application/E7B81670-67AC-4AD9-9F1B-16A841E459BD/_demo_.app/_demo_
  • 计算偏移量

0x0000000101a43854 - 0x1006ac000 = 20543572

  • 在xcode调试控制台 获取ipa包的 载入地址

image list demo

1
2
(lldb) image list _demo_
[ 0] C7C1A8CC-055A-3747-8B7A-1B96E5D6FD4F 0x00000001000e0000 /Users/winnchen/Library/Developer/Xcode/DerivedData/BSUITestProject-adqfyjzmhqfposhenyppejsiglud/Build/Products/Debug-iphoneos/BSUITestProject.app/_demo_
  • 计算实际地址,设置断点

0x00000001000e0000 + 20543572 = 0x0000000101477854

1
breakpoint set -a 0x0000000101477854

分析寄存器与变量之间的对应关系

进入断点后,进行进一步的分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
_demo_`+[xxx getImageInfo:path:targetSlide:validate:]:
0x10147781c <+0>: sub sp, sp, #0x150 ; =0x150
0x101477820 <+4>: stp x28, x27, [sp, #0xf0]
0x101477824 <+8>: stp x26, x25, [sp, #0x100]
0x101477828 <+12>: stp x24, x23, [sp, #0x110]
0x10147782c <+16>: stp x22, x21, [sp, #0x120]
0x101477830 <+20>: stp x20, x19, [sp, #0x130]
0x101477834 <+24>: stp x29, x30, [sp, #0x140]
0x101477838 <+28>: add x29, sp, #0x140 ; =0x140
0x10147783c <+32>: str x4, [sp, #0x30]
0x101477840 <+36>: mov x26, x2
0x101477844 <+40>: adrp x8, 8947
0x101477848 <+44>: ldr x8, [x8, #0xcb0]
0x10147784c <+48>: ldr x8, [x8]
0x101477850 <+52>: stur x8, [x29, #-0x58]
-> 0x101477854 <+56>: ldp w8, w9, [x26, #0x4]
0x101477858 <+60>: ldr w19, [x26, #0x10]
0x10147785c <+64>: str x3, [sp, #0x20]

此处,正在读取 x26 + 0x4 的值,对应代码:

1
image->cputype = header->cputype;

沿着数据流向分析:

1
2
3
4
x26 来自 x2,
x2 为第1个参数 header;
x3 为第2个参数 path;
x23 来自 循环遍历 _dyld_get_image_header(int i) 的计数器 i

从 crash 报告的保留 register 来看:

1
2
3
4
5
6
7
8
9
10
Thread 32 crashed with ARM-64 Thread State:
cpsr: 0x0000000060000000 fp: 0x00000001720126e0 lr: 0x0000000101a43f2c pc: 0x0000000101a43854
sp: 0x00000001720125a0 x0: 0x000000010462bf28 x1: 0x0000000103852073 x10: 0x0000000283224200
x11: 0x000f000283224200 x12: 0x0000000283224230 x13: 0x000000010462bf50 x14: 0x7feffffffffffffe
x15: 0x7000000000000000 x16: 0x000000010462bf50 x17: 0x0000000101a4381c x18: 0x0000000000000000
x19: 0x0000000283745880 x2: 0x0000000000000000 (const mach_header_t *)header x20: 0x0000000000000000 x21: 0x0000000000000000
x22: 0x0000000000000219 x23: 0x0000000000000218 (for loop : count) x24: 0x00000001038520cf x25: 0x00000001ed3e40fe
x26: 0x0000000000000000 x27: 0x00000001ed3dbec1 x28: 0x00000001045a5000 x29: 0x00000001720126e0
x3: 0x0000000000000000 (const char *)path x4: 0x0000000000000000 (long)targetSlide x5: 0x0000000000000000 (BOOL)validate x6: 0x0000000000000000
x7: 0x0000000000000000 x8: 0x169c41e5cecf004f x9: 0x0000000103852073

几个重要的参数 x2, x26, x3 均为 0x0, 说明从 dyld_xxx 返回的数据有问题,而 x24 保留有 loop count,说明这个值是异常的

经过测试,给 dyld_xxx 使用一个特别大的值越界之后返回的正是 0x0, 说明 image 在动态增加的时候这个接口是不安全的,有可能导致在另外一个线程当中读取出来 0x0的数据,就会造成 crash,这里由于没有办法同步知道这些 image 的加载变更,所以需要判断 header, path 等参数是否为 null 进行简单校验。