Skip to content

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 不需要混淆的类

类别是否混淆原因
数据模型/DTOJSON 反序列化需要类名/字段名
ArkUI 组件ArkUI 编译器依赖类名
Ability 入口系统反射调用
第三方 SDK可能被自身逻辑反射调用
枚举类valueOf() 需要枚举名
接口接口名需要稳定
注解处理器编译器需要

5. ArkTS 字节码混淆原理

5.1 混淆流程

ArkTS 源码

编译为 ArkTS 字节码(.bc)

字节码解析器

AST(抽象语法树)

混淆器(Obfuscator)
    ├─ 类名替换
    ├─ 方法名替换
    ├─ 字段名替换
    ├─ 控制流改写
    ├─ 字符串加密
    └─ 常量池压缩

混淆后的字节码

打包为 .app

代码签名

上架 AppGallery

5.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 对比

概念AndroidHarmonyOS
混淆工具ProGuard / R8ArkCompiler Obfuscator
配置文件proguard-rules.proobfuscation-rules.pro
配置位置build.gradlebuild-profile.json5
映射文件mapping.txtobfuscation-map.json
资源压缩shrinkResources trueresourceShrink
最小化minifyEnabled trueobfuscation.minifyOptions