百趣云 百趣云的博客

360 加固脱壳实战:内存 dump 与 dex 修复全流程

先认清壳的结构

360 加固(jiagu)的核心思路:原 dex 加密后藏在壳 dex 的资源/资产里,运行时壳先启动,解密原 dex 并通过自定义 ClassLoader 加载。脱壳的本质就是等它解密加载完成后,从内存里把明文 dex 抠出来。

判断是不是 360 加固:jadx 打开 APK,入口 Application 是 com.stub.StubApp,assets 里有 libjiagu.so / libjiagu_a64.so,基本实锤。

脱壳时机:等"解密完成"这个窗口

壳的工作流程:StubApp.attachBaseContext → 解密 dex → 替换 ClassLoader → 启动原 Application。明文 dex 在内存中的存活窗口从解密完成开始,到进程结束。所以脱壳脚本要在原 Application 的 onCreate 之后动手——这时所有 dex 都已解密映射。

方案一:FRIDA-DEXDump 暴力搜内存

最省事的方案,用 FRIDA-DEXDump 工具直接扫内存里的 dex 魔数:

frida -U -f com.target.app -l agent.js
# 进入 REPL 后
// agent.js 核心逻辑:搜内存里的 "dex\n035" 魔数
Java.perform(function() {
    setTimeout(function() {   // 等壳解密完成
        Process.enumerateRanges('r--').forEach(function(range) {
            try {
                Memory.scan(range.base, range.size, '64 65 78 0a 30 33 35 00', {
                    onMatch: function(address, size) {
                        console.log('发现 dex @ ' + address);
                        // 读 dex 头里的 fileSize 字段(偏移 0x20)
                        var fileSize = address.add(0x20).readU32();
                        var dex = address.readByteArray(fileSize);
                        send({type: 'dex', addr: address}, dex);
                    },
                    onComplete: function() {}
                });
            } catch(e) {}
        });
    }, 3000);
});

Python 侧接收内存块存成 .dex 文件。搜出来的通常有多个:壳自己的 dex、原 App 的 dex(可能拆成多个)、框架 dex。按大小和包名分辨。

方案二:hook 解密函数精准拿

360 加固的解密在 native 层(libjiagu.so),但 dex 最终要交给 Android 运行时。hook 运行时的 dex 加载点更精准:

// hook libart 的 OpenMemory / DexFile 构造
var openMemory = Module.findExportByName('libart.so', 
    '_ZN3art7DexFile10OpenMemoryEPKhmRKNSt3__112basic_stringIc...');
// 符号名随 Android 版本变化,用 frida 的 ApiResolver 模糊匹配:
var resolver = new ApiResolver('module');
resolver.enumerateMatches('exports:*!*OpenMemory*').forEach(function(m) {
    if (m.name.includes('libart')) {
        Interceptor.attach(m.address, {
            onEnter: function(args) {
                var base = args[1];   // dex 内存基址
                var size = args[2];   // 大小
                send({type: 'dex'}, base.readByteArray(size.toInt32()));
            }
        });
    }
});

精髓:dump 之后的修复才是关键

内存 dump 出来的 dex 经常不能直接分析,360 加固会做两类破坏:

  1. 方法体抽取(VMP 级防护):部分方法的 CodeItem 被清空,运行时才回填。dump 出来的 dex 里这些方法是空的(No instructions)。修复办法是 hook ArtMethod 的 invoke,在方法被调用时把 CodeItem dump 出来回填——这就是"主动调用式脱壳",工具如 Youpk、Fart 走这个路线。
  2. 字符串加密:jadx 打开看到 StubApp.getString(12345) 之类的调用,真实字符串运行时才解密。批量修复要写脚本模拟解密函数回填,或直接用 Frida 在运行时批量调用 getString 建映射表。

验证脱壳成功

# 看 dex 里有没有业务包名
strings dump.dex | grep "com/target/app" | head
# jadx 打开看入口 Activity 是否有真实代码
jadx-gui dump.dex

总结

  1. 360 加固特征:StubApp 入口 + libjiagu.so
  2. 脱壳时机:原 Application onCreate 之后,dex 全部解密映射
  3. 暴力方案搜 dex\n035 魔数,精准方案 hook libart 的 OpenMemory
  4. dump 只是第一步,方法体抽取和字符串加密才是修复的主战场

交流微信:run1255

By 百趣云 阅读量:3 On