-
-
[原创]某讯QQ钱包转账付款算法分析篇
-
2021-10-31 08:48
41595
-
0x01 抓包
具体如何抓包就不在陈述,抓包我们会发现qpay_unified_config.cgi、qpay_balance.cgi等接口,但它的POST以及返回的数据都是十六进制的数据,毫无疑问是经过了加密,接下来就是分析如何找到它加密部分。(具体它的每个参数含义就暂时不讲解)
0x02 定位
把整个apk拖入jadx-gui后,初始化检索之后可以直接通过网址进行定位。
1 | ! str .contains( "qpay_ydt.cgi" ) && ! str .contains( "qpay_balance.cgi" ) && ! str .contains( "qpay_fpay_regbind.cgi" )
|
看到此处,它判断的这3个URL接口名称,恰是支付的,估计是它排除掉不是它的接口。
这里直接对它进行HOOK,我使用的是frida(雷电4模拟器环境),我们对这个signEncdata函数进行分析它具体的传参,根据代码过程大概判断出,str是URL,剩余2个,拭目以待。
1 2 3 4 5 6 7 8 9 | var EncryptRequest = Java.use( "com.tenpay.sdk.basebl.EncryptRequest" );
EncryptRequest[ 'signEncdata' ].implementation = function( str ,str2, map ){
console.log( '[signEncdata] --> str:' + str )
console.log( '[signEncdata] --> str2:' + str2)
console.log( map )
var ret = this.signEncdata( str ,str2, map )
console.log( "[signEncdata] ret -- >" + ret)
return ret
}
|
…………
结果就是,该HOOK代码并没有触发,也就是并没有执行到此处,但是我们发现当前class处几乎每个函数都是加密相关,我对他们进行了逐一HOOK,最终发现encypt函数有了反应。
如下面图所示:
但是代码没有能正常解析出来,具体它的参数打印出来的是:
1 2 3 4 5 6 7 8 9 10 11 12 13 | [encypt] - - > qquin: 276265453
[encypt] - - > url:https: / / myun.tenpay.com / cgi - bin / qwallet_misc / qpay_unified_config.cgi?ver = 2.0 &chv = 3
[encypt] - - > keyIndex: 8
[encypt] - - > postdata:skey = v0aaf855a20617de1e4cf7b447711a49&uin = 276265453 &skey_type = 0
[encypt] - - > pskey:D2bVXKh4GsqkNgFevM37b3Qf8h6yNyC4F4 - e937 - w58_
- - - - - - - -
[encypt] - - > qquin: 276265453
[encypt] - - > url:https: / / myun.tenpay.com / cgi - bin / clientv1. 0 / qpay_balance.cgi?ver = 2.0 &chv = 3
[encypt] - - > keyIndex: 8
[encypt] - - > postdata:token_id = 5M6477be1c9445ba1621c6319e403075 &p = 31363335363339383237F0D6C4CEE093903BFD05D6303A581B97E8442ABD7934E8CCC9D71AAF6B60BD5186B12729677B20A25232EDECB539B81775F69AD79B48AF90B0D96BD8AB1D18E3EEAC513F451010C782F3919935171EA650F937A4C733865ECE77E9B4352D6BE6360D50605106FD9581221F92A809A42FC8617884EEAD942F721C71A3A32B9A642F94676A49F7C74E45B0D7B62802809FDEE2EE68 ×tamp = 1635639827 &is_reentry = 0 &skey = v0aaf855a20617de1e4cf7b447711a49
[encypt] - - > pskey:D2bVXKh4GsqkNgFevM37b3Qf8h6yNyC4F4 - e937 - w58_
|
参数名我是根据查找调用它的上一层代码进行翻译出来的,如图片箭头所示
这些数据最长的就是postdata,也就是它发出去的数据的明文。
这个函数触发了2次,第一次估计是在支付之前获取某些信息,而第二次则是提交支付密码(参数p,也是加密后的)以及token_id(支付订单号)进行完成支付。
而skey估计则是cookie之类的关键token
0x03 枯燥无味的分析
找到了它的关键代码,就需要对它进行算法的还原,具体它postdata数据是如何构造的,它的每个参数含义就暂时不往回追溯,根据当前的encypt函数,继续往下阅读,但因为代码是无法正常翻译成java,只能通过阅读class的原生指令。
我们直接拉到最后,前面基本上都是构造它的数据以及获取更多的其它设备信息,因为我们看到了如 getNetworkType、getDeviceInfo、getMsgNo等关键字,估计是在进一步获取更多信息。
1 2 3 4 5 | L_0x01b4:
com.tenpay.ndk.DesEncUtil r0 = com.tenpay.sdk.basebl.EncryptRequest.des / / Catch:{ all - > 0x01cc }
java.lang.String r2 = r8.toString() / / Catch:{ all - > 0x01cc }
postdata2 = keyIndex
r0.encryptDes(postdata2, r2) / / Catch:{ all - > 0x01cc }
|
这里我直接拉倒最后(部分参数名被我改了一下),r2 就是它最终构造完的信息,postdata2其实是keyIndex(忘记改回名字),那么就可以继续跳转到 r0.encryptDes 里面下个hook查看它传进去的r2了。
1 2 3 4 5 6 7 8 9 | var DesEncUtil = Java.use( "com.tenpay.ndk.DesEncUtil" );
DesEncUtil[ 'encryptDes' ].overload( 'int' , 'java.lang.String' ).implementation = function(key,data){
var ret = this.encryptDes(key,data)
console.log( "[encryptDes] data --> " + data)
console.log( "[encryptDes] key --> " + key)
console.log( "[encryptDes] ret --> " + ret)
return ret
}
|
以下HOOK出来的打印数据:
1 2 3 4 5 6 7 8 9 | [encryptDes] data - - > pskey = D2bVXKh4GsqkNgFevM37b3Qf8h6yNyC4F4 - e937 - w58_&skey = v0aaf855a20617de1e4cf7b447711a49&uin = 276265453 &skey_type = 0 &h_net_type = WIFI&h_model = android_mqq&h_edition = 84 &h_location = 2F55FF2C8A2AA4435A87356EA9373F29 % 7C % 7CMI % 209 % 7C7 . 1.2 % 2Csdk25 % 7C3E2A52202DF351F3821979EE78BEB82C % 7CD41D8CD98F00B204E9800998ECF8427E % 7C1 % 7C &h_qq_guid = 3E2A52202DF351F3821979EE78BEB82C &h_qq_appid = 537100433 &h_exten =
[encryptDes] key - - > 8
[encryptDes] ret - - > true
- - - - - -
[encryptDes] data - - > pskey = D2bVXKh4GsqkNgFevM37b3Qf8h6yNyC4F4 - e937 - w58_&token_id = 5M460db95537d6b5adb2a100c1870f77 &p = 31363335363339383237F0D6C4CEE093903BFD05D6303A581B97E8442ABD7934E8CCC9D71AAF6B60BD5186B12729677B20A25232EDECB539B81775F69AD79B48AF90B0D96BD8AB1D18E3EEAC513F451010C782F3919935171EA650F937A4C733865ECE77E9B4352D6BE6360D50605106FD9581221F92A809A42FC8617884EEAD942F721C71A3A32B9A642F94676A49F7C74E45B0D7B62802809FDEE2EE68 ×tamp = 1635639827 &is_reentry = 0 &skey = v0aaf855a20617de1e4cf7b447711a49&h_net_type = WIFI&h_model = android_mqq&h_edition = 84 &h_location = 2F55FF2C8A2AA4435A87356EA9373F29 % 7C % 7CMI % 209 % 7C7 . 1.2 % 2Csdk25 % 7C3E2A52202DF351F3821979EE78BEB82C % 7CD41D8CD98F00B204E9800998ECF8427E % 7C1 % 7C &h_qq_guid = 3E2A52202DF351F3821979EE78BEB82C &h_qq_appid = 537100433 &h_exten =
[encryptDes] key - - > 8
[encryptDes] ret - - > true
|
我们看到,data数据其实就是上一个hook的数据拼接在一起,但是看到它后面多出了h_net_type和h_model等相关设备的状态信息,也就证实了刚刚说的那些关键字所获取出来的数据。 而这个key = 8 是比较迷惑的,我们现在把跟到encryptDes函数里面查看它具体进行的部分(因为我们看到它返回的是bool型,而不是最终的加密结果)
如上图所示,跟进去我们发现了它真正的加密过程是在so层中完成。
通过看到SoLoad.loadSo(context);部分,我们需要找到它加载的so文件
我们看到它加载的是cftutils_v1、qwssl两个文件。
但奈何查找整个apk资源,并没有发现2个so文件。
尝试全局检索他们的名字,也无结果
0x04 运气?
与其所是运气,也可以说是技巧,通过XSearch工具,我按照搜索qwssl名字,搜出了一张后缀名jpg图片格式的文件
但是它并不能正常打开查看,通过十六进制查看发现,它竟然是一个APK格式
把他转换成zip格式打开,发现了新大陆
进入lib发现了2个惊喜文件
最后我们使用ida载入这2个so,并看到了具体实现过程
而前面所说过的keyindex,其实就如箭头所示,unk_26004是一个存放加密公钥的常量,它应该是偏移了几位进行读取,以达到部分混淆作用把。
此处我没有继续往下分析了,因为基本上也差不多了明了了整个算法过程,只需要时间基本上都能完成了。(当然还有它那个密码p没去看)
[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。
最后于 2021-10-31 08:55
被kzzll编辑
,原因: