爱伪装(AWZ)/爱立思(ALS)改机改串一键新机原理分析
爱伪装(AWZ)/爱立思(ALS)改机改串一键新机原理分析
简介
爱伪装(AWZ)/爱立思(ALS)是一款iOS越狱系统上的改机工具,可以修改多种系统参数达到伪装设备型号及各种软硬件属性的目的,同时提供了防越狱检测机制,常用于iOS上的推广刷量,配合代理/VPN使用。 除了AWZ以外,该软件商还有类似工具ALS/IGG/LRN/NZT/AXJ等,功能大体一致。AWZ/ALS支持iOS 7/8/9/10/11/12的全息备份,一键新机功能
AWZ伪装哪些参数?
- IDFA
- IDFV
- 用户名
- 系统版本
- 设备型号,固件版本
- User-Agent
- 移动网络运营商信息
- 地理位置
- uname / sysctl等参数
- WIFI SSID BSSID
- IMEI
- 序列号
- MAC地址
AWZ有哪些屏蔽刷机检测的手段?
- VPN隐藏
- 代理隐藏
- WIFI隐藏
- 反越狱检测,越狱文件检测/模块检测/APP检测等
AWZ的Cydia源
|
1
2
3
4
5
6
7
8
9
|
als.ucydia.com ALS 11.0.5awz.ucydia.com AWZ 10.0.5 awz.itouchgo.com AWZ 10.0.5apt.awzcn.com ALS 11.6.5 apt.awzcn.com AWZ 8.2.5 apt.awzcn.com AWZ 10.5.7 apt.abogeek.com ALS 11.6.5apt.abogeek.com AWZ 8.2.5 apt.abogeek.com AWZ 10.5.7 |
分析要点
这里只做学习讨论改机原理及ALS自身反逆向机制。ALS安装后,有如下文件:
- /Applications/ALS.app 主程序,用于生成改机参数,参数保存在
- /Library/LaunchDaemons/dhpdaemon.plist 用于daemon方式执行DHPDaemon,用于帮助ALS实现一些隐藏操作
- /usr/bin/DHPDaemon
- /MobileSubstrate/DynamicLibraries/ALS.{dylib,plist},该tweak通过hook一些可以获取系统属性和app属
性的C函数和ObjC函数实现的修改app参数
第一阶段
- ALS属性2755防止加载tweak
- 存在restrict段,防止被加载tweak
-
存在syscall函数进行ptrace系统调用,存在ptrace函数,以及svc汇编指令实现的ptrace,防止调试器附加
-
对于restrict段和汇编指令反调试的处理,解法就是patch文件然后重签名,但是要写一个通用的命令是比较困
难的,慢慢收集吧,这里提供的方式如下:
sed -i 's/RESTRICT/RXSTRICT/g' ALS
sed -i 's/\x80\x52\x01\x10\x00\xd4/\x80\x52\x1f\x20\x03\xd5/g' ALS - 对于文件属性和函数级的反调试,方法不再赘述,写tweak即可
第二阶段
在第一阶段破解后,仍然是闪退的,此时反调试已经去除,所以可以加调试器看检测点
- -[NSBundle executablePath]检测主进程文件是否被修改
- _dyld_get_image_name检测tweak模块
- sysctl检测进程的p_flag是否有调试器flag
- isatty检测终端
- ioctl(TIOCGWINSZ)检测终端
- fopen检测主进程文件是否被修改(在DHPDaemon中)
- posix_spawn检测/Application/ALS.app下是否存在超过3M的文件(补丁啊)
注意
在IFMagicMainVC的几个按钮handler函数中,存在大量检测代码,这里建议直接自己实现,例如:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
void (*old_IFMagicMainVC_doGoMagicSetting)(Class cls, SEL sel, void* click);void new_IFMagicMainVC_doGoMagicSetting(Class cls, SEL sel, void* click) { UIStoryboard* board = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; UIViewController* newcontrol = [board instantiateViewControllerWithIdentifier:@"IFMagicSettingVC"]; UIViewController* control = (UIViewController*)cls; [[control navigationController] pushViewController:newcontrol animated:YES];}void (*old_IFMagicMainVC_paramSettingClick)(Class cls, SEL sel, void* click);void new_IFMagicMainVC_paramSettingClick(Class cls, SEL sel, void* click) { UIStoryboard* board = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; UIViewController* newcontrol = [board instantiateViewControllerWithIdentifier:@"IFMagicDeviceSettingVC"]; UIViewController* control = (UIViewController*)cls; [[control navigationController] pushViewController:newcontrol animated:YES];}void (*old_IFMagicMainVC_appListClick)(Class cls, SEL sel, void* click);void new_IFMagicMainVC_appListClick(Class cls, SEL sel, void* click) { UIStoryboard* board = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; UIViewController* newcontrol = [board instantiateViewControllerWithIdentifier:@"IFApplicationSelectorVC"]; UIViewController* control = (UIViewController*)cls; [[control navigationController] pushViewController:newcontrol animated:YES];} |
第三阶段
在第二阶段后,不闪退了,但是显示注册码过期
- 解密栈字符串,ALS和DHPDaemon几乎全用的栈字符串混淆,蛮体力活的
- 定位到注册的函数,一方面通过socket通信,用加密本机信息获取软件激活状态,另一方面通过cjson反序列化回写注册状态。
- 定位get_json_value函数,该函数为c层cjson解析函数,用于从json数据的到key对应的value,该函数刚好位于socket网络通信,解密响应得到json数据后。其中必要重要的key有:ps,vs,hs,ts,as,aes
- 还原ObjC函数调用关系
- system函数会独立向服务器验证激活码,如果校验不过会删除backup文件,这样操作记录都没了,但是并没实际删除,可通过fopen检测绕过
- 控制软件注册状态的主要字段是status和expiry_date,分别对应注册状态及过期时间字符串,其中status字段含义为:
enum {
};123456STATE_LOCKED=0,//已锁定STATE_NORMAL=1,//已激活STATE_INACTIVE1=2,//未激活STATE_OUTDATE=3,//激活码过期STATE_INACTIVE2=4,//未激活STATE_LOGOFF=5,//已注销 - 对于栈字符串的处理,见:https://github.com/lich4/personal_script/blob/master/IDA_Script/
parse_stack_string.py - 对于ObjC函数调用关系还原,见:https://github.com/lich4/personal_script/blob/master/IDA_Script/
add_xref_for_macho.py - 使用网络请求方式更新注册状态的响应中,get_json_value获取的as键对应status,aes键对应于expiry_date
另外一些字段用于激活码验证,如果不通过则结束进程,可以自行在newAppEnvClick函数中研究。 - 使用cjson反序列化回写注册状态逻辑存在于文件/private/var/mobile/Library/Preferences/
com.app1e.mobile.ifalscommon.plist,解密后仍然是json数据,要修改的字段如下:
{
}1234"authInfo": {"status": @0,"expiry_date": @"21000101080000000"} - mapapi.bundle模块存在一些干扰,在hook函数的时候要注意
第四阶段
a. 捕获按钮触发的功能函数
b. 分析ALS和DHPDaemon的notify通信,有些重要函数是ALS调用DHPDaemon执行
a. 捕获按钮触发,利用frida脚本,https://github.com/lich4/personal_script/blob/master/
Frida_script/utils.js,这里tranverse_view用于检测当前呈现的界面可以获取的元素,以及对应的响应
selector,如果找按钮的回调,又不想触发,可以用这个。另外更通用的得是trace_view函数,可以拦截到
所有界面消息以及响应selector,在执行点击等操作后可以得到更全的信息
b. 下面是一些分析结果:
清理safari逻辑在函数中-[IFMagicMainVC cleanSafariClick:]
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
// 杀死进程BKSTerminateApplicationForReasonAndReportWithDescription(__bridge CFStringRef)@"com.apple.mobilesafari", 5, 0, NULL);NSFileManager* man = [NSFileManager defaultManager];// 清理cookieNSString cookiepath = @"/var/mobile/Library/Cookies";if ([man fileExistsAtPath:cookiepath]) { NSString* cmd = [NSString stringWithFormat:@"rm -rf %@/*", cookiepath]; system(cmd);}cookiepath = @"/private/var/root/Library/Cookies";if ([man fileExistsAtPath:cookiepath]) { NSString* cmd = [NSString stringWithFormat:@"rm -rf %@/*", cookiepath]; system(cmd);}// 获取safari的沙盒路径NSString* safaricontainer = nil;NSString* installplist = @"/var/mobile/Library/Caches/com.apple.mobile.installation.plist";if ([man fileExistsAtPath:]) { NSDictionary* plist = [NSDictionary dictionaryWithContentsOfFile:installplist]; id obj = plist[@"User"][@"com.apple.mobilesafari"]; if (obj == nil) { obj = plist[@"System"][@"com.apple.mobilesafari"]; } if (obj != nil) { safaricontainer = obj[@"Container"]; }} else { Class* LSApplicationProxy = NSClassFromString(@"LSApplicationProxy"); id obj = [LSApplicationProxy performSelector:applicationProxyForIdentifier: withObject:@"com.apple.mobilesafari"]); if (obj != nil && [obj respondsToSelector:@selector(dataContainerURL)]) { safaricontainer = [[obj performSelector:@selector(dataContainerURL)] path]; }}// 清理libraryNSString* libpath = [safaricontainer stringByAppendingPathComponent:@"Library"];NSString* libcachepath = [libpath stringByAppendingPathComponent:@"Caches"];if ([man fileExistsAtPath:libcachepath]) { NSString* cmd = [NSString stringWithFormat:@"rm -rf %@/*", libcachepath]; |
|
1
|
清理keychain逻辑在函数中-[IFMagicMainVC cleanKeychainClick:] |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
NSFileManager* man = [NSFileManager defaultManager];if ([man fileExistsAtPath:@"/var/Keychains/keychain-2.db"]) {system("cp /var/Keychains/keychain-2.db /tmp/");void* ppDb = 0;char cmd[256];if (0 == sqlite3_open("/tmp/keychain-2.db", &ppDb)) { strcpy(cmd, "DELETE FROM cert WHERE agrp<>'apple' and agrp not like '%apple%' and agrp <> 'ichat' and agrp <>'lockdown-identities'"); sqlite3_exec(ppDb, cmd, 0, 0, 0); strcpy(cmd, "DELETE FROM keys WHERE agrp<>'apple' and agrp not like '%apple%' and agrp <> 'ichat' and agrp <>'lockdown-identities'"); sqlite3_exec(ppDb, cmd, 0, 0, 0); strcpy(cmd, "DELETE FROM inet WHERE agrp<>'apple' and agrp not like '%apple%' and agrp <> 'ichat' and agrp <>'lockdown-identities'"); sqlite3_exec(ppDb, cmd, 0, 0, 0); system("cp /tmp/keychain-2.* /var/Keychains/");} |
|
1
|
清理pasteboard逻辑在函数中-[IFMagicMainVC cleanPastboardClick:] |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
UIPasteboard* pb = [UIPasteboard generalPasteboard];if (pb != nil) { NSArray* items = [pb items]; if (items != nil) { [items removeAllObjects]; } [pb setItems:items];}NSFileManager* man = [NSFileManager defaultManager];NSProcessInfo* proc = [NSProcessInfo processInfo];BOOL isbe8 = FALSE;NSOperatingSystemVersion ver;ver.majorVersion = 8;ver.minorVersion = 0;ver.patchVersion = 0;if ([proc respondsToSelector:@selector(isOperatingSystemAtLeastVersion:&ver)]) { isbe8 = [proc isOperatingSystemAtLeastVersion:&ver];}NSString* pbplist = nil;NSString* pbbundle = nil;if ([man fileExistsAtPath:@"/System/Library/LaunchDaemons/com.apple.UIKit.pasteboardd.plist"]) { pbplist = @"/System/Library/LaunchDaemons/com.apple.UIKit.pasteboardd.plist"; pbbundle = @"com.apple.UIKit.pasteboardd";}else if ([man fileExistsAtPath:@"/Library/LaunchDaemons/com.apple.UIKit.pasteboardd.plist"]) { pbplist = @"/Library/LaunchDaemons/com.apple.UIKit.pasteboardd.plist"; pbbundle = @"com.apple.UIKit.pasteboardd";}else if ([man fileExistsAtPath:@"/System/Library/LaunchDaemons/com.apple.pasteboard.pasted.plist"]) { pbplist = @"/System/Library/LaunchDaemons/com.apple.pasteboard.pasted.plist"; pbbundle = @"com.apple.pasteboard.pasted";}BOOL pbdbexist = [man fileExistsAtPath:@"/var/mobile/Library/Caches/com.apple.UIKit.pboard/pasteboardDB"];NSString* pbcontainer = nil;if ([man fileExistsAtPath:@"/var/mobile/Library/Caches/com.apple.UIKit.pboard"]) { pbcontainer = @"/var/mobile/Library/Caches/com.apple.UIKit.pboard";} else if ([man fileExistsAtPath:@"/var/mobile/Library/Caches/com.apple.Pasteboard"]) { pbcontainer = @"/var/mobile/Library/Caches/com.apple.Pasteboard";}if (!isbe8 && [man fileExistsAtPath:pbplist]) { system("launchctl unload -w");}if (pbcontainer != nil && [man fileExistsAtPath:pbcontainer]) { NSString* cmd = [NSString stringWithFormat:@"rm -rf %@/*", pbcontainer]; system([cmd UTF8String]);}if (pbdbexist) { NSString* cmd = [NSString stringWithFormat:@"cp %@ %@", @"/Applications/ALS.app/pb.dat", @"/var/mobile/Library/Caches/com.apple.UIKit.pboard/pasteboardDB"]; system([cmd UTF8String]);} |
思考
改机原理是什么?
在iOS上目前所有流行的改机工具,本质上是利用substrate框架对某些用来获取设备和系统参数函数进行hook,从而欺骗App达到修改的目的,具体的如下:
- 用作获取设备参数的函数,无论是C函数,还是Objective-C/Swift函数,可以使用hook框架来修改其返回值
- 屏蔽VPN/HTTP代理检测
- 屏蔽越狱检测
一键新机怎么实现的?
在用户进行一键新机时,ALS有如下操作:
- 生成设备参数并保存到文件
|
1
2
3
|
/private/var/mobile/Library/Preferences/com.app1e.mobile.ifalscommon.plist 保存伪造设备参数数据com.app1e.mobile.ifalslocation.plist 保存伪造位置数据 |
-
将应用沙盒目录下的数据备份,同时为新环境创建沙盒目录结构
备份的数据存放在/private/var/mobile/alsdata下 -
应用启动后,ALS.dylib会hook关键函数,并根据plist文件修改函数返回的数据
在这一步,ALS还会根据情况清理keychain,同时做简单的反越狱检测
我该如何保护自己的App,防止被改机软件篡改设备信息?
可以将一些重要检测使用汇编指令(SVC)代替函数调用完成,同时使用内存校验来检测汇编代码是否被篡改。这样AWZ这种通用工具便无能为力,因为hook框架只能做到函数级别的hook。
ALS都修改了哪些参数
那么怎么确定ALS是否修改了参数,以及改了哪些参数呢?可以自行开发模块来进行检测,这里提供我开发好的checktweak工具(链接: https://pan.baidu.com/s/1vGB-rENpz0ift03QT0QQww 提取码: 4qm8),在下载安装过checktweak.deb之后,可以在任意App运行后,在App界面上,以三根手指长按屏幕即可出现弹窗。使用ALS之前,先进行一次检测,一键新机以后,再进行一次检测。
checktweak可以用来对比使用ALS前和使用ALS后,App中检测到的参数变化;当然如果你不用ALS的话,也可以用来获取机器本身的参数了。此外该工具可以看到ALS已经设置好的参数和历史参数信息。
通过分析ALS.dylib可以发现其hook了如下函数(注意这里只做技术探讨,不要用于商业用途):
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
sysctl 修改设备名,设备型号,iOS版本等sysctlbyname 修改设备名,设备型号,iOS版本等uname 修改设备名,设备型号,iOS版本等SCNetworkReachabilityGetFlags 修改网络类型,WIFI/2G/3G/4GCNCopySupportedInterfaces 修改WIFI名和BSSIDCNCopyCurrentNetworkInfo 修改WIFI名和BSSIDIORegistryEntrySearchCFProperty 修改设备串号,IMEIIORegistryEntryCreateCFProperty 修改设备串号,IMEI_CTServerConnectionCopyMobileIdentity 修改IMEIUIDevice 修改设备型号,iOS版本,设备名,IDFVASIdentifierManager 修改IDFACTCarrier 修改运营商信息,包括运营商名,MCC,MNC,ICCCTTelephonyNetworkInfo 修改运营商信息,包括运营商名,MCC,MNC,ICCNSProcessInfo 修改设备名,iOS版本NSBundle 修改App版本号CLLocationManager 修改位置参数,包括经纬度,海拔,速度等MKUserLocation 修改位置参数,包括经纬度,海拔,速度等ChromeViewController 修改位置参数,包括经纬度,海拔,速度等MapsMainModeController 修改位置参数,包括经纬度,海拔,速度等LocationShare 修改位置参数,包括经纬度,海拔,速度等SAKCLLocationDelegateProxy 修改位置参数,包括经纬度,海拔,速度等MOARegionalMonitoring 修改位置参数,包括经纬度,海拔,速度等CLLocationManagerBlocks 修改位置参数,包括经纬度,海拔,速度等QMLocationManager 修改位置参数,包括经纬度,海拔,速度等TDLocationInfo 修改位置参数,包括经纬度,海拔,速度等TaxiCancelOrderViewController 修改位置参数,包括经纬度,海拔,速度等ONELocationStore 修改位置参数,包括经纬度,海拔,速度等NSKVONotifying_ONELocationStore 修改位置参数,包括经纬度,海拔,速度等NSKVONotifying_MomoLocationManager 修改位置参数,包括经纬度,海拔,速度等AMapDiscoverManager 修改位置参数,包括经纬度,海拔,速度等MMUDeviceObserver 修改位置参数,包括经纬度,海拔,速度等MMUJSLocation 修改位置参数,包括经纬度,海拔,速度等MomoLocationManager 修改位置参数,包括经纬度,海拔,速度等IphoneGPSMan 修改位置参数,包括经纬度,海拔,速度等YiXinLocationManager 修改位置参数,包括经纬度,海拔,速度等CFNetworkCopySystemProxySettings 屏蔽代理检测NEVPNConnection 屏蔽VPN检测UIStatusBarIndicatorItemView 屏蔽VPN检测AppsFlyerUtils 屏蔽越狱检测CLSAnalyticsMetadataController 屏蔽越狱检测WXOMTAEnv 屏蔽越狱检测lstat 屏蔽越狱检测stat 屏蔽越狱检测access 屏蔽越狱检测fopen 屏蔽越狱检测NSFileManager 屏蔽越狱检测 |
如果细心对比的话,可以发现ALS的一个漏洞是未设置sysctlbyname的机器名,结果sysctl和sysctlbyname结果不一致,以下是个检测样本:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
以下是一个检测报告的样本:检测时间:2019-11-26 22:11:30检测报告文件保存路径:/var/mobile/Containers/Data/Application/4930BF56-D0BE-43CE-80F1-406427F716C9/Documents/checkparam_2019-11-26 22:11:30.txt----------------基本信息----------------应用标识符:com.apple.mobilesafari应用路径:/Applications/MobileSafari.app数据路径:/var/mobile/Containers/Data/Application/4930BF56-D0BE-43CE-80F1-406427F716C9IDFA:685385AB-DFBB-4784-B69D-BB43CDF51944IDFV:99CF0686-7CF3-451E-8477-C26E94C87CEC----------------设备信息----------------UIDevice设备类型:iPhoneUIDevice设备类型(方式2):iPhone。。。。----------------iTunes信息--------------------------------SIM卡信息----------------SIM卡运营商:中国联通SIM卡VOIP:允许----------------区域信息----------------区域信息国家代码:US区域信息活跃输入法0:en-US。。。。----------------存储信息----------------存储信息分区剩余空间(Byte):8758792192存储信息分区空间(Byte):12040671232----------------音频信息----------------音频信息输入延迟:0.5音频信息缓冲区持续时间:0.5音频信息输出延迟:0.5音频信息输出0:Speaker音频信息音量:0.5----------------网络信息----------------网关en0.2:192.168.1.1DNS服务器:2408:8888::8,2408:8899::8,192.168.1.1,MAC地址ap1:020000000000MAC地址en0:020000000000MAC地址awdl0:020000000000MAC地址en1:020000000000UserAgent:Mozilla/5.0 (iPhone; CPU iPhone OS 10_2 like Mac OS X) AppleWebKit/602.3.12 (KHTML, like Gecko) Mobile/14C92。。。。----------------代理/VPN----------------VPN:未开启iPhone7:/var/mobile/Containers/Data/Application/4930BF56-D0BE-43CE-80F1-406427F7 |




浙公网安备 33010602011771号