混淆后 OKHTTP 框架的通用抓包方案探索

共 2615字,需浏览 6分钟

 ·

2021-10-22 02:54

看雪论坛作者ID:Avacci

https://bbs.pediy.com/user-home-879855.htm

目标:


okhttp因其稳定、开源、简单易用被众多的app开发人员所使用。okhttp中的execute和enqueue分别为发出同步和异步请求,通过对okhttp发起请求的整个流程进行简单分析就可以快速得到合适的hook点完成对app网络请求的抓包和溯源。但是,为了防止被快速逆向分析,部分app针对okhttp的代码进行了混淆,请以课程中的被混淆了的okhttp框架的apk为例,提出针对混淆了的okhttp框架,进行抓包和溯源的通用解决方案。

 

针对okhttp框架的抓包,暂时知道两种使用frida的hook方式。


第一种是自定义一个拦截器,并添加到okhttp框架的拦截器链中。如Yang神的实现(
https://bbs.pediy.com/thread-252129.htm


 

还有一种是hook RealCall类的enqueue和execute方法,获取到返回的response对象。然后逐步解析出请求和响应的内容。(如https://github.com/siyujie/OkHttpLogger-Frida

 

OkHttpLogger-Frida的作者还考虑到了okhttp框架的代码被混淆的情况。它专门打包了一个okhttpfind.dex文件来从Java层寻找okhttp的特征,并记录所有后续要用到的类名,方法名和变量名。

 

通过查阅其代码(https://github.com/siyujie/okhttp_find),发现它寻找特征的方法也很简单粗暴。就是先去所有可能属于okhttp框架层的类中找到OkHttpClient$Builder内部类。该类的特征为有四个List类型的成员变量,其中两个有final修饰符,两个的列表类型为接口类型。

 

一旦找到满足这个条件的类,就可以通过getEnclosingClass方法获取该类的外部类,也就定位到了关键类OkHttpClient。关键实现代码如下:

 

接着就以OkHttpClient和OkHttpClient$Builder为起点,根据成员变量的类型或是成员方法的参数和返回值类型不断定位到后续要用到的类、方法和变量。OkHttpLogger中定位到的主要内容如下:
如果是混淆了的话,具体值会在定位之后更新。对于作业的目标app,更新后的情况为

 
可以发现,该app对okhttp的混淆还是比较严重的。很多方法名和变量名显示出来的名字都是一样的。这就对后续frida hook造成一定影响,因为在OkHttpLogger中不管是对方法hook还是直接调用,又或者是获取某个对象的成员变量,都是直接通过名字进行的。当一个类中以非自然(重载方法不算)的形式出现了同名的方法或变量时,frida运行起来的结果就不能达到预期了。
 
比如,假设知道RealCall混淆后的类名为okhttp3.x。打印出该类所有的方法:
得到结果为(可以看到很多方法名相同):
即使通过特征识别,确定那个返回类型为okhttp3.aa,没有参数的public方法为execute,也无法通过方法名hook该方法。
因为没有参数的overload也匹配到了多个,除了一个以外都不是execute方法。通过.overload() hook到的那个方法是哪个是不确定的。(可能是第一个?
所以现在的问题为如何做到不依赖函数名实现混淆okhttp的抓包。
 
暂时想到的方法是hook一个只有一个方法的类,这样只要hook那个方法就不会错。而这个方法又是okhttp请求链中的关键一环。Okhttp中正好有多个这样的待选类,都是Interceptor接口的实现类。它只有一个intercept方法:
 
Okhttp的所有请求和响应肯定都要流经多个Interceptor实现类的intercept()方法,正常只要hook链中的一个类就行了,比如hook链中最后一个Interceptor。
 
课上有提到过,即使被混淆,还是可以通过打印出的调用堆栈与正常的堆栈对比,定位到关键类和方法。

通过hook NativeCrypto类的SSL_write方法,得到一下调用栈:

 
可以看到最后一个Interceptor为CallServerInterceptor,混淆后的类名为okhttp3.internal.b.b。用frida hook一下:

 
发现已经可以得到响应了:
那接下来就可以参照OkHttpLogger中寻找特征的方式,定位出解析请求和响应时用到的类,方法和成员变量。不同的是,对于方法和成员变量,不要只记录其名字,而是记录对应的整个对象。如一个方法就记录一个对应的Method对象,一个成员变量就记录一个对应的Field对象。
 
然后在需要调用方法的地方,用Method对象的invoke方法调用:
在需要获取成员变量内容的时候,用Field对象的get方法:
中间步骤比较繁杂,所以直接附上了代码。
 
最后hook结果为:
 
 
还有不少缺陷:
  1. 定位CallServerInterceptor的过程应该也可以用frida完成,就是先hook出调用堆栈后,从上至下逐个类判读是否满足Interceptor接口实现类的特征。将找到的第一个满足条件的类作为拦截器链中的最后一个拦截器。
  2. 解析过程中对RequestBody的处理没处理好。
  3. ResponseBody只处理了UTF-8编码的内容,对于其他编码的内容(如Gzip),则暂时未处理。
  4. 不太稳定,时不时会挂掉。但可能是app自带的反hook措施。

暂时未来得及处理,慢慢完善。

逆锋起笔是一个专注于程序员圈子的技术平台,你可以收获最新技术动态最新内测资格BAT等大厂的经验精品学习资料职业路线副业思维,微信搜索逆锋起笔关注!


推荐阅读:

别再用 LayUI 了
再见,Linux
浏览器缓存的力量
8 年开发,连登陆接口都写这么烂...
横空出世,比 Visio 快 10 倍的画图工具来了

明天见(。・ω・。)ノ♡
浏览 79
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报