梆梆的特征与防护思路
梆梆加固(bangcle)的识别特征:入口是 com.secneo.apkwrapper.ApplicationWrapper 或 MyWrapperProxyApplication,assets 里有 libsecexe.so / libsecmain.so。它的防护比 360 更"黏"一点——除了 dex 加密,还有反调试、模拟器检测、dex 加载点混淆的组合拳。
脱壳前先过反调试,否则 Frida 一 attach 就闪退。
姿势零:先绕过反调试
梆梆的反调试常见手段:检测 TracerPid、检测 frida 端口(27042)、检测 frida-agent 的内存特征。通用绕过:
// 1. 绕过 TracerPid 检测:hook open/read 伪造 /proc/self/status
var openPtr = Module.findExportByName('libc.so', 'open');
Interceptor.attach(openPtr, {
onEnter: function(args) {
var path = args[0].readCString();
if (path.indexOf('/proc/') >= 0 && path.indexOf('status') >= 0) {
this.isStatus = true;
}
},
onLeave: function(retval) { this.isStatus = false; }
});
// 配合 hook read,把 TracerPid: xxx 改成 TracerPid: 0
// 2. spawn 模式启动,抢在反调试初始化之前
// frida -U -f com.target.app -l bypass.js --no-pause姿势一:hook dex 加载点(推荐)
梆梆最终也要把明文 dex 交给系统加载。Android 运行时的必经之路是 libart.so 里的 DexFile 相关函数:
function hookDexLoad() {
var patterns = ['*!*OpenMemory*', '*!*OpenCommon*', '*!*DexFile*'];
var resolver = new ApiResolver('module');
patterns.forEach(function(p) {
resolver.enumerateMatches('exports:' + p).forEach(function(m) {
if (!m.name.includes('libart')) return;
try {
Interceptor.attach(m.address, {
onEnter: function(args) {
// 参数布局随 Android 版本不同,一般前两个参数是 base+size
for (var i = 0; i < 4; i++) {
try {
var addr = args[i];
if (addr.readU32() === 0x0a786564) { // "dex\n" 小端
var size = addr.add(0x20).readU32();
console.log('dex @ ' + addr + ' size=' + size);
send({type: 'dex'}, addr.readByteArray(size));
}
} catch(e) {}
}
}
});
} catch(e) {}
});
});
}在参数里找 dex 魔数,找到就按头部 fileSize 字段完整 dump。
姿势二:内存扫描兜底
加载点 hook 失败(符号混淆严重)时,退回内存扫描:
setTimeout(function() {
Process.enumerateRanges('r--').forEach(function(range) {
if (range.size > 20 * 1024 * 1024) return; // 跳过超大区域防卡死
try {
Memory.scan(range.base, range.size, '64 65 78 0a 30 33 35 00', {
onMatch: function(addr) {
var size = addr.add(0x20).readU32();
if (size > 1024 && size < 50 * 1024 * 1024) { // 尺寸合理性校验
send({type: 'dex'}, addr.readByteArray(size));
}
},
onComplete: function() {}
});
} catch(e) {}
});
}, 5000);尺寸校验很重要——内存里有大量误命中,fileSize 在合理区间(1KB~50MB)才是真 dex。
姿势三:hook 文件操作抓落地文件
梆梆某些版本会把解密后的 dex 写到 /data/data/包名/ 下的临时文件再加载。hook 文件 API 直接截胡:
var open = Module.findExportByName('libc.so', 'open');
Interceptor.attach(open, {
onEnter: function(args) {
var path = args[0].readCString();
if (path.indexOf('.dex') >= 0 || path.indexOf('.jar') >= 0) {
console.log('[open] ' + path);
// 记下路径,稍后直接 adb pull
}
}
});看到落地路径后 adb pull 即可,连内存 dump 都省了。
精髓:dump 后的验收清单
- 数量对不对:梆梆可能把原 dex 拆成多个,dump 出的 dex 数量要和 jadx 打开 APK 看到的"缺失程度"对照——业务类全了才算完。
- 方法体空不空:梆梆企业版有方法体抽取,jadx 里看到大量
throw new RuntimeException()占位就是中招了,需要主动调用回填(Youpk/Fart 路线)。 - Manifest 里的入口要改回:重打包时把 Application 从壳的
ApplicationWrapper改回原 App 的入口(从 dump 出的 dex 里找)。
总结
- 先过反调试(TracerPid/frida 特征),否则 attach 即闪退
- 首选 hook libart 加载点,内存扫描兜底,文件截胡看运气
- dump 后验收三件套:dex 数量、方法体完整性、Manifest 入口
- 方法体抽取是企业版分水岭,中招就得上主动调用式脱壳
交流微信:run1255