-
-
[原创]把 OpenHarmony 逆向工程接入 LLM
-
发表于: 2026-6-17 10:36 839
-
把 OpenHarmony 逆向工程接入 LLM:ABCDE MCP 的设计与实现
项目连接: 5d4K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6#2x3K6y4H3K9#2)9J5c8X3q4T1j5#2)9J5k6r3#2U0M7l9`.`.
一、为什么要做这件事
OpenHarmony / HarmonyOS 应用采用 ArkCompiler 编译为方舟字节码(ABC,Ark ByteCode),打包为 .hap 格式。对于安全研究人员、逆向工程师和开发者来说,分析这些二进制文件需要理解 ABC 指令集(pandaASM)、控制流结构、类布局、资源索引等复杂知识,门槛较高。如果能让 LLM 直接读取 ABC 文件——就像读取文本文件一样——将大幅降低鸿蒙应用分析的门槛。这正是 ABCDE MCP 项目的初衷:在 ABCDE 核心库之上实现一个完整的结构化反编译器,打通从 pandaASM 到可读 JavaScript/TypeScript 的通路,并在此基础上封装一层 MCP 协议 服务,把反编译能力转化为 LLM 可调用的工具集。
二、项目架构一览
项目采用 Kotlin Multiplatform 构建,核心模块分工如下:
| 模块 | 职责 |
|---|---|
modules/common |
通用基础类型(LEByteBuf 等) |
modules/abcde |
ABC 字节码解析、反汇编、反编译、交叉引用索引 |
modules/resde |
资源索引解析(新旧版 resources.index 兼容) |
modules/hapde |
HAP 包解析(.hap 自动提取 ABC) |
modules/mcp |
MCP 服务协议层,暴露 19 个 Tools |
反编译器内部架构借鉴了多个成熟开源项目的经验:
- 结构化分析:移植自 xpanda的 Cooper-Harvey-Kennedy 支配树算法、循环检测、控制流规约(Structuring by Reduction)
- 交叉引用索引:借鉴 jadx 的
UsageInfo设计思想,一次扫描全库建立反向索引 - 优化 Pass:采用 LLVM 风格的 Pass 架构,包括代数化简、拷贝传播、死代码消除、重复字段读取消除等
三、核心特色:LLM 能做什么
3.1 结构化反编译,而非线性翻译
很多反编译工具只做"指令到语句"的线性翻译,输出类似汇编的伪代码。我们实现了完整的 控制流结构化分析(Control Flow Structuring):
CFG 基本块 → 支配树 → 循环检测 → 控制流规约 → if / while / do-while / for-of / for-in
基于 StructureAnalysis 的规约算法,能够将任意 reducible CFG 逐步折叠为高级控制流结构。对于不可规约图(irreducible graph),通过尾节点复制(tail region duplication)进行修复,并设置了复制膨胀上限(maxTailDuplicationFactor = 100),避免输出指数级爆炸。
3.2 19 个 MCP Tools,覆盖逆向分析全链路
| 类别 | 工具 | 说明 |
|---|---|---|
| ABC 基础 | open_abc / list_classes / get_class_detail |
打开文件、列类、看详情 |
| 反编译 | decompile_class / decompile_method / reconstruct_class |
反编译类/方法,重组 ArkTS class 语法 |
| 反汇编 | disassemble_method / search_in_method |
字节码级别搜索,不受输出上限限制 |
| 交叉引用 | get_xrefs_to_method / get_xrefs_to_field / get_xrefs_to_class / get_class_hierarchy |
谁调用了谁、谁 new 了谁、继承关系 |
| 字符串 | search_strings |
正则搜索字符串常量 |
| HAP 包 | open_hap / get_hap_manifest / get_obfuscation_map |
自动提取 ABC、解析 module.json、获取混淆映射 |
| 资源 | search_resources / resolve_resource |
搜索资源、解析 $string:app_name 等引用 |
3.3 Class 语法重组:从 func_main_0 还原 class Foo extends Bar
ArkCompiler 将 .ets 源文件编译为模块入口函数 func_main_0,类定义被展开为 defineclasswithbuffer + definemethod + Object.defineProperty 的指令序列。我们实现了 ClassReconstructionPass,在 func_main_0 中识别这些模式并重组为:
class SimpleBrowser extends AtkTsGlobal.ViewPU {
constructor() { ... }
get url() { ... }
set url(v) { ... }
aboutToAppear() { ... }
}
通过构造器去重避免 finalizeConstruction 等编译器生成路径导致的重复输出。父类解析同时支持从 ABC 类头读取和从 NewClass 指令的 parent 寄存器回溯两种策略。
3.4 词法环境变量名还原:让闭包可读
ArkCompiler 对闭包使用 ldlexvar / stlexvar 访问词法环境变量。这些指令只有 (lvl, slot) 编号,没有名字。我们解析 newlexenvwithname 引用的 literal array,建立词法环境栈快照,将 __lex{lvl}_{slot}__ 替换为真实变量名。
实测在 HAP 样本中,1180 个含 newlexenvwithname 的方法里有 974 个成功还原了变量名。
3.5 Async/Await 与迭代器语法降级
- Async/Await:映射
asyncfunctionenter/asyncfunctionawaituncaught等指令,输出async function ...和await表达式 - 迭代器:映射
getiterator/getnextpropname/closeiterator等指令,预扫描iterator init + while模式,降级为for (const x of iterable)/for (const k in obj)
3.6 数组展开:[...src] 而不是 arr.push(...src)
实现 starrayspread 指令的 IR 映射和 ArraySpreadMergingPass,将典型模式:
_acc_ = [];
v0 = _acc_;
_acc_ = src;
starrayspread v0, idx
合并为 v0 = [...src]。当无法追踪数组字面量或源表达式含副作用时,优雅降级为 arr.push(...src)。
3.7 超大方法:当前只提供摘要,完整输出仍需探索
反编译输出设置了 10 MB 字符预算和 100 行展示上限。当方法过大时,目前不会输出完整代码,而是返回截断提示 + MethodSummary(包含指令数、基本块数、字符串常量 Top 20、调用方法 Top 20 等)。这让 LLM 能"知道"这个方法在做什么,但无法"阅读"其完整逻辑。
这是一个务实的妥协:超大方法(动辄数万条指令)的完整反编译输出会立刻耗尽上下文窗口,对 LLM 毫无帮助。但如何让超大函数也能以 LLM 友好的方式呈现——比如按功能块切分、生成提纲式摘要、或支持按需逐级展开——仍是下一步需要重点思考的方向。
3.8 交叉引用索引:一次扫描,全库可查
借鉴 jadx 的 UsageInfo 设计:
- 方法调用:精确索引到
AbcMethod - 字段读写:类内启发式索引(在类 C 的方法内访问字段 f,视为 C.f 的引用)+ 字段名兜底索引
- 类实例化:索引
NewClass和NewInst,后者采用方法级寄存器回溯启发式 - instanceof 检查:索引
InstOf指令 - 类层次结构:双向索引父类、接口、子类、实现者,并支持导入别名规范化
四、优化 Pass:让输出更干净
| Pass | 功能 | 典型效果 |
|---|---|---|
AlgebraicSimplificationPass |
常量折叠、恒等变换 | 2 + 3 → 5,x * 1 → x |
CopyPropagationPass |
拷贝传播 | a = b; c = a → c = b |
DeadCodeEliminationPass |
死代码消除 | 移除无使用赋值 |
AccCopyPropagationPass |
ACC 拷贝传播 | _acc_ = x; y = _acc_ → y = x |
ExpressionPropagationPass |
表达式传播 | 合并简单表达式链 |
RedundantLoadEliminationPass |
重复字段读取消除 | 支持写入/调用副作用失效 |
ArraySpreadMergingPass |
数组展开合并 | [] + starrayspread → [...x] |
特别值得一提的是 常量折叠 的实现:不仅支持二元数值运算,还覆盖了 BigInt 运算、位运算、字符串拼接、IsTrue/IsFalse、TypeOf、ToNumber/ToNumeric 等。
五、已知缺陷与局限性(诚实告知)
我们不想只展示光鲜的一面,以下是目前已知的、在代码中明确标注的缺陷:
5.1 参数名还原尚未在真实数据中验证
MethodItem.argsStr() 优先读取 DebugInfo.params,发行版 HAP 通常已剥离调试参数名,当前样本中 DebugInfo.params 均为空。我们仍需找到携带调试信息的 ABC 来验证 arg0 → 真实参数名的还原逻辑。
5.2 逻辑短路(&& / ||)识别未实现
StructureAnalysis.simplifyConditionalSequences() 中标注了 TODO:需要识别 condition -> condition (same dest) 和 condition -> condition (dest = next) 两种模式来生成 && / ||。当前代码中条件判断仍输出为完整 if 语句。
5.3 Async 状态机的乐观假设
getresumemode 被固定翻译为常量 0,这会让正常 resume 分支保留,异常/完成 resume 分支虽然被 throw 终止点剪枝,但仍可能在输出中留下形式可达的死分支(如 _acc_ = false; if (!(_acc_ == 0)) { throw ... })。后续需要添加常量分支剪枝 Pass 进一步清理。
5.4 词法环境块内的优化受限
含 newlexenv* / poplexenv 的基本块当前禁用了完整 IR 优化,导致输出保留 _acc_ = arg0; menuItems = _acc_; 这类 ACC 中转语法。需要让优化器在传播表达式时 aware 到作用域边界,同时修复 .length 链式膨胀等错误表达式。
5.5 未实现指令:callruntime.* 系列
callruntime.notifyconcurrentresult、callruntime.supercallforwardallargs 等并发任务相关运行时调用尚未实现。这是当前 HAP 样本中主要剩余的未实现指令类别。
5.6 发行版 HAP 的调试信息缺失
如前所述,发行版 HAP 通常已剥离 DebugInfo.params,参数名、行号映射等调试信息不可用。反编译输出在方法名、变量名上仍依赖编译器生成的命名规则。
六、开源致谢
本项目站在多个优秀开源项目的肩膀上:
- Yricky/ABCDE:项目的基石,提供了完整的 ABC 字节码解析框架和反汇编能力。没有 Yricky 的工作,本项目无从谈起。
- xpanda:已停止维护的鸿蒙反编译器(Java 实现)。我们移植了其核心的 DominatorGraph(支配树)、LoopFinder(循环检测)、StructureAnalysis(控制流规约)和 RegionGraph(轻量级有向图)算法。这些组件是结构化反编译的核心。
- jadx:Android 领域的优秀反编译器。本项目的交叉引用(XRef)索引与缓存架构直接借鉴了 jadx 的
UsageInfo/UsageInfoVisitor/IUsageInfoCache设计思想:扫描一次建立反向索引,通过缓存接口抽象内存/持久化存储,引用结果按类/方法/字段分类。底层字节码解析逻辑按 ABC 格式重新实现,未直接复用 jadx 代码。 - LLVM:优化器采用 LLVM 风格的 Pass 架构,每个 Pass 独立运行、可组合、可扩展。
七、结语与下一步
ABCDE MCP 的目标不是做一个完美的反编译器——那是需要数年工程积累的目标——而是做一个能让 LLM 有效工作的、务实的逆向分析工具。当前已经实现的结构化反编译、交叉引用索引、类重组等功能,已经在真实 HAP 样本(如 Kazumi、Melotopia)上验证了可用性。我们真正打通的是从 pandaASM 到可读 JS 的通路,而 MCP 只是让这条通路可以被 LLM 直接调用。
接下来的重点方向:
- 超大函数的 LLM 友好输出:当前超大方法只提供摘要,不输出完整代码。如何让数万行指令的方法也能以 LLM 有效消化的方式呈现——比如按功能块切分、生成提纲式摘要、或支持按需逐级展开——是下一步最核心的探索方向。
- 逻辑短路识别:实现
&&/||语法恢复 - 常量分支剪枝:清理 async 状态机的死分支
- 词法环境优化恢复:在闭包作用域内重新启用 ACC 拷贝传播
- callruntime 系列:实现并发运行时调用指令
- 真实调试样本:寻找携带
DebugInfo.params的 ABC 以验证参数名还原
如果你也在做 OpenHarmony 逆向、安全分析或 MCP 工具开发,欢迎交流。项目代码完全开源,构建命令只需 ./gradlew :modules:mcp:fatJar 即可获得可运行的 fat jar。
本文基于 ABCDE MCP 项目截至 2026 年 6 月的实现状态撰写。项目持续迭代中,部分细节可能随版本更新而变化。
[内核课程]《Windows内核攻防实战》!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。