• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 文档更新说明
2
3## 该文档不再维护,后续混淆文档迁移至[官方指南](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5)以及[社区doc仓](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/arkts-utils/source-obfuscation.md)
4
5注:代码混淆功能与DevEco Studio版本同步更新,建议优先阅读与DevEco Studio版本配套的官方指南。
6社区doc仓文档对应开源代码,领先于发布的DevEco Studio中的代码混淆功能。
7
8# Arkguard
9
10Arkguard 是Javascript和Typescript的源码混淆工具。
11
12# 在DevEco Studio中的用法
13
14Arkguard已经被集成了到SDK中。可以在DevEco Studio中很方便地使用。Arkguard只能用于Stage模型
15(不支持FA模型)。目前Arkguard只提供名称混淆的能力(因为其它混淆能力会劣化性能)。
16使用Arkguard可以混淆以下名称:
17
18* 参数名和局部变量名
19* 顶层作用域的名称
20* 属性名称
21
22Arkguard默认使能对参数名和局部变量名的混淆。顶层作用域名称和属性名称的混淆是默认关闭的,
23因为默认打开可能会导致运行时错误。你可以通过[混淆选项](#混淆选项)来开启它们。
24
25创建一个新工程的时候,配置文件`build-profile.json5`中会自动生成以下内容:
26
27```
28"arkOptions": {
29  "obfuscation": {
30    "ruleOptions": {
31      "enable": true,
32      "files": ["obfuscation-rules.txt"],
33    }
34  }
35}
36```
37
38创建一个新的library的时候,还会额外生成`consumerFiles`属性:
39
40```
41"arkOptions": {
42  "obfuscation": {
43    "ruleOptions": {
44      "enable": true,
45      "files": ["obfuscation-rules.txt"],
46    }
47    "consumerFiles": ["consumer-rules.txt"]
48  }
49}
50```
51
52要想开启混淆,需要满足下面的条件:
53
54* 属性`ruleOptions.enable`的值为`true`,并且所有依赖的library的`ruleOptions.enable`属性是`true`
55* 在release模式构建
56
57属性`ruleOptions.files`中指定的混淆配置文件会在构建HAP或HAR的时候被应用。
58
59属性`consumerFiles`中指定的混淆配置文件会在构建依赖这个library的工程或library时被应用。
60这些混淆配置文件的内容还会被合并到HAR包中的`obfuscation.txt`文件。
61
62当构建HAP或者HAR的时候,最终的混淆规则是自身的`ruleOptions.files`属性,依赖的library的`consumerFiles`属性,
63以及依赖的HAR包中的`obfuscation.txt`文件的合并。如果构建的是HAR,`obfuscation.txt`是自身的`consumerFiles`属性,
64依赖的library的`consumerFiles`属性,以及依赖的HAR包中的`obfuscation.txt`文件的合并。
65构建HAP不会生成`obfuscation.txt`。详细合并的策略可以查看[混淆规则合并策略](#混淆规则合并策略)。
66
67## 配置混淆规则
68
69在创建工程或library的时候,DevEco Studio会自动生成`obfuscation-rules.txt`和`consumer-rules.txt`文件,
70但是它们默认不会包含任何混淆规则。你可以在这些文件中写混淆规则,或者也可以将规则写在其它文件,
71然后将文件路径放到`ruleOptions.files`和`consumerFiles`中,如下面的例子所示。
72
73```
74"buildOption": {
75  "arkOptions": {
76    "obfuscation": {
77      "ruleOptions": {
78        "enable": true,
79        "files": ["obfuscation-rules.txt", "myrules.txt"],
80      }
81      "consumerFiles": ["consumer-rules.txt", "my-consumer-rules.txt"]
82    }
83  }
84}
85```
86
87在混淆规则文件中,你可以写[混淆选项](#混淆选项)和[保留选项](#保留选项)。
88
89### 混淆选项
90
91#### -disable-obfuscation
92
93关闭所有混淆。如果你使用这个选项,那么构建出来的HAP或HAR将不会被混淆。默认情况下,
94Arkguard只混淆参数名和局部变量名(通过将它们重新命名为随机的短名字)。
95
96#### -enable-property-obfuscation
97
98开启属性混淆。 如果你使用这个选项,那么所有的属性名都会被混淆,除了下面场景:
99
100* 被`import/export`直接导入或导出的类或对象的属性名不会被混淆。比如下面例子中的属性名`data`不会被混淆。
101
102    ```
103    export class MyClass {
104       data: string;
105    }
106    ```
107
108    对于间接导出的场景,比如`export MyClass`和`let a = MyClass; export {a};`,如果你不想混淆它们的属性名,那么你需要使用[保留选项](#保留选项)来保留这些属性名。另外,对于直接导出的类或对象的属性的属性名,比如下面例子中的`name`和`age`, 如果你不想混淆它们,那么你也需要使用[保留选项](#保留选项)来保留这些属性名。
109
110    ```
111    export class MyClass {
112       person = {name: "123", age: 100};
113    }
114    ```
115
116    如果想混淆直接导入/导出的名称,请参考[`-enable-export-obfuscation`](#-enable-export-obfuscation)选项。
117* ArkUI组件中的属性名不会被混淆。比如下面例子中的`message`和`data`不会被混淆。
118
119    ```
120    @Component struct MyExample {
121        @State message: string = "hello";
122        data: number[] = [];
123        ...
124    }
125    ```
126
127* 被[保留选项](#保留选项)指定的属性名不会被混淆。
128* 系统API列表中的属性名不会被混淆。系统API列表是构建时从SDK中自动提取出来的一个名称列表,其缓存文件为systemApiCache.json,路径为工程目录下build/cache/{...}/release/obfuscation129* 在Native API场景中,在so的d.ts文件中声明的API不会被混淆。
130* 字符串字面量属性名不会被混淆。比如下面例子中的`"name"`和`"age"`不会被混淆。
131
132    ```
133    let person = {"name": "abc"};
134    person["age"] = 22;
135    ```
136
137    如果你想混淆字符串字面量属性名,你需要在该选项的基础上再使用`-enable-string-property-obfuscation`选项。比如
138
139    ```
140    -enable-property-obfuscation
141    -enable-string-property-obfuscation
142    ```
143
144    **注意**:
145    **1.** 如果代码里面有字符串属性名包含特殊字符(除了`a-z, A-Z, 0-9, _`之外的字符),比如`let obj = {"\n": 123, "": 4, " ": 5}`,建议不要开启`-enable-string-property-obfuscation`选项,因为当不想混淆这些名字时,可能无法通过[保留选项](#保留选项)来指定保留这些名字。
146    **2.** 系统API的属性白名单中不包含声明文件中使用的字符串常量值,比如示例中的字符串'ohos.want.action.home'不被包含在属性白名单中
147
148    ```
149    // 系统API文件@ohos.app.ability.wantConstant片段:
150    export enum Params {
151      ACTION_HOME = 'ohos.want.action.home'
152    }
153    // 开发者源码示例:
154    let params = obj['ohos.want.action.home'];
155    ```
156
157    因此在开启了`-enable-string-property-obfuscation`选项时,如果想保留代码中使用的系统API字符串常量的属性不被混淆,比如obj['ohos.want.action.home'], 那么需要使用keep选项保留。
158
159#### -enable-toplevel-obfuscation
160
161开启顶层作用域名称混淆。如果你使用这个选项,那么所有的顶层作用域的名称都会被混淆,除了下面场景:
162
163* 被`import/export`的名称不会被混淆。
164* 当前文件找不到声明的名称不会被混淆。
165* 被[保留选项](#保留选项)指定的顶层作用域名称不会被混淆。
166* 系统API列表中的顶层作用域名称不会被混淆。
167
168#### -enable-filename-obfuscation
169
170开启文件/文件夹名称混淆。如果使用这个选项,那么所有的文件/文件夹名称都会被混淆,除了下面场景:
171
172* oh-package.json5文件中'main'、'types'字段配置的文件/文件夹名称不会被混淆。
173* 模块内module.json5文件中'srcEntry'字段配置的文件/文件夹名称不会被混淆。
174* 被[`-keep-file-name`](#保留选项)指定的文件/文件夹名称不会被混淆。
175* 非ECMAScript模块引用方式(ECMAScript模块示例:`import {foo} from './filename'`)
176* 非路径引用方式,比如例子中的json5不会被混淆 `import module from 'json5'`
177
178**注意**:
179**1.** 由于系统会在应用运行时加载某些指定的文件,针对这类文件,开发者需要手动在[`-keep-file-name`]选项中配置相应的白名单,防止指定文件被混淆,导致运行失败。
180上述需要手动配置白名单的情况,包括但不限于以下场景:
181(1) 当模块中包含Ability组件时。用户需要将`scr/main/module.json5`中,'abilities'字段下所有'srcEntry'对应的路径配置到白名单中。
182(2) 当模块中包含Worker多线程服务时,用户需要将`build-profiles.json5`中,'buildOption'-'sourceOption'-'workers'字段下所有的路径配置到白名单中。
183
184#### -enable-export-obfuscation
185
186开启直接导入或导出的类或对象的名称和属性名混淆。如果使用这个选项,那么模块中的直接导入或导出的名称都会被混淆,除了下面场景:
187
188* 远程HAR(真实路径在oh_modules中的包)中导出的类或对象的名称和属性名不会被混淆。
189* 被[保留选项](#保留选项)指定的名称与属性名不会被混淆。
190* 系统API列表中的名称不会被混淆。
191
192**注意**:
193
1941. 混淆导入或导出的类中属性名称需要同时开启`-enable-property-obfuscation`与`-enable-export-obfuscation`选项。
1952. 编译HSP时,如果开启`-enable-export-obfuscation`选项,需要在模块中的混淆配置文件`obfuscation-rules.txt`中保留对外暴露的接口。
1963. HAP/HSP/HAR依赖HSP场景下,编译时如果开启`-enable-export-obfuscation`选项,需要在模块中的混淆配置文件`obfuscation-rules.txt`中保留HSP导入的接口。
197
198    ```
199    // 代码示例(HSP中入口文件Index.ets):
200    export { add, customApiName } from './src/main/ets/utils/Calc'
201
202    // 保留接口名称配置示例:
203    // HSP以及依赖此HSP的模块中obfuscation-rules.txt文件配置:
204    keep-global-name
205    add
206    customApiName
207    ```
208
209#### -compact
210
211去除不必要的空格符和所有的换行符。如果使用这个选项,那么所有代码会被压缩到一行。
212**注意**:release模式构建的应用栈信息仅包含代码行号,不包含列号,因此compact功能开启后无法依据报错栈中的行号定位到源码具体位置。
213
214#### -remove-log
215
216删除以下场景中对 console.*语句的调用,要求console.*语句返回值未被调用。
217
2181. 文件顶层的调用
2192. 代码块Block中的调用
2203. 模块Module中的调用
2214. switch语句中的调用
222
223#### `-print-namecache` filepath
224
225将名称缓存保存到指定的文件路径。名称缓存包含名称混淆前后的映射。
226注意:每次全量构建工程时都会生成新的namecache.json文件,因此您每次发布新版本时都要注意保存一个该文件的副本。
227
228#### `-apply-namecache` filepath
229
230复用指定的名称缓存文件。名字将会被混淆成缓存映射对应的名字,如果没有对应,将会被混淆成新的随机段名字。
231该选项应该在增量编译场景中被使用。
232
233默认情况下,DevEco Studio会在临时的缓存目录中保存缓存文件,并且在增量编译场景中自动应用该缓存文件。
234缓存目录:build/cache/{...}/release/obfuscation
235
236#### -remove-comments
237
238删除文件中的所有注释,包括单行、多行,及JsDoc注释。以下场景除外:
239声明文件中,在`-keep-comments`中配置的类、方法、struct、枚举等名称上方的JsDoc注释。
240**注意**:编译生成的源码文件中的注释默认会被全部删除,不支持配置保留。
241
242### 保留选项
243
244#### `-keep-property-name` [,identifiers,...]
245
246指定你想保留的属性名。比如下面的例子:
247
248```
249-keep-property-name
250age
251firstName
252lastName
253```
254
255**注意**:该选项在开启`-enable-property-obfuscation`时生效
256
257`-keep-comments`
258保留声明文件中元素上方的JsDoc注释。比如想保留声明文件中Human类上方的JsDoc注释,可进行以下配置:
259
260```
261-keep-comments
262Human
263```
264
265**注意**:
266
2671. 该选项在开启`-remove-comments`时生效
2682. 当声明文件中某个元素名称被混淆时,该元素上方的JsDoc注释无法通过`-keep-comments`保留。比如当在`-keep-comments`中配置了
269exportClass时,如果下面的类名被混淆,其JsDoc注释无法被保留:
270
271```
272/**
273** @class exportClass
274*/
275export class exportClass {}
276```
277
278**哪些属性名应该被保留?**
279
280为了保障混淆的正确性,我们建议你保留所有不通过点语法访问的属性。
281
282例子:
283
284```
285var obj = {x0: 0, x1: 0, x2: 0};
286for (var i = 0; i <= 2; i++) {
287    console.log(obj['x' + i]);  // x0, x1, x2 应该被保留
288}
289
290Object.defineProperty(obj, 'y', {});  // y 应该被保留
291console.log(obj.y);
292
293obj.s = 0;
294let key = 's';
295console.log(obj[key]);        // s 应该被保留
296
297obj.u = 0;
298console.log(obj.u);           // u 可以被正确地混淆
299
300obj.t = 0;
301console.log(obj['t']);        // 在开启字符串字面量属性名混淆时t和't'会被正确地混淆,但是我们建议保留
302
303obj['v'] = 0;
304console.log(obj['v']);        // 在开启字符串字面量属性名混淆时'v'会被正确地混淆,但是我们建议保留
305```
306
307在Native API场景中,没有在so的d.ts文件中声明的API,如果要在ets/ts/js文件中使用需要手动保留。
308
309#### `-keep-global-name` [,identifiers,...]
310
311指定要保留的顶层作用域的名称。比如,
312
313```
314-keep-global-name
315Person
316printPersonName
317```
318
319**哪些顶层作用域的名称应该被保留?**
320
321在Javascript中全局变量是`globalThis`的属性。如果在代码中使用`globalThis`去访问全局变量,那么该变量名应该被保留。
322
323例子:
324
325```
326var a = 0;
327console.log(globalThis.a);  // a 应该被保留
328
329function foo(){}
330globalThis.foo();           // foo 应该被保留
331
332var c = 0;
333console.log(c);             // c 可以被正确地混淆
334
335function bar(){}
336bar();                      // bar 可以被正确地混淆
337
338class MyClass {}
339let d = new MyClass();      // MyClass 可以被正确地混淆
340```
341
342#### `-keep-file-name` [,identifiers,...]
343
344指定要保留的文件/文件夹的名称(不需要写文件后缀)。比如,
345
346```
347-keep-file-name
348index
349entry
350```
351
352**哪些文件名应该被保留?**
353
354```
355const module1 = require('./file1')   // ARKTs不支持CommonJS语法,这种路径引用应该被保留
356const moduleName = './file2'
357const module2 = import(moduleName)    // 动态引用方式无法识别moduleName是否是路径,应该被保留
358```
359
360#### `-keep-dts` filepath
361
362保留指定路径的`.d.ts`文件中的名称。这里的文件路径可以是一个目录,这种情况下目录中所有`.d.ts`文件中的名称都会被保留。
363如果在构建HAR时使用了这个选项,那么文件中的名称会被合并到最后的`obfuscation.txt`文件中。
364
365#### `-keep` path
366
367保留指定路径中的所有名称(例如变量名、类名、属性名等)不被混淆。这个路径可以是文件与文件夹,若是文件夹,则文件夹下的文件及子文件夹中文件都不混淆。
368路径仅支持相对路径,`./`与`../`为相对于混淆配置文件所在目录。
369
370```
371-keep
372./src/main/ets/fileName.ts   // fileName.ts中的名称不混淆
373../folder                    // folder目录下文件及子文件夹中的名称都不混淆
374../oh_modules/json5          // 引用的三方库json5里所有文件中的名称都不混淆
375```
376
377注:该功能不影响文件名混淆`-enable-filename-obfuscation`的功能
378
379### 保留选项支持通配符
380
381#### 名称类通配符
382
383以下保留选项支持配置名称类通配符:
384
385`-keep-property-name`
386
387`-keep-global-name`
388
389`-keep-file-name`
390
391`-keep-comments`
392
393名称类通配符使用方式如下:
394
395| 通配符 | 含义                   | 示例                                       |
396| ------ | ---------------------- | ------------------------------------------ |
397| ?      | 匹配任意单个字符       | "AB?"能匹配"ABC"等,但不能匹配"AB"         |
398| \*     | 匹配任意数量的任意字符 | "\*AB\*"能匹配"AB"、"aABb"、"cAB"、"ABc"等 |
399
400**使用示例**:
401
402保留所有以a开头的属性名称:
403
404```
405-keep-property-name
406a*
407```
408
409保留所有单个字符的属性名称:
410
411```
412-keep-property-name
413?
414```
415
416保留所有属性名称:
417
418```
419-keep-property-name
420*
421```
422
423#### 路径类通配符
424
425以下保留选项支持配置路径类通配符:
426
427`-keep`
428
429路径类通配符使用方式如下:
430
431| 通配符 | 含义                                                                     | 示例                                              |
432| ------ | ------------------------------------------------------------------------ | ------------------------------------------------- |
433| ?      | 匹配任意单个字符,除了路径分隔符'/'                                      | "../a?"能匹配"../ab"等,但不能匹配"../a/"         |
434| \*     | 匹配任意数量的任意字符,除了路径分隔符'/'                                | "../a*/c"能匹配"../ab/c",但不能匹配"../ab/d/s/c" |
435| \*\*   | 匹配任意数量的任意字符                                                   | "../a**/c"能匹配"../ab/c",也能匹配"../ab/d/s/c"  |
436| !      | 表示非,只能写在某个路径最前端,用来排除用户配置的白名单中已有的某种情况 | "!../a/b/c.ets"表示除"../a/b/c.ets"以外           |
437
438**使用示例**:
439
440表示路径../a/b/中所有文件夹(不包含子文件夹)中的c.ets文件不会被混淆:
441
442```
443-keep
444../a/b/*/c.ets
445```
446
447表示路径../a/b/中所有文件夹(包含子文件夹)中的c.ets文件不会被混淆:
448
449```
450-keep
451../a/b/**/c.ets
452```
453
454表示路径../a/b/中,除了c.ets文件以外的其它文件都不会被混淆:
455
456```
457-keep
458../a/b/
459!../a/b/c.ets
460```
461
462无意义:
463
464```
465-keep
466!../a/b/c.ets
467```
468
469表示所有文件都不会被混淆:
470
471```
472-keep
473*
474```
475
476**注意**:
477
478(1)以上选项,不支持配置通配符'*'、'?'、'!'作其它含义使用。
479例如:
480
481```
482class A {
483  '*'= 1
484}
485
486-keep-property-name
487*
488```
489
490此时'\*'表示匹配任意数量的任意字符,配置效果为所有属性名称都不混淆,而不是只有'\*'属性不被混淆。
491
492(2)-keep选项中只允许使用'/'路径格式,不支持'\\'或'\\\\'。
493
494### 注释
495
496可以使用`#`在混淆规则文件中进行注释。每行以`#`开头的文本会被当做是注释,比如下面的例子:
497
498```
499# white list for MainAbility.ets
500-keep-global-name
501MyComponent
502GlobalFunction
503
504-keep-property-name # white list for dynamic property names
505firstName
506lastName
507age
508```
509
510构建HAR时,注释不会被合并到最后的`obfuscation.txt`文件中。
511
512### 混淆规则合并策略
513
514一个工程中经常会有许多混淆规则文件,这些文件来自于:
515
516* 主工程的`ruleOptions.files` (这里主工程我们指的是正在构建的工程)
517* 本地依赖的library中的`consumerFiles`选项中指定的文件
518* 远程依赖的HAR包中的`obfuscate.txt`文件
519
520当构建主工程的时候,这些文件中的混淆规则会按照下面的合并策略(伪代码)进行合并:
521
522```
523let `listRules` 表示上面提到的所有混淆规则文件的列表
524let finalRule = {
525    disableObfuscation: false,
526    enablePropertyObfuscation: false,
527    enableToplevelObfuscation: false,
528    compact: false,
529    removeLog: false,
530    keepPropertyName: [],
531    keepGlobalName: [],
532    keepDts: [],
533    printNamecache: string,
534    applyNamecache: string
535}
536for each file in `listRules`:
537    for each option in file:
538        switch(option) {
539            case -disable-obfuscation:
540                finalRule.disableObfuscation = true;
541                continue;
542            case -enable-property-obfuscation:
543                finalRule.enablePropertyObfuscation = true;
544                continue;
545            case -enable-toplevel-obfuscation:
546                finalRule.enableToplevelObfuscation = true;
547                continue;
548            case -compact:
549                finalRule.compact = true;
550                continue;
551            case -remove-log:
552                finalRule.removeLog = true;
553                continue;
554            case -print-namecache:
555                finalRule.printNamecache = #{指定的路径名};
556                continue;
557            case -apply-namecache:
558                finalRule.applyNamecache = #{指定的路径名};
559                continue;
560            case -keep-property-name:
561                finalRule.keepPropertyName.push(#{指定的名称});
562                continue;
563            case -keep-global-name:
564                finalRule.keepGlobalName.push(#{指定的名称});
565                continue;
566            case -keep-dts:
567                finalRule.keepDts.push(#{指定的路径});
568                continue;
569        }
570    end-for
571end-for
572```
573
574最后使用的混淆规则来自于对象`finalRule`。
575
576如果构建的是HAR,那么最终的`obfuscate.txt`文件内容来自于主工程和本地依赖的library的`consumerFiles`选项,
577以及依赖的HAR的`obfuscate.txt`文件的合并。合并策略和上面一样,除了以下的不同:
578
579* `-keep-dts`选项会被转换成`-keep-global-name`和`-keep-property-name`。
580* `-print-namecache`和`apply-namecache`选项会被忽略,不会出现在最后的`obfuscate.txt`文件中。
581