一、核心问题梳理针对新版 LibDexHelper.so 加密壳现有通用绕过方案均失效核心痛点在于其反调试逻辑与 DEX 解密时机特殊JNI_OnLoad 并非初始导出而是在 init_array 执行完成后才动态生成反调试自杀函数会直接导致进程崩溃且 DEX 解密入口需精准定位才能完成 dump。本文将从样本信息出发分步实现反调试绕过与 DEX 解密解决 Frida 调试崩溃、hook 无效等关键问题。二、样本基础信息项目值说明package namecn.samsclub.app目标应用包名versionCode659内部递增版本号versionName5.0.144对外版本号sdkVersion (minSdk)23最低支持 Android 6BuildConfig.DEFAULT_PATCH_ID2026-05-26 15:44:11热修复时间三、核心操作步骤步骤1明确壳加载核心逻辑LibDexHelper.so 的加载与常规 SO 不同核心有三点一是初始 ELF 导出表无 JNI_OnLoad 符号直接 hook 会返回 null 甚至导致崩溃二是 JNI_OnLoad 在 init_array 执行完毕后通过解密 payload 并替换符号表才“出现”三是正确 hook 窗口为 dlopen 返回时此时 init_array 已执行、符号表已替换但 ART 尚未调用 JNI_OnLoad。其加载调用顺序如下java.lang.System.loadLibrary libart.so!Runtime_void_Runtime_load0 linker64!__dl___loader_android_dlopen_ext // 加载目标 SO linker64!call_constructors // 执行构造函数 lib.so!init_array // 反调试、解密逻辑在此执行 libart.so!art::JavaVMExt::LoadNativeLibrary(...) lib.so!JNI_OnLoad // 动态生成后被调用步骤2定位并 patch 反调试自杀函数通过 Frida trace 调试发现进程崩溃核心源于 sub_11C64 函数该函数通过计算非法地址执行 MOV SP, X0; BR X12 指令导致进程自杀。解决思路是将该函数 patch 为空函数阻断自杀逻辑。具体操作如下通过 IDA 逆向定位 sub_11C64 函数确认其指令逻辑为自杀触发编写 Frida 脚本在 dlopen 返回后、JNI_OnLoad 调用前获取 sub_11C64 的内存地址使用 NativeCallback 将该函数替换为空实现确保 JNI_OnLoad 能完整执行。关键脚本片段patch 核心逻辑// Patch sub_11C64 为空函数 function patchSub11C64() { const target gPayloadBase.add(CFG.sub11C64Off); if (!looksLikeCode(target)) { console.log([!] sub_11C64 未解密无法 patch); return false; } gSub11C64Callback new NativeCallback(function () {}, void, []); try { Interceptor.replace(target, gSub11C64Callback); console.log([] 成功 patch sub_11C64阻断自杀逻辑); return true; } catch (e) { console.log([!] patch 失败: e); return false; } }步骤3定位 DEX 解密入口并 dump 数据DEX 解密核心函数为 sub_F490该函数通过反射调用 art::DexFileLoader::OpenCommon将内存中的 DEX 加载进 ART。在该函数入口处 hook即可捕获解密后的 DEX 数据并 dump。具体操作逆向分析定位 sub_F490 函数确认其为 DEX 解密入口在 dlopen 返回后获取 sub_F490 的内存地址并 hook在 hook 的 onEnter 阶段读取 DEX 基地址与大小将数据写入本地文件。关键脚本片段dump 核心逻辑// Hook sub_F490 并 dump DEX function hookAndDumpSubF490() { const target gPayloadBase.add(CFG.subF490Off); if (!looksLikeCode(target)) { console.log([!] sub_F490 未解密无法 hook); return false; } Interceptor.attach(target, { onEnter(args) { this.a1 args[0]; const dexBase this.a1.readPointer(); const dexSize dexBase.add(32).readU32(); const dexBytes dexBase.readByteArray(dexSize); const fileName CFG.dumpDir /dex_ (gDumpCount) .dex; const f new File(fileName, wb); f.write(dexBytes); f.close(); console.log([] 成功 dump DEX: fileName); } }); console.log([] 成功 hook sub_F490); return true; }步骤4验证与后续分析执行脚本后观察日志确认 JNI_OnLoad 完整执行返回 0x10004且成功 dump 出 8 个 DEX 文件。后续可通过 Java层 trace 分析应用启动流程确认崩溃点为 Java 层初始化异常而非壳的反调试逻辑。同时可整理 native 函数绑定表为后续系统性分析提供依据。四、核心结论与补充说明1. 绕过核心新版 LibDexHelper.so 的反调试绕过关键是 patch sub_11C64 自杀函数且需精准把握 hook 时机dlopen 返回后、JNI_OnLoad 调用前通用绕过脚本对该版本无效。2. DEX 解密通过 hook sub_F490 函数可成功 dump 解密后的 DEX共获取 8 个有效文件验证了壳的解密流程为“init_array 解密 payload → 替换符号表 → JNI_OnLoad 执行 → DEX 加载”。3. 后续问题JNI_OnLoad 执行完毕后应用仍出现 access-violation 崩溃经 trace 确认是 Java 层初始化异常非 native 层反调试导致可通过 Java.perform hook 相关类和方法进一步排查。4. 工具提示实操过程中可借助 LumaFrida 官方 GUI 工具进行调试其支持持久会话、实时 REPL 等功能可提升调试效率相关工具可访问 luma.frida.re 获取。五、下一步行动建议1. 基于本文提供的脚本针对目标应用调整配置参数如 SO 名称、函数偏移量快速完成反调试绕过与 DEX 解密2. 结合 dump 出的 DEX 文件对应用的 native 函数绑定表进行系统性分析排查 Java 层崩溃原因3. 若需提升调试稳定性可优化脚本的异常处理逻辑结合 Stalker 工具进行更细致的流程追踪。