在字节码级别的名称混淆过程中,通常会修改类名、方法名和字段名等元信息,以增强代码的安全性和反编译难度。然而,一旦程序中使用了反射机制,混淆过程就可能遇到问题。反射通常依赖于字符串形式的类名、方法名或字段名来动态获取类对象、访问成员或调用方法。若这些名称在混淆过程中被修改,原有的反射逻辑将无法正确执行,导致运行时错误或功能异常。
因此,在进行名称混淆之前,必须明确目标平台所提供的反射能力及其实现机制,从而制定合理的混淆策略。
鸿蒙系统中的反射机制特点
鸿蒙系统(HarmonyOS)所提供的反射功能与常见的反射有所不同,算是一种伪反射机制。它并不真正改变原有类的结构,也不支持通过字符串类名动态加载类、遍历类的成员信息或根据方法名称动态调用方法等典型反射操作。
以下是一段在鸿蒙ABC开发中典型的反射使用示例:
class ReflectionTest {
value: number = 42;
fun() {
console.log('fun print')
}
}
function TestFun(params: number) {
console.log('TestFun print, par:' + params)
}
exportfunction reflectionTest(): string {
let aClass = new ReflectionTest();
Reflect.set(aClass, 'key', 'keyvalue');
let str: string = Reflect.get(aClass, 'key');
console.log(str)
Reflect.set(aClass, 'keyFun', () => {
console.log('lan print.')
});
let lanFun: Function = Reflect.get(aClass, 'keyFun');
lanFun();
Reflect.set(aClass, 'keyFun2', TestFun);
let globeFun: Function = Reflect.get(aClass, 'keyFun2');
globeFun(4);
Reflect.set(aClass, 'keyFun3', aClass.fun);
let classFun: Function = Reflect.get(aClass, 'keyFun3');
classFun();
return str;
}
从上述代码可以看出,鸿蒙中的 Reflect 对象仅支持基本的属性设置(set)和获取(get)操作。这些属性并未真正注入到目标类的结构之中,而是作为一种附加在对象上的动态元信息存在。因此,该机制不会干扰原有类的成员布局或方法表结构。
名称混淆的应对策略
基于鸿蒙反射机制的上述特点,在实施名称混淆时,无需针对使用了反射的类做特殊处理。由于鸿蒙反射不依赖于字符串形式的类名或方法名进行类结构查询或方法调用,因此即使类名、方法名等被混淆,也不会影响 Reflect.get 和 Reflect.set 的正常功能。
然而,混淆过程中仍需注意以下几类特殊情况:
- 入口类与主页面需排除混淆:程序的入口类、UI 主页面等核心类型通常会在配置文件中被明文引用。若这些名称被混淆,系统将无法正确识别和加载相应组件,导致程序启动失败。因此这类元素应加入混淆排除列表。
- 字符串常量需审慎处理:若代码中存在与类名、方法名相同的字符串常量(如日志输出、动态加载逻辑等),混淆时需能够识别并避免修改这些字符串内容,否则会影响程序的显示逻辑和功能正确性。
总结
在鸿蒙ABC开发中,由于其反射机制并不依赖于传统的类结构元信息,名称混淆的实施相对更为直接。开发者可以放心对大部分代码进行混淆处理,只需确保程序入口及明文配置所引用的名称不被更改,同时避免修改可能与名称相关的字符串常量。
开启名称混淆后的应用,可以增加攻击者逆向软件的难度,防止软件被轻易破解。如果想要专业的软件加固方案,可以尝试一些专业的工具,如 Virbox Protector,它提供 Native, Java, Android, .Net 等多种应用类型的加固方案,具备代码虚拟化、高级混淆引擎、加密与数据保护、反调试与反注入等多重高级安全功能,降低软件被破解的风险。值得一提的是,它也即将支持鸿蒙 Hap 应用的加固保护。