首页
社区
课程
招聘
[原创]把 OpenHarmony 逆向工程接入 LLM
发表于: 2026-6-17 10:36 839

[原创]把 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)
  • 交叉引用索引:借鉴 jadxUsageInfo 设计思想,一次扫描全库建立反向索引
  • 优化 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 的引用)+ 字段名兜底索引
  • 类实例化:索引 NewClassNewInst,后者采用方法级寄存器回溯启发式
  • instanceof 检查:索引 InstOf 指令
  • 类层次结构:双向索引父类、接口、子类、实现者,并支持导入别名规范化

四、优化 Pass:让输出更干净

Pass 功能 典型效果
AlgebraicSimplificationPass 常量折叠、恒等变换 2 + 35x * 1x
CopyPropagationPass 拷贝传播 a = b; c = ac = b
DeadCodeEliminationPass 死代码消除 移除无使用赋值
AccCopyPropagationPass ACC 拷贝传播 _acc_ = x; y = _acc_y = x
ExpressionPropagationPass 表达式传播 合并简单表达式链
RedundantLoadEliminationPass 重复字段读取消除 支持写入/调用副作用失效
ArraySpreadMergingPass 数组展开合并 [] + starrayspread[...x]

特别值得一提的是 常量折叠 的实现:不仅支持二元数值运算,还覆盖了 BigInt 运算、位运算、字符串拼接、IsTrue/IsFalseTypeOfToNumber/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.notifyconcurrentresultcallruntime.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 直接调用。

接下来的重点方向:

  1. 超大函数的 LLM 友好输出:当前超大方法只提供摘要,不输出完整代码。如何让数万行指令的方法也能以 LLM 有效消化的方式呈现——比如按功能块切分、生成提纲式摘要、或支持按需逐级展开——是下一步最核心的探索方向。
  2. 逻辑短路识别:实现 && / || 语法恢复
  3. 常量分支剪枝:清理 async 状态机的死分支
  4. 词法环境优化恢复:在闭包作用域内重新启用 ACC 拷贝传播
  5. callruntime 系列:实现并发运行时调用指令
  6. 真实调试样本:寻找携带 DebugInfo.params 的 ABC 以验证参数名还原

如果你也在做 OpenHarmony 逆向、安全分析或 MCP 工具开发,欢迎交流。项目代码完全开源,构建命令只需 ./gradlew :modules:mcp:fatJar 即可获得可运行的 fat jar。


本文基于 ABCDE MCP 项目截至 2026 年 6 月的实现状态撰写。项目持续迭代中,部分细节可能随版本更新而变化。


[内核课程]《Windows内核攻防实战》!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。

收藏
免费 3
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回