先看结论与判断条件

  • 先用业务损失和攻击收益给函数分级,再讨论使用 VMP、Java2C、控制流处理还是仅做名称混淆。
  • 启动、渲染循环、高频加解密与跨语言边界属于高敏感路径,不能用普通功能回归代替性能和兼容性基线。
  • 保护清单必须版本化,并与唯一候选包、签名身份、构建配置和回归记录绑定,否则无法归因异常。
  • VMP 提高客户端代码的分析与复用成本,不替代签名治理、平台完整性信号、服务端授权和风险控制。

先把函数清单改成业务资产清单

全量覆盖的第一个问题不是性能,而是缺乏选择标准。代码仓库里同时存在核心算法、授权判断、协议编解码、界面绑定、通用工具和第三方适配层。它们被还原后的损失完全不同。如果只按包名、类名或函数数量圈选,保护成本会被低价值代码迅速消耗,真正关键的路径反而难以获得稳定的回归资源。

可执行的做法是让业务、安全和研发共同填写资产表。每个候选函数至少回答四个问题:攻击者读懂它能获得什么,修改它能造成什么损失,逻辑能否移到服务端,失败时是否有安全回退。只有损失明确、客户端必须保留且边界可测的路径,才进入下一轮技术选择。

OWASP MASVS 把抗逆向与抗篡改归为纵深防御,并明确这些措施不能替代正确的安全架构。这个边界意味着 VMP 的选择条件必须来自威胁模型,而不是把技术名称直接等同于安全结果。

候选函数分级时应记录的最小信息
判断维度需要回答的问题适合进入高强度保护的信号需要降级或后移的信号
业务损失逻辑被复制、跳过或修改后会损失什么授权、权益、核心算法或关键协议会被直接绕过只影响界面展示或低价值辅助功能
客户端必要性最终决策是否必须留在本地离线、时延或平台能力要求客户端执行高风险决策可以由服务端完成
执行特征调用频率、线程和启动阶段是什么低频、边界清楚、可单独测量主线程启动、高频循环或无界耗时
故障回退保护异常时能否安全停止或切换有明确失败状态和可回滚配置异常会阻断启动且无法快速隔离
可验证性怎样证明保护后业务仍正确输入输出、场景和验收责任人明确依赖隐式状态且没有稳定测试路径
  • 资产负责人确认损失模型
  • 研发确认调用边界和依赖
  • 测试确认可重复验收路径
  • 发布负责人确认回滚条件

VMP 改变的是执行表示,不是全部安全责任

名称混淆降低符号和结构的可读性,控制流处理提高还原路径的工作量,Java2C 把部分托管代码迁移到 Native 表示,VMP 则让选中的逻辑通过新的指令表示和执行机制运行。它们可以叠加,但解决的问题、运行成本和故障模式并不相同。把多种手段写成一条可配置的分层策略,比给全部函数套同一个级别更容易验证。

攻击者仍然可以观察输入输出、调用时机、网络行为和运行状态。涉及支付、权益、账号授权或高风险资源访问时,服务端仍应验证账号权限、版本集合、请求上下文和平台完整性信号。客户端保护的职责是增加分析、修改和规模化复用的成本,而不是把客户端变成绝对可信环境。

保护范围还要考虑可维护性。频繁变化的业务胶水代码如果每次发布都重新进入高强度处理,会扩大构建差异和回归面。相对稳定、价值高、接口清楚的核心模块更适合作为长期保护单元。

不同保护层的职责不能互相替代
保护层主要作用典型成本仍需配合的控制
名称与结构混淆降低静态阅读和批量定位效率调试、崩溃归因与映射文件管理完整性、服务端授权、关键逻辑保护
控制流与字符串处理增加局部还原成本,减少直接敏感线索包体、运行开销和兼容风险密钥治理、日志脱敏、运行验证
Java2C 或 Native 化改变部分托管代码的分析面JNI 边界、ABI 和 Native 崩溃SO 依赖、符号、异常与线程检查
VMP改变选中代码的执行表示和分析路径性能、故障半径与候选包回归签名、版本、服务端策略和发布门禁

启动链和高频路径必须先有可比较基线

应用启动不是一个单点。Android 官方把冷启动拆成进程创建、Application 创建、主线程启动、Activity 创建、布局和首次绘制,并使用 TTID 与 TTFD 分别观察首帧和完全可交互时间。保护代码如果位于 Application、ContentProvider、类初始化或首屏关键路径,异常可能在监控 SDK 初始化之前发生,普通线上日志未必能完整捕获。

高频函数的风险来自累计成本。单次增加很小的执行时间,在渲染循环、音视频处理、协议循环或批量数据处理里会被放大。验收不能只记录一次平均值,应在相同设备状态和候选包身份下比较分布、长尾、主线程占用、内存变化和异常率。本文不给出通用损耗数字,因为具体结果依赖函数结构、保护配置、设备、编译器和运行频率。

冷启动、热启动和已经预热的测试环境不能混为一谈。至少要固定安装状态、进程状态、账号数据和网络条件,并把未保护基线与保护候选包放在同一测量方法下。

高敏感路径的验证重点
路径为什么敏感必须观察放行条件
Application 与 ContentProvider发生在首屏和多数监控初始化之前进程创建、初始化顺序、最早异常、TTID无新增启动失败,时间变化在项目预算内
主线程高频函数累计延迟会直接影响交互调用次数、单次与总耗时、卡顿和 ANR关键用户路径分布可接受且无新长尾
Native 与 JNI 边界涉及 ABI、注册、异常和线程约束库装载、JNI 异常、目标 ABI、崩溃栈目标矩阵逐项通过,未覆盖项被标记
后台批处理可能放大 CPU、电量和内存成本任务时长、峰值资源、取消与重试不破坏系统限制和业务时限
  • 分别记录冷启动和业务可交互时间
  • 使用相同安装与账号数据条件
  • 同时观察平均值和长尾分布
  • 把性能预算写进验收而不是事后解释

把保护范围做成可审阅、可回滚的配置

一个可以长期维护的保护清单,不能只是工具界面里的若干勾选。它应当像发布配置一样进入版本管理,记录资产标识、选择原因、保护级别、依赖、性能预算、负责人和回滚条件。这样出现问题时,团队才能回答某个函数为什么被保护、从哪个版本开始、由谁验收。

配置变更应采用小批量推进。先选择少量价值最高且边界最清楚的路径形成 PoC,再逐组扩大。每组扩展都生成新的候选包身份和回归记录,不能在同一文件名下覆盖旧产物。

下面的 YAML 只是公开安全的数据形态示例,不对应御盾内部配置格式,也不包含真实类名、函数名或产品实现。

  • 每项选择都有业务原因
  • 配置变化能对应到候选包
  • 高风险路径拥有独立回归
  • 回滚不依赖重新猜测旧配置
保护范围清单的公开安全示例
asset: premium-entitlement-decision
owner: commerce-team
client_required: true
threats:
  - unauthorized-logic-reuse
  - local-branch-tampering
execution:
  phase: post-login
  frequency: low
  main_thread: false
protection:
  tier: high
  rollback_group: entitlement-v1
acceptance:
  - output-parity
  - latency-budget
  - target-os-matrix
  - signed-candidate-identity

验收必须绑定同一候选包和同一发布链

构建成功只能证明工具链生成了产物,安装成功只能证明当前包在当前设备满足安装条件。最终验收还要覆盖签名身份、从线上版本升级、冷启动、关键业务路径、异常恢复、目标系统和目标 ABI。静态分析、性能测量和兼容回归必须指向同一文件身份。

建议为未保护基线和每个保护候选包记录文件摘要、包名、版本、签名证书摘要、构建来源、保护配置版本和渠道处理顺序。任何重新构建、重签名或渠道修改都会产生新候选身份,需要重新进入受影响的验证步骤。

发布判断应明确三类结论:已验证范围、未执行范围和失败范围。没有设备、系统或业务数据时,应写成未覆盖并限制灰度,而不是借用另一个版本或另一台设备的成功结果。

从 PoC 到发布的最小门禁
门禁证据不能接受的替代物失败动作
候选身份摘要、版本、签名、配置与构建来源同名文件或口头确认停止流转并重新固定产物
功能一致关键输入输出和异常路径对照只打开首页或单次演示缩小范围并定位最早差异
性能预算相同条件下的启动和关键路径分布不同设备的单个平均值回退高频路径或调整层级
兼容矩阵目标系统、ABI、设备类型和第三方路径模拟器或单一新系统标记未覆盖并限制发布
发布闭环升级、签名、渠道、监控与回滚演练重新签名后的另一个包重新执行受影响门禁

哪些情况不应直接扩大 VMP 范围

如果团队还不能说明关键资产、没有稳定候选包、缺少目标系统矩阵或连未保护基线都没有,继续扩大范围只会增加无法归因的变量。此时正确动作是补齐资产和测试条件,而不是用更高覆盖率掩盖验证缺口。

反射、序列化、动态类加载、热修复、插件框架、JNI 注册、第三方自校验和启动期 SDK 都可能对名称、代码布局、装载顺序或异常行为有隐式依赖。它们不是一律不能保护,但必须单独列出并在真实业务入口验证。

服务端能够承担的高风险决策,应优先在服务端形成最终授权。客户端 VMP 可以保护必要的本地计算和决策材料,却不能保证运行环境永远可信,也不能独自阻止合法客户端凭据被滥用。

最终范围不是一次会议里的永久结论。业务逻辑、编译链、SDK 和目标系统变化后,需要重新审阅资产价值、执行频率和兼容边界。

  • 没有基线时先补基线
  • 没有回滚时不扩大故障半径
  • 隐式依赖路径单独建组
  • 服务端可承担的决策不留给客户端独自完成

保护清单必须能够还原到构建产物

资产清单如果只写类名或需求名称,到了编译产物里往往无法直接复核。Kotlin 的默认参数、挂起函数、内联函数、lambda、数据类和 Compose 编译插件都会生成额外方法;Java 的匿名类、桥接方法与编译器合成访问器也会改变最终边界。评审时应把业务入口、源码符号、编译后符号、所属 DEX、调用来源和排除原因连成一条记录,并保存与候选包一致的映射文件。这样才能回答某段核心逻辑是否真的进入保护范围,而不是依靠配置文件里出现过一个名称来推断。

选择规则还要防止范围在升级后静默漂移。仅按模糊包名前缀匹配,可能把新接入的日志、序列化或第三方 SDK 一起纳入;只按具体方法签名匹配,又可能在重构、混淆或编译器升级后漏掉新生成入口。更可靠的做法是把业务资产标识与可解析的产物扫描结合:构建时输出命中、未命中、新增和移除项,出现无法解释的变化就停止发布。公开文章中的示例可以展示校验方法,但真实类名、包名和映射文件必须留在项目证据包。

配置审阅不能只由加固人员完成。业务研发确认入口和异常语义,性能人员标记启动链与高频路径,测试人员确认可触达场景,发布负责人核对最终候选身份。四类输入合在一起,才能避免把一个静态上重要但运行时不可达的方法当成重点,也能避免漏掉由深链、推送、后台任务或远程配置触发的真实关键路径。

  • 源码资产能够映射到编译后方法
  • 合成方法和异步状态机被纳入审阅
  • 每次构建输出命中与漂移差异
  • 映射文件与候选包身份绑定
  • 模糊规则的扩张范围有人工复核
  • 公开材料不暴露真实业务符号

用可失败的实验决定是否扩围

扩围前应先写出能够推翻方案的条件。例如启动阶段新增同步工作、关键交易路径的尾延迟超出预算、异常类型或错误码发生变化、崩溃无法用当前符号归因、某个目标系统只能通过关闭全部保护才能运行,这些都应触发停止或回退。没有预先定义失败条件,团队很容易在看到问题后不断放宽口径,最后把能安装、能打开误写成保护方案已经可发布。

实验顺序宜从一小组高价值且边界清楚的方法开始,固定基线包、保护包、设备状态、账号数据和测试脚本,只改变保护清单。观察结果至少包括功能输出、异常分支、启动阶段、内存峰值、崩溃与 ANR、第三方回调以及回滚结果。一次只增加一组资产,记录新增收益和新增故障半径;如果无法说明哪一组配置导致差异,就没有足够依据继续扩大范围。

最终决策可以明确保留部分未保护代码。低价值胶水代码、频繁执行且对延迟敏感的方法、依赖复杂的框架生成代码,可能更适合使用混淆、完整性检查、服务端授权或发布监控承担风险。这样的取舍不是保护失败,而是把强控制放在攻击收益最高的位置,同时维持可测试、可定位和可回滚的商业发布能力。

评审记录还应说明预期对手需要付出什么额外工作,以及控制失效后还有哪些后续防线。若目标只是隐藏一个最终会由服务端重新校验的本地提示值,继续增加执行复杂度的收益可能有限;若目标是离线许可、核心计算或高价值状态转换,保护强度和运行证据的优先级会明显提高。用具体攻击路径比较前后差异,才能让范围决定服务风险,而不是服务某个覆盖率数字。

同一资产也可能需要按平台或版本使用不同配置。旧系统、低端设备和新编译器产生的代码形态并不相同,统一规则如果只能靠大量例外维持,说明范围划分过粗。配置应允许按候选版本分支,并定期合并已经验证的规则,避免历史排除项永久留存却没人知道原因。

VMP 扩围实验的决策记录
观察项继续扩围的依据停止条件替代控制
业务输出正常与异常结果保持一致金额、权限或状态机语义变化服务端复核与规则校验
运行成本启动和高频路径仍在预算内出现稳定且不可解释的退化缩小范围或调整执行位置
兼容范围目标矩阵逐项有同候选证据高优先系统或 SDK 无法通过分组配置与明确版本边界
故障归因日志、符号和配置能够定位只能全关保护才能恢复单组回退与候选包重建
攻击收益关键算法和决策材料暴露减少只增加复杂度而未改变重点暴露混淆、完整性与后端授权
  • 扩围前写明可推翻条件
  • 一次只改变一组保护对象
  • 功能、异常和性能使用同一输入
  • 保留保护外的替代控制
  • 结论绑定唯一候选包与配置版本

事实依据与适用边界

以下内容区分官方事实、本文工程判断和不能外推的范围,避免把设计建议写成未经验证的产品结论。

本文判断事实或工程依据适用限制
VMP 应作为威胁驱动的纵深防御,而不是安全架构替代品。OWASP MASVS-RESILIENCE 将混淆、抗篡改、抗静态和动态分析列为提高韧性的控制,同时强调安全仍依赖可验证设计、密码学和服务端验证。该标准说明控制目标,不证明任何具体产品或配置已经达到目标。
启动路径需要独立性能基线。Android 官方把启动分为冷、温、热状态,并使用 TTID 与 TTFD 区分首帧和完全可交互时间。官方指标定义不能替代项目在真实候选包和目标设备上的测量。
签名和升级连续性必须独立验收。Android 官方说明每个 APK 必须签名,平台使用签名身份判断已安装应用的更新是否来自同一密钥持有者。签名一致只证明发布身份链的一部分,不证明业务逻辑未被滥用。
平台完整性信号应由服务端纳入策略。Play Integrity 返回应用、设备、账号和环境相关 verdict,后端可依据风险分级响应。信号可能不可用或受分发环境限制,不能把单个 verdict 当成绝对信任。
不存在脱离函数、设备和配置的通用性能损耗数字。VMP 成本与执行频率、线程、代码结构、保护实现、编译器和设备共同相关,因此必须通过同条件对照建立结论。这是工程判断,不是对御盾或其他产品性能的实测声明。

工程常见问题

是不是覆盖越多,逆向成本就一定越高?

覆盖增加可能提高部分分析工作量,也会扩大性能、兼容和回归成本。真正影响攻击收益的是高价值路径是否被有效处理,以及签名、完整性和服务端策略是否形成闭环。

启动函数是否绝对不能进入 VMP?

不是绝对禁止,但启动期故障半径大、监控可能尚未初始化,必须先有冷启动基线、明确预算、目标系统矩阵和快速回滚方案。

如何判断某个函数是高价值函数?

看它被还原或修改后是否能绕过授权、复制算法、滥用协议或造成直接业务损失,同时确认逻辑必须留在客户端并且能够独立回归。

没有当前候选包时可以先确定最终范围吗?

可以形成初步资产分级和 PoC 清单,但不能提前承诺性能、兼容性或最终保护效果。最终范围必须由真实候选包的对照结果确认。

VMP 之后还需要服务端风控吗?

需要。客户端保护提高分析和修改成本,账号授权、交易、权益和高风险资源访问仍应由服务端结合版本、账号和风险信号做最终决定。

内测状态

内测人员已满,请耐心等待新一轮内测开放。当前不接受在线申请、邀请码登记、APK/AAB/IPA 提交或候补登记;公开技术与产品资料仍可查看。

继续阅读: VMP 加固是什么,如何选择保护范围