1# ArkGuard混淆常见问题 2 3## 如何排查功能异常 4 5### 排查功能异常步骤 61. 先在obfuscation-rules.txt配置-disable-obfuscation选项关闭混淆,确认问题是否由混淆引起。 72. 若确认是开启混淆后功能出现异常,请先阅读文档了解[-enable-property-obfuscation](source-obfuscation.md#-enable-property-obfuscation)、[-enable-toplevel-obfuscation](source-obfuscation.md#-enable-toplevel-obfuscation)、[-enable-filename-obfuscation](source-obfuscation.md#-enable-filename-obfuscation)、[-enable-export-obfuscation](source-obfuscation.md#-enable-export-obfuscation)等混淆规则的能力以及哪些语法场景需要配置白名单来保证应用功能正常。下文简要介绍默认开启的四项选项功能,细节还请阅读对应选项的完整描述。 8 1. [-enable-toplevel-obfuscation](source-obfuscation.md#-enable-toplevel-obfuscation)为顶层作用域名称混淆开关。 9 2. [-enable-property-obfuscation](source-obfuscation.md#-enable-property-obfuscation)为属性混淆开关,配置白名单的主要场景为网络数据访问、json字段访问、动态属性访问、调用so库接口等不能混淆场景,需要使用[-keep-property-name](source-obfuscation.md#-keep-property-name)来保留指定的属性名称。 10 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)来指定保留导出/导入名称。 11 4. [-enable-filename-obfuscation](source-obfuscation.md#-enable-filename-obfuscation)为文件名混淆,配置白名单的主要场景为动态import或运行时直接加载的文件路径,需要使用[-keep-file-name](source-obfuscation.md#-keep-file-name)来保留这些文件路径及名称。 123. 参考下文常见报错案例,若是相似场景可参考对应的解决方法快速解决。 134. 若常见案例中未找到相似案例,建议依据各项配置功能正向定位(若不需要相应功能,可删除对应配置项)。 145. 应用运行时崩溃分析方法: 15 1.打开应用运行日志或者点击DevEco Studio中出现的Crash弹窗,找到运行时崩溃栈。 16 2.应用运行时崩溃栈中的行号为[编译产物](source-obfuscation-guide.md#查看混淆效果)的行号,方法名也可能为混淆后名称;因此排查时建议直接根据崩溃栈查看编译产物,进而分析哪些名称不能被混淆,然后将其配置进白名单中。 176. 应用在运行时未崩溃但出现功能异常的分析方法(比如白屏): 18 1.打开应用运行日志:选择HiLog,检索与功能异常直接相关的日志,定位问题发生的上下文。 19 2.定位异常代码段:通过分析日志,找到导致功能异常的具体代码块。 20 3.增强日志输出:在疑似异常的功能代码中,对处理的数据字段增加日志记录。 21 4.分析并确定关键字段:通过对新增日志输出的分析,识别是否由于混淆导致该字段的数据异常。 22 5.配置白名单保护关键字段:将确认在混淆后对应用功能产生直接影响的关键字段添加到白名单中。 23 24#### 排查非预期的混淆能力 25若出现预期外的混淆效果,检查是否是依赖的本地模块/三方库开启了某些混淆选项。 26 27示例: 28假设当前模块未配置`-compact`,但是混淆的中间产物中代码都被压缩成一行,可按照以下步骤排查混淆选项: 29 301. 查看当前模块的oh-package.json5中的dependencies,此字段记录了当前模块的依赖信息。 312. 在依赖的模块/三方库中的混淆配置文件内检索"-compact": 32 * 在本地依赖的library中的consumer-rules.txt文件中检索"-compact"。 33 * 在工程目录下的oh_modules文件夹中,对全部的obfuscation.txt文件检索"-compact"。 34 35**说明**: 36三方库中obfuscation.txt不建议配置如下开关选项,这些选项会在主模块开启混淆时生效,进而出现预期外的混淆效果,甚至会出现应用运行时崩溃。因此建议联系发布此三方库方删除这些选项并重新出包。 37``` 38-enable-property-obfuscation 39-enable-string-property-obfuscation 40-enable-toplevel-obfuscation 41-remove-log 42-compact 43``` 44 45## 属性混淆的问题 46 47### 数据库相关的字段,开启属性混淆时报错 48 49**问题现象** 50 51报错内容为 `table Account has no column named a23 in 'INSET INTO Account(a23)'`。 52 53**问题原因** 54 55代码里使用了数据库字段,混淆时该SQL语句中字段名称被混淆,但数据库中字段为原始名称,从而导致报错。 56 57**解决方案** 58 59使用`-keep-property-name`选项将使用到的数据库字段配置到白名单。 60 61### 使用Record<string, Object>作为对象的类型时,属性被混淆 62 63**问题现象** 64 65`parameters`的类型为`Record<string, Object>`,在开启属性混淆后,`parameters`对象中的属性`linkSource`被混淆,进而导致功能异常。示例如下: 66 67``` 68// 混淆前 69import { Want } from '@kit.AbilityKit'; 70let petalMapWant: Want = { 71 bundleName: 'com.example.myapplication', 72 uri: 'maps://', 73 parameters: { 74 linkSource: 'com.other.app' 75 } 76} 77``` 78 79``` 80// 混淆后 81import type Want from "@ohos:app.ability.Want"; 82let petalMapWant: Want = { 83 bundleName: 'com.example.myapplication', 84 uri: 'maps://', 85 parameters: { 86 i: 'com.other.app' 87 } 88}; 89``` 90 91**问题原因** 92 93在这个示例中,所创建的对象的内容需要传递给系统来加载某个页面,因此对象中的属性名称不能被混淆,否则会造成功能异常。示例中`parameters`的类型为`Record<string, Object>`,这只是一个表示以字符串为键的对象的泛型定义,并没有详细描述其内部结构和属性类型。因此,混淆工具无法识别该对象内部哪些属性不应被混淆,从而可能导致内部属性名`linkSource`被混淆。 94 95**解决方案** 96 97将混淆后会出现问题的属性名配置到属性白名单中,示例如下: 98 99``` 100-keep-property-name 101linkSource 102``` 103 104### 跨文件调用某属性,该属性在一个文件中保留,在另一个文件中被混淆 105 106**问题现象** 107 108使用如下混淆配置: 109 110``` 111-enable-property-obfuscation 112-keep 113./file1.ts 114``` 115 116并且在`file2.ts`中导入`file1.ts`的接口。此时,接口中有属性的类型为对象类型,该对象类型的属性在`file1.ts`中被保留,在`file2.ts`中被混淆,从而导致调用时引发功能异常。示例如下: 117 118``` 119// 混淆前 120// file1.ts 121export interface MyInfo { 122 age: number; 123 address: { 124 city1: string; 125 } 126} 127 128// file2.ts 129import { MyInfo } from './file1'; 130const person: MyInfo = { 131 age: 20, 132 address: { 133 city1: "shanghai" 134 } 135} 136``` 137 138``` 139// 混淆后 140// file1.ts的代码被保留 141// file2.ts 142import { MyInfo } from './file1'; 143const person: MyInfo = { 144 age: 20, 145 address: { 146 i: "shanghai" 147 } 148} 149``` 150 151**问题原因** 152 153`-keep`选项保留`file1.ts`文件时,`file1.ts`中代码不会被混淆。对于导出属性(如address)所属类型内的属性,不会被自动收集在属性白名单中。因此,该类型内的属性在其他文件中被使用时,会被混淆。 154 155**解决方案** 156 157方案一:使用`interface`定义该属性的类型,并使用`export`进行导出,这样该属性会被自动被收集到属性白名单中。示例如下: 158 159``` 160// file1.ts 161export interface AddressType { 162 city1: string; 163} 164export interface MyInfo { 165 age: number; 166 address: AddressType; 167} 168``` 169 170方案二:使用`-keep-property-name`选项,将未直接导出的类型内的属性配置到属性白名单中。示例如下: 171 172``` 173-keep-property-name 174city1 175``` 176 177### 未开启-enable-string-property-obfuscation,字符串字面量属性名却被混淆 178 179**问题现象** 180 181``` 182// 混淆前 183person["age"] = 22; 184``` 185 186``` 187// 混淆后 188person["b"] = 22; 189``` 190 191**问题原因** 192 193主工程的依赖模块中开启了开启了字符串属性名混淆,其混淆规则导出合并至主模块中。 194 195**解决方案** 196 197方案一:确认是否有依赖的模块开启了字符串属性名混淆,若开启了,则会影响主工程,需将其关闭。 198方案二:若工程复杂无法找到开启了该混淆配置选项的模块,可以将属性名直接配置到白名单中。 199 200## 导入导出名称不一致的问题 201 202### 动态导入某个类,类的定义处被混淆,调用处未被混淆 203 204**问题现象** 205 206当不开启`-enable-property-obfuscation`,动态导入某个类时,类的定义处被混淆,调用处未被混淆,导致调用处报错。 207 208``` 209// 混淆前 210// file1.ts 211export class Test1 {} 212// file2.ts 213let mytest = (await import('./file1')).Test1; 214``` 215 216``` 217// 混淆后 218// file1.ts 219export class w1 {} 220// file2.ts 221let mytest = (await import('./file1')).Test1; 222``` 223 224**问题原因** 225 226导出的类 "Test1" 是一个顶层作用域名,当 "Test1" 被动态使用时,它是一个属性。因为没有开启`-enable-property-obfuscation`选项,所以名称混淆了,但属性没有混淆。 227 228**解决方案** 229 230使用`-keep-global-name`选项将 "Test1" 配置到白名单。 231 232### 导出namespace中的方法时,该方法定义处被混淆,调用处未被混淆 233 234**问题现象** 235 236当未开启`-enable-property-obfuscation`选项,导出namespace中的方法时,该方法定义处被混淆,调用处未被混淆,导致调用处报错。 237 238``` 239// 混淆前 240//file1.ts 241export namespace ns1 { 242 export class person1 {} 243} 244//file2.ts 245import {ns1} from './file1'; 246let person1 = new ns1.person1(); 247``` 248 249``` 250// 混淆后 251//file1.ts 252export namespace a3 { 253 export class b2 {} 254} 255//file2.ts 256import {a3} from './file1'; 257let person1 = new a3.person1(); 258``` 259 260**问题原因** 261 262namespace里的 "person1" 属于export元素,当通过 "ns1.person1" 调用时,它被视为一个属性。由于未开`-enable-property-obfuscation`选项,导致在使用时未对其进行混淆。 263 264**解决方案** 265 266方案一:开启`-enable-property-obfuscation`选项。 267方案二:将namespace里导出的方法使用`-keep-global-name`选项添加到白名单。 268 269## 模块间相互依赖的问题 270 271### 主模块依赖HSP模块时,HSP模块导出接口被错误混淆问题 272 273**问题现象** 274 275主模块中,使用的HSP导出接口被错误的混淆。 276 277``` 278//混淆前 279import { MyHspClass } from "myhsp"; 280new MyHspClass().myHspMethod(); 281``` 282 283``` 284//混淆后 285import { t } from "@normalized:N&myhsp&&myhsp/Index&"; 286new t().a1(); 287``` 288 289**问题原因** 290 291当开启-enable-export-obfuscation和-enable-toplevel-obfuscation时,主模块调用其他模块方法时涉及的方法名称混淆情况如下: 292 293| 主模块 | 依赖模块 | 导入与导出的名称混淆情况 | 294| ------- | ------- | ----------------------------| 295| HAP/HSP | HSP | HSP和主模块是独立编译的,混淆后名称会不一致,因此都需要配置白名单。 | 296| HAP/HSP | 本地HAR | 本地HAR与主模块一起编译,混淆后名称一致。 | 297| HAP/HSP | 三方库 | 三方库中导出的名称及其属性会被收集到白名单,因此导入和导出时都不会被混淆。 | 298 299**解决方案** 300 301为了让其他模块能够正常调用HSP模块的方法,需要在混淆配置中添加白名单。由于主模块和HSP模块需要保持相同的白名单配置,建议采用以下步骤: 302 3031. 在HSP模块的混淆配置文件(如 hsp-white-list.txt)中添加白名单。 3042. 在依赖HSP的其他模块的混淆配置中,通过files字段引入该配置文件。 305这样可以确保白名单配置的一致性,避免重复维护。配置方法参考下图: 306 307 308 309### HAP与HSP依赖相同的本地源码HAR模块,单例功能异常/接口调用失败 310 311**问题现象** 312 313* 若开启文件名混淆,会出现以下问题: 314 * 问题一:单例功能异常问题。原因是HAP与HSP独立执行构建与混淆流程,本地源码HAR模块在HAP与HSP的包中可能会出现相同的文件名被混淆成不同文件名的情况。 315 * 问题二:接口调用失败问题。原因是HAP与HSP独立执行构建与混淆流程,本地源码HAR模块在HAP与HSP的包中可能会出现不同的文件名被混淆成相同的文件名的情况。 316* 若开启`-enable-export-obfuscation`和`-enable-toplevel-obfuscation`选项,在应用运行时会出现加载接口失败的问题。 317 318**问题原因** 319 320HAP与HSP独立执行构建与混淆流程,本地源码HAR模块中暴露的接口在HAP与HSP中被混淆成不同的名称。 321 322**解决方案** 323 324方案一:将HAP与HSP共同依赖的本地源码HAR改造为[字节码HAR](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-hvigor-build-har-V5#section179161312181613),这样此HAR在被依赖时不会被二次混淆。 325方案二:将HAP与HSP共同依赖的本地源码HAR以[release模式构建打包](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-hvigor-build-har-V5#section19788284410),这样此HAR在被依赖时,其文件名与对外接口不会被混淆。