├── 360getString.js ├── BinderProxy.js ├── HashMap.js ├── JNI_OnLoad.js ├── MT.js ├── NIMClient.js ├── README.md ├── System.loadLibrary-libmsaoaidsec.js ├── System.loadLibrary.js ├── ZenTracer.js ├── all_classes.js ├── android-make-toast.js ├── app └── bili │ ├── 7.26.1.js │ ├── 7.76.0.js │ ├── bypass_bilibili.js │ ├── create_fake_pthread_create.js │ ├── hook_pthread_create-7.26.1.js │ ├── replace_init_proc-7.26.1.js │ └── replace_init_proc-7.76.0.js ├── byPassSSL.js ├── byteDance.js ├── call_constructors.js ├── clone.js ├── cn.zhilianda.photo.scanner.pro.js ├── cocos2djs-xxtea-key.js ├── dlopen-replace.js ├── dlopen.js ├── dlsym.js ├── dump-so.js ├── dump_so.js ├── dygod.js ├── hookUrl.js ├── hook_RegisterNatives.js ├── hook_okhttp3.js ├── hook_reg.js ├── init_array.js ├── javaEnc.js ├── jni ├── RegisterNatives.js ├── hook_RegisterNatives.js ├── hook_RegisterNatives2.js ├── hook_art.js ├── hook_artmethod.js └── jni.md ├── just_trust_me.js ├── just_trust_me_okhttp_hook_finder.js ├── loadClass.js ├── nimlibChatAttachParser.js ├── open.js ├── privacy.js ├── pthread_create-fake.js ├── pthread_create-replace-jni.js ├── pthread_create.js ├── r0capture.js ├── replaceBaiduAPI_KEY.js ├── replaceInitProcAndPatch.js ├── replaceM3U8url.js ├── stalker-decode.js ├── stalker-template.js ├── stalker.js └── webview-load-url.js /360getString.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | 360加固字符串解密 4 | */ 5 | 6 | function decode360String(s){ 7 | if (!s) { 8 | console.log('请输入360加固的密文字符串!'); 9 | return; 10 | } 11 | 12 | Java.perform(function(){ 13 | var Class = Java.use('com.stub.StubApp'); 14 | var result = Class.getString2(s); 15 | console.log('解密结果: ' + result); 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /BinderProxy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * refer: https://codeshare.frida.re/@dvdface/trace-android-binder-call-from-binderproxy/ 3 | */ 4 | function hookBinderProxy() { 5 | // used to add trace 6 | const Trace = Java.use('android.os.Trace'); 7 | // used to get callstack 8 | const Thread = Java.use('java.lang.Thread'); 9 | // used to hook binder call from binder proxy 10 | const BinderProxy = Java.use('android.os.BinderProxy'); 11 | // hook transact of BinderProxy 12 | BinderProxy.transact.implementation = function (...args) { 13 | // get callstacks 14 | const stacktrace = Thread.currentThread().getStackTrace(); 15 | // the binder call is in the 4th line 16 | const callingStack = stacktrace[3]; 17 | // begin trace 18 | Trace.beginSection(callingStack.toString()); 19 | // call 20 | var result = this.transact(...args); 21 | // end trace 22 | Trace.endSection(); 23 | // return 24 | return result; 25 | } 26 | } 27 | 28 | // 打印堆栈 29 | function printStack() { 30 | Java.perform(function () { 31 | console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())); 32 | }); 33 | } 34 | 35 | function hook() { 36 | Java.perform(function () { 37 | hookBinderProxy(); 38 | }); 39 | } 40 | 41 | setImmediate(hook()); -------------------------------------------------------------------------------- /HashMap.js: -------------------------------------------------------------------------------- 1 | Java.perform(function(){ 2 | var textUtils = Java.use("android.text.TextUtils") 3 | textUtils.isEmpty.overload('java.lang.CharSequence').implementation=function(a) { 4 | printStack(); 5 | console.log("TextUtils.isEmpty: " + a); 6 | return this.isEmpty(a); 7 | }; 8 | 9 | var hashMap = Java.use("java.util.HashMap");//HOOK系统函数HashMap去实现打印 10 | hashMap.put.implementation = function(key, value) { 11 | console.log("HashMap.put key: " + key + " value: " + value); 12 | if(key.equals("targetKey")){ 13 | printStack(); 14 | } 15 | return this.put(key, value); 16 | } 17 | }); 18 | 19 | function printStack() { 20 | Java.perform(function () { 21 | console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())); 22 | }); 23 | } -------------------------------------------------------------------------------- /JNI_OnLoad.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Frida Hook 脚本:监控动态库加载并 Hook `JNI_OnLoad` 3 | * 4 | * 目标: 5 | * - 监视 `dlopen` 和 `android_dlopen_ext`,检测目标so是否被加载。 6 | * - 在库加载后,尝试找到并 Hook 其 `JNI_OnLoad` 函数。 7 | * - 提供调试日志,便于分析动态库的加载及其 `JNI_OnLoad` 调用情况。 8 | * 9 | * 说明: 10 | * - `dlopen` 和 `android_dlopen_ext` 用于动态加载 `.so` 库,Hook 这些函数可拦截库的加载过程。 11 | * - `JNI_OnLoad` 是 JNI(Java Native Interface)中初始化的入口点,许多 Android 应用在此执行 native 代码初始化。 12 | * - `Module.findExportByName` 用于查找导出符号,如果找不到 `JNI_OnLoad`,则遍历所有符号进行匹配。 13 | */ 14 | 15 | const TARGET_LIB_NAME = "libmsaoaidsec.so"; 16 | 17 | function hook_JNI_OnLoad() { 18 | // 先尝试通过 `findExportByName` 19 | let jniOnLoad = Module.findExportByName(TARGET_LIB_NAME, "JNI_OnLoad"); 20 | 21 | // 如果找不到,就遍历所有导出符号 22 | if (!jniOnLoad) { 23 | console.log("[Info] `JNI_OnLoad` 未导出,尝试遍历导出符号..."); 24 | for (let symbol of module.enumerateSymbols()) { 25 | if (symbol.name.indexOf("JNI_OnLoad") >= 0) { 26 | jniOnLoad = symbol.address; 27 | console.log("[Success] 找到 JNI_OnLoad: ", jniOnLoad); 28 | break; 29 | } 30 | } 31 | } 32 | 33 | if (!jniOnLoad) { 34 | console.error("[Error] 未找到 `JNI_OnLoad` 函数"); 35 | return; 36 | } 37 | 38 | // Hook `JNI_OnLoad` 39 | Interceptor.attach(jniOnLoad, { 40 | onEnter(args) { 41 | console.log("[Hooked] JNI_OnLoad 被调用"); 42 | } 43 | }); 44 | } 45 | 46 | function hook_dlopen() { 47 | ["android_dlopen_ext", "dlopen"].forEach(funcName => { 48 | let addr = Module.findExportByName(null, funcName); 49 | if (addr) { 50 | Interceptor.attach(addr, { 51 | onEnter(args) { 52 | let libName = ptr(args[0]).readCString(); 53 | if (libName && libName.indexOf(TARGET_LIB_NAME) >= 0) { 54 | this.is_can_hook = true; 55 | console.log(`[+] ${funcName} onEnter: ${libName}`); 56 | } 57 | }, 58 | onLeave: function (retval) { 59 | if (this.is_can_hook) { 60 | console.log(`[+] ${funcName} onLeave, start hook JNI_OnLoad `); 61 | hook_JNI_OnLoad(); 62 | } 63 | } 64 | }); 65 | } 66 | }); 67 | } 68 | 69 | hook_dlopen(); 70 | -------------------------------------------------------------------------------- /MT.js: -------------------------------------------------------------------------------- 1 | Java.perform(function () { 2 | 3 | /* //先HOOK libc.so->dlopen 4 | //第三方库导入后,HOOK第三方库中偏移地址为0x000BFA0C的函数。 5 | var funcname = 'dlopen'; 6 | var funcAddress = Module.findExportByName("libc.so", funcname) 7 | 8 | var result_pointer; 9 | var pmValue = ''; 10 | Interceptor.attach(funcAddress,{ 11 | onEnter: function(args){ 12 | this.pmValue = Memory.readUtf8String(args[0]); 13 | console.log(this.pmValue); 14 | //if(this.pmValue.indexOf("libgame.so") != -1){ 15 | // this.found = true; 16 | //} 17 | }, 18 | onLeave: function(retval){ 19 | if(this.found){ 20 | console.log('dlopen return value: ' + retval); 21 | frida_Module(); 22 | dlopentodo(); 23 | } 24 | } 25 | }); */ 26 | 27 | // 常量定义 28 | var PACKAGENAME = "com.zyzc.mt"; 29 | var SAVEDIR = "cache" 30 | 31 | 32 | 33 | var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext"); 34 | if (android_dlopen_ext != null) { 35 | Interceptor.attach(android_dlopen_ext, { 36 | onEnter: function (args) { 37 | var soName = args[0].readCString(); 38 | if (soName.indexOf("libgame.so") != -1) {//libcocos2dlua.so 39 | console.log("android_dlopen_ext: " + soName); 40 | this.hook = true; 41 | } 42 | }, 43 | onLeave: function (retval) { 44 | if (this.hook) { 45 | //frida_Module(); 46 | dlopentodo(); 47 | console.log("hook ok"); 48 | }; 49 | } 50 | }); 51 | } 52 | 53 | // 字节序列保存到文件中 54 | function save2File(filename, bytes) { 55 | console.log("save file to: " + filename); 56 | var file = new File(filename, "w"); 57 | file.write(bytes); 58 | file.flush(); 59 | file.close(); 60 | } 61 | 62 | // 输出显示十六进制数据 63 | function printDataHexStr(byteArr, size) { 64 | var len = size; 65 | if (len == undefined) { len = 0x40; } 66 | console.log(byteArr); 67 | 68 | /* var b = new Uint8Array(byteArr); 69 | var str = ""; 70 | 71 | for(var i = 0; i < len; i++) { 72 | str += (b[i].toString(16) + " "); 73 | } 74 | console.log(str); */ 75 | } 76 | 77 | function frida_Module() { 78 | Java.perform(function () { 79 | 80 | var modules = Process.enumerateModules(); 81 | for (var i = 0; i < modules.length; i++) { 82 | if (modules[i].path.indexOf("libgame") != -1) { 83 | console.log("模块名称:", modules[i].name); 84 | console.log("模块地址:", modules[i].base); 85 | console.log("大小:", modules[i].size); 86 | console.log("文件系统路径", modules[i].path); 87 | } 88 | } 89 | }); 90 | } 91 | 92 | 93 | function dlopentodo() { 94 | console.log("hook start"); 95 | 96 | //getFileData 97 | Interceptor.attach(Module.findExportByName("libgame.so", "_ZN7cocos2d11CCFileUtils11getFileDataEPKcS2_Pmb"), { 98 | onEnter: function (args) { 99 | var name = Memory.readCString(args[1]); 100 | this.fileDescriptor = name; 101 | this.sizePtr = ptr(args[3]); 102 | if (name.indexOf("ea_autofight_list.lua") != -1) { 103 | console.log("getFileData onEnter, name: " + name); 104 | } 105 | }, 106 | onLeave: function (retval) { 107 | if (this.fileDescriptor.indexOf("ea_autofight_list.lua") != -1) { 108 | var name = this.fileDescriptor; 109 | var size = Memory.readInt(this.sizePtr); 110 | console.log("getFileData onLeave, name: " + name + " size: ", size); 111 | //var buff = Memory.readCString(retval); 112 | //console.log(buff); 113 | printDataHexStr(Memory.readByteArray(retval, size)); 114 | 115 | if (!name.endsWith(".lua")) { name = name + ".lua"; } 116 | var filename = "/data/user/0/" + PACKAGENAME + "/" + SAVEDIR + "/" + name.split("/").join("."); 117 | save2File(filename, Memory.readByteArray(retval, size)); 118 | } 119 | } 120 | }); 121 | 122 | //luaL_loadbuffer 加载lua文件函数 libcocos2dlua.so 123 | var addr = Module.findExportByName('libgame.so', "luaL_loadbuffer"); 124 | console.log("luaL_loadbuffer: " + addr); 125 | if (addr != null) { 126 | Interceptor.attach(addr, { 127 | onEnter: function (args) { 128 | var name = Memory.readCString(args[3]); 129 | if (name == "ea_autofight_list") { 130 | var buff = Memory.readCString(args[1]); 131 | var size = args[2].toInt32(); 132 | //console.log("lual_loadbuffer, name: " + name + " size: ", size); 133 | //console.log(buff); 134 | var byteArr = Memory.readByteArray(args[1], size); 135 | printDataHexStr(byteArr); 136 | 137 | if (!name.endsWith(".lua")) { name = name + ".lua"; } 138 | var filename = "/data/user/0/" + PACKAGENAME + "/" + SAVEDIR + "/" + name.split("/").join("."); 139 | save2File(filename, byteArr); 140 | } 141 | } 142 | }); 143 | } 144 | 145 | /* 146 | Interceptor.attach(Module.findExportByName("libgame.so" , "_ZN7cocos2d11CCLuaEngine13executeStringEPKc"),{ 147 | onEnter:function (args){ 148 | console.log(' called from:\n' +Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n'); 149 | }, 150 | onLeave:function (retval){ 151 | //console.log(retval) 152 | } 153 | }); 154 | 155 | Interceptor.attach(Module.findExportByName("libc.so" , "open"),{ 156 | onEnter:function (args){ 157 | var name = Memory.readUtf8String(args[0]); 158 | if(name.indexOf(".lua")!=-1){ 159 | console.log( 160 | "open(" + 161 | "path=\"" + name + "\"" + 162 | ", flag=" + args[1] + 163 | ")" 164 | ); 165 | console.log('called from:\n' +Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n'); 166 | } 167 | } 168 | }); 169 | 170 | Interceptor.attach(Module.findExportByName("libgame.so" , "_ZN13CScriptSystem6DoFileEPKc"),{ 171 | onEnter:function (args){ 172 | var name = Memory.readCString(args[1]); 173 | console.log("DoFile: " + name); 174 | //console.log('called from:\n' +Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n'); 175 | } 176 | }); 177 | */ 178 | 179 | 180 | //再贴个获取xxtea秘钥的 libcocos2dlua.so _Z13xxtea_decryptPhjS_jPj 181 | 182 | Interceptor.attach(Module.findExportByName("libgame.so", "_Z13getPackagekeyiPi"), { 183 | onEnter: function (args) { 184 | console.log("getPackagekey onEnter"); 185 | }, 186 | onLeave: function (retval) { 187 | console.log("getPackagekey onLeave"); 188 | } 189 | }); 190 | Interceptor.attach(Module.findExportByName("libgame.so", "npk_read_encrypt"), { 191 | onEnter: function (args) { 192 | console.log("npk_read_encrypt onEnter"); 193 | }, 194 | onLeave: function (retval) { 195 | console.log("npk_read_encrypt onLeave"); 196 | } 197 | }); 198 | 199 | Interceptor.attach(Module.findExportByName("libgame.so", "tea_decode_buffer"), { 200 | onEnter: function (args) { 201 | console.log("tea_decode_buffer"); 202 | var pKey = ptr(arg[2]); 203 | console.log("tea_decode_buffer key: ", Memory.readUInt(pKey), Memory.readUInt(pKey + 4), Memory.readUInt(pKey + 8), Memory.readUInt(pKey + _12)); 204 | } 205 | }); 206 | Interceptor.attach(Module.findExportByName("libgame.so", "tea_decode"), { 207 | onEnter: function (args) { 208 | console.log("tea_decode"); 209 | } 210 | }); 211 | Interceptor.attach(Module.findExportByName("libgame.so", "npk_read_encrypt"), { 212 | onEnter: function (args) { 213 | console.log("npk_read_encrypt"); 214 | var pKey = ptr(arg[0]); 215 | console.log("tea_decode_buffer key: ", Memory.readUInt(pKey), Memory.readUInt(pKey + 4), Memory.readUInt(pKey + 8), Memory.readUInt(pKey + _12)); 216 | } 217 | }); 218 | 219 | /* Interceptor.attach(Module.findExportByName("libgame.so" , "xxtea_decode"),{ 220 | onEnter:function (args){ 221 | console.log("xxtea: xxtea_decode");// +Memory.readUtf8String(args[2]) 222 | } 223 | }); */ 224 | Interceptor.attach(Module.findExportByName("libgame.so", "xxtea_decode_byte"), { 225 | onEnter: function (args) { 226 | console.log("xxtea: xxtea_decode_byte"); 227 | }, 228 | onLeave: function (retval) { 229 | //console.log(retval) 230 | } 231 | }); 232 | Interceptor.attach(Module.findExportByName("libgame.so", "xxtea_decode_buffer"), { 233 | onEnter: function (args) { 234 | console.log("xxtea: xxtea_decode_buffer"); 235 | }, 236 | onLeave: function (retval) { 237 | //console.log(retval) 238 | } 239 | }); 240 | 241 | /* Interceptor.attach(Module.findExportByName("libgame.so" , "xxtea_encode"),{ 242 | onEnter:function (args){ 243 | console.log("xxtea: xxtea_encode"); 244 | } 245 | }); */ 246 | Interceptor.attach(Module.findExportByName("libgame.so", "xxtea_encode_byte"), { 247 | onEnter: function (args) { 248 | console.log("xxtea: xxtea_encode_byte"); 249 | } 250 | }); 251 | Interceptor.attach(Module.findExportByName("libgame.so", "xxtea_encode_buffer"), { 252 | onEnter: function (args) { 253 | console.log("xxtea: xxtea_encode_buffer"); 254 | } 255 | }); 256 | 257 | 258 | } 259 | 260 | }); 261 | 262 | 263 | -------------------------------------------------------------------------------- /NIMClient.js: -------------------------------------------------------------------------------- 1 | /** 2 | 适用对象:通用 3 | 作用:通过hook NIMClient获取App中的appkey 4 | 参考: 5 | */ 6 | 7 | var targetClass = "com.netease.nimlib.sdk.NIMClient"; 8 | 9 | function hook() { 10 | Java.perform(function () { 11 | 12 | var dexclassLoader = Java.use("dalvik.system.DexClassLoader"); 13 | dexclassLoader.loadClass.overload('java.lang.String').implementation = function (name) { 14 | var result = this.loadClass(name); 15 | console.log(name); 16 | 17 | // APP自己的Application时,想要hook的类一定是加载了的,否则时机过早会出现类找不到的情况。 18 | if (name.indexOf("Application") != -1 && name != "android.app.Application") { 19 | var application = Java.use(name); 20 | console.log("application:" + application); 21 | application.onCreate.implementation = function () { 22 | //hookTargetClass(); //二选一均可 23 | var ret = this.onCreate(); 24 | return ret; 25 | } 26 | } 27 | 28 | // 这里一般找不到 29 | if (name === targetClass) { 30 | console.log("got targetClass: ", targetClass); 31 | } 32 | return result; 33 | } 34 | 35 | 36 | var application = Java.use('android.app.Application'); 37 | console.log("application:" + application); 38 | 39 | application.attach.overload('android.content.Context').implementation = function (context) { 40 | console.log("application attach called"); 41 | var result = this.attach(context); 42 | 43 | hookTargetClass(); 44 | 45 | return result; 46 | } 47 | 48 | application.onCreate.implementation = function () { 49 | console.log("application onCreate called"); 50 | 51 | hookTargetClass(); //二选一均可 52 | 53 | var result = this.onCreate(); 54 | return result; 55 | } 56 | 57 | }); 58 | } 59 | 60 | function hookTargetClass() { 61 | var cls = Java.use(targetClass); 62 | if (cls) { 63 | console.log(cls); 64 | cls.init.implementation = function (context, loginInfo, sDKOptions) { 65 | console.log(targetClass + ".init() \n\tSDKOptions: ", sDKOptions.appKey.value, sDKOptions.sdkStorageRootPath.value); 66 | printStack(); 67 | return this.init(context, loginInfo, sDKOptions); 68 | } 69 | } 70 | } 71 | 72 | // 打印堆栈 73 | function printStack() { 74 | console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())); 75 | } 76 | 77 | setImmediate(hook()); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 说明:配合 [ADBGUI](https://github.com/bigsinger/adbgui) 工具,更加高效快捷。 2 | 3 | 4 | 5 | # 安装配置 6 | 7 | # 注入方式 8 | 9 | ```bash 10 | # spawn方式 11 | frida -U --no-pause -f com.abc.xxx -l xxx.js 12 | 13 | # attach方式 14 | frida -U pid -l xxx.js 15 | ``` 16 | 17 | 18 | 19 | # 基础语法 20 | 21 | | API名称 | 描述 | 22 | | :----------------------------------------- | :--------------------------------------------- | 23 | | `Java.use(className)` | 获取指定的Java类并使其在JavaScript代码中可用。 | 24 | | `Java.perform(callback)` | 确保回调函数在Java的主线程上执行。 | 25 | | `Java.choose(className, callbacks)` | 枚举指定类的所有实例。 | 26 | | `Java.cast(obj, cls)` | 将一个Java对象转换成另一个Java类的实例。 | 27 | | `Java.enumerateLoadedClasses(callbacks)` | 枚举进程中已经加载的所有Java类。 | 28 | | `Java.enumerateClassLoaders(callbacks)` | 枚举进程中存在的所有Java类加载器。 | 29 | | `Java.enumerateMethods(targetClassMethod)` | 枚举指定类的所有方法。 | 30 | 31 | ## 日志输出 32 | 33 | | 日志方法 | 描述 | 说明 | 34 | | :-------------- | :-------------------------------------------------- | :----------------------------------------------------------- | 35 | | `console.log()` | 使用JavaScript直接进行日志打印 | 多用于在CLI模式中,`console.log()`直接输出到命令行界面,使用户可以实时查看。在RPC模式中,`console.log()`同样输出在命令行,但可能被Python脚本的输出内容掩盖。 | 36 | | `send()` | Frida的专有方法,用于发送数据或日志到外部Python脚本 | 多用于RPC模式中,它允许JavaScript脚本发送数据到Python脚本,Python脚本可以进一步处理或记录这些数据。 | 37 | 38 | ## 模板代码 39 | 40 | ```js 41 | function main(){ 42 | Java.perform(function(){ 43 | hook(); 44 | }); 45 | } 46 | setImmediate(main); 47 | ``` 48 | 49 | 50 | 51 | # 常用代码 52 | 53 | ## Java层 54 | 55 | ### 打印堆栈 56 | 57 | ```js 58 | console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));//java打印堆栈 59 | 60 | console.log(' called from:\n' +Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n');//SO打印堆栈 61 | ``` 62 | 63 | 64 | 65 | ```js 66 | // 打印jni函数参数 67 | function hook_native_function(addr) { 68 | Interceptor.attach(addr, { 69 | onEnter: function (args) { 70 | var module = Process.findModuleByAddress(addr); 71 | this.args0 = args[0]; 72 | this.args1 = args[1]; 73 | this.args2 = args[2]; 74 | this.args3 = args[3]; 75 | this.args4 = args[4]; 76 | this.logs = [] 77 | this.logs.push("call " + module.name + "!" + ptr(addr).sub(module.base) + "\r\n"); 78 | this.logs.push("this.args0: " + print_arg(this.args0)); 79 | this.logs.push("this.args1: " + print_arg(this.args1)); 80 | this.logs.push("this.args2: " + print_arg(this.args2)); 81 | this.logs.push("this.args3: " + print_arg(this.args3)); 82 | this.logs.push("this.args4: " + print_arg(this.args4)); 83 | }, onLeave: function (ret) { 84 | this.logs.push("this.args0: onLeave: " + print_arg(this.args0)); 85 | this.logs.push("this.args1: onLeave: " + print_arg(this.args1)); 86 | this.logs.push("this.args2: onLeave: " + print_arg(this.args2)); 87 | this.logs.push("this.args3: onLeave: " + print_arg(this.args3)); 88 | this.logs.push("this.args4: onLeave: " + print_arg(this.args4)); 89 | this.logs.push("retValue: " + print_arg(ret)); 90 | console.log(this.logs) 91 | } 92 | }) 93 | } 94 | 95 | function print_arg(addr){ 96 | var range = Process.findRangeByAddress(addr); 97 | if(range!=null){ 98 | return hexdump(addr)+"\r\n"; 99 | }else{ 100 | return ptr(addr)+"\r\n"; 101 | } 102 | } 103 | 104 | function main() { 105 | // hook_14E20() 106 | var baseAddr = Module.findBaseAddress("libnative-lib.so") 107 | hook_native_function(baseAddr.add(0x12c0c)); 108 | // hook_native_function(baseAddr.add(0xf6e0)); free 109 | hook_native_function(baseAddr.add(0x5796c)); 110 | hook_native_function(baseAddr.add(0x103a4)); 111 | hook_native_function(baseAddr.add(0x13e18)); 112 | hook_native_function(baseAddr.add(0x157fc)); 113 | // hook_native_function(baseAddr.add(0xf670)); malloc 114 | hook_native_function(baseAddr.add(0x5971c)); 115 | hook_native_function(baseAddr.add(0x59670)); 116 | hook_native_function(baseAddr.add(0x177f8)); 117 | hook_native_function(baseAddr.add(0x19a84)); 118 | hook_native_function(baseAddr.add(0x57bec)); 119 | // hook_native_function(baseAddr.add(0xf310)); new 120 | // hook_native_function(baseAddr.add(0xf580)); delete 121 | hook_native_function(baseAddr.add(0x16a94)); 122 | // hook_native_function(baseAddr.add(0xf6a0)); memset 123 | hook_native_function(baseAddr.add(0xff10)); 124 | hook_native_function(baseAddr.add(0x16514)); 125 | hook_native_function(baseAddr.add(0x151a4)); 126 | hook_native_function(baseAddr.add(0xfcac)); 127 | hook_native_function(baseAddr.add(0x18024)); 128 | // hook_native_function(baseAddr.add(0xf680)); memcpy 129 | hook_native_function(baseAddr.add(0x57514)); 130 | // hook_native_function(baseAddr.add(0xf630)); strlen 131 | hook_native_function(baseAddr.add(0x167c0)); 132 | hook_native_function(baseAddr.add(0x12580)); 133 | hook_native_function(baseAddr.add(0x17ce8)); 134 | hook_native_function(baseAddr.add(0x18540)); 135 | } 136 | 137 | setImmediate(main) 138 | ``` 139 | 140 | ### 字节序列转字符串 141 | 142 | ```js 143 | let s = String.fromCharCode.apply(null, bytes); // 字节序列转字符串 144 | ``` 145 | 146 | ### 字节序列打印输出 147 | 148 | ```js 149 | // 输出显示十六进制数据 150 | function printDataHexStr(byteArr, size){ 151 | var len = size; 152 | if(len==undefined){len=0x40;} 153 | console.log(byteArr); 154 | 155 | /* var b = new Uint8Array(byteArr); 156 | var str = ""; 157 | 158 | for(var i = 0; i < len; i++) { 159 | str += (b[i].toString(16) + " "); 160 | } 161 | console.log(str); */ 162 | } 163 | ``` 164 | 165 | 166 | 167 | ### 字节序列保存到文件 168 | 169 | ```js 170 | // 字节序列保存到文件中 171 | function save2File(filename, bytes){ 172 | console.log("save file to: " + filename); 173 | var file = new File(filename, "w"); 174 | file.write(bytes); 175 | file.flush(); 176 | file.close(); 177 | } 178 | ``` 179 | 180 | 181 | 182 | ### Java对象、byte[]输出 183 | 184 | ```js 185 | function jobj2Str(jobject) { 186 | var ret = JSON.stringify(jobject); 187 | return ret; 188 | } 189 | ``` 190 | 191 | ### jstring、jbytearray 输出 192 | 193 | ```js 194 | function jstring2Str(jstring) { 195 | var ret; 196 | Java.perform(function() { 197 | var String = Java.use("java.lang.String"); 198 | ret = Java.cast(jstring, String); 199 | }); 200 | return ret; 201 | } 202 | 203 | function jbyteArray2Array(jbyteArray) { 204 | var ret; 205 | Java.perform(function() { 206 | var b = Java.use('[B'); 207 | var buffer = Java.cast(jbyteArray, b); 208 | ret = Java.array('byte', buffer); 209 | }); 210 | return ret; 211 | } 212 | ``` 213 | 214 | ### base64编码 215 | 216 | ```js 217 | function byte2Base64(bytes) { 218 | var jBase64 = Java.use('android.util.Base64'); 219 | return jBase64.encodeToString(bytes, 2); 220 | } 221 | ``` 222 | 223 | ### HOOK base64 224 | 225 | ```js 226 | function hookBase64() { 227 | // Base64 228 | var Base64Class = Java.use("android.util.Base64"); 229 | Base64Class.encodeToString.overload("[B", "int").implementation = function (a, b) { 230 | var rc = this.encodeToString(a, b); 231 | console.log(">>> Base64 " + rc); 232 | return rc; 233 | } 234 | } 235 | ``` 236 | 237 | ### HOOK HashMap 238 | 239 | ```js 240 | function hookHashMap() { 241 | var Build = Java.use("java.util.HashMap"); 242 | Build["put"].implementation = function (key, val) { 243 | console.log("key : " + key) 244 | console.log("val : " + val) 245 | return this.put(key, val) 246 | } 247 | } 248 | ``` 249 | 250 | 251 | 252 | ### HOOK构造函数 253 | 254 | ```js 255 | function hook(){ 256 | var utils = Java.use("com.xxx.Demo"); 257 | 258 | // $init表示构造函数 259 | utils.$init.overload('java.lang.String').implementation = function(str){ 260 | console.log(str); 261 | this.$init(str); 262 | } 263 | } 264 | ``` 265 | 266 | ### HOOK所有重载函数 267 | 268 | ```js 269 | function hookAllOverloads(targetClass, targetMethod) { 270 | Java.perform(function () { 271 | var targetClassMethod = targetClass + '.' + targetMethod; 272 | var hook = Java.use(targetClass); 273 | var overloadCount = hook[targetMethod].overloads.length; 274 | for (var i = 0; i < overloadCount; i++) { 275 | hook[targetMethod].overloads[i].implementation = function() { 276 | var retval = this[targetMethod].apply(this, arguments); 277 | //这里可以打印结果和参数 278 | return retval; 279 | } 280 | } 281 | }); 282 | } 283 | ``` 284 | 285 | ### HOOK内部类 286 | 287 | ```js 288 | function hook(){ 289 | Java.perform(function(){ 290 | var innerClass = Java.use("com.xxx.Demo$innerClass"); 291 | console.log(innerClass); 292 | innerClass.$init.implementation = function(){ 293 | console.log("内部类"); 294 | } 295 | }); 296 | } 297 | ``` 298 | 299 | 300 | 301 | ### 修改成员变量 302 | 303 | ```js 304 | function hook(){ 305 | Java.perform(function(){ 306 | 307 | // 修改静态成员变量 308 | var cls = Java.use("com.xxx.Demo"); 309 | cls.staticField.value = "hello"; 310 | console.log(cls.staticField.value); 311 | 312 | //非静态字段的修改。 使用`Java.choose()`枚举类的所有实例 313 | Java.choose("com.xxx.Demo", { 314 | onMatch: function(obj){ 315 | obj._privateInt.value = "hello"; 316 | obj.privateInt.value = 123456; 317 | }, 318 | onComplete: function(){ 319 | } 320 | }); 321 | }); 322 | } 323 | ``` 324 | 325 | 326 | 327 | ### 调用成员函数 328 | 329 | ```js 330 | // 调用静态函数 331 | var cls=Java.use("com.xxx.Demo"); 332 | cls.func("args"); 333 | 334 | // 调用非静态成员函数 335 | Java.choose("com.xxx.Demo", { 336 | onMatch:function(obj){ 337 | var ret = obj.func("args"); 338 | }, 339 | onComplete:function() { 340 | } 341 | }); 342 | 343 | // 调用jni函数 344 | function invoke(str){ 345 | Java.perform(function (){ 346 | var javaString = Java.use('java.lang.String').$new(str) 347 | var result = Java.use("com.xxx.MainActivity").encodeFromJni_70(javaString); 348 | console.log("result is => ",result) 349 | }) 350 | } 351 | ``` 352 | 353 | ### 获取动态加载的类 354 | 355 | ```js 356 | var cls = Java.use("dalvik.system.DexClassLoader"); 357 | cls.loadClass.overload('java.lang.String').implementation = function (name) { 358 | var result = this.loadClass(name); 359 | console.log(name); 360 | return result; 361 | } 362 | ``` 363 | 364 | 365 | 366 | ### 获取所有加载的类 367 | 368 | ```js 369 | 370 | // 异步枚举所有的类与类的所有方法 371 | Java.enumerateLoadedClasses({ 372 | onMatch: function(name, handle) { 373 | console.log(name); 374 | if(name.indexOf("com.xxx.Demo") !=-1){ 375 | console.log(name); 376 | var clazz = Java.use(name); 377 | console.log(clazz); 378 | var methods = clazz.class.getDeclaredMethods(); 379 | console.log(methods); 380 | } 381 | }, 382 | onComplete: function(){} 383 | }) 384 | 385 | ``` 386 | 387 | ### 获取类所有方法 388 | 389 | ```js 390 | var cls = Java.use(targetClass); 391 | var methods = cls.class.getDeclaredMethods(); 392 | methods.forEach(function(s) { 393 | console.log(s); 394 | }) 395 | 396 | for(var j=0; j < methods.length; j++){ 397 | var methodName = methods[j].getName(); 398 | console.log(methodName); 399 | for(var k=0; k { 451 | Java.use("android.widget.Toast") 452 | .makeText(Java.use("android.app.ActivityThread").currentApplication().getApplicationContext(), Java.use("java.lang.StringBuilder").$new("Text to Toast here"), 0).show(); 453 | }); 454 | ``` 455 | 456 | 457 | 458 | ### 获取Webview加载的URL 459 | 460 | ```js 461 | Java.use("android.webkit.WebView").loadUrl.overload("java.lang.String").implementation = function (s) { 462 | send(s.toString()); 463 | this.loadUrl.overload("java.lang.String").call(this, s); 464 | }; 465 | ``` 466 | 467 | 468 | 469 | ## Native层 470 | 471 | ### fopen 472 | 473 | ```js 474 | Interceptor.attach(Module.findExportByName("libc.so" , "open"),{ 475 | onEnter:function (args){ 476 | var name = Memory.readUtf8String(args[0]); 477 | if(name.indexOf("xxx.xxx")!=-1){ 478 | console.log( 479 | "open(" + 480 | "path=\"" + name + "\"" + 481 | ", flag=" + args[1] + 482 | ")" 483 | ); 484 | console.log('called from:\n' +Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n'); 485 | } 486 | } 487 | }); 488 | ``` 489 | 490 | ### 拦截模块加载并hook 491 | 492 | ```js 493 | var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext"); 494 | if(android_dlopen_ext != null){ 495 | Interceptor.attach(android_dlopen_ext,{ 496 | onEnter: function(args){ 497 | var soName = args[0].readCString(); 498 | if(soName.indexOf("libgame.so") != -1){//libcocos2dlua.so 499 | console.log("android_dlopen_ext: " + soName); 500 | this.hook = true; 501 | } 502 | }, 503 | onLeave: function(retval){ 504 | if(this.hook) { 505 | dlopentodo(); 506 | console.log("hook ok"); 507 | }; 508 | } 509 | }); 510 | } 511 | ``` 512 | 513 | ### Hook模块中的函数 514 | 515 | ```js 516 | //getFileData 517 | Interceptor.attach(Module.findExportByName("libgame.so" , "_ZN7cocos2d11CCFileUtils11getFileDataEPKcS2_Pmb"),{ 518 | onEnter:function (args) { 519 | var name = Memory.readCString(args[1]); 520 | this.sizePtr = ptr(args[3]); 521 | console.log("getFileData onEnter, name: " + name); 522 | }, 523 | onLeave:function (retval) { 524 | } 525 | }); 526 | ``` 527 | 528 | 529 | 530 | ### 枚举进程模块列表 531 | 532 | ```js 533 | var modules = Process.enumerateModules(); 534 | for(var i = 0; i < modules.length; i++) { 535 | if(modules[i].path.indexOf("libgame")!=-1) { 536 | console.log("模块名称:",modules[i].name); 537 | console.log("模块地址:",modules[i].base); 538 | console.log("大小:",modules[i].size); 539 | console.log("文件系统路径",modules[i].path); 540 | } 541 | } 542 | ``` 543 | 544 | 545 | 546 | ### 获取模块基址 547 | 548 | ```js 549 | // 获取基地址 550 | var baseAddr = Module.findBaseAddress("libnative-lib.so") 551 | ``` 552 | 553 | ### luaL_loadbuffer 554 | 555 | ```js 556 | //luaL_loadbuffer 加载lua文件函数 libcocos2dlua.so 557 | var addr = Module.findExportByName('libgame.so', "luaL_loadbuffer"); 558 | console.log("luaL_loadbuffer: " + addr); 559 | if(addr!=null){ 560 | Interceptor.attach(addr,{ 561 | onEnter:function (args){ 562 | var name = Memory.readCString(args[3]); 563 | if(name=="xxxx"){ //测试使用,具体dump时可以去掉 564 | var buff = Memory.readCString(args[1]); 565 | var size = args[2].toInt32(); 566 | //console.log("lual_loadbuffer, name: " + name + " size: ", size); 567 | //console.log(buff); 568 | var byteArr = Memory.readByteArray(args[1], size); 569 | printDataHexStr(byteArr); 570 | 571 | if(!name.endsWith(".lua")){name=name+".lua";} 572 | var filename = "/data/user/0/" + PACKAGENAME + "/" + SAVEDIR + "/" + name.split("/").join("."); 573 | save2File(filename, byteArr); 574 | } 575 | } 576 | }); 577 | } 578 | 579 | // 字节序列保存到文件中 580 | function save2File(filename, byteArr){ 581 | console.log("save file to: " + filename); 582 | var file = new File(filename, "w"); 583 | file.write(byteArr); 584 | file.flush(); 585 | file.close(); 586 | } 587 | 588 | // 输出显示十六进制数据 589 | function printDataHexStr(byteArr, size){ 590 | var len = size; 591 | if(len==undefined){len=0x40;} 592 | console.log(byteArr); 593 | } 594 | ``` 595 | 596 | 597 | 598 | ### 获取模块所有导出函数 599 | 600 | ```js 601 | // 获取所有导出函数 602 | var symbols = Module.enumerateSymbolsSync("libart.so"); 603 | symbols.forEach(function (item) { 604 | console.log(JSON.stringify(item)) 605 | }) 606 | ``` 607 | 608 | ### 实时跟踪CPU指令 609 | 610 | ```js 611 | // 实时跟踪cpu指令 612 | 613 | "use strict" 614 | console.log("Hello world"); 615 | const mainThread = Process.enumerateThreads()[0]; 616 | 617 | Stalker.follow(mainThread.id, { 618 | events: { 619 | call: true, // 调用指令 620 | 621 | // 其他事件: 622 | ret: false, // 返回指令 623 | exec: false, // 全部指令:不推荐, 因为数据量过大 624 | block: false, // 已计算的块: 粗略执行轨迹 625 | compile: false // 已编译的块: 对覆盖率很有用 626 | }, 627 | 628 |  onReceive: function (events) { 629 |    var parsedEvent = Stalker.parse(events); 630 |    //console.log("buring"+parsedEvent); 631 |  }, 632 | 633 |  transform: function (iterator) { 634 |    let instruction = iterator.next(); 635 |    do { 636 |      console.log("instruction:"+instruction); 637 |      iterator.keep(); 638 |    } while ((instruction = iterator.next()) !== null); 639 |  } 640 | }) 641 | ``` 642 | 643 | ### DUMP数据 644 | 645 | ```js 646 | function dumpAddr(address, length) { 647 | length = length || 1024; 648 | console.log(hexdump(address, { 649 | offset: 0, 650 | length: length, 651 | header: true, 652 | ansi: false 653 | })); 654 | } 655 | ``` 656 | 657 | ### 读取内存数据 658 | 659 | ```js 660 | // 读取内存中的字符串 661 | var s = Memory.readUtf8String(addr); 662 | var s = Memory.readCString(addr); 663 | 664 | // 读取内存中的字节序列 665 | var bytes = Memory.readByteArray(addr, size) 666 | 667 | // 读取内存中的数值 668 | var size = Memory.readInt(addr); 669 | ``` 670 | 671 | ### DUMP so文件 672 | 673 | ```js 674 | function dump_so(so_name) { 675 | Java.perform(function () { 676 | var currentApplication = Java.use("android.app.ActivityThread").currentApplication(); 677 | var dir = currentApplication.getApplicationContext().getFilesDir().getPath(); 678 | var libso = Process.getModuleByName(so_name); 679 | console.log("[name]:", libso.name); 680 | console.log("[base]:", libso.base); 681 | console.log("[size]:", ptr(libso.size)); 682 | console.log("[path]:", libso.path); 683 | var file_path = dir + "/" + libso.name + "_" + libso.base + "_" + ptr(libso.size) + ".so"; 684 | var file_handle = new File(file_path, "wb"); 685 | if (file_handle && file_handle != null) { 686 | Memory.protect(ptr(libso.base), libso.size, 'rwx'); 687 | var libso_buffer = ptr(libso.base).readByteArray(libso.size); 688 | file_handle.write(libso_buffer); 689 | file_handle.flush(); 690 | file_handle.close(); 691 | console.log("[dump]:", file_path); 692 | } 693 | }); 694 | } 695 | setImmediate(dump_so("libshield.so")) 696 | ``` 697 | 698 | 699 | 700 | # 参考 701 | 702 | - https://github.com/lasting-yang/frida_hook_libart 703 | - https://github.com/hookmaster/frida-all-in-one 704 | 705 | -------------------------------------------------------------------------------- /System.loadLibrary-libmsaoaidsec.js: -------------------------------------------------------------------------------- 1 | /** 2 | 适用对象:通用 3 | 作用:监控 System.loadLibrary 加载的库文件,替换msaoaidsec的加载。 4 | */ 5 | 6 | const TargetLibName = 'msaoaidsec' 7 | const MyLibName = 'dummy' 8 | 9 | function hook() { 10 | const System = Java.use('java.lang.System'); 11 | const Runtime = Java.use('java.lang.Runtime'); 12 | const VMStack = Java.use('dalvik.system.VMStack'); 13 | 14 | System.loadLibrary.implementation = function (libName) { 15 | try { 16 | console.log('System.loadLibrary("' + libName + '")'); 17 | //printStack(); // 想知道是哪个类加载的可以打开日志 18 | 19 | var name = libName; 20 | if (libName == TargetLibName) { 21 | console.warn(`replace ${TargetLibName} as ${MyLibName}`); 22 | name = MyLibName; 23 | } 24 | return Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), name); 25 | } catch (e) { 26 | //console.log(e); 27 | } 28 | } 29 | } 30 | 31 | 32 | // 打印堆栈 33 | function printStack() { 34 | Java.perform(function () { 35 | console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())); 36 | }); 37 | } 38 | 39 | hook(); -------------------------------------------------------------------------------- /System.loadLibrary.js: -------------------------------------------------------------------------------- 1 | /** 2 | 适用对象:通用 3 | 作用:监控 System.loadLibrary 加载的库文件 4 | */ 5 | 6 | const TargetLibName = 'cocos2djs' 7 | const MyLibName = 'test' 8 | 9 | function hook() { 10 | const System = Java.use('java.lang.System'); 11 | const Runtime = Java.use('java.lang.Runtime'); 12 | const VMStack = Java.use('dalvik.system.VMStack'); 13 | 14 | System.loadLibrary.implementation = function (libName) { 15 | try { 16 | console.log('\nSystem.loadLibrary("' + libName + '")'); 17 | //printStack(); // 想知道是哪个类加载的可以打开日志 18 | 19 | const loaded = Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libName); 20 | if (libName == TargetLibName) { 21 | console.log(TargetLibName + " is loaded!"); 22 | try { 23 | Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), MyLibName); 24 | } catch (e) { 25 | console.log(e); 26 | } 27 | } 28 | 29 | return loaded; 30 | } catch (ex) { 31 | //console.log(ex); 32 | } 33 | } 34 | } 35 | 36 | 37 | // 打印堆栈 38 | function printStack() { 39 | Java.perform(function () { 40 | console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())); 41 | }); 42 | } 43 | 44 | hook(); -------------------------------------------------------------------------------- /ZenTracer.js: -------------------------------------------------------------------------------- 1 | function log(text) { 2 | var packet = { 3 | 'cmd': 'log', 4 | 'data': text 5 | }; 6 | send("ZenTracer:::" + JSON.stringify(packet)) 7 | } 8 | 9 | function enter(tid, tname, cls, method, args) { 10 | var packet = { 11 | 'cmd': 'enter', 12 | 'data': [tid, tname, cls, method, args] 13 | }; 14 | send("ZenTracer:::" + JSON.stringify(packet)) 15 | } 16 | 17 | function exit(tid, retval) { 18 | var packet = { 19 | 'cmd': 'exit', 20 | 'data': [tid, retval] 21 | }; 22 | send("ZenTracer:::" + JSON.stringify(packet)) 23 | } 24 | 25 | function getTid() { 26 | var Thread = Java.use("java.lang.Thread") 27 | return Thread.currentThread().getId(); 28 | } 29 | 30 | function getTName() { 31 | var Thread = Java.use("java.lang.Thread") 32 | return Thread.currentThread().getName(); 33 | } 34 | 35 | function traceClass(clsname) { 36 | try { 37 | var target = Java.use(clsname); 38 | var methods = target.class.getDeclaredMethods(); 39 | methods.forEach(function (method) { 40 | var methodName = method.getName(); 41 | var overloads = target[methodName].overloads; 42 | overloads.forEach(function (overload) { 43 | var proto = "("; 44 | overload.argumentTypes.forEach(function (type) { 45 | proto += type.className + ", "; 46 | }); 47 | if (proto.length > 1) { 48 | proto = proto.substr(0, proto.length - 2); 49 | } 50 | proto += ")"; 51 | log("hooking: " + clsname + "." + methodName + proto); 52 | overload.implementation = function () { 53 | var args = []; 54 | var tid = getTid(); 55 | var tName = getTName(); 56 | for (var j = 0; j < arguments.length; j++) { 57 | args[j] = arguments[j] + "" 58 | } 59 | enter(tid, tName, clsname, methodName + proto, args); 60 | var retval = this[methodName].apply(this, arguments); 61 | exit(tid, "" + retval); 62 | return retval; 63 | } 64 | }); 65 | }); 66 | } catch (e) { 67 | log("'" + clsname + "' hook fail: " + e) 68 | } 69 | } 70 | 71 | function match(ex, text) { 72 | if (ex[1] == ':') { 73 | var mode = ex[0]; 74 | if (mode == 'E') { 75 | ex = ex.substr(2, ex.length - 2); 76 | return ex == text; 77 | } else if (mode == 'M') { 78 | ex = ex.substr(2, ex.length - 2); 79 | } else { 80 | log("Unknown match mode: " + mode + ", current support M(match) and E(equal)") 81 | } 82 | } 83 | return text.match(ex) 84 | } 85 | 86 | if (Java.available) { 87 | Java.perform(function () { 88 | log('ZenTracer Start...'); 89 | var matchRegEx = { MATCHREGEX }; 90 | var blackRegEx = { BLACKREGEX }; 91 | Java.enumerateLoadedClasses({ 92 | onMatch: function (aClass) { 93 | for (var index in matchRegEx) { 94 | // console.log(matchRegEx[index]); 95 | if (match(matchRegEx[index], aClass)) { 96 | var is_black = false; 97 | for (var i in blackRegEx) { 98 | if (match(blackRegEx[i], aClass)) { 99 | is_black = true; 100 | log(aClass + "' black by '" + blackRegEx[i] + "'"); 101 | break; 102 | } 103 | } 104 | if (is_black) { 105 | break; 106 | } 107 | log(aClass + "' match by '" + matchRegEx[index] + "'"); 108 | traceClass(aClass); 109 | } 110 | } 111 | 112 | }, 113 | onComplete: function () { 114 | log("Complete."); 115 | } 116 | }); 117 | }); 118 | } -------------------------------------------------------------------------------- /all_classes.js: -------------------------------------------------------------------------------- 1 | // 遍历App加载的所有类 2 | 3 | function hook() { 4 | Java.perform(function () { 5 | Java.enumerateLoadedClasses({ 6 | onMatch: function (className) { 7 | console.log(className); 8 | }, 9 | onComplete: function () { } 10 | }); 11 | }); 12 | } 13 | 14 | setImmediate(hook()); -------------------------------------------------------------------------------- /android-make-toast.js: -------------------------------------------------------------------------------- 1 | // 0 = // https://developer.android.com/reference/android/widget/Toast#LENGTH_LONG 2 | Java.scheduleOnMainThread(() => { 3 | Java.use("android.widget.Toast") 4 | .makeText(Java.use("android.app.ActivityThread").currentApplication().getApplicationContext(), Java.use("java.lang.StringBuilder").$new("Text to Toast here"), 0).show(); 5 | }); -------------------------------------------------------------------------------- /app/bili/7.26.1.js: -------------------------------------------------------------------------------- 1 | function hook_dlopen(soName = '') { 2 | Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"), { 3 | onEnter: function(args) { 4 | var pathptr = args[0]; 5 | if (pathptr) { 6 | var path = ptr(pathptr).readCString(); 7 | console.log("Loading: " + path); 8 | if (path.indexOf(soName) >= 0) { 9 | console.log("Already loading: " + soName); 10 | hook_system_property_get(); 11 | } 12 | } 13 | } 14 | }); 15 | } 16 | 17 | function hook_system_property_get() { 18 | var system_property_get_addr = Module.findExportByName(null, "__system_property_get"); 19 | if (!system_property_get_addr) { 20 | console.log("__system_property_get not found"); 21 | return; 22 | } 23 | 24 | Interceptor.attach(system_property_get_addr, { 25 | onEnter: function(args) { 26 | var nameptr = args[0]; 27 | if (nameptr) { 28 | var name = ptr(nameptr).readCString(); 29 | if (name.indexOf("ro.build.version.sdk") >= 0) { 30 | console.log("Found ro.build.version.sdk, need to patch"); 31 | hook_pthread_create(); 32 | // bypass() 33 | } 34 | } 35 | } 36 | }); 37 | } 38 | 39 | function hook_pthread_create() { 40 | var pthread_create = Module.findExportByName("libc.so", "pthread_create"); 41 | var libmsaoaidsec = Process.findModuleByName("libmsaoaidsec.so"); 42 | 43 | if (!libmsaoaidsec) { 44 | console.log("libmsaoaidsec.so not found"); 45 | return; 46 | } 47 | 48 | console.log("libmsaoaidsec.so base: " + libmsaoaidsec.base); 49 | 50 | if (!pthread_create) { 51 | console.log("pthread_create not found"); 52 | return; 53 | } 54 | 55 | Interceptor.attach(pthread_create, { 56 | onEnter: function(args) { 57 | var thread_ptr = args[2]; 58 | if (thread_ptr.compare(libmsaoaidsec.base) < 0 || thread_ptr.compare(libmsaoaidsec.base.add(libmsaoaidsec.size)) >= 0) { 59 | console.log("pthread_create other thread: " + thread_ptr); 60 | } else { 61 | console.log("pthread_create libmsaoaidsec.so thread: " + thread_ptr + " offset: " + thread_ptr.sub(libmsaoaidsec.base)); 62 | } 63 | }, 64 | onLeave: function(retval) {} 65 | }); 66 | } 67 | function nop_code(addr) 68 | { 69 | Memory.patchCode(ptr(addr),4,code => { 70 | const cw =new ThumbWriter(code,{pc:ptr(addr)}); 71 | cw.putNop(); 72 | cw.putNop(); 73 | cw.flush(); 74 | }) 75 | } 76 | 77 | function bypass() 78 | { 79 | let module = Process.findModuleByName("libmsaoaidsec.so") 80 | nop_code(module.base.add(0x010AE4)) 81 | nop_code(module.base.add(0x113F8)) 82 | } 83 | setImmediate(hook_dlopen, "libmsaoaidsec.so"); -------------------------------------------------------------------------------- /app/bili/7.76.0.js: -------------------------------------------------------------------------------- 1 | function hook_pthread_create() { 2 | var pthread_create = Module.findExportByName("libc.so", "pthread_create"); 3 | var libmsaoaidsec = Process.findModuleByName("libmsaoaidsec.so"); 4 | 5 | if (!libmsaoaidsec) { 6 | console.log("libmsaoaidsec.so not found"); 7 | return; 8 | } 9 | 10 | console.log("libmsaoaidsec.so base: " + libmsaoaidsec.base); 11 | 12 | if (!pthread_create) { 13 | console.log("pthread_create not found"); 14 | return; 15 | } 16 | 17 | Interceptor.attach(pthread_create, { 18 | onEnter: function(args) { 19 | var thread_ptr = args[2]; 20 | if (thread_ptr.compare(libmsaoaidsec.base) < 0 || thread_ptr.compare(libmsaoaidsec.base.add(libmsaoaidsec.size)) >= 0) { 21 | console.log("pthread_create other thread: " + thread_ptr); 22 | } else { 23 | console.log("pthread_create libmsaoaidsec.so thread: " + thread_ptr + " offset: " + thread_ptr.sub(libmsaoaidsec.base)); 24 | Interceptor.replace(libmsaoaidsec.base.add(0x1c544),new NativeCallback(function(){ 25 | console.log("Interceptor.replace: 0x1c544") 26 | },"void",[])) 27 | Interceptor.replace(libmsaoaidsec.base.add(0x1b8d4),new NativeCallback(function(){ 28 | console.log("Interceptor.replace: 0x1c544") 29 | },"void",[])) 30 | Interceptor.replace(libmsaoaidsec.base.add(0x26e5c),new NativeCallback(function(){ 31 | console.log("Interceptor.replace: 0x1c544") 32 | },"void",[])) 33 | } 34 | }, 35 | onLeave: function(retval) {} 36 | }); 37 | } -------------------------------------------------------------------------------- /app/bili/bypass_bilibili.js: -------------------------------------------------------------------------------- 1 | function create_fake_pthread_create() { 2 | const fake_pthread_create = Memory.alloc(4096) 3 | Memory.protect(fake_pthread_create, 4096, "rwx") 4 | Memory.patchCode(fake_pthread_create, 4096, code => { 5 | const cw = new Arm64Writer(code, { pc: ptr(fake_pthread_create) }) 6 | cw.putRet() 7 | }) 8 | return fake_pthread_create 9 | } 10 | 11 | function hook_dlsym() { 12 | var count = 0 13 | console.log("=== HOOKING dlsym ===") 14 | var interceptor = Interceptor.attach(Module.findExportByName(null, "dlsym"), 15 | { 16 | onEnter: function (args) { 17 | const name = ptr(args[1]).readCString() 18 | console.log("[dlsym]", name) 19 | if (name == "pthread_create") { 20 | count++ 21 | } 22 | }, 23 | onLeave: function(retval) { 24 | if (count == 1) { 25 | retval.replace(fake_pthread_create) 26 | } 27 | else if (count == 2) { 28 | retval.replace(fake_pthread_create) 29 | // 完成2次替换, 停止hook dlsym 30 | interceptor.detach() 31 | } 32 | } 33 | } 34 | ) 35 | return Interceptor 36 | } 37 | 38 | function hook_dlopen() { 39 | var interceptor = Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"), 40 | { 41 | onEnter: function (args) { 42 | var pathptr = args[0]; 43 | if (pathptr !== undefined && pathptr != null) { 44 | var path = ptr(pathptr).readCString(); 45 | console.log("[LOAD]", path) 46 | if (path.indexOf("libmsaoaidsec.so") > -1) { 47 | hook_dlsym() 48 | } 49 | } 50 | } 51 | } 52 | ) 53 | return interceptor 54 | } 55 | 56 | // 创建虚假pthread_create 57 | var fake_pthread_create = create_fake_pthread_create() 58 | var dlopen_interceptor = hook_dlopen() 59 | -------------------------------------------------------------------------------- /app/bili/create_fake_pthread_create.js: -------------------------------------------------------------------------------- 1 | function create_fake_pthread_create() { 2 | const fake_pthread_create = Memory.alloc(4096) 3 | Memory.protect(fake_pthread_create, 4096, "rwx") 4 | Memory.patchCode(fake_pthread_create, 4096, code => { 5 | const cw = new Arm64Writer(code, { pc: ptr(fake_pthread_create) }) 6 | cw.putRet() 7 | }) 8 | return fake_pthread_create 9 | } 10 | 11 | function hook_dlsym() { 12 | var count = 0 13 | console.log("=== HOOKING dlsym ===") 14 | var interceptor = Interceptor.attach(Module.findExportByName(null, "dlsym"), 15 | { 16 | onEnter: function (args) { 17 | const name = ptr(args[1]).readCString() 18 | console.log("[dlsym]", name) 19 | if (name == "pthread_create") { 20 | count++ 21 | } 22 | }, 23 | onLeave: function(retval) { 24 | if (count == 1) { 25 | retval.replace(fake_pthread_create) 26 | } 27 | else if (count == 2) { 28 | retval.replace(fake_pthread_create) 29 | } 30 | else if (count == 3) { 31 | retval.replace(fake_pthread_create) 32 | // 完成3次替换, 停止hook dlsym 33 | interceptor.detach() 34 | } 35 | } 36 | } 37 | ) 38 | return Interceptor 39 | } 40 | 41 | function hook_dlopen() { 42 | var interceptor = Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"), 43 | { 44 | onEnter: function (args) { 45 | var pathptr = args[0]; 46 | if (pathptr !== undefined && pathptr != null) { 47 | var path = ptr(pathptr).readCString(); 48 | console.log("[LOAD]", path) 49 | if (path.indexOf("libmsaoaidsec.so") > -1) { 50 | hook_dlsym() 51 | } 52 | } 53 | } 54 | } 55 | ) 56 | return interceptor 57 | } 58 | 59 | // 创建虚假pthread_create 60 | var fake_pthread_create = create_fake_pthread_create() 61 | var dlopen_interceptor = hook_dlopen() -------------------------------------------------------------------------------- /app/bili/hook_pthread_create-7.26.1.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Frida Hook - 监视目标库 `libmsaoaidsec.so`,拦截线程创建并进行指令修改 3 | * 4 | * 目标: 5 | * - 监视 `dlopen` 和 `android_dlopen_ext`,检测 `libmsaoaidsec.so` 何时加载。 6 | * - 通过 `call_constructors` 确保 `pthread_create` Hook 在目标库加载后执行。 7 | * - 拦截 `pthread_create`,检查线程函数是否在目标库 `so` 的范围内,并阻止其执行。 8 | * - 通过 `nop_code()` 修改目标库代码,使其关键逻辑失效(NOP 掉特定指令)。 9 | * 10 | * 说明: 11 | * - `pthread_create` 是创建线程的标准函数,Hook 它可以拦截所有新建线程。 12 | * - `call_constructors` 由 `linker` 调用,用于执行动态库的全局构造函数。 13 | * - `hook_call_constructors()` 确保 `TargetLibModule` 记录目标库的基地址,并在适当时机 Hook `pthread_create`。 14 | * - `nop_code()` 通过 `Memory.patchCode()` 修改代码,将关键指令替换为 NOP(空指令)。 15 | */ 16 | 17 | const TARGET_LIB_NAME = "libmsaoaidsec.so"; 18 | var TargetLibModule = null; // 存储目标库模块信息 19 | 20 | ///////////////////////////////////////// 21 | 22 | function nop_code(addr) { 23 | Memory.patchCode(ptr(addr), 4, code => { 24 | const cw = new ThumbWriter(code, { pc: ptr(addr) }); 25 | cw.putNop(); 26 | cw.putNop(); 27 | cw.flush(); 28 | }) 29 | } 30 | 31 | function bypass() { 32 | nop_code(TargetLibModule.base.add(0xc603 - 1)); 33 | } 34 | 35 | ///////////////////////////////////////// 36 | 37 | /** 38 | * Hook pthread_create,拦截目标库创建的线程 39 | */ 40 | function hook_pthread_create() { 41 | let pthread_create_addr = Module.findExportByName("libc.so", "pthread_create"); 42 | if (!pthread_create_addr) { 43 | console.error("Failed to find pthread_create!"); 44 | return; 45 | } 46 | 47 | Interceptor.attach(pthread_create_addr, { 48 | onEnter(args) { 49 | let thread_func_ptr = args[2]; // 线程函数地址 50 | console.log("[+] pthread_create called, thread function address: " + thread_func_ptr); 51 | 52 | // 确保目标库已加载 53 | if (!TargetLibModule) { 54 | //console.warn("Target library not loaded yet!"); 55 | return; 56 | } 57 | 58 | // 判断线程函数是否在目标库 `so` 的范围内 59 | if (thread_func_ptr.compare(TargetLibModule.base) > 0 && 60 | thread_func_ptr.compare(TargetLibModule.base.add(TargetLibModule.size)) < 0) { 61 | 62 | console.warn("[!] Intercepted thread function at: " + thread_func_ptr + 63 | " (Offset: " + thread_func_ptr.sub(TargetLibModule.base) + ")"); 64 | 65 | // 替换线程函数,防止执行 66 | Interceptor.replace(thread_func_ptr, new NativeCallback(() => { 67 | console.log("[*] Fake thread function executed, doing nothing..."); 68 | }, "void", [])); 69 | } 70 | } 71 | }); 72 | } 73 | 74 | function find_call_constructors() { 75 | is64Bit = Process.pointerSize === 8; 76 | var linkerModule = Process.getModuleByName(is64Bit ? "linker64" : "linker"); 77 | var symbols = linkerModule.enumerateSymbols(); 78 | for (var i = 0; i < symbols.length; i++) { 79 | if (symbols[i].name.indexOf('call_constructors') > 0) { 80 | console.warn(`call_constructors symbol name: ${symbols[i].name} address: ${symbols[i].address}`); 81 | return symbols[i].address; 82 | } 83 | } 84 | } 85 | 86 | function hook_call_constructors() { 87 | var ptr_call_constructors = find_call_constructors(); 88 | var listener = Interceptor.attach(ptr_call_constructors, { 89 | onEnter: function (args) { 90 | console.warn(`call_constructors onEnter`); 91 | if (!TargetLibModule) { 92 | TargetLibModule = Process.findModuleByName(TARGET_LIB_NAME); 93 | } 94 | hook_pthread_create(); 95 | bypass(); 96 | listener.detach(); 97 | }, 98 | }) 99 | } 100 | 101 | function hook_dlopen() { 102 | ["android_dlopen_ext", "dlopen"].forEach(funcName => { 103 | let addr = Module.findExportByName(null, funcName); 104 | if (addr) { 105 | Interceptor.attach(addr, { 106 | onEnter(args) { 107 | let libName = ptr(args[0]).readCString(); 108 | if (libName && libName.indexOf(TARGET_LIB_NAME) >= 0) { 109 | hook_call_constructors(); 110 | } 111 | }, 112 | onLeave: function (retval) { 113 | } 114 | }); 115 | } 116 | }); 117 | } 118 | 119 | var is64Bit = Process.pointerSize === 8; 120 | hook_dlopen() 121 | -------------------------------------------------------------------------------- /app/bili/replace_init_proc-7.26.1.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Frida Hook 脚本 - 动态拦截并修改目标库 `libmsaoaidsec.so` 的init函数 3 | * 4 | * 本脚本的主要功能: 5 | * 1. 监听 `dlopen`,在目标库加载时执行 Hook 逻辑。 6 | * 2. Hook `call_constructors`,在目标库初始化时执行自定义逻辑。 7 | * 3. 通过 `replace_init_proc` 替换多个关键函数,阻止它们的执行。 8 | * 4. 通过 `bypass` 进行指令 NOP 处理,绕过特定的校验代码。 9 | * 10 | * 适用环境: 11 | * - Android 平台 12 | * - 目标进程加载 `libmsaoaidsec.so` 并执行其内部逻辑 13 | * - Frida 框架支持 14 | * 15 | * 使用方式: 16 | * 1. 启动目标应用。 17 | * 2. 在终端运行 `frida -U -n <应用包名> -s <本脚本路径> --no-pause`。 18 | * 3. 观察日志输出,确保 Hook 生效。 19 | * 20 | * 注意事项: 21 | * - 目标库偏移地址可能随版本更新而变化,使用前请校准。 22 | * - 该脚本的 Hook 逻辑可能影响应用稳定性,请谨慎使用。 23 | * - 仅用于安全研究和逆向分析,切勿用于非法用途。 24 | */ 25 | 26 | 27 | const TARGET_LIB_NAME = "libmsaoaidsec.so"; 28 | var TargetLibModule = null; // 存储目标库模块信息 29 | 30 | ///////////////////////////////////////// 31 | 32 | /** 33 | * 替换目标库中的多个关键函数,阻止其执行 34 | */ 35 | function replace_init_proc() { 36 | if (!TargetLibModule) return; 37 | 38 | // 需要替换的偏移地址列表 39 | const offsets = [0xc40d, 0x53a9, 0x53e5, 0x53f5]; 40 | 41 | offsets.forEach(offset => { 42 | Interceptor.replace(TargetLibModule.base.add(offset), new NativeCallback(function () { 43 | console.log(`replace ${offset.toString(16)}`); 44 | }, "void", [])); 45 | }); 46 | } 47 | 48 | function nop_code(addr) { 49 | Memory.patchCode(ptr(addr), 4, code => { 50 | const cw = new ThumbWriter(code, { pc: ptr(addr) }); 51 | cw.putNop(); 52 | cw.putNop(); 53 | cw.flush(); 54 | }) 55 | } 56 | 57 | function bypass() { 58 | nop_code(TargetLibModule.base.add(0xc74d - 1)); 59 | } 60 | 61 | ///////////////////////////////////////// 62 | 63 | function find_call_constructors() { 64 | is64Bit = Process.pointerSize === 8; 65 | var linkerModule = Process.getModuleByName(is64Bit ? "linker64" : "linker"); 66 | var symbols = linkerModule.enumerateSymbols(); 67 | for (var i = 0; i < symbols.length; i++) { 68 | if (symbols[i].name.indexOf('call_constructors') > 0) { 69 | console.warn(`call_constructors symbol name: ${symbols[i].name} address: ${symbols[i].address}`); 70 | return symbols[i].address; 71 | } 72 | } 73 | } 74 | 75 | function hook_call_constructors() { 76 | var ptr_call_constructors = find_call_constructors(); 77 | var listener = Interceptor.attach(ptr_call_constructors, { 78 | onEnter: function (args) { 79 | console.warn(`call_constructors onEnter`); 80 | if (!TargetLibModule) { 81 | TargetLibModule = Process.findModuleByName(TARGET_LIB_NAME); 82 | } 83 | replace_init_proc(); 84 | bypass(); 85 | listener.detach(); 86 | }, 87 | }) 88 | } 89 | 90 | function hook_dlopen() { 91 | ["android_dlopen_ext", "dlopen"].forEach(funcName => { 92 | let addr = Module.findExportByName(null, funcName); 93 | if (addr) { 94 | Interceptor.attach(addr, { 95 | onEnter(args) { 96 | let libName = ptr(args[0]).readCString(); 97 | if (libName && libName.indexOf(TARGET_LIB_NAME) >= 0) { 98 | hook_call_constructors(); 99 | } 100 | }, 101 | onLeave: function (retval) { 102 | } 103 | }); 104 | } 105 | }); 106 | } 107 | 108 | var is64Bit = Process.pointerSize === 8; 109 | hook_dlopen() -------------------------------------------------------------------------------- /app/bili/replace_init_proc-7.76.0.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Frida Hook 脚本 - 动态拦截并修改目标库 `libmsaoaidsec.so` 的init函数 3 | * 4 | * 本脚本的主要功能: 5 | * 1. 监听 `dlopen`,在目标库加载时执行 Hook 逻辑。 6 | * 2. Hook `call_constructors`,在目标库初始化时执行自定义逻辑。 7 | * 3. 通过 `replace_init_proc` 替换多个关键函数,阻止它们的执行。 8 | * 4. 通过 `bypass` 进行指令 NOP 处理,绕过特定的校验代码。 9 | * 10 | * 适用环境: 11 | * - Android 平台 12 | * - 目标进程加载 `libmsaoaidsec.so` 并执行其内部逻辑 13 | * - Frida 框架支持 14 | * 15 | * 使用方式: 16 | * 1. 启动目标应用。 17 | * 2. 在终端运行 `frida -U -n <应用包名> -s <本脚本路径> --no-pause`。 18 | * 3. 观察日志输出,确保 Hook 生效。 19 | * 20 | * 注意事项: 21 | * - 目标库偏移地址可能随版本更新而变化,使用前请校准。 22 | * - 该脚本的 Hook 逻辑可能影响应用稳定性,请谨慎使用。 23 | * - 仅用于安全研究和逆向分析,切勿用于非法用途。 24 | */ 25 | 26 | 27 | const TARGET_LIB_NAME = "libmsaoaidsec.so"; 28 | var TargetLibModule = null; // 存储目标库模块信息 29 | 30 | ///////////////////////////////////////// 31 | 32 | /** 33 | * 替换目标库中的多个关键函数,阻止其执行 34 | */ 35 | function replace_init_proc() { 36 | if (!TargetLibModule) return; 37 | 38 | // 需要替换的偏移地址列表 39 | const offsets = [0x14400, 0x83fc, 0x8448, 0x8460, 0x84b4, 0x85a8]; 40 | 41 | offsets.forEach(offset => { 42 | Interceptor.replace(TargetLibModule.base.add(offset), new NativeCallback(function () { 43 | console.log(`replace ${offset.toString(16)}`); 44 | }, "void", [])); 45 | }); 46 | } 47 | 48 | function nop_code(addr) { 49 | Memory.patchCode(ptr(addr), 4, code => { 50 | const cw = new Arm64Writer(code, { pc: ptr(addr) });// 64位 51 | cw.putNop(); 52 | cw.putNop(); 53 | cw.flush(); 54 | }) 55 | } 56 | 57 | function bypass() { 58 | nop_code(TargetLibModule.base.add(0x8750)); // 64位不用减一 59 | //nop_code(TargetLibModule.base.add(0x13b38)); // 64位不用减一 60 | 61 | // 62 | // var libbili_core = Process.getModuleByName('libbili_core.so'); 63 | // console.log(libbili_core.base) 64 | // nop_code(libbili_core.base.add(0x93b8)); // 64位不用减一 65 | } 66 | 67 | ///////////////////////////////////////// 68 | 69 | function find_call_constructors() { 70 | is64Bit = Process.pointerSize === 8; 71 | var linkerModule = Process.getModuleByName(is64Bit ? "linker64" : "linker"); 72 | var symbols = linkerModule.enumerateSymbols(); 73 | for (var i = 0; i < symbols.length; i++) { 74 | if (symbols[i].name.indexOf('call_constructors') > 0) { 75 | console.warn(`call_constructors symbol name: ${symbols[i].name} address: ${symbols[i].address}`); 76 | return symbols[i].address; 77 | } 78 | } 79 | } 80 | 81 | function hook_call_constructors() { 82 | var ptr_call_constructors = find_call_constructors(); 83 | var listener = Interceptor.attach(ptr_call_constructors, { 84 | onEnter: function (args) { 85 | console.warn(`call_constructors onEnter`); 86 | if (!TargetLibModule) { 87 | TargetLibModule = Process.findModuleByName(TARGET_LIB_NAME); 88 | } 89 | replace_init_proc(); 90 | bypass(); 91 | listener.detach(); 92 | }, 93 | }) 94 | } 95 | 96 | function hook_dlopen() { 97 | ["android_dlopen_ext", "dlopen"].forEach(funcName => { 98 | let addr = Module.findExportByName(null, funcName); 99 | if (addr) { 100 | Interceptor.attach(addr, { 101 | onEnter(args) { 102 | let libName = ptr(args[0]).readCString(); 103 | if (libName && libName.indexOf(TARGET_LIB_NAME) >= 0) { 104 | hook_call_constructors(); 105 | } 106 | }, 107 | onLeave: function (retval) { 108 | } 109 | }); 110 | } 111 | }); 112 | } 113 | 114 | var is64Bit = Process.pointerSize === 8; 115 | hook_dlopen() -------------------------------------------------------------------------------- /byPassSSL.js: -------------------------------------------------------------------------------- 1 | // 过字节系的App的防抓包方案 2 | 3 | function patch(address) { 4 | Memory.protect(address, 4, 'rwx'); 5 | Memory.writeByteArray(address, [0x00, 0x00, 0x80, 0x52]); 6 | } 7 | 8 | function onLoad(name, callback) { 9 | //void* android_dlopen_ext(const char* filename, int flag, const android_dlextinfo* extinfo);//原型 10 | const android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext"); 11 | if (android_dlopen_ext != null) { 12 | Interceptor.attach(android_dlopen_ext, { 13 | onEnter: function (args) { 14 | if (args[0].readCString().indexOf(name) !== -1) { 15 | this.hook = true; 16 | } 17 | }, onLeave: function (retval) { 18 | if (this.hook) { 19 | callback(); 20 | } 21 | } 22 | }); 23 | } 24 | } 25 | 26 | 27 | 28 | 29 | function main() { 30 | Java.perform(function () { 31 | const soName = 'libsscronet.so'; 32 | onLoad(soName, () => { 33 | // void SSL_CTX_set_custom_verify(SSL_CTX *ctx, int mode, enum ssl_verify_result_t (*callback)(SSL *ssl, uint8_t *out_alert)) { 34 | // ctx->verify_mode = mode; 35 | // ctx->custom_verify_callback = callback; 36 | // }//原型 37 | let SSL_CTX_set_custom_verify = Module.getExportByName(soName, 'SSL_CTX_set_custom_verify'); 38 | if (SSL_CTX_set_custom_verify != null) { 39 | Interceptor.attach(SSL_CTX_set_custom_verify, { 40 | onEnter: function (args) { 41 | Interceptor.attach(args[2], { 42 | onLeave: function (retval) { 43 | // enum ssl_verify_result_t BORINGSSL_ENUM_INT { 44 | // ssl_verify_ok, 45 | // ssl_verify_invalid, 46 | // ssl_verify_retry, 47 | // }; 48 | //全部替换成 ssl_verify_ok 49 | if (retval > 0x0) retval.replace(0x0); 50 | } 51 | }); 52 | } 53 | }); 54 | } 55 | }); 56 | }); 57 | } 58 | 59 | setImmediate(main); 60 | -------------------------------------------------------------------------------- /byteDance.js: -------------------------------------------------------------------------------- 1 | function patch(address) { 2 | Memory.protect(address, 4, 'rwx'); 3 | Memory.writeByteArray(address, [0x00, 0x00, 0x80, 0x52]); 4 | } 5 | 6 | // function onLoad(name, callback) { 7 | // var Runtime = Java.use('java.lang.Runtime'); 8 | // var System = Java.use('java.lang.System'); 9 | // var VMStack = Java.use('dalvik.system.VMStack'); 10 | // var VERSION = Java.use('android.os.Build$VERSION'); 11 | // System.loadLibrary.overload('java.lang.String').implementation = function (libName) { 12 | // if (VERSION.SDK_INT.value >= 29) { 13 | // Runtime.getRuntime().loadLibrary0(Java.use('sun.reflect.Reflection').getCallerClass(), libName); 14 | // } else if (VERSION.SDK_INT.value >= 24) { 15 | // Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libName); 16 | // } else { 17 | // Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader()); 18 | // } 19 | // if (libName.includes(name)) { 20 | // callback();//无法执行到这里 21 | // } 22 | // }; 23 | // } 24 | 25 | //参考: https://www.jianshu.com/p/4291ee42c412 26 | function onLoad(name, callback) { 27 | //void* android_dlopen_ext(const char* filename, int flag, const android_dlextinfo* extinfo);//原型 28 | const android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext"); 29 | if (android_dlopen_ext != null) { 30 | Interceptor.attach(android_dlopen_ext, { 31 | onEnter: function (args) { 32 | if (args[0].readCString().indexOf(name) !== -1) { 33 | this.hook = true; 34 | } 35 | }, onLeave: function (retval) { 36 | if (this.hook) { 37 | callback(); 38 | } 39 | } 40 | }); 41 | } 42 | } 43 | 44 | function main() { 45 | Java.perform(function () { 46 | //28.4.0 47 | const soName = 'libsscronet.so'; 48 | //方法1, 内存搜索 49 | // onLoad(soName, function () { 50 | // let libsscronet = Process.getModuleByName(soName); 51 | // const verifyCertMatches = Memory.scanSync(libsscronet.base, libsscronet.size, "E0 E3 00 91 C1 14 80 12"); 52 | // verifyCertMatches.forEach(function (result) { 53 | // let verifyCert = result.address.add(0xC); 54 | // if (Instruction.parse(verifyCert).toString() === "mov w0, #1") { 55 | // // 设置可读可写可执行 56 | // Memory.protect(verifyCert, 4, 'rwx'); 57 | // // 修改为 mov w0, #0 58 | // Memory.writeByteArray(verifyCert, [0x00, 0x00, 0x80, 0x52]); 59 | // } 60 | // 61 | // let handleVerifyInstruction = Instruction.parse(result.address.add(0x1A4)); 62 | // if (Instruction.parse(result.address.add(0x1A0)).toString() === "mov x0, x19" && handleVerifyInstruction.mnemonic === "bl") { 63 | // let handleVerifyResult = new NativePointer(handleVerifyInstruction.opStr.replace('#', '')); 64 | // Interceptor.attach(handleVerifyResult, { 65 | // onLeave: function (retval) { 66 | // if (retval > 0x0) retval.replace(0x0); 67 | // } 68 | // }); 69 | // } 70 | // }); 71 | // }); 72 | 73 | //方法2, 直接patch 74 | // onLoad(soName, function () { 75 | // let libsscronet = Module.getBaseAddress(soName); 76 | // let verifyCert = libsscronet.add(0x3700F0); 77 | // let handleVerifyResult1 = libsscronet.add(0x370448); 78 | // let handleVerifyResult2 = libsscronet.add(0x370494); 79 | // console.log("修改前: " + Instruction.parse(verifyCert), Instruction.parse(handleVerifyResult1), Instruction.parse(handleVerifyResult2)); 80 | // patch(verifyCert); 81 | // patch(handleVerifyResult1); 82 | // patch(handleVerifyResult2); 83 | // console.log("修改后: " + Instruction.parse(verifyCert), Instruction.parse(handleVerifyResult1), Instruction.parse(handleVerifyResult2)); 84 | // }) 85 | 86 | 87 | //方法3, hook SSL_CTX_set_custom_verify, 基本通杀 88 | onLoad(soName, () => { 89 | // void SSL_CTX_set_custom_verify(SSL_CTX *ctx, int mode, enum ssl_verify_result_t (*callback)(SSL *ssl, uint8_t *out_alert)) { 90 | // ctx->verify_mode = mode; 91 | // ctx->custom_verify_callback = callback; 92 | // }//原型 93 | let SSL_CTX_set_custom_verify = Module.getExportByName(soName, 'SSL_CTX_set_custom_verify'); 94 | if (SSL_CTX_set_custom_verify != null) { 95 | Interceptor.attach(SSL_CTX_set_custom_verify, { 96 | onEnter: function (args) { 97 | Interceptor.attach(args[2], { 98 | onLeave: function (retval) { 99 | // enum ssl_verify_result_t BORINGSSL_ENUM_INT { 100 | // ssl_verify_ok, 101 | // ssl_verify_invalid, 102 | // ssl_verify_retry, 103 | // }; 104 | //全部替换成 ssl_verify_ok 105 | if (retval > 0x0) retval.replace(0x0); 106 | } 107 | }); 108 | } 109 | }); 110 | } 111 | }); 112 | 113 | //只需要选择其中一种即可, 推荐使用方法3 114 | }); 115 | } 116 | 117 | setImmediate(main); 118 | // setTimeout(main, 3000); 119 | // frida -U -f com.ss.android.ugc.aweme -l Android/byteDance.js -------------------------------------------------------------------------------- /call_constructors.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Frida Hook 脚本:监视 `call_constructors` 并拦截目标动态库加载 3 | * 4 | * 目标: 5 | * - 监视 `dlopen` 和 `android_dlopen_ext`,检测目标so是否被加载。 6 | * - 通过 `call_constructors` 确保拦截目标库的全局构造函数调用。 7 | * - 在 `call_constructors` 进入时打印日志,并自动解除 Hook 以减少干扰。 8 | * 9 | * 说明: 10 | * - `call_constructors` 是 ELF 动态库加载过程中执行全局构造函数的关键函数。 11 | * - `dlopen` 和 `android_dlopen_ext` 用于加载 `.so` 库,Hook 这些函数可拦截库的加载过程。 12 | * - `find_call_constructors()` 在 `linker` 或 `linker64` 中查找 `call_constructors` 的地址。 13 | * - `hook_call_constructors()` 在 `call_constructors` 执行时打印日志,并在第一次调用后解除 Hook。 14 | */ 15 | 16 | const TARGET_LIB_NAME = "libmsaoaidsec.so"; 17 | 18 | 19 | function find_call_constructors() { 20 | is64Bit = Process.pointerSize === 8; 21 | var linkerModule = Process.getModuleByName(is64Bit ? "linker64" : "linker"); 22 | var symbols = linkerModule.enumerateSymbols(); 23 | for (var i = 0; i < symbols.length; i++) { 24 | if (symbols[i].name.indexOf('call_constructors') > 0) { 25 | console.warn(`call_constructors symbol name: ${symbols[i].name} address: ${symbols[i].address}`); 26 | return symbols[i].address; 27 | } 28 | } 29 | } 30 | 31 | function hook_call_constructors() { 32 | var ptr_call_constructors = find_call_constructors(); 33 | var listener = Interceptor.attach(ptr_call_constructors, { 34 | onEnter: function (args) { 35 | console.warn(`call_constructors onEnter`); 36 | listener.detach(); 37 | }, 38 | }) 39 | } 40 | 41 | function hook_dlopen() { 42 | ["android_dlopen_ext", "dlopen"].forEach(funcName => { 43 | let addr = Module.findExportByName(null, funcName); 44 | if (addr) { 45 | Interceptor.attach(addr, { 46 | onEnter(args) { 47 | let libName = ptr(args[0]).readCString(); 48 | if (libName && libName.indexOf(TARGET_LIB_NAME) >= 0) { 49 | hook_call_constructors(); 50 | } 51 | }, 52 | onLeave: function (retval) { 53 | } 54 | }); 55 | } 56 | }); 57 | } 58 | 59 | var is64Bit = Process.pointerSize === 8; 60 | hook_dlopen() 61 | -------------------------------------------------------------------------------- /clone.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Frida Hook 脚本: 3 | * 主要功能: 4 | * 1. 监控 `clone` 调用,获取子线程的加载库信息。 5 | * 2. Hook `android_dlopen_ext`,在目标库 `libDexHelper.so` 加载时进行处理。 6 | * 3. 在目标库加载后,对指定偏移地址的指令进行 NOP 处理(屏蔽部分功能)。 7 | * 8 | * 适用架构:ARM32 & ARM64 9 | */ 10 | const TARGET_LIB_NAME = "libDexHelper.so"; 11 | const patch_offsets = [0x34b79, 0x334fd, 0x38c29, 0x394b9]; // 这里填需要nop掉的偏移地址 12 | var TargetLibModule = null; // 存储目标库模块信息 13 | 14 | 15 | var clone = Module.findExportByName('libc.so', 'clone'); 16 | Interceptor.attach(clone, { 17 | onEnter: function (args) { 18 | // args[3] 子线程的栈地址。如果这个值为 0,可能意味着没有指定栈地址 19 | if (args[3] != 0) { 20 | var addr = args[3].add(48).readPointer() 21 | var so_name = Process.findModuleByAddress(addr).name; 22 | var so_base = Module.getBaseAddress(so_name); 23 | var offset = (addr - so_base); 24 | console.log('clone(pthread_create ): ', so_name, addr, '0x' + offset.toString(16)); 25 | } 26 | }, 27 | onLeave: function (retval) { 28 | } 29 | }); 30 | 31 | function hook_dlopen(so_name) { 32 | Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"), { 33 | onEnter: function (args) { 34 | var pathptr = args[0]; 35 | if (pathptr !== undefined && pathptr != null) { 36 | var path = ptr(pathptr).readCString(); 37 | if (path.indexOf(so_name) !== -1) { 38 | this.match = true 39 | } 40 | } 41 | }, 42 | onLeave: function (retval) { 43 | if (this.match) { 44 | console.log(so_name, "加载成功"); 45 | if (!TargetLibModule) { 46 | TargetLibModule = Process.findModuleByName(TARGET_LIB_NAME); 47 | } 48 | 49 | bypass(); 50 | } 51 | } 52 | }); 53 | } 54 | 55 | function bypass() { 56 | patch_offsets.forEach(offset => { 57 | console.log(`patch 0x${offset.toString(16)}`); 58 | nopCodeArm32(TargetLibModule.base.add(offset - 1)); // 32位应用减一 59 | //nopCodeArm64(TargetLibModule.base.add(offset)); // 64位应用不用减一 60 | }); 61 | } 62 | 63 | function nopCodeArm32(addr) { 64 | Memory.patchCode(ptr(addr), 4, code => { 65 | const cw = new ThumbWriter(code, { pc: ptr(addr) }); 66 | cw.putNop(); 67 | cw.putNop(); 68 | cw.flush(); 69 | }) 70 | } 71 | 72 | function nopCodeArm64(addr) { 73 | Memory.patchCode(ptr(addr), 4, code => { 74 | const cw = new Arm64Writer(code, { pc: ptr(addr) });// 64位 75 | cw.putNop(); 76 | cw.putNop(); 77 | cw.flush(); 78 | }) 79 | } 80 | 81 | hook_dlopen(TARGET_LIB_NAME) 82 | // find_clone(); -------------------------------------------------------------------------------- /cn.zhilianda.photo.scanner.pro.js: -------------------------------------------------------------------------------- 1 | // 照片恢复大师 2 | /** 3 | 思路:导出时弹出登录页面,此时获取登录页面的Activity名,然后搜索引用,找到启动登录页面的上下文代码,找到判断登录状态的函数调用:checkLogin 4 | 然后找到checkLogin的上下文代码,hook之。 5 | */ 6 | 7 | 8 | function hook() { 9 | Java.perform(function () { 10 | var SimplifyUtil = Java.use("cn.zld.data.http.core.utils.SimplifyUtil"); 11 | SimplifyUtil.checkLogin.implementation = function(){ 12 | console.log("checkLogin called"); 13 | //console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));//java打印堆栈 14 | return true; 15 | } 16 | SimplifyUtil.checkMode.implementation = function(){ 17 | console.log("checkMode called"); 18 | //console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));//java打印堆栈 19 | return true; 20 | } 21 | SimplifyUtil.isShowPay.implementation = function(){ 22 | console.log("isShowPay called"); 23 | //console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));//java打印堆栈 24 | return false; 25 | } 26 | SimplifyUtil.checkServiceTime.implementation = function(){ 27 | console.log("checkServiceTime called"); 28 | //console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));//java打印堆栈 29 | return true; 30 | } 31 | SimplifyUtil.checkFeedBackTime.implementation = function(){ 32 | console.log("checkFeedBackTime called"); 33 | //console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));//java打印堆栈 34 | return true; 35 | } 36 | SimplifyUtil.checkIsGoh.implementation = function(){ 37 | console.log("checkIsGoh called"); 38 | //console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));//java打印堆栈 39 | return true; 40 | } 41 | SimplifyUtil.checkIsSgoh.implementation = function(){ 42 | console.log("checkIsSgoh called"); 43 | //console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));//java打印堆栈 44 | return true; 45 | } 46 | SimplifyUtil.isShowPromotionDialog.implementation = function(){ 47 | console.log("isShowPromotionDialog called"); 48 | //console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));//java打印堆栈 49 | return true; 50 | } 51 | 52 | var SPCommonUtil = Java.use("cn.zld.data.http.core.utils.sp.SPCommonUtil"); 53 | SPCommonUtil.get.implementation = function(str, obj){ 54 | var ret = this.get(str, obj); 55 | console.log("SPCommonUtil.get called: " + str + " ret: ", ret); 56 | if(str!=null){ 57 | if(str.indexOf('time_interval')!=-1){ 58 | //console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));//java打印堆栈 59 | } else if(str.indexOf('goods_type_ranknum')!=-1){ 60 | //console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));//java打印堆栈 61 | return "1"; 62 | } else if(str.indexOf('buy_')!=-1){ 63 | //return true; 64 | } 65 | } 66 | return ret; 67 | } 68 | 69 | 70 | }); 71 | } 72 | 73 | setImmediate(hook()); -------------------------------------------------------------------------------- /cocos2djs-xxtea-key.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 脚本名称:Cocos2d-js XXTEA 密钥提取工具 3 | * 适用对象:基于 Cocos2d-js 引擎开发的游戏 4 | * 作用:通过 Frida 挂载动态链接库(.so 文件),捕获并提取 XXTEA 加密算法所使用的密钥 5 | * 原理: 6 | * 1. 使用 Frida 挂载 `dlopen` 和 `android_dlopen_ext` 函数,监控目标动态库(如 libcocos2djs.so)的加载过程。 7 | * 2. 当目标动态库加载时,通过 `Interceptor.attach` 挂载目标函数(如 xxtea_decrypt)。 8 | * 3. 在目标函数被调用时,捕获其参数(通常是密钥),并将其打印到控制台。 9 | * 使用说明: 10 | * 1. 安装 Frida:确保你的设备已安装 Frida,并且可以正常运行。推荐使用ROOT真机,模拟器可能存在问题。 11 | * 2. 启动目标应用:使用以下命令启动目标应用并加载脚本: 12 | * `frida -U -f com.example.app -l your_script.js --no-pause` 13 | * 其中: 14 | * - `-U` 表示连接到设备。 15 | * - `-f` 表示启动目标应用(替换为实际应用的包名)。 16 | * - `-l` 表示加载脚本(替换为你的脚本文件名)。 17 | * - `--no-pause` 表示启动应用后不暂停。 18 | * 3. 查看输出:脚本运行后,控制台会输出捕获到的密钥。 19 | * 注意事项: 20 | * 1. 确保目标应用使用了 XXTEA 加密算法,并且密钥通过参数传递。 21 | * 2. 如果目标动态库名称或函数名称与脚本中的不一致,请自行修改 `TARGET_LIB_NAME` 和 `TargetFuncName`。 22 | * 3. 本脚本仅用于学习和研究目的,请勿用于非法用途。 23 | */ 24 | 25 | // 目标动态库名称 26 | var TARGET_LIB_NAME = "libcocos2djs.so"; 27 | 28 | // 目标函数名称 29 | var TargetFuncName = "xxtea_decrypt"; 30 | 31 | /** 32 | * 挂载目标函数的 Hook 33 | */ 34 | function do_hook() { 35 | // 查找目标函数的地址 36 | var addr = Module.findExportByName(TARGET_LIB_NAME, TargetFuncName); 37 | console.log("找到目标函数地址:", addr); 38 | 39 | // 挂载目标函数 40 | Interceptor.attach(addr, { 41 | onEnter: function (args) { 42 | // args[2] 是目标函数的第三个参数(假设是密钥) 43 | // 读取并打印密钥 44 | console.log("捕获到的密钥: " + Memory.readCString(args[2])); 45 | }, 46 | onLeave: function (retval) { 47 | // 函数返回时的操作(此处无操作) 48 | } 49 | }); 50 | } 51 | 52 | 53 | function hook_dlopen() { 54 | ["android_dlopen_ext", "dlopen"].forEach(funcName => { 55 | let addr = Module.findExportByName(null, funcName); 56 | if (addr) { 57 | Interceptor.attach(addr, { 58 | onEnter(args) { 59 | let libName = ptr(args[0]).readCString(); 60 | if (libName && libName.indexOf(TARGET_LIB_NAME) >= 0) { 61 | this.is_can_hook = true; 62 | console.log(`[+] ${funcName} onEnter: ${libName}`); 63 | } 64 | }, 65 | onLeave: function (retval) { 66 | if (this.is_can_hook) { 67 | console.log(`[+] ${funcName} onLeave, start hook ${TargetFuncName} `); 68 | do_hook(); 69 | } 70 | } 71 | }); 72 | } 73 | }); 74 | } 75 | 76 | hook_dlopen(); -------------------------------------------------------------------------------- /dlopen-replace.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 替换模板库的加载 3 | */ 4 | 5 | const TARGET_LIB_NAME = "libmsaoaidsec.so"; 6 | const REPLACE_LIB_NAME = "libdummy.so"; 7 | 8 | 9 | function hook_dlopen() { 10 | ["android_dlopen_ext", "dlopen"].forEach(funcName => { 11 | let addr = Module.findExportByName(null, funcName); 12 | if (addr) { 13 | Interceptor.attach(addr, { 14 | onEnter(args) { 15 | let libName = ptr(args[0]).readCString(); 16 | if (libName && libName.indexOf(TARGET_LIB_NAME) >= 0) { 17 | console.log(`[+] ${funcName} onEnter: ${libName}`); 18 | 19 | // 替换为加载 REPLACE_LIB_NAME 20 | let newLib = Memory.allocUtf8String(REPLACE_LIB_NAME); 21 | args[0] = newLib; 22 | console.log(`[+] ${funcName}: 替换 ${libName} -> ${REPLACE_LIB_NAME}`); 23 | } 24 | }, 25 | onLeave: function (retval) { 26 | } 27 | }); 28 | } 29 | }); 30 | } 31 | 32 | hook_dlopen(); 33 | -------------------------------------------------------------------------------- /dlopen.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Frida Hook 脚本:监控目标动态库的加载 3 | * 4 | * 目标: 5 | * - 监视 `dlopen` 和 `android_dlopen_ext` 函数的调用,以检测指定的动态库是否被加载。 6 | * - 在库加载时打印日志,方便调试和分析。 7 | * 8 | * 说明: 9 | * - `dlopen` 和 `android_dlopen_ext` 是 Android 用于加载动态库的常见 API。 10 | * - `Interceptor.attach` 允许我们在函数进入 (`onEnter`) 和返回 (`onLeave`) 时执行自定义代码。 11 | */ 12 | 13 | // 目标动态库名称 14 | const TARGET_LIB_NAME = "libmsaoaidsec.so"; 15 | 16 | 17 | function hook_dlopen() { 18 | ["android_dlopen_ext", "dlopen"].forEach(funcName => { 19 | let addr = Module.findExportByName(null, funcName); 20 | if (addr) { 21 | Interceptor.attach(addr, { 22 | onEnter(args) { 23 | let libName = ptr(args[0]).readCString(); 24 | if (libName) { 25 | console.log(`[+] ${funcName} onEnter: ${libName}`); 26 | } 27 | }, 28 | onLeave: function (retval) { 29 | console.log(`[+] ${funcName} onLeave`); 30 | } 31 | }); 32 | } 33 | }); 34 | } 35 | 36 | hook_dlopen(); -------------------------------------------------------------------------------- /dlsym.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Frida Hook 脚本 - 监控动态库加载与符号解析 3 | * 4 | * 本脚本的主要功能: 5 | * 1. 监听 `dlopen` 和 `android_dlopen_ext`,记录加载的动态库及其基地址。 6 | * 2. 维护 `libMap`,存储已加载库的基地址与库名称的映射关系。 7 | * 3. Hook `dlsym`,拦截符号解析并输出符号来源库的信息。 8 | * 9 | * 适用环境: 10 | * - Android 平台 11 | * - 目标应用加载多个 `.so` 库,且需要分析库的加载与符号解析情况 12 | * - Frida 框架支持 13 | * 14 | * 使用方式: 15 | * 1. 启动目标应用。 16 | * 2. 在终端运行 `frida -U -n <应用包名> -s <本脚本路径> --no-pause`。 17 | * 3. 观察日志输出,查看加载的动态库及解析的符号信息。 18 | * 19 | * 注意事项: 20 | * - `libMap` 仅记录通过 `dlopen` 或 `android_dlopen_ext` 加载的库,某些库可能未被捕获。 21 | * - 该脚本不会修改应用行为,仅用于监控和分析。 22 | * - 仅用于安全研究和逆向分析,切勿用于非法用途。 23 | */ 24 | 25 | var libMap = {}; // 存储库的基地址 -> 库名称映射 26 | 27 | 28 | /** 29 | * 记录已加载库的基地址 30 | */ 31 | function load_so_load() { 32 | // 获取 dlopen 函数的地址 33 | var dlopen = Module.findExportByName(null, "dlopen"); 34 | // 获取 android_dlopen_ext 函数的地址 35 | var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext"); 36 | 37 | // 挂载 dlopen 函数 38 | Interceptor.attach(dlopen, { 39 | onEnter: function (args) { 40 | // 获取动态库路径的指针 41 | var path_ptr = args[0]; 42 | // 将指针转换为字符串 43 | var path = ptr(path_ptr).readCString(); 44 | // 保存路径 45 | this.path = path; 46 | console.log("[+] dlopen onEnter: ", path); 47 | }, 48 | onLeave: function (retval) { 49 | if (retval.toInt32() !== 0) { // 确保加载成功 50 | var baseAddr = ptr(retval); 51 | libMap[baseAddr] = this.path; 52 | console.log("[+] dlopen onLeave: " + this.path + " at " + baseAddr); 53 | } 54 | } 55 | }); 56 | 57 | // 挂载 android_dlopen_ext 函数 58 | Interceptor.attach(android_dlopen_ext, { 59 | onEnter: function (args) { 60 | // 获取动态库路径的指针 61 | var path_ptr = args[0]; 62 | // 将指针转换为字符串 63 | var path = ptr(path_ptr).readCString(); 64 | // 保存路径 65 | this.path = path; 66 | console.log("[+] android_dlopen_ext onEnter: ", path); 67 | }, 68 | onLeave: function (retval) { 69 | if (retval.toInt32() !== 0) { // 确保加载成功 70 | var baseAddr = ptr(retval); 71 | libMap[baseAddr] = this.path; 72 | console.log("[+] android_dlopen_ext onLeave: " + this.path + " at " + baseAddr); 73 | } 74 | } 75 | }); 76 | } 77 | 78 | // Hook dlsym 解析符号并输出库名 79 | function hook_dlsym() { 80 | var dlsym = Module.findExportByName(null, "dlsym"); 81 | if (dlsym !== null) { 82 | Interceptor.attach(dlsym, { 83 | onEnter: function (args) { 84 | this.handle = args[0]; // 记录库的 handle 85 | this.symbol = ptr(args[1]).readCString(); // 读取符号名 86 | 87 | this.libName = "Unknown"; 88 | this.libName = libMap[this.handle]; 89 | //console.log("\t[+] dlsym: " + this.symbol + " (from " + this.libName + ")"); 90 | }, 91 | onLeave: function (retval) { 92 | console.log("\t[+] dlsym: " + this.symbol + " (from " + this.libName + ") -> " + retval.toString()); 93 | } 94 | }); 95 | } 96 | } 97 | 98 | console.log("[+] hook dlopen android_dlopen_ext dlsym--------"); 99 | load_so_load(); 100 | hook_dlsym(); 101 | -------------------------------------------------------------------------------- /dump-so.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 在目标库加载时dump so文件 3 | */ 4 | 5 | const TARGET_LIB_NAME = "libmsaoaidsec.so"; 6 | var TargetLibModule = null; // 存储目标库模块信息 7 | 8 | ///////////////////////////////////////// 9 | 10 | function dump_so(so_name) { 11 | // 获取加载的模块信息 12 | var module = Process.getModuleByName(so_name); 13 | console.log("[name]:", module.name); 14 | console.log("[base]:", module.base); 15 | console.log("[size]:", ptr(module.size)); 16 | console.log("[path]:", module.path); 17 | 18 | var path = "/data/local/tmp/" + module.name + "_" + module.base + "_" + ptr(module.size) + ".so"; 19 | var handle = new File(path, "wb"); 20 | if (handle && handle != null) { 21 | Memory.protect(ptr(module.base), module.size, 'rwx'); 22 | var buffer = ptr(module.base).readByteArray(module.size); 23 | handle.write(buffer); 24 | handle.flush(); 25 | handle.close(); 26 | console.log("[dump]:", path); 27 | } 28 | } 29 | 30 | 31 | function hook_dlopen() { 32 | ["android_dlopen_ext", "dlopen"].forEach(funcName => { 33 | let addr = Module.findExportByName(null, funcName); 34 | if (addr) { 35 | Interceptor.attach(addr, { 36 | onEnter(args) { 37 | let libName = ptr(args[0]).readCString(); 38 | if (libName && libName.indexOf(TARGET_LIB_NAME) >= 0) { 39 | this.canHook = true; 40 | } 41 | }, 42 | onLeave: function (retval) { 43 | if (this.canHook) { 44 | dump_so(TARGET_LIB_NAME); 45 | } 46 | } 47 | }); 48 | } 49 | }); 50 | } 51 | 52 | hook_dlopen(); -------------------------------------------------------------------------------- /dump_so.js: -------------------------------------------------------------------------------- 1 | function dump_so(so_name) { 2 | Java.perform(function () { 3 | var currentApplication = Java.use("android.app.ActivityThread").currentApplication(); 4 | var dir = currentApplication.getApplicationContext().getFilesDir().getPath(); 5 | var libso = Process.getModuleByName(so_name); 6 | console.log("[name]:", libso.name); 7 | console.log("[base]:", libso.base); 8 | console.log("[size]:", ptr(libso.size)); 9 | console.log("[path]:", libso.path); 10 | var file_path = dir + "/" + libso.name + "_" + libso.base + "_" + ptr(libso.size) + ".so"; 11 | var file_handle = new File(file_path, "wb"); 12 | if (file_handle && file_handle != null) { 13 | Memory.protect(ptr(libso.base), libso.size, 'rwx'); 14 | var libso_buffer = ptr(libso.base).readByteArray(libso.size); 15 | file_handle.write(libso_buffer); 16 | file_handle.flush(); 17 | file_handle.close(); 18 | console.log("[dump]:", file_path); 19 | } 20 | }); 21 | } 22 | setImmediate(dump_so("libshield.so")) 23 | // example: frida -U com.xingin.xhs -l dump_so.js -------------------------------------------------------------------------------- /dygod.js: -------------------------------------------------------------------------------- 1 | function hook() { 2 | Java.perform(function () { 3 | var symbols = Module.enumerateSymbolsSync("libart.so"); 4 | symbols.forEach(function (item) { 5 | console.log(JSON.stringify(item)) 6 | }) 7 | 8 | 9 | // var Verify = Java.use("com.movies.jni.Verify"); 10 | // Verify.headerRequestKey.implementation = function(context, s1, s2){ 11 | // console.log("s1: ", s1); 12 | // console.log("s2: ", s2); 13 | // var ret = this.headerRequestKey(context, s1, s2); 14 | // console.log("ret: ", ret); 15 | // //showStacks3("111"); 16 | // //console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));//java打印堆栈 17 | // return ret; 18 | // }; 19 | 20 | 21 | }); 22 | } 23 | 24 | 25 | function showStacks2(name) 26 | { 27 | var Exception = Java.use("java.lang.Exception"); 28 | var ins = Exception.$new("Exception"); 29 | var straces = ins.getStackTrace(); 30 | if (straces != undefined && straces != null) 31 | { 32 | var strace = straces.toString(); 33 | var replaceStr = strace.replace(/,/g, "\\n"); 34 | console.log("=============================" + name + " Stack strat======================="); 35 | console.log(replaceStr); 36 | console.log("=============================" + name + " Stack end=======================\r\n"); 37 | Exception.$dispose(); 38 | } 39 | } 40 | 41 | function showStacks3(str_tag) 42 | { 43 | var Exception= Java.use("java.lang.Exception"); 44 | var ins = Exception.$new("Exception"); 45 | var straces = ins.getStackTrace(); 46 | 47 | if (undefined == straces || null == straces) 48 | { 49 | return; 50 | } 51 | 52 | console.log("=============================" + str_tag + " Stack strat======================="); 53 | console.log(""); 54 | 55 | 56 | for (var i = 0; i < straces.length; i++) 57 | { 58 | var str = " " + straces[i].toString(); 59 | console.log(str); 60 | } 61 | 62 | console.log(""); 63 | console.log("=============================" + str_tag + " Stack end=======================\r\n"); 64 | Exception.$dispose(); 65 | } 66 | 67 | setImmediate(hook()); 68 | // frida -U 2345 -l dygod.js -------------------------------------------------------------------------------- /hookUrl.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | 适用对象:通用 4 | 作用:对App进行抓包 5 | 参考: 6 | https://blog.csdn.net/weixin_42840266/article/details/132279975 7 | https://www.anquanke.com/post/id/197657#h2-9 8 | */ 9 | 10 | 11 | // hook java.net.URL 12 | function hookURL() { 13 | var URL = Java.use('java.net.URL'); 14 | URL.$init.overload('java.lang.String').implementation = function (a) { 15 | console.log('URL:' + a) 16 | //printStack(); 17 | this.$init(a) 18 | } 19 | } 20 | 21 | // hook okhttp3 HttpUrl 22 | function hookOkhttp3() { 23 | try { 24 | var Builder = Java.use('okhttp3.Request$Builder'); 25 | Builder.url.overload('okhttp3.HttpUrl').implementation = function (a) { 26 | console.log('okhttp3.HttpUrl: ' + a) 27 | var res = this.url(a); 28 | //printStack(); 29 | //console.log("res: " + res) 30 | return res; 31 | } 32 | } catch (error) { 33 | console.log(error); 34 | } 35 | } 36 | 37 | // hook okhttp3 addHeader 38 | function hookOkhttp3addHeader() { 39 | try { 40 | var Builder = Java.use("okhttp3.Request$Builder"); 41 | Builder["addHeader"].implementation = function (str, str2) { 42 | console.log("key: " + str) 43 | console.log("val: " + str2) 44 | //printStack(); 45 | var result = this["addHeader"](str, str2); 46 | console.log("result: " + result); 47 | return result; 48 | }; 49 | } catch (error) { 50 | console.log(error); 51 | } 52 | } 53 | 54 | 55 | function hook_KeyStore_load() { 56 | var StringClass = Java.use("java.lang.String"); 57 | var KeyStore = Java.use("java.security.KeyStore"); 58 | KeyStore.load.overload('java.security.KeyStore$LoadStoreParameter').implementation = function (arg0) { 59 | //printStack(); 60 | console.log("KeyStore.load1:", arg0); 61 | this.load(arg0); 62 | }; 63 | KeyStore.load.overload('java.io.InputStream', '[C').implementation = function (arg0, arg1) { 64 | //printStack(); 65 | console.log("KeyStore.load2:", arg0, arg1 ? StringClass.$new(arg1) : null); 66 | this.load(arg0, arg1); 67 | }; 68 | 69 | console.log("hook_KeyStore_load..."); 70 | } 71 | 72 | function hookOkHttpClient() { 73 | try { 74 | var OkHttpClient = Java.use("okhttp3.OkHttpClient"); 75 | OkHttpClient.newCall.implementation = function (request) { 76 | var result = this.newCall(request); 77 | console.log(request.toString()); 78 | return result; 79 | }; 80 | } catch (error) { 81 | console.log(error); 82 | } 83 | } 84 | 85 | // 打印堆栈 86 | function printStack() { 87 | console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())); 88 | } 89 | 90 | // hook Base64 91 | function hookBase64() { 92 | // Base64 93 | var Base64Class = Java.use("android.util.Base64"); 94 | Base64Class.encodeToString.overload("[B", "int").implementation = function (a, b) { 95 | var rc = this.encodeToString(a, b); 96 | console.log(">>> Base64 " + rc); 97 | return rc; 98 | } 99 | } 100 | 101 | // hook HashMap 102 | function hookHashMap() { 103 | var Build = Java.use("java.util.HashMap"); 104 | Build["put"].implementation = function (key, val) { 105 | console.log("key : " + key) 106 | console.log("val : " + val) 107 | return this.put(key, val) 108 | } 109 | } 110 | 111 | 112 | 113 | function hook() { 114 | Java.perform(function () { 115 | hookURL(); 116 | hookOkhttp3(); 117 | hookOkHttpClient(); 118 | hookOkhttp3addHeader(); 119 | hook_KeyStore_load(); 120 | //hookBase64(); 121 | //hookHashMap(); 122 | }); 123 | } 124 | 125 | setImmediate(hook()); -------------------------------------------------------------------------------- /hook_RegisterNatives.js: -------------------------------------------------------------------------------- 1 | /** 2 | 适用对象:通用 3 | 作用:通过拦截RegisterNatives获取注册的native函数 4 | 参考: 5 | */ 6 | 7 | 8 | function klog(data) { 9 | var message = {}; 10 | message["jsname"] = "hook_RegisterNatives"; 11 | message["data"] = data; 12 | send(message); 13 | } 14 | function klogData(data, key, value) { 15 | var message = {}; 16 | message["jsname"] = "hook_RegisterNatives"; 17 | message["data"] = data; 18 | message[key] = value; 19 | send(message); 20 | } 21 | 22 | 23 | function hook_RegisterNatives() { 24 | klogData("", "init", "hook_RegisterNatives.js init hook success"); 25 | var symbols = Module.enumerateSymbolsSync("libart.so"); 26 | var addrRegisterNatives = null; 27 | for (var i = 0; i < symbols.length; i++) { 28 | var symbol = symbols[i]; 29 | 30 | //_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi 31 | if (symbol.name.indexOf("art") >= 0 && 32 | symbol.name.indexOf("JNI") >= 0 && 33 | symbol.name.indexOf("RegisterNatives") >= 0 && 34 | symbol.name.indexOf("CheckJNI") < 0) { 35 | addrRegisterNatives = symbol.address; 36 | klog("RegisterNatives is at " + symbol.address + " " + symbol.name); 37 | } 38 | } 39 | 40 | if (addrRegisterNatives != null) { 41 | Interceptor.attach(addrRegisterNatives, { 42 | onEnter: function (args) { 43 | klog("[RegisterNatives] method_count: " + args[3]); 44 | var env = args[0]; 45 | var java_class = args[1]; 46 | var class_name = Java.vm.tryGetEnv().getClassName(java_class); 47 | 48 | var methods_ptr = ptr(args[2]); 49 | var method_count = parseInt(args[3]); 50 | for (var i = 0; i < method_count; i++) { 51 | var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3)); 52 | var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize)); 53 | var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2)); 54 | 55 | var methodName = Memory.readCString(name_ptr); 56 | var sig = Memory.readCString(sig_ptr); 57 | var find_module = Process.findModuleByAddress(fnPtr_ptr); 58 | if (find_module) { 59 | klog("[RegisterNatives] java_class: " + class_name + " name: " + methodName + " sig: " + sig + " fnPtr: " + fnPtr_ptr + " module_name: " + find_module.name + " module_base: " + find_module.base + " offset: " + ptr(fnPtr_ptr).sub(find_module.base)); 60 | } else { 61 | klog("[RegisterNatives] java_class: " + class_name + " name: " + methodName + " sig: " + sig + " fnPtr: " + fnPtr_ptr + " module: null"); 62 | } 63 | 64 | } 65 | } 66 | }); 67 | } 68 | } 69 | 70 | setImmediate(hook_RegisterNatives); -------------------------------------------------------------------------------- /hook_okhttp3.js: -------------------------------------------------------------------------------- 1 | /** 2 | 适用对象:通用 3 | 作用:对App进行抓包Okhttp 4 | 参考: 5 | https://blog.csdn.net/cqcre/article/details/107602760 6 | */ 7 | 8 | function hook_okhttp3() { 9 | // 1. frida Hook java层的代码必须包裹在Java.perform中,Java.perform会将Hook Java相关API准备就绪。 10 | Java.perform(function () { 11 | 12 | 13 | // 2. 准备相应类库,用于后续调用,前两个库是Android自带类库,后三个是使用Okhttp网络库的情况下才有的类 14 | var ByteString = Java.use("com.android.okhttp.okio.ByteString"); 15 | var Buffer = Java.use("com.android.okhttp.okio.Buffer"); 16 | var Interceptor = Java.use("okhttp3.Interceptor"); 17 | var ArrayList = Java.use("java.util.ArrayList"); 18 | var OkHttpClient = Java.use("okhttp3.OkHttpClient"); 19 | 20 | 21 | // 注册一个Java类 22 | var MyInterceptor = Java.registerClass({ 23 | name: "okhttp3.MyInterceptor", 24 | implements: [Interceptor], 25 | methods: { 26 | intercept: function (chain) { 27 | var request = chain.request(); 28 | try { 29 | console.log("MyInterceptor.intercept onEnter:", request, "\nrequest headers:\n", request.headers()); 30 | var requestBody = request.body(); 31 | var contentLength = requestBody ? requestBody.contentLength() : 0; 32 | if (contentLength > 0) { 33 | var BufferObj = Buffer.$new(); 34 | requestBody.writeTo(BufferObj); 35 | try { 36 | console.log("\nrequest body String:\n", BufferObj.readString(), "\n"); 37 | } catch (error) { 38 | try { 39 | console.log("\nrequest body ByteString:\n", ByteString.of(BufferObj.readByteArray()).hex(), "\n"); 40 | } catch (error) { 41 | console.log("error 1:", error); 42 | } 43 | } 44 | } 45 | } catch (error) { 46 | console.log("error 2:", error); 47 | } 48 | var response = chain.proceed(request); 49 | try { 50 | console.log("MyInterceptor.intercept onLeave:", response, "\nresponse headers:\n", response.headers()); 51 | var responseBody = response.body(); 52 | var contentLength = responseBody ? responseBody.contentLength() : 0; 53 | if (contentLength > 0) { 54 | console.log("\nresponsecontentLength:", contentLength, "responseBody:", responseBody, "\n"); 55 | 56 | 57 | var ContentType = response.headers().get("Content-Type"); 58 | console.log("ContentType:", ContentType); 59 | if (ContentType.indexOf("video") == -1) { 60 | if (ContentType.indexOf("application") == 0) { 61 | var source = responseBody.source(); 62 | if (ContentType.indexOf("application/zip") != 0) { 63 | try { 64 | console.log("\nresponse.body StringClass\n", source.readUtf8(), "\n"); 65 | } catch (error) { 66 | try { 67 | console.log("\nresponse.body ByteString\n", source.readByteString().hex(), "\n"); 68 | } catch (error) { 69 | console.log("error 4:", error); 70 | } 71 | } 72 | } 73 | } 74 | 75 | 76 | } 77 | 78 | 79 | } 80 | 81 | 82 | } catch (error) { 83 | console.log("error 3:", error); 84 | } 85 | return response; 86 | } 87 | } 88 | }); 89 | 90 | 91 | OkHttpClient.$init.overload('okhttp3.OkHttpClient$Builder').implementation = function (Builder) { 92 | console.log("OkHttpClient.$init:", this, Java.cast(Builder.interceptors(), ArrayList)); 93 | this.$init(Builder); 94 | }; 95 | 96 | 97 | var MyInterceptorObj = MyInterceptor.$new(); 98 | var Builder = Java.use("okhttp3.OkHttpClient$Builder"); 99 | console.log(Builder); 100 | Builder.build.implementation = function () { 101 | this.interceptors().clear(); 102 | this.interceptors().add(MyInterceptorObj); 103 | var result = this.build(); 104 | return result; 105 | }; 106 | 107 | 108 | Builder.addInterceptor.implementation = function (interceptor) { 109 | this.interceptors().clear(); 110 | this.interceptors().add(MyInterceptorObj); 111 | return this; 112 | }; 113 | 114 | 115 | console.log("hook_okhttp3..."); 116 | }); 117 | } 118 | 119 | 120 | setImmediate(hook_okhttp3()); 121 | 122 | -------------------------------------------------------------------------------- /hook_reg.js: -------------------------------------------------------------------------------- 1 | 2 | function get_func_addr(module, offset) { 3 | var base_addr = Module.findBaseAddress(module); 4 | var func_addr = base_addr.add(offset); 5 | if (Process.arch == 'arm') 6 | return func_addr.add(1); //如果是32位地址+1 7 | else 8 | return func_addr; 9 | } 10 | 11 | 12 | // 对指定的寄存器进行hook 13 | // module : so库 14 | // offset : IDA中的偏移地址 15 | function hook_native(module, offset) { 16 | 17 | var fun_addr = get_func_addr(module, offset) 18 | console.log("fun_addr:", fun_addr); 19 | if (fun_addr) { 20 | Interceptor.attach(fun_addr, { 21 | onEnter: function (args) { 22 | // 自定义对指定寄存器hook 23 | console.log("len:", this.context.r1); 24 | }, onLeave: function (retval) { 25 | } 26 | }); 27 | } 28 | 29 | 30 | } 31 | 32 | function hook_dlopen() { 33 | var targetLib = 'libxyz.so'; 34 | var base_android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext"); 35 | if (base_android_dlopen_ext) { 36 | Interceptor.attach(base_android_dlopen_ext, { 37 | onEnter: function (args) { 38 | //加个判断标识位 39 | this.dist_so = false; 40 | var so_name = ptr(args[0]).readCString(); 41 | if (so_name.indexOf(targetLib) >= 0) { 42 | //打印android_dlopen_ext打开的so文件 43 | console.log("hook_android_dlopen_ext:", ptr(args[0]).readCString()) 44 | this.dist_so = true; 45 | } 46 | }, onLeave: function (retval) { 47 | if (this.dist_so) { 48 | hook_native(targetLib, 0x89FE); 49 | } 50 | } 51 | }) 52 | } 53 | } 54 | 55 | function main() { 56 | hook_dlopen(); 57 | 58 | } 59 | 60 | setImmediate(main); -------------------------------------------------------------------------------- /init_array.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 参考:[frida hook init_array自吐新解](https://blog.seeflower.dev/archives/299/) 3 | * 4 | * @file Frida Hook init_array 脚本 5 | * @description 本脚本通过 Frida 的 CModule 模块解析 soinfo 结构体,hook call_constructors 函数以输出 init_array 信息。 6 | * 适用于 Android 8 ~ 14 的 32/64 位系统,可兼容 Android 5/6/7(需根据实际情况调整)。 7 | * 8 | * @usage 9 | * 1. 使用 Frida 进行注入,例如: 10 | * frida -U -f [目标应用包名] -l [本脚本路径] -o [输出日志文件] 11 | * 2. 确保目标设备的 Frida 版本与脚本兼容。 12 | * 3. 根据实际需求调整脚本中的模块名、符号地址等参数。 13 | * 14 | * @note 15 | * - 本脚本依赖 Frida 的 CModule 功能,确保运行环境支持该模块。 16 | * - 在不同 Android 版本或设备架构上,可能需要重新验证 soinfo 结构体的定义及符号地址。 17 | * - 输出的日志信息包含 init_array 的详细数据,可根据实际分析需求进一步处理。 18 | */ 19 | 20 | let cm_include = ` 21 | #include 22 | #include 23 | ` 24 | 25 | let cm_code = ` 26 | #if defined(__LP64__) 27 | #define USE_RELA 1 28 | #endif 29 | 30 | // http://aosp.app/android-14.0.0_r1/xref/bionic/libc/include/link.h 31 | #if defined(__LP64__) 32 | #define ElfW(type) Elf64_ ## type 33 | #else 34 | #define ElfW(type) Elf32_ ## type 35 | #endif 36 | 37 | // http://aosp.app/android-14.0.0_r1/xref/bionic/libc/kernel/uapi/asm-generic/int-ll64.h 38 | typedef signed char __s8; 39 | typedef unsigned char __u8; 40 | typedef signed short __s16; 41 | typedef unsigned short __u16; 42 | typedef signed int __s32; 43 | typedef unsigned int __u32; 44 | typedef signed long long __s64; 45 | typedef unsigned long long __u64; 46 | 47 | // http://aosp.app/android-14.0.0_r1/xref/bionic/libc/kernel/uapi/linux/elf.h 48 | typedef __u32 Elf32_Addr; 49 | typedef __u16 Elf32_Half; 50 | typedef __u32 Elf32_Off; 51 | typedef __s32 Elf32_Sword; 52 | typedef __u32 Elf32_Word; 53 | typedef __u64 Elf64_Addr; 54 | typedef __u16 Elf64_Half; 55 | typedef __s16 Elf64_SHalf; 56 | typedef __u64 Elf64_Off; 57 | typedef __s32 Elf64_Sword; 58 | typedef __u32 Elf64_Word; 59 | typedef __u64 Elf64_Xword; 60 | typedef __s64 Elf64_Sxword; 61 | 62 | typedef struct dynamic { 63 | Elf32_Sword d_tag; 64 | union { 65 | Elf32_Sword d_val; 66 | Elf32_Addr d_ptr; 67 | } d_un; 68 | } Elf32_Dyn; 69 | typedef struct { 70 | Elf64_Sxword d_tag; 71 | union { 72 | Elf64_Xword d_val; 73 | Elf64_Addr d_ptr; 74 | } d_un; 75 | } Elf64_Dyn; 76 | typedef struct elf32_rel { 77 | Elf32_Addr r_offset; 78 | Elf32_Word r_info; 79 | } Elf32_Rel; 80 | typedef struct elf64_rel { 81 | Elf64_Addr r_offset; 82 | Elf64_Xword r_info; 83 | } Elf64_Rel; 84 | typedef struct elf32_rela { 85 | Elf32_Addr r_offset; 86 | Elf32_Word r_info; 87 | Elf32_Sword r_addend; 88 | } Elf32_Rela; 89 | typedef struct elf64_rela { 90 | Elf64_Addr r_offset; 91 | Elf64_Xword r_info; 92 | Elf64_Sxword r_addend; 93 | } Elf64_Rela; 94 | typedef struct elf32_sym { 95 | Elf32_Word st_name; 96 | Elf32_Addr st_value; 97 | Elf32_Word st_size; 98 | unsigned char st_info; 99 | unsigned char st_other; 100 | Elf32_Half st_shndx; 101 | } Elf32_Sym; 102 | typedef struct elf64_sym { 103 | Elf64_Word st_name; 104 | unsigned char st_info; 105 | unsigned char st_other; 106 | Elf64_Half st_shndx; 107 | Elf64_Addr st_value; 108 | Elf64_Xword st_size; 109 | } Elf64_Sym; 110 | typedef struct elf32_phdr { 111 | Elf32_Word p_type; 112 | Elf32_Off p_offset; 113 | Elf32_Addr p_vaddr; 114 | Elf32_Addr p_paddr; 115 | Elf32_Word p_filesz; 116 | Elf32_Word p_memsz; 117 | Elf32_Word p_flags; 118 | Elf32_Word p_align; 119 | } Elf32_Phdr; 120 | typedef struct elf64_phdr { 121 | Elf64_Word p_type; 122 | Elf64_Word p_flags; 123 | Elf64_Off p_offset; 124 | Elf64_Addr p_vaddr; 125 | Elf64_Addr p_paddr; 126 | Elf64_Xword p_filesz; 127 | Elf64_Xword p_memsz; 128 | Elf64_Xword p_align; 129 | } Elf64_Phdr; 130 | 131 | // http://aosp.app/android-14.0.0_r1/xref/bionic/linker/linker_soinfo.h 132 | typedef void (*linker_dtor_function_t)(); 133 | typedef void (*linker_ctor_function_t)(int, char**, char**); 134 | 135 | #if defined(__work_around_b_24465209__) 136 | #define SOINFO_NAME_LEN 128 137 | #endif 138 | 139 | typedef struct { 140 | #if defined(__work_around_b_24465209__) 141 | char old_name_[SOINFO_NAME_LEN]; 142 | #endif 143 | const ElfW(Phdr)* phdr; 144 | size_t phnum; 145 | #if defined(__work_around_b_24465209__) 146 | ElfW(Addr) unused0; // DO NOT USE, maintained for compatibility. 147 | #endif 148 | ElfW(Addr) base; 149 | size_t size; 150 | 151 | #if defined(__work_around_b_24465209__) 152 | uint32_t unused1; // DO NOT USE, maintained for compatibility. 153 | #endif 154 | 155 | ElfW(Dyn)* dynamic; 156 | 157 | #if defined(__work_around_b_24465209__) 158 | uint32_t unused2; // DO NOT USE, maintained for compatibility 159 | uint32_t unused3; // DO NOT USE, maintained for compatibility 160 | #endif 161 | 162 | void* next; 163 | uint32_t flags_; 164 | 165 | const char* strtab_; 166 | ElfW(Sym)* symtab_; 167 | 168 | size_t nbucket_; 169 | size_t nchain_; 170 | uint32_t* bucket_; 171 | uint32_t* chain_; 172 | 173 | #if !defined(__LP64__) 174 | ElfW(Addr)** unused4; // DO NOT USE, maintained for compatibility 175 | #endif 176 | 177 | #if defined(USE_RELA) 178 | ElfW(Rela)* plt_rela_; 179 | size_t plt_rela_count_; 180 | 181 | ElfW(Rela)* rela_; 182 | size_t rela_count_; 183 | #else 184 | ElfW(Rel)* plt_rel_; 185 | size_t plt_rel_count_; 186 | 187 | ElfW(Rel)* rel_; 188 | size_t rel_count_; 189 | #endif 190 | 191 | linker_ctor_function_t* preinit_array_; 192 | size_t preinit_array_count_; 193 | 194 | linker_ctor_function_t* init_array_; 195 | size_t init_array_count_; 196 | linker_dtor_function_t* fini_array_; 197 | size_t fini_array_count_; 198 | 199 | linker_ctor_function_t init_func_; 200 | linker_dtor_function_t fini_func_; 201 | } soinfo; 202 | 203 | void tell_init_info(soinfo* ptr, void (*cb)(int, void*, void*)) { 204 | cb(ptr->init_array_count_, ptr->init_array_, ptr->init_func_); 205 | } 206 | ` 207 | 208 | let cm = null; 209 | let tell_init_info = null; 210 | 211 | function setup_cmodule() { 212 | if (Process.pointerSize == 4) { 213 | cm_code = cm_include + "#define __work_around_b_24465209__ 1" + cm_code; 214 | } else { 215 | cm_code = cm_include + "#define __LP64__ 1" + cm_code; 216 | } 217 | cm = new CModule(cm_code, {}); 218 | tell_init_info = new NativeFunction(cm.tell_init_info, "void", ["pointer", "pointer"]); 219 | } 220 | 221 | function get_addr_info(addr) { 222 | let mm = new ModuleMap(); 223 | let info = mm.find(addr); 224 | if (info == null) return "null"; 225 | return `[${info.name} + ${addr.sub(info.base)}]`; 226 | } 227 | 228 | function hook_call_constructors() { 229 | let get_soname = null; 230 | let call_constructors_addr = null; 231 | let hook_call_constructors_addr = true; 232 | 233 | let linker = null; 234 | if (Process.pointerSize == 4) { 235 | linker = Process.findModuleByName("linker"); 236 | } else { 237 | linker = Process.findModuleByName("linker64"); 238 | } 239 | 240 | let symbols = linker.enumerateSymbols(); 241 | for (let index = 0; index < symbols.length; index++) { 242 | let symbol = symbols[index]; 243 | if (symbol.name == "__dl__ZN6soinfo17call_constructorsEv") { 244 | call_constructors_addr = symbol.address; 245 | } else if (symbol.name == "__dl__ZNK6soinfo10get_sonameEv") { 246 | get_soname = new NativeFunction(symbol.address, "pointer", ["pointer"]); 247 | } 248 | } 249 | if (hook_call_constructors_addr && call_constructors_addr && get_soname) { 250 | Interceptor.attach(call_constructors_addr, { 251 | onEnter: function (args) { 252 | let soinfo = args[0]; 253 | let soname = get_soname(soinfo).readCString(); 254 | tell_init_info(soinfo, new NativeCallback((count, init_array_ptr, init_func) => { 255 | console.log(); 256 | console.log(`[call_constructors] ${soname} count:${count}`); 257 | console.log(`[call_constructors] init_array_ptr:${init_array_ptr}`); 258 | console.log(`[call_constructors] init_func:${init_func} -> ${get_addr_info(init_func)}`); 259 | for (let index = 0; index < count; index++) { 260 | let init_array_func = init_array_ptr.add(Process.pointerSize * index).readPointer(); 261 | let func_info = get_addr_info(init_array_func); 262 | console.log(`[call_constructors] init_array:${index} ${init_array_func} -> ${func_info}`); 263 | } 264 | }, "void", ["int", "pointer", "pointer"])); 265 | } 266 | }); 267 | } 268 | } 269 | 270 | function main() { 271 | setup_cmodule(); 272 | hook_call_constructors(); 273 | } 274 | 275 | setImmediate(main); 276 | 277 | // frida -U -f pkg_name -l hook.js -o hook.log -------------------------------------------------------------------------------- /jni/RegisterNatives.js: -------------------------------------------------------------------------------- 1 | /* 2 | 先动态获取 libart 里所有可以动态注册native函数的函数,然后逐个拦截。 3 | */ 4 | 5 | var RegisterNativesarray = []; 6 | var symbols = Module.enumerateSymbolsSync("libart.so"); 7 | for (var i = 0; i < symbols.length; i++) { 8 | var symbol = symbols[i]; 9 | if (symbol.name.indexOf("art") >= 0 &&symbol.name.indexOf("JNI") >= 0 && symbol.name.indexOf("RegisterNatives") >= 0 10 | ) { 11 | RegisterNativesarray.push(symbol.address); 12 | console.log("RegisterNatives is at ", symbol.address, symbol.name); 13 | continue; 14 | } 15 | } 16 | 17 | 18 | if (RegisterNativesarray.length > 0) { 19 | for (let i = 0; i < RegisterNativesarray.length; i++) { 20 | // 使用闭包确保当前的 i 被正确捕获 21 | (function(i) { 22 | Interceptor.attach(RegisterNativesarray[i], { 23 | onEnter: function (args) { 24 | console.log("come to addrRegisterNatives[" + i + "]"); // 输出正确的 i 25 | var env = args[0]; // jni对象 26 | var java_class = args[1]; // 类 27 | var class_name = Java.vm.tryGetEnv().getClassName(java_class); 28 | var taget_class = "com.luckincoffee.safeboxlib.CryptoHelper"; // 目标类名 29 | if (class_name === taget_class) { 30 | console.log("\n[RegisterNatives] method_count:", args[3]); 31 | var methods_ptr = ptr(args[2]); 32 | var method_count = parseInt(args[3]); 33 | for (var j = 0; j < method_count; j++) { // 这里使用另一个 i 作为方法索引 34 | var name_ptr = Memory.readPointer(methods_ptr.add(j * Process.pointerSize * 3)); 35 | var sig_ptr = Memory.readPointer(methods_ptr.add(j * Process.pointerSize * 3 + Process.pointerSize)); 36 | var fnPtr_ptr = Memory.readPointer(methods_ptr.add(j * Process.pointerSize * 3 + Process.pointerSize * 2)); 37 | var name = Memory.readCString(name_ptr); 38 | var sig = Memory.readCString(sig_ptr); 39 | var find_module = Process.findModuleByAddress(fnPtr_ptr); 40 | var offset = ptr(fnPtr_ptr).sub(find_module.base); 41 | console.log('class_name:', class_name, "name:", name, "sig:", sig, 'module_name:', find_module.name, "offset:", offset); 42 | } 43 | } 44 | } 45 | }); 46 | })(i); // 在此传入当前的 i 47 | } 48 | } -------------------------------------------------------------------------------- /jni/hook_RegisterNatives.js: -------------------------------------------------------------------------------- 1 | 2 | function find_RegisterNatives(params) { 3 | var symbols = Module.enumerateSymbolsSync("libart.so"); 4 | var addrRegisterNatives = null; 5 | for (var i = 0; i < symbols.length; i++) { 6 | var symbol = symbols[i]; 7 | 8 | //_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi 9 | if (symbol.name.indexOf("art") >= 0 && 10 | symbol.name.indexOf("JNI") >= 0 && 11 | symbol.name.indexOf("RegisterNatives") >= 0 && 12 | symbol.name.indexOf("CheckJNI") < 0) { 13 | addrRegisterNatives = symbol.address; 14 | console.log("RegisterNatives is at ", symbol.address, symbol.name); 15 | hook_RegisterNatives(addrRegisterNatives) 16 | } 17 | } 18 | 19 | } 20 | 21 | function hook_RegisterNatives(addrRegisterNatives) { 22 | 23 | if (addrRegisterNatives != null) { 24 | Interceptor.attach(addrRegisterNatives, { 25 | onEnter: function (args) { 26 | console.log("[RegisterNatives] method_count:", args[3]); 27 | var env = args[0]; 28 | var java_class = args[1]; 29 | var class_name = Java.vm.tryGetEnv().getClassName(java_class); 30 | //console.log(class_name); 31 | 32 | var methods_ptr = ptr(args[2]); 33 | 34 | var method_count = parseInt(args[3]); 35 | for (var i = 0; i < method_count; i++) { 36 | var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3)); 37 | var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize)); 38 | var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2)); 39 | 40 | var name = Memory.readCString(name_ptr); 41 | var sig = Memory.readCString(sig_ptr); 42 | var find_module = Process.findModuleByAddress(fnPtr_ptr); 43 | console.log("[RegisterNatives] java_class:", class_name, "name:", name, "sig:", sig, "fnPtr:", fnPtr_ptr, " fnOffset:", ptr(fnPtr_ptr).sub(find_module.base), " callee:", DebugSymbol.fromAddress(this.returnAddress)); 44 | 45 | } 46 | } 47 | }); 48 | } 49 | } 50 | 51 | setImmediate(find_RegisterNatives); 52 | -------------------------------------------------------------------------------- /jni/hook_RegisterNatives2.js: -------------------------------------------------------------------------------- 1 | // hook register 打印动态注册的函数地址 2 | function hook_register(){ 3 | // libart.so 所有导出函数表 4 | var symbols = Module.enumerateSymbolsSync("libart.so"); 5 | var addr_register = null; 6 | for(var i = 0; i < symbols.length; i++){ 7 | var symbol = symbols[i]; 8 | var method_name = symbol.name; 9 | if(method_name.indexOf("art") >= 0){ 10 | 11 | if(method_name.indexOf("_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi") >= 0){ 12 | addr_register = symbol.address; 13 | } 14 | } 15 | } 16 | 17 | // 开始hook 18 | if(addr_register){ 19 | Interceptor.attach(addr_register, { 20 | onEnter: function(args){ 21 | var methods = ptr(args[2]); 22 | var method_count = args[3]; 23 | console.log("[RegisterNatives] method_count:", method_count); 24 | for(var i = 0; i < method_count; i++){ 25 | var fn_ptr = methods.add(i * Process.pointerSize * 3 + Process.pointerSize * 2).readPointer(); 26 | var find_module = Process.findModuleByAddress(fn_ptr); 27 | if(i == 0){ 28 | console.log("module name", find_module.name); 29 | console.log("module base", find_module.base); 30 | } 31 | console.log("\t method_name:", methods.add(i * Process.pointerSize * 3).readPointer().readCString(), "method_sign:", methods.add(i * Process.pointerSize * 3 + Process.pointerSize).readPointer().readCString(), "method_fnPtr:", fn_ptr, "method offset:", fn_ptr.sub(find_module.base)); 32 | } 33 | }, onLeave(retval){ 34 | 35 | } 36 | }) 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /jni/hook_art.js: -------------------------------------------------------------------------------- 1 | 2 | const STD_STRING_SIZE = 3 * Process.pointerSize; 3 | class StdString { 4 | constructor() { 5 | this.handle = Memory.alloc(STD_STRING_SIZE); 6 | } 7 | 8 | dispose() { 9 | const [data, isTiny] = this._getData(); 10 | if (!isTiny) { 11 | Java.api.$delete(data); 12 | } 13 | } 14 | 15 | disposeToString() { 16 | const result = this.toString(); 17 | this.dispose(); 18 | return result; 19 | } 20 | 21 | toString() { 22 | const [data] = this._getData(); 23 | return data.readUtf8String(); 24 | } 25 | 26 | _getData() { 27 | const str = this.handle; 28 | const isTiny = (str.readU8() & 1) === 0; 29 | const data = isTiny ? str.add(1) : str.add(2 * Process.pointerSize).readPointer(); 30 | return [data, isTiny]; 31 | } 32 | } 33 | 34 | function prettyMethod(method_id, withSignature) { 35 | const result = new StdString(); 36 | Java.api['art::ArtMethod::PrettyMethod'](result, method_id, withSignature ? 1 : 0); 37 | return result.disposeToString(); 38 | } 39 | 40 | /* 41 | GetFieldID is at 0xe39b87c5 _ZN3art3JNI10GetFieldIDEP7_JNIEnvP7_jclassPKcS6_ 42 | GetMethodID is at 0xe39a1a19 _ZN3art3JNI11GetMethodIDEP7_JNIEnvP7_jclassPKcS6_ 43 | NewStringUTF is at 0xe39cff25 _ZN3art3JNI12NewStringUTFEP7_JNIEnvPKc 44 | RegisterNatives is at 0xe39e08fd _ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi 45 | GetStaticFieldID is at 0xe39c9635 _ZN3art3JNI16GetStaticFieldIDEP7_JNIEnvP7_jclassPKcS6_ 46 | GetStaticMethodID is at 0xe39be0ed _ZN3art3JNI17GetStaticMethodIDEP7_JNIEnvP7_jclassPKcS6_ 47 | GetStringUTFChars is at 0xe39d06e5 _ZN3art3JNI17GetStringUTFCharsEP7_JNIEnvP8_jstringPh 48 | FindClass is at 0xe399ae5d _ZN3art3JNI9FindClassEP7_JNIEnvPKc 49 | */ 50 | 51 | function hook_libart() { 52 | var symbols = Module.enumerateSymbolsSync("libart.so"); 53 | var addrGetStringUTFChars = null; 54 | var addrNewStringUTF = null; 55 | var addrFindClass = null; 56 | var addrGetMethodID = null; 57 | var addrGetStaticMethodID = null; 58 | var addrGetFieldID = null; 59 | var addrGetStaticFieldID = null; 60 | var addrRegisterNatives = null; 61 | var so_name = "lib"; //TODO 这里写需要过滤的so 62 | console.log("todo?") 63 | 64 | for (var i = 0; i < symbols.length; i++) { 65 | 66 | var symbol = symbols[i]; 67 | // console.log(JSON.stringify( symbol)) 68 | if (symbol.name.indexOf("art") >= 0 && 69 | symbol.name.indexOf("JNI") >= 0 && 70 | symbol.name.indexOf("CheckJNI") < 0 71 | // symbol.name.indexOf("_ZN3art3JNIILb0") >= 0 72 | ) { 73 | if (symbol.name.indexOf("GetStringUTFChars") >= 0) { 74 | addrGetStringUTFChars = symbol.address; 75 | console.log("GetStringUTFChars is at ", symbol.address, symbol.name); 76 | } else if (symbol.name.indexOf("NewStringUTF") >= 0) { 77 | addrNewStringUTF = symbol.address; 78 | console.log("NewStringUTF is at ", symbol.address, symbol.name); 79 | } else if (symbol.name.indexOf("FindClass") >= 0) { 80 | addrFindClass = symbol.address; 81 | console.log("FindClass is at ", symbol.address, symbol.name); 82 | } else if (symbol.name.indexOf("GetMethodID") >= 0) { 83 | addrGetMethodID = symbol.address; 84 | console.log("GetMethodID is at ", symbol.address, symbol.name); 85 | } else if (symbol.name.indexOf("GetStaticMethodID") >= 0) { 86 | addrGetStaticMethodID = symbol.address; 87 | console.log("GetStaticMethodID is at ", symbol.address, symbol.name); 88 | } else if (symbol.name.indexOf("GetFieldID") >= 0) { 89 | addrGetFieldID = symbol.address; 90 | console.log("GetFieldID is at ", symbol.address, symbol.name); 91 | } else if (symbol.name.indexOf("GetStaticFieldID") >= 0) { 92 | addrGetStaticFieldID = symbol.address; 93 | console.log("GetStaticFieldID is at ", symbol.address, symbol.name); 94 | } else if (symbol.name.indexOf("RegisterNatives") >= 0) { 95 | addrRegisterNatives = symbol.address; 96 | console.log("RegisterNatives is at ", symbol.address, symbol.name); 97 | } else if (symbol.name.indexOf("CallStatic") >= 0) { 98 | console.log("CallStatic is at ", symbol.address, symbol.name); 99 | Interceptor.attach(symbol.address, { 100 | onEnter: function (args) { 101 | var module = Process.findModuleByAddress(this.returnAddress); 102 | if (module != null && module.name.indexOf(so_name) == 0) { 103 | var java_class = args[1]; 104 | var mid = args[2]; 105 | var class_name = Java.vm.tryGetEnv().getClassName(java_class); 106 | if (class_name.indexOf("java.") == -1 && class_name.indexOf("android.") == -1) { 107 | var method_name = prettyMethod(mid, 1); 108 | console.log("<>CallStatic:", DebugSymbol.fromAddress(this.returnAddress), class_name, method_name); 109 | } 110 | } 111 | }, 112 | onLeave: function (retval) { } 113 | }); 114 | } else if (symbol.name.indexOf("CallNonvirtual") >= 0) { 115 | console.log("CallNonvirtual is at ", symbol.address, symbol.name); 116 | Interceptor.attach(symbol.address, { 117 | onEnter: function (args) { 118 | var module = Process.findModuleByAddress(this.returnAddress); 119 | if (module != null && module.name.indexOf(so_name) == 0) { 120 | var jobject = args[1]; 121 | var jclass = args[2]; 122 | var jmethodID = args[3]; 123 | var obj_class_name = Java.vm.tryGetEnv().getObjectClassName(jobject); 124 | var class_name = Java.vm.tryGetEnv().getClassName(jclass); 125 | if (class_name.indexOf("java.") == -1 && class_name.indexOf("android.") == -1) { 126 | var method_name = prettyMethod(jmethodID, 1); 127 | console.log("<>CallNonvirtual:", DebugSymbol.fromAddress(this.returnAddress), class_name, obj_class_name, method_name); 128 | } 129 | } 130 | }, 131 | onLeave: function (retval) { } 132 | }); 133 | } else if (symbol.name.indexOf("Call") >= 0 && symbol.name.indexOf("Method") >= 0) { 134 | console.log("Call<>Method is at ", symbol.address, symbol.name); 135 | Interceptor.attach(symbol.address, { 136 | onEnter: function (args) { 137 | var module = Process.findModuleByAddress(this.returnAddress); 138 | if (module != null && module.name.indexOf(so_name) == 0) { 139 | var java_class = args[1]; 140 | var mid = args[2]; 141 | var class_name = Java.vm.tryGetEnv().getObjectClassName(java_class); 142 | if (class_name.indexOf("java.") == -1 && class_name.indexOf("android.") == -1) { 143 | var method_name = prettyMethod(mid, 1); 144 | console.log("<>Call<>Method:", DebugSymbol.fromAddress(this.returnAddress), class_name, method_name); 145 | } 146 | } 147 | }, 148 | onLeave: function (retval) { } 149 | }); 150 | } 151 | } 152 | } 153 | 154 | if (addrGetStringUTFChars != null) { 155 | Interceptor.attach(addrGetStringUTFChars, { 156 | onEnter: function (args) { 157 | }, 158 | onLeave: function (retval) { 159 | if (retval != null) { 160 | var module = Process.findModuleByAddress(this.returnAddress); 161 | if (module != null && module.name.indexOf(so_name) == 0) { 162 | var bytes = Memory.readCString(retval); 163 | console.log("[GetStringUTFChars] result:" + bytes, DebugSymbol.fromAddress(this.returnAddress)); 164 | } 165 | } 166 | } 167 | }); 168 | } 169 | if (addrNewStringUTF != null) { 170 | Interceptor.attach(addrNewStringUTF, { 171 | onEnter: function (args) { 172 | if (args[1] != null) { 173 | var module = Process.findModuleByAddress(this.returnAddress); 174 | if (module != null && module.name.indexOf(so_name) == 0) { 175 | var string = Memory.readCString(args[1]); 176 | console.log("[NewStringUTF] bytes:" + string, DebugSymbol.fromAddress(this.returnAddress)); 177 | } 178 | 179 | } 180 | }, 181 | onLeave: function (retval) { } 182 | }); 183 | } 184 | 185 | if (addrFindClass != null) { 186 | Interceptor.attach(addrFindClass, { 187 | onEnter: function (args) { 188 | if (args[1] != null) { 189 | var module = Process.findModuleByAddress(this.returnAddress); 190 | if (module != null && module.name.indexOf(so_name) == 0) { 191 | var name = Memory.readCString(args[1]); 192 | console.log("[FindClass] name:" + name, DebugSymbol.fromAddress(this.returnAddress)); 193 | } 194 | } 195 | }, 196 | onLeave: function (retval) { } 197 | }); 198 | } 199 | if (addrGetMethodID != null) { 200 | Interceptor.attach(addrGetMethodID, { 201 | onEnter: function (args) { 202 | if (args[2] != null) { 203 | var clazz = args[1]; 204 | var class_name = Java.vm.tryGetEnv().getClassName(clazz); 205 | var module = Process.findModuleByAddress(this.returnAddress); 206 | if (module != null && module.name.indexOf(so_name) == 0) { 207 | var name = Memory.readCString(args[2]); 208 | if (args[3] != null) { 209 | var sig = Memory.readCString(args[3]); 210 | console.log("[GetMethodID] class_name:" + class_name + " name:" + name + ", sig:" + sig, DebugSymbol.fromAddress(this.returnAddress)); 211 | } else { 212 | console.log("[GetMethodID] class_name:" + class_name + " name:" + name, DebugSymbol.fromAddress(this.returnAddress)); 213 | } 214 | } 215 | } 216 | }, 217 | onLeave: function (retval) { } 218 | }); 219 | } 220 | if (addrGetStaticMethodID != null) { 221 | Interceptor.attach(addrGetStaticMethodID, { 222 | onEnter: function (args) { 223 | if (args[2] != null) { 224 | var clazz = args[1]; 225 | var class_name = Java.vm.tryGetEnv().getClassName(clazz); 226 | var module = Process.findModuleByAddress(this.returnAddress); 227 | if (module != null && module.name.indexOf(so_name) == 0) { 228 | var name = Memory.readCString(args[2]); 229 | if (args[3] != null) { 230 | var sig = Memory.readCString(args[3]); 231 | console.log("[GetStaticMethodID] class_name:" + class_name + " name:" + name + ", sig:" + sig, DebugSymbol.fromAddress(this.returnAddress)); 232 | } else { 233 | console.log("[GetStaticMethodID] class_name:" + class_name + " name:" + name, DebugSymbol.fromAddress(this.returnAddress)); 234 | } 235 | } 236 | } 237 | }, 238 | onLeave: function (retval) { } 239 | }); 240 | } 241 | if (addrGetFieldID != null) { 242 | Interceptor.attach(addrGetFieldID, { 243 | onEnter: function (args) { 244 | if (args[2] != null) { 245 | var module = Process.findModuleByAddress(this.returnAddress); 246 | if (module != null && module.name.indexOf(so_name) == 0) { 247 | var name = Memory.readCString(args[2]); 248 | if (args[3] != null) { 249 | var sig = Memory.readCString(args[3]); 250 | console.log("[GetFieldID] name:" + name + ", sig:" + sig, DebugSymbol.fromAddress(this.returnAddress)); 251 | } else { 252 | console.log("[GetFieldID] name:" + name, DebugSymbol.fromAddress(this.returnAddress)); 253 | } 254 | } 255 | } 256 | }, 257 | onLeave: function (retval) { } 258 | }); 259 | } 260 | if (addrGetStaticFieldID != null) { 261 | Interceptor.attach(addrGetStaticFieldID, { 262 | onEnter: function (args) { 263 | if (args[2] != null) { 264 | var module = Process.findModuleByAddress(this.returnAddress); 265 | if (module != null && module.name.indexOf(so_name) == 0) { 266 | var name = Memory.readCString(args[2]); 267 | if (args[3] != null) { 268 | var sig = Memory.readCString(args[3]); 269 | console.log("[GetStaticFieldID] name:" + name + ", sig:" + sig, DebugSymbol.fromAddress(this.returnAddress)); 270 | } else { 271 | console.log("[GetStaticFieldID] name:" + name, DebugSymbol.fromAddress(this.returnAddress)); 272 | } 273 | } 274 | } 275 | }, 276 | onLeave: function (retval) { } 277 | }); 278 | } 279 | 280 | if (addrRegisterNatives != null) { 281 | Interceptor.attach(addrRegisterNatives, { 282 | onEnter: function (args) { 283 | console.log("[RegisterNatives] method_count:", args[3], DebugSymbol.fromAddress(this.returnAddress)); 284 | var env = args[0]; 285 | var java_class = args[1]; 286 | var class_name = Java.vm.tryGetEnv().getClassName(java_class); 287 | 288 | var methods_ptr = ptr(args[2]); 289 | 290 | var method_count = parseInt(args[3]); 291 | for (var i = 0; i < method_count; i++) { 292 | var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3)); 293 | var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize)); 294 | var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2)); 295 | 296 | var name = Memory.readCString(name_ptr); 297 | var sig = Memory.readCString(sig_ptr); 298 | var find_module = Process.findModuleByAddress(fnPtr_ptr); 299 | console.log("[RegisterNatives] java_class:", class_name, "name:", name, "sig:", sig, "fnPtr:", fnPtr_ptr, "module_name:", find_module.name, "module_base:", find_module.base, "offset:", ptr(fnPtr_ptr).sub(find_module.base)); 300 | 301 | } 302 | }, 303 | onLeave: function (retval) { } 304 | }); 305 | } 306 | } 307 | 308 | setImmediate(hook_libart); 309 | 310 | -------------------------------------------------------------------------------- /jni/hook_artmethod.js: -------------------------------------------------------------------------------- 1 | 2 | const STD_STRING_SIZE = 3 * Process.pointerSize; 3 | class StdString { 4 | constructor() { 5 | this.handle = Memory.alloc(STD_STRING_SIZE); 6 | } 7 | 8 | dispose() { 9 | const [data, isTiny] = this._getData(); 10 | if (!isTiny) { 11 | Java.api.$delete(data); 12 | } 13 | } 14 | 15 | disposeToString() { 16 | const result = this.toString(); 17 | this.dispose(); 18 | return result; 19 | } 20 | 21 | toString() { 22 | const [data] = this._getData(); 23 | return data.readUtf8String(); 24 | } 25 | 26 | _getData() { 27 | const str = this.handle; 28 | const isTiny = (str.readU8() & 1) === 0; 29 | const data = isTiny ? str.add(1) : str.add(2 * Process.pointerSize).readPointer(); 30 | return [data, isTiny]; 31 | } 32 | } 33 | 34 | function prettyMethod(method_id, withSignature) { 35 | const result = new StdString(); 36 | Java.api['art::ArtMethod::PrettyMethod'](result, method_id, withSignature ? 1 : 0); 37 | return result.disposeToString(); 38 | } 39 | 40 | function hook_dlopen(module_name, fun) { 41 | var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext"); 42 | 43 | if (android_dlopen_ext) { 44 | Interceptor.attach(android_dlopen_ext, { 45 | onEnter: function (args) { 46 | var pathptr = args[0]; 47 | if (pathptr) { 48 | this.path = (pathptr).readCString(); 49 | if (this.path.indexOf(module_name) >= 0) { 50 | this.canhook = true; 51 | console.log("android_dlopen_ext:", this.path); 52 | } 53 | } 54 | }, 55 | onLeave: function (retval) { 56 | if (this.canhook) { 57 | fun(); 58 | } 59 | } 60 | }); 61 | } 62 | var dlopen = Module.findExportByName(null, "dlopen"); 63 | if (dlopen) { 64 | Interceptor.attach(dlopen, { 65 | onEnter: function (args) { 66 | var pathptr = args[0]; 67 | if (pathptr) { 68 | this.path = (pathptr).readCString(); 69 | if (this.path.indexOf(module_name) >= 0) { 70 | this.canhook = true; 71 | console.log("dlopen:", this.path); 72 | } 73 | } 74 | }, 75 | onLeave: function (retval) { 76 | if (this.canhook) { 77 | fun(); 78 | } 79 | } 80 | }); 81 | } 82 | console.log("android_dlopen_ext:", android_dlopen_ext, "dlopen:", dlopen); 83 | } 84 | 85 | 86 | function hook_native() { 87 | var module_libart = Process.findModuleByName("libart.so"); 88 | var symbols = module_libart.enumerateSymbols(); 89 | var ArtMethod_Invoke = null; 90 | for (var i = 0; i < symbols.length; i++) { 91 | var symbol = symbols[i]; 92 | var address = symbol.address; 93 | var name = symbol.name; 94 | var indexArtMethod = name.indexOf("ArtMethod"); 95 | var indexInvoke = name.indexOf("Invoke"); 96 | var indexThread = name.indexOf("Thread"); 97 | if (indexArtMethod >= 0 98 | && indexInvoke >= 0 99 | && indexThread >= 0 100 | && indexArtMethod < indexInvoke 101 | && indexInvoke < indexThread) { 102 | console.log(name); 103 | ArtMethod_Invoke = address; 104 | } 105 | } 106 | if (ArtMethod_Invoke) { 107 | Interceptor.attach(ArtMethod_Invoke, { 108 | onEnter: function (args) { 109 | var method_name = prettyMethod(args[0], 0); 110 | if (!(method_name.indexOf("java.") == 0 || method_name.indexOf("android.") == 0)) { 111 | console.log("ArtMethod Invoke:" + method_name + ' called from:\n' + 112 | Thread.backtrace(this.context, Backtracer.ACCURATE) 113 | .map(DebugSymbol.fromAddress).join('\n') + '\n'); 114 | } 115 | } 116 | }); 117 | } 118 | } 119 | 120 | function main() { 121 | hook_dlopen("libart.so", hook_native); 122 | hook_native(); 123 | } 124 | 125 | setImmediate(main); -------------------------------------------------------------------------------- /jni/jni.md: -------------------------------------------------------------------------------- 1 | # hook art 2 | 3 | ## 1. hook_art 4 | 5 | ```text 6 | frida -U --no-pause -f package_name -l hook_art.js 7 | ``` 8 | 9 | ## 2. hook_RegisterNatives 10 | 11 | ```text 12 | frida -U --no-pause -f package_name -l hook_RegisterNatives.js 13 | ``` 14 | 15 | ### 2.1 show RegisterNatives 16 | 17 | ```text 18 | [RegisterNatives] method_count: 0x1 19 | [RegisterNatives] java_class: com.facebook.react.bridge.ProxyJavaScriptExecutor name: initHybrid sig: (Lcom/facebook/react/bridge/JavaJSExecutor;)Lcom/facebook/jni/HybridData; fnPtr: 0x9ca00b61 module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x6fb61 20 | [RegisterNatives] method_count: 0xe 21 | [RegisterNatives] java_class: com.facebook.react.bridge.CatalystInstanceImpl name: initHybrid sig: ()Lcom/facebook/jni/HybridData; fnPtr: 0x9c9e1c65 module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x50c65 22 | [RegisterNatives] java_class: com.facebook.react.bridge.CatalystInstanceImpl name: initializeBridge sig: (Lcom/facebook/react/bridge/ReactCallback;Lcom/facebook/react/bridge/JavaScriptExecutor;Lcom/facebook/react/bridge/queue/MessageQueueThread;Lcom/facebook/react/bridge/queue/MessageQueueThread;Ljava/util/Collection;Ljava/util/Collection;)V fnPtr: 0x9c9e2401 module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x51401 23 | [RegisterNatives] java_class: com.facebook.react.bridge.CatalystInstanceImpl name: jniExtendNativeModules sig: (Ljava/util/Collection;Ljava/util/Collection;)V fnPtr: 0x9c9e2f11 module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x51f11 24 | [RegisterNatives] java_class: com.facebook.react.bridge.CatalystInstanceImpl name: jniSetSourceURL sig: (Ljava/lang/String;)V fnPtr: 0x9c9e3171 module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x52171 25 | [RegisterNatives] java_class: com.facebook.react.bridge.CatalystInstanceImpl name: jniRegisterSegment sig: (ILjava/lang/String;)V fnPtr: 0x9c9e3445 module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x52445 26 | [RegisterNatives] java_class: com.facebook.react.bridge.CatalystInstanceImpl name: jniLoadScriptFromAssets sig: (Landroid/content/res/AssetManager;Ljava/lang/String;Z)V fnPtr: 0x9c9e3801 module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x52801 27 | [RegisterNatives] java_class: com.facebook.react.bridge.CatalystInstanceImpl name: jniLoadScriptFromFile sig: (Ljava/lang/String;Ljava/lang/String;Z)V fnPtr: 0x9c9e3b35 module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x52b35 28 | [RegisterNatives] java_class: com.facebook.react.bridge.CatalystInstanceImpl name: jniLoadScriptFromDeltaBundle sig: (Ljava/lang/String;Lcom/facebook/react/bridge/NativeDeltaClient;Z)V fnPtr: 0x9c9e3f61 module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x52f61 29 | [RegisterNatives] java_class: com.facebook.react.bridge.CatalystInstanceImpl name: jniCallJSFunction sig: (Ljava/lang/String;Ljava/lang/String;Lcom/facebook/react/bridge/NativeArray;)V fnPtr: 0x9c9e438d module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x5338d 30 | [RegisterNatives] java_class: com.facebook.react.bridge.CatalystInstanceImpl name: jniCallJSCallback sig: (ILcom/facebook/react/bridge/NativeArray;)V fnPtr: 0x9c9e4c45 module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x53c45 31 | [RegisterNatives] java_class: com.facebook.react.bridge.CatalystInstanceImpl name: setGlobalVariable sig: (Ljava/lang/String;Ljava/lang/String;)V fnPtr: 0x9c9e4f01 module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x53f01 32 | [RegisterNatives] java_class: com.facebook.react.bridge.CatalystInstanceImpl name: getJavaScriptContext sig: ()J fnPtr: 0x9c9e509d module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x5409d 33 | [RegisterNatives] java_class: com.facebook.react.bridge.CatalystInstanceImpl name: getJSCallInvokerHolder sig: ()Lcom/facebook/react/turbomodule/core/JSCallInvokerHolderImpl; fnPtr: 0x9c9e519d module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x5419d 34 | [RegisterNatives] java_class: com.facebook.react.bridge.CatalystInstanceImpl name: jniHandleMemoryPressure sig: (I)V fnPtr: 0x9c9e539d module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x5439d 35 | [RegisterNatives] method_count: 0x1 36 | [RegisterNatives] java_class: com.facebook.react.bridge.queue.NativeRunnable name: run sig: ()V fnPtr: 0x9c9e0935 module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x4f935 37 | [RegisterNatives] method_count: 0x1 38 | [RegisterNatives] java_class: com.facebook.react.bridge.CxxModuleWrapperBase name: getName sig: ()Ljava/lang/String; fnPtr: 0x9ca00e49 module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x6fe49 39 | [RegisterNatives] method_count: 0x1 40 | [RegisterNatives] java_class: com.facebook.react.bridge.CxxModuleWrapper name: makeDsoNative sig: (Ljava/lang/String;Ljava/lang/String;)Lcom/facebook/react/bridge/CxxModuleWrapper; fnPtr: 0x9ca010f9 module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x700f9 41 | [RegisterNatives] method_count: 0x1 42 | [RegisterNatives] java_class: com.facebook.react.bridge.CxxCallbackImpl name: nativeInvoke sig: (Lcom/facebook/react/bridge/NativeArray;)V fnPtr: 0x9ca013bd module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x703bd 43 | [RegisterNatives] method_count: 0x1 44 | [RegisterNatives] java_class: com.facebook.react.bridge.NativeArray name: toString sig: ()Ljava/lang/String; fnPtr: 0x9c9fec1d module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x6dc1d 45 | [RegisterNatives] method_count: 0x3 46 | [RegisterNatives] java_class: com.facebook.react.bridge.NativeDeltaClient name: initHybrid sig: ()Lcom/facebook/jni/HybridData; fnPtr: 0x9c9ffcfd module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x6ecfd 47 | [RegisterNatives] java_class: com.facebook.react.bridge.NativeDeltaClient name: processDelta sig: (Ljava/nio/channels/ReadableByteChannel;)V fnPtr: 0x9c9ffeed module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x6eeed 48 | [RegisterNatives] java_class: com.facebook.react.bridge.NativeDeltaClient name: reset sig: ()V fnPtr: 0x9c9fffc5 module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x6efc5 49 | [RegisterNatives] method_count: 0x2 50 | [RegisterNatives] java_class: com.facebook.react.bridge.ReadableNativeArray name: importArray sig: ()[Ljava/lang/Object; fnPtr: 0x9ca03641 module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x72641 51 | [RegisterNatives] java_class: com.facebook.react.bridge.ReadableNativeArray name: importTypeArray sig: ()[Ljava/lang/Object; fnPtr: 0x9ca03b01 module_name: libreactnativejni.so module_base: 0x9c991000 offset: 0x72b01 52 | ``` 53 | 54 | ## 3 hook_artmethod 55 | 56 | ```text 57 | 58 | frida -U --no-pause -f package_name -l hook_artmethod.js -o hook_artmethod.log 59 | ``` 60 | 61 | ### 3.1 show PrettyMethod 62 | 63 | ```text 64 | ____ 65 | / _ | Frida 14.2.18 - A world-class dynamic instrumentation toolkit 66 | | (_| | 67 | > _ | Commands: 68 | /_/ |_| help -> Displays the help system 69 | . . . . object? -> Display information about 'object' 70 | . . . . exit/quit -> Exit 71 | . . . . 72 | . . . . More info at https://www.frida.re/docs/home/ 73 | Spawning `package_name`... 74 | Spawned `package_name`. Resuming main thread! 75 | [Google Pixel XL::package_name]-> void java.lang.Thread.(java.lang.ThreadGroup, java.lang.String, int, boolean) 76 | void java.lang.ThreadGroup.add(java.lang.Thread) 77 | void java.lang.Thread.(java.lang.ThreadGroup, java.lang.String, int, boolean) 78 | void java.lang.ThreadGroup.add(java.lang.Thread) 79 | void android.app.ActivityThread.main(java.lang.String[]) 80 | void java.lang.ref.FinalizerReference.add(java.lang.Object) 81 | android.os.BinderProxy android.os.BinderProxy.getInstance(long, long) 82 | void java.lang.ref.FinalizerReference.add(java.lang.Object) 83 | void java.lang.ref.FinalizerReference.add(java.lang.Object) 84 | android.os.BinderProxy android.os.BinderProxy.getInstance(long, long) 85 | boolean android.os.Binder.execTransact(int, long, long, int) 86 | void java.lang.ref.FinalizerReference.add(java.lang.Object) 87 | void java.lang.ref.FinalizerReference.add(java.lang.Object) 88 | void java.lang.ref.FinalizerReference.add(java.lang.Object) 89 | android.os.BinderProxy android.os.BinderProxy.getInstance(long, long) 90 | android.os.BinderProxy android.os.BinderProxy.getInstance(long, long) 91 | android.os.BinderProxy android.os.BinderProxy.getInstance(long, long) 92 | android.os.BinderProxy android.os.BinderProxy.getInstance(long, long) 93 | android.os.BinderProxy android.os.BinderProxy.getInstance(long, long) 94 | android.os.BinderProxy android.os.BinderProxy.getInstance(long, long) 95 | android.os.BinderProxy android.os.BinderProxy.getInstance(long, long) 96 | android.os.BinderProxy android.os.BinderProxy.getInstance(long, long) 97 | android.os.BinderProxy android.os.BinderProxy.getInstance(long, long) 98 | android.os.BinderProxy android.os.BinderProxy.getInstance(long, long) 99 | android.os.BinderProxy android.os.BinderProxy.getInstance(long, long) 100 | android.os.BinderProxy android.os.BinderProxy.getInstance(long, long) 101 | android.os.BinderProxy android.os.BinderProxy.getInstance(long, long) 102 | android.os.BinderProxy android.os.BinderProxy.getInstance(long, long) 103 | android.os.BinderProxy android.os.BinderProxy.getInstance(long, long) 104 | android.os.BinderProxy android.os.BinderProxy.getInstance(long, long) 105 | ``` -------------------------------------------------------------------------------- /just_trust_me_okhttp_hook_finder.js: -------------------------------------------------------------------------------- 1 | function loadDexfile(dexfile) { 2 | Java.perform(function () { 3 | Java.openClassFile(dexfile).load(); 4 | }); 5 | }; 6 | 7 | 8 | loadDexfile('/data/user/0/com.smile.gifmaker/radar.dex'); 9 | 10 | 11 | function printOkhttp3FakeClass(okhttp3FakeClassObject) { 12 | if (okhttp3FakeClassObject) { 13 | console.log("-----------------------------------------------------------------------"); 14 | console.log("原类名:" + okhttp3FakeClassObject.getOriginalClassName()); 15 | console.log("混淆类名:" + okhttp3FakeClassObject.getFakeClassName()); 16 | console.log("\n"); 17 | for (let k = 0; k < okhttp3FakeClassObject.fakeMethodSize(); k ++) { 18 | let fakeMethod = okhttp3FakeClassObject.getFakeMethod(k); 19 | console.log("混淆方法" + k + ":"); 20 | console.log("原方法签名:" + fakeMethod.getOriginalMethod()); 21 | console.log("混淆方法签名:" + fakeMethod.getFakeMethod()); 22 | console.log("\n"); 23 | } 24 | if (okhttp3FakeClassObject.fakeMethodSize() == 0) { 25 | //手动找混淆方法 26 | console.error("自动定位混淆方法失败,请去jadx打开"+okhttp3FakeClassObject.getFakeClassName()+"手动分析混淆方法"); 27 | } 28 | } 29 | } 30 | 31 | //设置下混淆okhttp混淆类包名的前戳,以便加快扫描速度 32 | //一般okhttp用的版本是3,并且混淆没有到包名级别所以我这里默认给^okhttp3 33 | //如果你的apk混淆的到了包名级别。先用jadx静态分析找到包名前戳改到这即可 34 | //你可以用.*,但是全量扫描太慢了。frida不是那么的耐操,容易崩溃! 35 | var matchClassNameRegExp = RegExp(/^okhttp3/); 36 | 37 | 38 | Java.perform(function() { 39 | var OkHttp3FakeFinder = Java.use("gz.justtrustme.OkHttp3FakeFinder"); 40 | Java.enumerateLoadedClasses({ 41 | onMatch: function(className) { 42 | if (className.match(matchClassNameRegExp)) { 43 | try { 44 | let tempClassHandle = Java.use(className).class; 45 | let matchOkHttpClientBuilderCheckResult = OkHttp3FakeFinder.okHttpClientBuilderCheck(tempClassHandle); 46 | if (matchOkHttpClientBuilderCheckResult) { 47 | printOkhttp3FakeClass(matchOkHttpClientBuilderCheckResult); 48 | return; 49 | } 50 | 51 | let matchOk3CertificatePinnerCheckResult = OkHttp3FakeFinder.okHttpCertificatePinnerCheck(tempClassHandle); 52 | if (matchOk3CertificatePinnerCheckResult) { 53 | printOkhttp3FakeClass(matchOk3CertificatePinnerCheckResult); 54 | return; 55 | } 56 | 57 | let matchOk3OkHostnameVerifierVerifyResult = OkHttp3FakeFinder.okHttpOkHostnameVerifierVerify(tempClassHandle); 58 | if (matchOk3OkHostnameVerifierVerifyResult) { 59 | printOkhttp3FakeClass(matchOk3OkHostnameVerifierVerifyResult); 60 | return; 61 | } 62 | 63 | } catch(error) { 64 | //console.error(error); 65 | } 66 | } 67 | }, 68 | onComplete: function() {} 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /loadClass.js: -------------------------------------------------------------------------------- 1 | /** 2 | 适用对象:通用 3 | 作用:获取动态加载的类(loadClass) 4 | 参考: 5 | */ 6 | 7 | function hook() { 8 | Java.perform(function () { 9 | 10 | var dexclassLoader = Java.use("dalvik.system.DexClassLoader"); 11 | dexclassLoader.loadClass.overload('java.lang.String').implementation = function (name) { 12 | var result = this.loadClass(name); 13 | console.log(name); 14 | 15 | // APP自己的Application时,想要hook的类一定是加载了的,否则时机过早会出现类找不到的情况。 16 | if (name.indexOf("Application") != -1 && name != "android.app.Application") { 17 | var application = Java.use(name); 18 | console.log("application:" + application); 19 | application.onCreate.implementation = function () { 20 | //hookTargetClass(); //二选一均可 21 | var ret = this.onCreate(); 22 | return ret; 23 | } 24 | } 25 | 26 | return result; 27 | } 28 | 29 | 30 | var application = Java.use('android.app.Application'); 31 | console.log("application:" + application); 32 | 33 | application.attach.overload('android.content.Context').implementation = function (context) { 34 | console.log("application attach called"); 35 | var result = this.attach(context); 36 | 37 | hookTargetClass(); 38 | 39 | return result; 40 | } 41 | 42 | application.onCreate.implementation = function () { 43 | console.log("application onCreate called"); 44 | 45 | hookTargetClass(); //二选一均可 46 | 47 | var result = this.onCreate(); 48 | return result; 49 | } 50 | 51 | }); 52 | } 53 | 54 | function hookTargetClass() { 55 | } 56 | 57 | // 打印堆栈 58 | function printStack() { 59 | Java.perform(function () { 60 | console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())); 61 | }); 62 | } 63 | 64 | setImmediate(hook()); -------------------------------------------------------------------------------- /nimlibChatAttachParser.js: -------------------------------------------------------------------------------- 1 | /** 2 | 适用对象:通用 3 | 作用:获取云信聊天附件信息 4 | 参考: 5 | */ 6 | 7 | 8 | // hook java.net.URL 9 | function hookURL() { 10 | var URL = Java.use('java.net.URL'); 11 | URL.$init.overload('java.lang.String').implementation = function (a) { 12 | console.log('URL:' + a) 13 | printStack(); 14 | this.$init(a) 15 | } 16 | } 17 | 18 | function hookChatAttachParser() { 19 | var clsName = 'com.android.common.nim.parser.ChatAttachParser'; 20 | var cls = Java.use(clsName); 21 | cls.parse.overload('java.lang.String').implementation = function (s) { 22 | var ret = this.parse(s); 23 | console.log(clsName + '.parse(): \n\t' + s); 24 | printStack(); 25 | return ret; 26 | } 27 | } 28 | 29 | function hookIMMessageImpl() { 30 | var clsName = "com.netease.nimlib.session.IMMessageImpl"; 31 | var cls = Java.use(clsName); 32 | cls.setAttachStr.overload('java.lang.String').implementation = function (s) { 33 | console.log(clsName + '.setAttachStr(): \n\t' + s); 34 | printStack(); 35 | return this.setAttachStr(s); 36 | }; 37 | cls.setAttachStrOnly.overload('java.lang.String').implementation = function (s) { 38 | console.log(clsName + '.setAttachStrOnly(): \n\t' + s); 39 | printStack(); 40 | return this.setAttachStrOnly(s); 41 | }; 42 | cls.getContent.overload().implementation = function () { 43 | var text = this.toStringSimple(this); 44 | console.log(`${clsName}.getContent(): \n\tnick: ${this.getFromNick()} \n\ttext: ${text} \n\tAttachStr: ${this.getAttachStr()}`); 45 | printStack(); 46 | return text; 47 | }; 48 | } 49 | 50 | // 打印堆栈 51 | function printStack() { 52 | console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())); 53 | } 54 | 55 | function byte2Base64(bytes) { 56 | //let s = String.fromCharCode.apply(null, bytes); // 字节序列转字符串 57 | var jBase64 = Java.use('android.util.Base64'); 58 | return jBase64.encodeToString(bytes, 2); 59 | } 60 | 61 | function hook() { 62 | Java.perform(function () { 63 | hookURL(); 64 | hookIMMessageImpl(); 65 | hookChatAttachParser(); 66 | }); 67 | } 68 | 69 | setImmediate(hook()); -------------------------------------------------------------------------------- /open.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 通过hook open函数来获取打开的文件名 3 | */ 4 | 5 | const TARGET_LIB_NAME = "libmsaoaidsec.so"; 6 | var TargetLibModule = null; // 存储目标库模块信息 7 | 8 | ///////////////////////////////////////// 9 | 10 | function hook_open() { 11 | var pth = Module.findExportByName(null, "open"); 12 | Interceptor.attach(ptr(pth), { 13 | onEnter: function (args) { 14 | this.filename = args[0]; 15 | console.log(this.filename.readCString()) 16 | }, onLeave: function (retval) { 17 | } 18 | }) 19 | } 20 | 21 | function hook_dlopen() { 22 | ["android_dlopen_ext", "dlopen"].forEach(funcName => { 23 | let addr = Module.findExportByName(null, funcName); 24 | if (addr) { 25 | Interceptor.attach(addr, { 26 | onEnter(args) { 27 | let libName = ptr(args[0]).readCString(); 28 | if (libName && libName.indexOf(TARGET_LIB_NAME) >= 0) { 29 | hook_open(); 30 | } 31 | }, 32 | onLeave: function (retval) { 33 | } 34 | }); 35 | } 36 | }); 37 | } 38 | 39 | hook_dlopen(); -------------------------------------------------------------------------------- /privacy.js: -------------------------------------------------------------------------------- 1 | function getContext() { 2 | return Java.use('android.app.ActivityThread').currentApplication().getApplicationContext().getContentResolver(); 3 | } 4 | function logAndroidId() { 5 | console.log('[?] android_id: ', Java.use('android.provider.Settings$Secure').getString(getContext(), 'android_id')); 6 | } 7 | 8 | 9 | function hook() { 10 | Java.perform(function () { 11 | var TelephonyManager = Java.use("android.telephony.TelephonyManager"); 12 | TelephonyManager.getDeviceId.overload().implementation = function(){ 13 | var ret = this.getDeviceId(); 14 | console.log("TelephonyManager.getDeviceId() called ret: ", ret); 15 | return ret; 16 | }; 17 | TelephonyManager.getDeviceId.overload('int').implementation = function(p){ 18 | var ret = this.getDeviceId(p); 19 | console.log("TelephonyManager.getDeviceId(int) called ret: ", ret); 20 | return ret; 21 | }; 22 | TelephonyManager.getImei.overload("int").implementation = function(i){ 23 | var ret = this.getImei(i); 24 | console.log("TelephonyManager.getImei() called ret: ", ret); 25 | return ret; 26 | }; 27 | TelephonyManager.getSimSerialNumber.overload().implementation = function () { 28 | var ret = this.getSimSerialNumber(); 29 | console.log("TelephonyManager.getSimSerialNumber() called ret: ", ret); 30 | return ret; 31 | }; 32 | 33 | 34 | var secure = Java.use("android.provider.Settings$Secure"); 35 | secure.getString.implementation = function(obj, s){ 36 | var ret = this.getString(obj, s); 37 | console.log("Settings$Secure.getString called: " + s + " ret: ", ret); 38 | return ret; 39 | } 40 | 41 | // //android的hidden API,需要通过反射调用 42 | // var SP = Java.use("android.os.SystemProperties"); 43 | // SP.get.overload('java.lang.String').implementation = function (p) { 44 | // var tmp = this.get(p); 45 | // console.log("[*]android.os.SystemProperties "+p+" : "+tmp); 46 | // return tmp; 47 | // } 48 | // SP.get.overload('java.lang.String', 'java.lang.String').implementation = function (p1,p2) { 49 | // var tmp = this.get(p1,p2) 50 | // console.log("[*]android.os.SystemProperties "+p1+","+p2+" : "+tmp); 51 | // return tmp; 52 | // } 53 | 54 | // hook MAC 55 | var wifi = Java.use("android.net.wifi.WifiInfo"); 56 | wifi.getMacAddress.implementation = function () { 57 | var tmp = this.getMacAddress(); 58 | console.log("[*]android.net.wifi.WifiInfo.getMacAddress() ret: "+tmp); 59 | return tmp; 60 | } 61 | 62 | }); 63 | } 64 | 65 | setImmediate(hook()); -------------------------------------------------------------------------------- /pthread_create-fake.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 替换目标库使用的pthread_create函数。 3 | * @description 该脚本使用 Frida 进行动态分析,主要目标是: 4 | * 1. 监听 `dlopen` / `android_dlopen_ext`,在加载目标库时执行后续 Hook 操作。 5 | * 2. 监听 `call_constructors` 以确保目标库完全初始化后再 Hook `dlsym`。 6 | * 3. Hook `dlsym` 并拦截特定符号(如 `pthread_create`),替换其返回值。 7 | * 4. 通过 `fake_pthread_create` 生成虚假 `pthread_create` 实现,使目标库无法正确调用线程创建函数。 8 | */ 9 | 10 | const TARGET_LIB_NAME = "libmsaoaidsec.so"; 11 | var TargetLibModule = null; // 存储目标库模块信息 12 | 13 | 14 | function create_fake_pthread_create() { 15 | const fake_pthread_create = Memory.alloc(4096) 16 | Memory.protect(fake_pthread_create, 4096, "rwx") 17 | Memory.patchCode(fake_pthread_create, 4096, code => { 18 | const cw = new Arm64Writer(code, { pc: ptr(fake_pthread_create) }) 19 | cw.putRet() 20 | }) 21 | return fake_pthread_create 22 | } 23 | 24 | function hook_dlsym() { 25 | var dlsym = Module.findExportByName(null, "dlsym"); 26 | if (dlsym !== null) { 27 | Interceptor.attach(dlsym, { 28 | onEnter: function (args) { 29 | // 获取调用 dlsym 的返回地址 30 | let caller = this.context.lr; 31 | if (caller.compare(TargetLibModule.base) > 0 && 32 | caller.compare(TargetLibModule.base.add(TargetLibModule.size)) < 0) { 33 | var name = ptr(args[1]).readCString(); // 读取符号名 34 | if (name == 'pthread_create') { 35 | console.warn(`replace symbol name: pthread_create`); 36 | this.canFake = true; 37 | } 38 | } 39 | }, 40 | onLeave: function (retval) { 41 | if (this.canFake) { 42 | retval.replace(fake_pthread_create); 43 | } 44 | } 45 | }); 46 | } 47 | } 48 | 49 | function find_call_constructors() { 50 | is64Bit = Process.pointerSize === 8; 51 | var linkerModule = Process.getModuleByName(is64Bit ? "linker64" : "linker"); 52 | var symbols = linkerModule.enumerateSymbols(); 53 | for (var i = 0; i < symbols.length; i++) { 54 | if (symbols[i].name.indexOf('call_constructors') > 0) { 55 | console.warn(`call_constructors symbol name: ${symbols[i].name} address: ${symbols[i].address}`); 56 | return symbols[i].address; 57 | } 58 | } 59 | } 60 | 61 | function hook_call_constructors() { 62 | var ptr_call_constructors = find_call_constructors(); 63 | var listener = Interceptor.attach(ptr_call_constructors, { 64 | onEnter: function (args) { 65 | if (!TargetLibModule) { 66 | TargetLibModule = Process.findModuleByName(TARGET_LIB_NAME); 67 | } 68 | console.warn(`call_constructors onEnter: ${TARGET_LIB_NAME} Module Base: ${TargetLibModule.base}`); 69 | hook_dlsym(); 70 | listener.detach(); 71 | }, 72 | }) 73 | } 74 | 75 | function hook_dlopen() { 76 | ["android_dlopen_ext", "dlopen"].forEach(funcName => { 77 | let addr = Module.findExportByName(null, funcName); 78 | if (addr) { 79 | Interceptor.attach(addr, { 80 | onEnter(args) { 81 | let libName = ptr(args[0]).readCString(); 82 | if (libName && libName.indexOf(TARGET_LIB_NAME) >= 0) { 83 | hook_call_constructors(); 84 | } 85 | }, 86 | onLeave: function (retval) { 87 | } 88 | }); 89 | } 90 | }); 91 | } 92 | 93 | var is64Bit = Process.pointerSize === 8; 94 | // 创建虚假pthread_create 95 | var fake_pthread_create = create_fake_pthread_create(); 96 | hook_dlopen() 97 | -------------------------------------------------------------------------------- /pthread_create-replace-jni.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Frida Hook - 监视 `pthread_create`,检测目标库创建的线程 3 | * 4 | * 目标: 5 | * - 监视 `dlopen` 和 `android_dlopen_ext`,检测目标库何时加载。 6 | * - 通过 `call_constructors` 确保 `pthread_create` Hook 在目标库加载后执行。 7 | * - 拦截 `pthread_create`,检查线程函数是否在目标库 `so` 的范围内。 8 | * - 若发现目标库创建的线程,则替换线程函数,阻止其执行。 9 | * - 并在 JNI_OnLoad 里替换未注册成功的jni函数。 10 | * 11 | * 说明: 12 | * - `pthread_create` 是创建线程的标准函数,Hook 它可以拦截所有新建线程。 13 | * - `call_constructors` 由 `linker` 调用,用于执行动态库的全局构造函数。 14 | * - `hook_call_constructors()` 确保 `TargetLibModule` 记录目标库的基地址,并在适当时机 Hook `pthread_create`。 15 | */ 16 | 17 | const TARGET_LIB_NAME = "libmsaoaidsec.so"; 18 | var TargetLibModule = null; // 存储目标库模块信息 19 | 20 | ///////////////////////////////////////// 21 | 22 | /** 23 | * Hook pthread_create,拦截目标库创建的线程 24 | */ 25 | function hook_pthread_create() { 26 | let pthread_create_addr = Module.findExportByName("libc.so", "pthread_create"); 27 | if (!pthread_create_addr) { 28 | console.error("Failed to find pthread_create!"); 29 | return; 30 | } 31 | 32 | Interceptor.attach(pthread_create_addr, { 33 | onEnter(args) { 34 | let thread_func_ptr = args[2]; // 线程函数地址 35 | console.log("[+] pthread_create called, thread function address: " + thread_func_ptr); 36 | 37 | // 确保目标库已加载 38 | if (!TargetLibModule) { 39 | //console.warn("Target library not loaded yet!"); 40 | return; 41 | } 42 | 43 | // 判断线程函数是否在目标库 `so` 的范围内 44 | if (thread_func_ptr.compare(TargetLibModule.base) > 0 && 45 | thread_func_ptr.compare(TargetLibModule.base.add(TargetLibModule.size)) < 0) { 46 | 47 | console.warn("[!] Intercepted thread function at: " + thread_func_ptr + 48 | " (Offset: " + thread_func_ptr.sub(TargetLibModule.base) + ")"); 49 | 50 | // 替换线程函数,防止执行 51 | Interceptor.replace(thread_func_ptr, new NativeCallback(() => { 52 | console.log("[*] Fake thread function executed, doing nothing..."); 53 | }, "void", [])); 54 | } 55 | } 56 | }); 57 | } 58 | 59 | function hook_JNI_OnLoad() { 60 | // 先尝试通过 `findExportByName` 61 | let jniOnLoad = Module.findExportByName(TARGET_LIB_NAME, "JNI_OnLoad"); 62 | 63 | // 如果找不到,就遍历所有导出符号 64 | if (!jniOnLoad) { 65 | console.log("[Info] `JNI_OnLoad` 未导出,尝试遍历导出符号..."); 66 | for (let symbol of module.enumerateSymbols()) { 67 | if (symbol.name.indexOf("JNI_OnLoad") >= 0) { 68 | jniOnLoad = symbol.address; 69 | console.log("[Success] 找到 JNI_OnLoad: ", jniOnLoad); 70 | break; 71 | } 72 | } 73 | } 74 | 75 | if (!jniOnLoad) { 76 | console.error("[Error] 未找到 `JNI_OnLoad` 函数"); 77 | return; 78 | } 79 | 80 | // Hook `JNI_OnLoad` 81 | Interceptor.attach(jniOnLoad, { 82 | onEnter(args) { 83 | console.log("[Hooked] JNI_OnLoad 被调用"); 84 | bypass(); 85 | } 86 | }); 87 | } 88 | 89 | function bypass() { 90 | try { 91 | var targetClassName = "com.bun.miitmdid.e"; 92 | var targetClass = Java.use(targetClassName); 93 | console.log("[*] Successfully loaded class: " + targetClass); 94 | 95 | targetClass.a.overloads.forEach(function (overload) { 96 | overload.implementation = function () { 97 | console.log("[*] Hooked " + targetClass + ".a() with args: " + JSON.stringify(arguments)); 98 | return 0; // 理论上要返回对应的值,这里粗暴一点随便返回个数值,不影响App正常运行即可。 99 | }; 100 | }); 101 | } catch (e) { 102 | console.log("[!] Error: " + e); 103 | } 104 | } 105 | 106 | function find_call_constructors() { 107 | is64Bit = Process.pointerSize === 8; 108 | var linkerModule = Process.getModuleByName(is64Bit ? "linker64" : "linker"); 109 | var symbols = linkerModule.enumerateSymbols(); 110 | for (var i = 0; i < symbols.length; i++) { 111 | if (symbols[i].name.indexOf('call_constructors') > 0) { 112 | console.warn(`call_constructors symbol name: ${symbols[i].name} address: ${symbols[i].address}`); 113 | return symbols[i].address; 114 | } 115 | } 116 | } 117 | 118 | function hook_call_constructors() { 119 | var ptr_call_constructors = find_call_constructors(); 120 | var listener = Interceptor.attach(ptr_call_constructors, { 121 | onEnter: function (args) { 122 | console.warn(`call_constructors onEnter`); 123 | if (!TargetLibModule) { 124 | TargetLibModule = Process.findModuleByName(TARGET_LIB_NAME); 125 | } 126 | hook_pthread_create(); 127 | listener.detach(); 128 | }, 129 | }) 130 | } 131 | 132 | function hook_dlopen() { 133 | ["android_dlopen_ext", "dlopen"].forEach(funcName => { 134 | let addr = Module.findExportByName(null, funcName); 135 | if (addr) { 136 | Interceptor.attach(addr, { 137 | onEnter(args) { 138 | let libName = ptr(args[0]).readCString(); 139 | if (libName && libName.indexOf(TARGET_LIB_NAME) >= 0) { 140 | this.is_can_hook = true; 141 | hook_call_constructors(); 142 | } 143 | }, 144 | onLeave: function (retval) { 145 | if (this.is_can_hook) { 146 | console.log(`[+] ${funcName} onLeave, start hook JNI_OnLoad `); 147 | hook_JNI_OnLoad(); 148 | } 149 | } 150 | }); 151 | } 152 | }); 153 | } 154 | 155 | var is64Bit = Process.pointerSize === 8; 156 | hook_dlopen() 157 | -------------------------------------------------------------------------------- /pthread_create.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Frida Hook - 监视 `pthread_create`,检测目标库创建的线程 3 | * 4 | * 目标: 5 | * - 监视 `dlopen` 和 `android_dlopen_ext`,检测目标库何时加载。 6 | * - 通过 `call_constructors` 确保 `pthread_create` Hook 在目标库加载后执行。 7 | * - 拦截 `pthread_create`,检查线程函数是否在目标库 `so` 的范围内。 8 | * - 若发现目标库创建的线程,则替换线程函数,阻止其执行。 9 | * 10 | * 说明: 11 | * - `pthread_create` 是创建线程的标准函数,Hook 它可以拦截所有新建线程。 12 | * - `call_constructors` 由 `linker` 调用,用于执行动态库的全局构造函数。 13 | * - `hook_call_constructors()` 确保 `TargetLibModule` 记录目标库的基地址,并在适当时机 Hook `pthread_create`。 14 | */ 15 | 16 | const TARGET_LIB_NAME = "libmsaoaidsec.so"; 17 | var TargetLibModule = null; // 存储目标库模块信息 18 | 19 | ///////////////////////////////////////// 20 | 21 | /** 22 | * Hook pthread_create,拦截目标库创建的线程 23 | */ 24 | function hook_pthread_create() { 25 | let pthread_create_addr = Module.findExportByName("libc.so", "pthread_create"); 26 | if (!pthread_create_addr) { 27 | console.error("Failed to find pthread_create!"); 28 | return; 29 | } 30 | 31 | Interceptor.attach(pthread_create_addr, { 32 | onEnter(args) { 33 | let thread_func_ptr = args[2]; // 线程函数地址 34 | console.log("[+] pthread_create called, thread function address: " + thread_func_ptr); 35 | 36 | // 确保目标库已加载 37 | if (!TargetLibModule) { 38 | //console.warn("Target library not loaded yet!"); 39 | return; 40 | } 41 | 42 | // 判断线程函数是否在目标库 `so` 的范围内 43 | if (thread_func_ptr.compare(TargetLibModule.base) > 0 && 44 | thread_func_ptr.compare(TargetLibModule.base.add(TargetLibModule.size)) < 0) { 45 | 46 | console.warn("[!] Intercepted thread function at: " + thread_func_ptr + 47 | " (Offset: " + thread_func_ptr.sub(TargetLibModule.base) + ")"); 48 | 49 | // 替换线程函数,防止执行 50 | Interceptor.replace(thread_func_ptr, new NativeCallback(() => { 51 | console.log("[*] Fake thread function executed, doing nothing..."); 52 | }, "void", [])); 53 | } 54 | } 55 | }); 56 | } 57 | 58 | function find_call_constructors() { 59 | is64Bit = Process.pointerSize === 8; 60 | var linkerModule = Process.getModuleByName(is64Bit ? "linker64" : "linker"); 61 | var symbols = linkerModule.enumerateSymbols(); 62 | for (var i = 0; i < symbols.length; i++) { 63 | if (symbols[i].name.indexOf('call_constructors') > 0) { 64 | console.warn(`call_constructors symbol name: ${symbols[i].name} address: ${symbols[i].address}`); 65 | return symbols[i].address; 66 | } 67 | } 68 | } 69 | 70 | function hook_call_constructors() { 71 | var ptr_call_constructors = find_call_constructors(); 72 | var listener = Interceptor.attach(ptr_call_constructors, { 73 | onEnter: function (args) { 74 | console.warn(`call_constructors onEnter`); 75 | if (!TargetLibModule) { 76 | TargetLibModule = Process.findModuleByName(TARGET_LIB_NAME); 77 | } 78 | hook_pthread_create(); 79 | listener.detach(); 80 | }, 81 | }) 82 | } 83 | 84 | function hook_dlopen() { 85 | ["android_dlopen_ext", "dlopen"].forEach(funcName => { 86 | let addr = Module.findExportByName(null, funcName); 87 | if (addr) { 88 | Interceptor.attach(addr, { 89 | onEnter(args) { 90 | let libName = ptr(args[0]).readCString(); 91 | if (libName && libName.indexOf(TARGET_LIB_NAME) >= 0) { 92 | hook_call_constructors(); 93 | } 94 | }, 95 | onLeave: function (retval) { 96 | } 97 | }); 98 | } 99 | }); 100 | } 101 | 102 | var is64Bit = Process.pointerSize === 8; 103 | hook_dlopen() 104 | -------------------------------------------------------------------------------- /r0capture.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Initializes 'addresses' dictionary and NativeFunctions. 3 | */ 4 | "use strict"; 5 | rpc.exports = { 6 | setssllib: function (name) { 7 | console.log("setSSLLib => " + name); 8 | libname = name; 9 | initializeGlobals(); 10 | return; 11 | } 12 | }; 13 | 14 | var addresses = {}; 15 | var SSL_get_fd = null; 16 | var SSL_get_session = null; 17 | var SSL_SESSION_get_id = null; 18 | var getpeername = null; 19 | var getsockname = null; 20 | var ntohs = null; 21 | var ntohl = null; 22 | var SSLstackwrite = null; 23 | var SSLstackread = null; 24 | 25 | var libname = "*libssl*"; 26 | 27 | function uuid(len, radix) { 28 | var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); 29 | var uuid = [], i; 30 | radix = radix || chars.length; 31 | 32 | if (len) { 33 | // Compact form 34 | for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]; 35 | } else { 36 | // rfc4122, version 4 form 37 | var r; 38 | 39 | // rfc4122 requires these characters 40 | uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; 41 | uuid[14] = '4'; 42 | 43 | // Fill in random data. At i==19 set the high bits of clock sequence as 44 | // per rfc4122, sec. 4.1.5 45 | for (i = 0; i < 36; i++) { 46 | if (!uuid[i]) { 47 | r = 0 | Math.random() * 16; 48 | uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; 49 | } 50 | } 51 | } 52 | 53 | return uuid.join(''); 54 | } 55 | function return_zero(args) { 56 | return 0; 57 | } 58 | function initializeGlobals() { 59 | var resolver = new ApiResolver("module"); 60 | var exps = [ 61 | [Process.platform == "darwin" ? "*libboringssl*" : "*libssl*", ["SSL_read", "SSL_write", "SSL_get_fd", "SSL_get_session", "SSL_SESSION_get_id"]], // for ios and Android 62 | [Process.platform == "darwin" ? "*libsystem*" : "*libc*", ["getpeername", "getsockname", "ntohs", "ntohl"]] 63 | ]; 64 | // console.log(exps) 65 | for (var i = 0; i < exps.length; i++) { 66 | var lib = exps[i][0]; 67 | var names = exps[i][1]; 68 | for (var j = 0; j < names.length; j++) { 69 | var name = names[j]; 70 | // console.log("exports:" + lib + "!" + name) 71 | var matches = resolver.enumerateMatchesSync("exports:" + lib + "!" + name); 72 | if (matches.length == 0) { 73 | if (name == "SSL_get_fd") { 74 | addresses["SSL_get_fd"] = 0; 75 | continue; 76 | } 77 | throw "Could not find " + lib + "!" + name; 78 | } 79 | else if (matches.length != 1) { 80 | // Sometimes Frida returns duplicates. 81 | var address = 0; 82 | var s = ""; 83 | var duplicates_only = true; 84 | for (var k = 0; k < matches.length; k++) { 85 | if (s.length != 0) { 86 | s += ", "; 87 | } 88 | s += matches[k].name + "@" + matches[k].address; 89 | if (address == 0) { 90 | address = matches[k].address; 91 | } 92 | else if (!address.equals(matches[k].address)) { 93 | duplicates_only = false; 94 | } 95 | } 96 | if (!duplicates_only) { 97 | throw "More than one match found for " + lib + "!" + name + ": " + s; 98 | } 99 | } 100 | addresses[name] = matches[0].address; 101 | } 102 | } 103 | if (addresses["SSL_get_fd"] == 0) { 104 | SSL_get_fd = return_zero; 105 | } else { 106 | SSL_get_fd = new NativeFunction(addresses["SSL_get_fd"], "int", ["pointer"]); 107 | } 108 | SSL_get_session = new NativeFunction(addresses["SSL_get_session"], "pointer", ["pointer"]); 109 | SSL_SESSION_get_id = new NativeFunction(addresses["SSL_SESSION_get_id"], "pointer", ["pointer", "pointer"]); 110 | getpeername = new NativeFunction(addresses["getpeername"], "int", ["int", "pointer", "pointer"]); 111 | getsockname = new NativeFunction(addresses["getsockname"], "int", ["int", "pointer", "pointer"]); 112 | ntohs = new NativeFunction(addresses["ntohs"], "uint16", ["uint16"]); 113 | ntohl = new NativeFunction(addresses["ntohl"], "uint32", ["uint32"]); 114 | } 115 | initializeGlobals(); 116 | 117 | function ipToNumber(ip) { 118 | var num = 0; 119 | if (ip == "") { 120 | return num; 121 | } 122 | var aNum = ip.split("."); 123 | if (aNum.length != 4) { 124 | return num; 125 | } 126 | num += parseInt(aNum[0]) << 0; 127 | num += parseInt(aNum[1]) << 8; 128 | num += parseInt(aNum[2]) << 16; 129 | num += parseInt(aNum[3]) << 24; 130 | num = num >>> 0;//这个很关键,不然可能会出现负数的情况 131 | return num; 132 | } 133 | 134 | /** 135 | * Returns a dictionary of a sockfd's "src_addr", "src_port", "dst_addr", and 136 | * "dst_port". 137 | * @param {int} sockfd The file descriptor of the socket to inspect. 138 | * @param {boolean} isRead If true, the context is an SSL_read call. If 139 | * false, the context is an SSL_write call. 140 | * @return {dict} Dictionary of sockfd's "src_addr", "src_port", "dst_addr", 141 | * and "dst_port". 142 | */ 143 | function getPortsAndAddresses(sockfd, isRead) { 144 | var message = {}; 145 | var src_dst = ["src", "dst"]; 146 | for (var i = 0; i < src_dst.length; i++) { 147 | if ((src_dst[i] == "src") ^ isRead) { 148 | var sockAddr = Socket.localAddress(sockfd) 149 | } 150 | else { 151 | var sockAddr = Socket.peerAddress(sockfd) 152 | } 153 | if (sockAddr == null) { 154 | // 网络超时or其他原因可能导致socket被关闭 155 | message[src_dst[i] + "_port"] = 0 156 | message[src_dst[i] + "_addr"] = 0 157 | } else { 158 | message[src_dst[i] + "_port"] = (sockAddr.port & 0xFFFF) 159 | message[src_dst[i] + "_addr"] = ntohl(ipToNumber(sockAddr.ip.split(":").pop())) 160 | } 161 | } 162 | return message; 163 | } 164 | /** 165 | * Get the session_id of SSL object and return it as a hex string. 166 | * @param {!NativePointer} ssl A pointer to an SSL object. 167 | * @return {dict} A string representing the session_id of the SSL object's 168 | * SSL_SESSION. For example, 169 | * "59FD71B7B90202F359D89E66AE4E61247954E28431F6C6AC46625D472FF76336". 170 | */ 171 | function getSslSessionId(ssl) { 172 | var session = SSL_get_session(ssl); 173 | if (session == 0) { 174 | return 0; 175 | } 176 | var len = Memory.alloc(4); 177 | var p = SSL_SESSION_get_id(session, len); 178 | len = Memory.readU32(len); 179 | var session_id = ""; 180 | for (var i = 0; i < len; i++) { 181 | // Read a byte, convert it to a hex string (0xAB ==> "AB"), and append 182 | // it to session_id. 183 | session_id += 184 | ("0" + Memory.readU8(p.add(i)).toString(16).toUpperCase()).substr(-2); 185 | } 186 | return session_id; 187 | } 188 | 189 | Interceptor.attach(addresses["SSL_read"], 190 | { 191 | onEnter: function (args) { 192 | var message = getPortsAndAddresses(SSL_get_fd(args[0]), true); 193 | message["ssl_session_id"] = getSslSessionId(args[0]); 194 | message["function"] = "SSL_read"; 195 | message["stack"] = SSLstackread; 196 | this.message = message; 197 | this.buf = args[1]; 198 | }, 199 | onLeave: function (retval) { 200 | retval |= 0; // Cast retval to 32-bit integer. 201 | if (retval <= 0) { 202 | return; 203 | } 204 | send(this.message, Memory.readByteArray(this.buf, retval)); 205 | } 206 | }); 207 | 208 | Interceptor.attach(addresses["SSL_write"], 209 | { 210 | onEnter: function (args) { 211 | var message = getPortsAndAddresses(SSL_get_fd(args[0]), false); 212 | message["ssl_session_id"] = getSslSessionId(args[0]); 213 | message["function"] = "SSL_write"; 214 | message["stack"] = SSLstackwrite; 215 | send(message, Memory.readByteArray(args[1], parseInt(args[2]))); 216 | }, 217 | onLeave: function (retval) { 218 | } 219 | }); 220 | 221 | if (Java.available) { 222 | Java.perform(function () { 223 | function storeP12(pri, p7, p12Path, p12Password) { 224 | var X509Certificate = Java.use("java.security.cert.X509Certificate") 225 | var p7X509 = Java.cast(p7, X509Certificate); 226 | var chain = Java.array("java.security.cert.X509Certificate", [p7X509]) 227 | var ks = Java.use("java.security.KeyStore").getInstance("PKCS12", "BC"); 228 | ks.load(null, null); 229 | ks.setKeyEntry("client", pri, Java.use('java.lang.String').$new(p12Password).toCharArray(), chain); 230 | try { 231 | var out = Java.use("java.io.FileOutputStream").$new(p12Path); 232 | ks.store(out, Java.use('java.lang.String').$new(p12Password).toCharArray()) 233 | } catch (exp) { 234 | console.log(exp) 235 | } 236 | } 237 | //在服务器校验客户端的情形下,帮助dump客户端证书,并保存为p12的格式,证书密码为r0ysue 238 | Java.use("java.security.KeyStore$PrivateKeyEntry").getPrivateKey.implementation = function () { 239 | var result = this.getPrivateKey() 240 | var packageName = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext().getPackageName(); 241 | storeP12(this.getPrivateKey(), this.getCertificate(), '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12', 'r0ysue'); 242 | var message = {}; 243 | message["function"] = "dumpClinetCertificate=>" + '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12' + ' pwd: r0ysue'; 244 | message["stack"] = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()); 245 | var data = Memory.alloc(1); 246 | send(message, Memory.readByteArray(data, 1)) 247 | return result; 248 | } 249 | Java.use("java.security.KeyStore$PrivateKeyEntry").getCertificateChain.implementation = function () { 250 | var result = this.getCertificateChain() 251 | var packageName = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext().getPackageName(); 252 | storeP12(this.getPrivateKey(), this.getCertificate(), '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12', 'r0ysue'); 253 | var message = {}; 254 | message["function"] = "dumpClinetCertificate=>" + '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12' + ' pwd: r0ysue'; 255 | message["stack"] = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()); 256 | var data = Memory.alloc(1); 257 | send(message, Memory.readByteArray(data, 1)) 258 | return result; 259 | } 260 | 261 | //SSLpinning helper 帮助定位证书绑定的关键代码a 262 | Java.use("java.io.File").$init.overload('java.io.File', 'java.lang.String').implementation = function (file, cert) { 263 | var result = this.$init(file, cert) 264 | var stack = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()); 265 | if (file.getPath().indexOf("cacert") >= 0 && stack.indexOf("X509TrustManagerExtensions.checkServerTrusted") >= 0) { 266 | var message = {}; 267 | message["function"] = "SSLpinning position locator => " + file.getPath() + " " + cert; 268 | message["stack"] = stack; 269 | var data = Memory.alloc(1); 270 | send(message, Memory.readByteArray(data, 1)) 271 | } 272 | return result; 273 | } 274 | 275 | 276 | Java.use("java.net.SocketOutputStream").socketWrite0.overload('java.io.FileDescriptor', '[B', 'int', 'int').implementation = function (fd, bytearry, offset, byteCount) { 277 | var result = this.socketWrite0(fd, bytearry, offset, byteCount); 278 | var message = {}; 279 | message["function"] = "HTTP_send"; 280 | message["ssl_session_id"] = ""; 281 | message["src_addr"] = ntohl(ipToNumber((this.socket.value.getLocalAddress().toString().split(":")[0]).split("/").pop())); 282 | message["src_port"] = parseInt(this.socket.value.getLocalPort().toString()); 283 | message["dst_addr"] = ntohl(ipToNumber((this.socket.value.getRemoteSocketAddress().toString().split(":")[0]).split("/").pop())); 284 | message["dst_port"] = parseInt(this.socket.value.getRemoteSocketAddress().toString().split(":").pop()); 285 | message["stack"] = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); 286 | var ptr = Memory.alloc(byteCount); 287 | for (var i = 0; i < byteCount; ++i) 288 | Memory.writeS8(ptr.add(i), bytearry[offset + i]); 289 | send(message, Memory.readByteArray(ptr, byteCount)) 290 | return result; 291 | } 292 | Java.use("java.net.SocketInputStream").socketRead0.overload('java.io.FileDescriptor', '[B', 'int', 'int', 'int').implementation = function (fd, bytearry, offset, byteCount, timeout) { 293 | var result = this.socketRead0(fd, bytearry, offset, byteCount, timeout); 294 | var message = {}; 295 | message["function"] = "HTTP_recv"; 296 | message["ssl_session_id"] = ""; 297 | message["src_addr"] = ntohl(ipToNumber((this.socket.value.getRemoteSocketAddress().toString().split(":")[0]).split("/").pop())); 298 | message["src_port"] = parseInt(this.socket.value.getRemoteSocketAddress().toString().split(":").pop()); 299 | message["dst_addr"] = ntohl(ipToNumber((this.socket.value.getLocalAddress().toString().split(":")[0]).split("/").pop())); 300 | message["dst_port"] = parseInt(this.socket.value.getLocalPort()); 301 | message["stack"] = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); 302 | if (result > 0) { 303 | var ptr = Memory.alloc(result); 304 | for (var i = 0; i < result; ++i) 305 | Memory.writeS8(ptr.add(i), bytearry[offset + i]); 306 | send(message, Memory.readByteArray(ptr, result)) 307 | } 308 | return result; 309 | } 310 | 311 | if (parseFloat(Java.androidVersion) > 8) { 312 | Java.use("com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLOutputStream").write.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) { 313 | var result = this.write(bytearry, int1, int2); 314 | SSLstackwrite = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); 315 | return result; 316 | } 317 | Java.use("com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLInputStream").read.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) { 318 | var result = this.read(bytearry, int1, int2); 319 | SSLstackread = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); 320 | return result; 321 | } 322 | } 323 | else { 324 | Java.use("com.android.org.conscrypt.OpenSSLSocketImpl$SSLOutputStream").write.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) { 325 | var result = this.write(bytearry, int1, int2); 326 | SSLstackwrite = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); 327 | return result; 328 | } 329 | Java.use("com.android.org.conscrypt.OpenSSLSocketImpl$SSLInputStream").read.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) { 330 | var result = this.read(bytearry, int1, int2); 331 | SSLstackread = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); 332 | return result; 333 | } 334 | 335 | } 336 | } 337 | 338 | ) 339 | } -------------------------------------------------------------------------------- /replaceBaiduAPI_KEY.js: -------------------------------------------------------------------------------- 1 | /** 2 | 适用对象:通用 3 | 作用: 4 | */ 5 | 6 | Java.perform(function () { 7 | var ApplicationPackageManager = Java.use("android.app.ApplicationPackageManager"); 8 | 9 | ApplicationPackageManager.getApplicationInfo.overload('java.lang.String', 'int').implementation = function (packageName, flags) { 10 | var appInfo = this.getApplicationInfo(packageName, flags); 11 | if (appInfo !== null && appInfo.metaData !== null) { 12 | var originalKey = appInfo.metaData.getString("com.baidu.lbsapi.API_KEY"); 13 | console.log("Old Key: " + originalKey); 14 | 15 | // 修改 API_KEY 16 | appInfo.metaData.putString("com.baidu.lbsapi.API_KEY", "xxxxxxxxxxxxxxx"); 17 | } 18 | return appInfo; 19 | }; 20 | }); 21 | -------------------------------------------------------------------------------- /replaceInitProcAndPatch.js: -------------------------------------------------------------------------------- 1 | /* 2 | * - 监视 `dlopen` 和 `android_dlopen_ext`,拦截 `libmsaoaidsec.so` 的加载。 3 | * - 跳过其init_proc函数的调用,并通过补丁nop的方式跳过崩溃的函数调用。 4 | */ 5 | 6 | const TARGET_LIB_NAME = "libmsaoaidsec.so"; 7 | const init_offsets = [0x14400, 0x83fc, 0x8448, 0x8460, 0x84b4, 0x85a8]; // 这里填init函数列表,先通过init_array.js脚本获取 8 | const patch_offsets = [0x8750]; // 这里填需要nop掉的偏移地址 9 | var TargetLibModule = null; // 存储目标库模块信息 10 | 11 | 12 | 13 | function nop_code(addr) { 14 | Memory.patchCode(ptr(addr), 4, code => { 15 | const cw = new Arm64Writer(code, { pc: ptr(addr) });// 64位 16 | cw.putNop(); 17 | cw.putNop(); 18 | cw.flush(); 19 | }) 20 | } 21 | 22 | function bypass() { 23 | patch_offsets.forEach(offset => { 24 | console.log(`patch ${offset.toString(16)}`); 25 | nop_code(TargetLibModule.base.add(offset)); // 64位不用减一 26 | }); 27 | } 28 | 29 | 30 | function find_call_constructors() { 31 | is64Bit = Process.pointerSize === 8; 32 | var linkerModule = Process.getModuleByName(is64Bit ? "linker64" : "linker"); 33 | var symbols = linkerModule.enumerateSymbols(); 34 | for (var i = 0; i < symbols.length; i++) { 35 | if (symbols[i].name.indexOf('call_constructors') > 0) { 36 | console.warn(`call_constructors symbol name: ${symbols[i].name} address: ${symbols[i].address}`); 37 | return symbols[i].address; 38 | } 39 | } 40 | } 41 | 42 | function hook_call_constructors() { 43 | var ptr_call_constructors = find_call_constructors(); 44 | var listener = Interceptor.attach(ptr_call_constructors, { 45 | onEnter(args) { 46 | if (!TargetLibModule) { 47 | TargetLibModule = Process.findModuleByName(TARGET_LIB_NAME); 48 | } 49 | 50 | if (TargetLibModule != null) { 51 | init_offsets.forEach(offset => { 52 | Interceptor.replace(TargetLibModule.base.add(offset), new NativeCallback(function () { 53 | console.log(`replace ${offset.toString(16)}`); 54 | }, "void", [])); 55 | }); 56 | 57 | bypass(); 58 | listener.detach() 59 | } 60 | 61 | }, 62 | onLeave(retval) { 63 | if (this.shouldSkip) { 64 | retval.replace(0); // 直接返回,不执行 `.init_array` 65 | } 66 | } 67 | }); 68 | } 69 | 70 | function hook_dlopen() { 71 | ["android_dlopen_ext", "dlopen"].forEach(funcName => { 72 | let addr = Module.findExportByName(null, funcName); 73 | if (addr) { 74 | Interceptor.attach(addr, { 75 | onEnter(args) { 76 | let libName = ptr(args[0]).readCString(); 77 | if (libName && libName.indexOf(TARGET_LIB_NAME) >= 0) { 78 | console.warn(`[!] Blocking ${funcName} loading: ${libName}`); 79 | hook_call_constructors(); 80 | } 81 | }, 82 | onLeave(retval) { 83 | } 84 | }); 85 | } 86 | }); 87 | } 88 | 89 | var is64Bit = Process.pointerSize === 8; 90 | hook_dlopen(); 91 | -------------------------------------------------------------------------------- /replaceM3U8url.js: -------------------------------------------------------------------------------- 1 | function hookURL() { 2 | var URL = Java.use('java.net.URL'); 3 | URL.$init.overload('java.lang.String').implementation = function (url) { 4 | if (url.indexOf('start=') > 0 && url.indexOf('end=') > 0) { 5 | //console.log('URL:' + url); 6 | var url2 = replaceUrlArgs(url, "start", 0); 7 | url2 = replaceUrlArgs(url2, "end", 99999); 8 | console.log('URL:' + url2); 9 | this.$init(url2); 10 | } 11 | } 12 | } 13 | 14 | function replaceUrlArgs(url, name, value) { 15 | const re = new RegExp(name + '=[^&]*', 'gi') 16 | return url.replace(re, name + '=' + value) 17 | } 18 | 19 | function replaceUrlArgs2(url, name, value) { 20 | const re = new RegExp(`(?<=${name}=)[^&]*`, 'g'); 21 | return url.replace(re, value) 22 | } 23 | 24 | function hook() { 25 | Java.perform(function () { 26 | hookURL(); 27 | }); 28 | } 29 | 30 | setImmediate(hook()); -------------------------------------------------------------------------------- /stalker-decode.js: -------------------------------------------------------------------------------- 1 | 2 | var targetLib = 'libxyz.so'; 3 | 4 | 5 | function hook_NativeMethodByRegister(fnName) { 6 | var symbols = Module.enumerateSymbolsSync("libart.so"); 7 | var addrRegisterNatives = null; 8 | for (var i = 0; i < symbols.length; i++) { 9 | var symbol = symbols[i]; 10 | 11 | //_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi 12 | if (symbol.name.indexOf("art") >= 0 && 13 | symbol.name.indexOf("JNI") >= 0 && 14 | symbol.name.indexOf("RegisterNatives") >= 0 && 15 | symbol.name.indexOf("CheckJNI") < 0) { 16 | addrRegisterNatives = symbol.address; 17 | } 18 | } 19 | 20 | if (addrRegisterNatives != null) { 21 | Interceptor.attach(addrRegisterNatives, { 22 | onEnter: function (args) { 23 | var methods_ptr = ptr(args[2]); 24 | var method_count = parseInt(args[3]); 25 | for (var i = 0; i < method_count; i++) { 26 | var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3)); 27 | var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2)); 28 | var methodName = Memory.readCString(name_ptr); 29 | if (methodName == fnName) { 30 | console.log(methodName, fnPtr_ptr); 31 | printStack(); 32 | traceNative(null, fnPtr_ptr); 33 | } 34 | 35 | } 36 | } 37 | }); 38 | } 39 | } 40 | 41 | // stalker 42 | function traceNative(moduleName, addr) { 43 | var p = addr; 44 | if (moduleName) { 45 | var baseAddr = Module.findBaseAddress(targetLib) 46 | if (!baseAddr) { return; } 47 | p = baseAddr.add(addr); 48 | } 49 | Interceptor.attach(p, { 50 | onEnter: function (args) { 51 | console.log("Enter " + addr) 52 | printStack(); 53 | this.tid = Process.getCurrentThreadId(); 54 | 55 | Stalker.follow(this.tid, { 56 | events: { 57 | call: true, // CALL instructions: yes please 58 | 59 | // Other events: 60 | ret: false, // RET instructions 61 | exec: false, // all instructions: not recommended as it's 62 | // a lot of data 63 | block: false, // block executed: coarse execution trace 64 | compile: false // block compiled: useful for coverage 65 | }, 66 | // onReceive(events) { 67 | // var all_events = Stalker.parse(events); 68 | // console.log("onReceive: ", all_events.length); 69 | // all_events.forEach(function (i) { 70 | // // console.log(i); 71 | // try { 72 | // var addr1 = i[1]; 73 | // var module1 = Process.getModuleByAddress(addr1); 74 | // if (module1 != null && module1.name === targetLib) { 75 | // var addr2 = i[2]; 76 | // var module2 = Process.getModuleByAddress(addr2); 77 | // console.log("call: ", module1.name + "!" + addr1.sub(module1.base), module2.name + "!" + addr2.sub(module2.base)) 78 | // } 79 | // } catch (error) { 80 | // console.log("error:", error) 81 | // } 82 | // }) 83 | // }, 84 | onCallSummary(summary) { 85 | // console.log(JSON.stringify(summary)) 86 | for (const target in summary) { 87 | const number = summary[target]; 88 | if (number == 1) { 89 | var module = Process.findModuleByAddress(target); 90 | if (module != null && module.name === targetLib) { 91 | console.log(module.name + "!" + ptr(target).sub(module.base)); 92 | } 93 | } 94 | } 95 | 96 | } 97 | }) 98 | }, onLeave: function (retVal) { 99 | console.log("Entering 0x14E20...") 100 | Stalker.unfollow(this.tid) 101 | 102 | } 103 | }) 104 | } 105 | 106 | function hook_native_function(addr) { 107 | Interceptor.attach(addr, { 108 | onEnter: function (args) { 109 | var module = Process.findModuleByAddress(addr); 110 | this.args0 = args[0]; 111 | this.args1 = args[1]; 112 | this.args2 = args[2]; 113 | this.args3 = args[3]; 114 | this.args4 = args[4]; 115 | this.logs = [] 116 | this.logs.push("------------------------\n"); 117 | this.logs.push("call " + module.name + "!" + ptr(addr).sub(module.base) + "\n"); 118 | this.logs.push("onEnter args0: " + print_arg(this.args0)); 119 | this.logs.push("onEnter args1: " + print_arg(this.args1)); 120 | this.logs.push("onEnter args2: " + print_arg(this.args2)); 121 | this.logs.push("onEnter args3: " + print_arg(this.args3)); 122 | this.logs.push("onEnter args4: " + print_arg(this.args4)); 123 | }, onLeave: function (ret) { 124 | this.logs.push("onLeave args0: " + print_arg(this.args0)); 125 | this.logs.push("onLeave args1:" + print_arg(this.args1)); 126 | this.logs.push("onLeave args2:" + print_arg(this.args2)); 127 | this.logs.push("onLeave args3:" + print_arg(this.args3)); 128 | this.logs.push("onLeave args4:" + print_arg(this.args4)); 129 | this.logs.push("onLeave return: " + print_arg(ret)); 130 | console.log(this.logs) 131 | } 132 | }) 133 | } 134 | 135 | function print_arg(addr) { 136 | var range = Process.findRangeByAddress(addr); 137 | console.log(range) 138 | if (range != null) { 139 | return hexdump(addr) + "\r\n"; 140 | } else { 141 | return ptr(addr) + "\r\n"; 142 | } 143 | } 144 | 145 | // 打印堆栈 146 | function printStack() { 147 | console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())); 148 | } 149 | 150 | 151 | function main() { 152 | hook_NativeMethodByRegister('decode'); 153 | // var baseAddr = Module.findBaseAddress("libnative-lib.so"); 154 | // hook_native_function(baseAddr.add(0x57514)); 155 | // hook_native_function(baseAddr.add(0x5971c)); 156 | // hook_native_function(baseAddr.add(0x157fc)); 157 | // //hook_native_function(baseAddr.add(0xf6e0)); //free 158 | // hook_native_function(baseAddr.add(0x151a4)); 159 | // hook_native_function(baseAddr.add(0x59670)); 160 | // //hook_native_function(baseAddr.add(0xf630)); //strlen 161 | // hook_native_function(baseAddr.add(0x19a84)); 162 | // hook_native_function(baseAddr.add(0x12580)); 163 | // //hook_native_function(baseAddr.add(0xf6a0)); //memset 164 | // hook_native_function(baseAddr.add(0x16a94)); 165 | // //hook_native_function(baseAddr.add(0x18540)); //短的字符串的时候无 166 | // hook_native_function(baseAddr.add(0xfcac)); 167 | // hook_native_function(baseAddr.add(0x12c0c)); 168 | // hook_native_function(baseAddr.add(0x5796c)); 169 | } 170 | 171 | 172 | // 主动调用App的jni函数 173 | function callJni(str) { 174 | Java.perform(function () { 175 | var javaString = Java.use('java.lang.String').$new(str); 176 | var result = Java.use("com.kanxue.algorithmbase.MainActivity").encodeFromJni_70(javaString); 177 | console.log("result is => ", result) 178 | }) 179 | } 180 | setImmediate(main) -------------------------------------------------------------------------------- /stalker-template.js: -------------------------------------------------------------------------------- 1 | /** 2 | 功能: 跟踪native函数调用情况。 3 | */ 4 | 5 | 6 | var TargetLibName = 'libxyz.so'; // 待分析的so库文件名 7 | var TargetFuncOffset = 0x5B50; // 待分析的native函数偏移, 先在IDA里找到 8 | var TraceCallNum = 1; // 默认只输出调用1次的函数 9 | 10 | 11 | function hookTargetFunc() { 12 | var baseAddr = Module.findBaseAddress(TargetLibName) 13 | console.log(TargetLibName + " Module Base: " + baseAddr); 14 | if (baseAddr == null) { 15 | console.log('Please makesure ' + TargetLibName + ' is Loaded, by setting extractNativeLibs true.'); 16 | return; 17 | } 18 | 19 | // hook指定的函数地址 20 | Interceptor.attach(baseAddr.add(TargetFuncOffset), { 21 | onEnter: function (args) { 22 | console.log('\nCall ' + TargetFuncOffset.toString(16).toUpperCase() + ' In') 23 | this.tid = Process.getCurrentThreadId(); 24 | Stalker.follow(this.tid, { 25 | events: { 26 | call: true, // CALL instructions: yes please 27 | 28 | // Other events: 29 | ret: false, // RET instructions 30 | exec: false, // all instructions: not recommended as it's 31 | // a lot of data 32 | block: false, // block executed: coarse execution trace 33 | compile: false // block compiled: useful for coverage 34 | }, 35 | 36 | /* // 这段暂时不开启,不稳定 37 | onReceive(events) { 38 | var all_events = Stalker.parse(events); 39 | console.log("onReceive: ", all_events.length); 40 | all_events.forEach(function (i) { 41 | // console.log(i); 42 | try { 43 | var addr1 = i[1]; 44 | var module1 = Process.getModuleByAddress(addr1); 45 | if (module1 != null && module1.name == TargetLibName) { 46 | var addr2 = i[2]; 47 | var module2 = Process.getModuleByAddress(addr2); 48 | console.log("call: ", module1.name + "!" + addr1.sub(module1.base), module2.name + "!" + addr2.sub(module2.base)) 49 | } 50 | } catch (error) { 51 | console.log("error:", error) 52 | } 53 | }) 54 | }, 55 | */ 56 | 57 | onCallSummary(summary) { 58 | //console.log(JSON.stringify(summary)); // 调用的所有函数及次数,注意并不是实际调用顺序。 59 | for (const target in summary) { 60 | const number = summary[target]; 61 | if (number == TraceCallNum) { 62 | var module = Process.findModuleByAddress(target); 63 | if (module != null && module.name == TargetLibName) { 64 | console.log(module.name + "!" + ptr(target).sub(module.base)); 65 | } 66 | } 67 | } 68 | } 69 | 70 | }) 71 | }, onLeave: function (retVal) { 72 | console.log('Call ' + TargetFuncOffset.toString(16).toUpperCase() + ' Out\n') 73 | Stalker.unfollow(this.tid) 74 | } 75 | }) 76 | } 77 | 78 | function hook(addr) { 79 | Interceptor.attach(addr, { 80 | onEnter: function (args) { 81 | var module = Process.findModuleByAddress(addr); 82 | this.args0 = args[0]; 83 | this.args1 = args[1]; 84 | this.args2 = args[2]; 85 | this.args3 = args[3]; 86 | this.args4 = args[4]; 87 | this.logs = [] 88 | this.logs.push("------------------------\n"); 89 | this.logs.push("call " + module.name + "!" + ptr(addr).sub(module.base) + "\n"); 90 | this.logs.push("onEnter args0: " + print_arg(this.args0)); 91 | this.logs.push("onEnter args1: " + print_arg(this.args1)); 92 | this.logs.push("onEnter args2: " + print_arg(this.args2)); 93 | this.logs.push("onEnter args3: " + print_arg(this.args3)); 94 | this.logs.push("onEnter args4: " + print_arg(this.args4)); 95 | }, onLeave: function (ret) { 96 | this.logs.push("onLeave args0: " + print_arg(this.args0)); 97 | this.logs.push("onLeave args1:" + print_arg(this.args1)); 98 | this.logs.push("onLeave args2:" + print_arg(this.args2)); 99 | this.logs.push("onLeave args3:" + print_arg(this.args3)); 100 | this.logs.push("onLeave args4:" + print_arg(this.args4)); 101 | this.logs.push("onLeave return: " + print_arg(ret)); 102 | console.log(this.logs) 103 | } 104 | }) 105 | } 106 | 107 | function print_arg(addr) { 108 | var range = Process.findRangeByAddress(addr); 109 | console.log(range) 110 | if (range != null) { 111 | return hexdump(addr) + "\r\n"; 112 | } else { 113 | return ptr(addr) + "\r\n"; 114 | } 115 | } 116 | 117 | 118 | function main() { 119 | // 需要hook的函数地址列表 120 | var funcAddr = [ 121 | // 0xbd84, 122 | // 0x12190, 123 | ]; 124 | 125 | if (funcAddr.length == 0) { 126 | hookTargetFunc(); 127 | } else { 128 | var baseAddr = Module.findBaseAddress(TargetLibName); 129 | console.log(TargetLibName + " Module Base: " + baseAddr); 130 | 131 | for (var i = 0; i < funcAddr.length; i++) { 132 | hook(baseAddr.add(funcAddr[i])); 133 | } 134 | } 135 | } 136 | 137 | 138 | setImmediate(main) -------------------------------------------------------------------------------- /stalker.js: -------------------------------------------------------------------------------- 1 | // stalker 2 | function hook_14E20() { 3 | var baseAddr = Module.findBaseAddress("libnative-lib.so") 4 | //var symbols = Module.enumerateSymbolsSync("libart.so"); symbols.forEach(function (item) { console.log(JSON.stringify( item)) }) 5 | Interceptor.attach(baseAddr.add(0x14E20), { 6 | onEnter: function (args) { 7 | console.log("Entering 0x14E20...") 8 | this.tid = Process.getCurrentThreadId(); 9 | 10 | Stalker.follow(this.tid, { 11 | events: { 12 | call: true, // CALL instructions: yes please 13 | 14 | // Other events: 15 | ret: false, // RET instructions 16 | exec: false, // all instructions: not recommended as it's 17 | // a lot of data 18 | block: false, // block executed: coarse execution trace 19 | compile: false // block compiled: useful for coverage 20 | }, 21 | // onReceive(events) { 22 | // var all_events = Stalker.parse(events); 23 | // console.log("onReceive: ", all_events.length); 24 | // all_events.forEach(function (i) { 25 | // // console.log(i); 26 | // try { 27 | // var addr1 = i[1]; 28 | // var module1 = Process.getModuleByAddress(addr1); 29 | // if (module1 != null && module1.name == "libnative-lib.so") { 30 | // var addr2 = i[2]; 31 | // var module2 = Process.getModuleByAddress(addr2); 32 | // console.log("call: ", module1.name + "!" + addr1.sub(module1.base), module2.name + "!" + addr2.sub(module2.base)) 33 | // } 34 | // } catch (error) { 35 | // console.log("error:", error) 36 | // } 37 | // }) 38 | // }, 39 | onCallSummary(summary) { 40 | // console.log(JSON.stringify(summary)) 41 | for (const target in summary) { 42 | const number = summary[target]; 43 | if (number == 1) { 44 | var module = Process.findModuleByAddress(target); 45 | if (module != null && module.name == "libnative-lib.so") { 46 | console.log(module.name + "!" + ptr(target).sub(module.base)); 47 | } 48 | } 49 | } 50 | 51 | } 52 | }) 53 | }, onLeave: function (retVal) { 54 | console.log("Entering 0x14E20...") 55 | Stalker.unfollow(this.tid) 56 | 57 | } 58 | }) 59 | } 60 | 61 | function hook_native_function(addr) { 62 | Interceptor.attach(addr, { 63 | onEnter: function (args) { 64 | var module = Process.findModuleByAddress(addr); 65 | this.args0 = args[0]; 66 | this.args1 = args[1]; 67 | this.args2 = args[2]; 68 | this.args3 = args[3]; 69 | this.args4 = args[4]; 70 | this.logs = [] 71 | this.logs.push("------------------------\n"); 72 | this.logs.push("call " + module.name + "!" + ptr(addr).sub(module.base) + "\n"); 73 | this.logs.push("onEnter args0: " + print_arg(this.args0)); 74 | this.logs.push("onEnter args1: " + print_arg(this.args1)); 75 | this.logs.push("onEnter args2: " + print_arg(this.args2)); 76 | this.logs.push("onEnter args3: " + print_arg(this.args3)); 77 | this.logs.push("onEnter args4: " + print_arg(this.args4)); 78 | }, onLeave: function (ret) { 79 | this.logs.push("onLeave args0: " + print_arg(this.args0)); 80 | this.logs.push("onLeave args1:" + print_arg(this.args1)); 81 | this.logs.push("onLeave args2:" + print_arg(this.args2)); 82 | this.logs.push("onLeave args3:" + print_arg(this.args3)); 83 | this.logs.push("onLeave args4:" + print_arg(this.args4)); 84 | this.logs.push("onLeave return: " + print_arg(ret)); 85 | console.log(this.logs) 86 | } 87 | }) 88 | } 89 | 90 | function print_arg(addr) { 91 | var range = Process.findRangeByAddress(addr); 92 | console.log(range) 93 | if (range != null) { 94 | return hexdump(addr) + "\r\n"; 95 | } else { 96 | return ptr(addr) + "\r\n"; 97 | } 98 | } 99 | 100 | 101 | function main() { 102 | // hook_14E20(); 103 | var baseAddr = Module.findBaseAddress("libnative-lib.so"); 104 | hook_native_function(baseAddr.add(0x57514)); 105 | hook_native_function(baseAddr.add(0x5971c)); 106 | hook_native_function(baseAddr.add(0x157fc)); 107 | //hook_native_function(baseAddr.add(0xf6e0)); //free 108 | hook_native_function(baseAddr.add(0x151a4)); 109 | hook_native_function(baseAddr.add(0x59670)); 110 | //hook_native_function(baseAddr.add(0xf630)); //strlen 111 | hook_native_function(baseAddr.add(0x19a84)); 112 | hook_native_function(baseAddr.add(0x12580)); 113 | //hook_native_function(baseAddr.add(0xf6a0)); //memset 114 | hook_native_function(baseAddr.add(0x16a94)); 115 | //hook_native_function(baseAddr.add(0x18540)); //短的字符串的时候无 116 | hook_native_function(baseAddr.add(0xfcac)); 117 | hook_native_function(baseAddr.add(0x12c0c)); 118 | hook_native_function(baseAddr.add(0x5796c)); 119 | } 120 | 121 | 122 | // 主动调用App的jni函数 123 | function callJni(str) { 124 | Java.perform(function () { 125 | var javaString = Java.use('java.lang.String').$new(str); 126 | var result = Java.use("com.kanxue.algorithmbase.MainActivity").encodeFromJni_70(javaString); 127 | console.log("result is => ", result) 128 | }) 129 | } 130 | setImmediate(main) -------------------------------------------------------------------------------- /webview-load-url.js: -------------------------------------------------------------------------------- 1 | Java.use("android.webkit.WebView").loadUrl.overload("java.lang.String").implementation = function (s) { 2 | send(s.toString()); 3 | this.loadUrl.overload("java.lang.String").call(this, s); 4 | }; --------------------------------------------------------------------------------