本文仅限学习交流,请勿用于非法以及商业用途,由于时间和水平有限,文中错漏之处在所难免,敬请各位大佬多多批评指正。
目录:一、线上买菜场景简述二、风控在业务中的应用三、产品整体框架四、初始化分析五、反爬签名流程六、设备指纹分析七、算法还原八、总结
1. 产品基本信息产品名称:ppp买菜(匿称);产品版本:5.25.0;Slogan:30分钟送达,新鲜送得快;所处行业:生鲜电商;2. 设备环境机型:iPhone 7;系统:IOS 13.4;工具: IDA7.6 Frida;
一次完整的线上买菜过程都经过了哪些环节呢?大致流程是从供应商送货到仓或到店,再由零售商售卖,最终到用户手里,这样便完成了一次买菜,如图1-1所示:
图1-1
上图的业务流程从供应商送货到仓或到店,再由零售商售卖,最终可以多种方式到用户手里,完成了一次买菜的过程。
还记得在2020年的下半年时候,当时生鲜电商的社区团购大战非常火爆,各种买菜APP蜂拥而入,砸钱、抢流量,你争我抢玩得不亦乐乎。
不夸张地说,我记得当时最常见的情形是,你随便在小区溜达一圈,就能碰见穿着各种颜色制服的地推工作人员,追赶着小哥哥小姐姐下载APP给送福利,下载完APP后注册登录APP买菜。
烧大把的钱把流量吸引过来,这个过程中会有黑灰产人员通过非法的技术手段,伪造新增用户并从中获利的行为,如果只是把流量吸引过来不考虑质量的话,会增加大量的企业无效成本。怎么识别出有效的流量与虚假流量,需要一个完善的风控体系与制定有效的策略找出高质量流量,然后把这些流量留下来。
接下来为了提高用户的购买频率,实现反复转化,就出现了各种红包、优惠券活动吸引用户提高打开APP频率与购买频率。这个环节中就会有各种薅羊毛的人群出现,同样需要完善的风控体系与制定有效的策略来最大程度地甄别风险。
活动流程大致如图2-1所示:
图2-1
图3-1
图3-2
下面将围绕框架进行详细分析与算法还原。
在正式进行代码分析之前还是很必要交代一下我分析过程中发现的代码混淆,方便后继分析代码做准备。
__text:0000000103743850 FF C3 00 D1 SUB SP, SP, #0x30 ; '0'
原理是通过动态赋值给x30实现跳转,X30链接寄存器(LR),用于保存子程序的返回地址。通篇都是这样的代码混淆方式,基本不怎么影响分析或脚本直接清除。
动态调试时如图4-1所示:
图4-1
字符串加解密:
__text:000000010180C0C0 DecStrng_sub_1036F80C0
通用的解密字符串,同样可以用脚本跑一遍就可以解密出来。
__text:0000000101864F1C
只要在上面地方下好断点就能分析对应的采集设备信息的方法。
获取APP Bundle ID:
com.baobaoaichi.imaicai
解析Info.plist读取ss中的值:
885B25AAFD830249B81AF699187E5752
解密常量字符串:
WU@TEN
组合字符串,BundleID+常量字符串(WU@TEN)+Info.plist中的ss值:
com.baobaoaichi.imaicaiWU@TEN885B25AAFD830249B81AF699187E5752
计算组合后字符的hmac值:
__text:0000000101B834F8 hamc_256_loc_1052974F8
计算后的值:
39 D3 B7 71 76 74 09 F5 E7 4F 4B 57 9B 86 8A 5C 01 92 13 18 61 C1 79 1C
转换成字符串:
39d3b771767409f5e74f4b579b868a5c0192131861c1791c833b5c95e99c412b
读取mt_security
__text:0000000101B202CC Read_mt_security_loc_1052342CC
读取后数据(部分):
0000000104899800 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 .PNG........IHDR
解析图片:
__int64 __fastcall sub_1036B61F0(
解析返回PIC数据(部分):
00000001050CDA00 2E 50 49 43 90 01 00 00 10 02 00 00 AC 02 00 00 .PIC............
将上面计算得到的hmac值转换成16进制:
39d3b771767409f5e74f4b579b868a5c0192131861c1791c833b5c95e99c412b
转换后:
0000000281E86790 39 D3 B7 71 76 74 09 F5 E7 4F 4B 57 9B 86 8A 5C
取转换后的的前0x10字节生成最终的AES KEY
__text:0000000101B2170C GenAesKey_loc_102FD570C
生成后的AESKEY:
38 90 B6 70 76 74 00 C0 E6 4E 4A 02 98 80 8A 1C
AES解密PIC数据:
__text:0000000101B1FA10 82 07 00 94 BL DecPic_loc_105235818 ; x0:第1个指针pic数据,X1:key
解密后数据(部分)
0000000104486020 78 9C 45 92 6D 6F DA 30 14 85 FF 4B B4 7D DA 44 x.E.mo.....K.}..
解压缩AES解密后数据得到明文,后面的加解密数据都会使用里面数据做为KEY:
{"a1":1,"a10":400,"a2":"com.baobaoaichi.imaicai","a11":"0a76d34357f7c7859c1a3fd25516b4e4021ec931fd56b6a36ebf73e5aa34c406","a3":"b9eb65dbc4c7109259edc07826390faf3bd09e3920d66580b04a0853d3ee172b","a4":5230,"k0":{"k1":"meituan1sankuai0","k2":"meituan0sankuai1","k3":"$MXMYBS@HelloPay","k4":"Maoyan010iauknaS","k5":"34281a9dw2i701d4","k6":"X%rj@KiuU+|xY}?f"},"a5":"5.23.0","a0":"sdk9xWZTg5V9nKAxVFB5mB1ipZIJGmYSysreJ1f/rlvXJ7Ydxd3hJRdWb4QdZKr/","a6":"HdPfNPzY9GK6wzp0lEgaMaX06uEMke8y0H3eD0l4RapMpRmVaOWzyQkHMmOavR47","a7":"1yuZHjO43la6rhDXzMkjGiseg9yoRxxDtzwourYASiiAp4Yl0TUGvOiN4UcoJ6pQ","c0":{"c1":true,"c2":false},"a9":"gC4xYEhYfboH/8kOYsdIcbyYRTKfrVgmHLb3x8uNBag=","a8":1627281940842}
解析上面的json数据获取对应的key:
__text:0000000101AFF460 04 8E 00 94 BL getPICkey_sub_102CF2C70 ; x0:返回pic json中的key
App防爬主要通过对App客户端发起的请求进行签名。然后将签名与业务数据请求发送到服务器端,服务端WAF应用服务器收到的请求后,通过解析签名串进行风险识别、拦截恶意请求,通过校验App请求签名,识别App业务中的风险、拦截恶意请求,实现App防护的目的。
如图5-1所示,请求头中携带的签名信息:
图5-1
识别异常爬虫:
App签名异常:对使用未携带签名或签名非法的App访问防爬防护目标的请求进行检测和拦截。
设备特征异常:检测设备的异常特征,是否使用模拟器、使用代理、Root设备、HOOK框架等。
基于以上逻辑,所以app会检测客户端环境。
检测越狱、hook框架等, 风险特征:
{
检测后结果:
{
压缩后json
0000000107C97080 78 9C 6D 90 4D 0E 84 30 08 85 EF C2 DA 69 A0 4A x.m.M..0.....i.J
组合加密密钥:
1635653901 6d1efb41-1bb2-4db1-88ee-b89d21d06e5f //当前时间加获取appkey(ak:info.plist)
加密:
__text:00000001052F9848 Rc4Enc_sub_10310D848
RC4加密后:
0000000107C97080 25 F2 BB 8E 4A F9 CA 7C F7 9A 5F 7D CD 38 67 69 %.............gi
Base64加密:
JfK7jkr5ynz3ml99zThnaS5L743O5fhYgFXWDl6zy2oq2xSv1zXcpe0CqFjp2Kso5D/1COlqjX9Rx7MmT40OQrIc2cpvc8gGaA9kMNErfgB2oSWrauzR/meaKYKkRDEuFAuW5jGBHzTycaqGYKjAzj0WI4Nhp8Bq5qAqoHprH0KQMMxZXwN/7US2vDa1DJedgqDk5qvCyE4p91XKh9CaH45tV1IAaL8fYtjdZ+Q=
判断本地是否有存储,如果有优先读取本地,如果本地没有存储就生成一个,详细逻辑在设备指纹一节中再细说。
__text:00000001052E2E58 E0 7B 7B A9 LDP X0, X30, [SP,#-0x50]
如果是第一次运行APP或本地没有存储时就本地生成XID:
-[SAKGuardDeviceFingerprint generateLocalXID]
本地存储获取到的xid:
mw0bruZSgWId6ew08pp0a3d2Vpfq1fcZfyJrTVmk89oqGNr5754r2zbh6YfpvQ4CijQe+0LfaB+WbyR9njkTQ8iCiFQzqg8rh18j7EntWdk=
同样也是如果也是判断本地是否有存储,如果有优先读取本地,如果本地没有存储就生成一个
+[SAKGuardDeviceFingerprint getFingerprintID]
如果是第一次运行APP或本地没有存储时就本地生成dfpid:
+[SAKGuardLocalIDKeychainStorage generateLocalID]
本地存储获取到的dfpid:
dad72f7de813ef8dfd0bbd58f3a775dacf5121ec1a2552173a0e314b
{
压缩json
0000000283F81500 78 9C AB 56 32 50 B2 32 D2 51 32 54 B2 8A 56 D2 x..V2P.2..2T..V.
解密解析pic获取加密key(k6)
X%rj@KiuU+|xY}?f
计算压缩后数据的crc值:
__text:00000001052CC27C E0 7B 7B A9 LDP X0, X30, [SP,#-0x50]
计算后得到:
73bbf8c5
取PIC中获到的值(k6)后8字节与crc值组合做为AES KEY:
000000016B707800 55 2B 7C 78 59 7D 3F 66 00 00 00 00 00 00 00 00 U+|xY}?f
AES加密压缩后的数据:
IV 0102030405060708
加密后:
0000000280D20750 F7 7A 2E 6A 76 E2 C9 B4 70 F0 62 3B 07 62 91 D7 ....v...p....b..
base64加密与crc值组合:
73bbf8c593ouanbiybRw8GI7B2KR179Y36ZpGtEOD95qhzQAuGKu2soVnxJif9J7sNG8+ulF //前8字节为上面计算的crc值
第一次组合签名json,还差计算a2值:
{
获取请求体,与上面组合的json签名拼接在一起计算签名
__text:00000001052DC014 E0 7B 7B A9 LDP X0, X30, [SP,#-0x50]
组合后的请求体
00000001080B5600 50 4F 53 54 20 2F 61 70 70 75 70 64 61 74 65 2F POST /appupdate/
解密PIC获取a0值
sdk9xWZTg5V9nKAxVFB5mB1ipZIJGmYSysreJ1f/rlvXJ7Ydxd3hJRdWb4QdZKr/
解密a0
key appkey:6d1efb41-1bb2-4db1-88ee-b89d21d06e5f
解密后a0
0000000280A08750 7C 55 57 4A 14 0E 42 69 67 06 3B 06 4A 49 07 0C |UWJ..Big.;.JI..
再次解密a0分为两组
000000016D10C530 20 09 0B 16 48 52 1E 35 3B 5A 66666. ...HR.5;Z
将第二次解密后的a0值其中一组与请求体组合
00000001089E5A00 4A 63 61 7C 22 38 74 5F 51 30 0D 30 7C 7F 31 3A Jca|"8t_Q0.0|.1:
计算hmac值
__text:00000001052EEEDC hmac_256_sub_105182EDC
计算后的值
000000016B7078B0 D8 49 F8 46 FA 3A 4C 93 AC 68 76 4E 15 11 6C E2
计算后hmac值与解密后的a0其中一组组合
000000016B707850 20 09 0B 16 48
再次计算hmac值
000000016B707A50 58 26 1C D2 C2 35 BC D4 CE 83 F3 AF E0 BA 76 8C
加密计算的hmac值得到最终的签名值
__text:0000000105353BC4 loc_105353BC4
加密后
0000000280190580 7F 26 8F D8 7F 5D 01 F3 2D D5 C7 E0 86 84 87 8E
转换成字符串
7f268fd87f5d01f32dd5c7e08684878e
组合成最终的签名
{
整个请求体的签名结束,然后发起网络请求。
应用启动后会生成两个ID,一个是XID,一个是DFPID,如图6-1、6-2所示:
图6-1
图6-1
获取设备信息:
__text:00000001010F3234 getmDeviceInfo_loc_104F0F234
将每一个获取到的信息组合单个json值,格式如下
{"value":"E68684F0-7573-4EBC-99BD-A03D58888888","code":1} //获取的IDFA
获取本地XID,如果是第一次或本地没有存储就本地生成一个:
-[SAKGuardDeviceFingerprint generateLocalXID]
检测风险工具:
/Library/MobileSubstrate/DynamicLibraries/AXJ.plist
检测代码
__text:00000001010FAACC E0 7B 7B A9 LDP X0, X30, [SP,#-0x50]
转换成最终的json格式
{
压缩json文数据,压缩后(部分)
0000000104AF3800 78 9C 6D 56 CB 6E 2B C7 11 FD 15 82 AB 3C 44 BA x.mV..+......<D.
解密PIC获取key(k1)
meituan1sankuai0
AES加密压缩后数据
_text:00000001010BEA08 E0 7B 7B A9 LDP X0, X30, [SP,#-0x50]
AES加密后(部分)
0000000104AC3200 2C 36 30 1D 89 2C 90 E9 7E F7 1C DB 62 73 D7 5E ,60..,.......s..
base64加密
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
组合请求体
{
计算签名,签名过程与上面分析的流程是一样的。
+[SAKGuardCommon sign:attachSiua:]
dfpid逻辑差不多,这里就不分析了。
设备指纹相关用到的算法有AES、压缩、RC4、hmac、base64。
RC4:
#include <stdio.h>
AES
/*******************
测试解密设备指纹请求体
//解密fingerPrintData
解密出来的数据与上面分析的组合设备指纹json是一样的,解密成功,如图7-1所示:
图7-1
我从分析的角度说下自己的看法,不对的地方还请指正,抗分析能力一般,代码混淆规律性很强,字符串加密方法用的一个容易被一次性还原。获取设备信息过于频繁,影响性能,回到开始说的风控在业务中的作用,大部分用户使用生鲜类APP时的目的性比较强,业务在拉新促活增加用户粘性的同时高质量留存与业务安全更是重中之重,所以产品流畅的用户体验是促进高留存的重要条件之一。
还有一些隐藏的彩蛋比较有意思,感兴趣的可以去自行分析。
样本太大,获取方式,关注公众号,公众号输入框回复“mc” 获取下载链接。
作者简介:
我是小三,目前从事软件安全相关工作,虽己工作多年,但内心依然有着执着的追求,信奉终身成长,不定义自己,热爱技术但不拘泥于技术,爱好分享,喜欢读书和乐于结交朋友,欢迎加我微信与我交朋友(公众号输入框回复“wx”即可)