逆向防护:代码混淆与完整性校验机制(58)
针对鸿蒙(HarmonyOS)应用的逆向防护,构建“代码混淆”与“完整性校验”双重防线是保障应用安全的核心。
一、 代码混淆机制(Obfuscation)
代码混淆通过增加代码的复杂性与模糊性,防止他人轻易复制、窃取核心算法,从而大幅提高逆向工程的门槛。
1. 核心防护作用
- 保护知识产权:防止他人轻易复制和窃取软件代码,保护核心业务逻辑。
- 防止逆向工程:增加攻击者分析软件工作原理和实现细节的难度,保护应用免受恶意修改或破坏。
- 提高安全性:减少代码中的漏洞暴露,增加攻击者利用漏洞的难度。
- 降低反盗版和欺诈风险:增加攻击者破解软件许可验证系统或绕过付费机制的难度。
2. 开启条件与配置
从 DevEco Studio 4.0 Beta1 开始,hvigor 插件提供了代码混淆功能。开启需满足以下条件:
- 工程为 Stage 模型。
- 处于 Release 编译模式下(Debug 模式会直接打包源码,不进行混淆)。
- 在模块的
build-profile.json5文件中开启混淆配置:
"arkOptions": {
"obfuscation": {
"ruleOptions": {
"enable": true, // 默认为 false,需手动开启
"files": [
"./obfuscation-rules.txt"
]
}
}
}
3. 混淆能力与规则
在 obfuscation-rules.txt 中,开发者可以配置具体的混淆策略。从 DevEco Studio 5.0.3.600 版本起,系统默认启用了四项推荐的混淆选项,以提供更全面的保护:
-enable-property-obfuscation:混淆属性名称。-enable-toplevel-obfuscation:混淆全局作用域中的名称。-enable-filename-obfuscation:混淆文件名。-enable-export-obfuscation:混淆导出名称。
二、 完整性校验机制(Integrity Verification)
完整性校验是防止应用被二次打包、篡改代码或注入恶意逻辑的关键屏障。鸿蒙系统在安装和启动应用时,会执行严格的签名与完整性校验流程。
1. 核心校验流程
鸿蒙的签名校验主要分为三个关键步骤:
- SignatureSchemeBlock 区校验:校验 HAP 包中签名块的证书链完整性。系统会利用内置的根证书,逐级验证中间证书与叶子证书的签名,确保应用来源可信。
- Profile 校验和解析:验证 Profile 文件的签名,判断其类型,并校验设备的 UDID 是否在允许的安装来源列表内(防止调试包被非法安装到生产环境)。
- HAP 包完整性校验:计算 HAP 包中所有文件的哈希值,并与签名清单文件(如
.MF文件)中记录的 SHA-256 哈希进行比对。若发现任何不一致,即判定为内容被篡改,拒绝安装或运行。
2. 应用层增强检测(Device Attestation)
除了系统底层的安装校验,开发者还可以在应用运行时主动调用系统完整性检测接口,进一步防范运行环境风险。检测结果通常以 JWS(JSON Web Signature)格式返回,开发者需在服务端验证其合法性:
- 解析 JWS 的 Header、Payload 和 Signature,并使用 Root CA 验证证书链。
- 校验 Payload 中的
hapBundleName和hapCertificateSha256,确保应用身份未被伪造。 - 根据
basicIntegrity字段判断设备环境。若为false,detail字段会指出具体风险原因,如jailbreak(越狱)、emulator(模拟器)、attack(设备被攻击)或unlock(Bootloader 被解锁),应用可据此拒绝提供敏感服务。
1. 应用层防篡改:校验应用签名与完整性
在应用启动时,主动获取自身的签名证书哈希,并与预期的安全哈希值进行比对,防止应用被二次打包或注入恶意代码。
import { bundleManager } from '@kit.AbilityKit';
import { cryptoFramework } from '@kit.CryptoArchitectureKit';
// 场景:启动时校验应用签名,防止被二次打包篡改
async function verifyAppIntegrity(expectedHash: string): Promise<boolean> {
try {
// 1. 获取当前应用的证书信息
const certInfo = await bundleManager.getBundleCertInfo('com.example.myapp');
const certData = certInfo.certificate; // 获取证书内容
// 2. 计算证书的 SHA-256 哈希值
const hashAlg = cryptoFramework.createHash('SHA256');
await hashAlg.update(new cryptoFramework.DataBlob(certData));
const hashResult = await hashAlg.doFinal();
// 3. 将计算出的哈希转为字符串并与预期值比对
const currentHash = bufferToHex(hashResult.data);
if (currentHash !== expectedHash) {
console.error('应用完整性校验失败:签名被篡改!');
return false;
}
return true;
} catch (err) {
console.error('完整性校验异常:', err);
return false;
}
}
2. 运行时环境检测:调用 Device Security Kit
利用鸿蒙官方的设备安全服务,检测当前设备是否存在越狱、Root、模拟器或 Bootloader 解锁等高危环境。
import { deviceSecurityKit } from '@kit.DeviceSecurityKit';
// 场景:检测运行环境是否安全
async function checkDeviceEnvironment() {
try {
// 调用系统完整性增强检测接口
const result = await deviceSecurityKit.checkSysIntegrityEnhanced({
nonce: 'unique-nonce-string' // 传入随机字符串防重放攻击
});
// 解析返回的 JWS 字符串
const jwsPayload = parseJWSPayload(result.jwsToken);
if (!jwsPayload.basicIntegrity) {
console.warn('设备环境存在风险:', jwsPayload.detail);
// 根据 detail 字段进行针对性处理
if (jwsPayload.detail.includes('jailbreak')) {
console.error('设备已越狱,拒绝提供敏感服务');
} else if (jwsPayload.detail.includes('emulator')) {
console.error('检测到模拟器运行');
}
return false;
}
return true;
} catch (err) {
console.error('环境检测失败:', err);
return false;
}
}
3. 服务端安全校验:验证 JWS 签名与证书链
客户端返回的检测结果必须交由服务端进行严格校验,防止攻击者绕过客户端直接伪造请求。
// 场景:在服务端(Node.js 示例)验证客户端传来的 JWS 凭证
import jwt from 'jsonwebtoken';
import https from 'https';
async function verifyDeviceAttestation(jwsToken: string) {
try {
// 1. 解析 JWS 获取 Header 中的证书链 (x5c)
const decoded = jwt.decode(jwsToken, { complete: true });
const certChain = decoded.header.x5c;
// 2. 验证证书链:使用 Root CA 验证证书链的合法性
// 校验 x5c[0] 的 CN 是否为 'Harmony OS Device Attestation Service'
const isValidCertChain = await validateCertChain(certChain);
if (!isValidCertChain) {
throw new Error('证书链验证失败,凭证可能被伪造');
}
// 3. 使用证书链中的公钥验证 JWS 签名
const payload = jwt.verify(jwsToken, certChain[0], { algorithms: ['ES256'] });
// 4. 校验业务字段
if (payload.hapBundleName !== 'com.example.myapp') {
throw new Error('应用包名不匹配');
}
console.info('设备环境校验通过,安全等级:', payload.basicIntegrity);
return payload;
} catch (err) {
console.error('服务端 JWS 校验失败:', err);
return null;
}
}
三、 应用加密与分发级防护
除了开发阶段的混淆,鸿蒙系统还在分发和运行阶段提供了系统级的代码保护机制,防止静态分析。
- 应用市场端到端加密:开发者在上架应用时可选择开启加密。经过应用市场审核与加密后的安装包,在设备上安装落盘后仍处于受保护状态。应用启动时,系统会逐页按需解密,解密内容仅存在于内存中,不会写入磁盘,从而有效防止攻击者通过静态分析逆向破解应用代码结构。
- 强制代码签名与热更新防护:为防止恶意应用绕过审核,鸿蒙引入了强制代码签名机制。不仅在加载时进行合法性检查,还在应用代码执行前通过哈希校验验证内容的完整性,有效拦截通过热更新机制加载未审核代码的风险。
1. 上架阶段:开启应用市场端到端加密
应用加密特性在应用上架时配置,对开发者透明且无需修改业务代码。应用包在应用市场审核通过后会被加密,安装落盘后仍受保护,启动时由系统内核按需解密执行。
// 场景:在 AppGallery Connect 后台发布应用时的配置步骤
// 1. 登录 AppGallery Connect,进入应用的发布页面。
// 2. 在“选取待发布的软件包”步骤中,勾选“应用加密”选项。
// 3. 提交审核。审核通过后,应用市场会对包体中的关键资产(如 .abc 文件)进行端到端加密。
// 注意事项:
// - 加密后的应用在启动和运行过程中可能会有轻微的性能开销,包体大小也会略微增加。
// - 当前 ArkTS 卡片服务对应的 widgets.abc 文件暂不支持加密服务。
// - 由于应用加密特性对应用冷启动时延有影响,目前部分版本通过白名单方式受限开放,需提前申请。
2. 运行时防护:应用完整性自校验
为了防止应用被二次打包、篡改或注入恶意逻辑,开发者可以在应用启动时主动校验自身 HAP 包的完整性。
import { bundleManager } from '@kit.AbilityKit';
import { cryptoFramework } from '@kit.CryptoArchitectureKit';
// 场景:应用启动时校验自身完整性,防止被二次打包篡改
async function verifyAppIntegrity(expectedHapHash: string): Promise<boolean> {
try {
// 1. 获取当前应用的 HAP 包信息
const bundleInfo = await bundleManager.getBundleInfoForSelf(
bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_HAP
);
// 2. 计算当前 HAP 包的 SHA-256 哈希值
// 注意:此处为伪代码逻辑,实际需读取 HAP 文件流进行哈希计算
const hashAlg = cryptoFramework.createHash('SHA256');
// await hashAlg.update(hapFileData);
const hashResult = await hashAlg.doFinal();
// 3. 将计算出的哈希转为字符串并与预期值比对
const currentHash = bufferToHex(hashResult.data);
if (currentHash !== expectedHapHash) {
console.error('应用完整性校验失败:应用可能被篡改或二次打包!');
return false;
}
return true;
} catch (err) {
console.error('完整性校验异常:', err);
return false;
}
}
3. 运行时防护:防动态调试与热更新拦截
为防止攻击者通过动态调试或热更新机制加载未审核代码,需在运行时进行环境检测与内存保护。
import { process } from '@kit.ArkTS';
import { Debug } from '@kit.ArkTS';
// 场景:运行时防调试与防篡改检测
class AppSecurityGuard {
private static readonly CHECK_INTERVAL = 5000; // 每5秒检测一次
public static startMonitoring() {
setInterval(() => {
// 1. 检测是否被动态调试器附加
if (Debug.isAttached() || process.isDebugged()) {
console.error('检测到非法调试环境,应用即将退出');
AppSecurityGuard.secureWipeAndExit();
}
// 2. 可在此处增加 HAP 包哈希的定期校验
// 防止应用运行期间被内存注入或热更新篡改
}, AppSecurityGuard.CHECK_INTERVAL);
}
private static secureWipeAndExit() {
// 敏感数据内存擦除,防止内存转储泄露
// 实际场景中需对内存中的 Token、密钥等进行覆写清零
console.warn('正在执行安全擦除...');
process.exit(1); // 强制退出应用
}
}
四、 进阶代码混淆策略(控制流与数据混淆)
基础混淆仅重命名符号,高级攻击者仍可通过逻辑分析还原代码。针对高价值应用,需引入更深层的混淆技术:
- 控制流混淆(逻辑迷宫):将原本清晰的直线代码逻辑改造为复杂的结构。例如,插入“不透明谓词”和“虚假控制流”(如永远不会执行的代码块或假循环),并将真实逻辑拆分成多个基本块,通过复杂的跳转关系连接。这能极大延长逆向分析时间。
- 数据与字符串混淆(隐身术):明文字符串(如 API Key)极易被提取。通过数据混淆,将敏感字符串在编译时转化为加密的字节序列,仅在运行时首次访问时调用解密函数,且进程退出后自动清除内存。同时,对关键常量使用数学运算(如异或、加减法组合)进行等价替换,提升常量提取难度。
五、 核心资产的精细化访问控制
在防逆向的基础上,鸿蒙对应用运行时读取敏感数据(关键资产)实施了基于上下文的访问控制策略,即使设备被攻破也能限制数据泄露范围:
- 基于锁屏与认证状态的访问控制:
- 首次解锁后可访问:适用于普通社交应用,设备未解锁时关键资产不可访问,平衡了安全与便利。
- 解锁后可访问:适用于金融、银行类应用。只有在设备处于解锁状态且用户通过身份验证(如指纹、面容、PIN码)后才能访问。若设备丢失且未解锁,攻击者无法获取交易明细等核心数据。
- 基于锁屏密码设置状态:若设备未设置锁屏密码,应用可直接拒绝访问关键资产,防止设备在无基础保护时被轻易窃取数据。
- 基于所有权的严格隔离:每个关键资产都关联特定的所有者(应用)。不同应用之间的数据严格受限,即使社交应用被恶意攻击,攻击者也无法跨越沙箱获取银行应用中的用户登录凭据。
1. 基础访问控制:基于锁屏状态的策略
在存储敏感数据时,可通过设置 ACCESSIBILITY 属性来限制数据在何种锁屏状态下可被访问。
import { asset } from '@kit.AssetStoreKit';
import { util } from '@kit.ArkTS';
// 场景:存储普通社交应用的 Token,设置“首次解锁后可访问”
async function saveSocialToken(token: string) {
let attr: asset.AssetMap = new Map();
attr.set(asset.Tag.SECRET, new util.TextEncoder().encodeInto(token));
attr.set(asset.Tag.ALIAS, new util.TextEncoder().encodeInto('social_token'));
// 【关键】设置访问控制:设备首次解锁后即可访问,未解锁时不可读取
attr.set(asset.Tag.ACCESSIBILITY, asset.Accessibility.DEVICE_FIRST_UNLOCKED);
try {
await asset.add(attr);
console.info('社交 Token 存储成功,受锁屏状态保护');
} catch (err) {
console.error('存储失败:', err);
}
}
2. 高级访问控制:结合用户认证(指纹/人脸/PIN)
对于金融级核心资产,必须结合用户生物识别或 PIN 码认证,并设置认证有效期,实现“一次认证,多次访问”。
import { asset } from '@kit.AssetStoreKit';
import { userAuth } from '@kit.UserAuthKit';
import { util } from '@kit.ArkTS';
// 场景:存储银行卡号,要求必须通过指纹或PIN码认证,且认证有效期为 300 秒
async function saveBankCard(cardNumber: string) {
let attr: asset.AssetMap = new Map();
attr.set(asset.Tag.SECRET, new util.TextEncoder().encodeInto(cardNumber));
attr.set(asset.Tag.ALIAS, new util.TextEncoder().encodeInto('bank_card'));
// 【关键】设置认证类型:支持指纹或 PIN 码
attr.set(asset.Tag.AUTH_TYPE, userAuth.UserAuthType.FINGERPRINT | userAuth.UserAuthType.PIN);
// 【关键】设置认证有效期:300秒内再次访问无需重复认证
attr.set(asset.Tag.AUTH_VALIDITY_PERIOD, 300);
try {
await asset.add(attr);
console.info('银行卡号存储成功,已绑定强认证策略');
} catch (err) {
console.error('存储失败:', err);
}
}
// 场景:读取银行卡号(需经历 挑战值生成 -> 用户认证 -> 携带令牌查询 三步)
async function queryBankCard() {
let alias = new util.TextEncoder().encodeInto('bank_card');
// 1. 预处理:获取系统生成的挑战值 (Challenge)
let preQueryMap: asset.AssetMap = new Map();
preQueryMap.set(asset.Tag.ALIAS, alias);
preQueryMap.set(asset.Tag.AUTH_TYPE, userAuth.UserAuthType.FINGERPRINT | userAuth.UserAuthType.PIN);
const challenge = await asset.preQuery(preQueryMap);
// 2. 拉起系统认证框,获取认证令牌 (Auth Token)
const authParam: userAuth.AuthParam = {
challenge: challenge,
authType: [userAuth.UserAuthType.FINGERPRINT, userAuth.UserAuthType.PIN],
authTrustLevel: userAuth.AuthTrustLevel.ATL1,
};
const widgetParam: userAuth.WidgetParam = { title: '请验证身份以查看银行卡号' };
const userAuthInstance = userAuth.getUserAuthInstance(authParam, widgetParam);
userAuthInstance.on('result', async (result) => {
if (result.result === 12500000) { // 认证成功
// 3. 携带 Challenge 和 Token 查询真实数据
let queryMap: asset.AssetMap = new Map();
queryMap.set(asset.Tag.ALIAS, alias);
queryMap.set(asset.Tag.AUTH_CHALLENGE, challenge);
queryMap.set(asset.Tag.AUTH_TOKEN, result.token);
queryMap.set(asset.Tag.RETURN_TYPE, asset.ReturnType.ALL);
const assets = await asset.query(queryMap);
console.info('成功获取银行卡号:', new util.TextDecoder().decodeWithStream(assets[0].get(asset.Tag.SECRET)));
// 4. 清除挑战值(必须执行,否则后续查询会报错)
let postMap: asset.AssetMap = new Map();
postMap.set(asset.Tag.AUTH_CHALLENGE, challenge);
await asset.postQuery(postMap);
}
});
userAuthInstance.start();
}
3. 跨应用数据共享:基于群组的访问控制
在鸿蒙生态中,同一开发者开发的多个应用(如主应用与手表端应用)可以通过配置群组来安全地共享核心资产。
import { asset } from '@kit.AssetStoreKit';
import { util } from '@kit.ArkTS';
// 场景:将用户登录凭证存入群组,允许同开发者的其他应用读取
async function saveSharedCredential(credential: string) {
let attr: asset.AssetMap = new Map();
attr.set(asset.Tag.SECRET, new util.TextEncoder().encodeInto(credential));
attr.set(asset.Tag.ALIAS, new util.TextEncoder().encodeInto('user_credential'));
// 【关键】开启群组共享(需在 module.json5 中提前配置好 groupId)
// 注意:IS_PERSISTENT 为 true 的资产不允许设置为群组共享
attr.set(asset.Tag.IS_SHARED, true);
try {
await asset.add(attr);
console.info('凭证已存入群组,同生态应用可安全读取');
} catch (err) {
console.error('群组存储失败:', err);
}
}
六、 运行时环境检测(Safety Detect)
在应用运行过程中,需主动探测设备的运行环境是否可信,以阻断高危操作:
- 系统完整性检测(SysIntegrity):利用设备上的可信执行环境(TEE),检查用户设备是否被 Root、Bootloader 是否被解锁或权限是否被提升。若检测到异常,应用应限制在线支付等敏感操作。
- 应用与用户行为检测:通过
AppsCheck获取设备上的恶意应用列表以评估风险;通过UserDetect识别虚假用户(如利用验证码防止批量注册和爬虫),防止接口被恶意滥用。
七、 数据完整性与防篡改保护
在数据的存储和传输过程中,除了加密,还必须确保数据未被篡改:
- 哈希校验机制:系统采用哈希算法将关键资产转换为固定长度的哈希值并一同存储。读取时重新计算并比对,若不一致则说明数据已损坏或被篡改,系统将拒绝使用该数据。
- 应用层完整性自校验:在应用启动时,主动计算自身 HAP 包的 SHA256 哈希值,并与预期的硬编码哈希值进行比对。若发现被篡改(如二次打包),直接退出应用以防止恶意逻辑执行。
更多推荐




所有评论(0)