• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# ArkGuard混淆常见问题
2<!--Kit: ArkTS-->
3<!--Subsystem: ArkCompiler-->
4<!--Owner: @zju-wyx-->
5<!--Designer: @xiao-peiyang; @dengxinyu-->
6<!--Tester: @kirl75; @zsw_zhushiwei-->
7<!--Adviser: @foryourself-->
8
9## 如何排查功能异常
10
11### 排查功能异常步骤
121. 在`obfuscation-rules.txt`中配置`-disable-obfuscation`选项关闭混淆,确认问题是否由混淆引起。
132. 若确认开启混淆后功能出现异常,请先阅读文档,了解模块已配置的混淆规则的能力和需要配置白名单的语法场景,以确保应用功能正常。下文简要介绍默认开启的四项选项功能,详情请参阅对应选项的完整描述。
14    1. [-enable-toplevel-obfuscation](source-obfuscation.md#-enable-toplevel-obfuscation)为顶层作用域名称混淆开关。
15
16    2. [-enable-property-obfuscation](source-obfuscation.md#-enable-property-obfuscation)为属性混淆开关。配置白名单的主要场景包括网络数据访问、json字段访问、动态属性访问、调用so库接口等。需要使用[-keep-property-name](source-obfuscation.md#-keep-property-name)来保留指定的属性名称。
17
18    3. [-enable-export-obfuscation](source-obfuscation.md#-enable-export-obfuscation)为导入/导出名称混淆。一般与`-enable-toplevel-obfuscation`和`-enable-property-obfuscation`选项配合使用。配置白名单的主要场景为模块对外接口不能混淆。需要使用[-keep-global-name](source-obfuscation.md#-keep-global-name)来保留指定的导出/导入名称。
19
20    4. [-enable-filename-obfuscation](source-obfuscation.md#-enable-filename-obfuscation)为文件名混淆。配置白名单的主要场景为动态import或运行时直接加载的文件路径。需要使用[-keep-file-name](source-obfuscation.md#-keep-file-name)来保留这些文件路径及名称。
213. 参考以下典型报错案例,若遇到相似场景,可参照对应解决方法快速处理。
224. 若以下报错案例中未找到相似场景,建议依据各项配置功能正向定位(若不需要相应功能,可删除对应配置项)。
235. 应用运行时崩溃分析方法:
24    1. 打开应用运行日志,或点击DevEco Studio中出现的Crash弹窗,找到运行时崩溃栈。
25    2. 应用运行时异常栈中的行号为[编译产物](source-obfuscation-guide.md#查看混淆效果)的行号,方法名也可能为混淆后名称;因此排查时建议直接根据异常栈查看编译产物,进而分析哪些名称不能被混淆,然后将其配置到白名单中。
266. 应用在运行时未崩溃但出现功能异常(如白屏)的分析方法:
27    1. 打开应用运行日志:选择HiLog,检索与功能异常直接相关的日志,定位问题发生的上下文。
28
29    2. 定位异常代码段:分析日志,找到引发功能异常的代码块。
30
31    3. 增强日志输出:在疑似异常的功能代码中,增加日志打印以检查数据是否正常。
32
33    4. 分析并确定关键字段:通过分析新增的日志输出,判断数据异常是否由混淆导致。
34
35    5. 配置白名单以保护关键字段:将混淆后对应用功能有直接影响的关键字段添加到白名单中。
36
37### 排查非预期的混淆能力
38若出现预期外的混淆效果,检查是否由于依赖的本地模块或三方库开启了某些混淆选项。
39
40示例:
41假设当前模块未配置`-compact`,但混淆的中间产物中代码都被压缩成一行,可按照以下步骤排查混淆选项:
42
431. 查看当前模块的oh-package.json5中的dependencies,此字段记录了当前模块的依赖信息。
442. 在依赖的模块/三方库中的混淆配置文件内检索"-compact":
45    * 在本地依赖的library中的consumer-rules.txt文件中检索"-compact"。
46    * 在工程目录下的oh_modules文件夹中,对全部的obfuscation.txt文件检索"-compact"。
47
48从API version 18开始,主模块默认不合并三方库的`obfuscation.txt`文件中的混淆选项,保留选项仍然有效。
49
50> **说明**:
51>
52> 三方库中的`consumer-rules.txt`不建议配置以下开关选项。这些选项在主模块开启混淆时会生效,可能导致意外的混淆效果,甚至应用运行时崩溃。如果发现三方库的`obfuscation.txt`文件中包含以下开关选项,建议联系发布该三方库的团队删除这些选项并重新打包发布。
53> -enable-property-obfuscation
54> -enable-string-property-obfuscation
55> -enable-toplevel-obfuscation
56> -remove-log
57> -compact
58
59## 典型报错案例及解决方案
60
61### 报错信息为:Error message: Cannot read property xxx of undefined
62
63**问题现象**
64
65混淆规则配置如下所示:
66
67```
68-enable-property-obfuscation
69```
70
71示例代码如下:
72
73```ts
74// 示例JSON文件结构(test.json):
75/*
76{
77  "jsonObj": {
78    "jsonProperty": "value"
79  }
80}
81*/
82
83// 混淆前
84import jsonData from "./testjson";
85
86let jsonProp = jsonData.jsonObj.jsonProperty;
87
88// 混淆后
89import jsonData from "./test.json";
90
91let jsonProp = jsonData.i.j;
92```
93
94**问题原因**
95
96开启属性混淆后,源码会被混淆,但json文件不会。源码中通过`jsonData.i`访问属性时,由于属性名称已经被混淆,json数据中并不存在对应的字段,导致获取的值为`undefined`。
97
98**解决方案**
99
100将json文件中的字段配置到属性白名单中。示例如下:
101
102```
103-keep-property-name
104jsonObj
105jsonProperty
106```
107
108### 报错信息为:Error message: is not callable
109
110**场景一:导出namespace中的方法时,该方法定义处被混淆,调用时未被混淆**
111
112**问题现象**
113
114混淆规则配置如下所示:
115
116```
117-enable-toplevel-obfuscation
118-enable-export-obfuscation
119```
120
121示例代码如下:
122
123```ts
124// 混淆前
125// export.ts
126export namespace NS {
127  export function foo() {}
128}
129
130// import.ts
131import { NS } from './export';
132
133NS.foo();
134```
135
136```ts
137// 混淆后
138// export.ts
139export namespace i {
140  export function j() {}
141}
142
143// import.ts
144import { i } from './export';
145
146i.foo();
147```
148
149**问题原因**
150
151namespace中的foo属于export元素,当通过`NS.foo`调用时被视为属性。由于未开启`-enable-property-obfuscation`选项,导致foo在使用时未被混淆。
152
153**解决方案**
154
155方案一:开启`-enable-property-obfuscation`选项。
156
157方案二:使用`-keep-global-name`选项将namespace中导出的方法配置到白名单中。示例如下:
158
159```
160-keep-global-name
161foo
162```
163
164**场景二:动态导入某个类,类定义处被混淆,调用时未被混淆**
165
166**问题现象**
167
168混淆规则配置如下所示:
169
170```
171-enable-toplevel-obfuscation
172-enable-export-obfuscation
173```
174
175示例代码如下:
176
177```ts
178// 混淆前
179// utils.ts
180export function addNum(a: number, b: number): number {
181  return a + b;
182}
183
184// main.ts
185async function loadAndUseAdd() {
186  try {
187    const mathUtils = await import('./utils');
188    const result = mathUtils.addNum(2, 3);
189  } catch (error) {
190    console.error('Failure reason:', error);
191  }
192}
193
194loadAndUseAdd();
195```
196
197```ts
198// 混淆后
199// utils.ts
200export function c1(d1: number, e1: number): number {
201    return d1 + e1;
202}
203
204// main.ts
205async function i() {
206    try {
207        const a1 = await import("@normalized:N&&&entry/src/main/ets/pages/utils&");
208        const b1 = a1.addNum(2, 3);
209    }
210    catch (z) {
211        console.error('Failure reason:', z);
212    }
213}
214i();
215```
216
217**问题原因**
218
219函数addNum在定义时位于顶层作用域,但通过`.addNum`访问时被视为属性。由于未开启`-enable-property-obfuscation`选项,导致addNum被使用时未进行混淆。
220
221**解决方案**
222
223方案一:开启`-enable-property-obfuscation`选项。
224
225方案二:使用`-keep-global-name`选项将add配置到白名单中。示例如下:
226
227```txt
228-keep-global-name
229addNum
230```
231
232**场景三:调用so库的方法后导致crash**
233
234**问题现象**
235
236混淆规则配置如下所示:
237
238```
239-enable-property-obfuscation
240-enable-export-obfuscation
241```
242
243示例代码如下:
244
245```ts
246// src/main/cpp/types/libentry/Index.d.ts
247export const addNum: (a: number, b: number) => number;
248```
249
250```ts
251// example.ets
252// 混淆前
253import testNapi from 'libentry.so';
254
255testNapi.addNum();
256```
257
258```ts
259// example.ets
260// 混淆后
261import testNapi from "@normalized:Y&&&libentry.so&";
262
263testNapi.m();
264```
265
266**问题原因**
267
268混淆工具仅支持`js/ts/ets`代码的混淆。so库中的方法定义在C++侧,因此这些方法在定义处不会被混淆,但在调用处会被混淆。
269
270**解决方案**
271
272将so库导出的方法配置到属性白名单中。示例如下:
273
274```txt
275-keep-property-name
276addNum
277```
278
279### 报错信息为:'module1/file1' does not provide an export name 'x', which is imported by 'module2/file2'
280
281**问题现象**
282
283主模块和HSP模块的混淆规则配置如下所示:
284
285```
286-enable-toplevel-obfuscation
287-enable-export-obfuscation
288```
289
290示例代码如下:
291
292```ts
293// 混淆前
294// hsp模块
295export function addNum() {}
296
297// entry模块
298import { addNum } from 'hsp';
299
300addNum();
301```
302
303```ts
304// 混淆后
305// hsp模块
306export function b() {}
307
308// entry模块
309import { n } from '@normalized:N&myhsp&&myhsp/Index&';
310
311n();
312```
313
314**问题原因**
315
316当同时开启`-enable-toplevel-obfuscation`和`-enable-export-obfuscation`选项时,主模块与被调用模块的混淆情况如下:
317
318| 主模块 | 依赖模块 | 导入与导出的名称混淆情况 |
319| ------- | ------- | ----------------------------|
320| HAP/HSP | HSP     | HSP和主模块是独立编译的,混淆后名称会不一致,因此都需要配置白名单。 |
321| HAP/HSP | 本地HAR | 本地HAR与主模块一起编译,混淆后名称一致。 |
322| HAP/HSP | 三方库  | 三方库中导出的名称及其属性会被收集到白名单,因此导入和导出时都不会被混淆。 |
323
324由于HAP和HSP模块是独立编译,因此混淆后导入和导出名称不一致,从而导致HAP引用HSP的方法时报错。
325
326**解决方案**
327
328将HSP模块导出的方法配置到`-keep-global-name`下,并且需要在HSP的`consumer-rules.txt`和`obfuscation-rules.txt`文件中都进行对应配置。示例如下:
329
330```txt
331// consumer-rules.txt
332-keep-global-name
333addNum
334
335// obfuscation-rules.txt
336-keep-global-name
337addNum
338```
339
340## 应用运行后无crash信息,但功能异常的情况
341
342### 使用Record<string, Object>作为对象的类型定义时,属性被混淆
343
344**问题现象**
345
346`parameters`的类型为`Record<string, Object>`。开启属性混淆后,`parameters`对象中的`linkSource`属性被混淆,导致功能异常。
347
348示例代码如下:
349
350```ts
351// 混淆前
352import { Want } from '@kit.AbilityKit';
353
354let petalMapWant: Want = {
355  bundleName: 'com.example.myapplication',
356  uri: 'maps://',
357  parameters: {
358    linkSource: 'com.other.app'
359  }
360}
361```
362
363```ts
364// 混淆后
365import type Want from "@ohos:app.ability.Want";
366
367let petalMapWant: Want = {
368    bundleName: 'com.example.myapplication',
369    uri: 'maps://',
370    parameters: {
371        i: 'com.other.app'
372    }
373};
374```
375
376**问题原因**
377
378示例中`parameters`的类型为`Record<string, Object>`,这仅表示以字符串为键的对象的泛型定义,未详细描述其内部属性。因此,混淆工具无法识别对象内部哪些属性不应被混淆,导致`linkSource`被混淆后,引发功能异常。
379
380**解决方案**
381
382将混淆后会出现问题的属性名配置到属性白名单中,示例如下:
383
384```
385-keep-property-name
386linkSource
387```
388
389### 跨文件调用某属性,该属性在一个文件中保留,在另一个文件中被混淆
390
391**问题现象**
392
393混淆规则配置如下所示:
394
395```
396-enable-property-obfuscation
397-keep
398./file1.ts
399```
400
401在`file2.ts`中导入`file1.ts`的接口。该接口包含一个对象类型的属性。此对象属性在`file1.ts`中被保留,但在`file2.ts`中被混淆,导致调用时出现功能异常。
402
403示例代码如下:
404
405```ts
406// 混淆前
407// file1.ts
408export interface MyInfo {
409  age: number;
410  address: {
411    city1: string;
412  }
413}
414
415// file2.ts
416import { MyInfo } from './file1';
417
418const person: MyInfo = {
419  age: 20,
420  address: {
421    city1: "shanghai"
422  }
423}
424```
425
426```ts
427// 混淆后
428// file1.ts
429export interface MyInfo {
430  age: number;
431  address: {
432    city1: string;
433  }
434}
435
436// file2.ts
437import { MyInfo } from './file1';
438
439const person: MyInfo = {
440  age: 20,
441  address: {
442    i: "shanghai"
443  }
444}
445```
446
447**问题原因**
448
449使用`-keep`选项保留`file1.ts`文件时,该文件中的代码不会被混淆。导出属性(如address)所属类型内的属性不会自动加入白名单,因此在其他文件中使用时会被混淆。
450
451**解决方案**
452
453方案一:使用`interface`定义该属性的类型,并使用`export`进行导出,这样该属性将被自动加入到属性白名单中。示例如下:
454
455```ts
456// file1.ts
457export interface AddressType {
458  city1: string;
459}
460export interface MyInfo {
461  age: number;
462  address: AddressType;
463}
464```
465
466方案二:使用`-keep-property-name`选项,将未直接导出的类型内的属性配置到属性白名单中。示例如下:
467
468```
469-keep-property-name
470city1
471```
472
473### 未开启-enable-string-property-obfuscation,字符串字面量属性名却被混淆
474
475**问题现象**
476
477```ts
478// 混淆前
479const person = {
480  myAge: 18
481}
482person["myAge"] = 20;
483```
484
485```ts
486// 混淆后
487const person = {
488  myAge: 18
489}
490person["m"] = 20;
491```
492
493**问题原因**
494
495主模块所依赖的其他模块中的`consumer-rules.txt`文件配置了`-enable-string-property-obfuscation`选项,主模块会合并该选项,导致字符串字面量属性名被混淆。
496
497**解决方案**
498
499从API version 18开始,主模块默认不会被三方库的混淆规则所影响,因此不会有这种情况。但如果API version低于18,可参考以下两种解决方案。
500
501方案一:确认依赖的远程HAR包的`obfuscation.txt`文件中是否配置了`-enable-string-property-obfuscation`选项。若配置了则会影响主模块,需将其关闭。参考[排查非预期的混淆能力](source-obfuscation-questions.md#排查非预期的混淆能力)。
502方案二:若工程复杂无法找到配置了该混淆选项的远程HAR包,可以将属性名直接配置到白名单中。
503
504### 数据库相关的字段被混淆后导致功能异常
505
506**问题现象**
507
508Hilog日志中报错信息为:`table Account has no column named a23 in 'INSERT INTO Account(a23)'`。
509
510**问题原因**
511
512混淆时代码中的SQL语句字段名称被混淆,但数据库中字段为原始名称,从而导致报错。
513
514**解决方案**
515
516使用`-keep-property-name`选项将使用到的数据库字段配置到白名单。