本文主要介绍如何利用OC Runtime的特性,让OC野指针对象主动抛出自己的信息,秒杀某些全系统栈Crash。
陈其锋,腾讯软件开发工程师,主要负责iOS平台音视频功能开发,热衷于移动开发,以及各类APP体验。
(注:本文由于涉及一些技术比较猥琐,可能会引起处女座同学的不适,如果有任何疑问欢迎一起讨论。另外,本文只讨论Arm 32位情况)
为什么错误地址是0x55555561?
我们在前文里曾经介绍过在内存释放后填充0x55使野指针出现后数据不能访问,从而使野指针变成了必现的方法,那这里会有一个比较奇怪的问题:我们在释放的内存上填上了0x55,但为什么大部分时候野指针Crash了,出错的地址却是0x55555561?
为了解答这个问题,我们可以先看看Crash栈,就会发现这些Crash都是在objc_msgSend上。我们知道Obj-C的对象方法调用是通过objc_msgSend进行的,我们通过野指针访问一个对象的方法也一样,其实是通过objc_msgSend给已经释放的对象发了一条消息。
而objc_msgSend的函数签名是这样:
id objc_msgSend(id self, SEL op, ...)
我们再来看看objc_msgSend的代码:
libobjc.A.dylib`objc_msgSend: 0x2f879f40 <+0>: cbz r0, 0x2f879f7e ; <+62> 0x2f879f42 <+2>: ldr.w r9, [r0] 0x2f879f46 <+6>: ldrh.w r12, [r9, #0xc] 0x2f879f4a <+10>: ldr.w r9, [r9, #0x8] 0x2f879f4e <+14>: and.w r12, r12, r1 0x2f879f52 <+18>: add.w r9, r9, r12, lsl #3 0x2f879f56 <+22>: ldr.w r12, [r9] 0x2f879f5a <+26>: teq.w r12, r1 0x2f879f5e <+30>: bne 0x2f879f66 ; <+38> 0x2f879f60 <+32>: ldr.w r12, [r9, #0x4] 0x2f879f64 <+36>: bx r12 0x2f879f66 <+38>: cmp.w r12, #0x1 0x2f879f6a <+42>: blo 0x2f879f78 ; <+56> 0x2f879f6c <+44>: it eq 0x2f879f6e <+46>: ldreq.w r9, [r9, #0x4] 0x2f879f72 <+50>: ldr r12, [r9