xposed实现对okhttp拦截网络

一开始搜到有几个博客说用拦截器去实现,我也自己摸索下用拦截器

            XC_MethodHook xc_BuildHook = new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    super.afterHookedMethod(param);
                    try {
                        XpLog.INSTANCE.log(getTag(), "hookAllConstructors.param.thisObject:" + param.thisObject);
                        List interceptors = handleInterceptor(param, classLoader, "interceptors");
                        if ("okhttp3.OkHttpClient$Builder".equals(param.thisObject.getClass().getName())) {
                            List networkInterceptors = handleInterceptor(param, classLoader, "networkInterceptors");
                        }
                    } catch (Throwable e) {
                        XposedBridge.log(e);
                    }

                }
            };

            //hook okhttp对象
            Class<?> builderClass = classLoader.loadClass("okhttp3.OkHttpClient$Builder");
            XposedBridge.hookAllConstructors(builderClass, xc_BuildHook);
            XposedBridge.hookAllMethods(builderClass, "build", xc_BuildHook);

通过hook以上2个方法,做得到实例的interceptors成员变量,我们就可以修改拦截器的内容,当时我发现,如果interceptors空的话,我是没办法,好像是这样编,后来我实现编不下去,放弃hook OkHttpClient

后来我发现通过hook RealInterceptorChain,这个类在okhttp源码里面虽然会多次实例化,但是只有一个入口,就算代码混淆,也方便定位找到

XposedBridge.hookAllConstructors(aClassRealInterceptorChain, xc_BuildHook);

写上这代码,我能拿到okhttp默认添加的拦截器,现在我感觉有的活力去编下去了,利用动态代理做了一个对象加进去

private List handleInterceptor(XC_MethodHook.MethodHookParam param, ClassLoader classLoader, String interceptorsStr) {
        List interceptors = (List) XposedHelpers.getObjectField(param.thisObject, interceptorsStr);
        XpLog.INSTANCE.log(getTag(), "handleInterceptor.interceptors:" + interceptors);
        if (interceptors != null) {
            if (!interceptors.isEmpty()) {
                int i = interceptors.size() - 1;
                Object interceptor = interceptors.get(i);
                XpLog.INSTANCE.log(getTag(), "handleInterceptor.interceptor:" + interceptor.getClass());
                //动态代理加进去
                InterceptorProxyHandler interceptorProxyHandler = new InterceptorProxyHandler(interceptor);
                Object mObj = Proxy.newProxyInstance(classLoader, interceptor.getClass().getInterfaces(), interceptorProxyHandler);
                XpLog.INSTANCE.log(getTag(), "handleInterceptor.mObj:" + mObj);
                if (mObj != null) {
                    //移除原本的
                    interceptors.remove(i);
                    //加在最后一个地方
                    interceptors.add(mObj);
                }
            }
            //这里输出具体拦截器的类的名字
            Field interceptorsField = XposedHelpers.findField(param.thisObject.getClass(), interceptorsStr);
            ParameterizedType type = (ParameterizedType) interceptorsField.getGenericType();
            Type[] actualTypeArguments = type.getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                XpLog.INSTANCE.log(getTag(), "interceptors actualTypeArgument:" + ((Class) actualTypeArgument).getName());
                break;
            }
        }
        return interceptors;
    }

后来,我开心的编,编啊编,写上我的动态代理对象先

public class InterceptorProxyHandler implements InvocationHandler {

    final static String TAG = "InterceptorProxyHandler";

    private Object mTarget;

    public InterceptorProxyHandler(Object target) {
        this.mTarget = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            XpLog.INSTANCE.log(TAG, "invoke.method:" + method.getName());
            if ("intercept".equals(method.getName())) {
                //这里参考 HttpLoggingInterceptor 来实现,如果是混淆,需要自己分析类的每个方法
                if (args != null) {
                    XpLog.INSTANCE.log(TAG, "invoke.args:" + Arrays.toString(args));
                    Object realInterceptorChainObj = args[0];
                    Class<?> aClassRealInterceptorChain = realInterceptorChainObj.getClass();

                    //反射获取request对象
                    Method requestMethod = aClassRealInterceptorChain.getDeclaredMethod("request");
                    Object invokeRequest = requestMethod.invoke(realInterceptorChainObj);

                    Class<?> aClassRequest = invokeRequest.getClass();
                    //获取body
                    Method requestBodyMethod = aClassRequest.getDeclaredMethod("body");
                    Object invokeRequestBody = requestBodyMethod.invoke(invokeRequest);
                    boolean hasRequestBody = invokeRequestBody != null;

                    Method requestConnectionMethod = aClassRealInterceptorChain.getDeclaredMethod("connection");
                    Object invokeConnection = requestConnectionMethod.invoke(realInterceptorChainObj);
                    Object protocol = invokeConnection != null ? invokeConnection.getClass().getDeclaredMethod("protocol").invoke(invokeConnection) : "http/1.1";
                    Method methodMethod = aClassRequest.getDeclaredMethod("method");
                    Object invokeMethod = methodMethod.invoke(invokeRequest);

                    Method urlMethod = aClassRequest.getDeclaredMethod("url");
                    Object invokeUrl = urlMethod.invoke(invokeRequest);

                    String requestStartMessage = "--> " + invokeMethod + ' ' + invokeUrl + ' ' + protocol;

                    if (hasRequestBody) {
                        Class<?> invokeBodyClass = invokeRequestBody.getClass();
                        requestStartMessage += " (" + invokeBodyClass.getDeclaredMethod("contentLength").invoke(invokeRequestBody) + "-byte body)";
                    }
                    XpLog.INSTANCE.log(TAG, requestStartMessage);

                    if (hasRequestBody) {
                        Class<?> invokeBodyClass = invokeRequestBody.getClass();
                        // Request body headers are only present when installed as a network interceptor. Force
                        // them to be included (when available) so there values are known.
                        Method contentTypeMethod = invokeBodyClass.getDeclaredMethod("contentType");
                        Object invokeContentType = contentTypeMethod.invoke(invokeRequestBody);
                        if (invokeContentType != null) {
                            XpLog.INSTANCE.log(TAG, "Content-Type: " + invokeContentType);
                        }
                        Method contentLengthMethod = invokeBodyClass.getDeclaredMethod("contentLength");
                        Object invokeContentLength = contentLengthMethod.invoke(invokeRequestBody);
                        if (invokeContentLength != null) {
                            XpLog.INSTANCE.log(TAG, "Content-Length: " + invokeContentLength);
                        }
                        Method headersMethod = aClassRequest.getDeclaredMethod("headers");
                        Object invokeHeaders = headersMethod.invoke(invokeRequest);
                        if (invokeHeaders != null) {
                            Class<?> aClassHeaders = invokeHeaders.getClass();
                            Method sizeMethod = aClassHeaders.getDeclaredMethod("size");
                            int invokeSize = (int) sizeMethod.invoke(invokeHeaders);
                            for (int i = 0; i < invokeSize; i++) {
                                Method nameMethod = aClassHeaders.getDeclaredMethod("name", int.class);
                                String name = (String) nameMethod.invoke(invokeHeaders, i);
                                // Skip headers from the request body as they are explicitly logged above.
                                if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) {
                                    XpLog.INSTANCE.log(TAG, name + ": " + aClassHeaders.getDeclaredMethod("value", int.class).invoke(invokeHeaders, i));
                                }
                            }
                            //处理结束地方
                            if (!hasRequestBody) {
                                XpLog.INSTANCE.log(TAG, "--> END " + invokeMethod);
                            } else if (bodyEncoded(invokeHeaders)) {
                                XpLog.INSTANCE.log(TAG, "--> END " + invokeMethod + " (encoded body omitted)");
                            } else {
                                //逆向看某app这里 okio做了混淆,所以要特殊处理
                                // 混淆保留了这个,所以能看出是什么类
                                // /* compiled from: Buffer */
                                Class<?> aClassBuffer = mTarget.getClass().getClassLoader().loadClass("okio.c");//okio.Buffer
                                Object objBuffer = aClassBuffer.newInstance();
                                ///* compiled from: BufferedSink */
                                Class<?> aClassBufferedSink = mTarget.getClass().getClassLoader().loadClass("okio.d");
                                Method writeToMethod = invokeBodyClass.getDeclaredMethod("writeTo", aClassBufferedSink);
                                writeToMethod.invoke(invokeRequestBody, objBuffer);
                                Method readStringMethod = aClassBuffer.getDeclaredMethod("a", Charset.class);//readString
                                String invokeReadString = (String) readStringMethod.invoke(objBuffer, Charset.forName("UTF-8"));
                                XpLog.INSTANCE.log(TAG, "request params:" + invokeReadString);
                                XpLog.INSTANCE.log(TAG, "--> END " + invokeMethod
                                        + " (" + invokeContentLength + "-byte body)");

                            }
                        }
                    }
                }
            }
        } catch (Throwable e) {
            XpLog.INSTANCE.log(e);
        }
        Object invoke = method.invoke(mTarget, args);
        return invoke;
    }

    private boolean bodyEncoded(Object invokeHeaders) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        String contentEncoding = (String) invokeHeaders.getClass().getDeclaredMethod("get", String.class).invoke(invokeHeaders, "Content-Encoding");
        return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity");
    }
}

把请求编好,但是发现去解析响应的时候,代码突然编不下去了,okhttp里面做了判断抛了异常有点难受,我在反射proceed这个方法 ,okhttp给我抛了AssertionError,源码我找了下看,还是大哥RealInterceptorChain这个类

 public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException {
        if (this.index >= this.interceptors.size()) {
            throw new AssertionError();
        } else {
            ++this.calls;
            if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
                throw new IllegalStateException("network interceptor " + this.interceptors.get(this.index - 1) + " must retain the same host and port");
            } else if (this.httpCodec != null && this.calls > 1) {
                throw new IllegalStateException("network interceptor " + this.interceptors.get(this.index - 1) + " must call proceed() exactly once");
            } else {
                RealInterceptorChain next = new RealInterceptorChain(this.interceptors, streamAllocation, httpCodec, connection, this.index + 1, request);
                Interceptor interceptor = (Interceptor)this.interceptors.get(this.index);
                Response response = interceptor.intercept(next);
                if (httpCodec != null && this.index + 1 < this.interceptors.size() && next.calls != 1) {
                    throw new IllegalStateException("network interceptor " + interceptor + " must call proceed() exactly once");
                } else if (response == null) {
                    throw new NullPointerException("interceptor " + interceptor + " returned null");
                } else {
                    return response;
                }
            }
        }
    }

一开始想着我能不能跟拦截器那样动态代理,翻了翻代码,发现这对象每次都new,不好hook,算了,只能这样了

心想RealInterceptorChain这类,整个就唯独一个类,就算混淆也好定位,那我来hook RealInterceptorChain 的proceed方法吧

//hook RealInterceptorChain
            /**
             *     public Call newCall(Request request) {
             *         return new RealCall(this, request, false);
             *     }
             *
             *
             *     public Response getResponseWithInterceptorChain() throws IOException {
             *         ArrayList arrayList = new ArrayList();
             *         arrayList.addAll(this.client.interceptors());
             *         arrayList.add(this.retryAndFollowUpInterceptor);
             *         arrayList.add(new BridgeInterceptor(this.client.cookieJar()));
             *         arrayList.add(new CacheInterceptor(this.client.internalCache()));
             *         arrayList.add(new ConnectInterceptor(this.client));
             *         if (!this.forWebSocket) {
             *             arrayList.addAll(this.client.networkInterceptors());
             *         }
             *         arrayList.add(new CallServerInterceptor(this.forWebSocket));
             *         return new RealInterceptorChain(arrayList, null, null, null, 0, this.originalRequest, this, this.client.eventListenerFactory().create(this)).proceed(this.originalRequest);
             *     }
             */
            Class<?> aClassRealInterceptorChain = classLoader.loadClass("okhttp3.internal.http.RealInterceptorChain");
            //XposedBridge.hookAllConstructors(aClassRealInterceptorChain, xc_BuildHook);
            XposedHelpers.findAndHookMethod(
                    aClassRealInterceptorChain,
                    "proceed",
                    classLoader.loadClass("okhttp3.Request"),
                    new XC_MethodHook() {
                        @Override
                        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                            super.afterHookedMethod(param);
                            //入参
                            Object arg = param.args[0];
                            StringBuilder netStr = new StringBuilder();
                            netStr.append("\n");
                            netStr.append(">>>> start net");
                            netStr.append("\n");
                            netStr.append("procee@@@@@@arg.getClass:").append(arg.getClass());
                            netStr.append("\n");
                            netStr.append("proceed@@arg:").append(arg);

                            Class<?> aClassRequest = arg.getClass();
                            //获取body
                            Method requestBodyMethod = aClassRequest.getDeclaredMethod("body");
                            Object invokeRequestBody = requestBodyMethod.invoke(arg);
                            boolean hasRequestBody = invokeRequestBody != null;

                            Method methodMethod = aClassRequest.getDeclaredMethod("method");
                            Object invokeMethod = methodMethod.invoke(arg);

                            if (hasRequestBody) {
                                Class<?> invokeBodyClass = invokeRequestBody.getClass();
                                // Request body headers are only present when installed as a network interceptor. Force
                                // them to be included (when available) so there values are known.
                                Method contentTypeMethod = invokeBodyClass.getDeclaredMethod("contentType");
                                Object invokeContentType = contentTypeMethod.invoke(invokeRequestBody);
                                if (invokeContentType != null) {
                                    netStr.append("\n");
                                    netStr.append("Content-Type: ").append(invokeContentType);
                                }
                                Method contentLengthMethod = invokeBodyClass.getDeclaredMethod("contentLength");
                                Object invokeContentLength = contentLengthMethod.invoke(invokeRequestBody);
                                if (invokeContentLength != null) {
                                    netStr.append("\n");
                                    netStr.append("Content-Length: ").append(invokeContentLength);
                                }
                                Method headersMethod = aClassRequest.getDeclaredMethod("headers");
                                Object invokeHeaders = headersMethod.invoke(arg);
                                if (invokeHeaders != null) {
                                    Class<?> aClassHeaders = invokeHeaders.getClass();
                                    Method sizeMethod = aClassHeaders.getDeclaredMethod("size");
                                    int invokeSize = (int) sizeMethod.invoke(invokeHeaders);
                                    for (int i = 0; i < invokeSize; i++) {
                                        Method nameMethod = aClassHeaders.getDeclaredMethod("name", int.class);
                                        String name = (String) nameMethod.invoke(invokeHeaders, i);
                                        // Skip headers from the request body as they are explicitly logged above.
                                        if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) {
                                            netStr.append("\n");
                                            netStr.append(name).append(": ").append(aClassHeaders.getDeclaredMethod("value", int.class).invoke(invokeHeaders, i));
                                        }
                                    }
                                    //处理结束地方
                                    if (!hasRequestBody) {
                                        netStr.append("\n");
                                        netStr.append("--> END ").append(invokeMethod);
                                    } else if (bodyEncoded(invokeHeaders)) {
                                        netStr.append("\n");
                                        netStr.append("--> END ").append(invokeMethod).append(" (encoded body omitted)");
                                    } else {
                                        //逆向看某app这里 okio做了混淆,所以要特殊处理
                                        // 混淆保留了这个,所以能看出是什么类
                                        // /* compiled from: Buffer */
                                        Class<?> aClassBuffer = null;
                                        try {
                                            aClassBuffer = classLoader.loadClass("okio.Buffer");//okio.Buffer
                                        } catch (Exception e) {
                                            aClassBuffer = classLoader.loadClass("okio.c");//okio.Buffer
                                        }
                                        Object objBuffer = aClassBuffer.newInstance();
                                        ///* compiled from: BufferedSink */
                                        Class<?> aClassBufferedSink = null;
                                        try {
                                            aClassBufferedSink = classLoader.loadClass("okio.BufferedSink");//okio.BufferedSink
                                        } catch (Exception e) {
                                            aClassBufferedSink = classLoader.loadClass("okio.d");//okio.BufferedSink
                                        }
                                        Method writeToMethod = invokeBodyClass.getDeclaredMethod("writeTo", aClassBufferedSink);
                                        writeToMethod.invoke(invokeRequestBody, objBuffer);
                                        Method readStringMethod;
                                        try {
                                            readStringMethod = aClassBuffer.getDeclaredMethod("readString", Charset.class);//readString
                                        } catch (Exception e) {
                                            readStringMethod = aClassBuffer.getDeclaredMethod("a", Charset.class);//readString
                                        }
                                        String invokeReadString = (String) readStringMethod.invoke(objBuffer, Charset.forName("UTF-8"));
                                        netStr.append("\n");
                                        netStr.append("request params:").append(invokeReadString);
                                        netStr.append("\n");
                                        netStr.append("--> END ").append(invokeMethod).append(" (").append(invokeContentLength).append("-byte body)");

                                    }
                                }
                            }

                            //出参
                            Object result = param.getResult();
                            if (result != null) {
                                netStr.append("\n");
                                netStr.append("proceed@@result:").append(result);
                                Class<?> aClassResponse = result.getClass();
                                Method headersMethod = aClassResponse.getDeclaredMethod("headers");
                                Object invokeHeaders = headersMethod.invoke(result);
                                boolean isGzip = false;
                                if (invokeHeaders != null) {
                                    Class<?> aClassHeaders = invokeHeaders.getClass();
                                    Method sizeMethod = aClassHeaders.getDeclaredMethod("size");
                                    int invokeSize = (int) sizeMethod.invoke(invokeHeaders);
                                    for (int i = 0; i < invokeSize; i++) {
                                        Method nameMethod = aClassHeaders.getDeclaredMethod("name", int.class);
                                        String name = (String) nameMethod.invoke(invokeHeaders, i);
                                        String value = (String) aClassHeaders.getDeclaredMethod("value", int.class).invoke(invokeHeaders, i);
                                        if ("Content-Encoding".equalsIgnoreCase(name) && "gzip".equals(value)) {
                                            //标记下数据是否压缩
                                            isGzip = true;
                                        }
                                        netStr.append("\n");
                                        netStr.append(name).append(": ").append(value);
                                    }
                                }
                                Method bodyResponseMethod = aClassResponse.getDeclaredMethod("body");
                                Object invokeResponseBody = bodyResponseMethod.invoke(result);
                                Class<?> aClassResponseBody = invokeResponseBody.getClass();
                                Method sourceMethod = aClassResponseBody.getDeclaredMethod("source");
                                Object invokeBufferedSource = sourceMethod.invoke(invokeResponseBody);
                                //okio.e  BufferedSource
                                Class<?> aClassBufferedSource = invokeBufferedSource.getClass();
                                //b request
                                Method requestMethod;//request
                                try {
                                    requestMethod = aClassBufferedSource.getDeclaredMethod("request", long.class);//request
                                } catch (Exception e) {
                                    requestMethod = aClassBufferedSource.getDeclaredMethod("b", long.class);//request
                                }
                                requestMethod.invoke(invokeBufferedSource, Long.MAX_VALUE);
                                // c b() buffer
                                Method bufferMethod;//buffer
                                try {
                                    bufferMethod = aClassBufferedSource.getDeclaredMethod("buffer");//buffer
                                } catch (Exception e) {
                                    bufferMethod = aClassBufferedSource.getDeclaredMethod("b");//buffer
                                }
                                Object invokeBuffer = bufferMethod.invoke(invokeBufferedSource);
                                Class<?> aClassBuffer = invokeBuffer.getClass();
                                Method cloneMethod = aClassBuffer.getDeclaredMethod("clone");
                                Object invoke = cloneMethod.invoke(invokeBuffer);
                                Class<?> aClass = invoke.getClass();
                                Method readString = null;//readString
                                try {
                                    readString = aClass.getDeclaredMethod("readString", Charset.class);//readString
                                } catch (Exception e) {
                                    readString = aClass.getDeclaredMethod("a", Charset.class);//readString
                                }
                                String repResult = (String) readString.invoke(invoke, Charset.forName("UTF-8"));

                                netStr.append("\n");
                                netStr.append("rep result:").append(isGzip ? new String(uncompress(repResult.getBytes())) : repResult);
                            }

                            netStr.append("\n");
                            netStr.append(">>>> end net");

                            XpLog.INSTANCE.log(getTag(), netStr.toString());
                        }
                    });

这样一hook,我对所有app只要用okhttp我都能正常输出出入参数了,方便分析人家app,但是存在一个问题,出入的参数可能会加密或者压缩之类,这个就自己研究吧

最后会用到这2个方法

private boolean bodyEncoded(Object invokeHeaders) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        String contentEncoding = (String) invokeHeaders.getClass().getDeclaredMethod("get", String.class).invoke(invokeHeaders, "Content-Encoding");
        return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity");
    }

    public static byte[] uncompress(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
        try {
            GZIPInputStream ungzip = new GZIPInputStream(in);
            byte[] buffer = new byte[256];
            int n;
            while ((n = ungzip.read(buffer)) >= 0) {
                out.write(buffer, 0, n);
            }
            return out.toByteArray();
        } catch (Exception e) {
            //e.printStackTrace();
            return bytes;
        }
    }
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
要使用Xposed拦截OkHttp的URL请求,您需要编写一个Xposed模块。以下是一些大致步骤: 1. 创建一个Xposed模块项目 2. 在AndroidManifest.xml文件中声明您的模块作为Xposed模块 3. 在您的模块主类中,使用Xposed框架的“findAndHookMethod”方法来Hook OkHttp的“newCall”方法,并获取Request请求对象 4. 在获取请求对象后,您可以使用OkHttp的“url”方法获取请求的URL 5. 您可以在此处进行您的拦截逻辑,并阻止/修改请求,或者您可以将请求传递给原始的“newCall”方法 以下是一个简单的示例代码,演示如何使用Xposed拦截OkHttp的URL请求: ```java public class MyModule implements IXposedHookLoadPackage { @Override public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable { // Only hook into the package containing OkHttp if (lpparam.packageName.equals("com.squareup.okhttp")) { XposedHelpers.findAndHookMethod("com.squareup.okhttp.OkHttpClient", lpparam.classLoader, "newCall", Request.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { // Get the request object Request request = (Request) param.args[0]; // Get the URL from the request object String url = request.url().toString(); // Do your interception logic here // ... // Call the original method super.beforeHookedMethod(param); } }); } } } ``` 请注意,这只是一个简单的示例代码,并且您需要根据您的需求进行自定义。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落后程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值