那么,在当前的Android和iOS两大阵营上,系统是否提供了现成的能够唯一且准确的标识一台终端设备的ID给到开发者采集获取呢?毫无疑问,答案是否定的。Google官方的说法是,一切能跟踪用户的ID都涉及到用户隐私的保护,属于危险级权限,这个在Android6.0上就做了权限级别划分,同时也赋予了用户更灵活的权限控制主动权。iOS就不用说,一向做的”非常棒“,限制各种ID的采集。OK,下面会一一给大家介绍终端各种ID的采集方式以及准确性。
二、Android篇
1、IMEI/MEID
简介之维基百科:
终端标准采集方式:
TelephonyManagermanager=(TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);Stringimei=manager.getDeviceId();
好了,这里有人就会有疑问,对于市面上众多的双卡双待手机,是不是会有两个ID,如何把这两个ID都采集上来?
有两个IMEI,或者一个IMEI和一个MEID的设备都存在,Android原生系统并不支持双卡双待,均由厂商在硬件,系统层定制实现。由于没有统一的实现规范标准,那么对应的采集方式也需要进行厂商机型适配,例如三星的某些机型上,要拿到第二个TelephonyManager的实例,getSystemService(Context.TELEPHONY_SERVICE)中Context.TELEPHONY_SERVICE的值为"phone2",默认为"phone",见下图:
HTC的机型又为"htctelephony",基于MTK芯片解决方案或者华为,小米的又各不相同。
采集有效率:整体96%左右,其中接近一个点的为"","null"无效字符串,其他为
000000000000000","111111111111111","123456789123456"等各种奇葩无效串,以及大批量的山寨重复IMEI。
对于系统级应用或者桌面应用,由于获取采集时机在设备模块初始化未完成之前,也会导致设备ID采集不到的情况发生,另外由于像手机管家,360手机助手,和一些厂商ROM自带的权限管家,有阻止应用采集手机IMEI的功能,设置之后也会导致正常采集返回0串或者其他固定串。还有山寨手机厂商为了节省成本,批量复制重复IMEI串烧进手机的EEPROM里面。
2、MAC
WifiManagerwifi=(WifiManager)context.getSystemService(Context.WIFI_SERVICE);WifiInfoinfo=wifi.getConnectionInfo();Stringmac=info.getMacAddress();
好了,问题又来了,在Android6.0及以上的系统上,通过上面的系统接口采集到的MAC地址返回的是一个固定串:02:00:00:00:00:00。这个问题会在第六小节Android6.0特别篇里面统一介绍。
另外,我们在分析MAC地址的有效唯一性时发现,会出现同一个真实IMEI可对应多条MAC地址,也就是说MAC地址会发生变化(先排除作弊工具有意串改的场景,ID串改的在第八小节中统一介绍)。研究发现用户/刷机商在进行线刷时损坏nvram导致每次使用WIFI联网或者手机重启时MAC地址变化,nvram损坏的情况下,系统由于获取不到真实的MAC地址,Android内核会自动生成一个临时的虚拟MAC地址供用户联网,且每次重启或者断网重连时重新生成一个新的MAC地址(前3个字节不变,后3个字节随机生成),并且会在WIFI列表中显示一个“NVRAMWARNINGErr=0x10”信号项。
采集有效率:93%左右
3、IMSI
简介之百度百科:国际移动用户识别码(IMSI:InternationalMobileSubscriberIdentificationNumber)是区别移动用户的标志,储存在SIM卡中,可用于区别移动用户的有效信息。其总长度不超过15位,同样使用0~9的数字。其中MCC是移动用户所属国家代号,占3位数字,中国的MCC规定为460;MNC是移动网号码,由两位或者三位数字组成,中国移动的移动网络编码(MNC)为00;用于识别移动用户所归属的移动通信网;MSIN是移动用户识别码,用以识别某一移动通信网中的移动用户。
那这里注意了,此ID是跟SIM卡绑定的,更换SIM卡就会发生变化,同样在仅支持wifi的pad设备上是没有的。
TelephonyManagermanager=(TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);Stringimsi=manager.getSubscriberId();
IMSI跟IMEI一样,同样也会遇到双卡的问题,如果两个串号均想采集,那就需要根据不同的厂商机型的实现方式进行适配。
采集有效率:98%左右,存在无SIM卡的终端设备获取不到的问题,最大的问题是会发生变化,与终端设备非强绑定。
4、Android_ID
简介:Android_ID是设备首次启动时,系统生成的一个唯一串号,长16字节(例:70560687d711af97),由com.android.providers.settings这个系统程序所管理,Android6.0以下储存在/data/data/com.android.providers.settings/databases/settings.db中的secure表。
系统生成此ID的部分源码如下:
finalSecureRandomrandom=newSecureRandom();finalStringnewAndroidIdValue=Long.toHexString(random.nextLong());finalContentValuesvalues=newContentValues();values.put(Settings.NameValueTable.NAME,Settings.Secure.ANDROID_ID);values.put(Settings.NameValueTable.VALUE,newAndroidIdValue);
Settings.Secure.getString(context.getContentResolver(),"android_id")
了解了以上的信息,对此ID就比较清楚了,此ID与硬件无关,与Android系统有关,所以当系统还原出厂设置,刷机时这个ID就会重新生成了。另据官方资料显示,此ID在>=2.3的统上是可靠并且稳定的。But,由于国内的手机基本都是厂商定制的ROM,所以此ID的控制权均由厂商ROM层来控制,难免会发生一些bug,导致部分机型上的ID都相同。另外在OTAROM升级的时候,有些厂商会保留原有db信息不变,有些厂商会清除重新生成。
采集有效率:99%左右,与系统强依赖,稳定性不足。
5、CID(forMMC,SDCard)
简介:CardIdentificationRegister(CID),顾名思义,做为存储卡片的唯一识别信息,规范上要求每张卡片的ID都唯一。CID寄存器有16字节长,如下表所示,它包含了本卡的特别识别码(ID号)。这些信息是在卡的生产期间被编程(烧录),主控制器不能修改它们的内容。注意:SD卡的CID寄存器和MMC卡的CID寄存器在记录结构上是不同的。
adbshellcat/sys/class/mmc_host/mmc*/mmc*:*/cid
一般来讲MMC0保存的是内置存储卡的信息,MMC1保存的是扩展存储卡也就是SD卡的信息。因为SD卡跟终端设备又不是强关联的,会因为更换升级发生变化,那么我们主要看手机内置存储卡的CID。
采集有效率:95%~96%与IMEI相当,在部分山寨低端机器上,会出现采集不到和采集的是SD卡的CID,初步分析是由于山寨低端机器上采用的存储设备为Flash卡或者并未遵循标准的设计规范,因此而采集不到。
6、Android6.0特别篇
2)、MAC地址通过非系统接口可采集(6.0以下通过系统接口采集),采集场景仅限于处于wifi连接状态。
非系统接口,其实指的就是直接读取文件的方式,如下:
cat/sys/class/net/wlan0/address
cat/sys/devices/virtual/net/wlan0/address
3)、AndroidID采集不受影响,但此ID不与硬件绑定,刷机,系统还原,升级等场景会发生变化,不稳定。
说明:应用编译时选择的targetSdkVersion>=23,在6.0及以上的系统运行才会触发权限提示框,<23打出的apk包不受此影响。
7、Google官方推荐篇
在16年1月初,MIG商务合作中心的同事给予支持,我们团队与Google的官方技术人员有过两次针对6.0新特性(主要是权限管理方面)的交流咨询会议,这里也把官方人员推荐的做法也同步给大家了解。总结一下主要有三个建议:
TheInstanceIDAPIletsyouintegrateInstanceIDwithyourAndroidoriOSapp.InstanceIDprovidesauniqueidentifierforeachinstanceofyourappandamechanismtoauthenticateandauthorizeactions,likesendingmessagesviaGoogleCloudMessaging.TheInstanceIDislonglived,butmayexpireforthefollowingreasons:
Devicefactoryreset.Useruninstallstheapp.Userperforms“ClearData”intheapp.Deviceunusedforanextendedperiod(deviceandregiondeterminesthetimespan).InstanceIDservicedetectsabuseorerrorsandresetstheInstanceID.Server-sidecodeifyourclientapprequiresthatfunctionality.TheInstanceIDservicenotifiesyourappofanInstanceIDresetviacallbacktoaInstanceIDListenerService.Ifyourappreceivesthisnotification,itmustcallgetToken()andretrievethenewInstanceID,andupdateitsservers.
UsethegetTokenmethodtoprovetheownershipoftheInstanceIDandtoallowserverstoaccessdataorservicesassociatedwiththeapp.ThemethodfollowsthepatternsofOAuth2,andrequiresanauthorizedEntityandscope.TheauthorizedEntitycanbeaprojectIDoranotherInstanceID,anditdeterminestheservicesthatareauthorizedtousethegeneratedtoken.Thescopedeterminesthespecificserviceordatatowhichthetokenallowsaccess.
有几个问题,此ID主要是用于使用GCM服务,国内不可用,应用内唯一,可变因素太多。
2)Guid,JavaUtil库生成的全球唯一ID
应用内"唯一",需要终端自己实现本地/服务器存储。
官方:一切与硬件关联的ID,能跟踪到用户的都是属于危险级权限,涉及到用户隐私,不能随意采集。个人推测,以后Android在这方面会慢慢向苹果看齐。
8、ID串改方式了解(刷量篇)
对以上ID了解的差不多之后,那我们在看看黑产恶意作弊刷量的方式有哪些。
1)串改终端设备IDa)脚本修改系统配置文件b)工具框架hook串改系统接口函数
2)固定真机脚本调起应用运行3)肉鸡云端下发指令后台调起运行4)ROM+云端下发指令调起5)模拟器刷量,软件/硬件模拟器
ID串改,有比较多的小工具/软件就可以完成,例如EasyIMEIChanger;高端一些的方式采用的是系统函数的hook方式进行调用拦截串改,例如Xposed框架有做这些函数的hook:
好吧,这里就不多说了,我们有专门的团队在做推广渠道刷量的检测分析,如有产品想详细了解和接入试用,也请联系咨询[灯塔小秘]或者项目负责人janexiong。
三、iOS篇
1、UDID
简介:UniqueDeviceIdentifier的缩写,iOS设备唯一标识符
采集方式:[UIDevicecurrentDevice]uniqueIdentifier]
准确性/唯一性:iOS设备唯一标识符,iOS2.0以上及iOS5.0以下的系统可用,iOS5以上已禁用
2、UUID
简介:UniversallyUniqueIdentifier的缩写,中文意思是通用唯一识别码
准确性/唯一性:当App升级或重装后,UUID的值会发生变化
3、MAC
简介:手机无线网卡物理地址
采集方式:
structif_msghdr*ifm;structsockaddr_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_nametoindexerror/n");returnNULL;}
if(sysctl(mib,6,NULL,&len,NULL,0)<0){printf("Error:sysctl,take1/n");returnNULL;}
if((buf=(char*)malloc(len))==NULL){printf("Couldnotallocatememory.error!/n");returnNULL;}
if(sysctl(mib,6,buf,&len,NULL,0)<0){printf("Error:sysctl,take2");if(buf){free(buf);}returnNULL;}
ifm=(structif_msghdr*)buf;sdl=(structsockaddr_dl*)(ifm+1);ptr=(unsignedchar*)LLADDR(sdl);NSString*outstring=[NSStringstringWithFormat:@"%02x:%02x:%02x:%02x:%02x:%02x",*ptr,*(ptr+1),*(ptr+2),*(ptr+3),*(ptr+4),*(ptr+5)];if(buf){free(buf);}return[outstringuppercaseString];
准确性/唯一性:iOS7之前可用,iOS7之后,请求Mac地址都会返回一个固定值“02:00:00:00:00:00”,已被苹果禁用
4、IDFV
简介:Vendor标示符(IDFV-identifierForVendor)
采集方式:[[[UIDevicecurrentDevice]identifierForVendor]UUIDString]
准确性/唯一性:同一个开发者账户下的app,在同一台终端上生成的IDFV一致,但是将此开发者账户下所有的app删除,下次再安装第一个app时,又会生成一个不一致的IDFV。例如:对于com.tencent.appone和com.tencent.apptwo这两个BundleID生成的IDFV是一样的,如果将这两个APP都删除,IDFV就会重新生成。(公司内有多个发布账号,每个账号下IDFV不同)
5、IDFA
采集方式:[[[ASIdentifierManagersharedManager]advertisingIdentifier]UUIDString]
准确性/唯一性:
四、业务自建Guid体系了解
这里也说明一下业务GUID和QIMEI体系的区别:
各业务GUID与灯塔Qimei方案思路是一致的,Qimei可补充GUID的找回能力,提升GUID稳定性。GUID和Qimei由服务器生成下发,并建立和设备其他各种ID映射关系(imei,mac,imsi,android_id等),用于丢失后的找回。主要区别在于Qimei基于全业务范围的积累找回,GUID基本单业务范围的积累找回。以及终端对于基础ID的采集,QIMEI/GUID的存储共享能力上的区别。