一、逆向分析
首先感谢王同学提供的样本,因为王同学那天找到我咨询我说有一个应用Fiddler抓包失败,其实对于这类问题,我一般都会这么回答:第一你是否安装Fiddler证书了,他说他安装了。第二你是否用了我之前说的那个Xposed框架JustTrustMe,不了解这个框架的同学可以查看这里:Python爬取应用「英语流利说」的配音视频数据;他说了也用了。到这里我就没理由不帮他看了,自己下载了应用安装之后,的确有这个问题:
看到了,这里就是这样的提示,也没看到具体的请求参数啥的,所以这个就引发了我的兴趣,那么不多说了直接爆破吧,突破口很简单,直接全局搜这个接口api.izuiyou.com即可:
搜索结果很多,主要看纯接口那个,通过两三次的点击浏览最后定位到这一处:
到这里就看到了,其实他内部用的是okhttp进行网络请求的,使用AsyncTask类进行操作,但是在构建okhttp的时候他做了很多设置操作,首先是参数设置:
看到这个c类其实是设置okhttp的拦截器,这里简单看一下应该是post方式请求会把参数进行加密处理,我们看看那个加密代码:
这个不多说了,依然采用底层加密操作,到这里我们就直接hook它:
然后安装模块运行即可,然后我们手机不挂代理访问,这样就能正常拉取数据了,看看加密的都是啥信息,我们在首页进行下拉刷新操作:
看到了下拉数据接口是recommend的,然后参数就是请求参数信息,看到最后加密之后的只有一个sign字段了,应该是把参数放到so层进行加密处理了,那么这个加密逻辑不是本文的介绍重点,后续会继续单独介绍如何动态调试so来弄出加密算法。继续回到刚才设置了okhttp的拦截器之后,就设置okhttp的SSLSocketFactory和X509TrustManager值就是和证书相关的:
然后这里他自定义了一个SSLSocketFactory类,这个类中可以自定义的Socket信息:
一般都是在回调方法createSocket中处理通信的Socket信息:
然后就是okhttp的设置证书的方法调用了,后面的解决方案就是hook这个方法来
当然还有设置域名检查的方法HostnameVerifier,这里不多介绍了。那么现在可以看到抓包失败可能就是这三个值引起的,这个大家一定要熟悉okhttp框架的大致用法和原理,而且现在几乎很多应用都在用这个okhttp框架了,并且google官方已经把这个框架集成到系统中替换原来的apache的http框架了。所以后续如果抓包失败都可以直接分析应用中的这个框架即可。
到这里我们大致找到关键点了,不过这里需要说明一个现象就是抓包失败,在应用界面会看到有一个loading一直在转圈圈也就是这里有一个超时时间,诡异的是如果超时了,数据可以正常加载。但是这个超时太长了几乎几分钟。虽然最终可以看到数据也可以抓到包(文章开始说抓不到包是因为太慢了没心等待)。但是得解决这个问题不然等几分钟没法进行后面操作了。所以还得继续看这个问题吧。上面已经定位到问题大致就是okhttp设置了证书那些值导致的问题,那么是哪些值呢?我们先看看SSLSocketFactory这个类:
这个类的createSocket方法中处理了很多逻辑,那么我们在hook这个方法打印参数信息:
我们在方法调用前打印参数信息,方法调用结束之后打印结束信息,然后运行模块看日志信息:
我们挂了Fiddler代理之后看到打印的参数值信息了,不过可惜的是结束日志没有打印出来,那么问题就出在这个方法了,但是上面有好几处代码怎么定位到是哪个代码等待呢?这个我们可以选择对每个方法调用进行hook然后查看日志,但是这样太费劲了,这时候就需要一个技巧就是如果想定位到哪个方法等待,那么可以进行插入日志代码,看打印信息,但是我们加入很多日志,所以要区分是哪行日志打印了,所以这里有一个技巧就是通过获取当前方法被调用的行数来作为打印的信息,代码大致如下:
这样我们只需要调用log这个方法即可,因为这个方法是无参数的,在插入smali代码的时候有一条基本原则就是尽量把功能模块弄到一个static无参函数中,这样对于插入非常高效便捷也不会出错。有了这个java代码,然后用我之前写的java2smali工具直接运行即可,不了解这个工具的同学可以查看这里:Android中一键转化java2smali工具原理解析;然后就得到了对应的smali代码,然后我们对于这个app呢?不要去直接apktools反编译,因为反编译失败的,我们可以直接操作dex转化成smali即可,这个要用到baksmali和smali这两个工具,我们直接解压apk获取到classes.dex文件,然后执行命令:java -jar baksmali.jar -o classes classes.dex 其中classes是反编译dex之后的smali文件夹目录。然后把我们上面的MyLog.smali放到指定目录下,记住一定要有全路径,比如这里是cn.wjdiankong.log.MyLog,那么就要放到cn/wjdiankong/log/MyLog.smali,没有目录就自己手动新建即可。然后在上面想要插入代码的地方直接插入代码:invoke-static {}, Lcn/wjdiankong/log/MyLog;->log()V 这里我放了好几个:
记住放的地方别瞎乱放入,一般都在move-result...语句之后也就是方法调用结束之后插入,不然回编译dex会报错的。插入之后就在用smali进行回编译:java -jar smali.jar classes -o classes.dex 其中classes是反编译的smali目录,classes.dex是回编译之后的dex。成功回编译得到新的dex之后,为了验证插入成功,可以用Jadx打开进行查看:
我们看到好几处都插入日志了,并且日志是携带行号的,这样就能区分是哪里的日志打印结果了,好了把这个插入代码的dex在塞到原来的apk中,然后二次签名即可,因为这个应用没有签名校验,所以直接看打印日志:
然后在回过头看看代码:
但是这个createSocket方法是系统的了?得去看源码查看为何等待那么久?对到这里我没去深入看了,因为不是本文研究的重点了,不过可以告诉大家如果感兴趣可以自己写个demo然后用okhttp访问一下接口也是这个createSocket方法等待很久。
二、解决方案
我们问题找到了,解决办法就简单了,为了能够正常抓包,直接把这个okhttp的证书hook修改成系统默认的即可:
然后拦截之前分析的那个设置okhttp的证书代码方法:
然后运行模块之后就可以愉快的抓包啦:
看到了这里就可以愉快的抓到数据包了,其实这里虽然我们知道了解决办法,但是其实内部的原因还是没弄清楚,就是知道原因是okhttp内部的问题,其实这个搜索可以发现内部实现机制的原因,不过这个对于我们不是重点,那么通过这个样本我们又可以总结一个遇到抓不到包的问题解决方案了,那就是全局搜SSLSocketFactory,然后看看应用是否设置了证书信息。如果有就拦截替换成系统默认的证书即可。
三、抓包失败解决方案
通过以往知道的知识以及本文的案例,那么我们就可以总结一下app中抓包失败的解决方案大致如下:
第一、确认Fiddler证书安装正确
第二、是否安装Xposed模块JustTrustMe信任所有证书
第三、查看应用中使用的okhttp中是否设置了SSLSocketFactory
其实除了这三个还有其他方式,还有其他方式,不过不在本文介绍了,后面会详细介绍如何防止自己的应用被被人恶意抓包,因为如果应用被人抓包其实很多破解操作就变的很简单了。把这个入口做的安全会对后面的防护有一定效果。
四、总结
关于Android中的防止抓包现在很多应用都开始做了对应的策略,最常见的是采用https,然后Fiddler证书是不被Android系统根证书认可所以Fiddler代理证书抓包失败的,但是可以Hook系统的证书认证功能即可解决这个问题,而本文可以看到如果想很快的爆破这个问题,如果你前期非常了解okhttp框架的话,会很快解决,因为之前做过很多应用都用到这个框架,或者说这个框架已经被普遍用于绝大部分应用,Google官方也把这个框架集成到系统中了。那么关于okhttp框架设置证书那块要是熟悉本文爆破非常快。而且通过本文之后以后遇到抓不到包的情况可以再次参考这个解决方案即可。后续会单独介绍这个应用的加密算法解析!