android逆向奇技淫巧二十五:x音另类抓包(十)

  低版本x音client会验证server的证书,所以把抓包软件的证书安装在android系统后就可以冒充server欺骗client抓包了;但是后来的高版本libttboringssl.so中调用了SSL_CTX_set_custom_verify来设置server对client的验证,这下抓包软件就抓瞎了,因为不知道client的公钥,怎么在server面前冒充client了?(当然也可以想办法在app中找到client的公钥或证书,然后导入charlse这种抓包软件,但肯定不轻松)看雪有大佬通过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一下,看看都发了啥数据!

   3、附上抓包要点的整理和总结如下:

     

 

 

 

参考:

1、http://www.zhuoyue360.com/crack/73.html  android硬核抓包

2、https://bbs.pediy.com/thread-268014.htm 非标准sslpinning抓包

3、https://github.com/google/ssl_logger  ssl_logger

4、https://github.com/BigFaceCat2017/frida_ssl_logger   frida_ssl_logger

5、https://github.com/r0ysue/r0capture

posted @ 2022-03-26 22:20  第七子007  阅读(3232)  评论(0编辑  收藏  举报