原文: iOS逆向-支付宝蚂蚁森林
前言
发现iOS支付宝逆向的分析并不多,蚂蚁森林基于H5应用 套着UIWebView 基本也没这类JS和原生交互分析的帖子,就拿此练手吧 作技术分享
去掉 ptrace 和 __RESTRICT section 两个保护
脱壳和dump头文件
分析和调试
编写Tweak
总结
环境
MacBook,iPhone6,iOS 9.3.3越狱, iOS支付宝10.1.5
工具
theos
Tweak制作工具
xcode
用lldb来附加调试
class-dump
导出头文件
dumpdecrypted
脱appstore的壳
iHex
二进制编辑器
去掉 __RESTRICT section 和 ptrace 两个保护
去掉 __RESTRICT section 步骤
ssh到手机 执行命令ps -e
找到/var/containers/Bundle/Application/DD6D8BA3-95F2-4C6D-BFD7-0E20420A6E9C/AlipayWallet.app/AlipayWallet
切换到mac电脑 使用scp命令 把执行文件拷到电脑上scp [email protected]:/var/containers/Bundle/Application/C6F2DD99-6450-4838-98B8-2899E8EBC1A4/AlipayWallet.app/AlipayWallet /Users/hack/Desktop/wz
打开iHex 查找command+f 把 __RESTRICT和__restrict替换为其他值(比如:__RRRRRRRR和__rrrrrrrr。保证长度不变就行啦)看图1
拷贝文件回去scp /Users/hack/Desktop/wz/AlipayWallet [email protected]:/var/containers/Bundle/Application/C6F2DD99-6450-4838-98B8-2899E8EBC1A4/AlipayWallet.app/AlipayWallet
如果打开闪退 请Cydia中安装 AppSync
去除ptrace保护
使用theos 创建Tweak 代码如下 安装到手机就可以
#import#importstaticint(*orig_ptrace) (intrequest, pid_t pid, caddr_t addr,intdata);staticintmy_ptrace (intrequest, pid_t pid, caddr_t addr,intdata){if(request ==31){NSLog(@"[AntiAntiDebug] - ptrace request is PT_DENY_ATTACH");return0; }returnorig_ptrace(request,pid,addr,data);}%ctor{ MSHookFunction((void*)MSFindSymbol(NULL,"_ptrace"),(void*)my_ptrace,(void**)&orig_ptrace);NSLog(@"[AntiAntiDebug] Module loaded!!!");}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
脱壳和dump头文件
执行命令cycript -p AlipayWallet注入支付宝进程
执行命令[[NSFileManager defaultManager]URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask][0]
看到返回/var/mobile/Containers/Data/Application/5E744C4A-5B05-4280-A21C-9E6EFE8EF51D/Documents/
执行命令cd /var/mobile/Containers/Data/Application/5E744C4A-5B05-4280-A21C-9E6EFE8EF51D/Documents/
把 dumpdecrypted.dylib拷贝进去scp /Users/cardlan/Downloads/dumpdecrypted-master/dumpdecrypted.dylib [email protected]:/var/mobile/Containers/Data/Application/5E744C4A-5B05-4280-A21C-9E6EFE8EF51D/Documents/
执行命令DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib /var/containers/Bundle/Application/DD6D8BA3-95F2-4C6D-BFD7-0E20420A6E9C/AlipayWallet.app/AlipayWallet
如果发现提示Killed: 9 切换su mobile再试 提示成功如下
DISCLAIMER: This toolisonly meantforsecurity research purposes,notforapplicationcrackers.[+] detected64bit ARM binaryinmemory.[+]offsettocryptid found: @0x100034d90(from0x100034000) = d90[+] Found encrypted dataataddress00004000oflength56786944bytes - type1.[+] Opening /private/var/containers/Bundle/Application/C6F2DD99-6450-4838-98B8-2899E8EBC1A4/AlipayWallet.app/AlipayWalletforreading.[+] Reading header[+] Detecting header type[+] Executableisa plain MACH-O image[+] Opening AlipayWallet.decryptedforwriting.[+] Copyingthenotencrypted startofthefile[+] Dumpingthedecrypted dataintothefile[+] Copyingthenotencrypted remainderofthefile[+] SettingtheLC_ENCRYPTION_INFO->cryptidto0atoffsetd90[+] Closing originalfile[+] Closing dumpfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
执行ls找到 AlipayWallet.decrypted scp拷贝到电脑就可以
接下来使用 class-dump 导出头文件到当前文件夹
class-dump -H AlipayWallet.decrypted -o ./
分析和调试
打开支付宝到蚂蚁森林界面 cycript -p AlipayWallet
我们从UI入手 执行[[UIApp keyWindow]recursiveDescription]看到
我们返回上面找控制器[#0x1386dc970 nextResponder]看到
我们去之前导出的头文件翻翻 找到这个控制器 看看有什么敏感的方法和属性,我们的目要找到关键的点击事件或者相关的逻辑
我们使用choose(H5WebViewController)拿到实例 测试一下头文件的方法看看
看了一大圈真没什么什么进展,url 无法直接打开的 再想想UIWebView和原生交互的方法,如下代码获取个Html源码来看看
类似于WebViewJavascriptBridge 咱们找下callback方法或者send方法试试
[#0x136a28400 lastMainRequest]" { URL: https://60000002.h5app.alipay.com/app/src/home.html?__webview_options__=transparentTitle%3Dauto%26canPullDown%3DNO}"[#0x136a28400 h5WebView]#">"[#0x136a28400 stringByEvaluatingJavaScriptFromString:"document.documentElement.innerHTML"]
1
2
3
4
5
6
7
8
我们来使用THEOS 自带的logify.pl 生成H5WebViewController的全部方法调用和参数日志
/opt/theos/bin/logify.plH5WebViewController.h>Tweak.xm
1
编译 Tweak.xm 注意:有编译出错 有些类和代理block是不认识的 出错的删掉就可以
CDUnknownBlockType 作参数的可以替换成 id 继承于 DTViewController 替换成
安装之后 手机连上我们电脑 打开xcode -菜单Window-Devices-Simulators
我们点击那个能量按钮收取别人的能量看看
接下来我们使用xcode 自带的lldb来调试支付宝(默认xcode无法调试第三方应用)我们接下来给app加个权限
没权限提示
Ensure“AlipayWallet”isnotalready running,andcardlan_yuhuajunhaspermissiontodebug it.
1
注意:要使用脱壳之后的AlipayWallet执行下面的命令,也就是AlipayWallet.decrypted 重命名为AlipayWallet(否则闪退)
将应用程序从设备上拷贝到本地利用 ldid 将应用程序的 code sign 导出:ldid-eAlipayWallet>>AlipayWallet.xml在 AlipayWallet.xml文件中添加 get-task-allow权限get-task-allow利用 ldid 对应用进行重签名 ldid-SAlipayWallet.xml./AlipayWallet将应用拷回设备,将设置可执行权限 chmod755AlipayWallet完成
1
2
3
4
5
6
7
8
(图3)
ps:如果如上操作还闪退 请重启iphone
然后我们连上手机 xcode 随便弄个工程 选择iphone 看图来开始附加AlipayWallet(图4) 点到森林界面点击xcode自带的看界面工具 然后再xcode 右下角自带的lldb 下断点
我们先试着下断函数
b-[H5WebViewController reportClickTime]
1
提示找不到no locations 我们随便找个地址 看看堆栈信息
po[H5WebViewController _shortMethodDescription]
1
找一个地址
b 0x1057daab0
1
重开页面 触发断点 ,点开xcode的堆栈窗口如(图5)
发现很多地址是没函数名的 当然也无法直接对函数名下断点 我们借用大神写的工具来恢复这些信息 文章链接如下
http://www.jianshu.com/p/967b6631756c
之后 再附加一下
下这个断点
b-[H5WebViewController reportClickTime]
1
发现是可以成功下断了 继续查看堆栈信息 现在函数名全部全了
(图6)
往上回溯 思路 因为我们想找到是js过来的参数和调用原生方法是哪里过来的
毕竟 reportClickTime 不是我们要分析的
看到PSDJsBride 调用了很多次 而且看参数和函数名 很有可能 中转了消息和参数 为了验证我们的猜想 ,我们使用logify.pl 来生成tweak 打印他全部的调用日志
安装到手机之后,我们取消断电 打开xcode的日志看打印 有没有敏感的信息调用
brdelete
1
点击好友的那个能量收取 把日志全部copy 到Sublime text
看图(7)
我们看到这个英文单词 收集的意思 我们猜想这个就是收集能量的命令,看参数体有friedID 确定性90%了
- 证实猜想 我们用cycript 来试试 拿到PSDJsBridge 的实例 可以用choose也可以用日志打印那个
[#0x14a0bed80 _doFlushMessageQueue:@"把参数拷贝过来(自己处理转义 里面有/"号)" url:@"第二个参数"];
提供下我日志的
_doFlushMessageQueue:[{"handlerName":"remoteLog","data":{"seedId":"ANTFOREST-BEHAVIOR-CLICK-COLLECT","param1":"shareBiz=none^bubbleId=26219984^actionUserId=2088322012980000^type=behavior^currentTimestamp=1510797618289","param2":"monitor_type=clicked^remoteType=info^pageName=home.html^pageState=friend2088322012980000_enterhomeOff","bizType":"antForest"},"callbackId":"remoteLog_15107976182890.5985529306344688"},{"handlerName":"rpc","data":{"operationType":"alipay.antmember.forest.h5.collectEnergy","requestData":[{"userId":2088322012987680,"bubbleIds":[26219984],"av":"5","ct":"ios"}],"disableLimitView":true},"callbackId":"rpc_15107976182910.0005478465463966131"}] url:https://60000002.h5app.alipay.com/app/src/home.html?userId=2088322012980000]
1
2
3
我们执行 看看日志打印 有日志输出 看recv这些参数 和刚刚是有点差别 但是大致一样(这个能量只能收一次)来看bubbleId 这个猜一下是能量的ID
来找怎么获取的能量ID 我们打开一个有能量的好友,看日志 有获取的调用没
由于日志比较多 我们就是着重找有返回bubbleId和friedID 的列表,找到之后往上找调用着参数
[m -[ transformResponseData:{ bizNo ="092c5f8c-ee77-49ad-a3a2-dd3c059ee579-1510798661871"; bubbles = ( { business = { bigIconDisplayName ="\U884c\U8d70"; bizType = xingzou; dayIconUrl ="https://zos.alipayobjects.com/rmsportal/xxx.png";id=9; nightIconUrl ="https://zos.alipayobjects.com/rmsportal/xxx.png"; smallIconDisplayName ="\U884c\U8d70"; }; collectStatus = AVAILABLE; fullEnergy =92;id=26343893; produceTime =1510788093000; remainEnergy =72; userId =208890214255xxxx; } ); needGuide =0;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
结果有了 找调用
_doFlushMessageQueue:[{"handlerName":"remoteLog","data":{"seedId":"ANTFOREST-PAGE-READY-home","param1":"shareBiz=none^type=behavior^currentTimestamp=1510798661779","param2":"monitor_type=openPage^remoteType=info","bizType":"antForest"},"callbackId":"remoteLog_15107986617880.34833956067450345"},{"handlerName":"getSystemInfo","data":{},"callbackId":"getSystemInfo_15107986617920.08668778464198112"},{"handlerName":"hideOptionMenu","data":{},"callbackId":"hideOptionMenu_15107986617920.562577509554103"},{"handlerName":"setToolbarMenu","data":{"menus":[],"override":true},"callbackId":"setToolbarMenu_15107986617920.03417412145063281"},{"handlerName":"setGestureBack","data":{"val":true},"callbackId":"setGestureBack_15107986617920.6238974309526384"},{"handlerName":"remoteLog","data":{"seedId":"ANTFOREST-H5_PAGE_SET_PAGE_NAME","param1":"shareBiz=none^type=behavior^currentTimestamp=1510798661796","param2":"monitor_type=clicked^remoteType=info^pageName=home.html","bizType":"antForest"},"callbackId":"remoteLog_15107986617960.3546153837814927"},{"handlerName":"addNotifyListener","data":{"name":"NEBULANOTIFY_AFRefresh"},"callbackId":"addNotifyListener_15107986617960.04098325059749186"},{"handlerName":"rpc","data":{"operationType":"alipay.antmember.forest.h5.queryNextAction","requestData":[{"userId":"208890214255xxxx","av":"5","ct":"ios"}],"disableLimitView":true},"callbackId":"rpc_15107986617970.8864813686814159"}]url:https://60000002.h5app.alipay.com/app/src/home.html?userId=208890214255xxxx]
1
2
用cycript 测试这个方法 。结果如我们所愿 日志返回了我们需要的能量ID和好友ID 可以构造第一个找到那个方法 实现收一个好友的能量
- 由于这个方法需要好友ID的参数 我们需要拿到全部好友的ID 这样才能实现我们的一键全部收取
- 这里我偷个懒 不去往下折腾 点击页面在PSDJsBridge transformResponseData
取top 10的好友ID(打开页面他本身执行了 friendRanking系列方法 )
编写Tweak
整理下流程,进页面 他自身调用Top10的好友列表 我们拿到这10个用户的UID
拿到UID之后执行上面分析的第二个方法 拿到指定用户可以收的能量 collectStatus=AVAILABLE 返回参数中有 我们看字段就能猜到
拿到UID和能量ID 我们就可以执行收能量的功能了
具体的代码不一一分析 下面会提供代码下载地址
看成功图8
总结
UIWebView的应用不同于原生的应用 有正向开发经验能通过UI层能轻易找到触发事件
其实很多时候都在试错和猜想 ,逐步的去验证自己的猜想 直到找到答案
这个Tweak只作技术分析,并没有完善 Top10用户收取还不够用 大家可以试着动手 获取下一页好友 和全部好友 看日志找到字段是可以轻易实现的 还有其他功能比如 每天定时启动收取啥的
这是代码GitHub地址
https://github.com/hackxhj/alipayForestTweak.git
欢迎star