环境准备
- 使用 ApkScan-PKID.jar 查看是否加壳
- pip install frida
- pip install frida-tools
- 安装加固过的apk到手机
- adb devices 查看系统版本 然后 https://github.com/frida/frida/releases 处下载对应版本的 frida-server 需要和frida版本一样
- adb push frida-server-16.1.3-android-x86 /data/local/tmp/frida-server #解压下载的文件到安卓指定目录
- adb shell #进入安卓交互shell
- cd /data/local/tmp/frida-server
- chmod 777 frida-server
- ./frida-server
- 新开一个窗口执行下面的流程
- adb forward tcp:27042 tcp:27042 or adb forward tcp:27043 tcp:27043
- frida-ps -U -a #查看是否存在frida-server进程,同时查看包名
脱壳
frida-dexdump
- pip install frida-dexdump #安装脱壳库
- frida-dexdump -U -f APP包名 #指定包名脱壳 或者 frida-dexdump -FU #直接注入前台应用
- 使用下面的python代码批量Jadx反编译
import os g = os.walk("C:/hh/Python/MosTalk") for path, dir_list, file_list in g: for file_name in file_list: if file_name.endswith(('.dex')): _path = os.path.join(path, file_name) commd = "C:/hh/tools/jadx-1.3.3/bin/jadx -d pkg_output -j 4 " + _path print(commd) os.system(commd)
frida-dexdump 缺点
frida-dexdump操作非常简单,从复现路径就能看出来,但是也存在局限性。
- 不支持所有DEX文件版本:
frida-dexdump
只支持部分DEX文件版本,不能支持所有的DEX文件版本。如果解析的DEX文件版本不受支持,将出现解析错误或异常。 - 不支持自定义解析:
frida-dexdump
提供的解析方式是固定的,不支持自定义解析。如果需要解析特定的DEX文件结构或数据,需要编写自己的解析工具或脚本。(可以自己魔改agent.js) - 无法处理加固或混淆的DEX文件:
frida-dexdump
仅能解析未加固或未混淆的DEX文件,对于加固或混淆的DEX文件,将无法解析或解析出错误的结果。 - 不支持DEX文件的修改和重打包:
frida-dexdump
仅能解析DEX文件的结构和数据,不能对DEX文件进行修改或重打包。如果需要修改或重打包DEX文件,需要使用其他工具或编写自己的工具。
frida-dexdump 脱壳法结果
不过我这里最新的360加固脱壳失败,dex恢复出来全是空的。
frida-unpack
frida-unpack 下载地址:https://github.com/dstmath/frida-unpack
我这里图方便只下载了他的 OpenMemory.js HOOK脚本
执行 frida -U -l C:/hh/tools/frida/OpenMemory.js -f 包名 --no-pause
脱下来的dex保存在 /data/data/包名/*.dex 处
adb pull /data/data/包名 C:\hh\tools\frida\com.paopaotalk.im #将手机中的指定目录搬到电脑指定目录
然后在用上面的那个python代码批量Jadx反编译即可。
这里我测试最新的360加固成功脱壳,非常Nice
其他方式
Frida-Apk-Unpack: https://github.com/GuoQiang1993/Frida-Apk-Unpack
Frida-Android-unpack: https://github.com/xiaokanghub/Frida-Android-unpack
objection笔记
pip install objection #安装
objection -g 包名或者PID explore# 注入进程
#hook类,可以打印到类下有哪些方法在执行
android hooking watch class 类 --dump-args --dump-backtrace --dump-return
#hook方法,可以打印方法执行的详细过程
android hooking watch class_method 方法 --dump-args --dump-backtrace --dump-return
还有许多别的使用方法,我这里就不过多阐述了,就只记录我用的最多的。
frida hook 脚本
网传的加密过程自吐脚本
他将许多常见主流的加密方式汇总在里面,如 DES\AES 等,可以直接得到填充模式以及密钥和IV,非常酷的脚本
var N_ENCRYPT_MODE = 1 var N_DECRYPT_MODE = 2 function showStacks() { var Exception = Java.use("java.lang.Exception"); var ins = Exception.$new("Exception"); var straces = ins.getStackTrace(); if (undefined == straces || null == straces) { return; } console.log("============================= Stack strat======================="); console.log(""); for (var i = 0; i < straces.length; i++) { var str = " " + straces[i].toString(); console.log(str); } console.log(""); console.log("============================= Stack end=======================\r\n"); Exception.$dispose(); } //工具相关函数 var base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', base64DecodeChars = new Array((-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), 62, (-1), (-1), (-1), 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, (-1), (-1), (-1), (-1), (-1), (-1), (-1), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, (-1), (-1), (-1), (-1), (-1), (-1), 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, (-1), (-1), (-1), (-1), (-1)); function stringToBase64(e) { var r, a, c, h, o, t; for (c = e.length, a = 0, r = ''; a < c;) { if (h = 255 & e.charCodeAt(a++), a == c) { r += base64EncodeChars.charAt(h >> 2), r += base64EncodeChars.charAt((3 & h) << 4), r += '=='; break } if (o = e.charCodeAt(a++), a == c) { r += base64EncodeChars.charAt(h >> 2), r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4), r += base64EncodeChars.charAt((15 & o) << 2), r += '='; break } t = e.charCodeAt(a++), r += base64EncodeChars.charAt(h >> 2), r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4), r += base64EncodeChars.charAt((15 & o) << 2 | (192 & t) >> 6), r += base64EncodeChars.charAt(63 & t) } return r } function base64ToString(e) { var r, a, c, h, o, t, d; for (t = e.length, o = 0, d = ''; o < t;) { do r = base64DecodeChars[255 & e.charCodeAt(o++)]; while (o < t && r == -1); if (r == -1) break; do a = base64DecodeChars[255 & e.charCodeAt(o++)]; while (o < t && a == -1); if (a == -1) break; d += String.fromCharCode(r << 2 | (48 & a) >> 4); do { if (c = 255 & e.charCodeAt(o++), 61 == c) return d; c = base64DecodeChars[c] } while (o < t && c == -1); if (c == -1) break; d += String.fromCharCode((15 & a) << 4 | (60 & c) >> 2); do { if (h = 255 & e.charCodeAt(o++), 61 == h) return d; h = base64DecodeChars[h] } while (o < t && h == -1); if (h == -1) break; d += String.fromCharCode((3 & c) << 6 | h) } return d } function hexToBase64(str) { return base64Encode(String.fromCharCode.apply(null, str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" "))); } function base64ToHex(str) { for (var i = 0, bin = base64Decode(str.replace(/[ \r\n]+$/, "")), hex = []; i < bin.length; ++i) { var tmp = bin.charCodeAt(i).toString(16); if (tmp.length === 1) tmp = "0" + tmp; hex[hex.length] = tmp; } return hex.join(""); } function hexToBytes(str) { var pos = 0; var len = str.length; if (len % 2 != 0) { return null; } len /= 2; var hexA = new Array(); for (var i = 0; i < len; i++) { var s = str.substr(pos, 2); var v = parseInt(s, 16); hexA.push(v); pos += 2; } return hexA; } function bytesToHex(arr) { var str = ''; var k, j; for (var i = 0; i < arr.length; i++) { k = arr[i]; j = k; if (k < 0) { j = k + 256; } if (j < 16) { str += "0"; } str += j.toString(16); } return str; } function stringToHex(str) { var val = ""; for (var i = 0; i < str.length; i++) { if (val == "") val = str.charCodeAt(i).toString(16); else val += str.charCodeAt(i).toString(16); } return val } function stringToBytes(str) { var ch, st, re = []; for (var i = 0; i < str.length; i++) { ch = str.charCodeAt(i); st = []; do { st.push(ch & 0xFF); ch = ch >> 8; } while (ch); re = re.concat(st.reverse()); } return re; } //将byte[]转成String的方法 function bytesToString(arr) { var str = ''; arr = new Uint8Array(arr); for (var i in arr) { str += String.fromCharCode(arr[i]); } return str; } function bytesToBase64(e) { var r, a, c, h, o, t; for (c = e.length, a = 0, r = ''; a < c;) { if (h = 255 & e[a++], a == c) { r += base64EncodeChars.charAt(h >> 2), r += base64EncodeChars.charAt((3 & h) << 4), r += '=='; break } if (o = e[a++], a == c) { r += base64EncodeChars.charAt(h >> 2), r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4), r += base64EncodeChars.charAt((15 & o) << 2), r += '='; break } t = e[a++], r += base64EncodeChars.charAt(h >> 2), r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4), r += base64EncodeChars.charAt((15 & o) << 2 | (192 & t) >> 6), r += base64EncodeChars.charAt(63 & t) } return r } function base64ToBytes(e) { var r, a, c, h, o, t, d; for (t = e.length, o = 0, d = []; o < t;) { do r = base64DecodeChars[255 & e.charCodeAt(o++)]; while (o < t && r == -1); if (r == -1) break; do a = base64DecodeChars[255 & e.charCodeAt(o++)]; while (o < t && a == -1); if (a == -1) break; d.push(r << 2 | (48 & a) >> 4); do { if (c = 255 & e.charCodeAt(o++), 61 == c) return d; c = base64DecodeChars[c] } while (o < t && c == -1); if (c == -1) break; d.push((15 & a) << 4 | (60 & c) >> 2); do { if (h = 255 & e.charCodeAt(o++), 61 == h) return d; h = base64DecodeChars[h] } while (o < t && h == -1); if (h == -1) break; d.push((3 & c) << 6 | h) } return d } //stringToBase64 stringToHex stringToBytes //base64ToString base64ToHex base64ToBytes // hexToBase64 hexToBytes // bytesToBase64 bytesToHex bytesToString Java.perform(function () { var secretKeySpec = Java.use('javax.crypto.spec.SecretKeySpec'); secretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function (a, b) { showStacks(); var result = this.$init(a, b); console.log("======================================"); console.log("算法名:" + b + "|str密钥:" + bytesToString(a)); console.log("算法名:" + b + "|Hex密钥:" + bytesToHex(a)); return result; } var DESKeySpec = Java.use('javax.crypto.spec.DESKeySpec'); DESKeySpec.$init.overload('[B').implementation = function (a) { showStacks(); var result = this.$init(a); console.log("======================================"); var bytes_key_des = this.getKey(); console.log("des密钥 |str " + bytesToString(bytes_key_des)); console.log("des密钥 |hex " + bytesToHex(bytes_key_des)); return result; } DESKeySpec.$init.overload('[B', 'int').implementation = function (a, b) { showStacks(); var result = this.$init(a, b); console.log("======================================"); var bytes_key_des = this.getKey(); console.log("des密钥 |str " + bytesToString(bytes_key_des)); console.log("des密钥 |hex " + bytesToHex(bytes_key_des)); return result; } var mac = Java.use('javax.crypto.Mac'); mac.getInstance.overload('java.lang.String').implementation = function (a) { showStacks(); var result = this.getInstance(a); console.log("======================================"); console.log("算法名:" + a); return result; } mac.update.overload('[B').implementation = function (a) { //showStacks(); this.update(a); console.log("======================================"); console.log("update:" + bytesToString(a)) } mac.update.overload('[B', 'int', 'int').implementation = function (a, b, c) { //showStacks(); this.update(a, b, c) console.log("======================================"); console.log("update:" + bytesToString(a) + "|" + b + "|" + c); } mac.doFinal.overload().implementation = function () { //showStacks(); var result = this.doFinal(); console.log("======================================"); console.log("doFinal结果: |str :" + bytesToString(result)); console.log("doFinal结果: |hex :" + bytesToHex(result)); console.log("doFinal结果: |base64 :" + bytesToBase64(result)); return result; } mac.doFinal.overload('[B').implementation = function (a) { //showStacks(); var result = this.doFinal(a); console.log("======================================"); console.log("doFinal参数: |str :" + bytesToString(a)); console.log("doFinal参数: |hex :" + bytesToHex(a)); console.log("doFinal结果: |str :" + bytesToString(result)); console.log("doFinal结果: |hex :" + bytesToHex(result)); console.log("doFinal结果: |base64 :" + bytesToBase64(result)); return result; } var md = Java.use('java.security.MessageDigest'); md.getInstance.overload('java.lang.String', 'java.lang.String').implementation = function (a, b) { //showStacks(); console.log("======================================"); console.log("算法名:" + a); return this.getInstance(a, b); } md.getInstance.overload('java.lang.String').implementation = function (a) { //showStacks(); console.log("======================================"); console.log("算法名:" + a); return this.getInstance(a); } md.update.overload('[B').implementation = function (a) { //showStacks(); console.log("======================================"); console.log("update:" + bytesToString(a)) return this.update(a); } md.update.overload('[B', 'int', 'int').implementation = function (a, b, c) { //showStacks(); console.log("======================================"); console.log("update:" + bytesToString(a) + "|" + b + "|" + c); return this.update(a, b, c); } md.digest.overload().implementation = function () { //showStacks(); console.log("======================================"); var result = this.digest(); console.log("digest结果 |hex:" + bytesToHex(result)); console.log("digest结果 |base64:" + bytesToBase64(result)); return result; } md.digest.overload('[B').implementation = function (a) { //showStacks(); console.log("======================================"); console.log("digest参数 |str:" + bytesToString(a)); console.log("digest参数 |hex:" + bytesToHex(a)); var result = this.digest(a); console.log("digest结果: |hex" + bytesToHex(result)); console.log("digest结果: |base64" + bytesToBase64(result)); return result; } var ivParameterSpec = Java.use('javax.crypto.spec.IvParameterSpec'); ivParameterSpec.$init.overload('[B').implementation = function (a) { //showStacks(); var result = this.$init(a); console.log("======================================"); console.log("iv向量: |str:" + bytesToString(a)); console.log("iv向量: |hex:" + bytesToHex(a)); return result; } var cipher = Java.use('javax.crypto.Cipher'); cipher.getInstance.overload('java.lang.String').implementation = function (a) { //showStacks(); var result = this.getInstance(a); console.log("======================================"); console.log("模式填充:" + a); return result; } cipher.init.overload('int', 'java.security.Key').implementation = function (a, b) { //showStacks(); var result = this.init(a, b); console.log("======================================"); if (N_ENCRYPT_MODE == a) { console.log("init | 加密模式"); } else if(N_DECRYPT_MODE == a) { console.log("init | 解密模式"); } var bytes_key = b.getEncoded(); console.log("init key:" + "|str密钥:" + bytesToString(bytes_key)); console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key)); return result; } cipher.init.overload('int', 'java.security.cert.Certificate').implementation = function (a, b) { //showStacks(); var result = this.init(a, b); console.log("======================================"); if (N_ENCRYPT_MODE == a) { console.log("init | 加密模式"); } else if(N_DECRYPT_MODE == a) { console.log("init | 解密模式"); } return result; } cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation = function (a, b, c) { //showStacks(); var result = this.init(a, b, c); console.log("======================================"); if (N_ENCRYPT_MODE == a) { console.log("init | 加密模式"); } else if(N_DECRYPT_MODE == a) { console.log("init | 解密模式"); } var bytes_key = b.getEncoded(); console.log("init key:" + "|str密钥:" + bytesToString(bytes_key)); console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key)); return result; } cipher.init.overload('int', 'java.security.cert.Certificate', 'java.security.SecureRandom').implementation = function (a, b, c) { //showStacks(); var result = this.init(a, b, c); if (N_ENCRYPT_MODE == a) { console.log("init | 加密模式"); } else if(N_DECRYPT_MODE == a) { console.log("init | 解密模式"); } return result; } cipher.init.overload('int', 'java.security.Key', 'java.security.SecureRandom').implementation = function (a, b, c) { //showStacks(); var result = this.init(a, b, c); if (N_ENCRYPT_MODE == a) { console.log("init | 加密模式"); } else if(N_DECRYPT_MODE == a) { console.log("init | 解密模式"); } var bytes_key = b.getEncoded(); console.log("init key:" + "|str密钥:" + bytesToString(bytes_key)); console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key)); return result; } cipher.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters').implementation = function (a, b, c) { //showStacks(); var result = this.init(a, b, c); if (N_ENCRYPT_MODE == a) { console.log("init | 加密模式"); } else if(N_DECRYPT_MODE == a) { console.log("init | 解密模式"); } var bytes_key = b.getEncoded(); console.log("init key:" + "|str密钥:" + bytesToString(bytes_key)); console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key)); return result; } cipher.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters', 'java.security.SecureRandom').implementation = function (a, b, c, d) { //showStacks(); var result = this.init(a, b, c, d); if (N_ENCRYPT_MODE == a) { console.log("init | 加密模式"); } else if(N_DECRYPT_MODE == a) { console.log("init | 解密模式"); } var bytes_key = b.getEncoded(); console.log("init key:" + "|str密钥:" + bytesToString(bytes_key)); console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key)); return result; } cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec', 'java.security.SecureRandom').implementation = function (a, b, c, d) { //showStacks(); var result = this.init(a, b, c, d); if (N_ENCRYPT_MODE == a) { console.log("init | 加密模式"); } else if(N_DECRYPT_MODE == a) { console.log("init | 解密模式"); } var bytes_key = b.getEncoded(); console.log("init key:" + "|str密钥:" + bytesToString(bytes_key)); console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key)); return result; } cipher.update.overload('[B').implementation = function (a) { //showStacks(); var result = this.update(a); console.log("======================================"); console.log("update:" + bytesToString(a)); return result; } cipher.update.overload('[B', 'int', 'int').implementation = function (a, b, c) { //showStacks(); var result = this.update(a, b, c); console.log("======================================"); console.log("update:" + bytesToString(a) + "|" + b + "|" + c); return result; } cipher.doFinal.overload().implementation = function () { //showStacks(); var result = this.doFinal(); console.log("======================================"); console.log("doFinal结果: |str :" + bytesToString(result)); console.log("doFinal结果: |hex :" + bytesToHex(result)); console.log("doFinal结果: |base64 :" + bytesToBase64(result)); return result; } cipher.doFinal.overload('[B').implementation = function (a) { //showStacks(); var result = this.doFinal(a); console.log("======================================"); console.log("doFinal参数: |str :" + bytesToString(a)); console.log("doFinal参数: |hex :" + bytesToHex(a)); console.log("doFinal结果: |str :" + bytesToString(result)); console.log("doFinal结果: |hex :" + bytesToHex(result)); console.log("doFinal结果: |base64 :" + bytesToBase64(result)); return result; } var x509EncodedKeySpec = Java.use('java.security.spec.X509EncodedKeySpec'); x509EncodedKeySpec.$init.overload('[B').implementation = function (a) { //showStacks(); var result = this.$init(a); console.log("======================================"); console.log("RSA密钥:" + bytesToBase64(a)); return result; } var rSAPublicKeySpec = Java.use('java.security.spec.RSAPublicKeySpec'); rSAPublicKeySpec.$init.overload('java.math.BigInteger', 'java.math.BigInteger').implementation = function (a, b) { //showStacks(); var result = this.$init(a, b); console.log("======================================"); //console.log("RSA密钥:" + bytesToBase64(a)); console.log("RSA密钥N:" + a.toString(16)); console.log("RSA密钥E:" + b.toString(16)); return result; } var KeyPairGenerator = Java.use('java.security.KeyPairGenerator'); KeyPairGenerator.generateKeyPair.implementation = function () { //showStacks(); var result = this.generateKeyPair(); console.log("======================================"); var str_private = result.getPrivate().getEncoded(); var str_public = result.getPublic().getEncoded(); console.log("公钥 |hex" + bytesToHex(str_public)); console.log("私钥 |hex" + bytesToHex(str_private)); return result; } KeyPairGenerator.genKeyPair.implementation = function () { //showStacks(); var result = this.genKeyPair(); console.log("======================================"); var str_private = result.getPrivate().getEncoded(); var str_public = result.getPublic().getEncoded(); console.log("公钥 |hex" + bytesToHex(str_public)); console.log("私钥 |hex" + bytesToHex(str_private)); return result; } });
okhttp3 抓包脚本
function hook_okhttp3(classLoader) { Java.perform(function () { var ByteString = classLoader.use("com.android.okhttp.okio.ByteString"); var Buffer = classLoader.use("com.android.okhttp.okio.Buffer"); var Interceptor = classLoader.use("okhttp3.Interceptor"); var MyInterceptor = Java.registerClass({ name: "okhttp3.MyInterceptor", implements: [Interceptor], methods: { intercept: function (chain) { var request = chain.request(); try { console.log("MyInterceptor.intercept onEnter:", request, "\nrequest headers:\n", request.headers()); var requestBody = request.body(); var contentLength = requestBody ? requestBody.contentLength() : 0; if (contentLength > 0) { var BufferObj = Buffer.$new(); requestBody.writeTo(BufferObj); try { console.log("\nrequest body String:\n", BufferObj.readString(), "\n"); } catch (error) { try { console.log("\nrequest body ByteString:\n", ByteString.of(BufferObj.readByteArray()).hex(), "\n"); } catch (error) { console.log("error 1:", error); } } } } catch (error) { console.log("error 2:", error); } var response = chain.proceed(request); try { console.log("MyInterceptor.intercept onLeave:", response, "\nresponse headers:\n", response.headers()); var responseBody = response.body(); var contentLength = responseBody ? responseBody.contentLength() : 0; if (contentLength > 0) { console.log("\nresponsecontentLength:", contentLength, "responseBody:", responseBody, "\n"); var ContentType = response.headers().get("Content-Type"); console.log("ContentType:", ContentType); if (ContentType.indexOf("video") == -1) { if (ContentType.indexOf("application") == 0) { var source = responseBody.source(); if (ContentType.indexOf("application/zip") != 0) { try { console.log("\nresponse.body StringClass\n", source.readUtf8(), "\n"); } catch (error) { try { console.log("\nresponse.body ByteString\n", source.readByteString().hex(), "\n"); } catch (error) { console.log("error 4:", error); } } } } } } } catch (error) { console.log("error 3:", error); } return response; } } }); var ArrayList = classLoader.use("java.util.ArrayList"); var OkHttpClient = classLoader.use("okhttp3.OkHttpClient"); console.log(OkHttpClient); OkHttpClient.$init.overload('okhttp3.OkHttpClient$Builder').implementation = function (Builder) { console.log("OkHttpClient.$init:", this, Java.cast(Builder.interceptors(), ArrayList)); this.$init(Builder); }; var MyInterceptorObj = MyInterceptor.$new(); var Builder = classLoader.use("okhttp3.OkHttpClient$Builder"); console.log(Builder); Builder.build.implementation = function () { this.interceptors().clear(); //var MyInterceptorObj = MyInterceptor.$new(); this.interceptors().add(MyInterceptorObj); var result = this.build(); return result; }; Builder.addInterceptor.implementation = function (interceptor) { this.interceptors().clear(); //var MyInterceptorObj = MyInterceptor.$new(); this.interceptors().add(MyInterceptorObj); return this; //return this.addInterceptor(interceptor); }; console.log("hook_okhttp3..."); }); } Java.perform(function() { var application = Java.use("android.app.Application"); application.attach.overload('android.content.Context').implementation = function(context) { var result = this.attach(context); // 先执行原来的attach方法 var classloader = context.getClassLoader(); // 获取classloader Java.classFactory.loader = classloader; hook_okhttp3(Java.classFactory); } });
本文作者为Mr.Wu,转载请注明,尊守博主劳动成果!
由于经常折腾代码,可能会导致个别文章内容显示错位或者别的 BUG 影响阅读; 如发现请在该文章下留言告知于我,thank you !