灯光模拟HarmonyOS应用实战-76-选了release也不等于代码已混淆:从enable=false建立发布配置证据链

工程能选择 release,规则文件里也写着属性名、顶层名、文件名和导出名混淆选项,这两件事很容易让人得出“发布包已经混淆”的结论。继续查看 entry/build-profile.json5 才会发现,真正控制规则是否启用的 ruleOptions.enable 当前为 false。规则文件存在并被列入 files,不代表这些规则已经生效。

发布配置最怕把“名称”“文件”“开关”“任务参数”和“产物”混成一层证据。更稳的做法是建立一条可以回读的链:声明发布意图,核对模块开关,确认构建确实使用 release,保存规则摘要,再从构建报告和目标产物验证效果。任何一层缺失,都只能报告到已经证明的那一层。

release混淆证据链封面

本文依据当前 The_kemusan 的根配置、entry/HSP/HAR 模块配置与规则文件展开,不公开签名敏感字段,也不宣称已经重新构建或检查某个发布包。

一、根配置只说明工程拥有release构建模式

build-profile.json5buildModeSet 同时声明 debugrelease。这证明构建系统认识这两个模式名称,但没有说明某次命令选择了哪一个,更没有说明某个模块的混淆选项已打开。

{
  "app": {
    "buildModeSet": [
      { "name": "debug" },
      { "name": "release" }
    ]
  }
}

同一根配置还声明 entry、libraryHSP 和 libraryHAR 三个模块。每个模块都有自己的 build-profile.json5,所以根级存在 release 不会自动覆盖各模块的 ArkTS 选项。发布审计必须逐个查看实际被打入产品的模块。

证据能证明不能证明
buildModeSet 有 release工程支持该模式名当前任务确实用了 release
IDE 下拉选择 release当前界面选择意图命令、缓存和各模块最终输入一致
规则文件存在有候选规则文本规则主开关已启用
release 包文件存在曾产生某个文件文件来自当前源码和当前配置
包可安装基本打包、签名链可能可用名称已经按规则变换

二、entry当前主开关明确是false

entry/build-profile.json5 的 release 配置在 arkOptions.obfuscation.ruleOptions 下设置了 enable: false,同时列出 ./obfuscation-rules.txt

{
  "buildOptionSet": [
    {
      "name": "release",
      "arkOptions": {
        "obfuscation": {
          "ruleOptions": {
            "enable": false,
            "files": [
              "./obfuscation-rules.txt"
            ]
          }
        }
      }
    }
  ]
}

这里的决定性事实是 enable: falsefiles 表示规则文件路径已配置,主开关关闭时不能把它解释成规则已执行。至于 HarmonyOS 构建工具在当前 SDK 中如何记录具体选项,应以该版本构建报告和官方构建说明为准;本文只对源码配置做静态解读。

同文件的 resOptions.copyCodeResource.enable: false 控制的是代码资源复制选项,不是混淆主开关。两个字段都叫 enable,路径和职责不同,审计记录必须保存完整键路径。

三、规则文件内容不会越过主开关自行生效

entry 的 obfuscation-rules.txt 当前包含多个启用项:

-enable-property-obfuscation
-enable-toplevel-obfuscation
-enable-filename-obfuscation
-enable-export-obfuscation

这些行说明团队准备了较强的候选规则,但它们的存在不是运行证据。可以把关系写成一个简单判定:只有构建模式匹配、模块主开关打开、规则文件成功加载,候选规则才进入后续处理;最后还要从报告和产物确认实际效果。

发布意图到产物验证的分层流程

规则本身也需要风险审阅。属性名、导出名和文件名变化可能影响反射式访问、序列化字段、跨模块接口、动态加载或外部协议。不能为了得到“已混淆”状态直接把所有开关打开,再到运行阶段寻找崩溃。

四、三个模块目前都不能默认视为已启用

当前 libraryHSP/build-profile.json5libraryHAR/build-profile.json5 的 release ruleOptions.enable 同样为 false。HSP 还声明了 consumerFiles;这仍不改变主开关状态。

interface ModuleObfuscationObservation {
  moduleName: string;
  buildMode: string;
  ruleEnabled: boolean;
  ruleFiles: string[];
  consumerFiles: string[];
}

const currentObservations: ModuleObfuscationObservation[] = [
  {
    moduleName: 'entry',
    buildMode: 'release',
    ruleEnabled: false,
    ruleFiles: ['./obfuscation-rules.txt'],
    consumerFiles: []
  },
  {
    moduleName: 'libraryHSP',
    buildMode: 'release',
    ruleEnabled: false,
    ruleFiles: ['./obfuscation-rules.txt'],
    consumerFiles: ['./consumer-rules.txt']
  },
  {
    moduleName: 'libraryHAR',
    buildMode: 'release',
    ruleEnabled: false,
    ruleFiles: ['./obfuscation-rules.txt'],
    consumerFiles: ['./consumer-rules.txt']
  }
];

这是根据当前三个模块配置整理的观察模型,不是工程已有代码。它明确阻止“entry 打开即可代表整个应用”这样的推断。实际产品是否把 HSP/HAR 打入 entry,还要结合模块依赖和本次构建图核对;模块在根配置中声明,也不等于一定进入目标 HAP。

五、用ReleaseProtectionManifest声明发布意图

配置、任务、报告与产物证据结构

先把期望写成清单,能让配置差异在构建前暴露。清单不存口令、私钥、证书内容或本机绝对敏感路径。

interface ReleaseProtectionModule {
  moduleName: string;
  required: boolean;
  expectedRuleEnabled: boolean;
  expectedRuleFile: string;
  protectedExportsReviewed: boolean;
}

interface ReleaseProtectionManifest {
  manifestVersion: number;
  product: string;
  buildMode: 'release';
  modules: ReleaseProtectionModule[];
  approvedRuleDigest: string;
}

expectedRuleEnabled 表达产品意图,当前配置观察表达实现状态。二者不同就阻止发布准备继续。protectedExportsReviewed 要求跨模块 API、序列化字段和外部调用面已经审阅;它不能由脚本自动替业务负责人勾选。

approvedRuleDigest 保存规则文件内容摘要,避免审阅后规则又发生变化。摘要只能证明文件内容相同,不能证明工具已加载它,因此仍需后续任务和报告证据。

六、构建前静态门禁核对完整键路径

可以在构建前读取各模块 JSON5,输出一份不含敏感信息的配置报告。下面用已解析对象展示核心校验,正式脚本要使用能够正确处理 JSON5 的解析器,不能先粗暴删除注释和尾逗号。

interface ProtectionIssue {
  moduleName: string;
  code: string;
  configPath: string;
}

function compareProtectionIntent(
  expected: ReleaseProtectionModule,
  actual: ModuleObfuscationObservation
): ProtectionIssue[] {
  const issues: ProtectionIssue[] = [];
  if (actual.buildMode !== 'release') {
    issues.push({
      moduleName: actual.moduleName,
      code: 'WRONG_BUILD_MODE',
      configPath: 'buildOptionSet.name'
    });
  }
  if (actual.ruleEnabled !== expected.expectedRuleEnabled) {
    issues.push({
      moduleName: actual.moduleName,
      code: 'RULE_ENABLE_MISMATCH',
      configPath:
        'arkOptions.obfuscation.ruleOptions.enable'
    });
  }
  if (actual.ruleFiles.indexOf(
    expected.expectedRuleFile) < 0) {
    issues.push({
      moduleName: actual.moduleName,
      code: 'RULE_FILE_MISSING',
      configPath:
        'arkOptions.obfuscation.ruleOptions.files'
    });
  }
  return issues;
}

报告应该写 entry: RULE_ENABLE_MISMATCH,而不是笼统写“release 配置失败”。完整路径能防止把 copyCodeResource.enableruleOptions.enable 看混。若目标策略暂时明确要求关闭混淆,expectedRuleEnabled 可以为 false,报告就忠实记录“关闭符合意图”;不要把安全审计写成永远要求开启的单向脚本。

七、启用前先梳理必须保留的契约

混淆不是一个孤立布尔开关。属性名或导出名变化前,需要列出框架、跨模块、序列化和动态访问依赖,制定保留规则。

interface KeepContract {
  owner: string;
  symbolOrPattern: string;
  reason: string;
  evidence: string;
}

const keepContracts: KeepContract[] = [
  {
    owner: 'entry',
    symbolOrPattern: 'serialized-history-fields',
    reason: '持久化字段需要协议兼容',
    evidence: 'history-schema-review'
  },
  {
    owner: 'libraryHSP',
    symbolOrPattern: 'public-cross-module-api',
    reason: '跨模块调用面需与消费规则一致',
    evidence: 'export-consumer-review'
  }
];

示例只表达审阅形态,不声称当前工程已经存在这些正式清单。尤其不能直接把所有历史记录字段名称写进公共文章;真实项目应在受控仓库中保存精确协议和保留规则。

开启强规则后,应先做最小试验包,覆盖启动、页面加载、Preferences 读写、备份恢复、HSP/HAR 调用与错误日志。任何运行失败都要回到具体规则和契约,不要用“全部关闭”掩盖根因,也不要只增加宽泛 keep 让保护失去意义。

八、构建任务必须记录实际模式和输入摘要

IDE 下拉框属于操作意图,自动发布需要机器可读任务记录。每次构建生成一个 ReleaseBuildAttestation,写明源码修订、实际 buildMode、模块开关、规则摘要和输出摘要。

interface ReleaseBuildAttestation {
  sourceRevision: string;
  requestedBuildMode: string;
  resolvedBuildMode: string;
  moduleRuleDigests: string[];
  moduleRuleEnabled: boolean[];
  buildSucceeded: boolean;
  artifactDigest: string;
  generatedAt: string;
}

function canInspectArtifact(
  record: ReleaseBuildAttestation
): boolean {
  return record.buildSucceeded &&
    record.requestedBuildMode === 'release' &&
    record.resolvedBuildMode === 'release' &&
    record.artifactDigest.length > 0;
}

buildSucceeded 只证明构建任务成功结束;只有开关、规则摘要和产物进一步核对后,才能报告保护状态。产物摘要用于避免拿旧文件做后续分析。若工作区没有 Git,也应使用可复核的源码快照摘要,不能写一个虚构修订号。

本文没有运行任何构建命令。实际命令参数要以工程当前 hvigor 版本、项目脚本和官方文档为准,执行时建议禁用长期守护进程并保存完整退出码与报告路径。

九、产物核对关注差异而不是“文件能打开”

产物层可以比较一个受控样例在关闭与开启配置下的符号、文件名和体积变化,并执行相同功能回归。不要把体积变小单独当作混淆证据,压缩、资源裁剪和构建缓存都可能影响大小。

interface ArtifactProtectionEvidence {
  artifactDigest: string;
  ruleDigest: string;
  symbolSampleChanged: boolean;
  fileNameSampleChanged: boolean;
  protectedApiStillCallable: boolean;
  persistenceRoundTripPassed: boolean;
  smokeSuitePassed: boolean;
}

function hasProtectionEvidence(
  evidence: ArtifactProtectionEvidence
): boolean {
  return evidence.artifactDigest.length > 0 &&
    evidence.ruleDigest.length > 0 &&
    evidence.symbolSampleChanged &&
    evidence.protectedApiStillCallable &&
    evidence.persistenceRoundTripPassed &&
    evidence.smokeSuitePassed;
}

是否要求文件名样例变化取决于批准规则;未启用文件名选项时不应作为必选项。更可靠的方法是让证据结构根据 manifest 中实际启用的规则生成对应断言。

发布证据还要区分未签名中间产物、签名 HAP、安装包和商店提交版本。对其中一个文件的符号抽样,不能自动代表另一个文件。签名信息本身也要脱敏,报告只保留证书指纹或配置 ID,不记录口令。

十、排障表、验证清单与当前结论

现象优先核对常见原因处理方向
选了 release,符号仍可读ruleOptions.enable 完整路径主开关仍是 false先比对 manifest 与模块配置
规则文件写了四个启用项却无效果主开关和构建报告只配置 files,未启用规则不把文件存在当执行证据
entry 有变化,HSP/HAR 没变化各模块开关只修改了 entry按实际构建图逐模块核对
开启后跨模块调用失败consumer/keep 契约导出名或属性名被改写收窄规则并补精确保留项
历史数据读取异常序列化字段协议属性名变化影响持久化固定 DTO 字段或配置保留规则
包体变小就被报告为已保护产物证据定义把体积变化当唯一依据核对规则、符号样例与功能回归
报告显示开关开启,拿到的却是旧包产物摘要和时间输出目录残留旧文件每次记录摘要并隔离输出
审计报告泄露签名信息输出白名单直接复制根配置只保留配置 ID 与非敏感摘要
  • 根配置只用于确认 release 模式存在,不直接作为混淆结论。
  • entry、HSP、HAR 的 ruleOptions.enable 分别读取。
  • copyCodeResource.enable 没有被误认为混淆开关。
  • 规则文件摘要与批准版本一致。
  • manifest 明确每个实际参与构建模块的期望状态。
  • 属性名、顶层名、文件名和导出名规则逐项审阅风险。
  • 序列化字段、跨模块 API 和动态访问有精确保留契约。
  • 构建记录包含请求模式、解析模式、源码与产物摘要。
  • 构建报告能回读实际加载的模块选项。
  • 产物核对与启动、持久化、备份恢复和模块调用回归配套。
  • 未签名、签名和提交产物分别留存证据。
  • 报告不会写入口令、私钥或完整签名材料。

当前源码能够确认:根配置声明了 debug 与 release;entry 的 release 混淆 ruleOptions.enablefalse;规则文件列出了四项候选启用规则;libraryHSP 与 libraryHAR 的同一主开关也为 false。因此不能根据“选了 release”或“规则文件存在”报告当前代码已经混淆。

ReleaseProtectionManifest、配置门禁、保留契约、构建证明和产物证据结构都是建议方案,尚未写入 The_kemusan。本文没有执行构建,没有生成或反编译新的 HAP,也没有在模拟器、真机或应用市场环境验证启用后的行为。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐