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`选项将使用到的数据库字段配置到白名单。