本文摘自《九阴真经:iOS黑客攻防秘籍》
当今移动互联网时代,平台中的App为了增加人气会搞一些优惠活动,但实际上,App的激活、注册、优惠活动都有可能被“刷”。刷量团队利用的方法就是修改手机的信息,让应用获取假的数据,认为老用户就是新的用户。苹果对隐私的保护使开发者不能获取UDID作为设备的唯一标识,所以识别设备的唯一标识的方法都是使用IDFA、OpenUDID以及其他的第三方平台提供的ID,而IDFA是可以在系统设置中进行重置的,OpenUDID和其他第三方平台提供的ID也都有可能被重置。如图9-1所示,是某第三方应用安装统计平台,通过重置ID和修改手机信息进行作弊,实现了刷量的目的,实际是一台手机,统计新增却是11台。
由于将ID存放在沙盒中,应用被卸载掉,沙盒目录的数据会被清空,所以有些应用会将ID信息存在Keychain中,这样即使应用被卸载,数据也不会被清除,但在越狱环境下,Keychain也是可以清除的。
9.1 越狱环境下获取root权限
事实上,在已经越狱的iOS设备上使用Xcode编写的程序也是以mobile用户的身份运行,没有root权限,所以无法访问一些重要的文件和目录。
iOS的应用安装目录有两个,一个是/private/var/mobile/Containers/Bundle/Application,另一个是/Applications,前者存放使用Xcode安装或者在App Store上下载安装的应用,后者一般为系统自带的应用。如果想让应用以root身份运行,可以按以下的步骤来操作。
(1) 在应用的main函数添加如下代码:
setuid(0);
setgid(0);
(2) 将生成的应用上传到/Applications/yourApp.app。
(3) 这时桌面上没有图标,需要登录SSH运行uicache命令,该命令经常用于修复没有图标的问题。
(4) 执行chown root yourApp,更改所有者为root。
(5) 切换到应用的目录,运行chmod u+s yourApp。
这时在手机上点击你的应用,通过ps aux命令查看进程运行的用户就是root,而不是mobile。
(6) 以上方法在iOS 8系统没有问题,如果是iOS 9和iOS 10就得再多一个步骤,由于iOS 9安全限制,不允许具有root权限的应用启动,启动之后,你会发现马上就退出了。可以给原来应用的可执行文件改名,比如改为yourApp_,然后再新建一个yourApp名称的脚本并使用chmod的755命令设置可执行权限。脚本内容如下:
#!/bin/bash
root=$(dirname "$0")
exec "${root}"/yourApp_
相当于yourApp脚本执行之后,会执行yourApp_,这样就正常可以启动了。
9.2 修改手机信息
通过修改手机的信息可以“刷”应用的安装量,可以修改的信息有很多种,其中常见的有UDID、序列号、MAC地址、蓝牙地址、系统版本、机器名称、IDFA、IDFV、SSID、BSSID、DeviceToken、位置信息等。
9.2.1 修改基本信息
修改硬件信息常用的方法是hook MGCopyAnswer,在第6章讲解MSHookFuncation时提到,可以通过hook MGCopyAnswer实现修改本机的序列号。本节有两个目标,一是修改硬件相关的信息,二是修改系统版本、机型、IDFA、IDFV。
首先使用Theos新建工程,命令和参数如下:
exchen$ export THEOS=/opt/theos
exchen$ /opt/theos/bin/nic.pl
NIC 2.0 - New Instance Creator
------------------------------
......
[11.] iphone/tweak
Choose a Template (required): 11
Project Name (required): ChangeiPhoneInfo
Package Name [com.yourcompany.changeiphoneinfo]: net.exchen.ChangeiPhoneInfo
Author/Maintainer Name [boot]: exchen
[iphone/tweak] MobileSubstrate Bundle filter [com.apple.springboard]: com.apple.Preferences
[iphone/tweak] List of applications to terminate upon installation (space-separated, '-' for none) [SpringBoard]: Preferences
Instantiating iphone/tweak in changeiphoneinfo/...
然后编写如下代码:
#include <substrate.h>
#import <sys/utsname.h>
static CFTypeRef (*orig_MGCopyAnswer)(CFStringRef str);
static CFTypeRef (orig_MGCopyAnswer_internal)(CFStringRef str, uint32_t outTypeCode);
static int (*orig_uname)(struct utsname *);
CFTypeRef new_MGCopyAnswer(CFStringRef str);
CFTypeRef new_MGCopyAnswer_internal(CFStringRef str, uint32_t* outTypeCode);
int new_uname(struct utsname *systemInfo);
int new_uname(struct utsname * systemInfo){
NSLog(@"new_uname");
int nRet = orig_uname(systemInfo);
char str_machine_name[100] = "iPhone8,1";
strcpy(systemInfo->machine,str_machine_name);
return nRet;
}
CFTypeRef new_MGCopyAnswer(CFStringRef str){
//NSLog(@"new_MGCopyAnswer");
//NSLog(@"str: %@",str);
NSString *keyStr = (__bridge NSString *)str;
if ([keyStr isEqualToString:@"UniqueDeviceID"] ) {
NSString *strUDID = @"57359dc2fa451304bd9f94f590d02068d563d283";
return (CFTypeRef)strUDID;
}
else if ([keyStr isEqualToString:@"SerialNumber"] ) {
NSString *strSerialNumber = @"DNPJD17NDTTP";
return (CFTypeRef)strSerialNumber;
}
else if ([keyStr isEqualToString:@"WifiAddress"] ) {
NSString *strWifiAddress = @"98:FE:94:1F:30:0A";
return (CFTypeRef)strWifiAddress;
}
else if ([keyStr isEqualToString:@"BluetoothAddress"] ) {
NSString *strBlueAddress = @"98:FE:94:1F:30:0A";
return (CFTypeRef)strBlueAddress;
}
else if([keyStr isEqualToString:@"ProductVersion"]) {
NSString *strProductVersion = @"10.3.3";
return (CFTypeRef)strProductVersion;
}
else if([keyStr isEqualToString:@"UserAssignedDeviceName"]) {
NSString *strUserAssignedDeviceName = @"exchen's iPhone";
return (CFTypeRef)strUserAssignedDeviceName;
}
return orig_MGCopyAnswer(str);
}
CFTypeRef new_MGCopyAnswer_internal(CFStringRef str, uint32_t* outTypeCode) {
//NSLog(@"new_MGCopyAnswer_internal");
//NSLog(@"str: %@",str);
NSString *keyStr = (__bridge NSString *)str;
if ([keyStr isEqualToString:@"UniqueDeviceID"] ) {
NSString *strUDID = @"57359dc2fa451304bd9f94f590d02068d563d283";
return (CFTypeRef)strUDID;
}
else if ([keyStr isEqualToString:@"SerialNumber"] ) {
NSString *strSerialNumber = @"DNPJD17NDTTP";
return (CFTypeRef)strSerialNumber;
}
else if ([keyStr isEqualToString:@"WifiAddress"] ) {
NSString *strWifiAddress = @"98:FE:94:1F:30:0A";
return (CFTypeRef)strWifiAddress;
}
else if ([keyStr isEqualToString:@"BluetoothAddress"] ) {
NSString *strBlueAddress = @"98:FE:94:1F:30:0A";
return (CFTypeRef)strBlueAddress;
}
else if([keyStr isEqualToString:@"ProductVersion"]) {
NSString *strProductVersion = @"10.3.3";
return (CFTypeRef)strProductVersion;
}
else if([keyStr isEqualToString:@"UserAssignedDeviceName"]) {
NSString *strUserAssignedDeviceName = @"exchen's iPhone";
return (CFTypeRef)strUserAssignedDeviceName;
}
return orig_MGCopyAnswer_internal(str, outTypeCode);
}
void hook_uname(){
NSLog(@"hook_uname");
char str_libsystem_c[100] = {0};
strcpy(str_libsystem_c, "/usr/lib/libsystem_c.dylib");
void *h = dlopen(str_libsystem_c, RTLD_GLOBAL);
if(h != 0){
MSImageRef ref = MSGetImageByName(str_libsystem_c);
void * unameFn = MSFindSymbol(ref, "_uname");
NSLog(@"unameFn");
MSHookFunction(unameFn, (void *) new_uname, (void **)& orig_uname);
}
else {
strcpy(str_libsystem_c, "/usr/lib/system/libsystem_c.dylib");
h = dlopen(str_libsystem_c, RTLD_GLOBAL);
if(h != 0){
MSImageRef ref = MSGetImageByName(str_libsystem_c);
void * unameFn = MSFindSymbol(ref, "_uname");
NSLog(@"unameFn");
MSHookFunction(unameFn, (void *) new_uname, (void **)& orig_uname);
}
else {
NSLog(@"%s dlopen error", str_libsystem_c);
}
}
}
void hookMGCopyAnswer(){
char *dylib_path = (char*)"/usr/lib/libMobileGestalt.dylib";
void *h = dlopen(dylib_path, RTLD_GLOBAL);
if (h != 0) {
MSImageRef ref = MSGetImageByName([strDylibFile UTF8String]);
void * MGCopyAnswerFn = MSFindSymbol(ref, "_MGCopyAnswer");
//64位特征码
uint8_t MGCopyAnswer_arm64_impl[8] = {0x01, 0x00, 0x80, 0xd2, 0x01, 0x00, 0x00, 0x14};
//10.3特征码
uint8_t MGCopyAnswer_armv7_10_3_3_impl[5] = {0x21, 0x00, 0xf0, 0x00, 0xb8};
//处理64位系统
if (memcmp(MGCopyAnswerFn, MGCopyAnswer_arm64_impl, 8) == 0) {
MSHookFunction((void*)((uint8_t*)MGCopyAnswerFn + 8), (void*)new_MGCopyAnswer_internal,
(void**)&orig_MGCopyAnswer_internal);
}
//处理32位10.3到10.3.3系统
else if(memcmp(MGCopyAnswerFn, MGCopyAnswer_armv7_10_3_3_impl, 5) == 0){
MSHookFunction((void*)((uint8_t*)MGCopyAnswerFn + 6), (void*)new_MGCopyAnswer_internal,
(void**)&orig_MGCopyAnswer_internal);
}
else{
MSHookFunction(MGCopyAnswerFn, (void *) new_MGCopyAnswer, (void **)&orig_MGCopyAnswer);
}
}
}
%hook ASIdentifierManager
//IDFA
-(NSUUID*)advertisingIdentifier{
NSUUID *uuid = [[NSUUID alloc] init];
return uuid;
}
%end
%hook UIDevice