青岛专业网站开发公司,北京专业网站制作公司,扬中市住房和城乡建设局网站,app应用开发公司文章目录 ID 体系iOS设备信息详解IDFA介绍特点IDFA新政前世今生获取方式 IDFV介绍获取方式 UUID介绍特点获取方式 UDID介绍获取方式 OpenUDID介绍 Bundle ID介绍分类其他 IP地址介绍获取方式 MAC地址介绍获取方式正常获取MAC地址获取对应Wi-Fi的MAC地址 系统版本获取方式 设备型… 文章目录 ID 体系iOS设备信息详解IDFA介绍特点IDFA新政前世今生获取方式 IDFV介绍获取方式 UUID介绍特点获取方式 UDID介绍获取方式 OpenUDID介绍 Bundle ID介绍分类其他 IP地址介绍获取方式 MAC地址介绍获取方式正常获取MAC地址获取对应Wi-Fi的MAC地址 系统版本获取方式 设备型号获取方式 设备名称获取方式 磁盘大小获取方式 磁盘剩余空间获取方式 电量获取方式 电池状态获取方式 屏幕尺寸获取方式 屏幕亮度获取方式 音量大小获取方式 Wifi名称获取方式 网络制式获取方式 是否越狱获取方式 是否插入SIM卡获取方式 是否允许推送获取方式 剪切板内容获取方式 是否使用代理获取方式 附录1—Android主要设备信息DeviceId设备ID/DID介绍补充知识国际移动设备识别码IMEI移动设备识别码MEID 系统版本迭代带来的影响获取方式补充获取方式 ANDROID_ID介绍获取方式 AAIDOAID匿名设备标识符 附录2—Android 系统名字、版本、API level的对应关系 ID 体系
所谓的ID体系就是在我们想要追踪一个用户就必须先找到用户在这个过程中标识符ID就像我们的另一张身份证它们就代表了数字化之后的你和我。
不同 App 可能通过某些唯一标识符对你进行强制跟踪广告平台则会通过这个唯一标识符对你进行用户画像描绘进而共享给相关 App 及其后台一旦你打开了其中的某个 App那么你就会被识别到你点了什么、看过什么、可能需要什么它们比你自己都清楚。
而在智能设备的 ID 体系中存在许许多多不同种类的标识符本篇文章我们就简单介绍一下这些标识符。
iOS设备信息详解 IDFA
介绍
IDFA全称为Identifier for Advertising广告标识符在同一个设备上的所有App都会取到相同的值用于给开发者跟踪广告效果用的可看作是iPhone 的设备临时身份证说是临时身份证是因为它允许用户更换。
特点
以下方式都可以改变IDFA
重置系统设置程序 - 通用 - 还原 - 还原位置与隐私设置程序- 通用 - 关于本机 - 广告 - 还原广告标示符 - 重启。
用户可以在 设置 - 隐私 - 广告追踪里重置此id或者限制追踪就会出现idfa不一致或取不到故绝不可以作为业务分析的主id来识别用户。
IDFA新政
根据苹果《用户隐私和数据使用》规定 从iOS 14.5iPadOS 14.5和Apple tvOS 14.5开始App需要通过App Tracking Transparency框架征得用户许可然后才能跟踪用户或访问其设备的广告标识符。
新旧政策的主要区别在于新版本是opt-in手动选择打开旧版本是opt-out手动选择关闭。iOS 14增加“应用跟踪透明度”App Tracking Transparency的提示弹窗。根据苹果《用户隐私和数据使用》QAApp为满足以下用途而接收或共享其中任何标识符包括但不限于设备的广告标识符、会话ID、指纹ID和设备图形标识符App都必须使用ATT框架来征得用户同意。而在当前的系统版本里广告跟踪功能是默认打开状态的关闭选项需要手动操作。
新版本
前世今生 获取方式 (NSString *)getIDFA
{return [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
}IDFV
介绍
全名identifierForVendor 是给Vendor(开发商)标识用户用的。在同一设备上同一个Vender的应用的所有appIDFV的值相同。
判断Vender是否相同是通过BundleID的反转的前两部分进行匹配如果相同就是同一个Vender例如对于com.taobao.app1, com.taobao.app2 这两个BundleID来说就属于同一个Vender共享同一个idfv的值。和idfa不同的是idfv的值是一定能取到的所以非常适合于作为内部用户行为分析的主id来标识用户替代OpenUDID。
获取方式 (NSString *)getIDFV
{return [[UIDevice currentDevice].identifierForVendor UUIDString];
}UUID
介绍
全称时Universally Unique IDentifier它是基于iOS设备上面某个单个的应用程序只要用户没有完全删除应用程序则这个 UUID 在用户使用该应用程序的时候一直保持不变。
特点
如果用户删除了这个应用程序然后再重新安装刷机或重装系统后uuid还是会改变。
我们可以获取到UUID然后把UUID保存到KeyChain里面这样以后即使APP删了再装回来也可以从KeyChain中读取回来。使用group还可以保证同一个开发商的所有程序针对同一台设备能够获取到相同的不变的UDID,以此来实现设备的唯一标示。
获取方式 (NSString *)getUUID
{return [[NSUUID UUID] UUIDString];
}UDID
介绍
UDID全称为Unique Device Identifier Description是一个40个字符串的序号用来标示唯一的iOS设备。
iOS 2.0版本以后UIDevice提供一个获取设备唯一标识符的方法uniqueIdentifier通过该方法我们可以获取设备的序列号这个也是目前为止唯一可以确认唯一的标示符。 许多开发者把UDID跟用户的真实姓名、密码、住址、其它数据关联起来网络窥探者会从多个应用收集这些数据然后顺藤摸瓜得到这个人的许多隐私数据。同时大部分应用确实在频繁传输UDID和私人信息。iOS5之后废弃
虽然iOS5之后无法通过API获取UDID
获取方式
[UIDevice currentDevice].identifierForVendor.UUIDStringOpenUDID
介绍
不是苹果官方的是一个替代 UDID 的第三方解决方案 缺点是如果你完全删除全部带有 OpenUDID SDK 包的 App比如恢复系统等那么 OpenUDID 会重新生成而且和之前的值会不同相当于新设备
Bundle ID
介绍
全称Bundle identifier也叫 App ID 或者应用 ID一个开发者账号下每一个 iOS应用的唯一标识就像一个人的身份证号码。
分类
Explicit App ID「明确的 App ID」一般格式是com.company.appName这种 id 只能用在一个app上每一个新应用都要创建并只有一个。Wildcard App ID「通配符 App ID」 一般格式是com.domainname.* 这种 id 可以用在多个应用上虽然方便但是使用这种id的应用不能使用通知功能所以不常用。
其他
iOS 是 bundle idandroid 是 packageName一般情况下都叫 bundle ID 或者 App ID。
我们看一下淘宝同开发者应用的Bundle ID 下图是淘宝同开发者应用列表只截取了部分数据 来看一下Bundle ID结果 我们在上面IDFV也介绍了同一个Vendor前两个字段是相同的。
IP地址
介绍
IP地址Internet Protocol Address是指互联网协议地址又译为网际协议地址。IP地址是IP协议提供的一种统一的地址格式它为互联网上的每一个网络和每一台主机分配一个逻辑地址以此来屏蔽物理地址的差异。
获取方式
/** 获取设备当前网络IP地址*/
- (NSString *)getIPAddress:(BOOL)preferIPv4
{NSArray *searchArray preferIPv4 ?[ IOS_VPN / IP_ADDR_IPv4, IOS_VPN / IP_ADDR_IPv6, IOS_WIFI / IP_ADDR_IPv4, IOS_WIFI / IP_ADDR_IPv6, IOS_CELLULAR / IP_ADDR_IPv4, IOS_CELLULAR / IP_ADDR_IPv6 ] :[ IOS_VPN / IP_ADDR_IPv6, IOS_VPN / IP_ADDR_IPv4, IOS_WIFI / IP_ADDR_IPv6, IOS_WIFI / IP_ADDR_IPv4, IOS_CELLULAR / IP_ADDR_IPv6, IOS_CELLULAR / IP_ADDR_IPv4 ] ;NSDictionary *addresses [self getIPAddr];__block NSString *address;[searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL * _Nonnull stop) {address addresses[key];//筛选出IP地址格式if([self isValidatIP:address]) *stop YES;}];return address ? address : 0.0.0.0;
}
- (BOOL)isValidatIP:(NSString *)ipAddress {if (ipAddress.length 0) {return NO;}NSString *urlRegEx ^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])$;NSError *error;NSRegularExpression *regex [NSRegularExpression regularExpressionWithPattern:urlRegEx options:0 error:error];if (regex ! nil) {NSTextCheckingResult *firstMatch[regex firstMatchInString:ipAddress options:0 range:NSMakeRange(0, [ipAddress length])];return firstMatch;}return NO;
}
- (NSDictionary *)getIPAddr
{NSMutableDictionary *addresses [NSMutableDictionary dictionaryWithCapacity:8];// retrieve the current interfaces - returns 0 on successstruct ifaddrs *interfaces;if(!getifaddrs(interfaces)) {// Loop through linked list of interfacesstruct ifaddrs *interface;for(interfaceinterfaces; interface; interfaceinterface-ifa_next) {if(!(interface-ifa_flags IFF_UP) /* || (interface-ifa_flags IFF_LOOPBACK) */ ) {continue; // deeply nested code harder to read}const struct sockaddr_in *addr (const struct sockaddr_in*)interface-ifa_addr;char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];if(addr (addr-sin_familyAF_INET || addr-sin_familyAF_INET6)) {NSString *name [NSString stringWithUTF8String:interface-ifa_name];NSString *type;if(addr-sin_family AF_INET) {if(inet_ntop(AF_INET, addr-sin_addr, addrBuf, INET_ADDRSTRLEN)) {type IP_ADDR_IPv4;}} else {const struct sockaddr_in6 *addr6 (const struct sockaddr_in6*)interface-ifa_addr;if(inet_ntop(AF_INET6, addr6-sin6_addr, addrBuf, INET6_ADDRSTRLEN)) {type IP_ADDR_IPv6;}}if(type) {NSString *key [NSString stringWithFormat:%/%, name, type];addresses[key] [NSString stringWithUTF8String:addrBuf];}}}// Free memoryfreeifaddrs(interfaces);}return [addresses count] ? addresses : nil;
}MAC地址
介绍
用来定义网络设备的位置。一个主机会有一个 MAC 地址MAC 地址是网卡决定的是固定的为了保护用户隐私苹果已经禁止读取这个标识了。
获取方式
正常获取MAC地址
结果是020000000000
- (NSString *)getmacaddress2
{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, *(ptr1), *(ptr2), *(ptr3), *(ptr4), *(ptr5)];// NSString *outstring [NSString stringWithFormat:%02x%02x%02x%02x%02x%02x, *ptr, *(ptr1), *(ptr2), *(ptr3), *(ptr4), *(ptr5)];NSLog(outString:%, outstring);free(buf);return [outstring uppercaseString];
}
获取对应Wi-Fi的MAC地址
不同的Wi-Fi的MAC地址不同
- (nullable NSString *)getMacAddress3 {res_9_init();int len;//get currnet ip addressNSString *ip [self currentIPAddressOf:IOS_WIFI];if(ip nil) {fprintf(stderr, could not get current IP address of en0\n);return DUMMY_MAC_ADDR;}//end if//set port and destination_res.nsaddr_list[0].sin_family AF_INET;_res.nsaddr_list[0].sin_port htons(MDNS_PORT);_res.nsaddr_list[0].sin_addr.s_addr [self IPv4Pton:ip];_res.nscount 1;unsigned char response[NS_PACKETSZ];//send mdns queryif((len res_9_query(QUERY_NAME, ns_c_in, ns_t_ptr, response, sizeof(response))) 0) {fprintf(stderr, res_search(): %s\n, hstrerror(h_errno));return DUMMY_MAC_ADDR;}//end if//parse mdns messagens_msg handle;if(ns_initparse(response, len, handle) 0) {fprintf(stderr, ns_initparse(): %s\n, hstrerror(h_errno));return DUMMY_MAC_ADDR;}//end if//get answer lengthlen ns_msg_count(handle, ns_s_an);if(len 0) {fprintf(stderr, ns_msg_count return zero\n);return DUMMY_MAC_ADDR;}//end if//try to get mac address from dataNSString *macAddress nil;for(int i 0 ; i len ; i) {ns_rr rr;ns_parserr(handle, ns_s_an, 0, rr);if(ns_rr_class(rr) ns_c_in ns_rr_type(rr) ns_t_ptr !strcmp(ns_rr_name(rr), QUERY_NAME)) {char *ptr (char *)(ns_rr_rdata(rr) 1);int l (int)strcspn(ptr, );char *tmp calloc(l 1, sizeof(char));if(!tmp) {perror(calloc());continue;}//end ifmemcpy(tmp, ptr, l);macAddress [NSString stringWithUTF8String:tmp];free(tmp);}//end if}//end for eachmacAddress macAddress ? macAddress : DUMMY_MAC_ADDR;return macAddress;
}//end- (nonnull NSString *)currentIPAddressOf: (nonnull NSString *)device {struct ifaddrs *addrs;NSString *ipAddress nil;if(getifaddrs(addrs) ! 0) {return nil;}//end if//get ipv4 addressfor(struct ifaddrs *addr addrs ; addr ; addr addr-ifa_next) {if(!strcmp(addr-ifa_name, [device UTF8String])) {if(addr-ifa_addr) {struct sockaddr_in *in_addr (struct sockaddr_in *)addr-ifa_addr;if(in_addr-sin_family AF_INET) {ipAddress [self IPv4Ntop:in_addr-sin_addr.s_addr];break;}//end if}//end if}//end if}//end forfreeifaddrs(addrs);return ipAddress;
}//end currentIPAddressOf:- (nullable NSString *)IPv4Ntop: (in_addr_t)addr {char buffer[INET_ADDRSTRLEN] {0};return inet_ntop(AF_INET, addr, buffer, sizeof(buffer)) ?[NSString stringWithUTF8String:buffer] : nil;
}//end IPv4Ntop:- (in_addr_t)IPv4Pton: (nonnull NSString *)IPAddr {in_addr_t network INADDR_NONE;return inet_pton(AF_INET, [IPAddr UTF8String], network) 1 ?network : INADDR_NONE;
}//end IPv4Pton
系统版本
获取方式 (NSString *)getSystemVersion
{return [[UIDevice currentDevice] systemVersion];
}设备型号
获取方式
#import sys/utsname.h (NSString *)getDeviceModel
{struct utsname systemInfo;uname(systemInfo);NSString *deviceString [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];if ([deviceString isEqualToString:iPhone3,1]) return iPhone 4;if ([deviceString isEqualToString:iPhone3,2]) return iPhone 4;if ([deviceString isEqualToString:iPhone3,3]) return iPhone 4;if ([deviceString isEqualToString:iPhone4,1]) return iPhone 4S;if ([deviceString isEqualToString:iPhone5,1]) return iPhone 5;if ([deviceString isEqualToString:iPhone5,2]) return iPhone 5 (GSM CDMA);if ([deviceString isEqualToString:iPhone5,3]) return iPhone 5c (GSM);if ([deviceString isEqualToString:iPhone5,4]) return iPhone 5c (GSM CDMA);if ([deviceString isEqualToString:iPhone6,1]) return iPhone 5s (GSM);if ([deviceString isEqualToString:iPhone6,2]) return iPhone 5s (GSM CDMA);if ([deviceString isEqualToString:iPhone7,1]) return iPhone 6 Plus;if ([deviceString isEqualToString:iPhone7,2]) return iPhone 6;if ([deviceString isEqualToString:iPhone8,1]) return iPhone 6s;if ([deviceString isEqualToString:iPhone8,2]) return iPhone 6s Plus;if ([deviceString isEqualToString:iPhone8,4]) return iPhone SE;if ([deviceString isEqualToString:iPhone9,1]) return iPhone 7;if ([deviceString isEqualToString:iPhone9,2]) return iPhone 7 Plus;if ([deviceString isEqualToString:iPhone9,3]) return iPhone 7;if ([deviceString isEqualToString:iPhone9,4]) return iPhone 7 Plus;if ([deviceString isEqualToString:iPhone10,1]) return iPhone 8;if ([deviceString isEqualToString:iPhone10,4]) return iPhone 8;if ([deviceString isEqualToString:iPhone10,2]) return iPhone 8 Plus;if ([deviceString isEqualToString:iPhone10,5]) return iPhone 8 Plus;if ([deviceString isEqualToString:iPhone10,3]) return iPhone X;if ([deviceString isEqualToString:iPhone10,6]) return iPhone X;if ([deviceString isEqualToString:iPhone11,2]) return iPhone XS;if ([deviceString isEqualToString:iPhone11,4]) return iPhone XS Max;if ([deviceString isEqualToString:iPhone11,6]) return iPhone XS Max;if ([deviceString isEqualToString:iPhone11,8]) return iPhone XR;if ([deviceString isEqualToString:iPhone12,1]) return iPhone 11;if ([deviceString isEqualToString:iPhone12,3]) return iPhone 11 Pro;if ([deviceString isEqualToString:iPhone12,5]) return iPhone 11 Pro Max;if ([deviceString isEqualToString:iPhone12,8]) return iPhone SE (2nd generation);if ([deviceString isEqualToString:iPhone13,1]) return iPhone 12 mini;if ([deviceString isEqualToString:iPhone13,2]) return iPhone 12;if ([deviceString isEqualToString:iPhone13,3]) return iPhone 12 Pro;if ([deviceString isEqualToString:iPhone13,4]) return iPhone 12 Pro Max;if ([deviceString isEqualToString:iPhone14,2]) return iPhone 13 Pro;if ([deviceString isEqualToString:iPhone14,2]) return iPhone 13 Pro;if ([deviceString isEqualToString:iPhone14,2]) return iPhone 13 Pro;if ([deviceString isEqualToString:iPhone14,3]) return iPhone 13 Pro Max;if ([deviceString isEqualToString:iPhone14,4]) return iPhone 13 mini;if ([deviceString isEqualToString:iPhone14,5]) return iPhone 13;return deviceString;
}设备名称
获取方式 (NSString *)getDeviceName
{return [UIDevice currentDevice].name;
}磁盘大小
获取方式 (long)getDiskTotalSize
{NSDictionary *systemAttributes [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:nil];NSNumber *diskTotalSize [systemAttributes objectForKey:NSFileSystemSize];return (long)(diskTotalSize.floatValue / 1024.f / 1024.f);
}磁盘剩余空间
获取方式 (long)getDiskFreeSize
{NSDictionary *systemAttributes [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:nil];NSNumber *diskFreeSize [systemAttributes objectForKey:NSFileSystemFreeSize];return (long)(diskFreeSize.floatValue / 1024.f / 1024.f);
}电量
获取方式 (CGFloat)getBatteryLevel
{[UIDevice currentDevice].batteryMonitoringEnabled YES;return [[UIDevice currentDevice] batteryLevel];
}电池状态
获取方式 (NSString *)getBatteryState
{[UIDevice currentDevice].batteryMonitoringEnabled YES;UIDeviceBatteryState batteryState [UIDevice currentDevice].batteryState;switch (batteryState) {case UIDeviceBatteryStateUnplugged:return 未充电;case UIDeviceBatteryStateCharging:return 充电中;case UIDeviceBatteryStateFull:return 已充满;default:return 未知;}
}屏幕尺寸
获取方式 (CGSize)getScreenSize
{CGRect screenBounds [[UIScreen mainScreen] bounds];CGFloat screenScale [UIScreen mainScreen].scale;CGSize screenSize CGSizeMake(screenBounds.size.width * screenScale, screenBounds.size.height * screenScale);return screenSize;
}屏幕亮度
获取方式 (CGFloat)getScreenBrightness
{return [UIScreen mainScreen].brightness;
}音量大小
获取方式
#import AVFoundation/AVFoundation.h (CGFloat)getDeviceVolume
{return [[AVAudioSession sharedInstance] outputVolume];
}Wifi名称
获取方式
#import SystemConfiguration/CaptiveNetwork.h (NSString *)getWifiSSID
{NSArray *ifs (__bridge id)CNCopySupportedInterfaces();id info nil;for (NSString *ifnam in ifs) {info (__bridge id)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);if (info [info count]) {break;}}NSDictionary *dctySSID (NSDictionary *)info;return [dctySSID objectForKey:SSID];
}网络制式
获取方式
#import CoreTelephony/CTTelephonyNetworkInfo.h
#import CoreTelephony/CTCarrier.h (NSString *)getNetCarrier
{NSString *mobileCarrier;CTTelephonyNetworkInfo *networkInfo [[CTTelephonyNetworkInfo alloc] init];CTCarrier *carrier networkInfo.subscriberCellularProvider;NSString *MCC carrier.mobileCountryCode;NSString *MNC carrier.mobileNetworkCode;if (!MCC || !MNC) {mobileCarrier No Sim;} else {if ([MCC isEqualToString:460]) {if ([MNC isEqualToString:00] || [MNC isEqualToString:02] || [MNC isEqualToString:07]) {mobileCarrier China Mobile;} else if ([MNC isEqualToString:01] || [MNC isEqualToString:06]) {mobileCarrier China Unicom;} else if ([MNC isEqualToString:03] || [MNC isEqualToString:05] || [MNC isEqualToString:11]) {mobileCarrier China Telecom;} else if ([MNC isEqualToString:20]) {mobileCarrier China Tietong;} else {mobileCarrier [NSString stringWithFormat:MNC%, MNC];}} else {mobileCarrier Foreign Carrier;}}return mobileCarrier;
}是否越狱
获取方式
#import sys/stat.h
#import dlfcn.h//判断是否越狱(BOOL)isJailBreak
{//以下检测的过程是越往下越狱越高级//获取越狱文件路径NSString *cydiaPath /Applications/Cydia.app;NSString *aptPath /private/var/lib/apt/;if ([[NSFileManager defaultManager] fileExistsAtPath:cydiaPath]) {return YES;}if ([[NSFileManager defaultManager] fileExistsAtPath:aptPath]) {return YES;}//可能存在hook了NSFileManager方法此处用底层C stat去检测struct stat stat_info;if (0 stat(/Library/MobileSubstrate/MobileSubstrate.dylib, stat_info)) {return YES;}if (0 stat(/Applications/Cydia.app, stat_info)) {return YES;}if (0 stat(/var/lib/cydia/, stat_info)) {return YES;}if (0 stat(/var/cache/apt, stat_info)) {return YES;}//可能存在stat也被hook了可以看stat是不是出自系统库有没有被攻击者换掉。这种情况出现的可能性很小int ret;Dl_info dylib_info;int (*func_stat)(const char *,struct stat *) stat;if ((ret dladdr(func_stat, dylib_info))) {//相等为0不相等肯定被攻击if (strcmp(dylib_info.dli_fname, /usr/lib/system/libsystem_kernel.dylib)) {return YES;}}//通常越狱机的输出结果会包含字符串Library/MobileSubstrate/MobileSubstrate.dylib。//攻击者给MobileSubstrate改名原理都是通过DYLD_INSERT_LIBRARIES注入动态库。那么可以检测当前程序运行的环境变量char *env getenv(DYLD_INSERT_LIBRARIES);if (env ! NULL) {return YES;}return NO;
}是否插入SIM卡
获取方式
#import CoreTelephony/CTTelephonyNetworkInfo.h
#import CoreTelephony/CTCarrier.h (BOOL)isSimInserted
{CTTelephonyNetworkInfo *networkInfo [[CTTelephonyNetworkInfo alloc] init];CTCarrier *carrier [networkInfo subscriberCellularProvider];if (!carrier.isoCountryCode) {return NO;}return YES;
}是否允许推送
获取方式 (BOOL)isPushEnabled
{if ([[UIDevice currentDevice].systemVersion floatValue] 8.0f) {UIUserNotificationSettings *setting [[UIApplication sharedApplication] currentUserNotificationSettings];if (UIUserNotificationTypeNone setting.types) {return NO;} else {return YES;}} else {UIRemoteNotificationType type [[UIApplication sharedApplication] enabledRemoteNotificationTypes];if(UIRemoteNotificationTypeNone type){return NO;} else {return YES;}}
}剪切板内容
获取方式 (NSString *)getPasteBoardString
{return [UIPasteboard generalPasteboard].string;
}是否使用代理
获取方式 (BOOL)isViaProxy
{NSDictionary *proxySettings (__bridge NSDictionary *)(CFNetworkCopySystemProxySettings());NSArray *proxies (__bridge NSArray *)(CFNetworkCopyProxiesForURL((__bridge CFURLRef _Nonnull)([NSURL URLWithString:https://www.baidu.com/]), (__bridge CFDictionaryRef _Nonnull)(proxySettings)));NSDictionary *settings proxies[0];if (![[settings objectForKey:(NSString *)kCFProxyTypeKey] isEqualToString:kCFProxyTypeNone]){return YES;}return NO;
}附录1—Android主要设备信息 DeviceId设备ID/DID
介绍
DeviceId是用来标识一台Android物理设备的唯一id
Google提供了TelephonyManager.getDeviceId方法来获取Android的DID。该API是获取GSM手机的国际移动设备识别码IMEI或者 CDMA手机的移动设备识别码MEID 。但该API存在一些限制。
补充知识
国际移动设备识别码IMEI
**全称“International Mobile Equipment Identity”*是通常所说的手机序列号、手机“串号”。用于在移动电话网络中识别每一部独立的手机等移动通信设备相当于移动电话的身份证序列号共有15~17位数字通过在手机拨号键盘中输入#06#即可查询。
但存在以下限制
1、自API23Android 6.0开始获取IMEI需要用户予android.permission.READ_PHONE_STATE2、自API29Android 10.0开始您的应用必须是设备或个人资料所有者应用具有特殊运营商权限或具有 READ_PRIVILEGED_PHONE_STATE 特许权限才能访问这些标识符。3、某些小厂商某型号的手机IMEI可能相同。
移动设备识别码MEID
全称“Mobile Equipment Identifier”是CDMA手机的身份识别码也是每台CDMA手机或通讯平板唯一的识别码。通过这个识别码网络端可以对该手机进行跟踪和监管。用于CDMA制式的手机。MEID的数字范围是十六进制的和IMEI的格式类似。
存在的限制同IMEI的限制。
系统版本迭代带来的影响 为了更好保护用户隐私谷歌对安卓Q系统中所有获取设备识别码的接口都增加了新的权限控制READ_PRIVILEGED_PHONE_STATE该权限需要系统签名的应用才能申请。同时系统默认WiFi Mac地址随机化当设备连上不同的WiFi网络时随机生成Mac地址 通过READ_PHONE_STATE权限获取Device ID的应用以及将设备WiFi Mac地址作为设备唯一标志符的应用将受影响 在Android10及以上设备中对于TargetSdkVersionQ且没有申请READ_PHONE_STATE权限的应用和TargetSdkVersionQ的全部应用获取Device ID会抛异常SecurityException。 在Android10及以上设备中对于 TargetSdkVersionQ且申请了READ_PHONE_STATE权限的应用通过getDeviceId接口读取的值为Null。
获取方式
public static String getIMEIDeviceId(Context context) {String deviceId;//当APK运行在Android10API29及以上时获取到的是AndroidIDif (Build.VERSION.SDK_INT Build.VERSION_CODES.Q) {deviceId Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);} else {final TelephonyManager mTelephony (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);//当APK运行在Android6.0API23及以上时需要check有无READ_PHONE_STATE权限。if (Build.VERSION.SDK_INT Build.VERSION_CODES.M) {if (context.checkSelfPermission(Manifest.permission.READ_PHONE_STATE) ! PackageManager.PERMISSION_GRANTED) {return ;}}assert mTelephony ! null;//如果TelephonyManager获取到的DeviceId不为nullif (mTelephony.getDeviceId() ! null) {//获取GSM手机的国际移动设备识别码IMEI或者 CDMA手机的移动设备识别码MEID).deviceId mTelephony.getDeviceId();} else {//如果DeviceId为null我们的DID依然是AndroidID。deviceId Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);}}return deviceId;}补充获取方式
以上获取方式有诸多不足之处也有很多厂商选择自己用硬件拼出来一个UUID 但是这种方式也不是百分百准确。
public static String getUUID()
{String serial null;String m_szDevIDShort 35 Build.BOARD.length() % 10 Build.BRAND.length() % 10 Build.CPU_ABI.length() % 10 Build.DEVICE.length() % 10 Build.DISPLAY.length() % 10 Build.HOST.length() % 10 Build.ID.length() % 10 Build.MANUFACTURER.length() % 10 Build.MODEL.length() % 10 Build.PRODUCT.length() % 10 Build.TAGS.length() % 10 Build.TYPE.length() % 10 Build.USER.length() % 10; //13 位try {if (Build.VERSION.SDK_INT Build.VERSION_CODES.O) {serial android.os.Build.getSerial();} else {serial Build.SERIAL;}//API9 使用serial号return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();} catch (Exception exception) {//serial需要一个初始化serial serial; // 随便一个初始化}//使用硬件信息拼凑出来的15位号码return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
}
有一个比较详细的源码自取地址https://github.com/57xiaoyu/DeviceIDUtils
ANDROID_ID
介绍
又称SSAID。设备启动时随机生成一个 64 位数字表示为十六进制字符串对于应用签名密钥、用户和设备的每个组合都是唯一的。 ANDROID_ID 的值受签名密钥和用户的限制。
但存在如下限制
1、如果在设备上执行恢复出厂设置或 APK 签名密钥更改则该值可能会更改。2、某些小厂商的Android手机可能为null或相同。
Android 开发者文档中有关于Android 8.0后的隐私性和SSAID的变化说明 从图中不难看出在 Android 8.0 以后签名不同的 App 所获取的 Android IDSSAID是不一样的但同一个开发者可以根据自己的数字签名将所开发的不同 App 进行关联。
获取方式
private String android_id Secure.getString(getContext().getContentResolver(),Secure.ANDROID_ID); AAID
AAID 与 IDFA 作用相同——IDFA 是 iOS 平台内的广告跟踪 IDAAID 则用于 Android 平台。
它们都是一种非永久、可重置的标识符专门提供给 App 以进行广告行为用户随时可以重置该类 ID或通过系统设置关闭个性化广告跟踪。但 AAID 依托于 Google 服务框架因此如果手机没有内置该框架、或框架不完整、或无法连接到相关服务这些情况都有可能导致 AAID 不可用。
OAID匿名设备标识符
是指华为、小米、OPPO、VIVO等安卓系统设备提供一串非永久性设备标识符示例1fe9a970-efbb-29e0-0bdd-f5dbbf751ab5。
Android 10 之后的替代方案
OAID 的本质其实是一种在国行系统内使用的、应对 Android 10 限制读取 IMEI 的、「拯救」国内移动广告的广告跟踪标识符其背后是 移动安全联盟Mobile Security Alliance简称 MSA。
附录2—Android 系统名字、版本、API level的对应关系
代号版本API 级别/NDK 版本Android12L12API 级别 32Android1212API 级别 31Android1111API 级别 30Android1010API 级别 29Pie9API 级别 28Oreo8.1.0API 级别 27Oreo8.0.0API 级别 26Nougat7.1API 级别 25Nougat7API 级别 24Marshmallow6API 级别 23Lollipop5.1API 级别 22Lollipop5API 级别 21KitKat4.4 - 4.4.4API 级别 19Jelly Bean4.3.xAPI 级别 18Jelly Bean4.2.xAPI 级别 17Jelly Bean4.1.xAPI 级别 16Ice Cream Sandwich4.0.3 - 4.0.4API 级别 15NDK 8Ice Cream Sandwich4.0.1 - 4.0.2API 级别 14NDK 7Honeycomb3.2.xAPI 级别 13Honeycomb3.1API 级别 12NDK 6Honeycomb3API 级别 11Gingerbread2.3.3 - 2.3.7API 级别 10Gingerbread2.3 - 2.3.2API 级别 9NDK 5Froyo2.2.xAPI 级别 8NDK 4Eclair2.1API 级别 7NDK 3Eclair2.0.1API 级别 6Eclair2API 级别 5Donut1.6API 级别 4NDK 2Cupcake1.5API 级别 3NDK 1无代号1.1API 级别 2无代号1API 级别 1
参考文章 1、https://blog.csdn.net/u011774517/article/details/107456052 2、https://baijiahao.baidu.com/s?id1695811594529218926wfrspiderforpc 3、https://juejin.cn/post/6844904178653855752 4、https://www.helloworld.net/p/5706465559 5、https://www.jb51.net/shouji/674558.html 6、https://blog.csdn.net/weixin_38244174/article/details/120249949 7、https://blog.csdn.net/linxinfa/article/details/102395922 8、https://blog.csdn.net/weixin_42600398/article/details/117984064 9、https://blog.csdn.net/qiluoyiyi/article/details/118418383