JADX 看不到算法之后,公开脱壳工具还能走多远:一次御盾加固 APK 实测
原文承接页:JADX 能不能还原原始算法?御盾加固 APK 的静态还原难度实测
这篇稿按看雪读者的习惯写:先给目标,再给过程,再给失败边界。结论不是“不可逆”,而是普通静态反编译没有还原原始算法,公开运行态 dump 工具也没有产出可用 DEX。测评继续走到了动态层,但没有把未验证的启动、二次打包和危险环境写成通过。
看雪版本重点保留工具链过程和失败尝试。它不会给出可复现 dump 步骤,但会说明为什么这轮不是只看原始 DEX。
测评目标与非目标
本轮不是泛扫 APK,也不是把“反编译看不到”直接包装成安全结论。目标被限定为:在常规静态反编译之后,继续使用公开工具和隔离运行环境,观察是否能进一步还原原始算法或形成可用 DEX dump。
| 目标 |
本轮动作 |
可公开结论 |
非目标与边界 |
| 原始算法还原 |
使用 JADX 观察 Java 可读面,再用 apktool 看 smali 结构。 |
普通源码面没有直接形成原始算法链,恢复结果集中在壳层、桥接、代理、装载和完整性相关结构。 |
不公开类名、源码片段、真实调用链。 |
| DEX 动态 dump |
安装公开 dexdump 工具并尝试运行态 dump。 |
公开工具链没有产出可用 DEX dump 文件。 |
不写动态 dump 已完成,不公开执行细节。 |
| 安装验证 |
在干净运行环境里安装候选 APK。 |
安装阶段成立,可以作为动态专项的前置条件。 |
不把安装成功写成业务通过。 |
| 稳定启动 |
拉起入口并观察进程状态。 |
运行期进入 native 快速闭合,不能声明完整稳定启动结论。 |
不公开日志栈、设备、组件、进程。 |
| 二次打包与危险环境 |
本轮只作为下一步计划。 |
需要单独专项验证。 |
不写二次打包闭合、危险环境闭合。 |
| SO/Assets 载体 |
观察 native stripped、assets 大体量载荷和字符串面。 |
核心材料不在普通 Java 面直接展开,native 和 assets 是下一步重点。 |
不公开 SO 名、assets 名、构建标识、偏移。 |
这个目标矩阵的作用,是防止报告把“安装成功”“入口被拉起”“JADX 没有源码”混成一个大结论。每一行都只回答自己能回答的问题。
证据与测评依据
| # |
公开证据 |
复核方式 |
支撑判断 |
公开边界 |
| 1 |
APK 容器呈现双 DEX、双 native、多组 assets。 |
静态枚举容器结构。 |
样本不是单一 DEX 混淆形态,保护材料分布在多层载体。 |
不公开文件清单和真实名称。 |
| 2 |
JADX 生成 43 个 Java 文件,并报告 2 个反编译错误。 |
使用公开 Java 反编译工具观察恢复面。 |
普通源码面没有直接还原原始算法。 |
不公开源码目录、类名和代码片段。 |
| 3 |
apktool 生成 43 个 smali 文件,smali 体量约 9.2 万行。 |
资源解包和 smali 反汇编。 |
壳层、桥接、装载和控制逻辑体量明显。 |
不公开 smali 内容和方法名。 |
| 4 |
Manifest 呈现 release 配置、代理启动链和 native 不普通展开。 |
读取脱敏后的 Manifest 角色。 |
防护介入更早,native 载体不按普通资源方式暴露。 |
不公开 Application、Activity、Provider、factory 名称。 |
| 5 |
静态检索观察到 ClassLoader、ByteBuffer、native bridge、MessageDigest、签名摘要和 System.load 类痕迹。 |
按功能类别归纳恢复面。 |
DEX 还原需要跨越内存载入、native 桥接和完整性链。 |
不公开真实函数名、字符串和常量。 |
| 6 |
最大 assets 载体约 62 MB。 |
统计 assets 载体体量。 |
核心材料主要不在普通 Java 源码面直接展开。 |
不公开 assets 名、格式和内容。 |
| 7 |
native 载体为 arm64 ELF 且 stripped。 |
静态读取 native 表面。 |
native 低成本符号导航线索被收敛。 |
不公开 SO 名、构建标识、符号和偏移。 |
| 8 |
常见 secret、token、password、api-key 类明文模式未形成有效泄露证据。 |
字符串面模式扫描。 |
发布面明文暴露风险较低。 |
不公开完整 strings 输出。 |
| 9 |
干净环境下安装成功。 |
隔离环境安装烟测。 |
安装阶段可验证,不再沿用纯静态空结论。 |
不公开设备、包名、命令和安装路径。 |
| 10 |
入口拉起后进入 native 快速闭合,没有稳定业务界面。 |
最小动态启动观察。 |
不能公开声明完整稳定启动结论。 |
日志、栈、组件、进程只保留内部。 |
| 11 |
frida-dexdump 已安装并尝试,未产出可用 DEX dump。 |
公开工具 attach/spawn 两类路径尝试。 |
公开工具链未形成动态脱壳结果。 |
不公开具体命令、连接方式和原始错误。 |
| 12 |
Frida 能观察到短生命周期进程,但稳定 attach 窗口不足。 |
运行态进程枚举与状态观察。 |
运行期闭合速度对常规动态 dump 形成压力。 |
不公开进程号、应用标识和标签。 |
这些证据的共同点是:可以支撑“公开工具链未直接还原原始算法”,但不能支撑“所有动态对抗都完成”。这就是公开技术文章必须保留的边界。
攻击工具矩阵:本轮覆盖了什么,没有覆盖什么
这次补充的关键,不是把更多工具名堆上去,而是把攻击者的常规路线按阶段排清楚。每个阶段都要回答三个问题:这类工具想拿什么、这轮观察到了什么、为什么还不能写成最终通过或最终失败。
| 攻击阶段 |
常见工具/方案类别 |
攻击者目标 |
本轮纳入方式 |
本轮结论 |
后续专项 |
| Java 源码恢复 |
JADX、JEB 类 Java 反编译器 |
直接恢复业务类、算法类和常量。 |
已用公开 Java 反编译工具观察恢复面。 |
恢复到的 Java 面有限,未形成原始算法链。 |
换工具交叉确认,但不公开源代码。 |
| smali 反汇编 |
apktool、baksmali 类工具 |
绕过 Java 反编译失败,直接看 Dalvik 指令。 |
已做 smali 解码并统计体量。 |
smali 面主要支撑壳层、桥接、装载和控制流判断。 |
做控制流类别归纳,不公开方法级细节。 |
| DEX dump/脱壳 |
frida-dexdump、运行态 dump、内存提取类工具 |
在类加载之后拿到内存中的 DEX 或修复后的 DEX。 |
已尝试公开 dump 工具。 |
没有产出可用 DEX dump。 |
需要可控运行窗口、Gadget 或真机矩阵专项。 |
| 注入/Hook |
Frida、Xposed/LSPosed、inline hook、Java 层 hook |
挂载关键加载点、签名点、解密点或业务函数。 |
本轮只做运行态观测和短生命周期进程确认。 |
能观察到窗口,但未形成稳定 hook 证据。 |
下一轮做防御型 observer,不公开真实 hook 点。 |
| 调试/跟踪 |
JDWP、ptrace、gdb/lldb、IDA/Ghidra 静态辅助 |
暂停进程、看调用栈、追 native 分支和解密时机。 |
本轮没有做完整调试链,只把 native stripped 和快速闭合作为调试难点记录。 |
不写“调试已阻断”,只写“低成本调试线索不足”。 |
单独做 native 调试抗性和符号恢复专项。 |
| 二次打包 |
apktool 回包、签名替换、资源改写、入口替换 |
改包、重签、绕完整性或替换入口。 |
本轮没有执行二次打包闭环。 |
不写二次打包通过或失败。 |
单独测回包、重签、安装、启动和 fail-closed。 |
| 危险环境 |
root、模拟器、Magisk、调试镜像、Hook 框架环境 |
放大动态观测能力,降低注入和 dump 门槛。 |
本轮只做干净环境安装启动,不写危险环境结论。 |
危险环境抗性未验证。 |
后续做环境矩阵,结论按环境分层。 |
这张表解决的是“知识面太窄”的问题:文章不再只围绕一个反编译器和一个 dump 工具,而是把脱壳、注入、调试、重打包、危险环境全部放进攻击路径。已经执行的写结果,没执行的明确写成后续专项,避免伪造事实。
测评经过
这轮测评重新补上了上一版缺失的内容:不是只看原始 DEX,也不是只跑 JADX 就收工。流程先做容器枚举,再做 JADX 和 apktool;随后进入隔离运行环境完成干净安装;最后尝试公开 dexdump 工具。最终结论也因此变得更克制:安装阶段成立,公开工具未产出可用 DEX dump,启动后进入 native 快速闭合,所以不能写稳定启动结论。
现象复核链:看到效果以后,至少再核实一次
上一版的问题是:看到一个现象后很快跳到下一步,没有解释它是否被复核。本版把每个现象都拆成“首次观察、复核动作、仍然不能证明什么”三段。
| 首次观察 |
复核动作 |
复核后的判断 |
仍然不能证明 |
| Java 面没有出现原始算法链。 |
用 smali 面再看壳层、桥接和装载体量。 |
这不是单纯反编译器失败,smali 面也没有直接给出完整业务算法。 |
不能证明所有算法不可恢复。 |
| smali 体量明显。 |
结合 Manifest 入口代理和 ClassLoader/native bridge 类别观察。 |
smali 主要价值在证明启动链和运行态装载复杂,而不是直接恢复算法。 |
不能把 smali 数量写成安全强度。 |
| assets 存在大体量载体。 |
对照 Java 面和 native 面,确认核心材料没有在普通源码面直接展开。 |
资产化承载是原始算法不可直接读取的重要原因之一。 |
不能公开 assets 名称和格式,也不能断言其具体内容。 |
| native 载体 stripped。 |
对照静态符号面和加载痕迹。 |
低成本符号导航线索被收敛,静态追 native 成本上升。 |
不能写成 native 不可分析。 |
| 干净环境安装成功。 |
继续执行入口拉起,不把安装成功当业务成功。 |
安装阶段成立,但业务稳定启动没有成立。 |
不能写二次打包、兼容性和危险环境通过。 |
| 启动后快速闭合。 |
用运行态枚举确认存在短生命周期窗口,再看 dump 工具是否产出。 |
闭合现象对 attach/dump 时间窗形成压力。 |
不能公开日志、栈、进程、包名,也不能写成所有注入失败。 |
| 公开 dump 工具未产出。 |
检查输出侧没有得到可用 DEX 文件,并回看进程稳定性。 |
失败更可能来自运行窗口不足、运行态闭合和材料不在普通 Java 面。 |
不能写成“永远无法脱壳”。 |
这条复核链把“看到效果”变成“看到现象、复查一次、收住结论”。真正专业的测评不是每一步都赢,而是每一步都知道自己能证明什么。
原始报告事实映射
| 报告事实 |
公开转述 |
支撑判断 |
未公开边界 |
| 本轮从本地最新加固 APK 候选开始,而不是使用人工整理稿。 |
有明确样本输入,测评不是凭空写作。 |
文章具备事实源。 |
路径、文件名、摘要。 |
| 静态结构包含 DEX、native、assets 多层载体。 |
加固结果呈现多层承载,不是单点混淆。 |
需要跨层评估。 |
文件清单、资产名称。 |
| JADX 只恢复有限 Java 面并有错误。 |
普通 Java 反编译未直接恢复算法。 |
静态还原难度被抬高。 |
源码路径和片段。 |
| apktool 能反汇编,但 smali 主要表现为壳层、装载和桥接面。 |
反汇编结果不等于原始算法可读。 |
需要运行态和载体专项。 |
smali 方法名。 |
| Manifest 有代理链和 release 配置。 |
保护介入更早,发布形态更收敛。 |
启动链前置。 |
真实组件。 |
| assets 存在大体量载体。 |
核心材料可能转入资产化承载。 |
普通 Java 面不能代表全貌。 |
assets 细节。 |
| 干净安装成立。 |
动态验证不是空白,安装阶段已确认。 |
可进入下一轮动态专项。 |
设备和命令。 |
| 启动后进入快速闭合。 |
不能写稳定启动或业务通过。 |
保留事实边界。 |
原始日志和栈。 |
| frida-dexdump 未产出 dump。 |
公开工具没有拿到可用 DEX。 |
动态 dump 未形成,不夸大。 |
attach/spawn 细节。 |
| 短生命周期进程可被观察但不稳定。 |
普通 attach 时间窗不足。 |
后续需要可控环境专项。 |
进程与应用标识。 |
动态时间线
| 阶段 |
做了什么 |
观察结果 |
判断 |
公开边界 |
| 1 |
选择最新候选 |
得到一个约 63 MB 的加固 APK。 |
输入明确。 |
不公开文件名和摘要。 |
| 2 |
静态容器枚举 |
双 DEX、双 native、多组 assets。 |
多载体结构成立。 |
不公开清单。 |
| 3 |
JADX 反编译 |
43 个 Java 文件,2 个错误。 |
Java 面未直接还原算法。 |
不公开源码。 |
| 4 |
apktool 反汇编 |
43 个 smali 文件,约 9.2 万行。 |
反汇编可见但以壳层和装载为主。 |
不公开方法名。 |
| 5 |
Manifest 观察 |
release 配置、代理链、native 不普通展开。 |
启动链前置。 |
不公开组件。 |
| 6 |
安装烟测 |
干净环境安装成功。 |
安装阶段成立。 |
不公开设备和命令。 |
| 7 |
启动观察 |
入口拉起后 native 快速闭合。 |
不能写稳定启动结论。 |
不公开日志和栈。 |
| 8 |
Frida 枚举 |
可观察到短生命周期进程。 |
具备运行态窗口,但窗口不足。 |
不公开进程与应用标识。 |
| 9 |
frida-dexdump 尝试 |
未产出可用 DEX dump。 |
公开工具链未形成动态脱壳结果。 |
不公开运行方式。 |
| 10 |
专项规划 |
Gadget、真机、二次打包、DEX 修复、SO 载体进入后续。 |
当前文章只保留本轮事实。 |
不公开可复现路径。 |
攻防逻辑
攻击者做低成本还原,通常不会一开始就写复杂工具。他会先用 JADX 看 Java 面,再用 apktool 看 smali 和 Manifest,然后检查 native、assets 和字符串面。如果这些都没有直接给出算法,才会进入运行态 dump、Hook、Gadget、内存提取或定制环境。
本轮走到了公开工具的运行态尝试,而不是停在 JADX。这个差别很关键:只说“JADX 看不到”,懂行的人会认为证据很薄;写清楚 JADX、apktool、安装、Frida、frida-dexdump 的实际结果,才像真实测评记录。
| 攻击步骤 |
本轮观察 |
防守侧意义 |
不能公开的内容 |
| Java 反编译 |
只恢复有限 Java 面。 |
原始算法没有直接摊在普通源码层。 |
源码和类名。 |
| smali 反汇编 |
反汇编可见,但以壳层/桥接/装载为主。 |
需要跨层还原,不能只看 smali。 |
方法名和控制流细节。 |
| Manifest 定位 |
有代理链和 release 配置。 |
防护介入靠前。 |
真实组件。 |
| native 定位 |
native stripped。 |
低成本符号线索收敛。 |
符号和偏移。 |
| assets 提取 |
大体量载体存在。 |
核心材料不在普通 Java 面。 |
文件名和格式。 |
| 动态 dump |
公开工具未形成可用 DEX dump。 |
常规工具链没有直接拿到材料。 |
attach/spawn 细节。 |
| 稳定运行 |
入口拉起后快速闭合。 |
不能把安装成功写成业务通过。 |
日志与栈。 |
这里的“好话”不是硬夸,而是只写被验证的正向事实:静态源码面没有直接还原、公开 dump 工具未形成结果、安装阶段成立、运行边界被如实保留。没有证据的好话不写。
主因链:为什么没有复现出原始算法和可用 dump
本轮现象可以收束成一条主因链,而不是散点结论:
- 普通 Java 反编译没有直接恢复原始算法,说明核心逻辑没有以低成本源码面完整暴露。
- smali 面能看到较大体量的壳层、桥接、装载和控制结构,但它更像“运行态材料调度层”,不是业务算法明文层。
- Manifest 代理链、ClassLoader 类别、native bridge 类别和大体量 assets 共同指向同一件事:关键材料被放到跨 DEX、native、assets 和运行态加载路径里。
- 干净环境安装成功以后,入口拉起进入 native 快速闭合,说明动态分析不能只依赖普通 attach 时机。
- 公开 dump 工具没有产出可用 DEX,更可能是“可见 Java 面不足 + 运行态窗口短 + native/asset 载体参与 + 完整性/闭合路径介入”共同导致,而不是某一个工具单点失败。
所以,本轮最准确的结论是:常规静态还原无法直接复现原始算法;公开运行态 dump 工具在这个运行窗口下没有拿到可用 DEX;后续若要继续推进,必须把目标拆成 DEX dump 抗性、native 调试抗性、二次打包闭合和危险环境矩阵四个专项。文章不应该把这四个专项提前写成已通过。
public_evidence:
measurement_scope:
target: "static recovery plus public runtime dump attempt"
non_goal:
- "no claim of full business startup"
- "no claim of completed dynamic unpacking"
- "no claim of repackaging closure"
observations:
jadx_java_files: 43
jadx_errors: 2
apktool_smali_files: 43
smali_volume: "about 92k lines"
container_shape: "two DEX, two native carriers, multiple asset carriers"
install_result: "clean install succeeded"
runtime_result: "native fast closure after launch"
public_dexdump_result: "no usable DEX dump produced"
publish_boundary:
allowed:
- "tool categories"
- "redacted counts"
- "measurement sequence"
- "supported positive observations"
private:
- "package identifiers"
- "local paths"
- "raw commands"
- "class and component names"
- "logs, stacks, symbols, offsets"
defensive_assessment_flow:
1. classify_sample_without_publishing_identifiers
2. run_static_decompiler_and_record_visible_surface
3. run_resource_and_smali_decoder_for_shell_structure
4. inspect_native_and_asset_carriers_without_naming_them
5. install_in_clean_environment
6. observe_startup_boundary
7. attempt_public_runtime_dump_tooling
8. publish_only_supported_positive_findings
9. keep raw operational details private
过程证据行
| # |
过程 |
观察 |
支撑判断 |
公开边界 |
| 1 |
候选选择 |
找到最新可见加固 APK,体量约 63 MB。 |
本轮有明确输入。 |
不公开路径和文件摘要。 |
| 2 |
容器枚举 |
双 DEX、双 native、多组 assets。 |
多载体结构成立。 |
不公开清单。 |
| 3 |
JADX |
43 个 Java 文件,2 个反编译错误。 |
普通源码面未直接还原算法。 |
不公开源码。 |
| 4 |
apktool |
43 个 smali 文件,约 9.2 万行。 |
壳层、桥接、装载体量明显。 |
不公开 smali 内容。 |
| 5 |
Manifest |
release 配置、代理链、native 不普通展开。 |
保护介入更前置。 |
不公开组件名。 |
| 6 |
native |
arm64 stripped ELF。 |
native 符号线索收敛。 |
不公开 SO 名和构建标识。 |
| 7 |
assets |
最大载体约 62 MB。 |
核心材料不在普通 Java 面。 |
不公开 assets 名。 |
| 8 |
字符串面 |
常见敏感明文模式未形成有效泄露。 |
发布面更干净。 |
不公开完整输出。 |
| 9 |
安装 |
干净环境安装成功。 |
动态验证不再空白。 |
不公开设备和命令。 |
| 10 |
启动 |
入口拉起后 native 快速闭合。 |
不能写稳定启动结论。 |
不公开日志、栈和进程。 |
| 11 |
Frida |
可观察短生命周期进程。 |
有运行态窗口,但不稳定。 |
不公开应用标识。 |
| 12 |
dexdump |
公开工具未产出可用 DEX dump。 |
动态脱壳结果未形成。 |
不公开 attach/spawn 细节。 |
平台化写法说明
看雪读者通常会追问:有没有跑动态?有没有跑 dump?有没有解释失败?本稿把这三件事放在正文核心位置。安装成功只写安装成功,运行期闭合只写闭合,frida-dexdump 未产出就写未产出。这样的文字不如“全都通过”好看,但更接近专业测评记录。
结论边界
这篇平台稿只发布可验证的正向事实:普通静态反编译没有直接还原原始算法,公开 dexdump 工具没有形成可用 dump,干净安装阶段成立,运行期边界被保留。它不发布未验证结论,不把安装成功写成稳定业务通过,不把工具未产出写成绝对不可还原,也不提供任何可复现脱壳或绕过步骤。
下一步适合拆成四个专项:DEX dump anti-repair、二次打包 fail-closed、危险环境矩阵、SO 载体分析。每个专项都需要单独目标、单独证据和单独边界,不应混在同一篇里。