怎么做网站主页/电商营销
文章目录
- 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
- 介绍
- 获取方式
- AAID
- OAID(匿名设备标识符)
- 附录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.5,iPadOS 14.5和Apple tvOS 14.5开始,App需要通过App Tracking Transparency框架征得用户许可,然后才能跟踪用户或访问其设备的广告标识符。
新旧政策的主要区别在于,新版本是opt-in(手动选择打开),旧版本是opt-out(手动选择关闭)。iOS 14增加“应用跟踪透明度”(App Tracking Transparency)的提示弹窗。根据苹果《用户隐私和数据使用》QA,App为满足以下用途而接收或共享其中任何标识符(包括但不限于设备的广告标识符、会话ID、指纹ID和设备图形标识符),App都必须使用ATT框架来征得用户同意。而在当前的系统版本里,广告跟踪功能是默认打开状态的,关闭选项需要手动操作。
新版本:
前世今生
获取方式
+ (NSString *)getIDFA
{return [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
}
IDFV
介绍
全名:identifierForVendor ,是给Vendor(开发商)标识用户用的。在同一设备上,同一个Vender的应用的所有app,IDFV的值相同。
判断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.UUIDString
OpenUDID
介绍
不是苹果官方的,是一个替代 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 id,android 是 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(interface=interfaces; interface; interface=interface->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_family==AF_INET || addr->sin_family==AF_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, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)];// 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];
}
获取对应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、自API23(Android 6.0)开始,获取IMEI需要用户予"android.permission.READ_PHONE_STATE";
- 2、自API29(Android 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及以上设备中,对于TargetSdkVersion<Q且没有申请READ_PHONE_STATE权限的应用和TargetSdkVersion>=Q的全部应用,获取Device ID会抛异常SecurityException。
-
在Android10及以上设备中,对于 TargetSdkVersion<Q且申请了READ_PHONE_STATE权限的应用,通过getDeviceId接口读取的值为Null。
获取方式
public static String getIMEIDeviceId(Context context) {String deviceId;//当APK运行在Android10(API>=29)及以上时,获取到的是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.0(API>=23)及以上时,需要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;}//API>=9 使用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 ID(SSAID)是不一样的,但同一个开发者可以根据自己的数字签名,将所开发的不同 App 进行关联。
获取方式
private String android_id = Secure.getString(getContext().getContentResolver(),Secure.ANDROID_ID);
AAID
AAID 与 IDFA 作用相同——IDFA 是 iOS 平台内的广告跟踪 ID,AAID 则用于 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 版本 |
---|---|---|
Android12L | 12 | API 级别 32 |
Android12 | 12 | API 级别 31 |
Android11 | 11 | API 级别 30 |
Android10 | 10 | API 级别 29 |
Pie | 9 | API 级别 28 |
Oreo | 8.1.0 | API 级别 27 |
Oreo | 8.0.0 | API 级别 26 |
Nougat | 7.1 | API 级别 25 |
Nougat | 7 | API 级别 24 |
Marshmallow | 6 | API 级别 23 |
Lollipop | 5.1 | API 级别 22 |
Lollipop | 5 | API 级别 21 |
KitKat | 4.4 - 4.4.4 | API 级别 19 |
Jelly Bean | 4.3.x | API 级别 18 |
Jelly Bean | 4.2.x | API 级别 17 |
Jelly Bean | 4.1.x | API 级别 16 |
Ice Cream Sandwich | 4.0.3 - 4.0.4 | API 级别 15,NDK 8 |
Ice Cream Sandwich | 4.0.1 - 4.0.2 | API 级别 14,NDK 7 |
Honeycomb | 3.2.x | API 级别 13 |
Honeycomb | 3.1 | API 级别 12,NDK 6 |
Honeycomb | 3 | API 级别 11 |
Gingerbread | 2.3.3 - 2.3.7 | API 级别 10 |
Gingerbread | 2.3 - 2.3.2 | API 级别 9,NDK 5 |
Froyo | 2.2.x | API 级别 8,NDK 4 |
Eclair | 2.1 | API 级别 7,NDK 3 |
Eclair | 2.0.1 | API 级别 6 |
Eclair | 2 | API 级别 5 |
Donut | 1.6 | API 级别 4,NDK 2 |
Cupcake | 1.5 | API 级别 3,NDK 1 |
(无代号) | 1.1 | API 级别 2 |
(无代号) | 1 | API 级别 1 |
参考文章:
1、https://blog.csdn.net/u011774517/article/details/107456052
2、https://baijiahao.baidu.com/s?id=1695811594529218926&wfr=spider&for=pc
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
相关文章:

iOS设备信息详解
文章目录 ID 体系iOS设备信息详解IDFA介绍特点IDFA新政前世今生获取方式 IDFV介绍获取方式 UUID介绍特点获取方式 UDID介绍获取方式 OpenUDID介绍 Bundle ID介绍分类其他 IP地址介绍获取方式 MAC地址介绍获取方式正常获取MAC地址获取对应Wi-Fi的MAC地址 系统版本获取方式 设备型…...

如何使用支付宝沙箱环境支付并公网调用sdk创建支付单服务
文章目录 1.测试环境2.本地配置2. 内网穿透2.1 下载安装cpolar内网穿透2.2 创建隧道3. 测试公网访问4. 配置固定二级子域名4.1 保留一个二级子域名4.2 配置二级子域名5. 使用固定二级子域名进行访问 1.测试环境 MavenSpring bootJdk 1.8 2.本地配置 获取支付宝支付Java SDK,…...

[EFI]Dell Latitude-7400电脑 Hackintosh 黑苹果efi引导文件
硬件型号驱动情况主板 Dell Latitude-7400 处理器Intel Core i7-8665U已驱动内存16GB DDR4 RAM已驱动硬盘Toshiba KIOXIA 512GB SSD已驱动显卡Intel UHD 620 Graphics已驱动声卡Realtek ALC256已驱动有线网卡 无 无无线网卡蓝牙Intel Wireless-AC 9560已驱动 支持系统版本 maco…...

用芯片SIC8833可开发电子秤方案
SIC8833作为一款高性能的电子秤方案芯片,这款芯片是一个带24bitADC的8位RISC MCU,内置8k16位OTP程序存储器。具体24位双向I/O口的特性,广泛应用于电子衡器和精密测量及控制系统,能满足用户的不同需求和应用场景。 以下是电子秤方案…...

【Qt-QFile-QDir】
Qt编程指南 ■ Stream■ QTextStream■ QDataStream ■ QDial■ QDir■ QFile■■ ■ Stream ■ QTextStream /* 获取文件的路径 */ QString fileName QFileDialog::getOpenFileName(this);/* 指向文件 */ file.setFileName(fileName);/* 判断文件是否存在 */ if (!file.exi…...

设计模式之-单列设计模式,5种单例设计模式使用场景以及它们的优缺点
系列文章目录 设计模式之-6大设计原则简单易懂的理解以及它们的适用场景和代码示列 设计模式之-单列设计模式,5种单例设计模式使用场景以及它们的优缺点 设计模式之-3种常见的工厂模式简单工厂模式、工厂方法模式和抽象工厂模式,每一种模式的概念、使用…...

Android 13 - Media框架(25)- OMXNodeInstance(二)
上一节我们了解了 OMXNodeInstance 的创建过程,以及 IOmx 服务和 OMXNodeInstance、OMX组件之间的联系。接下来我们将一起了解 ACodec 是如何通过 OMXNodeInstance 这个中间层进行端口定义设置,以及端口Buffer分配的。 OMXNodeInstance 的代码还是比较长…...

生物系统学中的进化树构建和分析R工具包V.PhyloMaker2的介绍和详细使用
V.PhyloMaker2是一个R语言的工具包,专门用于构建和分析生物系统学中的进化树(也称为系统发育树或phylogenetic tree)。以下是对V.PhyloMaker2的一些基本介绍和使用说明: 论文介绍:V.PhyloMaker2: An updated and enla…...

XStream 反序列化漏洞 CVE-2021-39144 已亲自复现
XStream 反序列化漏洞 CVE-2021-39144 已亲自复现 漏洞名称漏洞描述影响版本 漏洞复现环境搭建 修复建议总结 漏洞名称 漏洞描述 在Unmarshalling Time处包含用于重新创建前一对象的类型信息。XStream基于这些类型的信息创建新实例。攻击者可以控制输入流并替换或注入对象&am…...

深入剖析LinkedList:揭秘底层原理
文章目录 一、 概述LinkedList1.1 LinkedList简介1.2 LinkedList的优点和缺点 二、 LinkedList数据结构分析2.1 Node节点结构体解析2.2 LinkedList实现了双向链表的原因2.3 LinkedList如何实现了链表的基本操作(增删改查)2.4 LinkedList的遍历方式 三、 …...

计算机网络复习-OSI TCP/IP 物理层
我膨胀了,挂我啊~ 作者简介: 每年都吐槽吉师网安奇怪的课程安排、全校正经学网络安全不超20人情景以及割韭菜企业合作的FW,今年是第一年。。 TCP/IP模型 先做两道题: TCP/IP协议模型由高层到低层分为哪几层: 这题…...

虚拟机服务器中了lockbit2.0/3.0勒索病毒怎么处理,数据恢复应对步骤
网络技术的不断发展也为网络威胁带来了安全隐患,近期,对于许多大型企业来说,许多企业的虚拟机服务器系统遭到了lockbit2.0/3.0勒索病毒攻击,导致企业所有计算机系统瘫痪,无法正常工作,严重影响了企业的正常…...

【MATLAB】 RGB和YCbCr互转
前言 在视频、图像处理领域经常会遇到不同色域图像的转换,比如RGB、YUV、YCbCr色域间的转换,这里提供一组转换公式,供大家参考。 色彩模型 RGB RGB色彩模型是一种用于表示数字图像的颜色空间,其中"RGB"代表红色&…...

【线性代数】决定张成空间的最少向量线性无关吗?
答1: 是的,张成空间的最少向量是线性无关的。 在数学中,张成空间(span space)是一个向量空间,它由一组向量通过线性组合(即每个向量乘以一个标量)生成。如果这组向量是线性无关的&…...

暴力破解(Pikachu)
基于表单的暴力破解 先随便输入一下,然后抓包,进行字典爆破 验证码绕过(on server) server服务端要输入正确的验证码后进行爆破 之后的操作没什么不一样 验证码绕过(on client) 这个也需要输入验证码,但是后面进行字典爆破的时候…...

如何使用CMake查看opencv封装好的函数
当我们有时想查看opencv自带的函数的源代码,比如函数cvCreateImage, 此时我们选中cvCreateImage, 点击鼠标右键->转到定义,我们会很惊讶的发现为什么只看到了cvCreateImage的一个简单声明,而没有源代码呢?这是因为openCV将很多…...

微盛·企微管家:用户运营API集成,电商无代码解决方案
连接电商平台的新纪元:微盛企微管家 随着电子商务的蓬勃发展,电商平台的高效运营已经成为企业成功的关键。在这个新纪元里,微盛企微管家以其创新的无代码开发连接方案,成为企业之间连接电商平台的强大工具。它允许企业轻松集成电…...

Hive 部署
一、介绍 Apache Hive是一个分布式、容错的数据仓库系统,支持大规模的分析。Hive Metastore(HMS)提供了一个中央元数据存储库,可以轻松地进行分析,以做出明智的数据驱动决策,因此它是许多数据湖架构的关键组…...

CopyOnWriteArrayList源码阅读
1、构造方法 无参构造函数 //创建一个空数组,赋值给array引用 public CopyOnWriteArrayList() {setArray(new Object[0]); }//仅通过getArray / setArray访问的数组。 private transient volatile Object[] array;//设置数组 final void setArray(Object[] a) {arra…...

Windows操作系统:共享文件夹,防火墙的设置
1.共享文件夹 1.1 共享文件夹的优点 1.2 共享文件夹的优缺点 1.3 实例操作 编辑 2.防火墙设置 2.1 8080端口设置 3.思维导图 1.共享文件夹 1.1 共享文件夹的优点 优点 协作和团队合作:共享文件夹使多个用户能够在同一文件夹中协作和编辑文件。这促进了团…...

STM32独立看门狗
时钟频率 40KHZ 看门狗简介 STM32F10xxx 内置两个看门狗,提供了更高的安全性、时间的精确性和使用的灵活性。两个看 门狗设备 ( 独立看门狗和窗口看门狗 ) 可用来检测和解决由软件错误引起的故障;当计数器达到给 定的超时值时,触发一个中…...

财务数据智能化:用AI工具高效制作财务分析PPT报告
Step1: 文章内容提取 WPS AI 直接打开文件,在AI对话框里输入下面指令: 假设你是财务总监,公司考虑与茅台进行业务合作、投资或收购,请整合下面茅台2021年和2022年的财务报告信息。整理有关茅台财务状况和潜在投资回报的信息&…...

vue3中使用three.js记录
记录一下three.js配合vitevue3的使用。 安装three.js 使用npm安装: npm install --save three开始使用 1.定义一个div <template><div ref"threeContainer" class"w-full h-full"></div> </template>可以给这个di…...

MySQL——表的内外连接
目录 一.内连接 二.外连接 1.左外连接 2.右外连接 一.内连接 表的连接分为内连和外连 内连接实际上就是利用where子句对两种表形成的笛卡儿积进行筛选,我们前面学习的查询都是内连接,也是在开发过程中使用的最多的连接查询。 语法: s…...

基于IPP-FFT的线性调频Z(Chirp-Z,CZT)的C++类库封装并导出为dll(固定接口支持更新)
上一篇分析了三种不同导出C++类方法的优缺点,同时也讲了如何基于IPP库将FFT函数封装为C++类库,并导出为支持更新的dll库供他人调用。 在此基础上,结合前面的CZT的原理及代码实现,可以很容易将CZT变换也封装为C++类库并导出为dll,关于CZT的原理和实现,如有问题请参考: …...

【C语言】指针
基本概念 在C语言中,指针是一种非常重要的数据类型,它用于存储变量的内存地址。指针提供了对内存中数据的直接访问,使得在C语言中可以进行灵活的内存操作和数据传递。以下是关于C语言指针的一些基本概念: 1. 指针的声明ÿ…...

PostgreSql 索引使用技巧
索引种类详情可参考《PostgreSql 索引》 一、适合创建索引的场景 经常与其他表进行连接的表,在连接字段上应该建索引。经常出现在 WHERE 子句中的字段,特别是大表的字段,应该建索引。经常出现在 ORDER BY 子句中的字段,应该建索…...

【华为数据之道学习笔记】6-7打造业务自助分析的关键能力
华为公司将自助分析作为一种公共能力,在企业层面进行了统一构建。一方面,面向不同的消费用户提供了差异性的能力和工具支撑;另一方面,引入了“租户”概念,不同类型的用户可以在一定范围内分析数据、共享数据结果。 1. …...

K8S从harbor中拉取镜像的规则imagePullPolicy
1、参数 配制参数为:imagePullPolicy: 可以选择的值有:Always,IfNotPresent,Never 2、参数结果 如果pod的镜像拉取策略为imagePullPolicy: Always:当harbor不能运行后,pod会一直从harbor上拉…...

LeetCode刷题--- 优美的排列
个人主页:元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客 个人专栏 力扣递归算法题 http://t.csdnimg.cn/yUl2I 【C】 http://t.csdnimg.cn/6AbpV 数据结构与算法 http://t.cs…...