iOS小技能:设备ID除了使用_idfa、_idfv 还可使用其他替代方案(Keychain 存储)

简介: 设备信息的获取:除了使用_idfa、_idfv, 还使用sysct 获取cpu信息。

前言

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

#pragma mark - ******** #import <sys/sysctl.h> //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设备?

原文: https://kunnan.blog.csdn.net/article/details/108049773
在这里插入图片描述

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

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

1、应用场景:签名函数
2、原理:为了提高代码的安全性,可以采用把把函数名隐藏在结构体里,以函数指针成员的形式存储。 编译后,只留了下地址,去掉了名字和参数表,提高了逆向成本和攻击门槛.
3、文章: https://kunnan.blog.csdn.net/article/details/115857706
- (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;
    
}
/**
 
 优先级顺序: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,我们可按照如下优先级顺序获取,基本上能满足业务需求。

/**
 
 优先级顺序: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 <AdSupport/AdSupport.h>;

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被系统重置的场景

  1. 通过设置→通用→还原→抹掉所有内容和设置。
  2. 通过iTunes还原设备。
  3. 卸载设备上某个开发者账号下的所有应用程序。 如果用户将属于此Vendor的所有应用程序都卸载,IDFV的值也会被系统重置。即使重装该Vendor的应用程序,获取到的也是一个全新的IDFV。
但是由于重复卸载引起的极光的regid变化,所以推荐用IDFV+账号ID生成别名,避免别名在同一个手机绑定了多台设备。 https://blog.csdn.net/z929118967/article/details/78039298

IV uni-app创建ios应用如何获取手机的idfa、idfv

4.1 编写manifest.json配置文件

"ios" : {

"frameworks" : [ "AdSupport.framework" ],

"idfa" : "true"

},

4.2 获取idfa,idfv函数

返回数据为json对象

getIdfa_idfv:function(){

var NSUUID = plus.ios.importClass('NSUUID');

var UIDevice = plus.ios.importClass("UIDevice");

var currentDevice = UIDevice.currentDevice()

var identifierForVendor = currentDevice.identifierForVendor().UUIDString();

var ASIdentifierManager = plus.ios.importClass("ASIdentifierManager");

var sharedManager = ASIdentifierManager.sharedManager();

if(sharedManager.isAdvertisingTrackingEnabled()){

var advertisingIdentifier = sharedManager.advertisingIdentifier();

var idfa = plus.ios.invoke(advertisingIdentifier,"UUIDString");

}

var result = {'idfa':idfa,'idfv':identifierForVendor}

return result;

}

4.3 提交打包测试

需要使用自定义基座

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

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

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

V see also

gzh: iOS逆向

目录
相关文章
|
4月前
|
Android开发 iOS开发 UED
探索未来:Android与iOS在智能穿戴设备上的较量
随着科技的飞速进步,智能穿戴设备已经成为我们日常生活中不可或缺的一部分。本文将深入探讨两大操作系统——Android和iOS——在智能穿戴领域的竞争与发展,分析它们各自的优势与挑战,并预测未来的发展趋势。通过比较两者在设计哲学、生态系统、用户体验及创新技术的应用等方面的差异,揭示这场较量对消费者选择和市场格局的影响。 【7月更文挑战第31天】
50 0
|
3月前
|
测试技术 Linux 虚拟化
iOS自动化测试方案(五):保姆级VMware虚拟机安装MacOS
详细的VMware虚拟机安装macOS Big Sur的保姆级教程,包括下载VMware和macOS镜像、图解安装步骤和遇到问题时的解决方案,旨在帮助读者顺利搭建macOS虚拟机环境。
122 3
iOS自动化测试方案(五):保姆级VMware虚拟机安装MacOS
|
3月前
|
测试技术 开发工具 iOS开发
iOS自动化测试方案(三):WDA+iOS自动化测试解决方案
这篇文章是iOS自动化测试方案的第三部分,介绍了在没有MacOS系统条件下,如何使用WDA(WebDriverAgent)结合Python客户端库facebook-wda和tidevice工具,在Windows系统上实现iOS应用的自动化测试,包括环境准备、问题解决和扩展应用的详细步骤。
226 1
iOS自动化测试方案(三):WDA+iOS自动化测试解决方案
|
3月前
|
测试技术 数据安全/隐私保护 iOS开发
iOS自动化测试方案(四):保姆级搭建iOS自动化开发环境
iOS自动化测试方案的第四部分,涵盖了基础环境准备、iPhone虚拟机设置、MacOS虚拟机与iPhone真机的连接,以及扩展问题和代码示例,确保读者能够顺利完成环境搭建并进行iOS自动化测试。
230 0
iOS自动化测试方案(四):保姆级搭建iOS自动化开发环境
|
3月前
|
测试技术 虚拟化 iOS开发
iOS自动化测试方案(二):Xcode开发者工具构建WDA应用到iphone
这篇文章是iOS自动化测试方案的第二部分,详细介绍了在Xcode开发者工具中构建WebDriverAgent(WDA)应用到iPhone的全过程,包括环境准备、解决构建过程中可能遇到的错误,以及最终成功安装WDA到设备的方法。
170 0
iOS自动化测试方案(二):Xcode开发者工具构建WDA应用到iphone
|
3月前
|
测试技术 开发工具 虚拟化
iOS自动化测试方案(一):MacOS虚拟机保姆级安装Xcode教程
这篇文章提供了一份保姆级的教程,指导如何在MacOS虚拟机上安装Xcode,包括环境准备、基础软件安装以及USB扩展插件的使用,以实现iOS自动化测试方案的第一步。
93 0
iOS自动化测试方案(一):MacOS虚拟机保姆级安装Xcode教程
|
3月前
|
运维 网络安全 iOS开发
厉害!外国网络工程师用Ansible给思科IOS设备升级!
厉害!外国网络工程师用Ansible给思科IOS设备升级!
|
3月前
|
BI Linux 数据安全/隐私保护
忘了 iOS(iPad、IPhone) 设备上的「屏幕使用时间」密码怎么办?找回屏幕密码
忘了 iOS(iPad、IPhone) 设备上的「屏幕使用时间」密码怎么办?找回屏幕密码
109 0
|
4月前
|
Android开发 数据安全/隐私保护 iOS开发
探索未来:安卓与iOS在智能穿戴设备领域的较量
随着科技的飞速发展,智能穿戴设备已逐渐成为我们日常生活的一部分。从健康监测到通讯交流,它们正以惊人的速度改变着我们的生活方式。本文将深入探讨安卓和iOS这两大操作系统在智能穿戴领域的现状、竞争以及未来发展趋势,揭示它们如何通过创新技术满足用户需求,并预测未来可能的发展方向。
41 0
|
6月前
|
移动开发 安全 数据安全/隐私保护
ios安全加固 ios 加固方案
ios安全加固 ios 加固方案
88 1
ios安全加固 ios 加固方案