我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情
前言
前段时间工作中,收到了安全组对我们项目隐私政策获取的一项修改任务。大概就是检测到系统Api中UIDevice的identifierForVendor属性(设备id)。在用户没同意隐私政策之前就调用了。安全组还打印出来,调用了此属性的类名以及方法。非常遗憾的是,这个类应该是Bugly内部封装的类,我们无法直接获取,那我们要如何进行方法交换呢?就有了接下来的文章。
案例分析
因为通过安全组得知,一个Bugly静态库的内部类BLYDevice的idfv方法调用了IDevice的identifierForVendor属性,导致触发了安全警告。所以首先我们需要确认我们App里面是否有BLYDevice这个类,且调用了idfv这个方法。
runtime方法查找
所以我们第一步需要通过runtime方法查找,由于我们BLYDevice这个类是Bugly静态库内部的,所以我们需要通过类名转Class,再尝试遍历该类的方法。代码如下。
Class dev = NSClassFromString(@"BLYDevice");
unsigned int methodCount =0;
Method* methodList = class_copyMethodList(dev,&methodCount);
NSMutableArray *methodsArray = [NSMutableArray arrayWithCapacity:methodCount];
for(int i=0;i<methodCount;i++)
{
Method temp = methodList[i];
const char* name_s =sel_getName(method_getName(temp));
NSString *name = [NSString stringWithUTF8String:name_s];
[methodsArray addObject:name];
}
free(methodList);
NSLog(@"====> BLYDevice 实例 methodList : %@", methodsArray.description);
通过结果得知Bugly内部确实有BLYDevice这个类,但是实例方法里面并没有idfv这个方法,难度出错了?这时候我们就要想到会不会是类方法没有打印出来?我们继续查找类方法。
把dev转成object_getClass(dev)这样打印出来的方法列表就是类方法了。
Method* methodList = class_copyMethodList(object_getClass(dev),&methodCount);
结果如下:
原来确实有idfv这个方法,接下来就好办了,我们只需用同样的方法,通过类名转Class。再进行方法交换此方法即可。
runtime方法替换
注意:由于是替换类方法,与常规替换实例方法稍有不同,类方法是通过class_getClassMethod,而实例方法则是通过class_getInstanceMethod获取。具体代码如下:
//从类名获取Bugly工具类
Class dev = NSClassFromString(@"BLYDevice");
//获取元类
Class spc = class_getSuperclass(dev);
SEL sel = NSSelectorFromString(@"idfv");
//步骤一
Method originIdfv = class_getClassMethod(dev, sel);
if (!originIdfv) {
return;
}
Method jf_bugly_uuid = class_getClassMethod([self class], @selector(jf_bugly_uuid));
class_addMethod(spc, @selector(jf_bugly_uuid), method_getImplementation(originIdfv), method_getTypeEncoding(originIdfv));
class_addMethod(spc, sel, method_getImplementation(jf_bugly_uuid), method_getTypeEncoding(jf_bugly_uuid));
method_exchangeImplementations(originIdfv, jf_bugly_uuid);
+ (NSString *)jf_bugly_uuid {
//如果检测到隐私弹窗没同意,不拿值。 这里可以自己拿持久化的值。
BOOL needShowAlert = NO;
if (needShowAlert) {
NSString *unknowID = @"00000000-0000-0000-0000-000000000000";
return unknowID;
}
NSString *buglyUUID = [self jf_bugly_uuid];
NSLog(@"buglyUUID: %@ ",buglyUUID);
return buglyUUID;
}
结论
以上通过方法交换,Bugly内部私有类BLYDevice的idfv方法成功的替换成了我定义jf_bugly_uuid方法,需要隐私权限同意后再获取设备id。理论上此替换方式,可以替换所有私有类(但是苹果系统私有类请谨慎使用)。