☰
分辨Java2C、JNI下沉与抽取壳:apk-reverse的2000倍密度差异指南
2026/10/2 7:16:28 网站建设 项目流程

分辨Java2C、JNI下沉与抽取壳:apk-reverse的2000倍密度差异指南

【免费下载链接】apk-reverseSuitable for Android APK reverse engineering analysis项目地址: https://gitcode.com/gh_mirrors/ap/apk-reverse

💡 在APK 逆向中,"方法体不见了"可能来自Java2C、JNI 下沉或抽取壳三种完全不同的机制。本文基于 apk-reverse 项目实测数据,教你用native 方法密度这一个指标,快速分辨这三者——真实样本间约2000 倍的密度差异,让判断不再靠猜。

为什么"方法体不见了"有三个答案

拿到一个加固的 APK,反编译后发现某堆类只剩下native声明、没有方法体。新手的第一反应往往是"壳把 dex 加密了,我去内存里 dump 一份"——但这条路线对其中一种保护永远无效,因为它从头到尾都不会产生解密后的 DEX。

apk-reverse 把这种情况拆成了三种形态,错配一行就可能浪费几天:

形态dex 静态长相内存里实际有什么正确路线
抽取壳看起来正常,方法体是桩(stub)方法体在被调用时才解密填充dump 内存,测stub%,主动调用
Java2C整个类都是native,完全没有 code_item永远没有 DEX——代码以 C 编译进.so直接读.so,重建 JNI 调用链
JNI 下沉只有几个native声明,其余是普通 Java普通 DEX定位 Java 调用点,反编译一两个 native 函数

📌 一句话判别法:这个产物在生命周期内是否曾持有你关心的方法的 dex 字节码?抽取壳"有",JNI 下沉"有",Java2C"从来没有"。完整对照表见 java2c-and-jni-sinking.md。

核心指标:native 密度,2000 倍的差距

区分 Java2C 和 JNI 下沉,靠的不是"看到 native 声明",而是一个可测量的数字——native 方法占总方法的比例。项目用 java2c_probe.py 在真实样本上测得:

样本dex 方法数native 方法数native 密度
JNI 下沉目标 A(MASTG L2)508120.04%
JNI 下沉目标 B(MASTG L3)1118230.03%
带真实 JNI 库的 App2360370.03%
Java2C 形态样本292482.76%

📊 82.76% ÷ 0.03% ≈2700 倍——这就是标题里"2000 倍密度差异"的出处(记录在 evidence-summary.md 能力矩阵中)。

背后的逻辑很简单:

  • JNI 下沉天然是"外科手术式"的——只把少数热点方法挪进 native,其余代码留在 Java,所以密度只有零点零几个百分点;
  • Java2C 是整体翻译——整个类的方法都被搬进.so,密度直接跳到百分之几十,且会有整类"只剩 native 方法"的类(构造函数除外:翻译流程实践中会把<init>/<clinit>留在 Java,所以"所有方法都是 native"这个判据几乎不可达,别用它)。

实操三步:如何用 apk-reverse 完成判别

第 1 步:测 dex 层密度。对 APK 里的 dex 统计native方法占比。密度在百分之几十、且有整类全是 native 方法的,是 Java2C 形态;只有个位数 native 方法的,更像 JNI 下沉。

第 2 步:交叉验证.so符号。数一下.so里导出的Java_*符号数量,与 dex 里native方法数对比——项目实测为2:2 和 3:3 的一一对应,这是静态链接翻译/下沉的强特征。

第 3 步:注意弱信号不是结论。JNI_OnLoad、RegisterNatives字符串、libc++_shared.so这类命中在普通 JNI 库里无处不在,单独出现不能作为加固判据。脚本输出时也会把弱命中单独列为警告块,提醒你别把它当结论。

⚠️ 一个容易踩的坑:如果你grep Java_符号表什么都没搜到,不代表"没有 native 实现"——动态注册 +-fvisibility=hidden会把符号全部藏起来。真正有效的检查是:该库导出JNI_OnLoad却导出零个Java_*符号,这就是动态注册的肯定证据。细节见 java2c-and-jni-sinking.md 的"JNI 边界"一节。

判别之后:三条路线怎么走

  • 判定为 Java2C→ 别 dump 内存(目标产物根本不存在,dump 只会让你误以为"dump 失败")。正确做法:以 dex 为索引、以.so为库,逐个读翻译出的 C 函数(每个都以JNIEnv *env, jobject thiz开头),沿着FindClass/GetMethodID/Call*Method这些反向 JNI 调用把原始 Java 逻辑拼回来。
  • 判定为抽取壳→ 先 dump 内存,用 dex_dump_validate.py 测stub%,再走 FART 式主动调用。形态诊断表见 advanced-unpacking.md。
  • 判定为 JNI 下沉→ dex 仍然可读,定位 Java 调用点后反编译对应的少数 native 函数即可,成本最低。

新手常见误判速查

你看到的现象它不是它其实是
整类是native,dump 内存后还看到同样的native声明"dump 失败 / 工具坏了"代码被永久移出 dex,去读.so
grep Java_符号表返回空"没有 native 实现"动态注册或隐藏可见性
库只导出JNI_OnLoad,其他都不认识"这是个 loader"可能是普通的动态注册 JNI 库
密度高但每个类都还有 Java 方法Java2C大面积 JNI 下沉,或 R8 剥离了方法体

延伸阅读

  • 判别依据与失败模式全记录:java2c-and-jni-sinking.md
  • 分类探针脚本(支持--apk/--dex/--so/--json):java2c_probe.py
  • 抽取壳 dump 后的形态诊断与恢复边界:advanced-unpacking.md
  • 全部实测数据与强度标注(observed / inferred / unverified):evidence-summary.md

💡小结:先测密度,再看符号,最后选路线。2000 倍的密度差异就是那张"五形态表"里最便宜也最可靠的判据——花一分钟量一下,能省下一整天的错误路线。

【免费下载链接】apk-reverseSuitable for Android APK reverse engineering analysis项目地址: https://gitcode.com/gh_mirrors/ap/apk-reverse

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询