DEV Community

HarmonyOS
HarmonyOS

Posted on

ArkGuard Source Code Obfuscation

Read the original article:ArkGuard Source Code Obfuscation

Context

Source code obfuscation is a critical security feature for protecting intellectual property and making reverse engineering more difficult in HarmonyOS applications. The ArkGuard obfuscation tool is integrated into the DevEco Studio development environment and provides comprehensive code protection capabilities.

The source code obfuscation feature is integrated into the system and can be enabled in DevEco Studio. Source code obfuscation is supported only for release builds, not for debug builds. This means that obfuscation will only be applied when a module is compiled in release mode, not in debug mode.

Obfuscation transforms readable code elements such as variable names, function names, property names, and file names into shorter, meaningless identifiers while preserving the original functionality. This process helps:

  • Protect proprietary algorithms and business logic from competitors
  • Reduce application size by shortening identifier names
  • Make debugging and reverse engineering significantly more difficult
  • Maintain runtime performance while enhancing security

The differences between release and debug builds extend beyond obfuscation. To determine whether application behavior differences are due to obfuscation, you should enable or disable the obfuscation switch rather than simply switching between release and debug builds.

Description

Enabling Source Code Obfuscation

Enabling the Obfuscation Switch

To enable obfuscation, set the enable field to true under arkOptions.obfuscation.ruleOptions in the build-profile.json5 file of your module:

"arkOptions": {
  "obfuscation": {
    "ruleOptions": {
      "enable": true,
      "files": ["./obfuscation-rules.txt"],
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
Configuring Obfuscation Rules

Enabling the obfuscation switch activates the default settings, which include obfuscation of local variables and parameters. To enable additional obfuscation features, customize the obfuscation-rules.txt file specified in the files field. Note that the default values in this file may vary across different versions of DevEco Studio.

For example, in DevEco Studio of version 5.0.3.600 and later, the obfuscation configuration file is as follows, which indicates that property name obfuscation, top-level scope name obfuscation, file name obfuscation, and imported/exported name obfuscation are enabled:

-enable-property-obfuscation
-enable-toplevel-obfuscation
-enable-filename-obfuscation
-enable-export-obfuscation
Enter fullscreen mode Exit fullscreen mode

You can also add comments to the obfuscation rule file by prefixing lines with the # symbol. For example:

# options:
-enable-property-obfuscation
-enable-toplevel-obfuscation
-enable-filename-obfuscation
# -enable-export-obfuscation
-keep-property-name # white list for dynamic property names
Enter fullscreen mode Exit fullscreen mode

Obfuscation Configuration Files

obfuscation-rules.txt

For HAP, HAR, and HSP modules, the arkOptions.obfuscation.ruleOptions.files field in the build-profile.json5 file specifies obfuscation rules applied during module compilation. A default obfuscation rule file, named obfuscation-rules.txt, is created when a new project is set up.

consumer-rules.txt

For HAR and HSP modules, an additional arkOptions.obfuscation.consumerFiles field is available in the build-profile.json5 file. This field specifies obfuscation rules that should be applied when this package is depended upon in the current compilation process. A default consumer-rules.txt file is created when a new HAR or HSP module is set up.

The key difference between consumer-rules.txt and obfuscation-rules.txt is as follows: obfuscation-rules.txt applies to the compilation of the current module, whereas consumer-rules.txt applies to the compilation of other modules that depend on the current module.

Configuration example of the build-profile.json5 file:

"arkOptions": {
  "obfuscation": {
    "ruleOptions": {
      "enable": true, // Enable the obfuscation switch.
      "files": ["./obfuscation-rules.txt"] // Specify the obfuscation rule file, which takes effect when the current module is compiled.
    }
    "consumerFiles": ["./consumer-rules.txt"] // Specify the obfuscation rule file, which takes effect when other modules that depend on the current module are compiled.
  }
}
Enter fullscreen mode Exit fullscreen mode

Important Note: If obfuscation options are configured in the consumer-rules.txt file, the main module may be affected. Therefore, you are advised to configure only retention options in this file.

obfuscation.txt

Unlike the above two files, obfuscation.txt is automatically generated based on consumer-rules.txt and the obfuscation rules of dependent modules during HAR or HSP compilation. It exists as a compilation product within the released HAR or HSP package. When other applications depend on this package, the obfuscation rules are merged and applied to the current compilation process.

Note: For third-party libraries, the obfuscation.txt file takes effect only when the third-party library is depended upon in the oh-package.json5 file of the module. If the dependency is specified in the oh-package.json5 file of the project, the obfuscation.txt file of the third-party library does not take effect.

Solution / Approach

1. Implementing Top-Level Scope Obfuscation

When -enable-toplevel-obfuscation is configured, access to global variables using globalThis fails. To rectify the fault, use -keep-global-name to retain the global variable name:

-enable-toplevel-obfuscation
-keep-global-name myGlobalVariable
-keep-global-name anotherGlobalVar
Enter fullscreen mode Exit fullscreen mode

2. Property Name Obfuscation Configuration

After the preceding adaptation is successful, configure -enable-property-obfuscation, and perform adaptation in the following scenarios:

Static Definition, Dynamic Access

If the code statically defines properties but dynamically accesses them (or vice versa), use -keep-property-name to retain the property names:

// Static definition, dynamic access: The property name is static during object definition, but is accessed by dynamically constructing the property name (usually using string concatenation).
const obj = {
  staticName: value  // Static definition
};
const fieldName = 'static' + 'Name';  // Dynamic construction of the property name
console.log(obj[fieldName]);  // Use square bracket notation to dynamically access the property.
-enable-property-obfuscation
-keep-property-name staticName
Enter fullscreen mode Exit fullscreen mode
Dynamic Definition, Static Access
// Dynamic definition, static access: The property name is determined during object definition through a dynamic expression, but is statically accessed using dot notation (assuming that you know the result of the property name).
const obj = {
  [dynamicExpression]: value  // Dynamic definition
};
console.log(obj.dynamicPropertyName);  // Use dot notation to statically access the property.
-keep-property-name dynamicPropertyName
Enter fullscreen mode Exit fullscreen mode
External Library and Data Integration

If the code uses dot notation to access fields not defined in ArkTS/TS/JS code:

  • For API calls to so libraries(for example, import testNapi from 'library.so'; testNapi.foo();), use -keep-property-name to retain the property name foo:
  -keep-property-name foo
Enter fullscreen mode Exit fullscreen mode
  • For fields in JSON files, use -keep-property-name to retain the JSON field names:
  -keep-property-name jsonFieldName
  -keep-property-name anotherJsonField
Enter fullscreen mode Exit fullscreen mode
  • For database-related fields, use -keep-property-name to retain the database field names:
  -keep-property-name databaseColumn
  -keep-property-name userId
Enter fullscreen mode Exit fullscreen mode
HAR Module Configuration

When building a HAR module for use by other modules, use -keep-property-name in the consumer-rules.txt file of the HAR module to retain properties that should not be re-obfuscated. The consumer-rules.txt file generates the obfuscation.txt file during HAR compilation. When the HAR module is depended upon by other modules, DevEco Studio parses the obfuscation.txt file to read the trustlist.

3. Export Obfuscation Configuration

After the preceding adaptations are successful, configure -enable-export-obfuscation, and perform adaptation in the following scenarios:

HSP Module Interfaces

For HSP modules that provide interfaces and properties to other modules, use -keep-global-name to retain the interface names and -keep-property-name to retain the property names in exposed classes/interfaces:

-enable-export-obfuscation
-keep-global-name PublicInterface
-keep-property-name exposedProperty
-keep-property-name publicMethod
Enter fullscreen mode Exit fullscreen mode
HAR Module Interfaces

When building HAR modules for use by other modules, use -keep-global-name to retain interface names and -keep-property-name to retain the property names in exposed classes/interfaces in the obfuscation-rules.txt file:

-keep-global-name ExportedClass
-keep-property-name publicAPI
Enter fullscreen mode Exit fullscreen mode
Native Library Integration

For API calls to so libraries (for example, import { napiA } from 'library.so'), use -keep-global-name to retain the so interface name napiA:

-keep-global-name napiA
Enter fullscreen mode Exit fullscreen mode

4. File Name Obfuscation Configuration

After the preceding adaptations are successful, configure -enable-filename-obfuscation, and perform adaptation in the following scenarios:

Dynamic Import Statements

If the code contains dynamic import statements (for example, const path = './filePath'; import(path)), use -keep-file-name filePath to retain the file path:

-enable-filename-obfuscation
-keep-file-name filePath
Enter fullscreen mode Exit fullscreen mode
Router Map Configuration

If the application has a routerMap configuration that describes routing information, use -keep-file-name to retain the page source file path, which is specified by pageSourceFile field in the routing information:

-keep-file-name pages/MainPage
-keep-file-name pages/DetailPage
Enter fullscreen mode Exit fullscreen mode
OhmUrl Navigation

If the code uses ohmUrl for page navigation (for example, router.pushUrl({url: '@bundle:com.example.routerPage/Library/Index'})), use -keep-file-name to retain the path:

-keep-file-name Library/Index
Enter fullscreen mode Exit fullscreen mode

5. Debugging and Verification Process

Verify application functionality and identify any missed scenarios. If the application functionality is abnormal, find the code of the error line in the corresponding intermediate products based on the obfuscated error stack, identify the necessary trustlist configurations, and use appropriate keep rules to retain them.

For file name obfuscation issues: If the application function is abnormal and the error stack contains obfuscated paths, you can query the original path in the build/default/[…]/release/obfuscation/nameCache.json file within the module and then locate the source code file. You can also use the hstack plugin to trigger automatic deobfuscation of error stacks.

Key Takeaways

1. Obfuscation Configuration Strategy

  • Start with top-level obfuscation: Begin with -enable-toplevel-obfuscation and resolve global variable access issues
  • Progressive enablement: Enable features incrementally (property → export → filename obfuscation) to identify issues early
  • Test thoroughly: Verify application functionality after each obfuscation feature is enabled
  • Use retention rules strategically: Apply -keep-* rules only where necessary to maintain maximum obfuscation effectiveness

2. File Configuration Management

  • obfuscation-rules.txt: Configure rules for current module compilation
  • consumer-rules.txt: Configure rules for dependent modules (HAR/HSP only, use retention rules only)
  • obfuscation.txt: Automatically generated during compilation, not user-editable
  • Version awareness: Different DevEco Studio versions have different default configurations

3. Common Scenarios Requiring Retention Rules

  • Dynamic property access: When properties are accessed using bracket notation with computed strings
  • External library integration: Native libraries, JSON data, database fields require property name retention
  • Module interfaces: Public APIs in HAR/HSP modules need global name and property name retention
  • Dynamic imports and routing: File paths used in dynamic imports or routing configurations need file name retention

4. Debugging and Troubleshooting

  • Use intermediate products: Examine obfuscated code in build/default/[…]/release/moduleName directory
  • Leverage name mapping: Use nameCache.json to map obfuscated names back to original names
  • System API trustlist: Check systemApiCache.json for APIs that won't be obfuscated
  • hstack plugin: Use DevEco Studio's hstack plugin for automatic error stack deobfuscation
  • Backup critical files: Always backup sourceMaps.map and nameCache.json for debugging

5. Build and Release Considerations

  • Release-only feature: Obfuscation only works in release builds, not debug builds
  • Performance impact: Obfuscation process adds compilation time but doesn't affect runtime performance
  • Third-party dependencies: Third-party library obfuscation rules only take effect when dependencies are specified in module-level oh-package.json5
  • Custom plugins: Currently, custom obfuscation plugins are not supported in the hvigor build process
  • Iterative obfuscation: If a module depending on an obfuscated HAR enables obfuscation, the HAR will be obfuscated again

6. Security and Maintenance Best Practices

  • Minimal retention: Keep only necessary identifiers to maximize obfuscation effectiveness
  • Documentation: Document all retention rules and their reasons for future maintenance
  • Testing strategy: Implement comprehensive testing to catch obfuscation-related issues early
  • Backup strategy: Maintain mapping files for production debugging and crash analysis

The effective use of ArkGuard obfuscation in HarmonyOS applications provides significant security benefits while requiring careful configuration and testing to ensure application functionality remains intact.

Written by Mucahid Kincir

Top comments (0)