首页 / 新闻资讯 / 用Frida和IDA测了4家加固方案,发现有一家完全防不住
先说结论:3家能防住常规攻击,1家10分钟被脱壳。 下面直接上测试日志。

厂商A(传统加壳)
jadx打开后直接报错"Unexpected magic",JEB提示"Obfuscated Dalvik bytecode"。很正常,加壳的基本操作。
但用JEB的unpacker模块跑了3分钟,内存中dump出来的DEX居然直接就是明文。壳没做内存加密,解密后完整的classes.dex躺在内存里等着被捞。
厂商B(DEX加密+SO校验)
jadx反编译后看到的是ProxyApplication壳类,真实的DEX在so里解密。这个套路见多了,问题是他们把解密密钥硬编码在了so的.rodata段,字符串都没混淆。IDA打开so,Shift+F12直接看到AES密钥。
厂商C(VMP虚拟化)
jadx看到的是全是native方法,JEB反编译后每个方法只有一行throw new RuntimeException()。so层用了自研虚拟机,核心逻辑不在ARM指令里,而在自定义字节码中。JEB的DEX反编译器没法直接还原。
厂商D(Java2C编译加密)
jadx打开后惊呆了——整个APK里一个Java方法都找不到?仔细看,原本的Java代码全部被编译成了C,再编译成so。Java层只剩JNI stub。静态分析等于面对一个纯Native程序,而C编译出来的ARM汇编基本没有可读性。
第一轮评分:
| 厂商 | jadx反编译结果 | JEB反编译结果 | 静态可读性 |
|---|---|---|---|
| A | 报错 | dump后全明文 | 极差(但原因菜) |
| B | 壳类+so解密 | 同左,密钥暴露 | 中(密钥都在) |
| C | 全native | 虚拟机字节码 | 低(需逆向VM) |
| D | 全native | Native代码 | 极低(C编译产物) |
先说通用步骤:
adb shell /data/local/tmp/as # 运行android_serveradb forward tcp:23946 tcp:23946# IDA附加进程厂商A(传统加壳)
IDA直接附加成功,没有任何反调试。断在libc.so的fopen,发现它在读/proc/self/status检查TracerPid。直接在JNI_OnLoad开头patch掉检测函数,继续单步。so没有任何anti-anti-debug手段,一路跟到解密函数,拿到完整DEX。
耗时:15分钟从附加到dump完成。
厂商B(DEX加密+SO校验)
有反调试。ptrace(PTRACE_TRACEME)自我附加,防止其他调试器附加。IDA附加后立即SIGTRAP崩溃。
绕过方案:用Frida提前hook ptrace:
Interceptor.attach(Module.findExportByName(null, "ptrace"), { onEnter: function(args) { if (args[0].toInt32() === 0) { // PTRACE_TRACEME this.ret = 0; console.log("[*] Blocked ptrace(PTRACE_TRACEME)"); } }, onLeave: function(retval) { if (this.ret !== undefined) retval.replace(0); }});绕过ptrace后IDA能附加,但so内还有多处时间差检测,单步执行会被识别。需要逐段patch跳过分支。
耗时:45分钟绕过反调试,dump成功。
厂商C(VMP虚拟化)

附加进程后,反调试触发更狠。除了ptrace检测,还会扫描/proc/self/maps里是否存在frida、gum-js等特征字符串。Frida进程名检测直接杀进程。
尝试用strongR-frida-android魔改版,改了frida-server二进制特征。能附上了,但VMP解释器内做了栈回溯检测,只要断点命中,立即检测当前PC是否在预期范围内。
VMP的核心问题是:即使你附加上去了,看到的也不是原始ARM代码,而是虚拟机解释器在跑字节码。想要还原原始逻辑,必须逆向整个VM的指令集。
耗时:4小时,仅摸清VM指令格式,未完成核心逻辑还原。
厂商D(Java2C编译加密)
附加后第一反应:进程里没有dex相关内存段。整个应用逻辑全在so里运行。
反调试强度很高:检查了/system/bin/app_process的cmdline、Xposed特征、frida端口扫描。用定制内核模块绕过基础检测后,IDA能单步了,但代码流程完全看不懂——C编译器做了O2优化,循环展开+内联+常量传播,再加上控制流平坦化,反汇编结果是一个巨大的switch-case分发。
这不是加壳的问题,是源码级混淆。你想要逆向的是一个正常的Native程序,但它原本应该是Java逻辑。
耗时:2小时,只定位到入口函数,核心算法完全迷失。
第二轮评分(对抗专业逆向人员+定制工具):
| 厂商 | ptrace检测 | Frida检测 | 内存扫特征 | 分析门槛 |
|---|---|---|---|---|
| A | 无 | 无 | 无 | 低 |
| B | 有(可绕过) | 无 | 无 | 中 |
| C | 有 | 有 | 有 | 高(需逆向VM) |
| D | 有 | 有 | 有 | 极高(源码级混淆) |
测试方法:编写通用的anti-anti-debug脚本,尝试hook所有可疑API。
// 常见检测点hookconst targets = [ 'ptrace', 'fopen', 'open', 'read', 'strstr', 'pthread_create', 'syscall', '__system_property_get'];厂商A:脚本跑起来后,所有检测全被hook掉,核心函数参数直接打印出来。成功率100%。
厂商B:部分检测通过hook绕过,但有一处在子进程中执行的检测没覆盖到。增加子进程监控后成功。
厂商C:VMP解释器内部做了环境检测,不在常规API层面,而在解释执行过程中检查时间和栈帧。简单的API hook不够用,需要用Stalker做指令级跟踪。开启Stalker后性能急剧下降,手机卡死。VMP + Stalker同时跑等于DoS。
厂商D:Java2C后,Java层的Hook点根本不存在。所有逻辑在Native执行,想要hook必须在汇编层面修改函数首字节,但该厂商做了代码段CRC校验,修改后立即崩溃。需要先过校验再hook,复杂度指数上升。
第三轮结果:
回顾标题——有一家完全防不住,说的是厂商A。

它的加固方案可以用一句话总结:静态加壳,内存明文,无反调试。这种方案在2016年还能唬人,2026年还在卖就是收割信息差。用传统的/proc/self/maps找dex基址,dd出来就是完整代码,连脱壳机都不需要。
技术层面排名(防护强度):
选型建议:
如果你的应用核心算法被拿到=公司倒闭(金融交易、风控模型、游戏数值),选D或C,别省预算。
如果只是防普通破解玩家,B够用。
如果厂商A的销售上门,直接送客。
// bypass_ptrace.js - 绕过ptrace自我附加var ptracePtr = Module.findExportByName(null, "ptrace");if (ptracePtr) { Interceptor.attach(ptracePtr, { onEnter: function(args) { var request = args[0].toInt32(); if (request === 0) { // PTRACE_TRACEME console.log("[+] Blocking PTRACE_TRACEME"); this.block = true; } }, onLeave: function(retval) { if (this.block) retval.replace(0); } });}// dump_dex.js - 从内存中定位并dump dexvar ranges = Process.enumerateRanges({ protection: 'rw-', coalesce: true});ranges.forEach(function(range) { if (range.size > 1024 * 1024) { var data = ptr(range.base).readByteArray(range.size); if (data && data.length > 4 && data[0] === 0x64 && data[1] === 0x65 && data[2] === 0x78) { console.log("[+] Found DEX at " + range.base); // dump logic here } }});```