iOS小技能:设备ID除了使用

您所在的位置:网站首页 设备标识符idfa iOS小技能:设备ID除了使用

iOS小技能:设备ID除了使用

2023-08-26 03:37| 来源: 网络整理| 查看: 265

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

前言

设备信息的获取:除了使用_idfa、_idfv, 还使用sysctl 获取cpu、macaddress信息,以及使用sysctlbyname获取设备型号等信息、 使用CNCopyCurrentNetworkInfo获取ssid、bssid

#pragma mark - ******** #import //void * +[UIDevice getSysInfoByName:](void * self, void * _cmd, char * arg2) { int (*old_sysctlbyname)(const char *, void *, size_t *, void *, size_t); int new_sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen){ NSString* nameStr=[NSString stringWithUTF8String:name]; int ret=old_sysctlbyname(name,oldp,oldlenp,newp,newlen); NSLog(@"this is new_sysctlbyname() name:%@ oldp result :%s new ret:%d",nameStr,oldp,ret);//2018-08-24 16:19:58.856596 WeChat[1353:75559] this is new_sysctlbyname() name:hw.machine oldp result :iPhone6,1 new ret:0 return ret; // char result[1024]; // size_t result_len = 1024; //if(sysctlbyname([name UTF8String], &result, &result_len, NULL, 0) < 0) //[NSString stringWithUTF8String:result] //return old_sysctlbyname(name,oldp,oldlenp,newp,newlen); - (NSString *) platform { return [self getSysInfoByName:"hw.machine"]; } CFDictionaryRef (*oldCNCopyCurrentNetworkInfo)(CFStringRef interfaceName); CFDictionaryRef newCNCopyCurrentNetworkInfo(CFStringRef interfaceName) { CFDictionaryRef result = NULL; NSDictionary *dictionary = @{ @"BSSID":@"", @"SSID": @"", @"SSIDDATA":@"" }; result = (CFDictionaryRef)CFRetain((__bridge CFDictionaryRef)(dictionary)); if(!result) { result = oldCNCopyCurrentNetworkInfo(interfaceName); } NSLog(@"this is newCNCopyCurrentNetworkInfo() oldCNCopyCurrentNetworkInfo:%@ new result:%@",oldCNCopyCurrentNetworkInfo(interfaceName),result); return result; } %ctor { MSHookFunction(&CNCopyCurrentNetworkInfo, &newCNCopyCurrentNetworkInfo, &oldCNCopyCurrentNetworkInfo); } 关于设备ID的心得: 通过逆向研究,发现大部分的app设备ID以及OpenUDID都是基于CFUUIDCreate、CFUUIDCreateString 进行创建 // Main public method that returns the OpenUDID // This method will generate and store the OpenUDID if it doesn't exist, typically the first time it is called // It will return the null udid (forty zeros) if the user has somehow opted this app out (this is subject to 3rd party implementation) // Otherwise, it will register the current app and return the OpenUDID // + (NSString*) value { return [OpenUDID valueWithError:nil]; } + (NSString*) valueWithError:(NSError **)error { NSString * appUID = [defaults objectForKey:kOpenUDIDAppUIDKey]; if(appUID == nil) { // generate a new uuid and store it in user defaults CFUUIDRef uuid = CFUUIDCreate(NULL); appUID = (NSString *) CFUUIDCreateString(NULL, uuid); CFRelease(uuid); [appUID autorelease]; } .... } I 如何唯一标识一台iOS设备? 1.1 通过Safari浏览器获取iOS设备UDID(设备唯一标识符)

如何唯一标识一台iOS设备?

原文:kunnan.blog.csdn.net/article/det… 在这里插入图片描述

1.2 替代方案:使用Keychain 存储UUID

从CSDN下载Demo:https://download.csdn.net/download/u011018979/16751837

1、应用场景:签名函数 2、原理:为了提高代码的安全性,可以采用把把函数名隐藏在结构体里,以函数指针成员的形式存储。 编译后,只留了下地址,去掉了名字和参数表,提高了逆向成本和攻击门槛. 3、文章:kunnan.blog.csdn.net/article/det…

- (NSString *)strUUID{ if (_strUUID == nil || [_strUUID isEqualToString:@""]) { CMPayKeychainItemWrapper *wrapper = [[CMPayKeychainItemWrapper alloc] initWithIdentifier:@"https://kunnan.blog.csdn.net/"accessGroup:nil]; // 读测试 NSString *strMD5 = [wrapper objectForKey:(__bridge id)kSecAttrAccount]; NSLog(@"读出md5:%@",strMD5); if (strMD5 == nil || [strMD5 isEqualToString:@""]) { strMD5 = [MD5Generator MD5]; // 如果是模拟器 if (TARGET_IPHONE_SIMULATOR){ }else{ [wrapper setObject:strMD5 forKey:(__bridge id)kSecAttrAccount]; } NSLog(@"写入MD5:%@",strMD5); } _strUUID = strMD5; NSLog(@"strUUID:%@", strMD5); } return _strUUID; } - (NSString *)openUDID{ if (_openUDID == nil || [_openUDID isEqualToString:@""]) { CMPayKeychainItemWrapper *wrapper = [[CMPayKeychainItemWrapper alloc] initWithIdentifier:@"weiliu.openUdid"accessGroup:nil]; // 读测试 NSString *openUDID = [wrapper objectForKey:(__bridge id)kSecValueData]; NSLog(@"读出_openUDID:%@",openUDID); if (openUDID == nil || [openUDID isEqualToString:@""]) { openUDID = [OpenUDID value]; // 如果是模拟器 if (TARGET_IPHONE_SIMULATOR){ }else{ [wrapper setObject:openUDID forKey:(__bridge id)kSecValueData]; } NSLog(@"写入_openUDID:%@",openUDID); } _openUDID = openUDID; NSLog(@"_openUDID:%@", openUDID); } return _openUDID; } see also:使用Keychain 存储UUID /** 优先级顺序:IDFA→IDFV→UUID 推荐:这里可以修改为使用 UUID为最高优先级 */ - (NSString *)anonymousId { if (_anonymousId) { return _anonymousId; } // 从 NSUserDefaults 中读取设备 ID _anonymousId = [[NSUserDefaults standardUserDefaults] objectForKey:SensorsAnalyticsAnonymousId]; if (_anonymousId) { return _anonymousId; } SensorsAnalyticsKeychainItem *item = [[SensorsAnalyticsKeychainItem alloc] initWithService:SensorsAnalyticsKeychainService key:SensorsAnalyticsAnonymousId]; // 从 Keychain 中读取设备 ID _anonymousId = [item value]; if (_anonymousId) { // 将设备 ID 保存在 NSUserDefaults 中 [[NSUserDefaults standardUserDefaults] setObject:_anonymousId forKey:SensorsAnalyticsAnonymousId]; // 返回保存的设备 ID return _anonymousId; } // 获取 IDFA:使用NSClassFromString函数来获取ASIdentifierManager类,这是因为应用程序有可能没有导入AdSupport.framework库。 Class cls = NSClassFromString(@"ASIdentifierManager"); if (cls) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector" // 获取 ASIdentifierManager 的单利对象 id manager = [cls performSelector:@selector(sharedManager)]; SEL selector = NSSelectorFromString(@"isAdvertisingTrackingEnabled"); BOOL (*isAdvertisingTrackingEnabled)(id, SEL) = (BOOL (*)(id, SEL))[manager methodForSelector:selector]; if (isAdvertisingTrackingEnabled(manager, selector)) { // 使用 IDFA 作为设备 ID _anonymousId = [(NSUUID *)[manager performSelector:@selector(advertisingIdentifier)] UUIDString]; } #pragma clang diagnostic pop } if (!_anonymousId) { // 使用 IDFV 作为设备 ID _anonymousId = UIDevice.currentDevice.identifierForVendor.UUIDString; } if (!_anonymousId) { // 使用 UUID 作为设备 ID _anonymousId = NSUUID.UUID.UUIDString; } // 保存设备 ID(匿名 ID) [self saveAnonymousId:_anonymousId]; return _anonymousId; } 1.3 最佳实践:优先级顺序:IDFA→IDFV→UUID

对于常规数据分析中的iOS设备ID,我们可按照如下优先级顺序获取,基本上能满足业务需求。

anonymousId /** 优先级顺序:IDFA→IDFV→UUID 推荐:这里可以修改为使用 UUID为最高优先级 */ - (NSString *)anonymousId { if (_anonymousId) { return _anonymousId; } // 从 NSUserDefaults 中读取设备 ID _anonymousId = [[NSUserDefaults standardUserDefaults] objectForKey:SensorsAnalyticsAnonymousId]; if (_anonymousId) { return _anonymousId; } SensorsAnalyticsKeychainItem *item = [[SensorsAnalyticsKeychainItem alloc] initWithService:SensorsAnalyticsKeychainService key:SensorsAnalyticsAnonymousId]; // 从 Keychain 中读取设备 ID _anonymousId = [item value]; if (_anonymousId) { // 将设备 ID 保存在 NSUserDefaults 中 [[NSUserDefaults standardUserDefaults] setObject:_anonymousId forKey:SensorsAnalyticsAnonymousId]; // 返回保存的设备 ID return _anonymousId; } // 获取 IDFA Class cls = NSClassFromString(@"ASIdentifierManager"); if (cls) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector" // 获取 ASIdentifierManager 的单利对象 id manager = [cls performSelector:@selector(sharedManager)]; SEL selector = NSSelectorFromString(@"isAdvertisingTrackingEnabled"); BOOL (*isAdvertisingTrackingEnabled)(id, SEL) = (BOOL (*)(id, SEL))[manager methodForSelector:selector]; if (isAdvertisingTrackingEnabled(manager, selector)) { // 使用 IDFA 作为设备 ID _anonymousId = [(NSUUID *)[manager performSelector:@selector(advertisingIdentifier)] UUIDString]; } #pragma clang diagnostic pop } if (!_anonymousId) { // 使用 IDFV 作为设备 ID _anonymousId = UIDevice.currentDevice.identifierForVendor.UUIDString; } if (!_anonymousId) { // 使用 UUID 作为设备 ID _anonymousId = NSUUID.UUID.UUIDString; } // 保存设备 ID(匿名 ID) [self saveAnonymousId:_anonymousId]; return _anonymousId; } - (void)saveAnonymousId:(NSString *)anonymousId { // 保存设备 ID [[NSUserDefaults standardUserDefaults] setObject:anonymousId forKey:SensorsAnalyticsAnonymousId]; [[NSUserDefaults standardUserDefaults] synchronize]; SensorsAnalyticsKeychainItem *item = [[SensorsAnalyticsKeychainItem alloc] initWithService:SensorsAnalyticsKeychainService key:SensorsAnalyticsAnonymousId]; if (anonymousId) { // 当设备 ID(匿名 ID)不为空时,将其保存在 Keychain 中 [item update:anonymousId]; } else { // 当设备 ID(匿名 ID)为空时,将删除 Keychain 中的值 [item remove]; } } II、IDFA(Identifier For Advertising,广告标识符)

在同一个iOS设备上,同一时刻,所有的应用程序获取到的IDFA都是相同的

2.1 获取IDFA

从iOS 6开始,我们可以利用AdSupport.framework库提供的方法来获取IDFA,

#import ; NSString *idfa = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];

但是,IDFA的值并不是固定不变的.目前,以下操作均会改变IDFA的值:

通过设置→通用→还原→抹掉所有内容和设置 通过iTunes还原设备 通过设置→隐私→广告→限制广告追踪(一旦用户限制了广告追踪,我们获取到的IDFA将是一个固定的IDFA,即一连串零:00000000-0000-0000-0000-000000000000) 2.2 判断 是否限制了广告追踪 BOOL isLimitAdTracking = [[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled];

IDFA能解决应用程序卸载重装唯一标识设备的问题。因此,IDFA目前来说比较适合作为iOS设备ID属性。

III IDFV (Identifier For Vendor,应用开发商标识符) 3.1 获取idfv NSString *idfv = [[[UIDevice currentDevice] identifierForVendor] UUIDString];

是为了便于应用开发商(Vendor)标识用户,适用于分析用户在应用内的行为等。它也是一个由32位十六进制组成的序列,格式与UUID一致。

每一个iOS设备在所属同一个Vendor的应用里,获取到的IDFV是相同的。Vendor是通过反转后的BundleID的前两部分进行匹配的,如果相同就属于同一个Vendor。(比如,对于com.apple.example1和com.apple.example2这两个BundleID来说,它们就属于同一个Vendor,将共享同一个IDFV。)

和IDFA相比,IDFV不会出现获取不到的场景。 3.2 IDFV被系统重置的场景 通过设置→通用→还原→抹掉所有内容和设置。 通过iTunes还原设备。 卸载设备上某个开发者账号下的所有应用程序。 如果用户将属于此Vendor的所有应用程序都卸载,IDFV的值也会被系统重置。即使重装该Vendor的应用程序,获取到的也是一个全新的IDFV。

但是由于重复卸载引起的极光的regid变化,所以推荐用IDFV+账号ID生成别名,避免别名在同一个手机绑定了多台设备。blog.csdn.net/z929118967/…

IV IMEI(International Mobile Equipment Identity,国际移动设备身份码)

是由15位纯数字组成的串,并且是全球唯一的。 任何一部手机,在其生产并组装完成之后,都会被写入一个全球唯一的IMEI。

从iOS 2开始,苹果公司提供了相应的接口来获取IMEI。但后来为了保护用户隐私,从iOS 5开始,苹果公司就不再允许应用程序获取IMEI。因此,IMEI也不适合作为iOS设备ID。

V see also

公众号:iOS逆向



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3