A security analysis tool for HarmonyOS .hap and .app application packages. Parses the HAP signing block, extracts certificate chains, validates PKCS7 signatures, verifies code-signing integrity, and presents results through both a CLI and a native macOS GUI.
- HAP / APP Package Parsing — Reads the ZIP + signing block structure of HarmonyOS application packages
- Certificate Chain Extraction — Extracts and orders PKCS7 certificate chains (leaf → intermediate → root) from multiple signing sub-blocks
- Chain Validation — Validates issuer-subject links, cryptographic signatures, certificate expiry, and chain topology
- Identity Chain Matching — Matches the developer certificate against the profile to build a complete identity chain including system trust anchors
- Provisioning Profile — Parses profile metadata: bundle name, developer ID, app identifier, permissions, APL level, validity period
- Code Signing Integrity — Detects injected
.sofiles not covered by the code-signing block, highlights missing libraries - SHA256 Hashing — Computes per-file SHA256 hashes and compares against a reference manifest to detect tampering
- Runtime Memory Integrity — Verifies loaded
.sosegments and.abcbytecode in process memory at runtime via/proc/self/mapsandlink_mapcross-reference - Nested HAP Analysis — Recursively analyzes inner
.happackages embedded in.appfiles - Bilingual UI — English / Chinese toggle
hap_parser/
├── hap_parser/ # Core parsing library (C++)
│ ├── hap_parser.h # HapParser class + 15 data structures
│ ├── hap_parser.cpp # ZIP EOCD, signing block, PKCS7, profile parsing
│ ├── hap_parser_main.cpp # CLI entry point
│ ├── byte_view.h # ByteView utility class (buffer + LE readers)
│ ├── runtime_verify.h # RuntimeVerifier class + public types
│ └── runtime_verify.cpp # ELF segment hashing, /proc/maps parsing, PANDA scanning
├── gui/ # macOS GUI (GLFW + ImGui)
│ ├── AppState.h # UI state management class
│ ├── AppState.cpp # State management implementation
│ ├── main.cpp # ImGui UI: tabs, tables, drag-drop
│ ├── HapAnalyzer.h # C API bridging parser ↔ GUI
│ ├── HapAnalyzer.cpp # C API implementation
│ ├── macos_bridge.mm # macOS file dialog / open handler
│ ├── imgui/ # Dear ImGui (vendored)
│ ├── fonts/ # UI fonts
│ └── CMakeLists.txt # Build configuration
├── tests/ # Unit tests
│ └── test_runner.cpp # 20 test cases
├── .github/workflows/ # CI/CD
│ └── build.yml # Build + Test + Release
└── README.md
- CMake ≥ 3.20
- OpenSSL (macOS:
brew install openssl) - GLFW (auto-fetched if not found)
- Xcode Command Line Tools (macOS)
cd hap_parser
clang++ -std=c++17 hap_parser.cpp hap_parser_main.cpp \
-I/opt/homebrew/opt/openssl/include \
-L/opt/homebrew/opt/openssl/lib \
-lssl -lcrypto -o hap_parsercd gui
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
open build/hap_analyzer_gui.apphap_parser <file.hap|file.app> [options]
Options:
-a, --all Show all sections
-l, --layout Show file layout diagram
-p, --profile Show provision profile details
-c, --codesign Show code signing info
-H, --hashes Show file SHA256 hashes
-i, --integrity Show integrity verification table
--expect <file> Compare hashes against JSON manifest
Default: certificate verification graph only
- Launch
hap_analyzer_gui.app - Drag a
.hapor.appfile onto the window (or use File → Open) - Browse tabs: Summary, Certificates, Structure
A JSON file mapping filenames to expected SHA256 hashes:
{
"files": {
"entry-default-unsigned-so.hap": "ab12cd34...",
"libs/arm64-v8a/libentry.so": "ef56gh78..."
}
}Load via File → Load Manifest or auto-generate from a trusted reference build via File → Set as Reference.
cd hap_parser
clang++ -std=c++17 ../tests/test_runner.cpp hap_parser.cpp runtime_verify.cpp \
-I. -I/opt/homebrew/opt/openssl/include -L/opt/homebrew/opt/openssl/lib \
-lssl -lcrypto -o test_runner && ./test_runner20 test cases covering:
| Category | Tests |
|---|---|
| Parsing | Valid HAP, large HAP, empty file, tiny file, corrupted, garbage bytes |
| Certificates | Chain validation, field completeness, identity chain |
| Profile | Field extraction, bundle name, developer ID |
| Runtime | ELF segment hashing, ABC references, /proc/maps platform compatibility |
| Utilities | ByteView boundary checks, RuntimeVerifyResult helpers |
Tests also run automatically on every push via GitHub Actions.
- Find ZIP EOCD — Locate the End of Central Directory record at the end of the file
- Locate Signing Block — The HAP signing footer sits between the last file entry and the central directory; it contains a magic string, version, block count, and total size
- Parse Sub-blocks — Each sub-block header (type, length, offset) is read from the table at the start of the signing block:
Block Type Name Content 0x20000000AppSignatureBlock Main app CMS/PKCS7 signature 0x20000001ProofOfRotationBlock Certificate rotation chain 0x20000002ProfileBlock Provisioning profile (JSON) 0x20000003PropertyBlock Code-sign metadata + reference to 0x30000001 - Extract Certificates — PKCS7 signed-data blobs are DER-decoded via OpenSSL; certificates are deduplicated and ordered from leaf to root
- Validate Chain — Issuer-Subject links, cryptographic signatures, expiry, and self-signed root checks
- Verify Code Signing — Cross-reference
.sofiles in the ZIP against the code-sign block list; flag injected or missing libraries
After parsing a HAP file, reference hashes for .so and .abc files are stored in the Summary struct. At runtime, call HapParser::verifyRuntime() to check loaded libraries against these references:
HapParser parser;
auto summary = parser.parseFile("app.hap");
auto result = HapParser::verifyRuntime(*summary);
// result.allPassed() → true if all .so segments and .abc files match- Extract ELF PT_LOAD segment hashes from ZIP entries — only
p_filesz(file-mapped portion), excluding.bss - Parse
/proc/self/mapsto locate loaded.somemory regions - Compute SHA256 of each segment in memory and compare against reference
- Store the SHA256 from the signing block as reference
- Scan process memory for
PANDAmagic bytes to locate loaded.abcfiles - Read
file_sizefrom the Panda header and compute SHA256 of the full data
- link_map cross-reference: Enumerates loaded libraries via
dl_iterate_phdr(the runtime linker's internal chain) and compares against/proc/self/maps. If a library appears inlink_mapbut is missing frommaps,/proc/self/mapsmay have been tampered with. - Graceful fallback: If
/proc/self/mapsis unavailable, falls back tolink_mapenumeration.
MIT
一款面向 HarmonyOS .hap / .app 应用包的安全分析工具。解析 HAP 签名块、提取证书链、验证 PKCS7 签名、校验代码签名完整性,提供命令行和 macOS 原生 GUI 两种使用方式。
- HAP / APP 包解析 — 读取 HarmonyOS 应用包的 ZIP + 签名块结构
- 证书链提取 — 从多个签名子块中提取并排序 PKCS7 证书链(叶子证书 → 中间证书 → 根证书)
- 链验证 — 验证颁发者-主体关联、密码学签名、证书有效期及链拓扑结构
- 身份链匹配 — 将开发者证书与配置描述文件关联,构建包含系统信任锚点的完整身份链
- 配置描述文件 — 解析 profile 元数据:包名、开发者 ID、应用标识、权限、APL 级别、有效期
- 代码签名完整性 — 检测未受代码签名块保护的注入
.so文件,高亮缺失的库文件 - SHA256 哈希 — 逐文件计算 SHA256 并对比基准清单,检测篡改
- 运行时内存校验 — 运行时通过
/proc/self/maps和link_map交叉验证已加载.so段和.abc字节码的完整性 - 嵌套 HAP 分析 — 递归分析
.app文件中内嵌的子.hap包 - 双语界面 — 支持中英文切换
hap_parser/
├── hap_parser/ # 核心解析库(C++)
│ ├── hap_parser.h # HapParser 类 + 15 个数据结构
│ ├── hap_parser.cpp # ZIP EOCD、签名块、PKCS7、profile 解析
│ ├── hap_parser_main.cpp # 命令行入口
│ ├── byte_view.h # ByteView 工具类(字节缓冲 + LE 读取)
│ ├── runtime_verify.h # RuntimeVerifier 类 + 公共类型
│ └── runtime_verify.cpp # ELF 段哈希、/proc/maps 解析、PANDA 扫描
├── gui/ # macOS 图形界面(GLFW + ImGui)
│ ├── AppState.h # UI 状态管理类
│ ├── AppState.cpp # 状态管理实现
│ ├── main.cpp # ImGui UI:标签页、表格、拖放
│ ├── HapAnalyzer.h # C API 桥接解析器 ↔ GUI
│ ├── HapAnalyzer.cpp # C API 实现
│ ├── macos_bridge.mm # macOS 文件对话框 / 打开事件
│ ├── imgui/ # Dear ImGui(本地集成)
│ ├── fonts/ # 界面字体
│ └── CMakeLists.txt # 构建配置
├── tests/ # 单元测试
│ └── test_runner.cpp # 20 个测试用例
├── .github/workflows/ # CI/CD
│ └── build.yml # 构建 + 测试 + 发布
└── README.md
- CMake ≥ 3.20
- OpenSSL(macOS:
brew install openssl) - GLFW(如未找到会自动下载)
- Xcode Command Line Tools(macOS)
cd hap_parser
clang++ -std=c++17 hap_parser.cpp hap_parser_main.cpp \
-I/opt/homebrew/opt/openssl/include \
-L/opt/homebrew/opt/openssl/lib \
-lssl -lcrypto -o hap_parsercd gui
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
open build/hap_analyzer_gui.apphap_parser <文件.hap|文件.app> [选项]
选项:
-a, --all 显示所有内容
-l, --layout 显示文件布局图
-p, --profile 显示配置描述文件详情
-c, --codesign 显示代码签名信息
-H, --hashes 显示文件 SHA256 哈希
-i, --integrity 显示完整性校验表
--expect <文件> 对照 JSON 清单比对哈希
默认:仅输出证书验证图
- 启动
hap_analyzer_gui.app - 将
.hap或.app文件拖放到窗口中(或通过 文件 → 打开) - 浏览标签页:摘要(Summary)、证书(Certificates)、文件结构(Structure)
一个 JSON 文件,记录文件名与期望的 SHA256 哈希值:
{
"files": {
"entry-default-unsigned-so.hap": "ab12cd34...",
"libs/arm64-v8a/libentry.so": "ef56gh78..."
}
}通过 文件 → 加载清单 导入,或在可信基准版本上通过 文件 → 设为基准 自动生成。
cd hap_parser
clang++ -std=c++17 ../tests/test_runner.cpp hap_parser.cpp runtime_verify.cpp \
-I. -I/opt/homebrew/opt/openssl/include -L/opt/homebrew/opt/openssl/lib \
-lssl -lcrypto -o test_runner && ./test_runner20 个测试用例,覆盖:
| 分类 | 测试 |
|---|---|
| 解析 | 正常 HAP、大型 HAP、空文件、小文件、损坏文件、随机字节 |
| 证书 | 链校验、字段完整性、身份链 |
| Profile | 字段提取、包名、开发者 ID |
| 运行时 | ELF 段哈希、ABC 引用、/proc/maps 平台兼容 |
| 工具类 | ByteView 边界检查、RuntimeVerifyResult 辅助方法 |
每次 push 代码时 GitHub Actions 自动运行全部测试。
- 定位 ZIP EOCD — 在文件末尾找到中央目录结束记录
- 定位签名块 — HAP 签名尾部位于最后一个文件条目与中央目录之间,包含魔数字符串、版本号、块数量及总大小
- 解析子块 — 从签名块头部的表中读取每个子块头(类型、长度、偏移):
块类型 名称 内容 0x20000000AppSignatureBlock 应用主签名(CMS/PKCS7) 0x20000001ProofOfRotationBlock 证书轮换链 0x20000002ProfileBlock 配置描述文件(JSON) 0x20000003PropertyBlock 代码签名元数据 + 指向 0x30000001的引用 - 提取证书 — 通过 OpenSSL DER 解码 PKCS7 签名数据,去重并从叶子到根排序
- 验证链 — 检查颁发者-主体关联、密码学签名、有效期及自签名根证书
- 校验代码签名 — 将 ZIP 中的
.so文件与代码签名清单交叉比对,标记注入或缺失的库
解析 HAP 后,.so 和 .abc 的参考哈希存储在 Summary 结构体中。运行时调用 HapParser::verifyRuntime() 即可检查内存中已加载库与参考值是否一致:
HapParser parser;
auto summary = parser.parseFile("app.hap");
auto result = HapParser::verifyRuntime(*summary);
// result.allPassed() → 所有 .so 段和 .abc 匹配则返回 true- 从 ZIP 条目提取 ELF PT_LOAD 段哈希 — 只计算
p_filesz(文件映射部分),排除.bss - 解析
/proc/self/maps定位已加载.so的内存区域 - 计算内存中各段的 SHA256 并与参考值对比
- 从签名块中获取 SHA256 作为参考值
- 扫描进程内存中的
PANDA魔数定位已加载的.abc文件 - 从 Panda 头部读取
file_size,计算全文 SHA256 并对比
- link_map 交叉验证: 通过
dl_iterate_phdr(运行时链接器的内部链表)枚举已加载库,与/proc/self/maps对比。若link_map中有而maps中无,说明maps可能被篡改。 - 优雅降级: 若
/proc/self/maps不可用,自动回退到link_map枚举。
MIT