摘要:

   本文介绍如何通过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;
        }
    }
}

四、封面

magisk+lsposed hook okhttp3采集小红书app端接口数据(包含完整源码)(2024-09-03)_lsposed