入门
本指南将逐步引导您完成从安装 OZero Security 到应用首个安全功能的整个过程。即使没有安全专业知识,约 3 分钟即可完成基本设置。
按目标开始
选择当前要完成的任务,即可跳转到本手册的相关章节或 API 参考。
概述
OZero Security 通过在一般黑客工具难以访问的 Native C++ 层执行安全逻辑来保护 Unity 游戏。只需在 Unity 编辑器内的仪表板中激活模块,即可运行核心保护功能。无需场景设置或编写额外代码。
- 构建完整性检查(应用篡改检测)
- 速度黑客及时间黑客检测
- 内存注入监控
- 加密的游戏内变量类型(Secure Types)
- 加密的存档文件及 PlayerPrefs
- 激活的检测器将在 SDK 启动时自动准备就绪。无需在场景中放置独立对象或编写重复的初始化代码。
- 安全设置在构建过程中受到保护,因此明文设置不会暴露在一般玩家的发布版本中。
- Native C++ 运行时守卫在托管 Unity 代码之外提供了额外的验证层。
- 检测结果可通过回调、日志和 Pro 遥测进行确认,便于在测试和运营中追踪原因。
1
导入包
打开 Unity 编辑器并导入 OZero Security 包。您可以通过 Unity 包管理器或双击 .unitypackage 文件进行导入。
当出现 Import Unity Package 对话框时,在勾选所有项目的情况下点击 Import。所需的脚本、原生插件和编辑器工具将自动添加。
2
打开仪表板
导入完成后,从 Unity 菜单栏打开 Config Dashboard:
打开 Config Dashboard 后,可以在此屏幕上一次性管理预设、安全模块、许可证和构建完整性设置。无需在 Project 窗口中直接查找并修改文件。
Unity 编辑器菜单说明
OZeroSecurity 将设置窗口放在 Window > OZero Security,将诊断和 bake 命令放在 Tools > OZero Security。安全预设会在 Config & Dashboard 中应用,方便您在保存前确认实际变更的设置。
| 菜单 | 作用 | 使用场景 |
|---|---|---|
| Window > OZero Security > Config & Dashboard | 打开或创建主 OZeroSecurityConfig 资产。 | 首先在这里应用预设、启用模块、配置响应策略并检查构建完整性设置。 |
| Window > OZero Security > License Settings | 打开或创建 OZeroLicenseConfig。 | 用于输入 Standard / Pro 许可证设置、服务器端点和遥测选项。 |
| Window > OZero Security > Check Setup | 在编辑器中诊断常见的项目和发布配置问题。 | 在导入包后、发布构建前,或模块行为不符合预期时运行。 |
| Tools > OZero Security > Check Integrity Manifest | 打开 OZero 完整性清单并显示解码后的内容。 | 用于构建完整性故障排查或支持调查。 |
| Tools > OZero Security > Bake Security Config Blob | 在 StreamingAssets 中重新生成受保护的安全配置 blob。 | 用于高级调试或 CI 工作流。普通播放器构建会自动 bake。 |
| Tools > OZero Security > Bake Assembly Hash | 为最近一次构建输出重新生成 oz_ahash.bin。 | 仅在未重新运行完整构建流水线、只进行了代码重建之后使用。 |
| Tools > OZero Security > Keystore SHA Extractor | 从 Android keystore 中提取 SHA-1 和 SHA-256 指纹。 | 在填写 Build Integrity 的 Android 签名指纹时使用。 |
| Tools > OZero Security > Add Debug SHA Key to Config | 查找 Android debug keystore,并将其 SHA-256 指纹加入 config。 | 仅用于本地 Android 调试构建。发布构建应使用发布签名密钥指纹。 |
| Tools > OZero Security > Steam Anti-Piracy > Scan Steam Redistributable | 扫描 PC 构建或插件文件夹中的 Steam redistributable 文件和已知 emulator artifact。 | 在 Steam 发布打包前,或检查是否包含可疑 Steam 文件时使用。 |
| Tools > OZero Security > Check Time.timeScale Usage | 扫描可能与 OZero 时间保护冲突的直接 Time.timeScale 写入代码。 | 在发布前,或 Speed & Time Hack 模块报告项目侧 time-scale 策略问题时运行。 |
3
激活模块
在仪表板内可以查看安全模块列表及切换开关。请激活您要使用的模块。以下是推荐的初始配置。
选择安全预设
首先选择一个预设,然后根据项目进行个别模块调整。对于一般的实时在线游戏,建议从 Standard 开始。这能在保护级别、性能和误报率之间取得最佳平衡。
| 预设 | 推荐用途 | 适用策略摘要 |
|---|---|---|
| Low | 原型、开发版本、初期 QA | 仅保留轻量的核心检查。放宽平台原生检查及强制关闭策略,以免测试环境过早被封锁。 |
| Standard | 大多数发售游戏的推荐默认值 | 启用核心保护套件、启动验证、运行时再验证、模拟器检查以及推荐的 IL2CPP 文件覆盖。是一个平衡了兼容性与保护级别的配置。 |
| Strict | 高风险实时服务、PvP、竞技类构建 | 采用最广泛的覆盖范围,将更多的失败视为致命违规。请在充分测试平台、签名及商店发布流程后再采用。 |
| 模块 | 功能说明 | 是否推荐 |
|---|---|---|
| Build Integrity Validator | 检测应用二进制是否被篡改 | 推荐 |
| Speed Hack Detector | 检测时间操控作弊 | 推荐 |
| Injection Detector | 监控内存 Hook 工具 | 推荐 |
| Install Source Validator | 拦截非法 APK(仅限 Android) | 选择 |
只需在仪表板中开启要使用的模块并保存 OZeroSecurityConfig 资产即可。播放器启动时,SDK 会自动准备已激活的模块。
许可模式 — Standard / Pro
Standard无需服务器集成即可提供本地防护。Pro将项目专属原生二进制文件(Native Variant)和清单绑定与遥测、签名时间、远程安全配置、证明和服务器验证结合。
| 项目 | Standard | Pro |
|---|---|---|
| 许可证密钥 | — (无) | OZ-PRO-XXXX ×6 |
| 启动时网络连通 | 无需 — 可离线执行 | 每设备进行 1 次 POST /v1/activate 后缓存 |
| 10 个保护模块 | 全部 10 个保护模块激活 | 全部 10 个(与 Standard 相同) |
| 原生 Variant | 共用的原生模块 | 包含 |
| 云端遥测 | 不发送 | 开启 — 将威胁事件发至 /v1/telemetry |
| 签名时间 (防时钟篡改) | 关闭 — WebTime 仅使用 HTTPS HEAD |
开启 — 使用签名的 /v1/time 响应 |
| 设备限制 | 无限制 (无密钥,不强制) | 默认 5 台 / 可调节 |
| 源代码权限 | 仅托管 C# | 仅托管 C# |
OZeroLicenseConfig中配置Pro服务器功能。
注册Pro许可密钥
Standard无需单独配置许可。Pro需要在Unity中创建OZeroLicenseConfig资源并输入项目密钥,用于专属Variant绑定、运行时激活和服务器功能。
1. 创建设置资产
从 Unity 顶部菜单打开 Window → OZero Security → Config & Dashboard。在 License & Server 区域点击 Create OZeroLicenseConfig 按钮。该按钮会把 OZeroLicenseConfig 资产自动创建到正确的 Resources/ 文件夹中,因此不需要手动创建文件夹或移动资产。
2. 填写 Inspector 字段
| 字段 | 是否必需 | 说明 |
|---|---|---|
| tier | 所有层级 | Standard使用共享原生模块。Pro提供项目专属原生二进制文件、服务器激活、遥测和远程安全配置。 |
| licenseKey | Pro | 为项目签发的OZ-PRO-...格式Pro许可密钥。用于验证专属二进制文件的项目绑定,以及服务器激活和Pro服务器功能。 |
| allowStandardBuildWithPremiumLicense | Pro | 显式 native variant 兼容性覆盖。普通构建请保持关闭。只有在明确要用 Standard public native 模块构建 Pro 授权项目,或在 Standard 配置下构建 Pro private native variant 包时才开启。Standard public variant 构建即使开启此选项也不需要密钥。Standard + private variant 构建必须填写匹配的许可证密钥,构建/运行时验证也会继续检查 signed manifest、license key hash、项目 identity 和 native hash。 |
| appIdentifier | 自动传输 | 发送 Pro 激活请求时,SDK 会同时发送 Unity 的 Application.identifier。如果它与客户门户中登记的 Bundle ID 或 Package Name 不一致,激活可能会被拒绝。请先确认 Unity Player Settings 中的 Identifier。 |
| serverBaseUrl | Pro runtime | 用于Pro激活、遥测、签名时间、证明和服务器策略的服务器地址。无服务器模式不会调用它。除非支持团队另有说明,请保留https://api.ozerosecurity.com。 |
| serverPublicKeyHex | Pro runtime | 客户门户Server Key中显示的Pro服务器签名公钥,用于验证服务器响应的签名。专属二进制文件的清单使用SDK内置的二进制签名密钥单独验证,不使用此字段。 |
| previousServerPublicKeyHex | Pro 可选 | Pro服务器签名密钥轮换期间使用的旧公钥。仅在支持团队要求轮换时填写,平时请留空。 |
| tokenTtlSeconds | Pro runtime | Pro 激活成功一次后,离线状态下可以继续使用该结果的时间。默认值 604800 表示 7 天。超过该时间后,本地保护仍会运行,但遥测、signed time 等 Pro 服务器功能会关闭,直到下一次激活成功。 |
| offlineProPolicyMode | Pro | 这是 Pro 选项,用来决定设备离线时本地 Build Integrity 如何使用已签名的门户阻止策略。ApplyCachedBlockPolicies 只在存在有效缓存策略时应用,是推荐给大多数正式运营游戏的 fail-open 默认值。RequireFreshPolicy 在没有可用签名策略且服务器 Integrity 已启用时 fail-closed。IgnoreCachedBlockPolicies 会为了测试或迁移绕过缓存策略。每个值的含义可在 OZeroOfflineProPolicyMode 中查看。 |
| activationTimeoutSeconds | Pro runtime | Pro 激活请求 /v1/activate 最多等待几秒。默认值为 6.0。超过该时间也不会阻塞场景加载;如果有可用的 Pro 缓存就使用缓存。即使没有缓存,也只有已选择的服务器功能不可用,本地保护仍会继续。 |
| enableLog | 可选 | 开启后,许可证缓存使用、激活成功、超时、签名不一致等流程会写入 OZeroSecLog。集成阶段建议开启,方便排查问题。发布构建如果希望减少日志,可以关闭。 |
| Pro 服务器功能 | ||
| enableDevicePolicyHeartbeat | Pro | 定期向 Pro 服务器确认当前设备是否已在客户门户中被阻止。如果服务器返回 DEVICE_BLOCKED,SDK 会清除保存的 Pro 权限信息并阻止应用运行。 |
| devicePolicyHeartbeatInterval | Pro | 检查设备是否被阻止的基础间隔。默认值为 300 秒。设为 0 则不进行周期检查。 |
| devicePolicyHeartbeatJitterPercent | Pro | 为避免大量设备在同一时刻请求服务器,给检查时间增加少量错开的比例。默认值 20 表示 SDK 会在配置间隔前后略微调整时间。可输入范围为 0 到 75。 |
| enableSecurityLevelCheck | Pro | 开启后,应用启动时会通过 /v1/security-level 向服务器确认此构建的安全设置。如果低于门户要求的最低等级,服务器可能拒绝该构建。默认值为 false。 |
| declaredSecurityLevel | Pro | 此构建向服务器报告的安全等级。默认值为 Standard。Low 用于原型或内部测试。Strict 只应在 QA 确认更强策略不会阻止正常用户后使用。 |
| failOnSecurityLevelReject | Pro | 开启后,如果服务器明确拒绝安全等级或配置 hash,应用会执行阻止流程。普通网络错误、服务器维护或许可证维护状态不会被当作篡改,只会停用 Pro 服务器功能。 |
| securityLevelCheckInterval | Pro | 应用运行中重新检查安全等级的间隔。默认值 0 表示只在启动时检查一次。 |
| securityLevelCheckJitterPercent | Pro | 为避免大量设备同时重新检查安全等级,给检查间隔增加少量错开的比例。默认值为 20,可输入范围为 0 到 75。 |
3. 构建与验证
不需要额外编写代码。应用启动时,SDK 会自动读取 OZeroLicenseConfig 资产。首次运行后,请在 Player 日志中确认是否出现类似 [OZeroLicense] activated; tier=pro caps=5 的行。如果已设置为 Pro 但日志显示 Standard 模式,请先确认资产的位置和名称。资产必须位于 Resources/ 文件夹下,名称必须正好是 OZeroLicenseConfig。
离线操作
先看结论:网络断开不会停止游戏或本地保护。Standard 原本就不使用服务器,Pro 也保留相同的离线本地保护。只有明确选择的服务器功能可在一定时间内使用已保存的激活信息。
Standard — 无需互联网即可启动
Standard 不会向服务器请求许可。它只运行应用内置的本地保护模块,所以玩家离线时游戏也能启动。只有遥测、signed time 等需要 Pro 服务器的功能不可用。
Pro — 本地保护保持离线,所选服务器功能使用缓存
启用可选的 Pro 服务器功能后,SDK 会在联网时确认设备和许可证。所选服务器功能可在有限时间内容忍短暂断网。此检查不会限制使用已获取 Native Variant 的构建或本地保护。
tokenTtlSeconds 是已保存服务器功能结果的有效期,默认值为 604800 秒,即 7 天。离线过期后,只有遥测上传、signed-time 验证等依赖服务器的功能在下次成功激活前不可用。游戏、已获取的 Native Variant 和本地检测器保持不变;SDK 不会静默地把产品层级切换为 Standard。
Pro — 门户中阻止的版本离线也会被阻止
临时服务器或网络问题下继续以基础保护启动,和允许运营人员在门户中明确阻止的版本运行,是两件不同的事。Pro 激活成功时,服务器会同时下发带签名的阻止列表,例如被阻止的构建哈希、SDK 版本和应用版本。SDK 会将该策略与临时服务器功能状态分开处理。
请在 Config Dashboard 的 License & Server 区域通过 OZeroLicenseConfig.offlineProPolicyMode 配置此行为。它决定设备无法连接服务器时,本地 Build Integrity 如何处理最后一次收到的已签名 Pro 门户阻止策略。
| 模式 | 使用场景 |
|---|---|
| ApplyCachedBlockPolicies | 推荐给大多数正式运营游戏的默认值。如果存在有效的签名策略缓存,门户中阻止的构建或版本在离线时也会继续被阻止。相反,如果设备离线且缓存不存在、已删除或已过期,这个策略 gate 不会让构建失败,而是通过。 |
| RequireFreshPolicy | 适用于在线优先游戏的 strict 模式。如果没有可用的签名策略,例如首次启动、重新安装、缓存删除或缓存过期,就不会信任旧策略或缺失策略,而是让 Build Integrity 失败。这个 fail-closed 行为需要启用服务器 Integrity 选项。 |
| IgnoreCachedBlockPolicies | 用于测试或迁移的 bypass 模式。离线时 SDK 不会读取缓存的门户阻止策略,因此这个 gate 不会应用门户阻止规则。不建议用于正式运营构建。 |
总结来说,ApplyCachedBlockPolicies 是运营默认的 fail-open 模式,RequireFreshPolicy 是 fail-closed 策略模式,IgnoreCachedBlockPolicies 是测试用 bypass。如果每次验证都必须要求在线服务器确认,请不要只依赖此选项,还要同时配置服务器 Integrity 或 Managed Verification 的必需选项。
ApplyCachedBlockPolicies 时,完全离线的首次启动、重新安装、缓存删除或缓存过期启动无法应用门户阻止策略。如果这些情况也必须要求有效签名策略,请同时使用 RequireFreshPolicy 和服务器 Integrity。
| 状态 | 探测器 | 遥测 | Signed Time |
|---|---|---|---|
| 连线,刚完成激活 | 10 项皆起作用 | 开启 | 开启 |
| 离线,保存的 Pro 信息仍有效 | 10 项皆起作用 | 关闭 (不保存离线事件) | 可用时使用 WebTime 作为替代 |
| 离线,保存的 Pro 信息已过期 | 10 项皆起作用 | 关闭 (直到下次在线激活) | 关闭 (直到下次在线激活) |
| 首次启动 + 完全离线 | 10 项皆起作用 | 关闭,直到首次在线启动 | 关闭,直到首次在线启动 |
许可证问题解决
出现问题时,请先在 Unity Player 日志中搜索 [OZeroLicense]。这个日志会告诉你 SDK 是以 Standard 启动,还是 Pro 激活成功;如果失败,也会说明为什么降级到 Standard。安全事件上传问题则同时搜索 [OZeroTelemetry]。
| 日志中显示的内容 | 含义 | 处理方法 |
|---|---|---|
[OZeroLicense] Standard / serverless mode. |
SDK 以 Standard 模式启动。 | 如果本来就要使用 Standard,这是正常情况。如果预期是 Pro,请确认 OZeroLicenseConfig.asset 是通过 Unity 菜单 Window → OZero Security → Config & Dashboard 创建的,并且位于 Resources/ 下。资源名称必须正好是 OZeroLicenseConfig,同时 tier=Pro 和 licenseKey 也要填写。 |
Pro->Standard downgrade: /v1/activate returned LICENSE_NOT_FOUND |
服务器找不到这个许可证密钥,或该密钥当前不可用。 | 请把 licenseKey 与客户门户中显示的许可证密钥重新对照。大小写和短横线必须完全一致。如果门户中的状态是 pending、suspended、revoked 或 expired,Pro 激活不会成功。 |
ANDROID_BUNDLE_ID_MISMATCH / WINDOWS_PRODUCT_NAME_MISMATCH |
当前构建的应用信息与门户中登记的值不同。 | Android/iOS 请检查 Player Settings 中的 Application.identifier,Windows 请检查 Company/Product 名称,WebGL 请检查运行 origin。如果这些值与门户登记不一致,/v1/activate 会被拒绝。修改后请用新构建重新测试。 |
activation token signature verification failed |
服务器有响应,但 SDK 判断该响应不能被信任。 | 此项只适用于 Pro 激活。请将客户门户 > Server Key 的 Active publicKey 填入 OZeroLicenseConfig.serverPublicKeyHex。即使门户同时显示 kid,Unity 中也只填 64 位 hex publicKey。若值完全正确仍失败,请在不经过公司代理/MITM 设备的直接网络上再测试一次。 |
private native variant manifest signature is invalid |
Variant 包的 manifest 缺失、被编辑,或不是由受信任的 OZero Variant signing key 签名。 | 不要通过填写 serverPublicKeyHex 来修复此问题。对于 Pro Variant 包,请重新下载分配给该项目的 Variant 包,并确保 manifest 与 native plugin 文件来自同一个包。如果重新下载后仍失败,请将 manifest 文件和 Unity 构建日志发送给 OZero 支持团队。 |
DEVICE_BLOCKED |
这台设备已在客户门户中被阻止。 | 请在门户中查看该 deviceId 的安全事件。如果是真实攻击或违反策略,请保持阻止;如果是 QA 设备或误报,请在门户中解除阻止。SDK 收到这个响应后,会清除保存的 Pro 权限并执行阻止流程。 |
ACTIVATION_LIMIT |
该许可证可新激活的设备数量已经超出上限。 | 请在门户中清理不再使用的测试设备。如果这是由真实用户增长造成的,就需要提高该许可证的设备上限。 |
/v1/activate timed out / network error |
设备无法连接服务器,或服务器响应太慢。 | 请确认设备能访问 https://api.ozerosecurity.com/health。同时检查公司防火墙、代理和 DNS。高延迟地区可以稍微提高 activationTimeoutSeconds 后再测试。如果已经有保存的 Pro 信息,SDK 会优先使用它。 |
cached entitlement past TTL; clearing. |
保存的 Pro 信息已过有效期。 | 如果设备离线时间超过 tokenTtlSeconds,保存的 Pro 信息就不再被信任。游戏会继续以 Standard 级别运行,下一次在线激活成功后 Pro 功能会恢复。如果游戏经常长时间离线,可以提高 TTL。 |
SERVER_NOT_CONFIGURED / SIGN_FAILED |
服务器无法为激活令牌签名。 | 大多数情况下,这不是客户端设置问题,而是服务器 Server Key 或加密密钥状态问题。请检查客户门户中的 Server Key 状态。生产环境发生时,请把日志时间、部分 licenseKey 和发生平台提供给 OZero 支持团队。 |
enableLog。这些日志对 QA 很有帮助,但面向普通用户的构建不需要暴露 tier、功能状态或检测流程细节。
项目设置
在调整各个安全模块之前,请先检查影响原生插件、商店构建和平台验证的 Unity Player Settings。
最低构建目标
| 平台 | 最低目标 | 说明 |
|---|---|---|
| Unity Editor | 2022.3 LTS+ |
OZero Security SDK 1.0.5 需要 Unity 2022.3 LTS 或更高版本,Unity 6 也已验证。2021.3 项目在导入软件包前必须升级。 |
| iOS | 14.0+ |
iOS 构建请将 Project Settings > Player > iOS > Target minimum iOS Version 设为 14.0 或更高。ABI 21 的注入检测器使用 iOS 14 的 shared-cache API 区分 Apple 系统映像与注入代码,低于该版本时 Editor preflight 会失败。 |
| Android | API 24+ |
对于 Android 构建,请将 Project Settings > Player > Android > Minimum API Level 设置为 Android 7.0 Nougat (API level 24) 或更高版本。建议商店发布构建使用 IL2CPP 和 ARM64。 |
PrivacyInfo.xcprivacy。Unity post-process 会将其加入 iOS Xcode 应用 target,并复制到 macOS 的 Contents/Resources。由于 OZero 使用 Unity PlayerPrefs 保存应用内 SDK 状态,清单声明了 UserDefaults CA92.1 理由。请在最终应用中保留该清单,并在提交前检查 Xcode privacy report。
发布构建的安全与体积优化设置建议
发布最终版本时,OZero 建议使用以下 Unity 设置来增强构建安全性并减少应用体积。
安全强化(IL2CPP 设置):在 Android、iOS 等 Unity 支持的环境中,请将脚本后端设置为 IL2CPP。IL2CPP 不能完全阻止逆向分析,但可以减少 C# 代码和元数据的直接暴露范围,并帮助 OZero 安全检查在更稳固的发布环境中运行。路径:Project Settings > Player > Other Settings > Scripting Backend
体积优化(Managed Stripping 设置):Managed Stripping 会移除未使用的 managed code,从而减少应用体积。如果一开始移除过多代码,应用可能在运行时出错,因此建议先从 Low 或 Medium 开始。
注意事项:修改设置后,请先在真实设备上测试场景切换、数据保存、Addressables,以及支付/广告等外部 SDK 是否正常工作,再提高 stripping level。
Android ProGuard / R8 设置
OZero Security 不需要单独的 Java SDK 包。但是,如果您的 Android 部署版本启用了 Minify、ProGuard 或 R8,则必须保留 Unity Java 桥接以及项目中使用的自定义 Android 桥接类。这样才能确保获取包信息、安装来源、APK 签名证书检查和启动资产加载所需的 JNI 调用在混淆后依然稳定运行。
proguard-user.txt 中。如果不使用 Minify,则无需单独进行 ProGuard 设置。
# OZero Security - Unity Android ProGuard/R8 keep rules
-keep class com.unity3d.player.UnityPlayer { *; }
-keep class com.unity3d.player.UnityPlayerActivity { *; }
-keep class com.unity3d.player.UnityPlayerGameActivity { *; }
-keep class com.unity3d.player.UnityPlayerForActivityOrService { *; }
-keepattributes *Annotation*,InnerClasses,EnclosingMethod,Signature
# If your game adds custom Java/Kotlin bridge classes that OZero or your code
# calls through AndroidJavaClass / AndroidJavaObject, keep those classes too.
# Replace the package below with your own bridge package.
# -keep class com.yourcompany.yourgame.bridge.** { *; }
- 不要重命名、删除或重新打包
libOZeroSecurity.so。将插件保留在Assets/OZeroSDK/Plugins/Android/arm64-v8a下。如果您要分发 32 位版本,也请保留armeabi-v7a。 - 如果您使用 Build Integrity > Check Platform Native 和 Android SHA Keys,请务必在应用 Minify/R8 后,使用最终签名的 APK 或 AAB 进行测试。调试密钥库的指纹与发布密钥库的指纹是不同的。
- 如果只有在开启 Minify 之后,Android 日志中才出现 JNI 查找失败、安装来源检测失败或 APK 签名检查结果为空的情况,请先检查您的自定义桥接的 keep 规则。
- 对于商店分发的构建版本,请基于 IL2CPP、Release 签名、
Android Sha Keys中注册的预期 Android SHA-256 指纹,并在至少一台真机上进行干净运行测试后再做判断。
5
OZeroSecurityConfig 设置
OZeroSecurityConfig ScriptableObject 资产会在包导入时包含。在 Project 窗口中选择它即可查看并调整所有安全模块设置。在运行时通过 OZeroSecurityConfig.Instance 进行访问。
通用设置
这些是所有安全模块共享的顶级设置。本表中的默认值以代码中定义的序列化初始值为准。
| 字段 | Type | 默认值 | 说明 |
|---|---|---|---|
| developerSecret | string | "" | 用于保护 OZeroSV_File 和 OZeroSafePlayerPrefs 数据的项目级 secret。请使用 Config Dashboard 中的 Generate Secure Secret 按钮生成,不要手动随意输入字符串。每个游戏应使用不同值,并避免出现在文档、日志或公开仓库中。发布后如果更改该值,新版本将无法解密旧版本保存的受保护数据。 |
| enableLog | bool | false | 开启 Enable Debug Logs 会将 SDK 初始化、设置加载、许可证/遥测流程、检测事件等内部状态输出到 Unity Console 和 Player 日志中。这在开发/QA 阶段有助于查明原因,但可能暴露检测流程和模块状态,建议在生产构建中关闭。 |
| enableFailureDiagnostics | bool | false | 开启 Enable Failure Diagnostics 时,如果发生安全违规,本地诊断文件会保存到 Application.persistentDataPath。因文件内可能包含模块名、哈希、设备状态、安装包名以及部分运行时配置,建议仅在 QA 或客户服务支持时开启。按平台的默认保存位置:
|
developerSecret。发布后不要更改。更改该值后,新版本将无法解密现有 OZeroSV_File 和 OZeroSafePlayerPrefs 数据。
Managed UI(所有许可证)
Security Termination Notice、通用 dialog prefab 与 localization 对 Standard / Pro 全部开放。Pro 还显示 Build Integrity 与 Steam DRM 的 Managed Verification 重试、timeout 和 provider 设置。导入 optional package 前,请先从 Package Manager 安装 Unity UI 与 TextMeshPro。提示仅说明已经安排的终止,不能取消退出或延长 native deadline。
OZeroBuiltIn,或 Pro Managed Verification 使用 Built-In 时,都必须安装 optional TMP package。缺少条件会停止构建。Dedicated Server 不支持 Built-In UI,请选择 Disabled 或 custom callback/logging。图形项目可在 Window > OZero Security > Check Setup 中使用 Import Package 修复。
securityUiLanguageCode、配置的 fallback、英文顺序选择。不会直接显示 Managed Verification 服务器 Reason 原文。扩展请使用 Tools > OZero Security > Localization > OZero Security UI Text。
RuntimeNoticeReady 后适用。只显示安全的 OZ-SEC-* 代码,不暴露内部检测原文。
oz_boot.bin。缺失、格式错误、未签名、不匹配或 ABI 17 artifact 都会导致 build/runtime failure。oz_boot.bin 只包含明文环境标志,并不是篡改认证 artifact。
| 字段 | Type | 默认值 | 说明 |
|---|---|---|---|
| securityTerminationNoticePolicy | enum | Disabled | 所有许可证。选择 runtime fatal event 时不提示、OZero Built-In 或 custom callback。Low、Standard security preset 使用 Built-In,Strict 使用 Disabled。初始 fail-close 路径不会显示提示。 |
| securityUiDialogPrefabResourcePath | string | "" | 所有许可证。项目自有 security dialog prefab 的 Resources 路径。留空时使用 optional TMP package 默认值。旧 managedVerificationDialogPrefabResourcePath 值会自动迁移。 |
| securityUiLanguageCode | string | auto | 所有许可证。终止提示和 Managed Verification UI 的语言。auto 跟随 Application.systemLanguage。旧 managedVerificationLanguageCode 值会自动迁移。 |
| securityUiFallbackLanguageCode | string | en | 所有许可证。在内置英文安全文案之前使用的 fallback 语言。旧 managedVerificationFallbackLanguageCode 值会自动迁移。 |
| managedVerificationUiPolicy (Pro) | enum | BuiltInBlockingDialog | Pro Only 功能。选择 OZero Managed Build Integrity 和 Steam DRM 状态的用户界面流程。内置阻止型/提示型对话框需要 TextMeshPro,并且必须 import optional OZeroSecurity_BuiltInDialog_TMP.unitypackage。如果项目不 import 该 package,或要替换成自有 UI,请使用 Custom UI / Callback Only。 |
| managedVerificationRetryTimeoutSeconds (Pro) | int | 15 | 用户点击 Retry 后,进入 retry-timeout 状态前等待的最长时间。用于避免玩家无限期停留在验证等待状态。 |
| managedVerificationOnlineRequiredTimeoutSeconds (Pro) | int | 120 | 显示需要联网对话框后,在应用所配置 timeout action 前等待的最长时间。 |
| managedVerificationTimeoutAction (Pro) | enum | BlockSession | 验证未能在限定时间内恢复时的处理方式:继续显示对话框、阻止受保护会话、终止应用,或仅调用 callback 以便自定义流程处理。 |
| autoRetryManagedVerificationWhenNetworkRestored (Pro) | bool | false | 网络连接恢复后自动重试 Managed Verification。仅当游戏流程可以在没有玩家明确操作的情况下安全重试时启用。 |
Global Threat Response
决定确认安全威胁后游戏的响应方式。在测试期间请先检查回调和日志,在实际部署构建中请根据项目的运营策略选择给玩家的提示时间和终止策略。
| 字段 | Type | 默认值 | 说明 |
|---|---|---|---|
| forceQuitOnDetection | bool | true | forceQuitOnDetection 决定在检测到确切威胁时 SDK 是否自动终止游戏。关闭后只检查回调和日志,便于 QA,但在实际部署构建中可能导致被绕过的客户端继续运行,请慎重选择。 |
| fatalCallbackGraceSeconds | float | 10 | fatalCallbackGraceSeconds 是在检测到威胁后,游戏侧安全回调向玩家显示提示 UI 的最长容许时间。默认值为 10 秒。设置为 0 时会恢复之前的无提示直接退出行为。 |
接入问题自查指南
如果启用 OZero Security 后应用退出,或只在特定环境中出现问题,请先通过本地诊断文件确认是哪个模块触发。不要先凭感觉修改设置,而是根据文件中的 module、subCode、reason 按下面的表逐步排查。
OZ-SEC-* 支持代码,请在支持请求中附上该代码。没有弹窗并不代表与 OZero 无关:启动 fail-close、Boot-ACK/watchdog 终止、实际 crash 与 OS kill 都可能在 UI 前退出。请检查 ozero_abort.txt、Player log、Check Setup 和 build preflight 结果。Strict preset 的提示策略默认为 Disabled。
应用反复退出时先做什么
| 步骤 | 操作 | 如何查看 |
|---|---|---|
| 1 | 在 Unity 菜单中打开 Window > OZero Security > Config Dashboard,然后在 OZeroSecurityConfig 的 Key Common Settings > Enable Failure Diagnostics 中开启该选项。 | 仅在 QA 或支持排查原因时使用,不要在公开发布构建中保持开启。 |
| 2 | 在继续修改其他设置之前,用同一个构建复现一次问题。 | 如果复现前同时修改多个设置,会更难判断原因。请先保留当前状态下的诊断文件。 |
| 3 | 在 Application.persistentDataPath 中打开最新的 ozero_*_failure.log 文件。 | Build Integrity 也可能生成 ozero_integrity_failure.log 和更具体的文件,例如 ozero_integrity_platform_native_failure.log。如果应用在非常早的阶段退出而没有 failure log,请同时检查 ozero_abort.txt 和 Player log。 |
| 4 | 收集文件后,再次关闭 Enable Failure Diagnostics。 | 除非你的支持流程明确需要,否则不要在公开发布构建中保持本地诊断文件开启。 |
常见诊断字段
| 字段 | 含义 | 下一步检查 |
|---|---|---|
| module | 产生事件的保护区域。 | 先查看同一类 FAQ 行,必要时再打开对应的手册章节,例如 Build Integrity、Speed & Time Hack、Injection、Install Source 或 License。 |
| subCode / checkName | 较短的原因代码或检查名称。 | 用它在下方选择同一类症状。例如 platform_native 表示先检查平台、模拟器、签名、root 或越狱相关设置。 |
| reason / subReason | SDK 触发原因的可读摘要。 | 这只是排查提示,不是最终结论。请先看 module 和 subCode,再在下方症状表中查找相同关键词的行。 |
| platform / buildType | 应用运行的平台以及构建类型。 | 区分 QA 模拟器/开发构建问题和真实发布设备问题。 |
常见症状和第一步检查
| 症状 | 可能区域 | 先尝试什么 |
|---|---|---|
| 应用 OZero 后,应用启动后立即退出。 | 启动过程中可能触发了安全响应。 | 开启 Failure Diagnostics,复现一次,然后读取 module、subCode 和 reason。如果没有 failure log,请检查 ozero_abort.txt 和 Player log,并在 Unity 中运行 Window > OZero Security > Check Setup。 |
| Android 模拟器中应用反复退出。 | Build Integrity 或平台原生检查可能将模拟器视为不支持的运行环境。 | 用于模拟器 QA 时,可放宽 blockEmulator (Android) 或相关平台检查策略。如果你的游戏发布版需要阻止模拟器,请在商店发布前重新启用发布策略。 |
| 许可证激活失败,或可选的 Pro 服务器功能不可用。 | 可能是许可证密钥、应用身份、网络或服务器签名密钥问题。 | 打开 许可证问题解决,检查实际日志,例如 LICENSE_NOT_FOUND、ANDROID_BUNDLE_ID_MISMATCH、timeout 或 signature verification failure。 |
| Build Integrity 或 manifest 验证失败。 | manifest 可能缺失、过旧、未签名,或与当前构建不匹配。 | 运行 Check Setup,重新生成 integrity manifest,验证 signing key,并进行 clean build。代码、IL2CPP 输出、watched file 或 manifest signing key 改变后都需要重新生成。 |
| 暂停、慢动作或倍速后应用退出。 | 直接写入 Time.timeScale 的代码可能看起来像时间操控。 | 使用 OZeroTime.timeScale。TimeScaleTamperExemptions 只用于已验证的插件或 legacy adapter script。 |
| 长时间加载画面中应用退出。 | 主线程被阻塞了很长时间(Watchdog 会等待被阻塞的线程,但会关闭停顿超过五分钟的进程)。 | 仅将可信的长加载边界包在 OZeroWatchdog.BeginLoadingGrace 中。不要用它隐藏正常 gameplay 卡死。 |
| Android 在启用 Minify/ProGuard/R8 前正常,启用后退出。 | Unity 或自定义 Android bridge class 可能被移除或重命名。 | 应用 Android ProGuard / R8 keep rules,尤其要保留 Unity bridge class 和项目调用的自定义 Java/Kotlin bridge class。 |
| Windows Standalone 在干净 PC 上立即退出。 | OZero native plugin 或 VC++ runtime 可能未能加载。 | 安装 Microsoft Visual C++ Redistributable 2015-2022 (x64),并确认 OZero native plugin 已包含在构建输出中。 |
| 正常 overlay 或录屏工具触发 Injection 事件。 | Injection/Hooking 正在观察已加载模块。 | 请先在 Failure Diagnostics 或 Pro 门户确认检测到的模块名、哈希和签名者。若是正常程序,Pro 客户在门户(注入 → 检测到的模块)中点击该行的允许,下次列表刷新时即对所有设备生效。Standard 客户将哈希加入 injectionWhitelistEntries 并发布新构建。无法说出名字的模块请勿允许。 |
| 构建因 optional Built-In TMP Dialog package 尚未就绪而停止。 | 已选择 Built-In Managed Verification UI policy,但缺少 TextMeshPro、TMP Essential Resources、OZero 对话框组件或默认 prefab。 | 打开 Window > OZero Security > Check Setup,在 Built-In TMP dialog 错误项中点击 Import Package。等待 package import 与 script compilation 完成,重新执行 Check Setup 后再构建。若游戏提供自定义 UI,请选择 Custom UI / Callback Only。 |
构建完整性验证器设置
配置构建完整性检查器,以检测被修改的游戏文件、调试器连接以及异常的运行环境。
| 字段 | Type | 默认值 | 说明 |
|---|---|---|---|
| Activate Build Integrity | checkbox | On | Dashboard 中用于启用 Build Integrity 模块的复选框。内部会映射到 useIntegrity。 |
| validateOnStartup | bool | true | 游戏启动后立即执行构建完整性检查。 |
| validateInEditor | bool | false | 在 Unity 编辑器中也执行验证(对测试有帮助,建议在常规开发期间关闭)。 |
| enablePeriodicValidation | bool | true | 在游戏运行期间也循环进行完整性验证。如果您的构建不足以通过启动时的单次验证,建议开启此功能。 |
| periodicCheckInterval | float | 300 s | 游戏运行期间重复执行 Build Integrity 验证的基础间隔(秒)。设置为 0 或更小值可关闭定期检查。 |
| periodicCheckJitterPercent | float | 35% | 给验证周期添加随机变化,使检查时机更难被预测。公开文档中未提供具体周期与范围,推荐使用默认值。 |
| timingAnomalyConsecutiveRequired | int | 7 | 确定基于时间的异常信号需要累积多少次才判定为调试器时序漂移。强烈的调试器信号可能仍会导致立即失败。 |
| timingAnomalyWindowSeconds | float | 900 s | 用于累加时间异常信号的时间窗。越长容错越高,Strict 使用更短的时间窗。 |
| timingAnomalyFrameHitchSuppressionSeconds | float | 20 s | 在发生了如场景加载、着色器编译、GC 或 OS 调度延迟等大规模帧卡顿后,在一定时间内放宽对基于时间的调试器检测。 |
| checkAssemblyHash | bool | true | 根据构建时生成的 manifest 验证已编译程序集的哈希值。 |
| checkDebugger | bool | true | 检测调试器连接,以及类似调试器行为的运行时计时异常。 |
| failOnDebugBuild | bool | false | 将 Unity 的 Debug 构建视为违规(建议用于发布构建)。 |
| checkPlatformNative | bool | true | 执行平台原生检查。根据平台不同,可能检查 root/jailbreak、APK 签名、运行环境、代理或分析工具信号。 |
| failIfManifestMissing | bool | false* | Inspector 默认关闭,方便开发阶段迭代。但在非 development 的 player build 中会强制开启;如果 oz_manifest.ozero 缺失或无法读取,将被视为违规。 |
| failIfAssemblyHashBlobMissing | bool | false* | Inspector 默认关闭,方便开发阶段迭代。但在非 development 的 player build 中会强制开启,避免攻击者通过删除生成的程序集哈希 blob 来静默绕过哈希验证。 |
| requireCodeSignature (Windows) | bool | false | 主执行文件必须具有代码签名。仅限 Windows。 |
| blockVirtualMachine (Windows) | bool | false | 阻止游戏在虚拟机内部运行。仅限 Windows。 |
| blockHyperV (Windows) | bool | false | 拦截 Hyper-V VMBus 信号。WSL2、Docker Desktop 或 Windows Sandbox 用户可能也会被阻止,因此仅限在受控环境中审慎使用。 |
| blockNetworkProxies (Windows) | bool | false | 检测疑似网络代理、数据包检测或流量分析工具的运行信号。用于竞技型构建,但请事先测试是否存在误报可能。 |
| blockReverseEngineeringTools (Windows) | bool | false | 检测疑似逆向工程或调试工具的运行信号。请在开发/QA 环境与线上环境分别进行验证。 |
| blockSystemMonitorTools (Windows) | bool | false | 检测疑似进程/系统监控工具的运行信号。请注意普通用户环境中可能存在的误报情况。 |
| il2cppHashGameAssembly | bool | true | 对 Windows IL2CPP 构建的 GameAssembly.dll 进行哈希验证。这是 IL2CPP 文件保护的最低建议项。 |
| il2cppHashGlobalGameManagers | bool | false | 将 globalgamemanagers 加入 Windows IL2CPP 文件哈希范围。代码默认关闭,但 Standard 和 Strict 预设会开启。 |
| il2cppHashSharedAssets | bool | false | 将 sharedassets* 文件加入 Windows IL2CPP 文件哈希范围。代码默认关闭,但 Standard 和 Strict 预设会开启。 |
| il2cppHashSceneFiles | bool | false | 将 level* 等 Unity 场景文件加入 Windows IL2CPP 文件哈希范围。代码默认关闭,但 Standard 和 Strict 预设会开启。 |
| il2cppHashResourcesAssets | bool | false | 将 resources.assets 添加到 IL2CPP 验证范围内。有助于 Strict 模式,但请先测试补丁流程。 |
| il2cppAdditionalWatchedFiles | List<string> | — | 当项目包含额外的原生 payload 时,指定需要受监控的 Windows IL2CPP 额外输出文件。 |
| blockEmulator (Android) | bool | true | 仅限 Android。将模拟器或不支持的运行时信号视为完整性违规。建议在 QA/模拟器测试期间放宽,而在生产构建中使用更严格的策略。 |
| blockSystemRwMount (Android) | bool | true | 仅限 Android。若系统分区处于可写状态,或出现类似 root 的 mount 状态,将被视为完整性违规。Standard 预设会放宽此项,以减少在 unlocked/rooted QA 设备上的误报。 |
| linuxHostLayerPolicy (Linux) | enum | Monitor | 当 LD_PRELOAD、LD_AUDIT、WINEDLLOVERRIDES 等 Linux 宿主层介于游戏与操作系统之间时的处理方式。Off 忽略,Monitor(包括 Strict 在内所有预设的默认值)只记录不关闭游戏,Block 视为违规。正常的 Steam Linux 和 Proton 玩家也会使用这些层,因此只有在验证过玩家环境的游戏中才使用 Block。 |
| protonPolicy (Linux / Proton) | enum | Monitor | Windows 构建在 Proton 或 Wine 下运行时的策略,取值同上。默认的 Monitor 不影响 Steam Deck 与 Proton 玩家游玩,同时把环境记录到遥测中。 |
| acknowledgeHostLayerBlock | bool | false | 启用 Block 宿主层或 Proton 策略前必须先打开的确认开关。未确认就配置 Block 时构建 preflight 会失败,避免误发会关闭正常 Linux 玩家游戏的构建。 |
| nativeModuleInventoryPolicy | Off | Monitor | Block | Block (Low: Monitor) | 仅适用于 Windows 和 Linux 播放器。构建会把游戏目录下所有原生模块(*.dll / *.so)记录到已签名的完整性清单中。启动时 Build Integrity 枚举目录并哈希记录的文件,不在列表中的原生文件或哈希已变化的文件视为违规。Block(默认)终止,Monitor记录后继续,Off跳过检查。Injection 检测器用同一列表识别游戏自身的插件。若允许玩家向游戏目录添加原生模组加载器,请选择 Monitor。Low 预设为 Monitor。 |
| androidShaKeys | List<string> | — | 预期的 APK 签名证书 SHA-256 指纹列表。如果安装的 APK 没有用这些密钥之一签名,验证将失败(仅限 Android)。 |
| expectedBundleIds (iOS) | List<string> | — | 允许的 iOS Bundle ID 列表。留空则跳过 Bundle ID 检查。 |
| excludedAssemblies | List<string> | — | 要从哈希验证中排除的程序集名称列表(不带 .dll 扩展名)。用于在运行时发生改变的程序集(例如生成的代码)。 |
| checkIntegrityWithServer (Pro) | bool | false | 开启 Pro 的 nonce → attest 流程。本地 Build Integrity 通过后,SDK 会向 OZero 提交构建完整性证据。与 attestationVerificationMode=OZeroManaged、optional OZero TMP dialog package 和内置 Managed Verification UI 一起使用时,面向用户的验证流程不需要游戏代码。 |
| attestationVerificationMode (Pro) | enum | CustomerGameServer | 选择 OZA 令牌的最终判定方。OZeroManaged 由 SDK 向 OZero 请求托管会话判定。使用 CustomerGameServer 时,客户服务器签发 binding,客户端调用 RequestGameServerAttestation(audience, challenge, sessionId),再由客户服务器通过 OZero API 验证 OZA 令牌。 |
| attestationNetworkPolicy (Pro) | enum | RequireOnlineRevalidation | 无法完成或刷新服务器验证时,将进入需要联网状态。与 OZeroManaged 和内置 UI 一起使用时,SDK 对话框会自动显示重试、timeout 和阻止状态。 |
| manifestSigningPublicKey | string | "" | 用于验证已签名 Build Integrity manifest 的 Base64 RSA-2048 公钥。请在 Window > OZero Security > Config & Dashboard 中通过 Generate Key Pair 生成。它不同于 OZeroLicenseConfig 中填写的客户门户服务器密钥。 |
| requireManifestSignature | bool | false* | Inspector 默认在生成密钥前关闭,方便开发。在发布 player build 中,manifest 签名验证会被强制开启,因此发布前必须生成密钥对。 |
| manifestSigningPrivateKeyPath | string | "" | 构建期间用于 manifest 签名的私钥 PEM 路径。留空时,OZero 使用 [ProjectRoot]/OZeroSigningKeys/manifest_private_key.pem。该路径仅用于 Editor,不会包含在 player build 中。 |
使用默认 UI 的 OZero 服务器 attestation
无代码流程:开启checkIntegrityWithServer,将 attestationVerificationMode 设为 OZeroManaged,先 import optional OZero TMP dialog package,并在 General Settings 中保持 Managed Verification UI 策略使用内置对话框。SDK 会获取 nonce、提交 attest 证据、向 OZero 请求托管会话判定,并通过默认对话框显示需要联网、重试、timeout、警告或阻止状态。如果要替换默认 UI、不使用 TextMeshPro,或添加自有 telemetry/session gate,请注册 RegisterUserManagedVerificationStateCallback。
💡 Tip: 用更强策略保护线上构建
Pro Strict Attestation 是可在客户门户的 Pro 策略中开启的强化模式。在此模式下,OZero 服务器不会立刻签发 OZA 令牌,而是先确认请求是否来自门户中已登记的有效构建。服务器还会检查提交的 nonce、manifest hash、platform、SDK version 和应用标识是否属于同一次请求流程。同时,debugger、platform、speedhack、injection 检查结果必须正常,并且 assembly/file/IL2CPP hash 至少有一项已验证。若条件不满足,OZA 令牌不会签发,因此发布新构建前,请先在客户门户登记该构建版本与 manifest hash。Events
| Event | Description |
|---|---|
| OnValidationPassed | 当本地完整性检查顺利完成而无违规时发生。此时 Pro 服务器证明 (attestation) 可能仍在进行中。 |
| OnAttestationPassed | 仅在实际颁发了 Pro OZA 证明 (attestation) 令牌后触发。请使用此事件或 AttestationToken.IsValid(nowMillis) 来校验,只有通过后才能进行游戏服务器登录、PvP、排名、货币结算等流程。 |
| OnValidationFailed | 当完整性检查检测到违规时触发。同时也会随着 ModulationType.BuildIntegrity 触发全局的 onHackDetected 事件。 |
生成 RSA 签名密钥(Build Integrity Manifest)
Build Integrity 可以为构建时生成的 manifest 添加 RSA 签名。该 manifest 包含运行时要验证的 assembly hash 与文件完整性信息。对于发布构建,如果 manifest 本身被替换,验证基准就不再可信,因此发布前请在 Window > OZero Security > Config & Dashboard 中点击 Generate Key Pair 生成 manifest 签名密钥对。
构建时,OZero 会使用私钥为 manifest 签名。私钥不会包含在 player build 中,构建中只包含存储在 OZeroSecurityConfig 里的公钥。运行时,SDK 会用公钥确认 manifest 签名。如果签名有效,SDK 会认为“构建时生成的 manifest 仍保持原样”,并以该 manifest 作为 assembly/file hash 检查的可信基准。如果签名缺失或不匹配,则 manifest 可能已被替换或编辑,因此 Build Integrity 会将其视作失败。之后的处理会按照 Response Settings 执行,例如记录日志、触发回调或关闭应用。
生成密钥对
- 在 Unity 编辑器中打开 Window > OZero Security > Config & Dashboard。
- 在检查器 (Inspector) 中展开 Build Integrity 选项卡。
- 勾选 Require Manifest Signature 复选框。
- 点击 Generate Key Pair 按钮。
- OZero 会生成用于 Build Integrity manifest signing 的密钥对。公钥存储在
OZeroSecurityConfig中,私钥 默认保存在以下路径:[ProjectRoot]/OZeroSigningKeys/manifest_private_key.pem - 将显示指示私钥位置的确认对话框。点击 OK 关闭它。
- 私钥文件存储在
Assets/文件夹 外部,以防止 Unity 将其打包进构建中。切勿将其移入Assets/中。 - 请将
OZeroSigningKeys/从版本控制中排除。使用 Git 时加入.gitignore,使用 SVN 时加入svn:ignore。将私钥提交到仓库是严重的安全风险。 - 请将私钥备份到安全位置。任何拥有此文件的人都可以为该项目创建有效的 Build Integrity manifest。
- 如果丢失私钥,已发布的旧构建仍会继续运行。但是下一次更新构建无法再用旧密钥签署新的 manifest。请生成新的密钥对,重新生成 integrity manifest,并通过 clean build 发布更新。公钥、fingerprint、manifest 和 signature 必须互相匹配,否则 Build Integrity 验证可能失败。
自定义私钥路径(CI/CD 和团队环境)
Manifest Signing Private Key Path 字段不是用来选择 Generate Key Pair 生成新密钥的位置。它用于告诉构建和验证工具从哪里读取 PEM 私钥。Generate Key Pair 会在项目默认路径生成密钥,并把该路径记录到 Config 中。对于 CI/CD 或团队构建,请先把同一个 PEM 还原到安全路径,再让此字段指向该路径。
- CI/CD 管道 — 将 PEM 保存为 CI Secret,并在 Unity 构建前还原。构建 runner 不需要长期把私钥保存在磁盘上。
- 团队环境 — 使用只有发布构建负责人或构建 runner 可访问的安全挂载路径或 Secret Manager。所有发布构建机器都必须使用同一个 PEM。
验证现有密钥对
如果要确认磁盘上的私钥是否与 OZeroSecurityConfig 中保存的公钥一致,请点击 Validate Key Pair。OZero 会从 PEM 重新推导公钥,并与 Config 中的公钥比较。如果不匹配,请还原对应的私钥,或生成新的密钥对,然后进行 clean build。
- 当私钥丢失或泄露时。
- Validate Key Pair 报告不匹配(密钥未同步)时。
- 作为既定安全策略的一部分,有意轮换密钥时。
重新生成后,已经发布的旧构建仍会使用自身内置的密钥和 manifest 继续运行。下一次更新构建必须让新的公钥、fingerprint、manifest 和 signature 作为同一套匹配数据使用。请重新生成 integrity manifest,并通过 clean build 发布。
双指纹密钥轮换
点击 Generate Key Pair 按钮后,公钥会保存在 OZeroSecurityConfig 中,同时识别该密钥的 fingerprint 也会记录在 Assets/OZeroSDK/Scripts/Security/BuildIntegrity/OZeroManifestTrustAnchor.cs 中。运行时 SDK 会先确认 asset 中的公钥是否匹配受信任的 fingerprint,然后才接受已签名的 manifest。
ExpectedPublicKeyFingerprintHex 是当前构建使用的密钥 fingerprint。PreviousPublicKeyFingerprintHex 是只在密钥轮换期间使用的辅助槽位。它不能替代 manifest 重新生成,也不是服务端密钥管理功能。OZeroSecurityConfig 中的公钥、fingerprint、manifest 和 signature 仍然必须基于同一把密钥组成一套匹配数据。
安全的密钥轮换步骤
- 备份当前指纹。 打开
Assets/OZeroSDK/Scripts/Security/BuildIntegrity/OZeroManifestTrustAnchor.cs,将ExpectedPublicKeyFingerprintHex的值复制到临时记录中。 - 注册到旧密钥槽。 在同一文件中,将第 1 步复制的值粘贴到
PreviousPublicKeyFingerprintHex中并保存。 - 生成新的密钥对。 打开 Window > OZero Security > Config & Dashboard,在 Build Integrity 区域点击 Generate Key Pair。新的公钥和 Expected fingerprint 会自动更新。请保留第 2 步写入的 Previous 值。
- 重新生成 integrity manifest,并制作 clean build。 新的 manifest 和 signature 必须基于新的私钥生成。
- 保留更新过渡期。 已发布的旧构建会继续使用自身内置的密钥和 manifest 运行。Previous 槽位是在新构建发布过程中减少回滚或构建产物混用问题的安全措施。对于在线运营游戏,通常可以按 1 到 4 周作为参考。
- 清空旧密钥槽。 当旧版本的使用量足够低后,将代码改回
PreviousPublicKeyFingerprintHex = "",再次发布后轮换即告完成。
已经发布的旧构建不需要知道新密钥也能继续运行,因为旧构建中已经包含当时的公钥、fingerprint、manifest 和 signature。Previous 槽位是在新的发布线中进行受控密钥轮换时使用的安全措施。只在需要过渡时保留,之后的版本中建议清空。
故障排除
遇到问题时,先按简单顺序检查:没有密钥就执行 Generate Key Pair,已有密钥就执行 Validate Key Pair,然后重新生成 integrity manifest 并制作 clean build。大多数问题来自以下四项之一不同步:私钥 PEM、OZeroSecurityConfig 中的公钥、OZeroManifestTrustAnchor 中的 fingerprint、生成出的 manifest/signature 文件。
ExpectedPublicKeyFingerprintHex 可能为空。发布构建在缺少 fingerprint 时会为了安全直接失败。解决:执行 Generate Key Pair,等待 Unity 重新编译完成后制作 clean build。如果该值已经存在,请执行 Validate Key Pair,并清空构建输出文件夹后重新构建。
当 OZeroSecurityConfig.Integrity.ManifestSigningPublicKey 为空时触发。解决:打开 Window > OZero Security > Config & Dashboard,在 Build Integrity 区域点击 Generate Key Pair,然后重新构建。
当 OZeroSecurityConfig 的公钥与 OZeroManifestTrustAnchor 中记录的 fingerprint 不一致时发生。只还原了密钥对的一部分,或手动修改了生成文件时经常出现。解决:执行 Validate Key Pair。如果报告不一致,请还原匹配的 PEM,或生成新的密钥对,然后重新生成 integrity manifest 并制作 clean build。如果只有已发布构建出现此消息,也请确认 APK 或可执行文件是否被重新打包。
轮换后的密钥数据中可能有一部分仍是旧值。请确认公钥、fingerprint、manifest、signature 是否都基于同一把密钥。解决:只有在需要过渡期时才把旧 fingerprint 保留在 PreviousPublicKeyFingerprintHex,然后依次执行 Validate Key Pair、重新生成 integrity manifest、清理构建输出文件夹、重新进行 clean build。
CI 不会自动拥有本地 PC 上的 OZeroSigningKeys/manifest_private_key.pem 文件。解决:将 PEM 保存在 CI Secret 中,并在 Unity 构建前还原到默认路径,或还原到 Manifest Signing Private Key Path 指定的路径。所有发布 runner 都必须使用同一个 PEM,并建议在发布环境中执行一次 Validate Key Pair 进行确认。
安装来源设置
限制游戏仅在通过已批准的商店或途径安装时才能运行。这有助于防止侧载或重新打包的 APK。
| 字段 | Type | 默认值 | 说明 |
|---|---|---|---|
| Activate Install Source | bool | true | 激活 Install Source 模块。 |
| allowGooglePlayStore | bool | true | 允许通过 Google Play 商店安装。 |
| allowSamsungGalaxyStore | bool | false | 允许通过 Samsung Galaxy Store 安装。 |
| allowAmazonAppstore | bool | false | 允许通过 Amazon Appstore 安装。 |
| allowHuaweiAppGallery | bool | false | 允许通过华为 AppGallery 安装。 |
| allowOneStore | bool | false | 允许通过 ONE Store (韩国) 安装。 |
| allowXiaomiGetApps | bool | false | 允许通过小米 GetApps 安装。 |
| allowOppoAppMarket | bool | false | 允许通过 OPPO App Market 安装。 |
| allowVivoAppStore | bool | false | 允许通过 Vivo App Store 安装。 |
| allowADB | bool | false | 允许通过 ADB (Android Debug Bridge) 安装。仅供内部测试时启用。 |
| allowDetectionFailed | bool | false | 当 Android installer package 查询本身失败时仍允许启动。这不同于 ADB 返回空 installer 值,表示 JNI 或平台 API 没有完成查询。发布构建中通常应保持关闭。 |
| allowUnknownSources | bool | false | 允许不在内置商店列表、也不在 customAuthorizedPackages 中的 installer package。仅在需要区域商店分发并完成真机测试后使用。 |
| enableServerSync (Pro) | bool | false | 仅限 Pro。本地检测后将 installer package 发送到 /v1/install-source/verify,由服务器应用托管 allowlist 并记录审计日志。如果服务器尚未配置规则,会按 opt-in 行为允许该来源。 |
| customAuthorizedPackages | List<string> | — | 额外允许的安装程序包名称(例:com.yourcompany.launcher)。 |
| reportViolationToCallback | bool | true | 若检测到未被许可的安装来源,则向服务器发送报告。 |
| logRawInstallerPackage | bool | true | 记录原始 installer package name。QA 阶段查找自定义商店包名时很有用,但发布前请确认日志中暴露这些信息是否合适。 |
Steam 防盗版配置
对在 Steam 发售的 PC 端查明 Steam 调用通道、App ID、权限情况、发布档设定。Standard 是局端核实,要想通过 Steam 取得牢固权证唯有依赖 Pro 级别之内的 Steam Attestation 来供给。
| 字段 | Type | 默认值 | 说明 |
|---|---|---|---|
| Activate Steam Anti-Piracy | checkbox | Off | 启动 Steam 防盗版侦察的勾选位。有鉴于 Steam App ID 及铺货形态随项目多变,初始定作 Off。理应首先处 observe/QA 情境查其状,尔后针对发行端上紧限制。 |
| expectedSteamAppId | int | 0 | 项目的 Steam App ID 标识。设使之前倚仗除错用 AppID 480 则应在交差前必定修正为正当的 App ID。 |
| requireSteamLaunch | bool | true | 断明是否借由 Steam 而非执行文件独立启航。需厘清研发内部自测操作与外发政策。 |
| requireSteamApiInit | bool | true | 探察 Steam API 奠定起手成效。研发时期直接试玩因 Steamworks 配定生变有败局几率,决断还得以确实的 Steam 散播流为基准。 |
| requireSubscribedCurrentApp | bool | true | 核准当前挂号的 Steam 主能否掌握此件及赋予进入权限。此是机内考核,至于服端盘查必得用 Pro 解决。 |
| requiredDlcAppIds | List<int> | — | 需要检查所有权的 Steam DLC App ID 列表。如果有付费 DLC 或必需 DLC,请添加对应的 App ID。 |
| blockSteamAppIdTxtInRelease | bool | true | steam_appid.txt 是仅用于不通过 Steam 进行本地启动测试的开发文件。如果它留在发布构建中,可能允许绕过 Steam 启动,因此 OZero 会将其视为违规。发布前请从项目根目录和构建输出中移除它。 |
| validateSteamApiDllHash | bool | false | 通过将 Steam API DLL 的哈希值与已知的 SHA-256 值进行比较来进行验证。 |
| allowFamilySharing / allowFreeWeekend / allowTimedTrial | bool | true | 控制是否允许 Steam 家庭共享、免费周末以及限时试用权限状态。 |
| requireSteamBuildIdNonZero | bool | false | 如果 Steam 返回的 Build ID 为 0,或无法取得 Build ID,则按违规处理。请先确认 Steam depot/build 发布流程能正确取得 Build ID 后再开启。 |
| expectedSteamApiDllSha256Hashes | List<string> | — | 启用 validateSteamApiDllHash 后用于比对的 Steam API 文件允许 SHA-256 列表。请按实际发布的 Steamworks SDK 版本、平台和发布分支分别登记哈希。 |
| requireValveSignedSteamApi | bool | true | 要求 Steam API 可再分发文件匹配 Valve 的平台身份。Windows 使用 Authenticode 签名者验证,macOS 使用 Valve Team ID 验证,其他平台应使用 SHA-256 验证作为 fallback identity check。 |
| detectKnownSteamEmulators | bool | true | 在可执行文件附近查找 Goldberg、CreamAPI、SmartSteamEmu、ColdClientLoader 等 Steam 模拟器常留下的文件或文件夹。发现这些痕迹时,会视为可能不是正常 Steam 启动。 |
| requireSteamEnvironmentConsistency | bool | true | 在发布构建中,检查 Steam 是否返回有效的个人 SteamID 和非 0 的 Build ID。Steam 客户端/库路径的预检查只记录警告日志,SteamID 或 Build ID 不正确时才作为违规处理。 |
| observeSteamAuthTicketHeuristic | bool | true | 只检查本地 Steam 认证票据的大小和基本格式,作为参考信号。OZero 不会记录票据内容,也不会仅凭这一项检查阻止游戏运行。 |
| detectionAction | OZeroSteamDetectionAction | Callback | 决定 Steam 验证失败时 SDK 在本地处理到什么程度。Observe 只记录日志和诊断,Callback 会调用游戏回调,Block 会把该事件作为阻止策略处理。应用是否真正退出仍由 Global Threat Response 设置决定。 |
| reportViolationToCallback | bool | true | 在 Callback 模式下 Steam 验证失败时,是否把事件发送到普通 OZero 安全回调。只有在 QA 构建中想保留日志但不触发游戏回调时才关闭。 |
| forceSteamAntiPiracyObserveOnly | bool | false | 临时将 Steam 验证失败只记录为日志和诊断信息,不触发回调或阻止行为。仅用于测试特殊 Steam 启动环境,发布前请重新关闭。 |
| checkSteamDrmWithServer (Pro) | bool | false | Pro 专用。启用基于服务器证据的 Steam DRM 验证。需要用服务器证据确认 Steam 所有权,而不是只依赖本地 Steamworks 状态时使用。 |
| steamDrmVerificationMode (Pro) | OZeroSteamDrmVerificationMode | OZero Managed | 选择 Steam DRM 验证由谁负责。OZeroManaged 由 SDK 直接调用 OZero /v1/steam/attest、管理 activation cache,并在 import optional OZero TMP dialog package 后将面向用户的状态交给默认 Managed Verification UI。CustomerGameServer 适用于客户服务器接收 OZA token 和 Steam ticket 后调用 OZero server API 的结构。 |
| steamDrmNetworkPolicy (Pro) | OZeroSteamDrmNetworkPolicy | RequireOnlineRevalidation | 需要最新服务器再验证但网络或服务器不可用时,将进入需要联网状态。与 OZeroManaged 和已 import 的内置 UI package 一起使用时,会自动显示重试流程。 |
OZeroManaged 时,受保护的首次启动会通过 OZero 服务器在线取得有效 activation token。之后,只要 cache token 有效且离线宽限期仍然存在,就可以允许离线运行。如果令牌已过期、宽限期已结束,或需要重新验证时用户处于离线状态,import OZeroSecurity_BuiltInDialog_TMP.unitypackage 后的默认 Managed Verification UI 会自动处理需要联网提示、Retry 按钮、retry timeout 和阻止状态。
OZero 服务器托管的 Steam DRM
启用 checkSteamDrmWithServer,将 steamDrmVerificationMode 设置为 OZeroManaged,import OZeroSecurity_BuiltInDialog_TMP.unitypackage,并在 General Settings 中保持 Managed Verification UI 策略使用内置对话框。SDK 会把 Steam auth ticket 发送到 OZero,管理 activation token cache,并通过默认 UI 显示需要联网、重试、timeout、警告或阻止状态。保留默认 UI 时,不需要编写 callback 或 retry 代码。
客户游戏服务器 Steam 验证
启用 checkSteamDrmWithServer 并使用 CustomerGameServer。客户服务器签发 audience、256-bit hex challenge 和 session ID;本地验证后,客户端调用 RequestGameServerAttestation(...),并把 callback 中的 OZA token、相同 binding 值和 Steam Auth Ticket 发给服务器。服务器调用 /v1/validate 和 /v1/steam/attest,两个 server API key 都只能保存在服务器端。
设备绑定设置
保存首次信任设备的识别信息,并在后续启动时与当前设备进行比较。可用于发现同一账号或受保护存档是否被移动到其他设备。
运营中的使用方式
在 Pro 中,Device Binding 可以作为运营工具使用。服务器会记录每个许可证对应的设备指纹,因此可以阻断反复出现风险信号的设备,减少简单的许可证共享,并在不禁用整个许可证的情况下支持合法的设备更换。
阻断反复出现风险的设备
请在查看 SpeedHack、Injection、Install Source、Build Integrity 等安全事件后使用。被阻断的设备在下一次激活、设备注册、设备验证或策略检查时会收到 DEVICE_BLOCKED。
支持合法设备更换
当真实玩家更换手机、重装 OS、更换硬件,或因正常环境变化导致设备信息变化时,使用 Reset Token。令牌仅 5 分钟有效,只能使用一次,并绑定到准确的许可证和设备。
限制可注册设备数量
在 Pro 许可证中,maxDevices 限制一个许可证可注册的活动设备指纹数量。被阻断的设备不会自动变成安全的空槽位;重置或删除应在客服确认后处理。
Reset Token 支持流程
- 先通过正常客服流程确认玩家账号,以及为什么需要重置设备。
- 进入 Customer Portal → Device Binding,选择目标设备并发放 Reset Token。
- 令牌只能通过已认证的支持渠道发送。不要留在公开聊天、截图或长期保存的工单中。
- 游戏或支持用 UI 应将令牌传递给
OZeroDeviceBindingDetector.Instance?.ClearStoredFingerprint(token.Trim())。 - SDK 接受令牌后,重启应用或再次运行受保护的启动流程,当前设备指纹就会重新注册。
| 字段 | Type | 默认值 | 说明 |
|---|---|---|---|
| Activate Device Binding | bool | true | 开启 Device Binding 检查。开启后,SDK 会在启动时创建本地设备指纹,并与已保存的值比较。 |
| hardwareChangeTolerance | int (0–3) | 1 | 决定设备指纹中的多少项发生变化时仍视为同一设备。0 表示严格,3 对较大的硬件/OS 变化更宽容。默认值 1 用于吸收较小的 OS 或固件变化。 |
| storageKey | string | "ozero_dfp" | SDK 用来保存加密本地设备指纹的 PlayerPrefs 键。只有当游戏已经使用相同键时才需要修改。 |
| enableServerSync (Pro) | bool | false | 仅限 Pro。首次使用时通过 /v1/device/register 注册设备指纹,后续启动时通过 /v1/device/verify 与服务器记录比较。网络或服务器故障本身不会阻止游戏,但服务器明确返回 DEVICE_BLOCKED、FINGERPRINT_MISMATCH、DEVICE_LIMIT_REACHED 等拒绝时,会作为 Device Binding 违规处理。 |
| maxDevices (Pro) | int | 0 | 显示一个许可证可注册的设备数量参考值。0 表示不限制。实际上限由服务器许可证记录或 Customer Portal 策略执行,因此只修改客户端中的这个值不会提高正式运营上限。 |
Speed & Time Hack 设置
对比 Unity 时间、原生基准时钟、后台计时器以及 Web/Pro 服务器时间,用来检测加速或减速游戏的操控,以及设备时钟变化。
| 字段 | Type | 默认值 | 说明 |
|---|---|---|---|
| Activate Speed & Time Hack | bool | true | 开启 Speed & Time Hack 检查。开启后,SDK 会在运行时监控时间流速比例和设备时钟变化。 |
| autoStart | bool | true | 游戏执行时自动开始检测。如果禁用,需通过 OZeroSpeedHackDetector.StartDetection() 手动启动。 |
| checkInterval | float | 1.0 s | Unity 时间与基准计时器的比较周期。数值越低反应越快,但 CPU 成本和误报可能增加。 |
| requiredDetections | int | 3 | 连续出现多少次异常信号后才按违规处理。若不稳定设备上误报较多,可适当调高。 |
| ratioTolerance | float | 0.15 | Unity 时间与原生基准时间之间允许的正常差异比例。请先使用默认值,只在真实游玩中出现误报时再调整。 |
| maxAllowedRatio | float | 4.0 | 时间流速比例超过此值时,会被视为强烈的速度作弊信号。普通游戏建议保留默认值。 |
| detectSlowHack | bool | false | 不仅检测加速,也检测减速操控。默认值为 false。请先测试游戏中的慢动作、过场和有意速度变化,再决定是否开启。 |
| enableTimeScaleDetection | bool | true | 检查 Time.timeScale 是否被未授权代码或内存编辑修改。游戏内有意改变速度时,请使用 OZeroTime.timeScale。 |
| hackDetectMultiplier | float | 1.3 | 当时间流速远高于 maxAllowedRatio 时,将其作为更强可疑信号的判断标准。一般建议保留默认值。 |
| enableThreadTimerCheck | bool | true | 使用后台线程计时器作为额外的基准时钟,有助于捕获只操控主线程的工具。 |
| useWebTimeValidation | bool | true | 使用 HTTPS 时间响应的 Date 头或 Pro 服务器时间,交叉检查设备时钟篡改。默认值为 true。 |
| webSyncInterval | float | 15 s | 与 Web/服务器时间重新比较的周期。设置过短会增加网络请求。 |
| webRatioTolerance | float | 0.15 | 设备时间与 Web/服务器时间之间允许的正常差异比例。请结合地区网络延迟在真机上验证。 |
| timeOffsetTolerance | float | 60 s | 设备时钟与 Web/服务器时间的绝对差值超过此秒数时,会被视为时间篡改。 |
| focusIgnoreTime | float | 4 s | 应用重新获得焦点后忽略检测的秒数。可防止操作系统暂停应用时产生的误报。 |
| loadingGraceTime | float | 6 s | 应用启动或场景加载后,在这段时间内忽略检测结果。用于减少重负载加载阶段的误报。 |
| lagSpikeIgnore | float | 0.5 s | 帧时间超过此值的样本会被视为延迟并丢弃,避免把真实卡顿误认为时间篡改。 |
| buildFailIfTimeScaleTampered | bool | true | 如果受保护代码疑似在已批准策略之外直接修改 Time.timeScale,则让构建/验证阶段失败。 |
| timeScaleTamperExemptions | List<string> | — | 允许直接修改 Time.timeScale 的脚本/方法名称模式。只登记已经确认过的暂停、子弹时间、过场等代码,并尽量保持列表最小。 |
| webTimeUrls | string[] | — | 用于 Web/服务器时间检查的 HTTPS 地址列表。建议加入两个以上由您运营或信任的地址,避免一个地址被阻断后检查就停止。 |
| minSuccessfulEndpoints | int | 2 | 一次检查中,至少有多少个地址返回有效 Date 标头后才信任该时间。不要设置得比已配置地址数量更大。 |
| maxConsecutiveFailures | int | 6 | Web/服务器时间检查连续失败达到此次数后,执行 onWebTimeUnavailable 策略。它用于给临时网络故障留出缓冲,不会马上当作安全违规。 |
| onWebTimeUnavailable | WebTimeUnavailablePolicy | WarnOnly | 多次无法确认 Web/服务器时间时的处理策略。WarnOnly 只记录警告并继续运行。Strict 会把反复失败视为可疑情况并触发 SpeedHack 回调。Silent 不记录日志,除特殊测试外不要使用。 |
| detectTimeHack | bool | true | 检查设备时钟变化以及与 Web/服务器时间的差异。它独立于速度比例检查,用于捕获系统时钟篡改。 |
| webSyncJitterPercent | float | 20% | 让 Web/服务器时间检查不要在所有设备上同时发生,而是在设定周期附近分散执行。例如 20% 表示 15 秒周期大约会在 12 到 18 秒之间执行。 |
| sustainedLagThreshold | float | 0.15 | 如果最近平均帧时间慢于此值,SDK 会先按真实卡顿处理,不会立刻把这段时间的时间检查结果确认为违规。默认 0.15 秒约等于平均 6-7 FPS。 |
| sustainedLagGraceDuration | float | 3 s | 判断为卡顿后,暂时推迟严格时间判定的时长。卡顿持续时这个推迟时间会继续延长,运行时最多限制在 10 秒内。 |
| overloadStrictMultiplier | int | 3 | onWebTimeUnavailable 为 Strict 时,如果失败看起来是普通 timeout,而不是明确的服务器拒绝,就用这个倍数决定多等几次。默认 3 表示最多等到 maxConsecutiveFailures 的 3 倍后再触发回调。 |
| enableRemoteSpeedHackConfig (Pro) | bool | false | 通过 Pro 服务器策略在无需更新应用的情况下调整部分 Speed & Time Hack 基准值。若服务器不可达,会继续使用本地 Inspector 值,因此保护不会关闭。 |
| remoteSpeedHackConfigInterval | float | 300 s | 从 Pro 服务器重新获取 Speed & Time Hack 远程设置的周期。0 表示只在应用启动时获取一次,正数表示定期检查新策略。 |
| remoteSpeedHackConfigJitterPercent | float | 20% | 让多个设备不要同时请求 Pro 远程设置,而是把设置刷新时间稍微分散开。 |
| enableSignedServerTime (Pro) | bool | false | Pro 激活可用时,优先使用签名的 /v1/time 响应作为可信时间源。请在服务器密钥和时间端点配置并测试完成后再开启。 |
使用 OZeroTime.timeScale,而不是 Time.timeScale
启用 Speed & Time Hack 保护模块时,项目代码不得直接修改 Unity 内置的 Time.timeScale。如果绕过 OZero 直接修改,OZero detector 可能会将其视为外部作弊工具造成的可疑运行时时间篡改,并执行已配置的响应策略。
OZeroSecurityConfig.TimeScaleTamperExemptions 是用于登记仍需直接访问 Time.timeScale 的脚本的例外列表。请尽量保持该列表最小化。
仅在现有结构无法避免直接访问 Time.timeScale 时,将例外限制分配给已验证的第三方插件或 legacy adapter script。
除此之外,项目中的所有游戏逻辑都应无例外地优先使用 OZeroTime.timeScale。暂停、慢动作、过场速度控制、倍速播放等游戏内有意变速场景,都必须使用 OZeroTime.timeScale。
using OZeroSDK.Security;
// Correct: the detector knows this is an intentional game-side change.
OZeroTime.timeScale = 0.5f;
// Avoid: this bypasses OZero's expected time-scale tracking.
Time.timeScale = 0.5f;
OZeroTime.timeScale。
防止长时间加载时 Watchdog 意外终止应用
OZero 的原生 Watchdog 通过周期性的 heartbeat 确认 Unity 主线程仍在运行。发布版中 heartbeat 期限在所有平台上约为 10 秒(启动宽限期之后)。超过期限时,Watchdog 先查看主线程在做什么:线程被阻塞且不消耗 CPU 的状态(长时间同步加载、日志刷写、磁盘 I/O、虚拟机暂停线程)视为停顿,仅记录;游戏仍在运行却不再发送 heartbeat 则视为篡改,按配置的响应策略终止。停顿超过五分钟的进程按无响应关闭。
大型场景加载、同步资源解压、shader warmup 或 Addressables 准备等正当工作,也可能因为主线程被有意阻塞数秒而触发这种情况。
请使用 OZeroWatchdog.BeginLoadingGrace(maxGraceMs) 包住这类可信加载边界。加载开始时 grace 开始,scope 被 dispose 或调用 End() 时会立即结束,因此不需要提前知道准确的加载时长。
using OZeroSDK.Security;
using UnityEngine.SceneManagement;
public void LoadLargeScene()
{
using (OZeroWatchdog.BeginLoadingGrace(60000))
{
SceneManager.LoadScene("Battle", LoadSceneMode.Single);
// Grace ends as soon as the using scope exits.
}
}
public void WarmUpLargeAssets()
{
OZeroWatchdog.RunWithLoadingGrace(() =>
{
BuildLargeRuntimeCache();
}, 60000);
}
这个功能只是一个临时宽限期,用于避免应用在正常的长时间加载过程中被系统强制结束。它不能用于绕过安全检查,也不能用于让应用无限期保持运行。
如果某个较重的加载任务可能超过允许时间,请把任务拆成更小的步骤处理,或使用 Unity Coroutine / async 加载,避免画面和主线程长时间卡住。
物理黑客检测器
OZeroSecurityConfig.PhysicsHack 只控制全局开关和 Pro telemetry。速度限制、距离容差、穿墙检查和加速度检查都在各自的 OZeroPhysicsHackDetector 组件上配置。请把组件添加到玩家预制体,根据角色或载具的真实移动规则调整数值,并在生成后调用 Initialize(playerId)。| 字段 | Type | 默认值 | 说明 |
|---|---|---|---|
| physicsHack.useGlobalPhysicsHack | bool | true | 所有 Physics Hack 组件的全局开关。只有在确实想停止全部物理移动检查时才关闭。 |
| physicsHack.enableServerTelemetry (Pro) | bool | false | 仅限 Pro,默认关闭。只有项目明确同意后,才会发送一般安全事件与详细 PhysicsHack telemetry。关闭后将停止发送新的 telemetry;服务器策略可以关闭传输,但不能在没有本地同意的情况下启用传输。 |
| physicsHack.telemetryThrottlePerMinute (Pro) | int | 30 | 限制此客户端每分钟可发送的 PhysicsHack telemetry 数量。避免阈值设置错误时向服务器发送过多事件。 |
| 字段 | Type | 默认值 | 说明 |
|---|---|---|---|
| maxAllowedSpeed | float | 15 u/s | 此对象正常情况下可达到的最大移动速度(Unity units/second)。请设置为该角色或载具在游戏中合法可达到的最快速度。 |
| distanceTolerance | float | 2.0 u | 在根据 maxAllowedSpeed 计算出的可移动距离之外,额外允许的距离。用于吸收网络位置校正、物理计算误差和控制器的轻微抖动。 |
| obstacleLayer | LayerMask | — | 作为墙体或实体障碍物处理的 Layer。OZero 会检查从上一个安全位置到当前位置的路径;如果路径穿过这些 Layer,就可以视为穿墙。 |
| checkInterval | float | 0.05 s | 此组件检查位置、穿墙和加速度异常的周期。数值越小反应越快,但 CPU 使用量可能增加。 |
| maxDeltaTimeCap | float | 0.1 s | 距离计算中使用的最大帧时间。避免单个长帧让允许移动距离变得过大。 |
| violationThreshold | int | 2 | 触发回调前需要连续出现多少次异常检查。一次正常检查会降低计数,因此单次轻微物理抖动不会立刻变成违规。 |
| castRadius | float | 0 | 穿墙检查使用的半径。0 会尝试从 CharacterController 或 CapsuleCollider 自动推算;如果找不到,则退回为线段检查。 |
| enableAccelerationCheck | bool | true | 检查 Rigidbody 速度是否突然大幅变化。对于 kinematic 对象、服务器权威移动,或通过自定义逻辑正常改变速度的对象,请考虑关闭此项。 |
| maxAllowedAcceleration | float | 60 | 两次检查之间允许的最大速度变化量。如果游戏中有冲刺、击退、跳板、载具等正常的瞬间加速,请按这些移动调整数值。 |
| enableLog | bool | true | 调试和调参时显示此组件的日志。不过 obstacle layer 为空等重要配置问题,即使关闭此项也会警告。 |
注入检测器设置
检测进入运行中游戏的程序:被注入的原生模块、可执行私有内存、内联挂钩、远程线程式注入、高风险进程句柄以及 WebGL 运行时篡改。正常的覆盖层和录制工具也以同样方式进入游戏,因此检测器通过文件哈希和签名者识别每个模块,并允许逐个模块做出判定。
只有检测到正常覆盖层、录制软件、合作方 SDK 或随游戏发布的模块时才允许。请先在 Failure Diagnostics 或 Pro 门户确认模块名、文件哈希和签名者。不要仅因常见就允许——作弊工具同样常见。
| 字段 | Type | 默认值 | 说明 |
|---|---|---|---|
| Activate Injection & Hooking | checkbox | On | Config Dashboard 中 Injection & Hooking 的开关项,内部对应 useInjection。在玩家构建中会执行原生 Injection 检查;开发构建会使用更偏诊断的宽松流程;编辑器中通常不检查。 |
| injectionWhitelistEntries | OZeroInjectionWhitelistEntry[] | empty | 所有 tier 都可以使用的本地可信模块列表。每个条目通过模块文件的 SHA-256 hash 和可选 signer fingerprint,允许一个特定文件。只添加随游戏一起分发,或通过 QA、诊断文件、Pro telemetry、OZero 支持团队确认过的模块。 |
| enableServerWhitelist (Pro) | bool | false | 仅 Pro。下载由门户判定与 OZero 全局判定生成的签名允许/阻止列表,并与 SDK 基线和本地列表合并。允许的模块静默,阻止的模块立即关闭游戏。验证过的列表会缓存用于离线启动;无法连接服务器时,用最后的列表继续保护。 |
| serverWhitelistRefreshInterval | float | 0 s | 运行中的游戏重新下载 Pro 判定列表的间隔。0 表示仅启动时获取一次。间隔越短门户判定越快生效,但流量略增;下次启动时也会刷新。 |
| requireSignerForNativeWhitelist | bool | false | 要求 Windows PE 和 Apple Mach-O whitelist 条目除了文件 hash 外,还必须包含 signer fingerprint。这样更强,但只有在能收集准确 signer 值时才建议开启。Android/Linux 共享库通常按 hash-only 处理。 |
| enableRemoteInjectionConfig (Pro) | bool | true | 仅限 Pro。允许通过门户策略在不发布新版本的情况下调整部分 Injection 设置,例如扫描周期、signer 要求和单个检查开关。 |
| remoteInjectionConfigInterval | float | 300 s | 重新检查 Pro 远程 Injection 设置的周期。0 表示只在应用启动时获取一次。 |
| enableWindowsModuleIdentityScan | bool | true | 在所有原生平台上,通过文件哈希识别启动后出现的每个模块,Windows/macOS 还使用签名者指纹(字段名沿用旧名 Windows)。iOS/macOS 将应用包内的映像视为游戏自身代码,Android/Linux 通过哈希识别共享库。参见检测器如何识别模块。 |
| scanIntervalSeconds | float | 1 s | 执行 Injection 检查的基础周期。数值越低检测越快,但运行时开销可能增加。实际应用值会被限制在安全范围内。 |
| windowsModuleIdentityScanIntervalSeconds | float | 5 s | 较重的原生模块身份检查的独立周期。如果模块枚举、hash 计算或 signer 检查在目标设备上成本较高,可以调大此值。 |
| scanJitterPercent | float | 20% | 让周期性 Injection 检查不要总是在完全相同的秒数执行,而是在配置周期附近稍微分散执行时间。 |
| enableExecutablePrivateMemoryScan | bool | true | 检测未作为正常模块加载的可执行私有内存。可捕获 shellcode 类载荷,但仅有可执行区域时只累积证据,不会单独关闭游戏。在 Mono 脚本后端的构建中,所有平台都会自动关闭该扫描,因为 Mono JIT 正是会创建这类内存。 |
| enableInlineHookScan | bool | false | 检查重要原生 API 入口点是否出现被 patch 的痕迹。这是较强信号,但 overlay 或安全软件也可能 hook API,因此建议只在严格构建中经过兼容性测试后开启。 |
| enableThreadStartAddressScan | bool | true | 检查原生 thread 是否从可信模块之外的可疑内存开始执行。可帮助在支持的平台上发现 remote-thread 类型注入。 |
| enableExternalProcessHandleScan | bool | true | 检查调试器、内存编辑器等外部进程是否持有游戏进程的高风险 handle。主要用于 Windows。 |
| detectionConfidenceThreshold | int | 70 | 把强 Injection 信号立即视为违规的最低置信度。更低的信号仍由连续/滑动窗口判定处理。限制在 60~100:低于 60 时进程内的所有 JIT 引擎(WebView、Lua、Mono)都会像违规一样。 |
| enableWebRuntimeTamperScan (WebGL) | bool | true | WebGL 专用的浏览器运行时检查。WebGL 无法使用 Native C++ 模块扫描,因此这些检查应理解为浏览器侧篡改信号的辅助客户端证据。 |
| webRuntimeScanIntervalSeconds (WebGL) | float | 1.5 s | WebGL 运行时检查的基础周期。为了避免过于频繁地检查浏览器,实际应用值会限制在 0.5 到 30 秒。 |
| webRuntimeScanJitterPercent (WebGL) | float | 20% | 让 WebGL 运行时检查不要总是在同一时刻执行,而是在配置周期附近稍微分散执行时间。 |
| WebGL probes | bools | true | WebGL 构建中用于分别开启/关闭 DevTools、clock hook、network hook、WebAssembly hook、storage hook、crypto API hook 检查的开关。 |
检测器如何识别模块
检测器每隔几秒遍历游戏进程中加载的全部模块,并与启动时的集合比较。新模块通过文件哈希识别,在 Windows 和 macOS 上还通过签名证书指纹识别。规则只看模块本身而不看安装目录名称,因此正常程序不会因安装路径而被误判。
| 平台 | 视为游戏自身代码的标准 |
|---|---|
| Windows | 启动时已存在且通过与运行时相同规则的模块、操作系统自身的组件(System32、SysWOW64、WinSxS),以及游戏自身的原生模块。游戏模块通过构建时签名写入完整性清单的哈希列表识别,不看所在目录。证书链验证为已知厂商(Steam、Discord、NVIDIA、RivaTuner、Nahimic、OBS、Epic、Ubisoft、EA、Overwolf、Medal、NVDA)的覆盖层和辅助模块记录为静默观测;其他由操作系统信任的证书签名的模块不会单独导致终止,而是进入门户的待判定列表。游戏目录中出现签名列表之外的原生文件时,启动时即为 Build Integrity 违规。 |
| iOS / macOS | 属于应用自身 .app 包的映像以及 Apple 平台映像。构建时无需生成任何产物。需要 iOS 14.0 或更高,低于该版本时 Editor preflight 会失败。 |
| Android / Linux | 启动时通过与运行时相同规则的共享库、系统和引擎运行时库、Steam 覆盖层,以及应用自身的库。Linux 上通过构建签名的清单识别(不看目录位置),Android 上通过只有操作系统能写入的已安装包目录识别应用自身的库。Android 系统 WebView 提供方也视为平台组件。不属于任何文件的可执行内存也在同一次扫描中上报,但只做累积判定,不会单独终止。Linux 宿主层和 Proton 遵循上文 Build Integrity 策略(默认 Monitor)。 |
每次检查按检测族返回三种结果之一:已检测、扫描后正常或尚未到检查时间。只有真实样本才会改变计数;未执行的检查不会清除先前的检测,完全无法执行的检查会作为扫描器健康问题上报,而不是正常结果。
Pro:在门户中允许或阻止检测到的模块
开启 enableServerWhitelist 后,Pro 构建在启动时以及每个 serverWhitelistRefreshInterval 下载一份签名判定列表。该列表合并了团队在客户门户(注入 → 检测状态 → 检测到的模块)中做出的判定与 OZero 基于全部客户数据的判定。
| 判定 | 游戏的行为 |
|---|---|
| Allow | 该模块(哈希+签名者匹配)加入原生信任列表,在项目的所有设备上不再视为威胁。 |
| Block | 加载了该模块的游戏立即关闭,无需等待重复检测。仅用于已确认的作弊工具。 |
| Watch | 不下发到游戏。持续记录,便于团队日后依据更多证据决定。 |
- 列表绑定到您的许可证,并使用许可证服务器密钥(Ed25519)签名。SDK 用其在激活时已信任的同一公钥进行验证。
- 验证过的列表保存在设备上,离线启动时同样生效。验证失败的列表不会被接受,检测本身也绝不会关闭。
- 从未连接过的设备只使用构建中自带的基线信任列表。之后的刷新连上服务器时,会获取并缓存列表。
- 当 OZero 阻止了您项目允许的模块时,门户中的当 OZero 与本项目判定不一致时设置决定以谁为准(默认:OZero)。您项目阻止的模块始终被阻止。
- ABI 21 SDK 还会上报 Windows 和 macOS 模块的代码签名主体(例如
Discord Inc.),门户可在证书指纹旁显示公司名。iOS 和 Android 模块没有可读的签名者。 - 检测器已发现但尚未处理的模块会以来源 决定前已发现 进入门户的 已观测模块 列表,您可以在任何玩家受影响之前允许或阻止。设备上证书链验证通过的模块显示为 可信签名,此类模块不会被 identity 规则自动关闭,而是等待您的决定。
扫描器不可用(abort code 0x15)
若原生扫描器本身无法运行(配置应用失败、原生扫描反复出错,或 managed/native 契约不一致),SDK 会上报 ModulationType.InjectionScannerUnavailable(值 10,abort code 0x15,message key injection_scanner_unavailable),即使关闭了 ForceQuitOnDetection 也会关闭游戏。这不是攻击检测,而是防止悄然损坏的扫描器让游戏失去保护。请向玩家显示 OZ-SEC-15 等支持代码,并在日志中保留 abort code。
PlayerPrefs 加密
对于需要保护的新 key,请使用 OZeroSafePlayerPrefs,而不是 Unity 的 PlayerPrefs。常见的 Get/Set、HasKey、Delete、Save 方法名保持熟悉的形式,但加密数据与已有的普通 PlayerPrefs 数据不兼容。
using OZeroSDK.Security;
// Save a value
OZeroSafePlayerPrefs.SetInt("score", 4200);
OZeroSafePlayerPrefs.SetFloat("volume", 0.8f);
OZeroSafePlayerPrefs.SetString("username", "Hero");
// Read a value
int score = OZeroSafePlayerPrefs.GetInt("score", 0);
float volume = OZeroSafePlayerPrefs.GetFloat("volume", 1.0f);
string username = OZeroSafePlayerPrefs.GetString("username", "");
存档文件加密
要加密存档文件,请使用 OZeroSV_File 代替 File.ReadAllText / File.WriteAllText。文件在写入时自动加密,在读取时自动解密。加载时还会检查是否被篡改。
using OZeroSDK.Security;
string path = Application.persistentDataPath + "/save.json";
// Write encrypted file
OZeroSV_File.WriteAllText(path, jsonString);
// Read and decrypt file
string json = OZeroSV_File.ReadAllText(path);
developerSecret。developerSecret 是生成 OZeroSafePlayerPrefs 和 OZeroSV_File 数据加密密钥的基准值。若发布后更改此值,新版本将无法解密旧版本保存的受保护数据。
4
游戏内变量保护(Secure Types)
Secure Types 会将常规 C# 变量类型替换为加密类型。因为值存储在 Native C++ 堆中,诸如 Cheat Engine 的工具即使扫描内存也无法找到。只需更改类型名称,其余代码即可照常运行。
示例代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using OZeroSDK.Security;
public class PlayerStats : MonoBehaviour
{
[SerializeField] OZeroSV_Int Gold = 5000;
[SerializeField] OZeroSV_Float Speed = 3.5f;
[SerializeField] OZeroSV_Int HP = 1000;
}
支持类型
| 现有 | 应用 OZero 后 |
|---|---|
| int | OZeroSV_Int |
| long | OZeroSV_Int64 |
| uint | OZeroSV_UInt |
| ulong | OZeroSV_UInt64 |
| short | OZeroSV_Short |
| ushort | OZeroSV_UShort |
| byte | OZeroSV_Byte |
| float | OZeroSV_Float |
| double | OZeroSV_Double |
| decimal | OZeroSV_Decimal |
| bool | OZeroSV_Bool |
| string | OZeroSV_String |
| Vector2 | OZeroSV_Vector2 |
| Vector3 | OZeroSV_Vector3 |
| byte[] | OZeroSV_Buffer |
7
安全事件处理(可选)
默认情况下,当 OZero 检测到威胁时,它会按照配置的响应策略来终止应用或只留存日志。如果您需要直接显示警告屏幕、发送您自己的服务器日志或在退出之前执行保存操作,则可以注册回调。该回调将通过 OZeroSecurityEvent 提供修改类型、中止代码、消息键、详细消息以及是否即将退出等信息。
using UnityEngine;
using OZeroSDK.Security;
public class SecurityHandler : MonoBehaviour
{
void OnEnable()
{
// RegisterUserCallback runs on the user chain only.
// The built-in default handler runs independently and cannot be silenced.
OZeroSecurityManager.Instance.RegisterUserCallback(OnThreatDetected);
}
void OnDisable()
{
OZeroSecurityManager.Instance.UnregisterUserCallback(OnThreatDetected);
}
void OnThreatDetected(OZeroSecurityEvent evt)
{
// evt contains Type, AbortCode, AbortCodeHex, MessageKey, Message, and WillAbort.
Debug.LogWarning(
$"OZero threat: {evt.Type} {evt.AbortCodeHex} {evt.Message}");
switch (evt.Type)
{
case ModulationType.SpeedHack:
// e.g. kick the player, show warning, report to server
break;
case ModulationType.BuildIntegrity:
// evt.WillAbort is usually true for fatal build integrity violations.
break;
case ModulationType.Injection:
break;
}
if (evt.WillAbort)
{
// Last chance to flush your own analytics or save state.
}
}
}
OZeroSecurityEvent、ModulationType 以及 OZeroAbortCode 的完整列表已记录在 API 参考中。
evt.WillAbort 为 true,按照当前响应策略,应用将在回调之后退出。在这种情况下,请只执行简短的自定义 analytics flush 或是保存操作。
当检测到威胁时,OZero 会首先应用 SDK 基础的响应策略,并将同样的安全事件派发给项目中注册的回调和 Inspector 事件。如果希望直接显示警告画面或留下服务器日志,请注册回调。关于应用是否退出,可以通过 Config Dashboard 的 Response 设置以及 evt.WillAbort 的值来确认。
门户策略回调
Customer Portal 策略可以分阶段运营。Observe 只记录证据,Callback 将服务器下发的 policy action 传递给游戏,Device Block 会在后续服务器验证中阻止选中的设备。在使用更强的设备封禁之前,如需由游戏显示警告、限制匹配、要求重新登录或引导玩家联系支持,请使用 Callback。
using UnityEngine;
using OZeroSDK.Security;
public class PolicyActionHandler : MonoBehaviour
{
void OnEnable()
{
OZeroSecurityManager.Instance.RegisterUserPolicyActionCallback(OnPolicyAction);
}
void OnDisable()
{
OZeroSecurityManager.Instance.UnregisterUserPolicyActionCallback(OnPolicyAction);
}
void OnPolicyAction(OZeroPolicyActionEvent evt)
{
Debug.LogWarning(
$"Policy callback: {evt.Module}, score={evt.Score}, reason={evt.Reason}");
if (evt.Score >= 80)
{
// Show your own UI, limit sensitive actions, or ask the player to reconnect.
}
}
}
下一步
现在 OZero 的核心防线已经在为您保驾护航了。翻查文档,全面探究每个接口,类、或控制选项的真谛。
故障排除参考
Use this section when an OZero dialog appears, a security log is reported, or the app closes for a security reason. Start with the code shown on screen or in the log, then follow the first checks below.
Support Codes
Support codes are short labels shown in OZero dialogs and logs. When a player sends this code to support, the development or QA team can quickly identify which broad area to check. OZ-SEC-* means a security event was reported, and OZ-MV-* means an online or managed verification state needs attention.
| Code | Category | What to check |
|---|---|---|
| OZ-SEC-01 | Memory protection | Check whether protected values were changed by an unsupported path, a test tool, or an incorrect Secure Types integration. |
| OZ-SEC-02 | Injection or hook | Check whether an overlay, debugger, modding tool, or other external program was attached. Add exceptions only after QA confirms the program is trusted. |
| OZ-SEC-0A | Build integrity | Check whether the build, manifest, signing information, native variant, or packaged files changed after the release build was prepared. |
| OZ-SEC-0C | Speed or time change | Check speedhack tools, device clock changes, direct Time.timeScale writes, and legitimate slow-motion or pause flows. |
| OZ-SEC-0E | Device or install policy | Check the install source, store package identity, emulator/root/jailbreak policy, and device-binding support process. |
| OZ-SEC-0F | Physics check | Check whether movement limits, collision layers, dash, knockback, or vehicle movement are tuned for the actual game rules. |
| OZ-SEC-10 | Runtime environment | Check whether the app is running in a supported device, browser, emulator, debugger, or platform test environment. |
| OZ-SEC-13 | Steam verification | Check Steam launch state, AppID, ownership verification, required Steamworks files, and network connection. |
| OZ-SEC-15 | 注入扫描器不可用 | 不是攻击。原生注入扫描器无法运行(配置未应用、扫描反复失败或 managed/native 契约不一致)。请确认 SDK 与原生二进制来自同一软件包,重新安装游戏后仍持续再联系支持。 |
| OZ-SEC-1F | Unknown category | Collect the platform, SDK package version, native ABI, Player log, ozero_abort.txt, and exact reproduction steps. |
| OZ-MV-LOCAL | Managed verification | Check license activation, server/network access, registered app identity, platform signing information, and the currently applied variant manifest. |
Abort Codes
Abort codes are stable categories used in logs, callbacks, and OZ-SEC-* support codes. They tell the development team which protection area responded without exposing detailed evidence to the player.
| Code | Category | Meaning |
|---|---|---|
| 0x01 | Memory protection | Protected memory value changed unexpectedly. |
| 0x02 | Injection or hook | Unexpected module, hook, or runtime injection signal detected. |
| 0x0A | Build integrity | Build integrity validation failed. |
| 0x0C | Speed or time change | Suspicious execution speed, time scale, system clock, or trusted-time anomaly detected. |
| 0x0E | Device or install policy | Device binding or install-source policy rejected the current environment. |
| 0x0F | Physics check | Abnormal physics behavior exceeded the configured policy. |
| 0x10 | Runtime environment | Unsupported or unsafe runtime environment detected. |
| 0x13 | Steam verification | Steam ownership or ticket validation failed. |
| 0x15 | 注入扫描器不可用 | 注入扫描器本身无法运行;SDK 以 fail-close 方式关闭,而不是在无保护状态下运行。 |
| 0x1F | Unknown category | A security event did not match a more specific public category. |
For API fields and callback examples, see OZeroAbortCode & Event Messages.
解决流程
| Step | Action | Expected evidence |
|---|---|---|
| 1 | Record the support code shown on screen or in the log, plus the platform. | The screenshot or ticket should include the code, OS, Unity version, SDK package version, and whether the build is Development or Release. |
| 2 | Check ozero_abort.txt and the Player log first. | These files can still help when the app closes before a popup appears. |
| 3 | If the issue is reproducible in QA, enable Enable Failure Diagnostics, reproduce once, then disable it again. | The latest ozero_*_failure.log helps identify which protection area responded. Use this file only for QA or support investigation. |
| 4 | Run Window > OZero Security > Check Setup before changing policies. | Preflight can catch missing required packages, manifest/signing mismatch, platform settings, ProGuard/R8 settings, native plugins, and obvious license setup issues. |
| 5 | For Pro or managed verification cases, compare portal registration with the actual build information. | Confirm package/app ID, platform signing information, SDK package, native ABI, active variant manifest, license tier, server connection, and any downgraded or limited state. |
| 6 | After the fix, rerun the matching platform QA scenario. | Capture survival timing, Managed UI visibility, simulated or real attack result, popup countdown, and final logs before closing the ticket. |