Frida0A - 拦截okhttp

360 阅读2分钟

各位读者你们好呀!有两个星期没有更新了,主要是懒癌犯了!

OkHttp

square.github.io/okhttp/

Okhttp 是一个网络框架,现在的Android应用基本都使用的是这个开源库了。

想要拦截 OkHttp,首先就要熟悉它的API和原理。这里就不展开了,可以查找一些文章或者自己看源码。

OkHttp的工作模式有点像 OSI 模型,它里面的拦截器就相当于一个层次。每个拦截器都负责两部分的工作:

  • 将请求进行封装,y = f(x)
  • 将返回结果解封装,y’ = f’(x)

以 BridgeInterceptor 为例:

  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val userRequest = chain.request()
    val requestBuilder = userRequest.newBuilder()

    // ... 处理请求体

    val networkRequest = requestBuilder.build()
    val networkResponse = chain.proceed(networkRequest)

    cookieJar.receiveHeaders(networkRequest.url, networkResponse.headers)

    val responseBuilder =
      networkResponse.newBuilder()
        .request(networkRequest)

    // ... 处理响应体

    return responseBuilder.build()
  }

为了调试方便,OkHttp 里面还自带了一个日志拦截器:

github.com/square/okht…

有了这个拦截器,就可以打印所有请求的请求体与响应体。

所以一个最简单的思路就是使用 frida 将这个日志拦截器添加到 okhttp 的实例对象里面就ok。

拦截OkHttp

这里有一篇文章,使用 frida 的 api 实现了一个拦截器,感觉有点蛋疼。

bbs.kanxue.com/thread-2521…

肯定是使用加载 dex 的方式会好些,找到所有OkHttpClient的实例对象,然后给他们加上拦截器:

// 加载包含logging-interceptor拦截器的DEX
Java.openClassFile("/data/local/tmp/xxxxx.dex").load();

// 获取日志拦截器类
const MyInterceptor = Java.use("com.xxxx.okhttp3Logging");
var MyInterceptorObj = MyInterceptor.$new();

// interceptors 是一个不可变集合,所以需要重新创建一个新的集合
Java.choose("okhttp3.OkHttpClient", {
    onMatch: function (instance) {
        console.log("1. found instance:", instance)
        console.log("2. instance.interceptors():", instance.interceptors().$className)
        console.log("3. instance._interceptors:", instance._interceptors.value.$className)
        //console.log("4. interceptors:",gson2.$new().toJson(instance.interceptors())) 
        console.log("5. interceptors:", Java.use("java.util.Arrays").toString(instance.interceptors().toArray()))
        var newInter = Java.use("java.util.ArrayList").$new();
        newInter.addAll(instance.interceptors());
        console.log("6. interceptors:", Java.use("java.util.Arrays").toString(newInter.toArray()));
        console.log("7. interceptors:", newInter.$className);
        newInter.add(MyInterceptorObj);
        newInter.add(curlInter);
        instance._interceptors.value = newInter;

    }, onComplete: function () {
        console.log("Search complete!")
    }
})

未标题-1.jpg

混淆处理

为了防止被快速逆向分析,部分 app 针对 okhttp 的代码进行了混淆。

有一个开源项目就是做了这个事情:

github.com/siyujie/OkH…

它里面的核心逻辑是对混淆类的寻找:

github.com/siyujie/okh…

它寻找特征的方法也很简单粗暴。就是先去所有可能属于 okhttp 框架层的类中找到 OkHttpClient$Builder 内部类。该类的特征为有四个 List 类型的成员变量,其中两个有 final 修饰符,两个的列表类型为接口类型。一旦找到满足这个条件的类,就可以通过 getEnclosingClass 方法获取该类的外部类,也就定位到了关键类 OkHttpClient。然后顺藤摸瓜一直找下去。

感觉会有更好的方式,可以自己混淆一下代码观察下特征。

根据 README 的介绍来看:

  `find()`                                         检查是否使用了Okhttp & 是否可能被混淆 & 寻找okhttp3关键类及函数 
  `switchLoader("okhttp3.OkHttpClient")`         参数:静态分析到的okhttpclient类名
  `hold()`                                         开启HOOK拦截
  `history()`                                      打印可重新发送的请求
  `resend(index)`                                  重新发送请求

这个库需要在一定程度上结合静态分析才能使用,当然如果 find 能找出来最好,只需要替换一下就行了。