【Frida Android】实战篇:Frida-Trace 进阶追踪——JNI 函数调用栈与参数解析
1. 深入理解JNI函数调用栈追踪第一次用Frida-Trace追踪JNI函数时最让我困惑的就是如何看清整个调用链路。记得当时分析一个金融类APP发现它调用了十几个so库函数调用关系像蜘蛛网一样复杂。后来通过反复实践终于摸索出一套完整的分析方法。JNI函数调用栈Call Stack本质上记录了函数执行的上下文关系。当Java层通过JNI调用Native函数时系统会先将Java虚拟机状态压栈然后跳转到Native代码执行。这个过程会产生多层调用关系我们需要用特殊方法才能完整捕获。实际操作中最实用的工具是Frida的Backtracer模块。在自动生成的追踪脚本中加入以下代码onEnter(log, args, state) { // 打印当前线程的调用栈 log(Call stack:\n Thread.backtrace(this.context, Backtracer.ACCURATE) .map(DebugSymbol.fromAddress).join(\n) \n); }这段代码会在每次JNI函数被调用时打印出完整的调用链。我常用这个技巧来分析加密函数的调用源头比如发现某个AES加密操作是由某个按钮点击事件触发的。对于跨so库调用的复杂场景还需要特别注意dlopen和dlsym这两个关键函数。很多应用会动态加载so库这时候就需要先Hook这两个函数记录所有加载的库和符号地址。下面是我常用的一个模板Interceptor.attach(Module.findExportByName(null, dlopen), { onEnter: function(args) { this.path args[0].readCString(); console.log(Loading library: this.path); }, onLeave: function(retval) { if (!retval.isNull()) { console.log(Library loaded at: retval); } } });2. 复杂参数解析实战技巧解析JNI函数参数是个技术活特别是遇到结构体、联合体这些复杂类型时。去年分析一个视频编辑APP时就遇到过一个包含5层嵌套的结构体参数折腾了整整两天才完全解析出来。对于基本类型参数Frida-Trace生成的脚本已经自动帮我们处理好类型转换。比如Java的int对应jintboolean对应jboolean等。但遇到jobject、jclass这些引用类型时就需要手动处理了。我的经验是先用Java.cast方法转换类型onEnter(log, args, state) { // 解析jstring参数 const jstr args[1]; // 假设第二个参数是jstring const str Java.vm.getEnv().getStringUtfChars(jstr).readCString(); log(Received string: str); // 解析jobject参数 const obj Java.cast(args[2], Java.use(android.content.Context)); log(Context package: obj.getPackageName()); }结构体参数的解析更复杂些。首先要确定结构体的内存布局可以用IDA或Ghidra反编译so文件查看定义。比如遇到如下结构体struct ComplexData { int type; union { int num; char* str; } value; float weights[4]; };对应的解析代码应该是onEnter(log, args, state) { const structPtr args[1]; // 结构体指针 // 解析整型字段 const type structPtr.readInt(); // 解析联合体 let value; if (type 0) { value structPtr.add(4).readInt(); } else { value structPtr.add(4).readPointer().readCString(); } // 解析数组 const weights []; const weightsPtr structPtr.add(8); for (let i 0; i 4; i) { weights.push(weightsPtr.add(i * 4).readFloat()); } log(Parsed struct: type${type}, value${value}, weights[${weights}]); }3. 多线程环境下的追踪策略实际项目中最头疼的就是多线程问题。记得有次分析一个即时通讯APP加密函数在10个不同线程中被调用日志完全乱套了。后来总结出几个关键技巧首先是给日志加上线程信息。修改自动生成的脚本模板onEnter(log, args, state) { const threadId Process.getCurrentThreadId(); log([Thread ${threadId}] Entering ${this.functionName}); state.startTime Date.now(); }其次是处理线程同步问题。当多个线程调用同一个JNI函数时可以使用Frida的Mutex实现简单的线程安全日志const mutex new Mutex(); onEnter(log, args, state) { mutex.lock(); try { log([${Thread.currentThread().id}] Safe log entry); } finally { mutex.unlock(); } }对于需要跨线程追踪调用链的场景我通常会建立一个全局的调用树数据结构const callTree {}; function logCall(parentId, funcName) { const threadId Process.getCurrentThreadId(); if (!callTree[threadId]) { callTree[threadId] []; } const callId uuid(); // 生成唯一ID callTree[threadId].push({ id: callId, parent: parentId, name: funcName, timestamp: Date.now() }); return callId; }4. 性能优化与错误处理长时间追踪大型应用时性能问题就凸显出来了。有一次追踪一个游戏APP不到10分钟手机就发烫了。经过多次优化总结出这些经验首先是减少不必要的日志输出。可以通过环境变量控制日志级别const LOG_LEVEL parseInt(Process.getenv(FRIDA_LOG_LEVEL) || 1); onEnter(log, args, state) { if (LOG_LEVEL 1) { log(Detailed debug info: ${args[0]}); } }其次是优化内存使用。解析大块数据时要及时释放内存onEnter(log, args, state) { const env Java.vm.getEnv(); const jstr args[1]; const cstr env.getStringUtfChars(jstr); try { log(cstr.readCString()); } finally { env.releaseStringUtfChars(jstr, cstr); } }错误处理也很关键。JNI函数可能抛出异常需要用特殊方法检查onLeave(log, retval, state) { const env Java.vm.getEnv(); if (env.exceptionCheck()) { const ex env.exceptionOccurred(); env.exceptionClear(); log(Exception thrown: ${ex.toString()}); retval.replace(NULL); } }对于高频调用的JNI函数建议使用CModule来提升性能const cm new CModule( #include stdint.h int64_t fast_hash(const char* str) { int64_t hash 0; while (*str) { hash (hash * 31) *str; } return hash; } ); onEnter(log, args, state) { const fastHash new NativeFunction(cm.fast_hash, int64, [pointer]); const str args[1].readCString(); const hash fastHash(Memory.allocUtf8String(str)); log(Fast hash: ${hash}); }
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2466869.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!