最近一段时间有需求要抓取移动端通过SSL加密的HTTPS和WebSocket流量,使用WireShark中Lua插件进行私有协议的解包和统计.网上找到的大多是解密浏览器SSL流量的教程,通过设置环境变量的方式导出浏览器握手过程中产生的ClientRandom和(Pre)MasterKey到Wireshark来解密,针对移动端的SSL流量解密少有涉及,通过对一些工具及OpenSSL的了解,有几种方法可以做到在移动端解密通过SSL传输的流量,在这里记录下来。
搭建代理服务器,在移动端信任伪服务器即代理的证书。代理作为伪客户端与服务器握手,作为伪服务器与客户端握手,同时需要解决Sni等问题,其中原理在MitmProxy的文档中有详细的说明,见
https://docs.mitmproxy.org/stable/concepts-howmitmproxyworks/
,其实大多移动端可用的抓包工具例如Fiddler、Charles等都是采用这种方式实现的。这种方式可用的工具有SSLsplit、MitmProxy。这两个工具都可以将代理与真实服务器、代理与客户端握手过程中的MasteerKey导出到文件,实现与浏览器流量相同的解密方式。其中MitmProxy地址:https://mitmproxy.org/
,SSLsplit地址:https://www.roe.ch/SSLsplit
.针对自己的移动端App,可以在客户端调用OpenSSL握手的过程中使用如下代码将ClientRandom和MasterKey导出:
/* * Generates a NSS key log format compatible string containing the client
* random and the master key, intended to be used to decrypt externally
* captured network traffic using tools like Wireshark.
*
* Only supports the CLIENT_RANDOM method (SSL 3.0 - TLS 1.2).
*
* https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format
*/
char* ssl_ssl_masterkey_to_str(SSL *ssl)
{
char *str = NULL;
int rv;
unsigned char *k, *r;
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
unsigned char kbuf[48], rbuf[32];
k = &kbuf[0];
r = &rbuf[0];
SSL_SESSION_get_master_key(SSL_get0_session(ssl), k, sizeof(kbuf));
SSL_get_client_random(ssl, r, sizeof(rbuf));
#else /* OPENSSL_VERSION_NUMBER < 0x10100000L */
k = ssl->session->master_key;
r = ssl->s3->client_random;
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
rv = asprintf(&str,
"CLIENT_RANDOM "
"%02X%02X%02X%02X%02X%02X%02X%02X"
"%02X%02X%02X%02X%02X%02X%02X%02X"
"%02X%02X%02X%02X%02X%02X%02X%02X"
"%02X%02X%02X%02X%02X%02X%02X%02X"
" "
"%02X%02X%02X%02X%02X%02X%02X%02X"
"%02X%02X%02X%02X%02X%02X%02X%02X"
"%02X%02X%02X%02X%02X%02X%02X%02X"
"%02X%02X%02X%02X%02X%02X%02X%02X"
"%02X%02X%02X%02X%02X%02X%02X%02X"
"%02X%02X%02X%02X%02X%02X%02X%02X"
"\n",
r[ 0], r[ 1], r[ 2], r[ 3], r[ 4], r[ 5], r[ 6], r[ 7],
r[ 8], r[ 9], r[10], r[11], r[12], r[13], r[14], r[15],
r[16], r[17], r[18], r[19], r[20], r[21], r[22], r[23],
r[24], r[25], r[26], r[27], r[28], r[29], r[30], r[31],
k[ 0], k[ 1], k[ 2], k[ 3], k[ 4], k[ 5], k[ 6], k[ 7],
k[ 8], k[ 9], k[10], k[11], k[12], k[13], k[14], k[15],
k[16], k[17], k[18], k[19], k[20], k[21], k[22], k[23],
k[24], k[25], k[26], k[27], k[28], k[29], k[30], k[31],
k[32], k[33], k[34], k[35], k[36], k[37], k[38], k[39],
k[40], k[41], k[42], k[43], k[44], k[45], k[46], k[47]);
return (rv < 0) ? NULL : str;
}
保存为文件在WireShark设置SSLKEYLOGFILE即可。