Frida实战:动态脱壳360加固应用

发布时间:2026/7/4 2:13:17
Frida实战:动态脱壳360加固应用 1. 环境准备与工具安装动态脱壳的第一步是搭建好工作环境。我们需要准备一台已经root的Android设备或者模拟器我个人推荐使用真机因为部分模拟器可能会被360加固检测到。另外还需要安装好Python环境和Node.js因为Frida的很多工具链依赖这些环境。Frida的安装其实很简单直接用pip就能搞定pip install frida-tools但这里有个坑要注意frida-server的版本必须和本地frida-tools版本一致。我遇到过好几次因为版本不匹配导致连接失败的情况。可以去Frida的GitHub releases页面下载对应版本的frida-server比如当前最新稳定版是15.1.27wget https://github.com/frida/frida/releases/download/15.1.27/frida-server-15.1.27-android-arm64.xz解压后推送到手机adb push frida-server-15.1.27-android-arm64 /data/local/tmp adb shell chmod x /data/local/tmp/frida-server-15.1.27-android-arm64启动服务前记得先转发端口adb forward tcp:27042 tcp:27042 adb shell /data/local/tmp/frida-server-15.1.27-android-arm642. 目标应用分析与Hook点定位拿到一个加固应用首先要确定它的包名。这里有个小技巧可以用frida-ps命令列出所有正在运行的进程frida-ps -Ua找到目标应用后我们需要分析它的内存加载情况。360加固通常会通过libart.so来加载原始DEX所以我们的Hook点就选在DexFile类的OpenCommon方法上。这个方法在不同Android版本中的符号名会有些差异Android O_ZN3art7DexFile10OpenCommonEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPKNS_10OatDexFileEbbPS9_PNS0_12VerifyResultEAndroid P_ZN3art13DexFileLoader10OpenCommonEPKhmS2_mRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPKNS_10OatDexFileEbbPS9_NS3_10unique_ptrINS_16DexFileContainerENS3_14default_deleteISH_EEEEPNS0_12VerifyResultE实际操作中可以用下面这个脚本自动获取当前设备的函数名var moduleFuncName; var m Module.enumerateExportsSync(libart.so); m.forEach(function(m){ if(m.name.indexOf(OpenCommon) ! -1){ moduleFuncName m.name; console.log(module function name: m.name); }else if(m.name.indexOf(OpenMemory) ! -1){ moduleFuncName m.name; console.log(module function name: m.name); }; });3. Frida脚本编写与DEX Dump找到正确的Hook点后就可以编写核心的脱壳脚本了。这个脚本主要做三件事拦截DEX加载、读取内存中的DEX数据、保存到文件。下面是我在实际项目中验证过的脚本Interceptor.attach(OpenCommon,{ onEnter: function(args){ console.log(base: args[1]); console.log(size: args[2].toInt32()); // 打印前64字节的hexdump console.log(hexdump( args[1],{ offset: 0, length: 64, header: true, ansi: true })); var begin args[1]; console.log(magic : Memory.readUtf8String(begin)) // 计算DEX文件大小 var address parseInt(begin,16) 0x20; var dex_size Memory.readInt(ptr(address)); console.log(dex_size : dex_size); // 保存到文件 var file new File(/data/data/com.target.app/ dex_size .dex, wb); file.write(Memory.readByteArray(begin, dex_size)); file.flush(); file.close(); }, onLeave: function(retval){ console.log(Finished!!!); } });这里有几个关键点需要注意DEX文件的魔数一般是dex\n可以用来验证是否捕获到正确的内存区域DEX文件大小存储在头部偏移0x20的位置保存路径要确保有写入权限建议直接使用目标应用的数据目录4. 常见问题与解决方案在实际操作中你可能会遇到各种问题。下面分享几个我踩过的坑问题1Frida连接失败可能原因包括端口未正确转发检查adb forwardfrida-server版本不匹配用frida --version检查手机未root或selinux限制尝试setenforce 0问题2Hook不到OpenCommon这种情况通常是因为函数符号名不匹配先用脚本动态获取加固壳做了反调试尝试在非调试模式下启动应用Android版本太新/太旧需要调整Hook点问题3dump出的DEX无法解析可能原因是捕获的时机不对有些壳会多次加载内存数据被修改尝试不同的Hook点文件头损坏手动修复magic和checksum对于360加固我建议在应用刚启动时就附加Frida因为它的壳加载比较靠前。如果遇到反调试可以试试下面这个绕过脚本var pthread_create Module.findExportByName(null, pthread_create); Interceptor.replace(pthread_create, new NativeCallback(function (a, b, c, d) { return 0; }, int, [pointer, pointer, pointer, pointer]));5. 后续分析与处理成功dump出DEX后还需要做一些后续处理。我通常会用dex2jar转换成jar文件d2j-dex2jar.sh dumped.dex然后用JD-GUI或者直接使用jadx-gui查看代码。如果发现代码逻辑不完整可能是多dex的情况需要重复dump过程。对于360加固有时候会碰到抽取指令的情况这时候需要结合动态分析和静态分析。我常用的方法是用Frida trace关键方法调用对比原始DEX和运行时内存用IDA Pro分析so层逻辑最后提醒一点脱壳后的代码可能会有一些反调试陷阱在分析时要特别注意那些看起来不合理的条件判断和异常处理。