android逆向奇技淫巧二十五:x音另类抓包(十)
作者:互联网
低版本x音client会验证server的证书,所以把抓包软件的证书安装在android系统后就可以冒充server欺骗client抓包了;但是后来的高版本libttboringssl.so中调用了SSL_CTX_set_custom_verify来设置server对client的验证,这下抓包软件就抓瞎了,因为不知道client的公钥,怎么在server面前冒充client了(当然也可以想办法在app中找到client的公钥,但肯定不轻松)?看雪有大佬通过hook或者直接硬编码把SSL_CTX_set_custom_verify第二个参数改为0,不让server验证client;同时把第三个参数回调函数的返回值也改成0来绕开验证的逻辑,这是非常好的一种思路!我这里根据tls原理、libssl.so或libttboringssl.so源码从其他角度抓包!
1、(1)刚打开x音时,client和server要互相验明正身,由于抓包软件没有client的公钥,是没法在server面前冒充client的,这里只能罢了!client和server通信首要就是通过handshake协商出双方对称加密的key,后续一律通过对称加密通信!所以handshake阶段最重要的就是这个key了!有了这个key,后续一切通信的数据不就能解开了么?这个key怎么获取了?参考我上一篇文章https://www.cnblogs.com/theseventhson/p/16051195.html 介绍的frida脚本如下:
function startTLSKeyLogger(SSL_CTX_new, SSL_CTX_set_keylog_callback) { function keyLogger(ssl, line) { console.log(new NativePointer(line).readCString()); } const keyLogCallback = new NativeCallback(keyLogger, 'void', ['pointer', 'pointer']); Interceptor.attach(SSL_CTX_new, { onLeave: function(retval) { const ssl = new NativePointer(retval); const SSL_CTX_set_keylog_callbackFn = new NativeFunction(SSL_CTX_set_keylog_callback, 'void', ['pointer', 'pointer']); SSL_CTX_set_keylog_callbackFn(ssl, keyLogCallback); } }); } startTLSKeyLogger( Module.findExportByName('libssl.so', 'SSL_CTX_new'), Module.findExportByName('libssl.so', 'SSL_CTX_set_keylog_callback') )
我hook的结果是抓了50多个key,部分key如下:
CLIENT_RANDOM 05c10ca9bfb225e24e5fc03fa6dfef05292ce52dab90edd39b20124fd9ce64db 8ea99a617f99e59e1676f26e7266725227d8305fbb48dd36fe91e0e9133b7de4efb28edeacd52d5a06a69d41c4c0d232 CLIENT_RANDOM 75c5363ef6dd0e8ed769bb61dd3b6440d01eb1aa61b52e57be780446281e7b5d c72ba6c9fe54f643d79e330fc83130cd24f88151ea5f2ca9462577cd2585d1a72edd3132010f0bcf675f1aaf9ac5bf94 CLIENT_RANDOM a67744ecb0afb61a70b41604e7e711f56042af51266e02d79d73f28563b2de84 1ab1a45ed5407307e545a47a88a39f2c533c3e5d4effcccdf4a72b12927442fe1b6feaa38063e7ada28a9ca2553e026c CLIENT_RANDOM 64c75bc38d438669602690369f696df57349e66d5ee784a5e22b66b37f91bff6 f7008fac9daa766edcb636be5ebccc0c3dc3d73c119f39c38f9a7e89d54a508a9da9d96ea30d5b2745639ae275eb8d79 CLIENT_RANDOM b87890d1c9e17f6741985f7e42f96569df9a1153a19c215b107c19a088bec98d 4edec3ac50b381f67125da34014408b70b4c75973db6421fca3e0fde20c859187360e2c68b5efba493ea4efab78fe062 CLIENT_RANDOM bbebae8d1ec928eb785281d56baa50b38f41dd0e6848304af06f4227d1dd1f1c 33c9be07cfd31dbe6a77dfa2f89845f8cf72d1afa196d53bfc9c4bab3757b3b187f631d592e4ac7ec01fb29b87c2a1f5 CLIENT_RANDOM a0a78b7b2a4c100337cd6af637bf40af079ec6bd4f922a3bb1ffbf9f5f4e403b 5c9dd3e00dcac4838fefc498d29d445831e0b92af4480e0dc0815d85552c9f5452bef423a5481252d95066104ef597b1 CLIENT_RANDOM c4ec4ee28686ceb7d68c0c3c1262993848271f809ab3e0824043814e9fa49683 97f2510382981b51bdc3458bda2a0df1994039648ab5b6db9e3d131898952b0574d6772c90461161cc758453ab8e4e10 CLIENT_RANDOM 03572dfae7a9c42406b00d3a17443ed085b42f6094081b6ae951ae5f5a466019 84f2b4c3b08cb3b9335ba087e334fb92f01f0310a9547d220f18604e8e3b3d3780c62d1126ad586707f5fd5e3b6a03e2 CLIENT_RANDOM a190584887037a9deef70ee793ddc8876e379aa196d5c0298216370811c74824 91e661957726600703b2ac7580e0cdce16c135f8627fc1665fa436c4a2a7ea81a10ae55e9754fbc1896e90d4971ba050 CLIENT_RANDOM f379c78bd8efb66d6774cbafa7f7e4953c3d699c24a1cda0e2cfa72e122f9a67 22169928550d7daaf002eeb317de3aae816b2e1dc1b91839fe69f2385e367e39d8723548cfb1dada605c8e6a3d4f6fed CLIENT_RANDOM 132fd7ae00fa066077d8d99306ee4f248391567fe0cb1edc8859308074361101 4017bee954c38afc02437b472e5c6a175043696cb9587f1680aff3a0c0d5d8d4ec178171e095d4f615a273a445020e0c
key倒是有了,client和server之间通信的数据又怎么获取了?
(2)前面提到了普通的抓包软件抓不了包,是因为client和server在应用层互相验证身份,导致charles、fiddler、burpsuit等应用层的抓包软件立马抓瞎!换个角度想:所有的数据肯定都是要通过网卡、wifi等硬件设备发送出去的,既然在应用层又验证无法抓包,为啥不通过“降维打击”的思路在更底层的传输层、ip等、链路层等抓包了?这里就要用到wireshark了!由于wireshark是底层抓包,所以完全不用给操作系统装任何证书,走的也不是“中间人攻击”的方式抓包,而是直接简单粗暴地从网卡拿frame数据!现在网络通信的数据确实拿到了,但应用层是加密的,这时就用到了上面的sslkey了!在wireshark的编辑->首选项->protocols->tls这里把上面hook得到的sslkey上传,如下:
我这里的抓包结果(我用的是雷电模拟器):这个貌似是个图片
这里不知道又从x音的cdn拿了啥:
2、上述抓包需要先得到key,再用wireshark解析,各种操作较多,使用并不方便,还有另一种抓包方法:通信双方不是用tls协议通信么?既然数据是key加密的,在加密前hook总可以吧? 顺着这个思路去hook SSL_write(往外发送数据的)函数和SSL_read(接收外面数据)函数,还真能得到加密前的数据,我hook SSL_write部分结果如下:
(1) 这时get方法:
(2)这是post方法:
可以看出确实都是明文的!hook的脚本如下:
function SSL_hook() { if (!Java.available) { console.error('Java API not available'); return; } Java.perform(function () { console.log('hooked'); var SSL_write, SSL_read; //可查找当前已加载的共享库的导入与导出方法 const apiResolver = new ApiResolver('module'); apiResolver.enumerateMatches('exports:*lib*ssl*!SSL_*').forEach(function (v) { if (v.name.indexOf('SSL_write') > 0) { SSL_write = v.address; console.log('SSL_write:',SSL_write); } else if (v.name.indexOf('SSL_read') > 0) { SSL_read = v.address; console.log('SSL_read:',SSL_read); } }); if (SSL_write) { Interceptor.attach(SSL_write, { onEnter: function (args) { this.ssl = args[0].toString(); this.buf = ptr(args[1]); }, onLeave: function (retval) { const len = retval.toInt32(); if (len > 0) { console.log('SSL_write\n', this.buf.readByteArray(len), '\n', '*'.repeat(120)); //send({code: 100,ssl: this.ssl}, this.buf.readByteArray(len)); // send({ // code: 100, // ssl: this.ssl // }, Memory.readByteArray(this.buf, len)); } } }); } if (SSL_read) { Interceptor.attach(SSL_read, { onEnter: function (args) { this.ssl = args[0].toString(); this.buf = ptr(args[1]); }, onLeave: function (retval) { const len = retval.toInt32(); if (len > 0) { //console.log('SSL_read\n', this.buf.readByteArray(len), '\n', '*'.repeat(120)); //send({code: 200,ssl: this.ssl}, this.buf.readByteArray(len)); // send({ // code: 200, // ssl: this.ssl // }, Memory.readByteArray(this.buf, len)); } } }); } }); } function main() { SSL_hook(); } setImmediate(main);
注意:x音不同的版本貌似调用了libssl.so和libttboringssl.so,建议这两个不同的so都hook一下,看看都发了啥数据!
参考:
1、http://www.zhuoyue360.com/crack/73.html android硬核抓包
2、https://bbs.pediy.com/thread-268014.htm 非标准sslpinning抓包
标签:ssl,RANDOM,SSL,client,android,CLIENT,抓包,奇技淫巧 来源: https://www.cnblogs.com/theseventhson/p/16059276.html