内存越界

crash堆栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Thread 10 name:  xxx
Thread 10 Crashed:
0 _demo_ 0x0000000100ae249c _mach_copyMem (in _demo_) (BacktraceLogger.m:516)
1 _demo_ 0x0000000100ae17e8 xxx
2 _demo_ 0x0000000100ae166c xxx
3 _demo_ 0x0000000100ae1ca4 xxx
4 _demo_ 0x0000000100ae1488 xxx
5 _demo_ 0x0000000100ad8a1c xxx
6 CoreFoundation 0x00000001ac852718 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ (in CoreFoundation) + 28
7 CoreFoundation 0x00000001ac852448 __CFRunLoopDoTimer (in CoreFoundation) + 864
8 CoreFoundation 0x00000001ac851c7c __CFRunLoopDoTimers (in CoreFoundation) + 248
9 CoreFoundation 0x00000001ac84cb58 __CFRunLoopRun (in CoreFoundation) + 1880
10 CoreFoundation 0x00000001ac84c0e0 CFRunLoopRunSpecific (in CoreFoundation) + 436
11 CoreFoundation 0x00000001ac84ce7c CFRunLoopRun (in CoreFoundation) + 80
12 _demo_ 0x0000000100ad8b10 xxx
13 _demo_ 0x0000000100ad88e4 xxx
14 Foundation 0x00000001ad37523c __NSThread__start__ (in Foundation) + 1040
15 libsystem_pthread.dylib 0x00000001ac4dc25c _pthread_body (in libsystem_pthread.dylib) + 128
16 libsystem_pthread.dylib 0x00000001ac4dc1bc _pthread_start (in libsystem_pthread.dylib) + 48


Binary Images:
0x100250000 - 0x1011c3fff +_demo_ arm64 <71fd4f4612913b2da625f5eb515bcb9d> /var/containers/Bundle/Application/6BBDF1E8-CCFA-4FDC-B9BA-98C02A179BC3/_demo_.app/_demo_

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
kern_return_t mach_copyMem(const void *const src, void *const dst, const size_t numBytes, int useSafeRange, vm_address_t safeStart, vm_address_t safeEnd) {
if (useSafeRange && numBytes == sizeof(void *) * 2) {
vm_address_t srcAddr = (vm_address_t)src;
if (srcAddr > 0x10
&& srcAddr >= safeStart
&& srcAddr < safeEnd) {
memcpy(dst, src, numBytes);
return KERN_SUCCESS;
}
}
vm_size_t bytesCopied = 0;
return vm_read_overwrite(mach_task_self(), (vm_address_t)src, (vm_size_t)numBytes, (vm_address_t)dst, &bytesCopied);
}

crash现场还原

1
2
3
4
0   _demo_                       0x0000000100ae249c _mach_copyMem (in _demo_) (BacktraceLogger.m:516)

Binary Images:
0x100250000 - 0x1011c3fff +_demo_ arm64 <71fd4f4612913b2da625f5eb515bcb9d> /var/containers/Bundle/Application/6BBDF1E8-CCFA-4FDC-B9BA-98C02A179BC3/_demo_.app/_demo_
  • 计算偏移量

0x0000000100ae249c - 0x100250000 = 0x000000000089249c

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

image list demo

1
2
(lldb) image list _demo_
[ 0] 71FD4F46-1291-3B2D-A625-F5EB515BCB9D 0x00000001000e0000 /Users/winnchen/Library/Developer/Xcode/DerivedData/BSUITestProject-dnxfwcbufkxrwkeokoqpxlkvnphw/Build/Products/Debug-iphoneos/BSUITestProject.app/_demo_
  • 计算实际地址,设置断点

0x00000001000e0000 + 0x000000000089249c = 0x000000010097249c

1
breakpoint set -a 0x000000010097249c

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
_demo_`___lldb_unnamed_symbol39215$$_demo_:
0x10097246c <+0>: mov x8, x1
0x100972470 <+4>: mov x9, x0
0x100972474 <+8>: cmp x2, #0x10 ; =0x10
0x100972478 <+12>: b.ne 0x1009724a8 ; <+60>
0x10097247c <+16>: cbz w3, 0x1009724a8 ; <+60>
0x100972480 <+20>: cmp x9, #0x11 ; =0x11
0x100972484 <+24>: b.lo 0x1009724a8 ; <+60>
0x100972488 <+28>: cmp x9, x4
0x10097248c <+32>: b.lo 0x1009724a8 ; <+60>
0x100972490 <+36>: cmp x9, x5
0x100972494 <+40>: b.hs 0x1009724a8 ; <+60>
0x100972498 <+44>: mov w0, #0x0
-> 0x10097249c <+48>: ldr q0, [x9]
0x1009724a0 <+52>: str q0, [x8]
0x1009724a4 <+56>: ret

此处ldr,str指令是memcpy优化生成的指令,用于这种size对齐的内存读写。

对应代码:

1
memcpy(dst, src, numBytes);

沿着数据流向分析:

1
2
x9,来自x0,而x0对应 第1个参数src;
x8,来自x1,对应 第2个参数dst;

此处从src处读取发生crash,说明dst是一个不可读的地址。

接着分析进入这个代码分支的变量值

1
2
3
if (srcAddr > 0x10
&& srcAddr >= safeStart
&& srcAddr < safeEnd) {

可以看到safeStart和safeEnd是比较重要的两个值,在执行到0x10097249c的时候这些值对应x4,x5

将x8, x9, x4, x5的数据从crash报告当中读取出来

1
2
x4: 0x000000016fab4000(safeStart)     x5: 0x000000016fbb0000(safeEnd)
x8: 0x00000001703dc580(dst) x9: 0x000000016fbaffff(src)

此处问题就明显了,虽然src依然位于 safeStart 和 safeEnd之间,但是由于 safeEnd - src = 1,也就是说只有1个字节是可读的,因此这里读取的时候就越过了safeEnd,产生了越界。