Fanxs's Blog

安卓测试之Hook SSL_read和SSL_write

字数统计: 1.1k阅读时长: 4 min
2019/12/16 Share

前言:
这两周在群里和朋友们水群时,看到群友转发了一句别人的话:安卓APP抓双向认证的包釜底抽薪的一招就是hook ssl_read和ssl_write两个方法,完全不用配置代理,不用管APP客户端和服务端的证书校验问题。屡试不爽~~,感觉挺有趣的,就研究了一下,这篇文章只是一时兴起。

0x00. 思路

这里的思路是,hook SSL_readSSL_write,就可以绕过APP客户端的证书校验等问题,直接窥探到HTTPS里的请求内容和响应。

先了解一下这两个函数是干嘛的。根据SSL_read的介绍,SSL_read用于从已经建立的SSL session中读取数据,放入到缓冲区中。而SSL_write则是相反,在建立的SSL Session中写入数据到缓冲区,发送到远程服务器。

显而易见,在进行HTTPS数据传输时,就是SSL_read和SSL_write两个函数负责TLS/SSL数据的读写。SSL_read负责读取远程服务器发来的数据,而SSL_write则负责写入主机要发送的数据到缓冲区,发送到远程服务器。同时,这里读写的数据都是明文的,在调用SSL_read和SSL_write的前后会通过其他函数(猜测是crypto_set_ex_data)来进行SSL的加解密。

在hook这两个函数,在安卓系统里用frida自然是最方便的。基本思路就形成了。当APP使用了双向证书校验,或者对服务器证书进行了校验而且APP加了壳不好hook校验函数,或者其他非HTTP应用层协议的SSL通道的情况下,这个方法能帮助揭露、修改和拦截通信数据。

0x01. Hook脚本

这个要写脚本,凭我对Frida模块的认识,应该要写好一会儿。幸好,在Github上就有人写了在Linux上用Frida hook SSL_read和SSL_write的脚本:ssl_logger.py

这个脚本中,hook的脚本有几个主要函数,分别是initializeGlobalsgetPortsAndAddressesgetSslSessionId和拦截调用的Interceptor

initializeGlobals


这个函数主要用于定位到脚本中所需要的几个API,来在下文进行Hook或调用:SSL_read,SSL_write,SSL_get_fd,SSL_get_session,SSL_SESSION_get_id,getpeername, getsockname, ntohs, ntohl

这里利用Frida提供的ApiResolver, 选择type为module,可以枚举当前进程中已经加载的动态链接库的所有导出和导入函数。在安卓中,SSL_read和SSL_write函数位于libssl.so,而其他使用到几个函数,都在libc.so中。

ApiResolver的enumerateMatches函数可以在枚举时,返回匹配到query语句的函数的地址。因为我对这个ApiResolver这玩意儿不大熟,就多看了一下。网上没看到query语句的语法规则,这个脚本中针对libssl的SSL_read函数作的query是“exports:libssl!SSL_read”,猜测一下语法规则这里exports指向导出函数,用!符号分割模块和指定的API,*模糊匹配。在枚举到SSL_read时,完整的name值是/system/lib/libssl.so!SSL_read,所以才需要*作模糊匹配。

这个函数就是找到API的地址嘛,我寻思着用Module.enumerateExports也可以,写脚本试了一下,果然其实效果是一样的:

其他函数

getPortsAndAddresses函数用getpeername和getsockname两个API来获取socket的相关ip和port值。这两个API会将这些结构体struct sockaddr 的方式写入分配的内存地址addr中。根据结构体sockaddr的定义,增加指针偏移来读取到内存数据再使用ntohs, ntohl来转换为可读的端口号和IP地址。getSslSessionId函数用于获取的进行中的SSL的sessionId。

最后用Interceptor来拦截SSL_read和SSL_write,在进入这两个函数时记录他们的数据,以pcap的方式保存下来,就妥了。

0x02. 执行和效果

这个脚本有一些语法错误,我做了修正。而且pcap文件虽然方便,但我还是想要更直观的方法来观察来往的请求包,所以我写入了一个log函数,以html的形式输出请求和响应。

以每日生鲜APP为例。在脚本开头写入要hook的app package名,以及pcap和html的地址:

执行后,console输出了hook的记录:

结束后,pcap文件记录了所有的SSL流量:

HTML文件只会记录HTTP的数据包:

不过这里有一个bug,就是获取到的双方的ip都是0.0.0.0,不知道为啥,没找到原因,之后再看吧。

完成后的脚本为:
https://github.com/fanxs-t/Android-SSL_read-write-Hook/blob/master/frida-hook.py

CATALOG
  1. 1. 0x00. 思路
  2. 2. 0x01. Hook脚本
    1. 2.1. initializeGlobals
    2. 2.2. 其他函数
  3. 3. 0x02. 执行和效果