摘要:
本文介绍如何通过hook OkHttp3框架来拦截并处理应用的网络响应,特别是在某书APP中,通过深入理解Okhttp3的API,hook RealCall以获取请求和响应数据,实现关键词搜索、笔记和用户等数据的回传操作。
一、原理介绍
1、hook okhttp3类,得到接口数据;
使用Okhttp3中的call方法,在newRealCall()中初始化了RealCall,hook-okhttp3.RealCall即可拿到请求包。可以直接将关键词搜索的,笔记, 用户等多个数据进行hook回传。
2、hook headers 得到xy_common_params;
3、hook getSessionId方法 得到sid账号信息;
4、上传到数据到服务器HttpHelper类;
5、跳转至app某页面:
public static void open_xhs(Context context,String pagetype,String id)
{
String url="";
if(pagetype.equals("user")){ //用户信息页
url="xhsdiscover://user/"+id;///xhsdiscover://user/6226f1a200000000210234ee xhsdiscover://profile xhsdiscover://home
}
if(pagetype.equals("search")){ //搜索页
//xhsdiscover://search/result?keyword=
url="xhsdiscover://search/result?keyword="+id;
}
if(pagetype.equals("video_feed")){ //视频笔记页
url="xhsdiscover://video_feed/"+id;
}
if(pagetype.equals("item")){ //笔记页
url="xhsdiscover://item/"+id;
}
Uri uri = Uri.parse(url);
Intent intent =new Intent(Intent.ACTION_VIEW,uri);;
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
二、完整源代码
package com.coin.userinfo;
import android.app.AndroidAppHelper;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.os.Bundle;
import android.util.Base64;
import android.util.Log;
import android.widget.Toast;
import org.json.JSONObject;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
import utils.HttpHelper;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
public class MainHook implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
Log.i("byc","===================app-start1=========================");
//XposedBridge.log("===================app-start1==========================");
// 判断当前启动的目标程序是否是要hook的应用程序 技术支持:metabycf or byc6352 or 39848872
if (loadPackageParam.packageName.equals("com.xingin.xhs")) {
Log.i("byc","===================xhs-start=========================");
//XposedBridge.log("===================xhs-Sid=========================");
XposedHelpers.findAndHookMethod("com.xingin.account.entities.UserInfo", // 包名+类名
loadPackageParam.classLoader,
"getSessionId", // 要hook的方法名称
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
}
// hook之后
@Override
protected void afterHookedMethod(MethodHookParam param)
throws Throwable {
super.afterHookedMethod(param);
// 打印方法返回值信息
String result = param.getResult().toString();
//XposedBridge.log("====xhs-sid-Cookie:---" + result);
Log.i("byc","====xhs-sid-Cookie:---" + result);
}
});
//hook okhttp 得到接口数据
Class<?> RealCall = XposedHelpers.findClass("okhttp3.RealCall", loadPackageParam.classLoader);
XposedHelpers.findAndHookMethod(RealCall, // 包名+类名
"execute",
new XC_MethodHook() {
// hook之后
@Override
protected void afterHookedMethod(MethodHookParam param)
throws Throwable {
super.afterHookedMethod(param);
Object result = param.getResult();
// XposedBridge.log("====param:---" + result.toString());
Object request = XposedHelpers.getObjectField(result, "request");
Object headers = XposedHelpers.getObjectField(request, "headers");
// XposedBridge.log("====headers:---" + headers.toString());
String[] fields = (String[]) XposedHelpers.getObjectField(headers, "namesAndValues");
String xy_common_params = null;
for (int x = 0; x < fields.length; x = x + 1) {
try {
if (fields[x].equals("xy-common-params")) {
xy_common_params = fields[x + 1];
break;
}
} catch (Exception e) {
e.printStackTrace();
}
}
if (xy_common_params != null) {
//XposedBridge.log("====xy-common-paramsaders:---" + xy_common_params);
Log.i("byc","====xy-common-paramsaders:---" + xy_common_params);
// todo 等待发送到服务端队列
//
}
Object body = XposedHelpers.callMethod(result, "body");
Object source = XposedHelpers.callMethod(body, "source");
XposedHelpers.callMethod(source, "request", Long.MAX_VALUE);
Object getBuffer = XposedHelpers.callMethod(source, "getBuffer");
Object cloneBuffer = XposedHelpers.callMethod(getBuffer, "clone");
String message = (String) XposedHelpers.callMethod(cloneBuffer, "readString", StandardCharsets.UTF_8);
// XposedBridge.log("====message:---" + message);
//Log.i("byc",request.toString());
//Log.i("byc", message);
//关键词搜索
if (result.toString().contains("search/recommend?keyword=") || result.toString().contains("search/notes?keyword=")) {
// todo message
//XposedBridge.log("------------search---------" + message);
Log.i("byc","------------search---------");
Log.i("byc",request.toString());
Log.i("byc", message);
//String strBase64 = Base64.encodeToString(message.getBytes(), Base64.DEFAULT);
}
//用户主页
if (result.toString().contains("user/info?user_id=")) {
//XposedBridge.log("------------user---------" + message);
//Log.i("byc","------------user---------" + message);
//String strBase64 = Base64.encodeToString(message.getBytes(), Base64.DEFAULT);
Log.i("byc","------------user---------");
upload_userinfo(message);
}
//详情页
if (result.toString().contains("note/feed?note_id=") || result.toString().contains("note/videofeed?note_id=")) {
//XposedBridge.log("------------note---------" + message);
Log.i("byc","------------note---------" + message);
//String strBase64 = Base64.encodeToString(message.getBytes(), Base64.DEFAULT);
//xhsHttpClient(strBase64, "note");
}
//切换账号
if (result.toString().contains("/api/sns/v4/user/login/password")) {
}
}
});
}
}
private void upload_userinfo(String data){
try{
Log.i("byc",data);
JSONObject jsonObject = new JSONObject(data);
String userid=jsonObject.getJSONObject("data").getString("userid");
Log.i("byc",userid);
String url="http://*********:10012/put/user/info?key=234181402307&user_id="+userid;
HttpHelper.http_post(url,data);
}catch (Exception e){
Log.e("byc",e.getMessage().toString());
}
}
}
三、数据传输类
package utils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
public class HttpHelper {
/**
* 获取网页HTML源代码
* @param http_url 网页路径
*/
public static String http_get(String http_url) throws Exception {
URL url=new URL(http_url);
HttpURLConnection conn=(HttpURLConnection)url.openConnection();
conn.setConnectTimeout(5000);
conn.addRequestProperty("Connection","close");
conn.setRequestMethod("GET");
if(conn.getResponseCode()==200){
InputStream inStream=conn.getInputStream();
byte[] data=read(inStream);
String html=new String(data,"UTF-8");
return html;
//MyLog.i("byc","ok");
}
return null;
}
/**
* 读取流中的数据
*/
public static byte[] read(InputStream inputStream) throws IOException {
ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
byte[] b=new byte[1024];
int len=0;
while((len=inputStream.read(b))!=-1){
outputStream.write(b);
}
inputStream.close();
return outputStream.toByteArray();
}
public static boolean http_post(String http_url,String data) {
try {
boolean result=false;
URL url = new URL(http_url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Connection", "close");//"close"
conn.setRequestProperty("Content-Type", "application/json");
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
conn.connect();
OutputStream os = conn.getOutputStream();
os.write(data.getBytes(StandardCharsets.UTF_8));
os.flush();
os.close();
int responseCode = conn.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
InputStream input = conn.getInputStream();
StringBuilder sb = new StringBuilder();
int ss;
while ((ss = input.read()) != -1) {
sb.append((char) ss);
}
MyLog.i("请求结果 = " + sb.toString());
//android.util.Log.e("tag", "请求结果 = " + sb.toString());
input.close();
result=true;
}
return result;
} catch (Exception e) {
MyLog.i("出现异常: " + e.toString());
//android.util.Log.e("tag", "出现异常: " + e.toString());
//e.printStackTrace();
return false;
}
}
}