Appearance
10_Engineering/06 - 代码混淆
1. 代码混淆概述
代码混淆(Obfuscation)是通过将代码转换为功能等价但难以读懂的形式,来保护 ArkTS 源代码不被反编译破解的安全措施。HarmonyOS 使用基于 ArkCompiler 的字节码混淆机制。
1.1 混淆类型
| 类型 | 说明 | 效果 |
|---|---|---|
| 类名混淆 | 将类名替换为短名(a, b, c...) | 提高逆向难度 |
| 方法名混淆 | 将方法名替换为短名 | 隐藏业务逻辑 |
| 字段名混淆 | 将字段名替换为短名 | 隐藏数据结构 |
| 控制流混淆 | 将控制流改写为等价但复杂的结构 | 增加逻辑分析难度 |
| 字符串加密 | 将字符串字面量加密,运行时解密 | 防止敏感信息泄露 |
| 常量池压缩 | 合并重复的常量 | 减小代码体积 |
| 无用代码删除 | 移除未使用的代码 | 减小代码体积 |
2. build-profile.json5 配置
2.1 基础混淆配置
json5
// entry/build-profile.json5
{
"buildOptionSet": [
{
"name": "release",
"arkOptions": {
"sourceMap": false,
"obfuscation": {
"ruleOptions": {
"enable": true,
"includes": ["**/*.ets"], // 需要混淆的文件
"excludes": ["**/model/*.ets"] // 需要排除的文件
},
"minifyOptions": {
"enable": true,
"removeUnused": true, // 移除未使用代码
"collapse": true, // 类名压缩
"stringOptimization": true // 字符串优化
},
"resourceShrink": {
"enable": true, // 资源压缩
"keepOriginal": false // 是否保留原始资源
}
}
}
}
]
}2.2 高级混淆配置
json5
{
"buildOptionSet": [
{
"name": "release",
"arkOptions": {
"obfuscation": {
"ruleOptions": {
"enable": true,
"includes": ["**/*.ets"],
"excludes": [
"**/model/*.ets", // 数据模型不混淆(反序列化需要)
"**/api/*.ets", // API 定义不混淆
"**/config/*.ets" // 配置类不混淆
],
"proguardFiles": [
"obfuscation-rules.pro" // 自定义混淆规则文件
]
},
"minifyOptions": {
"enable": true,
"removeUnused": true,
"collapse": true,
"stringOptimization": true,
"controlFlow": true // 控制流混淆
}
}
}
}
]
}3. 自定义混淆规则文件
3.1 obfuscation-rules.pro
proguard
# ========================================
# HarmonyOS ArkTS 混淆规则
# ========================================
# ---------- 不混淆的类 ----------
# 保持 EntryAbility 不被混淆
-keep class com.example.myapp.entryability.** { *; }
# 保持 EntryAbility 不被混淆
-keep class com.example.myapp.EntryAbility { *; }
# ---------- 数据模型不混淆 ----------
# 用于 JSON 序列化/反序列化的模型不混淆
-keep class com.example.myapp.model.** { *; }
-keep class com.example.myapp.dto.** { *; }
# ---------- 第三方库不混淆 ----------
-keep class com.alibaba.fastjson.** { *; }
-keep class io.github.yangzongkai.** { *; }
# ---------- 反射相关的类和方法 ----------
# 如果被通过反射调用,必须保持
-keepclassmembers class ** {
@ohos.annotation.*** <methods>;
}
# ---------- 保持序列化接口 ----------
-keep interface com.example.myapp.serialization.** { *; }
# ---------- 保持枚举 ----------
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# ---------- 保持 ArkUI 组件 ----------
-keep class com.example.myapp.components.** { *; }
# ---------- 资源 ID 不混淆 ----------
-keep class **.resource { public static final *; }4. 混淆策略详解
4.1 需要混淆的类
| 类别 | 是否混淆 | 原因 |
|---|---|---|
| 业务逻辑类 | ✅ | 防止核心算法被逆向 |
| 网络请求层 | ✅ | 防止 API 签名逻辑泄露 |
| 加密工具 | ✅ | 防止加密密钥/算法泄露 |
| 登录鉴权 | ✅ | 防止认证逻辑被绕过 |
| 支付逻辑 | ✅ | 防止支付流程被篡改 |
4.2 不需要混淆的类
| 类别 | 是否混淆 | 原因 |
|---|---|---|
| 数据模型/DTO | ❌ | JSON 反序列化需要类名/字段名 |
| ArkUI 组件 | ❌ | ArkUI 编译器依赖类名 |
| Ability 入口 | ❌ | 系统反射调用 |
| 第三方 SDK | ❌ | 可能被自身逻辑反射调用 |
| 枚举类 | ❌ | valueOf() 需要枚举名 |
| 接口 | ❌ | 接口名需要稳定 |
| 注解处理器 | ❌ | 编译器需要 |
5. ArkTS 字节码混淆原理
5.1 混淆流程
ArkTS 源码
↓
编译为 ArkTS 字节码(.bc)
↓
字节码解析器
↓
AST(抽象语法树)
↓
混淆器(Obfuscator)
├─ 类名替换
├─ 方法名替换
├─ 字段名替换
├─ 控制流改写
├─ 字符串加密
└─ 常量池压缩
↓
混淆后的字节码
↓
打包为 .app
↓
代码签名
↓
上架 AppGallery5.2 混淆前后对比
typescript
// 混淆前
export class UserService {
private userId: string;
private token: string;
public async fetchUserProfile(userId: string): Promise<UserProfile> {
const response = await this.request('/api/user/profile', { userId });
return response.data as UserProfile;
}
private async request(url: string, params: object): Promise<ApiResponse> {
// 实际请求
}
}
// 混淆后(反编译视角)
export class a {
private a: string;
private b: string;
public async c(a: string): Promise<b> {
const d = await this.e('/api/user/profile', { a });
return d.data as b;
}
private async e(a: string, c: object): Promise<d> {
// ...
}
}6. 混淆常见问题
6.1 常见问题排查
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 反序列化失败 | 模型类被混淆 | 添加 -keep 规则 |
| 反射调用崩溃 | 方法名被混淆 | 检查 -keep 规则 |
| 反射找不到类 | 类名被混淆 | 检查 -keep 规则 |
| 包体积未减小 | 混淆未生效 | 检查 build-profile.json5 |
| 功能异常 | 混淆改变了行为 | 关闭控制流混淆,检查规则 |
6.2 混淆规则调试
json5
// 调试阶段:生成混淆映射文件
{
"buildOptionSet": [
{
"name": "release",
"arkOptions": {
"obfuscation": {
"ruleOptions": {
"enable": true,
"mapFile": "obfuscation-map.json" // 生成混淆映射
}
}
}
}
]
}7. 面试高频考点
Q1: 代码混淆的基本原理?
回答要点:
- 在字节码级别改写类名、方法名、字段名
- 控制流混淆改写程序执行路径
- 字符串加密防止敏感信息泄露
- ArkTS 编译为字节码后进行混淆,而非源码层面
Q2: 哪些类不应该被混淆?
回答要点:
- 数据模型类(JSON 序列化/反序列化需要类名和字段名)
- Ability 入口类(系统反射调用)
- ArkUI 组件类(ArkUI 编译器依赖)
- 枚举类(valueOf 需要枚举名)
- 第三方 SDK
- 通过反射调用的类/方法
Q3: 混淆后如何调试?
回答要点:
- 生成混淆映射文件(mapFile)
- 使用 mapFile 还原堆栈
- 先关闭混淆定位问题,再逐步恢复
- 测试环境中使用 debug 版本对比
8. Android 对比
| 概念 | Android | HarmonyOS |
|---|---|---|
| 混淆工具 | ProGuard / R8 | ArkCompiler Obfuscator |
| 配置文件 | proguard-rules.pro | obfuscation-rules.pro |
| 配置位置 | build.gradle | build-profile.json5 |
| 映射文件 | mapping.txt | obfuscation-map.json |
| 资源压缩 | shrinkResources true | resourceShrink |
| 最小化 | minifyEnabled true | obfuscation.minifyOptions |