代码肴杂简介
针对工程源码的肴杂可以低落工程被破解攻击的风险,缩短代码的类与成员的名称,减小应用的巨细。
DevEco Studio提供代码肴杂的能力并默认开启,API 10及以上版本的Stage模子、 编译模式为release 时自动进行代码肴杂。
使用约束
- 仅支持Stage工程
- 编译模式为release
- 模块及模块依赖的HAR均未配置关闭肴杂的规则-disable-obfuscation
肴杂范围
在应用工程中,代码肴杂支持以下格式文件肴杂,肴杂后的缓存文件保存在模块目录下的build/[…]/release目录下。
开启代码肴杂
代码肴杂已经被集成了到SDK中,可以在DevEco Studio中很方便地使用。
代码肴杂目前只提供名称肴杂的能力(由于其它肴杂能力会劣化性能)。 开启代码肴杂可以肴杂以下名称:
代码肴杂默认使能对参数名和局部变量名的肴杂。顶层作用域名称和属性名称的肴杂是默认关闭的,由于默认打开大概会导致运行时错误。这些肴杂功能通过肴杂选项来开启它们。
创建一个新工程的时候,配置文件build-profile.json5中会自动生成以下内容:
- "arkOptions": {
- "obfuscation": {
- "ruleOptions": {
- "enable": true,
- "files": ["./obfuscation-rules.txt"],
- }
- }
- }
复制代码 创建一个新的library的时候,还会额外生成consumerFiles属性:
- "arkOptions": {
- "obfuscation": {
- "ruleOptions": {
- "enable": true,
- "files": ["./obfuscation-rules.txt"],
- }
- "consumerFiles": ["./consumer-rules.txt"]
- }
- }
复制代码 肴杂功能默认开启,若被关闭盼望重新开启肴杂必要满意条件: 属性ruleOptions.enable的值为true。
属性ruleOptions.files中指定的肴杂配置文件会在构建HAP、HSP或HAR的时候生效。
属性consumerFiles中指定的肴杂配置文件会在构建依赖这个library的模块时生效。 这些肴杂配置文件的内容还会被归并到HAR包中的obfuscation.txt文件。
当构建HAP、HSP和HAR的时候,终极的肴杂规则是当前构建模块的ruleOptions.files属性,依赖library的consumerFiles属性,以及依赖HAR包中的obfuscation.txt文件的归并。
假如构建的是HAR,HAR包中的obfuscation.txt是自身的consumerFiles属性, 依赖library的consumerFiles属性,以及依赖HAR包中的obfuscation.txt文件的归并。构建HAP、HSP不会生成obfuscation.txt。详细归并的计谋可以检察肴杂规则归并计谋。
肴杂规则配置文件
在创建工程或library的时候,DevEco Studio会自动生成obfuscation-rules.txt和consumer-rules.txt文件,
但是它们默认不会包罗任何肴杂规则。肴杂规则可以写到这些文件中,或者其它自界说文件,
然后将文件路径放到ruleOptions.files和consumerFiles中,如下面的例子所示。
- "buildOption": {
- "arkOptions": {
- "obfuscation": {
- "ruleOptions": {
- "enable": true,
- "files": ["./obfuscation-rules.txt", "./myrules.txt"], //myrules.txt放入配置文件build-profile.json5同级目录下
- }
- "consumerFiles": ["./consumer-rules.txt", "./my-consumer-rules.txt"]
- }
- }
- }
复制代码 配置肴杂规则
肴杂规则分为两种类型,一种是 肴杂选项 ,一种是 保存选项 ;前者是提供顶层作用域名称、属性名称、文件名称等多种肴杂功能配置开关,后者是提供各种肴杂功能的白名单配置能力。
肴杂选项
-disable-obfuscation
关闭全部肴杂。假如使用这个选项,那么构建出来的HAP、HSP或HAR将不会被肴杂。
-enable-property-obfuscation
开启属性肴杂。 假如使用这个选项,那么全部的属性名都会被肴杂,除了下面场景:
- 被import/export直接导入或导出的类、对象的属性名不会被肴杂。例如下面例子中的属性名data不会被肴杂。
- export class MyClass {
- data: string;
- }
复制代码
- ArkUI组件中的属性名不会被肴杂。例如下面例子中的message和data不会被肴杂。
- @Component struct MyExample {
- @State message: string = "hello";
- data: number[] = [];
- ...
- }
复制代码
- 被 保存选项 指定的属性名不会被肴杂。
- SDK API列表中的属性名不会被肴杂。SDK API列表是构建时从SDK中自动提取出来的一个名称列表,其缓存文件为systemApiCache.json,路径为工程目录下build/cache/{…}/release/obfuscation中
- 在Native API场景中,so库对应的d.ts文件中声明的API不会被肴杂。
- 字符串字面量属性名不会被肴杂。例如下面例子中的"name"和"age"不会被肴杂。
- let person = {"name": "abc"};
- person["age"] = 22;
复制代码 假如想肴杂字符串字面量属性名,必要在该选项的根本上再使用-enable-string-property-obfuscation选项。例如
- -enable-property-obfuscation
- -enable-string-property-obfuscation
复制代码 注意:
1. 假如代码里面有字符串属性名包罗特殊字符(除了a-z, A-Z, 0-9, _之外的字符),例如let obj = {“\n”: 123, “”: 4, " ": 5},建议不要开启-enable-string-property-obfuscation选项,由于大概无法通过保存选项来指定保存这些名字。
2. SDK API的属性白名单中不包罗声明文件中使用的字符串常量值,例如示例中的字符串’ohos.want.action.home’未包罗在属性白名单中
- // SDK API文件@ohos.app.ability.wantConstant片段:
- export enum Params {
- ACTION_HOME = 'ohos.want.action.home'
- }
- // 开发者源码示例:
- let params = obj['ohos.want.action.home'];
复制代码 因此在开启了-enable-string-property-obfuscation选项时,假如想保存代码中使用的SDK API字符串常量的属性不被肴杂,例如obj[‘ohos.want.action.home’], 那么必要使用keep选项保存。
-enable-toplevel-obfuscation
开启顶层作用域名称肴杂。假如使用这个选项,那么全部的顶层作用域的名称都会被肴杂,除了下面场景:
- 被import/export的名称不会被肴杂。
- 当前文件找不到声明的名称不会被肴杂。
- 被 保存选项 指定的顶层作用域名称不会被肴杂。
- SDK API列表中的顶层作用域名称不会被肴杂。
-enable-filename-obfuscation
开启文件/文件夹名称肴杂。假如使用这个选项,那么全部的文件/文件夹名称都会被肴杂,除了下面场景:
- oh-package.json5文件中’main’、'types’字段配置的文件/文件夹名称不会被肴杂。
- 模块内module.json5文件中’srcEntry’字段配置的文件/文件夹名称不会被肴杂。
- 被 -keep-file-name 指定的文件/文件夹名称不会被肴杂。
- 非ECMAScript模块引用方式(ECMAScript模块示例:import {foo} from ‘./filename’)
- 非路径引用方式,例如例子中的json5不会被肴杂 import module from ‘json5’
注意:
由于系统会在应用运行时加载某些指定的文件,针对这类文件,开发者必要手动在[-keep-file-name]选项中配置相应的白名单,防止指定文件被肴杂,导致运行失败。
上述必要手动配置白名单的环境,包罗但不限于以了局景:
- 当模块中包罗Ability组件时。用户必要将src/main/module.json5中,'abilities’字段下全部’srcEntry’对应的路径配置到白名单中。
- 当模块中包罗Worker多线程服务时,用户必要将build-profiles.json5中,‘buildOption’-‘sourceOption’-'workers’字段下全部的路径配置到白名单中。
-enable-export-obfuscation
开启直接导入或导出的类或对象的名称和属性名肴杂。假如使用这个选项,那么模块中的直接导入或导出的名称都会被肴杂,除了下面场景:
- 远程HAR(真实路径在oh_modules中的包)中导出的类或对象的名称和属性名不会被肴杂。
- 被 保存选项 指定的名称与属性名不会被肴杂。
- SDK API列表中的名称不会被肴杂。
注意:
- 肴杂导入或导出的类中属性名称必要同时开启-enable-property-obfuscation与-enable-export-obfuscation选项。
- 编译HSP时,假如开启-enable-export-obfuscation选项,必要在模块中的肴杂配置文件obfuscation-rules.txt中保存对外暴露的接口。
- HAP/HSP/HAR依赖HSP场景下,编译时假如开启-enable-export-obfuscation选项,必要在模块中的肴杂配置文件obfuscation-rules.txt中保存HSP导入的接口。
- // 代码示例(HSP中入口文件Index.ets):
- export { add, customApiName } from './src/main/ets/utils/Calc'
- // 保留接口名称配置示例:
- // HSP以及依赖此HSP的模块中obfuscation-rules.txt文件配置:
- keep-global-name
- add
- customApiName
复制代码 -compact
去除不必要的空格符和全部的换行符。假如使用这个选项,那么全部代码会被压缩到一行。
注意:
release模式构建的应用栈信息仅包罗代码行号,不包罗列号,因此compact功能开启后无法依据报错栈中的行号定位到源码详细位置。
-remove-log
删除以了局景中对 console.*语句的调用,要求console.*语句返回值未被调用。
- 文件顶层的调用
- 代码块Block中的调用
- 模块Module中的调用
- switch语句中的调用
-print-namecache filepath
将名称缓存保存到指定的文件路径。名称缓存包罗名称肴杂前后的映射。
注意:
每次全量构建工程时都会生成新的namecache.json文件,因此您每次发布新版本时都要注意保存一个该文件的副本。
-apply-namecache filepath
复用指定的名称缓存文件。名字将会被肴杂成缓存映射对应的名字,假如没有对应,将会被肴杂成新的随机段名字。
该选项应该在增量编译场景中被使用。
默认环境下,DevEco Studio会在暂时的缓存目录中保存缓存文件,而且在增量编译场景中自动应用该缓存文件。
缓存目录:build/cache/{…}/release/obfuscation
-remove-comments
删除文件中的全部解释,包罗单行、多行,及JsDoc解释。以了局景除外:
声明文件中,在-keep-comments中配置的类、方法、struct、罗列等名称上方的JsDoc解释。
注意:
编译生成的源码文件中的解释默认会被全部删除,不支持配置保存。
保存选项
-keep-property-name [,identifiers,…]
指定想保存的属性名,支持使用名称类通配符。例如下面的例子:
- -keep-property-name
- age
- firstName
- lastName
复制代码 注意:
该选项在开启-enable-property-obfuscation时生效
哪些属性名应该被保存?
为了保障肴杂的精确性,建议保存全部不通过点语法访问的属性。
例子:
- var obj = {x0: 0, x1: 0, x2: 0};
- for (var i = 0; i <= 2; i++) {
- console.log(obj['x' + i]); // x0, x1, x2 应该被保留
- }
- Object.defineProperty(obj, 'y', {}); // y 应该被保留
- console.log(obj.y);
- obj.s = 0;
- let key = 's';
- console.log(obj[key]); // s 应该被保留
- obj.u = 0;
- console.log(obj.u); // u 可以被正确地混淆
- obj.t = 0;
- console.log(obj['t']); // 在开启字符串字面量属性名混淆时t和't'会被正确地混淆,但是建议保留
- obj['v'] = 0;
- console.log(obj['v']); // 在开启字符串字面量属性名混淆时'v'会被正确地混淆,但是建议保留
复制代码 对于间接导出的场景,例如export MyClass和let a = MyClass; export {a};,假如不想肴杂它们的属性名,那么必要使用 保存选项 来保存这些属性名。另外,对于直接导出的类或对象的属性的属性名,例如下面例子中的name和age, 假如不想肴杂它们,那么也必要使用 保存选项 来保存这些属性名。
- export class MyClass {
- person = {name: "123", age: 100};
- }
复制代码 没有在so库的d.ts文件中声明的API(例如示例中的foo),假如要在ArkTS/TS/JS文件中使用需手动保存API名称。
- import testNapi from 'library.so'
- testNapi.foo()
复制代码 -keep-global-name [,identifiers,…]
指定要保存的顶层作用域的名称,支持使用名称类通配符。例如,
- -keep-global-name
- Person
- printPersonName
复制代码 哪些顶层作用域的名称应该被保存?
在Javascript中全局变量是globalThis的属性。假如在代码中使用globalThis去访问全局变量,那么该变量名应该被保存。
示例:
- var a = 0;
- console.log(globalThis.a); // a 应该被保留
- function foo(){}
- globalThis.foo(); // foo 应该被保留
- var c = 0;
- console.log(c); // c 可以被正确地混淆
- function bar(){}
- bar(); // bar 可以被正确地混淆
- class MyClass {}
- let d = new MyClass(); // MyClass 可以被正确地混淆
复制代码 -keep-file-name [,identifiers,…]
指定要保存的文件/文件夹的名称(不必要写文件后缀),支持使用名称类通配符。例如,
- -keep-file-name
- index
- entry
复制代码 哪些文件名应该被保存?
- const module1 = require('./file1') // ArkTS不支持CommonJS语法,这种路径引用应该被保留
- const moduleName = './file2'
- const module2 = import(moduleName) // 动态引用方式无法识别moduleName是否是路径,应该被保留
复制代码 -keep-comments [,identifiers,…]
保存声明文件中元素上方的JsDoc解释,支持使用名称类通配符。例如想保存声明文件中Human类上方的JsDoc解释,可进行以下配置:
注意:
- 该选项在开启-remove-comments时生效
- 当声明文件中某个元素名称被肴杂时,该元素上方的JsDoc解释无法通过-keep-comments保存。例如当在-keep-comments中配置了exportClass时,假如下面的类名被肴杂,其JsDoc解释无法被保存:
- /*
- * @class exportClass
- */
- export class exportClass {}
复制代码 -keep-dts filepath
保存指定路径的.d.ts文件中的名称。这里的文件路径可以是一个目录,这种环境下目录中全部.d.ts文件中的名称都会被保存。
假如在构建HAR时使用了这个选项,那么文件中的名称会被归并到最后的obfuscation.txt文件中。
-keep path
保存指定路径中的全部名称(例如变量名、类名、属性名等)不被肴杂。这个路径可以是文件与文件夹,若是文件夹,则文件夹下的文件及子文件夹中文件都不肴杂。
路径仅支持相对路径,./与…/为相对于肴杂配置文件地点目录,支持使用路径类通配符。
- -keep
- ./src/main/ets/fileName.ts // fileName.ts中的名称不混淆
- ../folder // folder目录下文件及子文件夹中的名称都不混淆
- ../oh_modules/json5 // 引用的三方库json5里所有文件中的名称都不混淆
复制代码 注:该功能不影响文件名肴杂-enable-filename-obfuscation的功能
保存选项支持的通配符
名称类通配符
名称类通配符使用方式如下:
通配符含义示例?匹配恣意单个字符"AB?“能匹配"ABC"等,但不能匹配"AB”*匹配恣意数量的恣意字符“AB"能匹配"AB”、“aABb”、“cAB”、"ABc"等 使用示例:
保存全部以a开头的属性名称:
保存全部单个字符的属性名称:
保存全部属性名称:
路径类通配符
路径类通配符使用方式如下:
通配符含义示例?匹配恣意单个字符,除了路径分隔符/“…/a?“能匹配”…/ab"等,但不能匹配”…/a/"*匹配恣意数量的恣意字符,除了路径分隔符/“…/a*/c"能匹配”…/ab/c",但不能匹配"…/ab/d/s/c"**匹配恣意数量的恣意字符“…/a**/c"能匹配”…/ab/c",也能匹配"…/ab/d/s/c"!表示非,只能写在某个路径最前端,用来扫除用户配置的白名单中已有的某种环境“!../a/b/c.ets"表示除”…/a/b/c.ets"以外 使用示例:
表示路径…/a/b/中全部文件夹(不包罗子文件夹)中的c.ets文件不会被肴杂:
表示路径…/a/b/中全部文件夹(包罗子文件夹)中的c.ets文件不会被肴杂:
- -keep
- ../a/b/
- !../a/b/c.ets
复制代码 表示路径…/a/b/中,除了c.ets文件以外的其它文件都不会被肴杂。此中,!不可单独使用,只能用来扫除白名单中已有的环境:
- -keep
- ../a/b/
- !../a/b/c.ets
复制代码 表示路径…/a/中的全部文件(不包罗子文件夹)不会被肴杂:
表示路径…/a/下的全部文件夹(包罗子文件夹)中的全部文件不会被肴杂:
表示模块内的全部文件不会被肴杂:
注意:
(1)以上选项,不支持配置通配符*、?、!作其它含义使用。
例如:
- class A { '*'= 1}-keep-property-name
- *
复制代码 此时表示匹配恣意数量的恣意字符,配置效果为全部属性名称都不肴杂,而不是只有属性不被肴杂。
(2)-keep选项中只允许使用/路径格式,不支持\或\。
解释
可以使用#在肴杂规则文件中进行解释。每行以#开头的文本会被当做是解释,例如下面的例子:
- # white list for MainAbility.ets
- -keep-global-name
- MyComponent
- GlobalFunction
- -keep-property-name # white list for dynamic property names
- firstName
- lastName
- age
复制代码 构建HAR时,解释不会被归并到最后的obfuscation.txt文件中。
肴杂规则归并计谋
一个工程中常常会有许多肴杂规则文件,这些文件来自于:
- 主工程的ruleOptions.files (这里主工程指的是正在构建的工程)
- 当地依赖的library中的consumerFiles选项中指定的文件
- 远程依赖的HAR包中的obfuscate.txt文件
当构建主工程的时候,这些文件中的肴杂规则会按照下面的归并计谋(伪代码)进行归并:
- let `listRules` 表示上面提到的所有混淆规则文件的列表
- let finalRule = {
- disableObfuscation: false,
- enablePropertyObfuscation: false,
- enableToplevelObfuscation: false,
- compact: false,
- removeLog: false,
- keepPropertyName: [],
- keepGlobalName: [],
- keepDts: [],
- printNamecache: string,
- applyNamecache: string
- }
- for each file in `listRules`:
- for each option in file:
- switch(option) {
- case -disable-obfuscation:
- finalRule.disableObfuscation = true;
- continue;
- case -enable-property-obfuscation:
- finalRule.enablePropertyObfuscation = true;
- continue;
- case -enable-toplevel-obfuscation:
- finalRule.enableToplevelObfuscation = true;
- continue;
- case -compact:
- finalRule.compact = true;
- continue;
- case -remove-log:
- finalRule.removeLog = true;
- continue;
- case -print-namecache:
- finalRule.printNamecache = #{指定的路径名};
- continue;
- case -apply-namecache:
- finalRule.applyNamecache = #{指定的路径名};
- continue;
- case -keep-property-name:
- finalRule.keepPropertyName.push(#{指定的名称});
- continue;
- case -keep-global-name:
- finalRule.keepGlobalName.push(#{指定的名称});
- continue;
- case -keep-dts:
- finalRule.keepDts.push(#{指定的路径});
- continue;
- }
- end-for
- end-for
复制代码 最后使用的肴杂规则来自于对象finalRule。
假如构建的是HAR,那么终极的obfuscate.txt文件内容来自于主工程和当地依赖的library的consumerFiles选项,
以及依赖的HAR的obfuscate.txt文件的归并。归并计谋和上面一样,除了以下的不同:
- -keep-dts选项会被转换成-keep-global-name和-keep-property-name。
- -print-namecache和apply-namecache选项会被忽略,不会出如今最后的obfuscate.txt文件中。
报错栈还原
经过肴杂的应用程序中代码名称会发生更改,crash时打印的报错栈更难以理解,由于报错栈与源码不完全一致。开发职员可使用DevEco Studio命令工具Command Line Tools中的hstack插件来还原源码堆栈。
阐明
- 目前不支持在hvigor构建流程中插入自界说肴杂插件
- 肴杂的HAR包被模块依赖,若模块开启肴杂,则HAR包会被二次肴杂
- DevEco Studio右上角Product选项,将此中Build Mode选择release,可开启release编译模式
鸿蒙全栈开发全新学习指南
为了积极培养鸿蒙生态人才,让各人都能学习到鸿蒙开发最新的技术,针对一些在职职员、0根本小白、应届生/盘算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线【包罗了大厂APP实战项目开发】。
本路线共分为四个阶段:
第一阶段:鸿蒙初中级开发必备技能
第二阶段:鸿蒙南北双向高工技能根本:gitee.com/MNxiaona/733GH
第三阶段:应用开发中高级就业技术
第四阶段:全网首发-工业级南向装备开发就业技术:gitee.com/MNxiaona/733GH
鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH
写在最后
- 假如你觉得这篇内容对你还蛮有资助,我想约请你帮我三个小忙:
- 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
- 关注小编,同时可以期待后续文章ing
|