目录

ArkGuard混淆开启指南

本指南旨在帮助开发者了解和使用ArkGuard源码混淆功能,保护应用代码安全。通过启用源码混淆,开发者可以对ArkTS代码中的变量名、属性名、文件名等进行混淆处理,增加代码逆向难度,提升应用的安全性。文章将详细介绍如何在DevEco Studio中开启混淆、配置混淆规则、适配不同混淆场景,以及如何查看混淆效果和还原混淆后的报错堆栈。

开启源码混淆步骤

系统已集成源码混淆功能,开发者可通过以下方式在DevEco Studio中启用。

[h2]混淆配置文件

下表简要总结了三种混淆配置文件的差异:

配置文件(示例) 配置类型 是否可修改配置 是否影响本模块的混淆 是否影响其他模块的混淆
obfuscation-rules.txt 自定义 是 是 否
consumer-rules.txt 自定义 是 否 是
obfuscation.txt 编译产物 不涉及,构建HAR或HSP时自动生成。 不涉及 是

[h2]配置混淆选项

  1. 开启-enable-toplevel-obfuscation选项时,如果代码中使用globalThis访问全局变量,可能会导致访问失败。此时,需要使用-keep-global-name选项来保留该全局变量的名称。
  2. 待上述选项应用适配成功后,开启-enable-property-obfuscation选项。此选项开启后,以下场景需要适配:
    1. 若代码中存在静态定义、动态访问的情况,或动态定义、静态访问的情况,需要使用-keep-property-name保留属性名称。

      // 静态定义,动态访问:属性名在对象定义时是静态的,但访问时通过动态构建属性名(通常使用字符串拼接)来访问
      // ArkGuardAbility.ts
      const obj001 = {
        staticName: 'value'  // 静态定义属性
      };
      const fieldName = 'static' + 'Name';  // 动态构建属性名,需使用-keep-property-name staticName来保留该属性名
      console.info(obj001[fieldName]);  // 使用方括号语法动态访问属性
      // 动态定义,静态访问:属性名通过动态表达式在对象定义时确定,但访问时直接使用点语法(假设开发者知道属性名的结果)
      // ArkGuardAbility.ts
      const dynamicExpression = 'dynamicPropertyName';
      const obj002 = {
        [dynamicExpression]: 'value'  // 动态定义属性
      };
      console.info(obj002.dynamicPropertyName);// 使用点语法静态访问属性,需使用-keep-property-name dynamicPropertyName来保留该属性名
    2. 若代码中使用点语法访问未在ArkTS/TS/JS代码中定义的字段,比如访问native实现的so库,字段固定的json文件与数据库等场景:

      1. 若在代码中引用so库的API,如import testNapi from 'library.so';testNapi.foo();需要使用-keep-property-name foo 来保留属性名称。
      2. 若在代码中使用json文件中的字段,需要使用-keep-property-name保留json文件中的字段名称。
      3. 若在代码中使用数据库相关的字段,需要使用-keep-property-name保留数据库中的字段名称。
    3. 若构建HAR模块并发布给其他模块使用的场景,要在HAR模块中的consumer-rules.txt文件中将不能被二次混淆的属性使用-keep-property-name保留。consumer-rules.txt文件在构建HAR时会生成obfuscation.txt文件。此HAR被其它模块依赖时,DevEco Studio会解析obfuscation.txt文件,读取文件中的白名单。

    4. 验证应用功能,排查遗漏的场景。若应用出现功能异常,依据混淆后的报错栈从对应的中间产物中找到报错行的代码,排查需要配置的白名单并使用-keep-property-name进行保留。

  3. 待上述选项应用适配成功后,开启-enable-export-obfuscation选项。此选项开启后,以下场景需要适配:
    1. 若构建HSP模块,它会提供接口及其属性给其它模块调用,因此需要将对外接口使用-keep-global-name来保留、将对外暴露的class/interface等语法中的属性使用-keep-property-name保留。
    2. 若构建HAR模块并发布给其他模块使用的场景,要在HAR模块中的obfuscation-rules.txt文件中将对外接口使用-keep-global-name来保留,将对外暴露的class/interface等语法中的属性使用-keep-property-name保留。
    3. 若在代码中引用so库的API,如import { napiA } from 'library.so';需要使用-keep-global-name napiA保留so接口名称。
    4. 验证应用功能以及模块被依赖时的接口调用功能,排查遗漏的场景。若应用出现功能异常,依据混淆后的报错栈从对应的中间产物中找到报错行的代码,排查需要配置的白名单并进行保留。
  4. 待上述选项应用适配成功后,开启-enable-filename-obfuscation选项。此选项开启后,以下场景需要适配:
    1. 若代码中有动态import语句,如const path = './filePath'; import(path),会出现文件引用失败的情况,需要使用-keep-file-name filePath来保留这个文件名。
    2. 若应用中有描述路由表信息的routerMap配置,其中的pageSourceFile字段标记页面在模块的路径,需要使用-keep-file-name来保留这个路径。
    3. 若代码中有传入ohmUrl进行页面跳转,如router.pushUrl({url: '@bundle:com.example.routerPage/Library/Index'}),需要使用-keep-file-name来保留该路径中的文件名部分(如Library和Index)。
    4. 验证应用功能,排查遗漏的场景。若应用出现功能异常,且报错栈中的路径为混淆后的路径,可以在模块中的build/default/[...]/release/obfuscation/nameCache.json文件中查询到原始路径,进而找到源码文件。另外,插件hstack支持自动还原混淆后的报错堆栈。在定位到需要保留的路径后,使用-keep-file-name来保留此路径。

[h2]注意事项

查看混淆效果

混淆结束后,会在编译产物的build目录中生成混淆后的中间产物。开发者可以查看这些中间产物以确认混淆效果。同时,该目录中还会生成名称映射表和系统API白名单文件。

报错栈还原

经过混淆的应用程序,代码名称会更改,导致crash时打印的报错栈难以理解。可使用DevEco Studio命令工具Command Line Tools中的hstack插件还原源码堆栈,进而分析问题。

请备份应用编译过程中生成的sourceMaps.map文件和混淆名称映射文件nameCache.json,反混淆工具需要这些文件。

如果使用自建在线平台或流水线构建应用,则会获取不到编译过程中生成的sourceMaps.map文件和混淆名称映射文件nameCache.json,可以使用本地编译生成的对应文件进行代替。

源代码映射信息文件:sourceMaps.map,该文件记录了压缩/转换后的代码到原始源代码之间的映射关系。