29、商业加固产品分析:360加固、腾讯加固、娜迦加固的特征与脱壳思路

做逆向这些年,我接触最多的就是这三家加固:360、腾讯、娜迦。说实话,它们几乎垄断了国内Android加固市场。你随便从应用商店下个热门App,十有八九就是其中之一。

今天我就把这三家的特征和脱壳思路掰开揉碎了讲。嗯,都是实战中摸爬滚打出来的经验,希望能帮你少走弯路。

29.1 360加固:老牌劲旅,特征明显

360加固是我最早接触的商业加固产品。它的特征非常鲜明,一眼就能认出来。

特征识别

  • 包名特征:解压APK后,你会看到 libjiagu.solibjiagu_art.so 等文件。这是360加固的招牌文件。
  • 入口点修改:AndroidManifest.xml 中的 Application 类会被替换为 com.qihoo.util.StubApplication 或类似名称。
  • Dex结构:classes.dex 通常是一个壳dex,真正的业务代码被加密存储在 assets/ 目录或 lib/ 下的so文件中。
  • so文件:除了 libjiagu.so,还可能有 libjiagu_x86.solibjiagu_64.so 等,用于不同架构。

快速判断技巧:用jadx打开APK,如果看到 com.qihoo 包名,基本就是360加固无疑了。

脱壳思路

360加固的脱壳,我个人习惯用 Frida + DumpDex 方案。为什么?因为它的壳在运行时会把原始dex加载到内存中,我们只需要在合适时机dump出来就行。

// Frida脚本:Hook ArtMethod的入口,dump dex
function dumpDex() {
    var dexOffset = Module.findExportByName("libart.so", "_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPNS_12OatDexFileEPS9_");
    Interceptor.attach(dexOffset, {
        onEnter: function(args) {
            var dexAddr = args[1];
            var dexSize = args[2].toInt32();
            var dexData = ptr(dexAddr).readByteArray(dexSize);
            var file = new File("/sdcard/dump_" + dexSize + ".dex", "wb");
            file.write(dexData);
            file.close();
            console.log("[+] Dumped dex, size: " + dexSize);
        }
    });
}
setTimeout(dumpDex, 1000);

我曾经遇到过一个问题:360加固在Android 10以上版本增加了反调试,Frida一attach就闪退。后来我改用 frida-gadget 注入方式,绕过了这个检测。

避坑指南:360加固的so文件有完整性校验。如果你修改了so文件,App会直接crash。建议用内存dump方式,不要尝试静态修改。

29.2 腾讯加固:大厂风范,多层防护

腾讯加固(也叫腾讯云加固、乐固)是另一大主流。它的防护层级更多,脱壳难度也更高。

特征识别

  • 包名特征:解压后能看到 libshell.solibtup.solibBugly.so 等文件。其中 libshell.so 是核心壳so。
  • 入口点修改:Application 类被替换为 com.tencent.StubShellcom.tencent.mm 开头的类。
  • Dex结构:classes.dex 非常小,只有几百KB,真正的代码被加密存储在 lib/armeabi/libshell.so 中。
  • 资源保护:腾讯加固会对资源文件进行加密,直接解压看到的图片、布局文件都是乱码。

快速判断技巧:在jadx中搜索 StubShelltup 字符串,如果出现,基本就是腾讯加固。

脱壳思路

腾讯加固的脱壳,我建议用 内存dump + 修复 两步走。为什么不能一步到位?因为腾讯加固会把dex分段加载,直接dump出来的dex可能不完整。

// Frida脚本:Hook libart.so 的 LoadMethod 方法
function hookLoadMethod() {
    var symbols = Module.enumerateSymbolsSync("libart.so");
    var loadMethod = null;
    for (var i = 0; i < symbols.length; i++) {
        if (symbols[i].name.indexOf("LoadMethod") !== -1) {
            loadMethod = symbols[i].address;
            break;
        }
    }
    if (loadMethod) {
        Interceptor.attach(loadMethod, {
            onEnter: function(args) {
                // 这里可以获取到dex的起始地址和大小
                console.log("[+] LoadMethod called");
            }
        });
    }
}
setTimeout(hookLoadMethod, 2000);

我记得有一次,腾讯加固的某个版本用了 VMP(虚拟机保护),把核心逻辑都转成了自定义指令。那一次我不得不先分析它的虚拟机解释器,再写脚本还原。嗯,确实折腾了好几天。

注意事项:腾讯加固有反Frida检测。它会检查 /data/local/tmp/ 目录下是否有frida-server文件。建议把frida-server重命名,或者用 frida-gadget 模式。

29.3 娜迦加固:小而精悍,定制化强

娜迦加固虽然市场份额不如前两家,但它的技术方案很有特色。很多金融类App喜欢用它。

特征识别

  • 包名特征:解压后能看到 libnaga.solibnagavm.so 等文件。娜迦的so命名很统一,都带 naga 前缀。
  • 入口点修改:Application 类被替换为 com.naga.stubcom.naga.core 开头的类。
  • Dex结构:娜迦会把原始dex拆分成多个片段,分别加密存储。你可能会看到 classes2.dexclasses3.dex 等,但都是假的。
  • so文件:娜迦的so文件通常有 libnaga.so(核心壳)、libnagavm.so(虚拟机)、libnagadump.so(反调试)等。

快速判断技巧:在APK的 lib/ 目录下搜索 naga 关键字,如果有,就是娜迦加固。

脱壳思路

娜迦加固的脱壳,我个人觉得最有效的是 定制Frida脚本 + 手动修复。为什么?因为娜迦的壳会动态解密dex,而且解密时机很晚,需要耐心等待。

// Frida脚本:监控dex文件的加载
function monitorDexLoad() {
    var dexFileOpen = Module.findExportByName("libart.so", "_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPNS_12OatDexFileEPS9_");
    Interceptor.attach(dexFileOpen, {
        onEnter: function(args) {
            this.dexAddr = args[1];
            this.dexSize = args[2].toInt32();
        },
        onLeave: function(retval) {
            if (this.dexAddr && this.dexSize > 0) {
                var dexData = ptr(this.dexAddr).readByteArray(this.dexSize);
                // 检查dex魔数
                if (dexData[0] === 0x64 && dexData[1] === 0x65 && dexData[2] === 0x78) {
                    var file = new File("/sdcard/naga_dex_" + Date.now() + ".dex", "wb");
                    file.write(dexData);
                    file.close();
                    console.log("[+] Naga dex dumped, size: " + this.dexSize);
                }
            }
        }
    });
}
setTimeout(monitorDexLoad, 3000);

我曾经在脱娜迦加固时,发现dump出来的dex无法直接用jadx打开。后来分析发现,娜迦对dex的字符串表做了混淆。需要写一个修复脚本,把字符串表还原才能正常反编译。

避坑指南:娜迦加固有 时间戳校验。如果你用调试器附加,它会检测系统时间是否被修改。建议在真机上操作,不要用模拟器。

29.4 三家加固对比总结

说了这么多,我整理了一个对比表格,方便你快速查阅。

加固产品 核心特征 脱壳难度 推荐方案 常见坑点
360加固 libjiagu.so、StubApplication ★★☆☆☆ Frida内存dump so完整性校验
腾讯加固 libshell.so、StubShell ★★★☆☆ 内存dump + 修复 反Frida检测、VMP保护
娜迦加固 libnaga.so、naga包名 ★★★★☆ 定制脚本 + 手动修复 字符串混淆、时间戳校验

29.5 核心知识体系图

下面这张图,我把三家的特征和脱壳思路串起来了。你保存下来,以后遇到加固App,先对照这张图快速定位。

商业加固产品特征与脱壳思路 360加固 腾讯加固 娜迦加固 libjiagu.so / StubApplication libshell.so / StubShell libnaga.so / naga包名 Frida内存dump 内存dump + 修复 定制脚本 + 手动修复 ⚠ so完整性校验 ⚠ 反Frida检测 / VMP ⚠ 字符串混淆 / 时间戳校验 核心思路:内存中找dex,时机要对,工具要准

嗯,以上就是我对这三家商业加固产品的实战分析。说白了,脱壳这件事没有银弹,每家都有各自的防护特点。但核心思路是一样的:找到dex在内存中的加载时机,用合适的方式dump出来。你多练几次,自然就熟练了。