🛫 系列文章导航
- 【Frida】【Android】01_手把手教你环境搭建 https://blog.csdn.net/kinghzking/article/details/136986950
- 【Frida】【Android】02_JAVA层HOOK https://blog.csdn.net/kinghzking/article/details/137008446
- 【Frida】【Android】03_RPC https://blog.csdn.net/kinghzking/article/details/137050967
- 【Frida】【Android】04_Objection安装和使用 https://blog.csdn.net/kinghzking/article/details/137071768
- 【Frida】【Android】05_Objection实战 https://blog.csdn.net/kinghzking/article/details/137071826
▒ 目录 ▒
- 🛫 系列文章导航
- 🛫 导读
- 开发环境
- 1️⃣ 代码介绍
- okhttp3主逻辑
- 拦截器
- 2️⃣ newCall自吐
- 自吐frida脚本编写
- 验证步骤
- 3️⃣ 拦截器自吐
- 获取dex文件
- 自吐frida脚本编写【通用高效】
- 验证步骤
- 📖 参考资料
🛫 导读
开发环境
版本号 | 描述 | |
---|---|---|
文章日期 | 2024-03-24 | |
操作系统 | Win11 - 22H2 | 22621.2715 |
node -v | v20.10.0 | |
npm -v | 10.2.3 | |
夜神模拟器 | 7.0.5.8 | |
Android | 9 | |
python | 3.9.9 | |
frida | 16.2.1 | |
frida-tools | 12.3.0 | |
objection | 1.11.0 | |
1️⃣ 代码介绍
okhttp3主逻辑
implementation(“com.squareup.okhttp3:okhttp:3.12.0”)。
okhttp3的网络请求流程主要有以下几步:
- 通过以下代码创建一个
okhttpClient对象
- 码创建一个
Request请求对象
- 端将Request请求封装成
Call对象
- 通过调用Call对象的
enqueue()
函数产生一次真实的网络请求,该参数接受一个Callback对象
,用于处理响应结果。
public class example {
// TAG即为日志打印时的标签
private static final String TAG = "r0ysue666";
// 新建一个Okhttp客户端(不含拦截器等各种参数的OkHttpClient对象)
// OkHttpClient client = new OkHttpClient();
// 新建一个拦截器
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(new LoggingInterceptor())
.build();
void run(String url) throws IOException {
// 构造request
Request request = new Request.Builder()
.url(url)
.header("token","r0ysue")
.build();
// 发起异步请求
client.newCall(request).enqueue(
new Callback() {
@Override
public void onFailure(Call call, IOException e) {
call.cancel();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//打印输出
Log.d(TAG, response.body().string());
}
}
);
}
}
拦截器
拦截器是okhttp中的一个重要概念,okhttp通过Interceptor完成监控管理、重写和重试请求。每个网络请求和接收不管是GET还是PUT/POST等数据传输方式都必须经过okhttp3本身存在的五大拦截器,因此Interceptor是一个绝佳的Hook点,可以同时打印输出请求和响应。
下图就是okhttp3自带的五大拦截器:
拦截器链
下面的代码,是构造拦截器链的过程,可以看到:
- 通过 addInterceptor() 方法添加的
应用拦截器
是放在最前面的。- 通过 addNetworkInterceptor() 方法添加的
网络拦截器
,则是在非 WebSocket 请求时,添加在 ConnectInterceptor 和 CallServerInterceptor 之间的。
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
// 收集拦截器的临时列表
List<Interceptor> interceptors = new ArrayList<>();
// 先添加通过 OkHttpClient.Builder # addInterceptor() 方法添加的拦截器
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
// 如果不是 WebSocket 请求,
// 则添加通过 OkHttpClient.Builder # addNetworkInterceptor() 方法添加的拦截器
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
从上面我们可以看到
.addNetworkInterceptor(new LoggingInterceptor())
这样的代码,意思是给okhttp3增加网络拦截器。
LoggingInterceptor对象
,将打印请求和响应相关的内容。
拦截器执行结果如下:
2️⃣ newCall自吐
本篇文章自带的资源是带拦截器的版本,可以查看拦截器运行的效果。
下面的内容,是通过不带拦截器的apk(下载地址:https://download.csdn.net/download/kinghzking/89067530)进行讲解,打印出我们想要的内容。
自吐frida脚本编写
从《源码介绍》中我们可以看出,
OkHttpClient.newCall
接受了一个request参数,该参数包含了我们关心的主要内容,所以我们直接拦截newCall
即可打印出我们想要的结果,代码如下:
Java.perform(function() {
var OkHttpClient = Java.use("okhttp3.OkHttpClient")
OkHttpClient.newCall.implementation = function (request) {
var result = this.newCall(request)
console.log('[newCall] request = ', request.toString())
return result
};
});
验证步骤
- 通过frida -UF连接
- 执行上面js语句
- 点击
apk
中的发送请求
按钮,显示拦截结果。
效果如下图所示:
3️⃣ 拦截器自吐
前文已经说过,拦截器方式,可以更方便的实现请求内容的打印,我们如果将拦截器注入到目标APP中,将更通用而方便。
这种实现有两种方式:
- 将
LoggingInterceptor
这部分代码翻译成JavaScript代码- 将编译好的dex通过Frida将注入其他应用中
很明显,第二种更便于代码的编写和理解,我们也将以该方式进行演示。
获取dex文件
- 下载不带拦截器的apk(下载地址:https://download.csdn.net/download/kinghzking/89067530)
- 将这个文件解压后得到一个classes.dex
- 将classes.dex更名为okhttp3logging.dex
- 也可以直接下载:https://download.csdn.net/download/kinghzking/89068234
- 将其推送到测试手机的/data/local/tmp目录下
adb push okhttp3logging.dex /data/local/tmp
自吐frida脚本编写【通用高效】
- 加载dex:
Java.openClassFile
- 获取LoggingInterceptor类:Java.use
- 生成LoggingInterceptor对象:
MyInterceptor.$new()
- 获取OkHttpClient内部类Builder
- 修改Builder.build实现
- 在networkInterceptors()中增加拦截器
LoggingInterceptor对象
Java.perform(function () {
Java.openClassFile("/data/local/tmp/okhttp3logging.dex").load();
var MyInterceptor = Java.use("com.r0ysue.okhttp3demo.LoggingInterceptor");
var MyInterceptorObj = MyInterceptor.$new();
var Builder = Java.use("okhttp3.OkHttpClient$Builder");
console.log(Builder);
Builder.build.implementation = function () {
this.networkInterceptors().add(MyInterceptorObj);
return this.build();
};
console.log("hook_okhttp3...");
});
验证步骤
当我们通过frida
交互模式
,执行了上面代码的时候,发现并不会打印我们想要的结果:
这是因为,App只有一个全局对象client,一般在App启动的较早时机被创建,如果采用attach模式Hook okhttpClient,大概率会一无所获,因此只能用spawn模式
来启动,对应的Frida命令必须使用-f参数。
执行命令:
frida -U -f com.r0ysue.okhttp3demo -l hookInteceptor.js
再次点击发送请求
按钮,熟悉的okhttpGET:
日志又出现了:
📖 参考资料
- frida 常见问题和报错https://crifan.github.io/reverse_debug_frida/website/summary_note/common_issue/
- objection地址:https://github.com/sensepost/objection
- okhttp3logging.dex https://download.csdn.net/download/kinghzking/89068234
- okhttp3 读书笔记 https://blog.csdn.net/OneDeveloper/article/details/88381817
ps: 文章中内容仅用于技术交流,请勿用于违规违法行为。