背景与思路:
本文严重参照《如何定位 Obj-C 野指针随机 Crash ( 三篇 ) 》
橙其第一篇,提高野指针的出现 crash 的概率
因为野指针引起的崩溃,不是必现
已经分配的内存,与指向他的指针,存在 1 对多的关系
分配的内存,被标记回收了,随时可能被写入
写入的内容的随机的,体现出野指针, 崩不崩溃,啥时候崩溃的随机性质
这个时候,使用苟活的指针,很可能取出错误的内容,再去执行原来类的方法
一般出现这个错误,EXC_BAD_ACCESS
思路
第一篇采用手动代码,内存污染的形式,
通过 fishhook
, hook 对象的 free 方法,
拿到 free 的对象指针,涂改为 0x55
memset(obj, 0x55, memSiziee);
将随机问题,确定化。只要 free 掉,就不是原来的了
第 2 篇,极限提高野指针的出现 crash 的概率
该释放的内存,有可能我们代码涂成了 0x55,
苹果的操作系统,又分配了同样类型的对象,
( 起始地址,也相同 )
这样,还是不清楚崩溃的情况,
上文的操作,白费。
野指针保活思路
- 使用一个队列 ( 数组 ),添加
将被释放 ( 篡改 )的内存,( 然后被涂抹为 0x55 )
对应的指针, 来保活
- 这个队列里面,除了有数组,还有一把互斥锁,
保证操作安全
pthread_mutex_t mutate
- 对内存,有一个自动的伸缩
//最多存这么多内存,大于这个值就释放一部分
#define MAX_STEAL_MEM_SIZE 1024*1024*100
//最多保留这么多个指针,再多就释放一部分
#define MAX_STEAL_MEM_NUM 1024*1024*10
第 3 篇,野指针崩溃定位
野指针导致崩溃后,输出 crash 的定位信息,类名和方法
- 对象要释放的时候,重写他的 isa , 是一个非常好的时机
// 运行时,拿到对象的类信息
Class origClass= object_getClass(obj);
// 重写要释放的对象的 isa, 指定为一个代理类,这样拿到崩溃的方法
object_setClass(obj, [MOACatcher class]);
// 拿到对象的类信息,便于输出
((MOACatcher *)obj).originClass = origClass;
重写代理类的方法转发方法,拿到报错的 selector
,
配合之前拿到的类名
@interface MOACatcher : NSProxy
// ...
- (NSMethodSignature *)methodSignatureForSelector: (SEL)sel{
return [self.originClass instanceMethodSignatureForSelector:sel];
}
- (void)forwardInvocation: (NSInvocation *)invocation{
[self _throwMessageSentExceptionWithSelector: invocation.selector];
}
变化与前置
《三篇》写于 2018.3, 4 年前
原文的野指针不 crash 例子,被苹果优化了
UIView * testObj = [[UIView alloc] init];
[testObj release];
// 崩溃,是必现
[testObj setNeedsLayout];
ARC 环境下,使用平时开发的 API, 写出一个简单的野指针不崩溃的 demo
有点难度,首先要,能够编译
认识野指针
C 语言:
- 野指针产生
void main()
{
/* 野指针,指针没有被初始化,
该指针指向任意的内存位置,
可能导致程序崩溃,或者表现奇怪 */
int *p;
/* 随机位置的内存,被污染了 */
*p = 12;
}
我们希望的例子是,指针分配了内存,内存被回收,指针还在
- 正常的代码
void main()
{
int *p = (int *)malloc(sizeof(int));
*p = 12;
}
iOS app 中的野指针
C++ 代码产生的
void dooo(void){
// 分配 10 个 int 类型的内存
int* p1 = (int*)malloc(40);
for(int i = 0;i < 10;i++){
*(p1 + i) = i;
}
free(p1);
for(int i = 0;i < 10;i++){
//看野指针的效果
cout << *(p1 + i) << endl;
}
cout << "end ... " << endl;
}
执行的效果:
1884647424
8037
283
3
4
5
6
7
8
9
end ...
涂抹后,
free 的时候,先memset(p, 0x55, memCapacity);
1152095584
37299
1431634034
1431655765
1431655765
1431655765
1431655765
1431655765
1431655765
1431655765
end ...
可以看出,涂抹到内存的作用
野指针较高概率是 C 代码,C++ 代码,
2022 的 ARC, OK 吧