概述
代码混淆是一种软件安全技术,旨在增加代码的复杂性和模糊性,从而增加攻击者分析和理解代码的难度。代码混淆有以下几个方面的作用:
- 保护知识产权:代码混淆可以防止他人轻易地复制和窃取您的软件代码。通过混淆代码,您可以使其难以被逆向工程或复制,从而保护您的知识产权。
- 防止逆向工程:逆向工程是指分析软件并尝试了解其工作原理和实现细节的过程。代码混淆可以增加逆向工程的难度,使攻击者难以理解和修改您的代码,从而保护您的应用程序免受恶意修改或破坏。
- 提高安全性:通过混淆代码,可以减少代码中的漏洞和安全风险。即使存在漏洞,混淆的代码也会增加攻击者利用漏洞的难度,从而增强应用程序的安全性。
- 降低反盗版和欺诈风险:通过混淆代码,可以降低应用程序被盗版或篡改的风险。攻击者往往会尝试破解软件的许可验证系统或者修改代码以绕过付费机制,而混淆的代码会增加这些行为的难度,从而减少盗版和欺诈。
针对工程源码的混淆可以提高工程被破解攻击的难度,缩短代码的类和成员的名称,减小应用的大小。
混淆开启
从DevEco Studio版本:4.0 Beta1开始hvigor插件提供代码混淆功能。混淆开启条件如下:
- 工程为Stage模型
- 在Release编译模式下
- 模块build-profile.json5文件中开启混淆配置
"arkOptions": {
"obfuscation": {
"ruleOptions": {
"enable": true // 配置true,即可开启代码混淆功能
}
}
}
注意
enable默认为false,默认不开启代码混淆功能。
满足上述开启混淆条件后,选择目标模块点击Build -> Make Module选项开始编译。
如果你的工程或者模块是Static Library,那么该工程或模块是一个HAR。
HAR类型有如下两种构造方式:
DevEco Studio在Debug模式构建HAR,直接将源码打包
DevEco Studio在Release模式构建HAR,会对代码进行编译、混淆及压缩处理
若需要对发布的HAR代码资产进行保护时,建议在release模式下开启混淆能力。
图1 DevEco Studio选择release编译模式
图2 DevEco Studio指定模块编译
混淆配置能力
编译选项
上述编译过程中混淆开启的是默认配置,仅混淆了局部变量。若想混淆更多名称,需要在模块中的混淆配置文件obfuscation-rules.txt添加混淆选项。
混淆配置
在工程的每个模块下都能找到 build-profile.json5 文件,如下图所示,可以在该文件中配置当前模块是否开启混淆以及开启混淆时按照哪个混淆配置文件进行混淆。
图3 编译配置文件
新建工程时,每个模块下都有obfuscation-rules.txt文件,混淆开启时会加载obfuscation-rules.txt中的配置,完成指定的混淆功能。
图4 混淆配置文件
上图中,obfuscation-rules.txt中添加了-enable-property-obfuscation、-enable-toplevel-obfuscation开关,这代表打开了属性混淆和顶层作用域名称混淆。
DevEco Studio混淆现有选项及功能描述如下:
混淆自定义选项名称功能简述混淆选项-disable-obfuscation关闭混淆-enable-property-obfuscation属性混淆-enable-toplevel-obfuscation顶层作用域名称混淆-enable-filename-obfuscation文件名混淆-enable-export-obfuscationexport导出名称与属性混淆-compact代码压缩-remove-log删除console.*方法-print-namecache filepath指定路径输出namecache.json文件及内容-apply-namecache filepath复用指定的名称缓存文件-remove-comments删除注释保留选项-keep-property-name保留属性白名单-keep-global-name保留顶层作用域名称白名单-keep-file-name保留文件名白名单-keep-comments保留某个类的JsDoc注释-keep-dts filepath读取指定dts文件中的名称作为白名单
混淆优化建议
开发人员混淆工程,查看缓存文件或者SDK中文件发现大量源码名称未混淆,原因有两类:
- 混淆选项开启较少;可开启-enable-property-obfuscation、-enable-toplevel-obfuscation、-enable-export-obfuscation、-enable-filename-obfuscation选项来混淆更多的源码名称
- 源码名称与系统白名单、语言白名单等重名;可将源码名称添加后缀来避开白名单
混淆规则合并策略
工程的主模块(一般为entry)会依赖工程中的其他模块,同时也可能依赖一些远程har包,这使得我们在编译工程时难以弄清工程中的每个模块是按什么混淆配置进行混淆的。
实际上,当我们编译整个工程时会加载主模块的obfuscation-rules.txt、依赖的HAR中consumer-rules.txt和远程HAR包的obfuscation.txt三类混淆配置文件,进行规则合并获取此次编译过程的混淆规则。
HAR模块的build-profile.json5中consumerFiles配置了共享混淆配置文件,该文件生效条件:
- HAR模块被依赖
- HAR模块开启混淆
图5 HAR模块的共享混淆配置文件consumer-rules.txt
图6 远程HAR包中obfuscation.txt文件
合并规则(以下面四个选项为例,其它选项合并规则与其一致):
let `listRules` 表示上面提到的所有混淆规则文件的列表
let finalRule = {
disableObfuscation: false,
enablePropertyObfuscation: false,
keepPropertyName: [],
printNamecache: 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 -keep-property-name:
finalRule.keepPropertyName.push(#{指定的名称});
continue;
case -print-namecache:
finalRule.printNamecache = #{指定的路径名};
case -keep-dts:
finalRule.keepDts.push(#{指定的路径});
continue;
}
end-for
end-for
- 布尔型选项使用或运算进行合并,即存在配置文件中开启此选项,则此选项生效。
- 名单类型中的白名单列表会进行合并
- 输出与复用nameCache文件路径会被覆盖
- -keep-dts文件中的白名单分别被收集,然后合并白名单
查看混淆结果
开发人员可以在编译模块下的build目录中找到编译及混淆生成的缓存文件,以及混淆的名称映射表及系统API白名单文件。
- 源码编译及混淆缓存文件目录:build/[…]/release/模块名
- 混淆名称映射表及系统API白名单目录:build/[…]/release/obfuscation- 名称映射表文件:nameCache.json,该文件记录了源码名称混淆的映射关系- 系统API白名单文件:systemApiCache.json,该文件记录了SDK中的接口与属性名称,与其重名的源码不会被混淆图7 DevEco Studio编译产物与缓存文件
调试
经过混淆工具处理后的代码名称会发生更改,这可能会使运行时crash堆栈日志难以理解,因为堆栈与源代码不完全一致。如果未保留调试信息,就可能会出现因行号及名称更改无法定位问题的情况。此外,开启-enable-property-obfuscation、-enable-toplevel-obfuscation等选项,代码混淆可能会引发运行时crash或者是功能性错误,需要开发人员还原报错堆栈来排查定位哪些名称与属性需要配置白名单来保障功能正常。
函数调用栈还原
程序出现运行时crash会打印报错堆栈,堆栈行号及名称对应于编译及混淆后的缓存文件。开发人员可使用DevEco Studio命令工具Command Line Tools中的hstack插件来还原为源码堆栈。如需支持对应用的堆栈进行还原,需要确保保留足够的信息以进行还原。应用每次编译时都会生成sourceMaps.map,其中存储源代码与编译代码对应位置映射的信息。若开启混淆还会生成nameCache.json文件,其中存储源代码与混淆代码对应名称映射的信息。
DevEco Studio会将sourceMaps.map文件保存在build/default/cache/default/default@CompileArkTS/esmodule/release目录中,将nameCache.json文件保存在build/default/cache/default/default@CompileArkTS/esmodule/release/obfuscation目录中。Hstack插件读取sourceMaps.map文件中的位置映射关系与nameCache.json混淆名称映射关系实现将release应用crash堆栈还原为源码对应堆栈
反混淆工具hstack
hstack需要将Node.js配置到环境变量中
常见问题
- 混淆后出现类似hvigor ERROR: Could not resolve “…/…/…/…/…/z2undefined” from "oh_modules/.ohpm/${module_name}/oh_modules/KSAdSDK/src/main/ets/q/p2/v2/w2/x2.js"的问题导入导出语句中为文件夹路径,而不是文件路径时,混淆文件名时会出现undefined字符串。由于匹配文件后缀未匹配到,返回undefined。然后在拼接字符串时,undefined被拼接成了文件路径。请更新版本至4.1release版本。
- HAR开启混淆后资源ID为-1,ResourceManager等通过ID获取资源的API不再生效。参考[HAR],可以尝试使用resourceManager.getStringByName。
使用第三方加固
在HarmonyOS提供的代码混淆能力之外,开发者还可以使用第三方安全厂商提供的高级混淆和加固能力。一些安全加固厂商,已经启动HarmonyOS开发,开发者可以按需使用此类安全厂商提供的服务。这一部分由开发者与三方安全厂商自行沟通合作方式和范围,本文不做详细说明。具体的官方与三方代码混淆能力的关系如下:
特性特性描述HarmonyOS三方名称混淆混淆类、字段、属性、方法和Native库的名字,包括资源名、资源文件名、 asset 文件和资源 XML 属性。√√控制混淆混淆方法内的控制流以防御自动或手动代码分析,包括虚假控制流和控制流扁平化。×√指令替换通过将简单的算术和逻辑表达式转换为难以分析的代码来保护专有公式。×√数据混淆加密敏感字符串,以防止通过尝试搜索的黑客攻击,也用来加密类、 asset 文件、资源文件和 Native 库×√代码虚拟化转换方法实现为随机生成虚拟机的指令序列×√调用隐藏为访问敏感的 APIs 添加反射,比如用于签名校验和密码操作的标准APIs×√移除日志代码移除 logging 、调试和测试代码,以阻止任何利用此信息的企图×√
需要注意的是,由于HarmonyOS代码签名、应用加密等安全机制的限制,以及应用市场上架审核纯净安全的要求,三方加固厂商提供的安全加固内容需要满足以下四点要求:
1、不允许隐藏敏感系统API调用,需要让审核人员清晰的看到应用特性;
2、不允许混淆非自研的SDK,SDK应该由SDK厂商自行进行混淆保护,如果非自研SDK被混淆会影响应用市场审核相关SDK的指纹信息;
3、通过三方安全加固的应用,需要保证被加固的应用程序不能含有恶意行为,避免对生态造成影响,此条要求作为约束性条款,不遵循可能导致应用被下架;
4、不允许使用三方虚拟机,HarmonyOS系统通过代码签名等机制限制动态加载代码,此方法可能让应用无法正常运行。
版权归原作者 沙人防火历飞雨 所有, 如有侵权,请联系我们删除。