function

  • Print URL, header, requestBody, repsonseBody, etc
  • Modify the request. modify the URL, requestBody, header, etc., before the request is signed

background

[Demand] Simulation of black production, in the broker app at the end of the uploading of real video “stealthing”. For example, the previous shooting process was carried out in a orderly manner, and the video link prepared by myself was changed to upload at last. [Difficulty] If the network request is modified directly with software like Charles, the signing will fail.

Common signing practice

A common way to sign a Query Params(GET Request) or Request Body(POST Request) is to generate a signature based on an encryption rule and then add the signature to the header. After receiving the request, the back-end server generates a signature based on the same encryption rules. If the signature generated by the back-end is inconsistent with that provided by the front-end, the request is tampered with. For example,

  1. Client initiating
Request Header Request Body
sign: aaa {“uid”: “123”}

Ps: Where did AAA come from? {“uid”: “123”}+ salt is encrypted in c code.

  1. Charles rewrite Request Body

  2. The server receives

Request Header Request Body
sign: aaa {“uid”: “456”}
  1. The service side

Encrypt {“uid”: “456”}+salt(as with the client, this is agreed) to get CCC. Description CCC and AAA in header are inconsistent, and the request is rejected.

Change package way of thinking

Typically, signatures are placed into the OkHttp Interceptor for execution. So we just need to change the request before we sign it.

Review the OkHttp construction

    val builder = OkHttpClient.Builder().addInterceptor(...)
    val okHttpClient = builder.build()
Copy the code

Get the first user-defined interceptor and hook it into the Intercept method before the build method is implemented.

hookFun(interceptorName, clsLoader, "intercept"."okhttp3.Interceptor.Chain".object : MethodHookCallback() {
            override fun before(param: MethodHookParam) {
                // Modify the request
            }

            override fun after(param: MethodHookParam) {
                // Print the request}}Copy the code

The final print effect is as follows

The 2021-03-09 11:38:29. 242, 29940-11947 / com. Anjuke. Android. Newbroker I/Xposed: Daishuwen = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Start = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 2021-03-09 11:38:29. 242 29940-11947/com.anjuke.android.newbroker D/Xposed: Daishuwen = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Request = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 2021-03-09 11:38:29. 242 29940-11947/com.anjuke.android.newbroker V/Xposed: daishuwen request = Request{method=POST, url=https://api.anjuke.com/mobile-ajk-broker/3.0/wos/token?app=a-broker&_pid=29940&brokerId=1389002&fingerDeviceId=d2701 87be2274c98b45048fb64505b89&macid=4ad8e981a19c81c5&i=4ad8e981a19c81c5&m=Android-IN2010&uuid=9e70642a-7f95-4c08-8a7a-e02a 67606832&sesrid=bc39d5ac-f823-4369-91a9-c8d5c1ee56b3&o=OnePlus8-user+11+++release-keys&token=08593cb4762b0376daf5b3bef5a Fb68310d270187b & qtime = 20210309113829 & CV = 9.21 & v = 11 & the from = mobile&pm = debug & _chat_id = 0, Tags = {}} 11:38:29. 2021-03-09 242 29940-11947 / com. Anjuke. Android. Newbroker V/Xposed: daishuwen requestHeaders = sig: 321cb7c1931f135d090ccb7d96c891d2 body_md5: 31dbe576b28bc00332efe59be300ae1b Accept: application/json nsign: 1000deacd9efb7d47864e32170108400a093015d0011002d23a48d9d AuthToken: 08593cb4762b0376daf5b3bef5afb68310d270187b key: ee20a1640951687026333adf2083d9b1 get_md5: eec1404d553a4620ec2a7ad7389b184f015d0000 nsign_uuid: 23a48d9d-d491-400b-9f0e-62ef5e57614b User-Agent: :d270187be2274c98b45048fb64505b89 Content-Type: application/json; charset=utf-8 Content-Length: 45 Host: api.anjuke.com Connection: Keep-Alive Accept-Encoding: Gzip 11:38:29. 2021-03-09, 242, 29940-11947 / com. Anjuke. Android. Newbroker D/Xposed: Daishuwen = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = the Request Body = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 2021-03-09 11:38:29. 242 29940-11947/com.anjuke.android.newbroker V/Xposed: Daishuwen requestBody = {"brokerId":"1389002","type":"zhiNengShiKan"} 2021-03-09 11:38:29.242 29940-11947/com.anjuke.android.newbroker D/Xposed: Daishuwen = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Response = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 2021-03-09 11:38:29. 242 29940-11947/com.anjuke.android.newbroker V/Xposed: daishuwen response = Response{protocol=h2, code=200, message=, url=https://api.anjuke.com/mobile-ajk-broker/3.0/wos/token?app=a-broker&_pid=29940&brokerId=1389002&fingerDeviceId=d2701 87be2274c98b45048fb64505b89&macid=4ad8e981a19c81c5&i=4ad8e981a19c81c5&m=Android-IN2010&uuid=9e70642a-7f95-4c08-8a7a-e02a 67606832&sesrid=bc39d5ac-f823-4369-91a9-c8d5c1ee56b3&o=OnePlus8-user+11+++release-keys&token=08593cb4762b0376daf5b3bef5a Fb68310d270187b & qtime = 20210309113829 & CV = 9.21 & v = 11 & the from = mobile&pm = debug & _chat_id = 0} 11:38:29 2021-03-09. 247 29940-11947/com.anjuke.android.newbroker V/Xposed: daishuwen contentType = application/json; Charset = utf-8} 11:38:29 2021-03-09. 247. 29940-11947 / com anjuke. Android. Newbroker V/Xposed: daishuwen responseBody = {"status":"ok","data":{"token":"cmZpNFVlYnV5dzlGTEFCZ2lCVld4WVBsei9jPTplPTE2MTUyNjE3MDgmZj0xNjE1MjYxMTA4NDcxNy5tcDQmcj0y NTA3Mjc3MTU=","expired":"1615261708","file_id":"16152611084717.mp4","path":"\/16152611084717.mp4","bucket":"intelljaudit video","app_id":"mqkJiHgFmsu","wos_url":"https:\/\/wosajk1.anjukestatic.com","type":"zhiNengShiKan"}}} 2021-03-09 11:38:29. 247, 29940-11947 / com. Anjuke. Android. Newbroker I/Xposed: daishuwen ================================ End ================================Copy the code

Problems encountered & Solving problems

  1. I tried to integrate Okhttp into Xpose project, then write a Request Body, and then replace the Request Body written in Xpose project with the Request Body in app. The result is not feasible because although the two RequestBody classes are the same, they are loaded by different classloaders and do not know each other. All classes are not common except those in the JDK. The final implementation is performed by reflection.

  2. When printing the ResponseBody, try calling okHttp3. ResponseBody#string directly. This can print the ResponseBody normally, but the app will not get the Response. The reason is that the buffer is closed after the method completes execution.

package okhttp3;
public abstract class ResponseBody implements Closeable {
    public final String string(a) throws IOException {
        BufferedSource source = this.source();

        String var3;
        try {
            Charset charset = Util.bomAwareCharset(source, this.charset());
            var3 = source.readString(charset);
        } finally {
            Util.closeQuietly(source);
        }
        returnvar3; }}Copy the code

Httploggingtor: Httploggingtor: Httploggingtor: Httploggingtor: Httploggingtor: Httploggingtor: Httploggingtor: Httploggingtor: Httploggingtor

    logD("responseBody: ${String(bytes, getCharset(contentType))}")
    val responseBody = XposedHelpers.callStaticMethod("okhttp3.ResponseBody".toClass(clsLoader), "create", contentType, bytes)
Copy the code

conclusion

  1. Find the location where OkhttpClient was created, find the first interceptor, and hook it to the Intercept method, which executes the modification request and print the request.
  2. The Xpose Module operates on objects in the app (except JDK built-in objects) only by reflection. Xpose author’s reply
  3. This code is only for apps that use Okhttp.
  4. code