C野指针随机Crash(三):加点黑科技让Crash自报家门

本文主要介绍如何利用OC Runtime的特性,让OC野指针对象主动抛出自己的信息,秒杀某些全系统栈Crash。

陈其锋,腾讯SNG即通产品部音视频技术中心软件工程师,主要负责iOS平台音视频功能开发,热衷于移动开发,以及各类APP体验。

(注:本文由于涉及一些技术比较猥琐,可能会引起处女座同学的不适,如果有任何疑问欢迎一起讨论。另外,本文只讨论Arm 32位情况)

为什么错误地址是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, #8]!

0x2f879f76 <+54>: b 0x2f879f5a ; <+26>

0x2f879f78 <+56>: ldr.w r9, [r0]

0x2f879f7c <+60>: b 0x2f87a1c0 ; _objc_msgSend_uncached

0x2f879f7e <+62>: mov.w r1, #0x0

0x2f879f82 <+66>: bx lr

我们可以结合Obj-C类的内存布局再来解读一下上面的汇编代码(节选于Obj-C类的源代码):

struct objc_class : objc_object {

// Class ISA;

Class superclass;

cache_t cache;

uintptr_t data_NEVER_USE; // class_rw_t * plus custom rr/alloc flags

class_rw_t *data() {

return (class_rw_t *)(data_NEVER_USE & ~CLASS_FAST_FLAG_MASK);

}

void setData(class_rw_t *newData) {

uintptr_t flags = (uintptr_t)data_NEVER_USE & CLASS_FAST_FLAG_MASK;

data_NEVER_USE = (uintptr_t)newData | flags;

}

……..

struct cache_t {

struct bucket_t *buckets;

mask_t shiftmask;

mask_t occupied;

……..

struct bucket_t {

cache_key_t key;

IMP imp;

……

typedef uintptr_t cache_key_t;

根据苹果的函数调用约定,objc_msgSend被调用的时候,寄存器对应关系:r0是对象本身self,r1是sel,r2和r3是参数。根据objc_class的声明,我们可以知道:

0x2f879f40 <+0>: cbz r0, 0x2f879f7e //如果self为0就跳转到0x2f879f7e。给nil发消息的话就什么都不做

0x2f879f42 <+2>: ldr.w r9, [r0] //取对象的类到r9

0x2f879f46 <+6>: ldrh.w r12, [r9, #0xc] //取类的偏移#0xc的数据到r12,也就是shiftmask的值

0x2f879f4a <+10>: ldr.w r9, [r9, #0x8] //取类的偏移#0x8的成员到r9,也即是cache

0x2f879f4e <+14>: and.w r12, r12, r1 //r1和shiftmask与,放到r12,r1是参数一,也就是sel,用来计算sel的index

有理想在的地方,地狱就是天堂

C野指针随机Crash(三):加点黑科技让Crash自报家门

相关文章:

你感兴趣的文章:

标签云: