天天看點

iOS擷取裝置UUID和IDFA

一:

1.1 :UDID

簡介:UDID的全稱是

Unique Device Identifier

,顧名思義,它就是蘋果IOS裝置的唯一識别碼,它由40個字元的字母和數字組成。在很多需要限制一台裝置一個賬号的應用中經常會用到。在iOS5中可以擷取到裝置的UDID,iOS7中已經完全的禁用了它。iOS7之前的使用了的app如果在iOS7上運作,它不會傳回裝置的UDID,而是會傳回一串字元串,以

FFFFFFFF

開頭,跟着

identifierForVendor

的十六進制值。

擷取:

[[UIDevice currentDevice] uniqueIdentifier]

廢棄:iOS6

1.2 IDFV

簡介:iOS 6.0系統新增用于替換

uniqueIdentifier

的接口。是給Vendor辨別使用者用的,每個裝置在所屬同一個Vender的應用裡,都有相同的值。其中的Vender是指應用提供商,但準确點說,是通過

BundleID

的DNS反轉的前兩部分進行比對,如果相同就是同一個Vender,例如對于

com.somecompany.appone

,

com.somecompany.apptwo

這兩個BundleID來說,就屬于同一個Vender,共享同一個idfv的值。和idfa不同的是,idfv的值是一定能取到的,是以非常适合于作為内部使用者行為分析的主id,來辨別使用者,替代OpenUDID。如果使用者将屬于此Vender的所有App解除安裝,則idfv的值會被重置,即再重裝此Vender的App,idfv的值和之前不同。

擷取:

[[[UIDevice currentDevice] identifierForVendor] UUIDString]

适用:iOS6.0+

例子:

95955F33-BFBD-48BA-A630-866D2DAE482D

1.3 IDFA

簡介:廣告标示符,适用于對外:例如廣告推廣,換量等跨應用的使用者追蹤等。但如果使用者完全重置系統(

(設定程式 -> 通用 -> 還原 -> 還原位置與隐私

) ,這個廣告标示符會重新生成。另外如果使用者明确的還原廣告(設定程式-> 通用 -> 關于本機 -> 廣告 -> 還原廣告标示符) ,那麼廣告标示符也會重新生成。注意:如果程式在背景運作,此時使用者“還原廣告标示符”,然後再回到程式中,此時擷取廣 告标示符并不會立即獲得還原後的标示符。必須要終止程式,然後再重新啟動程式,才能獲得還原後的廣告标示符。在同一個裝置上的所有App都會取到相同的值,是蘋果專門給各廣告提供商用來追蹤使用者而設的,

使用者可以在 設定 -> 隐私 -> 廣告追蹤 裡重置此id的值,或限制此id的使用。

擷取:

[[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]

;

适用:iOS6.0+

例子:

9C287922-EE26-4501-94B5-DDE6F83E1475

1.4 MAC位址

簡介:MAC位址在網絡上用來區分裝置的唯一性,接入網絡的裝置都有一個MAC位址,他們肯定都是不同的,是唯一的。一部iPhone上可能有多個MAC位址,包括WIFI的、SIM的等,但是iTouch和iPad上就有一個WIFI的,是以隻需擷取WIFI的MAC位址就好了,也就是en0的位址。MAC位址就如同我們身份證上的身份證号碼,具有全球唯一性。但在iOS7之後,如果請求Mac位址都會傳回一個固定值。

注意:由于idfa會出現取不到的情況,故絕不可以作為業務分析的主id,來識别使用者。 比如開啟限制廣告追蹤

廢棄:iOS7.0+

擷取:

- (NSString *)macAddress  
{  

    int                 mib[6];  
    size_t              len;  
    char                *buf;  
    unsigned char       *ptr;  
    struct if_msghdr    *ifm;  
    struct sockaddr_dl  *sdl;  

    mib[0] = CTL_NET;  
    mib[1] = AF_ROUTE;  
    mib[2] = 0;  
    mib[3] = AF_LINK;  
    mib[4] = NET_RT_IFLIST;  

    if ((mib[5] = if_nametoindex("en0")) == 0) {  
        printf("Error: if_nametoindex error/n");  
        return NULL;  
    }  

    if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {  
        printf("Error: sysctl, take 1/n");  
        return NULL;  
    }  

    if ((buf = malloc(len)) == NULL) {  
        printf("Could not allocate memory. error!/n");  
        return NULL;  
    }  

    if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {  
        printf("Error: sysctl, take 2");  
        return NULL;  
    }  

    ifm = (struct if_msghdr *)buf;  
    sdl = (struct sockaddr_dl *)(ifm + 1);  
    ptr = (unsigned char *)LLADDR(sdl);  
    NSString *outstring = [NSString stringWithFormat:@"%02x:%02x:%02x:%02x:%02x:%02x", *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)];  

    NSLog(@"outString:%@", outstring);  

    free(buf);  

    return [outstring uppercaseString];  
}
           

1.5 KeyChain

簡介:iOS整個系統有一個

KeyChain

,每個程式都可以往

KeyChain

中記錄資料,而且隻能讀取到自己程式記錄在

KeyChain

中的資料。而且就算我們程式删除掉,系統經過更新以後再安裝回來,依舊可以擷取到與之前一緻的UDID(系統還原、刷機除外)。是以我們可以将UUID的字元串存儲到KeyChain中,然後下次直接從

KeyChain

擷取UUID字元串。(本示例中使用

KeychainItemWrapper

工具類)

擷取

+ (NSString *)UUID {
    KeychainItemWrapper *keyChainWrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"MYAppID" accessGroup:@"com.test.app"];
    NSString *UUID = [keyChainWrapper objectForKey:(__bridge id)kSecValueData];

    if (UUID == nil || UUID.length == 0) {
        UUID = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
        [keyChainWrapper setObject:UUID forKey:(__bridge id)kSecValueData];
    }

    return UUID;
}
           

1.6 AppleAccount

簡介:雖然蘋果在iOS6中禁用了擷取uuid的方式,但是隻要你研究下就知道這個API隻是私有化了,使用私有API還是可以擷取裝置的uuid。但是這個方面也面臨着風險:比如API變更以及AppStore稽核問題,但是在越獄裝置上你還是可以盡情享用的。

類:AADeviceInfo(dump出頭檔案)

@class NSObject<OS_dispatch_semaphore>, APSConnection, NSData;

@interface AADeviceInfo : NSObject {

APSConnection *_apsConnection;

BOOL _tokenDone;

NSData *_token;

NSObject<OS_dispatch_semaphore> *_tokenSema;

}

+ (id)userAgentHeader;

+ (id)signatureWithDictionary:(id)arg1;

+ (id)apnsToken;

+ (id)serialNumber;

+ (id)clientInfoHeader;

+ (id)appleIDClientIdentifier;

+ (id)productVersion;

+ (id)osVersion;

+ (id)udid;

+ (id)infoDictionary;

- (id)wifiMacAddress;

- (id)regionCode;

- (id)deviceClass;

- (id)osName;

- (id)productType;

- (id)apnsToken;

- (id)serialNumber;

- (id)deviceInfoDictionary;

- (id)appleIDClientIdentifier;

- (id)productVersion;

- (id)osVersion;

- (id)udid;

- (id)init;

- (void).cxx_destruct;

- (id)buildVersion;

@end
           

擷取:

[AADeviceInfo udid]

使用方法:在項目中将真機上的

AppleAccount.framework

架構導出,引入Xcode工程中,利用

runtime

或者直接使用該類就行。

(細節補充:導出AppleAccount.framework後,進入AppleAccount.framework的根目錄,建立Headers檔案夾,然後将dump出的頭檔案放在Headers目錄,就可以像引用第三方framework一樣在項目中使用)

二: iOS10 擷取idfa的坑

ios10更新之後一旦開啟了 

設定->隐私->廣告->限制廣告跟蹤

之後 擷取到的idfa将會是一串00000 跟mac位址一個尿性,而且每次開啟在關閉之後 相應的idfa也會重新生成,相當于還原了一次廣告辨別符。

iOS擷取裝置UUID和IDFA

擷取idfa的方法:

#import <AdSupport/AdSupport.h>

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

ios10 之後最好加一個判斷

[[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled]

 傳回值是BOOL值 如果傳回的YES說明沒有 “開啟限制廣告跟蹤”,可以擷取到正确的idfa 如果傳回的是NO,說明等待你的就是一串00000000000

三: IDFA的前世今生

為了保護使用者隐私,早在2012年就不再允許其生态中的玩家擷取使用者的唯一辨別符,但是商家在移動端打廣告的時候又希望能監控到每一次廣告投放的效果,是以,蘋果想出了折中的辦法,就是提供另外一套和硬體無關的辨別符,用于給商家監測廣告效果,同時使用者可以在設定裡改變這串字元,導緻商家沒有辦法長期跟蹤使用者行為。這個就叫做廣告辨別符(IDFA),設定路徑是“設定->隐私->廣告->還原廣告辨別符”,如下圖所示(iOS9)

iOS擷取裝置UUID和IDFA

因為這個IDFA不是唯一的,是以一開始行業内是很抵觸的,想方設法去擷取UDID(跟手機綁定的,使用者不能改變),引起蘋果大怒,在13年時禁止所有App擷取UDID,否則不能上架,也正因為其生态的封閉性,才能迫使大家就範。雖然IDFA不是唯一的,但是畢竟勝過沒有,況且也沒有多少使用者會去更改。是以,經過幾番争鬥,IDFA已經成為通用的iPhone使用者辨別符,這個過程分為6個階段,我用下圖總結

iOS擷取裝置UUID和IDFA

然而在今年iOS10推出後,廣告界大為震驚,因為蘋果推出了“限制廣告追蹤”功能,設定的路徑和iOS9一緻。可能細心的人注意到了,這個功能并非iOS10獨有啊,在之前的版本中也一樣存在。不過經過實際的測試,在iOS10之前,即使使用者打開這個功能,商家一樣可以擷取IDFA,隻不過與之前的不一樣了,每次切換這個開關與點選“還原廣告辨別符”的效果一樣。而iOS10就不一樣了,當使用者打開這個功能後,商家隻能擷取到一連串無意義的0,這才是廣告界大為震驚的原因所在

iOS擷取裝置UUID和IDFA

四: iOS10 IDFA 擷取不到問題解決

4.1 限制廣告跟蹤背景

中文說明文檔

Important

In iOS 10.0 and later, the value of advertisingIdentifier is all 
zeroes when the user has limited ad tracking.
           

也就是說在iOS10上,使用者如果開啟了 限制廣告跟蹤 , 擷取的idfa将是一串

00000000-0000-0000-0000-000000000000

4.2 SimulateIDFA

SimulateIDFA是根據一堆裝置資訊(每個app擷取的值都是一樣的)生成的一個MD5值。用于标志不同裝置。

#######4.2.1 使用:

CoreTelephony.framework
https://github.com/youmi/SimulateIDFA (下載下傳代碼)
           

在需要擷取 

SimulateIDFA

的地方調用代碼:

NSString *simulateIDFA = [SimulateIDFA createSimulateIDFA];
           

simulateIDFA的格式跟IDFA的格式一樣

626363D0-90D4-06BF-C281-384E4E69D3E2
           

#######4.2.2 生成原理

生成的MD5值分兩部分。

以 

626363D0-90D4-06BF-C281-384E4E69D3E2

 為例:

前16位

626363D0-90D4-06BF

是由比較穩定的參數組合獲得,這前16位隻有在系統更新的情況下才會變。

後16位

C281-384E4E69D3E2

 由 一些比較容易被改變的參數組合生成,比較常見的值變化情況是系統重新啟動。

  • 1: 參與前16位計算的參數有:
系統版本(9.3.2)、
硬體資訊(N53AP,iPhone6,2,中國移動46002,1048576000)、
coreServices檔案建立更新時間(2015-08-07 23:53:00 +0000,2016-06-07 23:53:09 +0000),
系統容量(12266725376)
           

這裡有一些資訊是更新的時候會變的,系統版本、

coreServices

檔案建立更新時間、系統容量

  • 2: 參與後16位計算的參數有:
系統開機時間(1473301191去掉後面的4位數 147330)、
國家代碼(CN)、
本地語言(zh-Hans-CN)、
裝置名稱(XXXX)
           

這裡的參數都是比較容易變化的,系統重新開機離上次重新開機有10000秒的話會變,其他參數在設定裡面可以修改

4.3 SimulateIDFA與OpenIDFA對比

OpenIDFA 是 Yann Lechelle的一個開源庫。同是IDFA的替換方案

#######4.3.1 生成的ID重複的機率對比

假設一個情況。一天内某個國家有10000000(1千萬)台相同型号的裝置更新到同一個系統。

  • 1: SimulateIDFA

    一天内這個算法可能的值計算, 

    24x3600

    (檔案建立時間,機關秒)

    x 10

    (檔案最後修改時間假設誤差在10秒)

    x 10000000

    (系統容量誤差範圍)

    x 1000000

    (裝置名稱範圍,這裡假設的是每100台就有2個重複)= 

    8640000000000000000

    裝置a的值為 K,那麼裝置b的值同為K的可能性為: 

    1/8640000000000000000

    . 總共有 

    10000000

    台裝置。是以,這10000000裝置中有與a裝置的值同為K的可能性為 

    1/8640000000000000000 x 10000000 = 1/864000000000。

  • 2: OpenIDFA

    先看一下OpenIDFA的生成算法,OpenIDFA是對下面的參數組合進行MD5.

系統開機時間(1473241127 減去後四位值為 147324)、系統容量(29230571520)、
系統版本(9.3.4)、機型(N78AP,iPod5,1)、國家代碼(CN)、本地語言(zh-Hans-CN)、
一些預裝的App(由于用的是canOpenURL這個接口,iOS9就已經廢了)、時區(Asia/Shanghai)、
當天時間(160804, 16年8月4日,這個值是他每天值都會變化的原因)
           

一天内可能的值為系統容量的誤差(10000000)。 ps: 系統啟動時間在這種情況下對重複率的降低沒起到作用,因為OpenIDFA是減去了系統啟動時間的後4位來計算的。同理當天時間也是。

裝置a的值為 K,那麼裝置b的值同為K的可能性為: 1/10000000. 總共有 10000000台裝置。是以,這10000000裝置中有與a裝置的值同為K的可能性為 

1/10000000 x 10000000 = 1

#######4.3.2 時效性對比

  • 1: OpenIDFA

    每天擷取的值都不一樣

  • 2: SimulateIDFA

    SimulateIDFA

    分兩部分,前16位是在系統更新的時候才會變化,後16位使用者的某些行為可能會導緻值變化(例如:重新開機手機、修改裝置名稱、修改手機本地語言)

#######4.3.3 總結:

OpenIDFA 有一些限制,生成的IDFA會每天變化,在一些極端條件下重複率比較高。 SimulateIDFA在這方面有更好的表現

五: iOS 送出稽核之IDFA的介紹

在我們送出程式進行稽核的時候,最後會有兩個選項供我們選擇,一個是

Export Compliance

(該選項主要是說你的程式設計是否使用了加密,我一般上傳的時候都選擇No,這個根據你項目實際情況來選擇.);另一個就是

Advertising Identifier

(廣告标示符).

iOS擷取裝置UUID和IDFA
iOS擷取裝置UUID和IDFA
iOS擷取裝置UUID和IDFA

#######5.1 檢查是否使用IDFA

檢查我們項目中是否使用廣告标示符,其實就是檢視我們-

  • 1: framework中是否有個叫做

    AdSupport.framework

    的架構;
  • 2: 如果檢查framework沒有,可能是我們接入的第三方裡面有,用以下方法檢查第三方中是否包含有IDFA版本;
(1)打開終端cd到要檢查的檔案的目錄;
(2)執行指令:grep -r advertisingIdentifier .(注意别少了點);
           

分别對我的項目中和ShareSDK裡面進行了檢查:

iOS擷取裝置UUID和IDFA

#######5.2 各個選項的含義

  • 1、 在 App 内投放廣告服務應用中的廣告。

    如果你的應用中內建了廣告的時候,你需要勾選這一項。

  • 2、 将此 App 安裝歸因于先前投放的特定廣告

    跟蹤廣告帶來的安裝。如果你使用了第三方的工具來跟蹤廣告帶來的激活以及一些其他事件,但是應用裡并沒有展示廣告你需要勾選這一項。

  • 3、将此 App 中發生的操作歸因于先前投放的特定廣告

    跟蹤廣告帶來的使用者的後續行為。如果你使用了第三方的工具來跟蹤廣告帶來的激活以及一些其他事件。

  • 4、 iOS 中的“限制廣告

    跟蹤”設定這一項下的内容其實就是對你的應用使用 IDFA 的目的做下确認,隻要你選擇了采集 IDFA,那麼這一項都是需要勾選的。

#######5.3 總結

  • 1: 如果你的應用裡隻是內建了廣告,不追蹤廣告帶來的激活行為,那麼選擇1和4;
  • 2: 如果你的應用沒有廣告,而又擷取了IDFA。建議選擇2和4;

參考資料:

http://www.jianshu.com/p/e222ff751a97

ios10 擷取idfa的坑

手機更新到Ios10後擷取的idfa全是0?

http://www.cnblogs.com/zxykit/p/5320259.html

iOS10 IDFA 擷取不到問題解決

http://www.ithao123.cn/content-8688001.html

http://blog.csdn.net/kaitiren/article/details/52562556

iOS 送出稽核之IDFA的介紹

繼續閱讀