入门
本指南将逐步引导您完成从安装 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。所需的脚本、原生插件和编辑器工具将自动添加。
Windows:从 1.2.0 更新到 1.2.1 时出现 "Copying file failed / Access is denied"
将 1.2.1 导入 1.2.0 项目时,可能会针对 Plugins/x86_64/OZeroSecurity64.dll 弹出 Copying file failed … Access is denied,并且重启 Unity 后仍会重复出现。这是因为编辑器启动时就会加载 SDK 的原生库,导致 Windows 锁定该文件。macOS 和 Linux 不受影响,我们计划在下一次更新中修复,使这些步骤不再需要。
- 在弹窗中点击 Cancel(不是 Force Quit)。
- 完全退出 Unity。在任务管理器中确认没有残留的
Unity.exe。 - 在文件资源管理器中,只删除
Assets/OZeroSecurity/Plugins/x86_64/中的OZeroSecurity64.dll和OZeroPCStoreBridge64.dll。保留.meta文件。 - 在 Unity 中打开项目。可以忽略与 DLL 相关的控制台错误。
- 在 Package Manager 中重新 Import 完整的 1.2.1 包。
- 重启 Unity,然后运行 Check Setup。
Welcome 窗口
导入 OZero SDK 包后,Welcome 窗口(Window > OZero Security > Welcome)会自动打开。每次导入 OZero SDK 包(即使再次导入相同版本)都会打开,新 SDK 版本首次加载时也会打开。导入 Built-in TMP 对话框包或 TMP 资源时不会打开。
窗口顶部有一张大卡片 STEP 1 · Run Setup。点击 Start setup 会打开 Check Setup,列出项目仍需的设置,Auto Setup 会在每次修改前确认后再应用。下方是两张在设置完成后使用的卡片。Read the Manual 中有 Online(OZero 网站手册)和 Offline(包内附带的 HTML 手册,Assets/OZeroSecurity/Documentation/HTML/index.html)按钮,若没有附带手册,Offline 按钮将不可用。Security Config(Open config)会打开 Edit > Project Settings > OZero Security,以便在设置完成后更改预设或单项保护功能。
安装了附加组件的项目中还会显示 Online Services 行。点击 Open 会打开 Window > OZero Security > Online Services 窗口(如设置不存在则创建)。
安装了附加组件时,Offline 按钮会打开附加组件附带的手册(Assets/OZeroSecurity/Online/Documentation/HTML/index.html)。
勾选 Don't show this window automatically again 后,此项目中将不再自动打开,但仍可随时通过菜单打开。在 batch 模式下或设置了 OZERO_SUPPRESS_WELCOME 环境变量时不会打开,因此不会中断 CI 和自动构建。
ProjectSettings/OZeroSecuritySettings.asset,Standard 预设)以及使用在线附加组件时的许可证设置(ProjectSettings/OZeroLicenseSettings.asset),会在导入后 Unity 编辑器空闲时自动创建。只用 -batchmode -quit 导入包的 CI 不会创建它们:请先用编辑器打开一次项目,或在构建脚本中调用 OZeroSecurityProjectSettings.LoadOrCreate()(附加组件还需调用 OZeroLicenseProjectSettings.LoadOrCreate())。请将 ProjectSettings/OZeroSecuritySettings.asset 和 ProjectSettings/OZeroLicenseSettings.asset 提交到版本控制;不要提交含有按用户保存机密的 UserSettings/OZeroSecuritySecrets.json。
2
打开仪表板
导入完成后,从 Unity 菜单栏打开 Config Dashboard:
打开 Config Dashboard 后,可以在此屏幕上一次性管理预设、安全模块、许可证和构建完整性设置。无需在 Project 窗口中直接查找并修改文件。仪表板就是 Edit > Project Settings 中的 OZero Security 页面,也可以直接在那里打开。
Unity 编辑器菜单说明
OZeroSecurity 将设置窗口放在 Window > OZero Security,将诊断和 bake 命令放在 Tools > OZero Security。安全预设会在 Config & Dashboard 中应用,方便您在保存前确认实际变更的设置。
- Window > OZero Security > Welcome:开始界面
- Window > OZero Security > Config & Dashboard:安全设置(打开 Edit > Project Settings > OZero Security)
- Window > OZero Security > Check Setup:设置检查与自动设置(Auto Setup)
详细:完整菜单列表
| 菜单 | 作用 | 使用场景 |
|---|---|---|
| Window > OZero Security > Welcome | 打开 SDK 开始画面,可直接进入 Check Setup、手册(在线/离线)和 Security Config。 | 导入 OZero SDK 包后会自动打开。关闭后可通过此菜单再次打开。 |
| Window > OZero Security > Config & Dashboard | 打开用于编辑安全设置的 Edit > Project Settings > OZero Security。如果项目还没有设置,会以 Standard 预设创建。 | 首先在这里应用预设、启用模块、配置响应策略并检查构建完整性设置。 |
| Window > OZero Security > Online Services | 在单独窗口中打开 Online Services 设置(ProjectSettings/OZeroLicenseSettings.asset),用于配置许可证、服务器连接和在线服务。相同的设置也在 Security Config 中(各模块的 Pro - Online 区块和 Pro - License & Online Services 卡片)。 | 用于输入 Standard、Pro 许可证设置、服务器端点和遥测选项。 |
| Window > OZero Security > Check Setup | 在编辑器中诊断常见的项目和发布配置问题。 | 在导入包后、发布构建前,或模块行为不符合预期时运行。 |
| Window > OZero Security > macOS Distribution Signing | 为每个 macOS 玩家构建自动执行 Developer ID 签名、可选公证和 OZero 完整性文件重新生成。 | 在发布于 Mac 上生成的 macOS 构建前开启。在 Windows 上生成的构建请使用签名脚本(参见 macOS 签名)。 |
| Tools > OZero Security > Internal > Check Integrity Manifest | 打开 OZero 完整性清单并显示解码后的内容。 | 用于构建完整性故障排查或支持调查。 |
| Tools > OZero Security > Internal > Bake Security Config Blob | 在 StreamingAssets 中重新生成受保护的安全配置 blob。 | 用于高级调试或 CI 工作流。普通播放器构建会自动 bake。 |
| Tools > OZero Security > Internal > Bake Assembly Hash | 为最近一次构建输出重新生成 oz_ahash.bin。 | 仅在未重新运行完整构建流水线、只进行了代码重建之后使用。 |
| Tools > OZero Security > Re-bake Integrity After Code Signing (Windows or Linux player)... | 为已代码签名的 Windows / Linux 玩家重新生成 OZero 完整性文件。 | 在为可执行文件或原生模块签名后使用(参见 Windows / Linux 签名)。 |
| Tools > OZero Security > Setup > Android > Keystore SHA Extractor | 从 Android keystore 中提取 SHA-1 和 SHA-256 指纹。 | 在填写 Build Integrity 的 Android 签名指纹时使用。 |
| Tools > OZero Security > Internal > Add Debug SHA Key to Config | 查找 Android debug keystore,并将其 SHA-256 指纹加入 config。 | 仅用于本地 Android 调试构建。发布构建应使用发布签名密钥指纹。 |
| Tools > OZero Security > Setup > PC Store > Scan Redistributable | 扫描 PC 构建或插件文件夹中的 Steam redistributable 文件和已知 emulator artifact。 | 在 Steam 发布打包前,或检查是否包含可疑 Steam 文件时使用。 |
| Tools > OZero Security > Diagnostics > Check Time.timeScale Usage | 扫描可能与 OZero 时间保护冲突的直接 Time.timeScale 写入代码。 | 在发布前,或 Speed & Time Hack 模块报告项目侧 time-scale 策略问题时运行。 |
Check Setup 与 Auto Setup
打开 Window > OZero Security > Check Setup,或在 Welcome 窗口中点击 Start setup。建议在导入软件包后和发布构建前各运行一次。
Check Setup 会检查项目、当前构建目标和安全设置,列出阻止构建的项目和建议修改的项目。每个项目都有 Docs(说明)和修复按钮,请先解决阻止构建的项目。
点击 Auto Setup,会逐步显示建议的修改。每一步都会显示当前值和修改后的值,请选择 Apply 或 Skip。完成后会显示已应用、已跳过和仍待处理项目的摘要。当不再有阻断项目和 Auto Setup 可应用的变更时,会显示 Setup complete 框和 Open Build Settings 按钮,之后照常构建即可。
Auto Setup 会修改的项目:关闭 Development Build、切换到 IL2CPP、将 Managed Stripping 设为 Low、Android API 24 与 ARM64、生成 DeveloperSecret、导入 TMP 对话框软件包、生成 manifest 签名密钥(并开启 Require Manifest Signature)、添加 Android 调试 SHA。
详细:Auto Setup 的步骤与行为
Check Setup(Window > OZero Security > Check Setup,或 Welcome 窗口中的 Start setup)会检查项目、当前构建目标、安全设置以及各平台的发布设置,列出阻断问题、建议事项和信息。每个项目都有 Docs 和修复按钮,窗口顶部会显示 Auto Setup 可以应用的建议变更数量。窗口顶部的按钮从左到右依次为 Auto Setup(显示待应用的变更数)、Re-check(重新运行检查)、Manual 和 Security Config。Security Config会打开 Edit > Project Settings > OZero Security。实际构建玩家时,构建时验证仍会照常运行。
导入 OZero SDK 包后首次打开 Check Setup 时,如果有 Auto Setup 可以应用的变更,会询问是否开始 Auto Setup(Start Auto Setup / Not now)。即使再次导入相同版本,每次导入后都会再次询问;在 batch 模式下不会询问。也可以稍后通过 Auto Setup 按钮开始。
Auto Setup 会先列出计划执行的步骤,只列出项目和当前构建目标需要的步骤,然后选择 Start(或 Cancel)。每一步都会显示当前值和变更后的值,让你选择 Apply、Skip 或 Stop。最后的摘要画面会列出已应用、已跳过和仍未解决的项目,并可通过 Run Auto Setup Again 从头再次运行。可能出现的步骤如下:
- 关闭 Development Build
- 将 Scripting Backend 改为 IL2CPP
- 仅限 IL2CPP:将 Managed Stripping Level 设为 Low
- Android:最低 API 级别 24,目标架构 ARM64
- 生成
DeveloperSecret - 导入 Built-in TMP 对话框包。如有需要会安装 TextMeshPro/Unity UI,并依次导入 TMP Essential Resources 和对话框包。全部完成之前,Processing 画面会显示当前阶段和已用秒数,完成后进入下一步。即使经过脚本重新加载也会继续。
- 生成清单签名密钥对(信任锚点会自动更新)。私钥文件的创建位置会显示在步骤画面中。不会弹出结果对话框,私钥位置和指纹会记录在控制台日志中。如果已存在密钥,会弹出对话框询问是否覆盖。下一步会开启 Require Manifest Signature。
- 添加 Android 调试 SHA。结果会记录在控制台日志中。如果没有调试 keystore,会弹出对话框询问是否创建。 通过 Security Config 或 Keystore SHA Extractor 的按钮直接运行时,仍会像以前一样弹出结果对话框。
安全设置中的 Fail Build on Setup Warnings 开启时(默认),密钥生成和 Require Manifest Signature 也是普通步骤:跳过后警告会保留,玩家构建会在 preflight 阶段失败,步骤画面和摘要画面也会提示这一点。只有在该设置关闭时,它们才会成为标有“(optional, review first)”的可选步骤。密钥生成步骤会显示私钥文件的创建位置。请将私钥放在项目之外并排除在版本控制之外,同时确保包括 CI 在内的所有构建机器都能使用。
此编辑器无法应用的步骤(例如 Unity 编辑器未安装 IL2CPP 模块时的 IL2CPP 步骤)不会逐步执行,而是在概览和摘要画面的 Needs your action 中单独列出。缺少 IL2CPP 模块时,请在 Unity Hub > Installs > Add modules 中添加目标平台的 IL2CPP 模块,然后重新运行 Auto Setup。Player Settings 的变更无法通过 Undo 撤销,所有已应用的变更都会显示在摘要画面中。
以下两项 Check Setup 错误用于保护 OZero 的原生加载锚点(客户端保护的局限)。OZero native plugins are not set to Load on startup:Auto Setup 会重新开启 OZero 原生插件的 Load on startup,请勿关闭该设置。Android boot provider library is missing:Mono 后端的 Android 构建需要该库,请重新导入 OZero Security 软件包以恢复 Assets/OZeroSecurity/Plugins/Android/OZeroSecurityBoot.androidlib。
安装在线附加组件后,Auto Setup 还可以让你输入只有你知道的值并完成设置。这类步骤会显示输入框(标签位于输入框上方),输入值后按 Apply 即可。值在保存前会先经过检查,不会写入 Console 或日志。检查失败时,该步骤会显示原因且不保存任何内容;请修改后重新输入,或按 Skip。
- License Key:用于 Pro license key is empty、Native Variant <NNNN> needs your license key 和 … is bound to a different license key。如果项目中有绑定许可证的 Native Variant,保存前会与该 Variant 的许可证核对,其他许可证的密钥不会被保存。
- Server Public Key:用于 Pro server signing public key is not configured。请从 Customer Portal > Server Key 复制签名公钥(64 个十六进制字符)。其他格式会被拒绝,并以小写形式保存。
- Application Identifier:用于 Application identifier is not registered for Native Variant <NNNN>。需要输入的值取决于当前构建目标,与 Customer Portal > Platform settings 一致。Android 输入 Package Name,iOS 输入 Bundle Identifier,输入框会预先填入当前标识符;由于该选项关闭时 Unity 会忽略输入的标识符,Apply 还会开启 Player Settings 中的 Override Default Package Name / Bundle Identifier。Windows、macOS、Linux 请在各自的输入框中分别输入在 Customer Portal > Platform settings 中为该平台登记的 Company Name 和 Product Name(门户中显示为 Company / Product);两个输入框会预先填入当前值,并按 company.product 或 com.company.product 核对。Apply 会设置 Player Settings 的 Company Name 和 Product Name;Product Name 也是构建出的应用名称。WebGL 通过门户中登记的页面 origin(域名)识别,项目中无需修改,该步骤会显示为不可用。输入的值必须已为你的许可证登记;如果未登记,会提示你先在 Customer Portal 中登记,再重新下载包。
编辑器 Play Mode(开发阶段)
编辑器 Play Mode 有单独的保护设置,不会妨碍开发。请在 Window > OZero Security > Config & Dashboard > Editor Play Mode > Protection 中选择 Off 或 Test。
| Protection | 编辑器 Play Mode 中的行为 | 适用场景 |
|---|---|---|
| Off | 不中断任何操作,检测只记录到日志。 | 默认值。日常开发时使用。 |
| Test | 在编辑器中执行响应、提示 UI 和回调,并以停止 Play 代替退出。 | 测试 Receiver 和提示 UI 时。 |
编辑器不是发布环境,最终行为请务必在播放器构建中确认。
详细:各模式的行为与日志
编辑器 Play Mode 有单独的保护设置,因此 OZero 不会干扰开发。在 Window > OZero Security > Config & Dashboard > Editor Play Mode > Protection 中选择 Off 或 Test。该设置保存在 ProjectSettings/OZeroEditorPlayMode.json 中,可通过版本控制与团队共享。它与安全设置分开,因此不会影响玩家构建或安全配置哈希。更改从下一次 Play 起生效,Play 开始时 Console 会用一行记录当前模式([OZero] Editor Play Mode Protection: Off - ...)。
Off: 不启用检测器(速度、注入、完整性等)。检测到你自己添加的 PhysicsHack 组件或 SecureVariable 篡改时,每种类型只输出一次警告日志([OZero] <type> detected in Editor Play Mode - no response because Editor Play Mode Protection is Off),不做其他处理:没有回调、提示 UI 和遥测,Play 也会继续。SecureVariable 加密本身照常工作。安全级别拒绝(例如来自安全配置哈希允许列表)在编辑器 Play Mode 中也只记录日志。
Test: 无需构建,直接按当前设置启用检测器;之前构建遗留的 StreamingAssets/oz_cfg.bin 会被忽略。检测到时执行已配置的响应(回调、提示 UI、遥测)。在玩家中会退出的情况下,经过相同的宽限时间后只停止 Play Mode,并输出日志 Editor Play Mode will stop in N ms。安全级别拒绝也会执行已配置的响应(Fail On Security Level Reject)。
两种模式下,SDK 都不会结束 Unity 编辑器进程,原生启动检查(调试器、模拟器、Linux 主机层)在编辑器中始终关闭。编辑器不是发布环境,最终行为请务必在玩家构建中确认。
使用在线附加组件时,Pro 激活和在线功能在编辑器中照常工作。附加组件的服务器循环会在退出 Play Mode 或重新加载脚本时停止。
3
激活模块
在仪表板内可以查看安全模块列表及切换开关。请激活您要使用的模块。以下是推荐的初始配置。
选择安全预设
首先选择一个预设,然后根据项目进行个别模块调整。对于一般的实时在线游戏,建议从 Standard 开始。这能在保护级别、性能和误报率之间取得最佳平衡。
| 预设 | 推荐用途 | 适用策略摘要 | Pro 服务器验证 (Check Integrity With Server) |
|---|---|---|---|
| Low | 原型、开发版本、初期 QA | 仅核心检查(用于测试) | 关闭。适用于需要离线运行的游戏(无服务器构建判定和运行期间再次确认) |
| Standard | 大多数已发布的游戏 | 核心保护 + 启动与运行时验证(推荐) | 开启。OZeroManaged 判定,启动时需要联网 |
| Strict | 高风险实时服务、PvP、竞技类构建 | 覆盖范围最大,充分测试后再使用 | 开启。CustomerGameServer 判定:游戏服务器下发 challenge,客户端调用 OZeroOnlineAttestation.RequestGameServerAttestation,再由游戏服务器通过 /v1/validate 验证 |
| 模块 | 功能说明 | 是否推荐 |
|---|---|---|
| Build Integrity Validator | 检测应用二进制是否被篡改 | 推荐 |
| Speed Hack Detector | 检测时间操控作弊 | 推荐 |
| Injection Detector | 监控内存 Hook 工具 | 推荐 |
| Install Source Validator | 拦截非法 APK(仅限 Android) | 选择 |
详细:预设会修改的值
Quick Preset 面板会显示与当前设置一致的预设,例如 Current: Standard,对应按钮以粗体和 * 标出。应用后手动修改数值时会显示 Current: Custom,此时任何按钮都不带标记。推荐值为 Standard,按钮上显示为 Standard (Recommended);预设名称仍为 Low / Standard / Strict。每个按钮下方会显示预设说明,按钮的工具提示中也有相同说明。
Windows 系统模块扫描(enableWindowsSystemModuleScan):Low OFF / Standard、Strict ON。这是安全预设,而非许可证等级。兼容性和观测规则请参阅 Injection 设置。
设置下载由 enableRemoteSpeedHackConfig、enableRemoteInjectionConfig 和 enableRemotePhysicsConfig 控制。安全预设只有在项目的许可证等级(Online Services 设置)为 Pro 时才会开启这些选项;若为 Standard 或没有许可证设置,预设会让所有 Pro 专用服务器选项保持关闭(将等级改为 Pro 后请重新应用预设)。这些选项仅在 Pro 中生效。远程配置选项本身不会开启 BIV 服务器验证、安装来源同步、服务器白名单或遥测;预设会另外更改这些值(参见上方的预设表)。
- Low: 仅保留轻量的核心检查。放宽平台原生检查及强制关闭策略,以免测试环境过早被封锁。
- Standard: 启用核心保护套件、启动验证、运行时再验证、模拟器检查以及推荐的 IL2CPP 文件覆盖。是一个平衡了兼容性与保护级别的配置。
- Strict: 采用最广泛的覆盖范围,将更多的失败视为致命违规。请在充分测试平台、签名及商店发布流程后再采用。
许可证模型 — Standard / Pro
OZero Security 提供 Standard 和 Pro 两种等级。Standard 模式无需服务器联动,仅使用本地保护功能。Pro 模式提供项目专属 Native Variant 包、清单 / Bundle ID 绑定、遥测、签名服务器时间、远程策略、服务器校验等运营功能。
| 项目 | Standard | Pro |
|---|---|---|
| 许可证密钥 | — (无) | OZ-PRO-XXXX ×6 |
| 10 个保护模块 | 全部 10 个保护模块激活 | 全部 10 个(与 Standard 相同) |
| 每位客户独立的原生安全模块 | 不提供 | 提供 |
| 云端遥测 | 无 | 已启用 |
| 远程设置 | 无 | 有 |
| 构建完整性服务器验证 | 无 | 有 |
| 签名时间 (防时钟篡改) | 关闭 — WebTime 仅使用 HTTPS HEAD |
开启 — 使用签名的 /v1/time 响应 |
可放入应用隐私政策的文本
开启 Pro 服务器功能的应用必须告知最终用户 OZero SDK 处理哪些信息。请将以下文本粘贴到应用的隐私政策中,并根据所开启的功能保留或删除方括号中的内容。完整项目请参阅OZeroSecurity 隐私政策。
한국어
이 앱은 부정 행위와 변조를 방지하기 위해 OZero Security SDK(오제로시큐리티 주식회사)를 사용합니다. 보안 검사는 기기 안에서 이루어지며, [Pro 서버 기능이 켜진 경우] 보안 이벤트가 발생하면 다음 정보가 OZero 서버로 전송됩니다: 앱별로 해시된 기기 식별자, 플랫폼·운영체제, 앱 버전, 탐지된 보안 이벤트의 종류와 결과, 탐지에 관련된 외부 프로그램 모듈의 파일명·서명자·해시, 접속 국가. [물리 검증을 켠 경우: 캐릭터 위치·속도·씬 이름.] [Steam 확인을 켠 경우: Steam 인증 티켓과 SteamID(해시로 저장).] 기기의 원문 식별자, IP 주소, 채팅·닉네임·결제 정보는 전송되지 않습니다. 전송된 기록은 90일 후 삭제되며, 외부 프로그램 모듈 정보는 개인·기기와 연결되지 않는 형태로 OZero의 부정 프로그램 판별 기준을 개선하는 데 사용됩니다. 자세한 내용: https://www.ozerosecurity.com/privacy.html
English
This app uses the OZero Security SDK (OZero Security Inc.) to prevent cheating and tampering. Security checks run on the device. [When Pro server features are enabled] the following is sent to OZero servers when a security event occurs: an app-specific hashed device identifier, platform and OS, app version, the type and result of the security event, the file name, signer and hash of third-party modules involved in the detection, and the country of connection. [Physics validation: character position, velocity and scene name.] [Steam verification: the Steam auth ticket and SteamID, stored hashed.] The raw device identifier, IP address, chat, nicknames and payment data are not sent. Records are deleted after 90 days. Module information is used, in a form not linked to any person or device, to improve OZero's cheat-program classification. Details: https://www.ozerosecurity.com/privacy.html
日本語
本アプリは不正行為と改ざんを防ぐために OZero Security SDK(OZero Security Inc.)を使用しています。セキュリティ検査は端末内で行われ、[Pro サーバー機能が有効な場合] セキュリティイベント発生時に次の情報が OZero サーバーへ送信されます:アプリごとにハッシュ化した端末識別子、プラットフォーム・OS、アプリのバージョン、検知したイベントの種類と結果、検知に関係した外部モジュールのファイル名・署名者・ハッシュ、接続国。[物理検証:キャラクターの位置・速度・シーン名。][Steam 確認:Steam 認証チケットと SteamID(ハッシュで保存)。] 端末の生の識別子、IP アドレス、チャット・ニックネーム・決済情報は送信されません。記録は 90 日後に削除され、モジュール情報は個人や端末と結び付かない形で OZero の不正プログラム判定基準の改善に使われます。詳細:https://www.ozerosecurity.com/privacy.html
中文(简体)
本应用使用 OZero Security SDK(OZero Security Inc.)防止作弊与篡改。安全检查在设备内进行,[启用 Pro 服务器功能时] 发生安全事件时会向 OZero 服务器发送以下信息:按应用哈希处理的设备标识符、平台与操作系统、应用版本、安全事件的类型与结果、与检测相关的第三方模块的文件名·签名者·哈希、连接国家。[物理验证:角色位置、速度与场景名。][Steam 验证:Steam 认证票据与 SteamID(以哈希保存)。] 不会发送设备原始标识符、IP 地址、聊天、昵称与支付信息。记录在 90 天后删除;模块信息以不关联个人或设备的形式用于改进 OZero 的作弊程序判定标准。详情:https://www.ozerosecurity.com/privacy.html
Online Services 设置
Security Config 会在两个位置显示附加组件设置。Protection Modules 组末尾,即页面最底部(Steam Anti-Piracy (PC Only) 之后)的 Pro - License & Online Services 卡片以 License(层级和许可证密钥)开头。Tier 为 Pro 时,还会显示 Server Connection、Offline & Device Policy、Security Level Enforcement(含 Security Config Hash (build allowlist) 列表)、Telemetry、Online Verification UI 和 Validate License Now,并且每个检测模块部分(Build Integrity、Install Source (Android Only)、Device Binding、Speed & Time Hack、Injection & Hooking、Steam Anti-Piracy (PC Only))中会出现包含该模块在线设置的 Pro - Online 区块。模块的 Activate 关闭时,该区块不可用。
这些值保存在 ProjectSettings/OZeroLicenseSettings.asset 中,请将该文件提交到版本控制;Project 窗口中没有设置资源。也可以在独立界面中编辑相同的设置:Security Config 和 Welcome 窗口中的 Online Services 按钮会打开 Window > OZero Security > Online Services 窗口(如设置不存在则创建),窗口中有 Select Settings Asset 和 Validate License Now 按钮。通过窗口顶部的 Security Config 和 Check Setup 按钮可以进入相应画面。本节其余部分以独立界面为准,项目名称在 Security Config 中相同。
在 License 部分选择层级。两种层级都会显示 License Key 字段。选择 Standard 时只显示 License 部分,并提示 Standard 无需服务器即可运行:在线服务处于关闭状态,不会向 OZero 服务器发送任何内容。只有在使用包含 Native Variant 的包时才需要输入 License Key,并且只在本地检查。选择 Pro 后会显示服务器连接和在线服务设置。
设置的结构与 Security Config 相同,顺序为 Quick Preset、License、Server Connection、Offline & Device Policy、Security Level Enforcement(通过标题中的 Enable 开启)、模块部分 Build Integrity、Install Source、Device Binding、Speed & Time Hack、Injection & Hooking、Steam Anti-Piracy,最后是 Telemetry 和 Online Verification UI。
整个 SDK 只有一个预设。这里的 Low / Standard / Strict 按钮与 Security Config 应用的是同一个预设,因此在任一处应用结果都相同。这里同样以 Standard 为推荐值,显示为 Standard (Recommended),每个按钮下方都有说明。应用预设会同时更新检测模块、在线服务默认值和 Declared Security Level,许可证、服务器 URL 和密钥保持不变。面板会显示当前状态,例如 Current: Standard,对应按钮以粗体和 * 标出;应用后手动修改在线设置时会显示 Current: Custom,此时任何按钮都不带标记。当 Declared Security Level 与所应用的预设不一致时,Security Level Enforcement 部分会显示警告。
模块部分的名称和顺序与 Security Config 相同。标题中显示该模块在 Security Config 中的状态(Active / Inactive,只读),每个部分开头用一行说明在线服务为该模块增加的内容。服务器开关位于该部分的第一行:
- Build Integrity:Check Integrity With Server
- Install Source:Check Installer With Server
- Device Binding:Verify Device With Server
- Speed & Time Hack 和 Injection & Hooking 有多个服务器功能,因此没有单一开关。
- Steam Anti-Piracy:Check Steam DRM With Server
在 Security Config 中,模块关闭时,该模块的 Pro - Online 区块会显示为不可用,并提示 Activate this module to use its online settings.。在独立界面中,未启用模块的部分会提示在启用模块之前这些在线设置不起作用,并提供 Open Security Config 按钮。
Telemetry 和 Online Verification UI 是在线专用部分。Telemetry 涉及发送到 Customer Portal 的安全事件和远程物理配置。Online Verification UI 决定服务器验证期间所显示画面(验证中、重试、需要在线、超时)的行为,设置项包括 UI 策略、重试与需要在线的时间、超时操作,以及网络恢复时自动重试。对话框的设计和语言遵循 Security Config > Player Notice (Managed UI)。当 Check Integrity With Server 和 Check Steam DRM With Server 都关闭时,会提示该部分未被使用。
附加组件没有单独的日志开关,日志遵循 Security Config 中的 Enable Debug Logs。
Pro 包与 Native Variant
Pro 客户从 Customer Portal 的下载页面获取 Pro unified package(Pro 统合包):为该许可证生成的一个 .unitypackage,包含 Standard SDK、在线附加组件以及所有平台的 Native Variant。卡片提供两个选项:
- With your Native Variant(包含 Native Variant):分配给该许可证的专用原生库。卡片会显示分配编号(Variant · Native · ABI),该包绑定到你的许可证(许可证密钥以及为其注册的 Bundle ID / 包名)。
- With the Standard variant (0000)(包含 Standard variant 0000):Standard SDK 自带的公共原生库,不绑定许可证。
更换(轮换)Variant 时:使用之前的 Variant 制作的现有构建仍可继续运行。新的 Variant 从更换后重新下载的软件包所制作的构建开始生效。更换会占用一次轮换次数,更换后无法退款。
要在 Tier Standard 下使用包含 Native Variant 的包,请将 Tier 保持为 Standard,只需在 Security Config > Pro - License & Online Services > License 中输入 License Key。构建和玩家只在本地检查密钥,Tier Standard 不会与 OZero 服务器通信。这依赖默认开启的 Allow Standard Build With Premium License;如果它被关闭,Check Setup 会提示你开启该选项或将 Tier 设为 Pro。
如果要在没有许可证密钥的情况下构建,请下载包含 Standard variant (0000) 的包。
如果你在 Unity Asset Store 购买了 SDK,请从 Asset Store 安装 SDK,并只从 Customer Portal 下载 Pro extension package(Pro 扩展包):其中包含针对卡片上所选 SDK 版本的在线附加组件和已分配的 Native Variant,不包含 SDK 本身。
Check Setup 中的 Native Variant 项目
当包中含有 Native Variant 时,Check Setup 会在构建前报告项目是否与之匹配。玩家构建也会因同样的原因停止。
| Check Setup 项目 | 处理方法 |
|---|---|
| Native Variant <NNNN> needs your license key | 在 Security Config > Pro - License & Online Services > License 中输入 License Key(Tier 可以保持 Standard),或下载包含 Standard variant (0000) 的包。也可以在 Check Setup > Auto Setup 中输入。 |
| Native Variant <NNNN> is bound to a different license key | 输入下载该包时所用许可证的密钥,或在使用此许可证登录的状态下重新下载该包。也可以在 Check Setup > Auto Setup 中输入。 |
| Application identifier is not registered for Native Variant <NNNN> | 根据构建目标,与 Customer Portal > Platform settings 中登记的值保持一致。Android / iOS:开启 Player Settings > Other Settings > Override Default Package Name / Bundle Identifier,并输入已登记的 Package Name / Bundle Identifier。Windows / macOS / Linux:将 Company Name 和 Product Name 设为已登记的值。WebGL:通过已登记的页面 origin 识别,项目中无需修改。也可以先在 Customer Portal 中为许可证登记该标识符,再重新下载包。还可以在 Check Setup > Auto Setup 中输入(设置前会与 Native Variant 核对)。 |
| Native Variant <NNNN> is license-bound, but Tier is Standard | 开启 Allow Standard Build With Premium License,或将 Tier 设为 Pro。 |
| Native Variant <NNNN> matches this project | 无需处理。Tier Standard 下只在本地检查密钥。 |
| Native Variant: Standard public (0000) | 无需处理。Standard variant (0000) 不需要许可证密钥。 |
游戏服务器集成:设备 ID 与用户 UUID
Pro 让你的游戏服务器可以配合 OZero 使用的 ID。读取 OZero 设备 ID 并与玩家账号一起保存,再关联游戏自己的用户 UUID(账号 ID),即可在 Customer Portal 中查找该用户使用过的设备并封禁该用户。
读取设备 ID
请读取 OZeroLicenseRuntime.CurrentDeviceId。在默认设置下,它与 Customer Portal 和遥测中显示的值相同。不要为了关联账号而替换 DeviceIdProvider,那样会改变服务器所知道的设备 ID。请改用下面的用户 UUID API,设备 ID 会保持不变。
关联用户 UUID
- 玩家登录后调用
OZeroLicenseRuntime.SetExternalUserId(uuid)。如果去掉首尾空白后为空、超过 128 个字符或包含控制字符,它会返回false并忽略该值。 - 登出时调用
OZeroLicenseRuntime.ClearExternalUserId()。可以通过HasExternalUserId判断当前是否关联了用户。 - UUID 会在设备内使用每个许可证独有的 salt 进行 SHA-256 哈希。遥测、物理事件和设备策略检查只发送哈希值,服务器也只保存哈希值。原始 UUID 不会离开设备,也不会写入磁盘。
- 可以在激活完成前调用。SDK 会先把 UUID 保存在内存中,激活后再开始发送哈希值。
封禁用户
在 Customer Portal 中,用户封禁规则有以下两种动作之一:
- 仅回调:游戏在每个会话中收到一次 policy action(
Module = "user_policy"、PolicyId= 规则 ID、Reason= 运营者填写的原因),由游戏决定如何处理。会话不会结束。 - 强制结束:游戏先收到 policy action(
Module = "user_policy"、ActionId = "user_blocked"、Reason = "terminate")。随后 SDK 显示与设备策略封禁相同的结束提示(OZ-SEC-0E)并结束会话。
通过 OZeroLicenseRuntime.LastServerBlockCode 区分用户封禁和设备封禁:用户为 "USER_BLOCKED",设备为 "DEVICE_BLOCKED"。判定会在调用 SetExternalUserId 后约 5 秒内执行(即使设备策略 heartbeat 已关闭),且最短间隔为 30 秒。如果网络失败,不会结束会话,由下一次检查判定。
Customer Portal:User UUID 页面
在 Customer Portal 中打开 Operations > User UUID(Pro)。该页面有三个标签页:
- Find user(查找用户):输入游戏传给
SetExternalUserId的 UUID。浏览器会在发送前进行哈希,原始值不会到达服务器。结果会显示该用户使用过的设备(首次和最近观测时间、事件数)、最近的事件(遥测、物理事件、策略封禁)以及封禁状态,并提供创建、编辑或解除封禁规则的按钮。Find by device(按设备查找)输入设备 ID(CurrentDeviceId),显示关联的用户数和每个用户键的前 8 个字符。原始 UUID 无法显示,请通过 UUID 搜索来确认用户。 - Block rules(封禁规则):查看规则列表(生效中或全部)。在 New rule 中填写用户 UUID、动作(Callback only 仅回调或 Terminate 强制结束)、同时会发送给游戏回调的原因,以及到期时间:Never(默认,直到解除)、7、30、90 天或指定日期。为已有规则的用户保存时会更新原规则。每条规则都会显示创建来源:Portal、Server API 或 Operator。
- Settings(设置):显示该许可证的 salt、计算公式、UUID 规范化规则、当前关联数量,以及在你的服务器上计算相同哈希(h1)的 C#、Node.js 和 Python 示例。你的服务器使用这个 h1 调用服务器 API。游戏客户端不需要 salt,SDK 会在激活时获取。
在 Settings 标签页中执行 Reissue salt(重新签发 salt)会断开现有关联:用旧 salt 建立的用户关联和封禁规则将不再与用户匹配,现有规则保留在旧键下。游戏会在下次激活时获取新 salt,你的服务器也必须用新 salt 重新计算 h1。
保存或解除封禁规则以及重新签发 salt 的操作,会记录在 Operation history(操作历史)的 User UUID 类别中。
Customer Portal 是单页应用。如果更新后页面显示仍是旧版或异常,请刷新页面。
与 Settings 标签页相同的示例。请用 API 参考(OZeroLicenseRuntime)中的测试向量验证你的实现。
// C#
using System.Security.Cryptography;
using System.Text;
static string ExtUserHash(string salt, string uuid)
{
var id = uuid.Trim(' ', '\t', '\n', '\v', '\f', '\r');
var bytes = SHA256.HashData(Encoding.UTF8.GetBytes("OZERO-EXTUID-V1|" + salt + "|" + id));
return Convert.ToHexString(bytes).ToLowerInvariant();
}
// Node.js
const crypto = require("crypto");
function extUserHash(salt, uuid) {
const id = uuid.replace(/^[ \t\n\v\f\r]+|[ \t\n\v\f\r]+$/g, "");
return crypto.createHash("sha256").update("OZERO-EXTUID-V1|" + salt + "|" + id, "utf8").digest("hex");
}
# Python
import hashlib
def ext_user_hash(salt: str, uuid: str) -> str:
uid = uuid.strip(" \t\n\v\f\r")
return hashlib.sha256(f"OZERO-EXTUID-V1|{salt}|{uid}".encode("utf-8")).hexdigest()
示例:登录、登出与 policy 回调
using OZeroSDK.Security;
using OZeroSDK.Security.License;
using UnityEngine;
public sealed class OZeroAccountLink : MonoBehaviour
{
private void OnEnable()
=> OZeroSecurityManager.Instance.RegisterUserPolicyActionCallback(OnPolicyAction);
private void OnDisable()
=> OZeroSecurityManager.Instance.UnregisterUserPolicyActionCallback(OnPolicyAction);
// Call after your sign-in succeeds.
public void OnSignedIn(string accountUuid)
{
if (!OZeroLicenseRuntime.SetExternalUserId(accountUuid))
Debug.LogWarning("OZero: user id rejected (empty, too long, or control characters).");
// Optional: keep the OZero device id with the account on your server.
string deviceId = OZeroLicenseRuntime.CurrentDeviceId;
MyGameServer.ReportDevice(accountUuid, deviceId);
}
// Call on sign-out.
public void OnSignedOut() => OZeroLicenseRuntime.ClearExternalUserId();
private void OnPolicyAction(OZeroPolicyActionEvent evt)
{
if (evt.Module != "user_policy")
return;
if (evt.ActionId == "user_blocked")
{
// Terminate rule: the SDK shows the termination notice and ends the session next.
MyGame.SaveQuickly();
return;
}
// Callback-only rule: the game decides what to do.
Debug.LogWarning($"User block rule {evt.PolicyId}: {evt.Reason}");
MyGame.RestrictAccount(evt.Reason);
}
}
隐私与商店披露
- 请使用化名的账号 ID 作为 UUID,不要使用邮箱地址或电话号码。
- iOS:附加组件的隐私清单已经声明了 User ID,无需额外添加。
- Google Play:如果关联了用户 UUID,请在 Data safety 表单中声明 User IDs。
安全设置哈希许可列表
每个构建都会封存其安全设置(OZeroSecurityConfig,包括在线附加组件设置)并计算 SHA-256 哈希。批准你发布的构建的哈希后,服务器的安全等级检查只放行这些构建,因此即使篡改构建削弱了设置并声明高安全等级,也无法通过。
- 兼容模式(没有已批准的哈希):只比较声明的安全等级,所有构建都会通过哈希检查。
- 强制模式(至少一个已批准的哈希):哈希未被批准的构建会被拒绝。开启 Fail On Security Level Reject 时游戏结束,否则降级为 Standard。
仅限 Pro。只有开启 Security Config > Pro - License & Online Services > Security Level Enforcement(标题中的 Enable,enableSecurityLevelCheck)时才会生效。关闭时构建不会发送哈希,许可列表不起作用,该部分会显示一条警告,而不是哈希列表。
查看构建的哈希
- 构建日志会输出
[OZero] Security config digest for this <Target> build: <64 hex>。 - 开启 Security Level Enforcement 时,Security Config 底部 Pro - License & Online Services 卡片的 Security Level Enforcement 部分中的 Security Config Hash (build allowlist) 会显示最近 5 次构建(目标平台、时间(UTC)、哈希和 Copy 按钮)。Window > OZero Security > Online Services 窗口的同一部分中也有相同的列表。
- 记录文件
Library/OZeroSecurity/BuildConfigDigests.json保存最近 20 次构建。 - 玩家发送的哈希与构建日志和 Pro 卡片(或 Online Services 窗口)中显示的摘要相同。
- 在另一台机器上用相同设置构建,哈希也相同。
- 编辑器 Play Mode 发送的是根据配置资源计算的哈希,而不是构建哈希。它可能出现在门户的 Hashes the server received 列表中,请不要批准它。
在 Customer Portal 中批准哈希
打开 Policy Settings > Security Config Hashes(Pro)。
- 顶部显示策略模式(Enforced 强制或 Compatibility 兼容)以及有效的已批准哈希数量。
- Approved hashes(已批准的哈希):点击 Revoke 解除。解除最后一个哈希后,许可证会回到兼容模式。
- Hashes the server received(服务器收到的哈希):开启 Security Level Enforcement 的构建发送的哈希(不保存设备 ID,观测次数每小时最多计一次)。在发布构建的哈希上点击 Approve。
- Register a hash(直接登记哈希):输入构建日志或 Pro 卡片中的哈希、标签以及到期时间(Never、30 天、90 天或 1 年)。
- 首次批准时会要求确认“未批准的构建将被拒绝”。开启前请先批准当前正在发布的所有构建。
批准和解除操作会记录在 Operation history(操作历史)中。要从 CI 登记哈希,请参阅 API 参考中的安全设置哈希(CI)。
Pro 许可证密钥注册
Standard 不需要单独的许可证设置即可使用。使用 Pro 时,请在 Online Services 设置(保存在 ProjectSettings/OZeroLicenseSettings.asset 中)中输入为项目签发的许可证密钥。Pro 密钥用于从门户下载项目专用 Native Variant 包,并在构建时确认该包属于当前项目。Pro 密钥还用于运行时服务器激活和 Pro 专用服务器功能。
1. 创建设置资产
在线附加组件会在导入后编辑器空闲时,自动在 ProjectSettings/OZeroLicenseSettings.asset 中创建许可证设置(如果 Assets/ 下仍有旧的 OZeroLicenseConfig 资源则不会创建)。请在 Security Config 底部的 Pro - License & Online Services 卡片中输入许可证:在 License 中选择层级并输入许可证密钥(Standard 仅在使用 Native Variant 包时需要);服务器 URL 和密钥位于 Server Connection。在 Security Config 或 Welcome 窗口中点击 Online Services 会打开包含相同设置的 Window > OZero Security > Online Services 窗口,如设置不存在则会创建。请将 ProjectSettings/OZeroLicenseSettings.asset 提交到版本控制。
ProjectSettings/OZeroLicenseSettings.asset 中。请通过 Online Services 按钮(Security Config 或 Welcome 窗口)或 Window > OZero Security > Online Services 打开。
2. 填写 Inspector 字段
| 字段 | 是否必需 | 说明 |
|---|---|---|
| tier | 所有层级 | 选择此构建要使用的许可证等级。Standard 只使用共享 Native 模块,不与服务器通信。Pro 在项目专用 Native Variant 和 manifest 验证之外,还提供服务器激活、遥测和远程策略等运营功能。 |
| licenseKey | Pro | 为项目签发的 OZ-PRO-... 格式许可证密钥。它用于验证 Native Variant 的项目绑定,并启用运行时激活和 Pro 服务器功能。 |
| appIdentifier | 自动传输 | 发送 Pro 激活请求时,SDK 会同时发送 Unity 的 Application.identifier。如果它与客户门户中登记的 Bundle ID 或 Package Name 不一致,激活可能会被拒绝。请先确认 Unity Player Settings 中的 Identifier。 |
| serverBaseUrl | Pro runtime | Pro 功能与 OZero 服务器通信时使用的基础地址。激活、遥测、signed time、attestation 和服务器策略检查都会使用它。Standard 运行时不会调用此 URL。除非 OZero 支持团队提供了其他地址,否则保留默认值 https://api.ozerosecurity.com。 |
| serverPublicKeyHex | Pro runtime | 客户门户 > Server Key 菜单中显示的服务器签名公钥。Pro 运行时用它确认 activation、signed time、attestation 和 offline policy 响应是否来自 OZero 服务器。Standard 运行时不使用此值。Pro Variant manifest 会通过 SDK 内置的 OZero Variant signing key 单独验证。 |
| previousServerPublicKeyHex | Pro 可选 | 仅在 Pro 服务器签名密钥切换的短暂期间使用的旧公钥。只有 OZero 支持团队要求轮换密钥时才填写,平时请留空。Standard 运行时不使用此值。 |
| offlineProPolicyMode | Pro | 这是 Pro 选项,用来决定设备离线时本地 Build Integrity 如何使用已签名的门户阻止策略。ApplyCachedBlockPolicies 只在存在有效缓存策略时应用,是推荐给大多数正式运营游戏的 fail-open 默认值。RequireFreshPolicy 在没有可用签名策略且服务器 Integrity 已启用时 fail-closed。IgnoreCachedBlockPolicies 会为了测试或迁移绕过缓存策略。每个值的含义可在 OZeroOfflineProPolicyMode 中查看。 |
| activationTimeoutSeconds | Pro runtime | SDK 等待 Pro 激活响应的时间,默认值为 15 秒。服务器未响应时,只有已保存的 Pro 信息仍然有效且门户策略允许离线使用,SDK 才会使用这些信息。策略要求在线确认时,应用会停止启动,直到可以连接服务器。 |
| enableLog | 可选 | 检查器中不再显示。许可证缓存使用、激活成功、超时、签名不一致等流程日志遵循 Security Config 中的 Enable Debug Logs。 |
| 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. 确认 Check Integrity With Server
构建前请确认 Security Config > Build Integrity > Pro - Online > Check Integrity With Server 是你想要的值。Standard 和 Strict 预设会开启它,Low 预设会关闭它;新建的 Online 设置以 Standard 的值(开启)开始。开启后会增加服务器构建判定和运行期间的再次确认,使用 Standard 时启动需要联网。门户的设备和 Variant 封禁无论此值是否开启都会生效。使用默认 Built-In 确认 UI 时需要 TMP dialog 包,Auto Setup 会导入它。如果游戏需要离线运行,请参阅离线操作。
4. 构建与验证
不需要额外编写代码。应用启动时,SDK 会自动读取构建时以加密资源形式放入玩家包的许可证设置。首次运行后,请在 Player 日志中确认是否出现类似 [OZeroLicense] activated; tier=pro caps=5 的行。如果已设置为 Pro 但日志显示 Standard 模式,请先确认 ProjectSettings/OZeroLicenseSettings.asset 存在且在构建前已保存,并且 Check Setup 没有显示 Old OZeroLicenseConfig asset found。
离线操作
Standard 无需服务器即可运行。Pro 可以使用已保存的激活信息离线启动;如果许可证策略要求每次启动都在线确认,则需要连接服务器。
Standard — 无需互联网即可启动
Standard 不会向服务器请求许可。它只运行应用内置的本地保护模块,所以玩家离线时游戏也能启动。只有遥测、signed time 等需要 Pro 服务器的功能不可用。
Pro — 本地保护保持离线,所选服务器功能使用缓存
启用可选的 Pro 服务器功能后,SDK 会在联网时确认设备和许可证。所选服务器功能可在有限时间内容忍短暂断网。此检查不会限制使用已获取 Native Variant 的构建或本地保护。
需要离线运行的游戏:Standard 和 Strict 预设会开启 Check Integrity With Server,因此需要联网完成服务器验证。若要离线运行,请应用 Low 预设,或关闭 Security Config > Build Integrity > Pro - Online > Check Integrity With Server。此时将不进行服务器构建判定和运行期间的再次确认。
已保存的 Pro 激活信息在服务器签名的激活令牌到期前有效。到期时间由 OZero 服务器在签发令牌时决定(目前为激活成功后 7 天),SDK 无法延长。超过该期限后,需要再次成功激活才能使用 Pro 服务器功能。已应用的 Native Variant 和本地检测设置不会改变。此有效期与下方的本地时间策略分别生效。
Pro — 选择如何使用设备时间
在客户门户 > 许可证 > 本地时间中,可以设置已保存的 Pro 激活信息能否用于离线启动。在线激活使用服务器时间,因此设备日期或时间不准确不会单独导致在线 Pro 功能降级。
| 设置 | 应用启动时的行为 |
|---|---|
| 使用本地时间 | 设备时间在所选允许范围内时,可以使用已保存的 Pro 激活信息离线启动。默认允许范围为 10 分钟,也可选择 3 分钟、1 小时、3 小时、6 小时、12 小时或 24 小时。 |
| 不使用本地时间 | 不检查设备时间,但应用每次启动时都必须连接许可证服务器。适合要求始终在线启动的服务。 |
Pro — 门户中阻止的版本离线也会被阻止
临时服务器或网络问题下继续以基础保护启动,和允许运营人员在门户中明确阻止的版本运行,是两件不同的事。Pro 激活成功时,服务器会同时下发带签名的阻止列表,例如被阻止的构建哈希、SDK 版本和应用版本。SDK 会将该策略与临时服务器功能状态分开处理。
请在 Security Config 的 Pro - License & Online Services 卡片中 Offline & Device Policy 部分,通过 Offline Pro Policy Mode(OZeroLicenseConfig.offlineProPolicyMode,Online Services 窗口中也有)配置此行为。它决定设备无法连接服务器时,本地 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,这是正常情况。没有 ProjectSettings/OZeroLicenseSettings.asset 时,构建以 Standard(不使用服务器)运行。如果预期是 Pro,请确认该文件存在(Security Config 或 Welcome 窗口中的 Online Services 按钮会创建它)、tier=Pro 和 licenseKey 已填写,并且 Check Setup 没有显示 Old OZeroLicenseConfig asset found。 |
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、macOS、Linux 请检查 Player Settings 中的 Company Name 和 Product Name,WebGL 请检查运行 origin。Unity 默认值(DefaultCompany、My project、New Unity Project 或空值)无法登记:在 Pro 且构建目标为桌面平台时,Check Setup 会显示 Company Name is still Unity's default(或 Product Name is still Unity's default)警告;若不修改,激活会以 403 DESKTOP_COMPANY_NAME_MISMATCH / DESKTOP_PRODUCT_NAME_MISMATCH 被拒绝,游戏将以 Standard 运行。如果这些值与门户登记不一致,/v1/activate 会被拒绝。修改后请用新构建重新测试。 |
activation token signature verification failed |
服务器有响应,但 SDK 判断该响应不能被信任。 | 此项只适用于 Pro 激活。请将客户门户 > Server Key 的 Active publicKey 填入 Window > OZero Security > Online Services > Server Connection > Server Public Key Hex。即使门户同时显示 kid,Unity 中也只填 64 位 hex publicKey。若值完全正确仍失败,请在不经过公司代理/MITM 设备的直接网络上再测试一次。如果 Check Setup 显示 Pro server signing public key is not configured,也可以在 Check Setup > Auto Setup 中粘贴。 |
private native variant manifest signature is invalid |
Variant 包的 manifest 缺失、被编辑,或不是由受信任的 OZero Variant signing key 签名。 | 不要通过填写 Server Public Key Hex 来修复此问题。对于 Pro Variant 包,请重新下载分配给该项目的 Variant 包,并确保 manifest 与 native plugin 文件来自同一个包。如果重新下载后仍失败,请将 manifest 文件和 Unity 构建日志发送给 OZero 支持团队。 |
DEVICE_BLOCKED |
这台设备已在客户门户中被阻止。 | 请在门户中查看该 deviceId 的安全事件。如果是真实攻击或违反策略,请保持阻止;如果是 QA 设备或误报,请在门户中解除阻止。SDK 收到这个响应后,会清除保存的 Pro 权限并执行阻止流程。 |
/v1/activate timed out / network error |
设备无法连接服务器,或服务器响应太慢。 | 请确认设备能访问 https://api.ozerosecurity.com/health。同时检查公司防火墙、代理和 DNS。高延迟地区可以稍微提高 activationTimeoutSeconds 后再测试。如果已经有保存的 Pro 信息,SDK 会优先使用它。 |
cached entitlement past TTL; clearing. |
保存的 Pro 信息已过有效期。 | 如果设备离线时间超过激活令牌的有效期(最近一次激活成功后 7 天),保存的 Pro 信息就不再被信任。游戏会继续以 Standard 级别运行,下一次在线激活成功后 Pro 功能会恢复。该期限由 OZero 服务器决定,无法在 SDK 中延长。 |
SERVER_NOT_CONFIGURED / SIGN_FAILED |
服务器无法为激活令牌签名。 | 大多数情况下,这不是客户端设置问题,而是服务器 Server Key 或加密密钥状态问题。请检查客户门户中的 Server Key 状态。生产环境发生时,请把日志时间、部分 licenseKey 和发生平台提供给 OZero 支持团队。 |
项目设置
在调整各个安全模块之前,请先检查影响原生插件、商店构建和平台验证的 Unity Player Settings。
最低构建目标
| 平台 | 最低目标 | 说明 |
|---|---|---|
| Unity Editor | 2022.3 LTS+ |
需要 2022.3 LTS 或更高版本,也支持 Unity 6。 |
| iOS | 14.0+ |
请将 Player Settings 中的 Target minimum iOS Version 设为 14.0 或更高。 |
| macOS | Apple Silicon |
仅支持 Apple Silicon(arm64)Mac,推荐使用 IL2CPP。 |
| Android | API 24+ |
请将 Minimum API Level 设为 24(Android 7.0)或更高。发布构建推荐使用 IL2CPP 和 ARM64。 |
详细:各平台的要求
- Unity Editor: OZero Security SDK 需要 Unity 2022.3 LTS 或更高版本,Unity 6 也已验证。2021.3 项目在导入软件包前必须升级。
- iOS: iOS 构建请将
Project Settings > Player > iOS > Target minimum iOS Version设为14.0或更高。ABI 21 的注入检测器使用 iOS 14 的 shared-cache API 区分 Apple 系统映像与注入代码,低于该版本时 Editor preflight 会失败。 - macOS: macOS 构建支持 Apple Silicon(arm64)Mac,不支持 Intel Mac。推荐使用 IL2CPP:Scripting Backend 为 Mono 时,Check Setup 会显示 IL2CPP 推荐警告,默认开启的 Fail Build on Setup Warnings 设置会使构建失败。如需有意使用 Mono,请关闭该设置并按照 macOS 签名 步骤操作。
Apple 隐私清单
Apple privacy manifest:SDK 附带 PrivacyInfo.xcprivacy,Unity 后处理会将应用的 manifest 写入 iOS Xcode 应用 target 和 macOS 的 Contents/Resources。Standard 构建只声明 UserDefaults 的 CA92.1 理由(OZero 使用 Unity PlayerPrefs 保存应用内 SDK 状态),不声明收集数据。使用 OZero 服务器功能的 Pro 许可证构建还会声明 PrivacyInfo.ServerFeatures.plist 中的 4 种收集数据(Device ID、User ID、Gameplay Content、Other Diagnostic Data),均与用户关联、不用于跟踪、用于应用功能。如果应用在同一位置(iOS Xcode 项目根目录,或 macOS 的 Contents/Resources)已有 PrivacyInfo.xcprivacy,SDK 不会覆盖,而是合并其声明:访问 API 理由和收集数据按类型合并,应用自身的 NSPrivacyTracking 和 NSPrivacyTrackingDomains 保持不变。请在最终应用中保留该 manifest,并在提交前检查 Xcode 隐私报告。
发布构建的推荐设置
发布构建推荐使用以下设置。
- 将 Scripting Backend 设为 IL2CPP(Project Settings > Player > Other Settings)。
- 将 Managed Stripping Level 设为 Low。使用 Mono 后端时请保持 Disabled。
- 修改设置后,请在真机上测试场景切换、存档/读档以及支付、广告等外部 SDK。
详细:推荐这些设置的原因
安全强化(IL2CPP 设置):在 Android、iOS 等 Unity 支持的环境中,请将脚本后端设置为 IL2CPP。IL2CPP 不能完全阻止逆向分析,但可以减少 C# 代码和元数据的直接暴露范围,并帮助 OZero 安全检查在更稳固的发布环境中运行。路径:Project Settings > Player > Other Settings > Scripting Backend
体积优化(Managed Stripping 设置):Managed Stripping 会移除未使用的 managed code,从而减少应用体积。如果一开始移除过多代码,应用可能在运行时出错,因此建议先从 Low 或 Medium 开始。使用 Mono 后端时,请将 Managed Stripping Level 保持为 Disabled:启用后 Unity 会一并放入 managed .pdb 文件(<Product>_Data/Managed/*.pdb),OZero 发布构建检查会将其视为调试符号并阻止构建。
注意事项:修改设置后,请先在真实设备上测试场景切换、数据保存、Addressables,以及支付/广告等外部 SDK 是否正常工作,再提高 stripping level。
客户端保护的局限
Mono 构建可以用常见工具反编译和修改,修改后的构建可以跳过客户端检查。IL2CPP 会大幅提高修改成本,但也不是完全的防护。真正的对策是服务器验证,以及让游戏代码依赖 Secure Variables 等 OZero API。
对于被绕过会造成较大损失的游戏,推荐以下配置:
- 将关键游戏数值(货币、分数、属性)交给 Secure Variables(OZeroSV) 管理。移除或禁用 SDK 会导致游戏无法正常进行,从而提高绕过成本。
- 开启服务器完整性验证(
OZeroManaged或CustomerGameServer),并在游戏服务器上检查 OZA 令牌或 managed session。修改后的客户端无法获得有效的服务器签名。
详细:启动文件编辑与加载锚点
编辑播放器启动文件。Unity 通过 RuntimeInitializeOnLoads.json、ScriptingAssemblies.json 以及 globalgamemanagers.assets 中的脚本表找到 OZero 的启动入口。在删除或修改了这些文件的构建中,OZero 可能根本不会启动。SDK 1.2.0 通过以下三项措施应对:
- 原生加载锚点:OZero 原生代码会在任何脚本运行之前先启动。Windows、macOS、Linux 由播放器通过插件的 Load on startup 设置加载。iOS 和 IL2CPP 的 Android 会把原生核心链接进应用二进制(Android 上是
libil2cpp.so中的Plugins/Android/<abi>/libOZeroSecurity.a),通过静态初始化启动,因此修改 manifest 或启动 JSON 文件不会影响它,要绕过必须修补libil2cpp.so二进制。Mono 的 Android 无法静态链接,由OZeroSecurityBoot.androidlib中的 ContentProvider 加载libOZeroSecurity.so。如果 90 秒内 OZero 引导程序没有启动,原生代码会终止进程(abort reason0x0BADB008)。 - 哈希范围:这些启动文件始终包含在 Build Integrity 聚合哈希(
oz_ahash)中:Windows、macOS、Linux(Mono 与 IL2CPP)包含全部四个文件,Android 包含RuntimeInitializeOnLoads.json和ScriptingAssemblies.json。因此 OZero 启动后,即使只修改了一部分也会被检测到。 - manifest 签名:Standard 预设现在也与 Strict 一样要求已签名的 manifest(开启 Require Manifest Signature)。
0x0BADB008)。我们没有提供开发用的例外开关,因为这种开关同样会被攻击者利用。
globalgamemanagers 的 preload 列表中移除 OZero(桌面),或修补应用二进制(iOS、IL2CPP 的 Android),然后重新签名构建,仍然可以让 OZero 无法启动。Mono 的 Android 要容易得多:只需从 manifest 中删除 provider 并重新签名 APK,锚点就会消失,因此Android 发布构建请使用 IL2CPP。根本对策是让游戏代码真正依赖 Secure Variables(OZeroSV) 等 OZero API,使去掉 OZero 的构建无法正常游玩。
macOS:Developer ID 签名与公证
Unity 生成的 macOS 构建是 ad-hoc 签名,开启 Build Integrity 时会在启动约 10 秒后退出。要分发,必须使用 Developer ID Application 证书签名。开启自动签名后,OZero 会在构建结束时签名。
开启自动签名(推荐)
- 打开 Window > OZero Security > macOS Distribution Signing,开启 Sign macOS builds。构建目标为 macOS 时,也可以通过 Check Setup 中的 Open signing settings 打开。
- 在 Certificate 中选择 Developer ID Application 证书。钥匙串中只有一个时,保持 Automatic 即可。
- 如需在构建时一并公证,请开启 Notarize,并输入事先用
xcrun notarytool store-credentials创建的 Notary keychain profile 名称。 - 点击 Save。之后每次 macOS 构建完成时都会自动签名,Console 中出现
macOS distribution signing complete后即可直接发布。
详细:自动签名设置与 CI
准备:安装了 Xcode command line tools 的 Mac,以及当前登录用户钥匙串中的 Developer ID Application 证书。如需公证,请先用 xcrun notarytool store-credentials <配置文件名> 保存一次公证凭据。
- 在 Certificate 列表中选择 Developer ID Application 证书。钥匙串中只有一个时保持 Automatic 即可。点击 Refresh 会重新读取列表。仅当证书位于钥匙串搜索列表之外(例如 CI 钥匙串)时,才在 Advanced (CI or a separate keychain) 中指定 Keychain path 并点击 Refresh;其他情况下保持 Advanced 为空。
- 点击 Save。之后每次 macOS 构建完成时,OZero 都会自动签名(嵌套代码 → 重新生成 OZero 完整性文件 → 应用签名 → 验证 → 选择时进行公证)。Console 中出现
macOS distribution signing complete (TeamIdentifier=…)后即可直接发布。设置保存在ProjectSettings/OZeroMacOSSigning.json中,不保存任何机密。 - 如果钥匙串中恰好有一个 Developer ID Application 证书,可以直接在 Auto Setup 中开启(macOS distribution signing: Off -> On 步骤)。
ProjectSettings/OZeroMacOSSigning.json 中,可通过版本管理与团队共享,不会保存密码。请在 Mac 的已登录会话中构建:codesign 需要已解锁的钥匙串,通过 SSH 或在锁定的 CI 会话中会失败。签名失败时构建也会失败。Valve 的 libsteam_api 会自动保留签名,其他厂商已签名的插件请填写到 Keep vendor signature。自动签名关闭(默认)时,构建日志会提示签名,应用保持 ad-hoc 签名。Windows 编辑器无法签名,开启此设置时在其上进行 macOS 构建会失败;请在该电脑上设置 OZERO_MACOS_AUTOSIGN=0,并在 Mac 上用脚本签名。 签名日志保存在项目的 Library/OZeroMacSign-logs/<应用名> 中,而不是构建目录旁。
OZERO_MACOS_AUTOSIGN(1 或 0)、OZERO_MACOS_SIGN_IDENTITY、OZERO_MACOS_SIGN_KEYCHAIN、OZERO_MACOS_KEEP_SIGNED、OZERO_MACOS_NOTARY_PROFILE(有值时开启公证)。请在构建前于作业中解锁签名用钥匙串。
在 Windows 上构建或为已有 .app 签名时:签名脚本
在 Mac 上运行 OZero 附带的脚本。指定 .app 以及生成该应用的 Unity 项目,运行前请先在编辑器中关闭该项目。
bash "/path/to/YourProject/Assets/OZeroSecurity/Tools/macOS/OZeroMacSign.sh" \
--app "/path/to/YourGame.app" \
--project "/path/to/YourProject" \
--notary-profile "YourNotaryProfile"
脚本输出 done 后即可直接发布。之后请勿再次签名;如果必须重新签名,请对该应用重新运行脚本。如果脚本中途停止,最后几行会显示失败的步骤和解决方法,日志保存在当前目录的 OZeroMacSign-logs 中(可用 --log-dir 更改)。
--identity 指定 security find-identity -v -p codesigning 中显示的证书 SHA-1。如需自行公证,省略 --notary-profile 即可。Steamworks(libsteam_api)会自动保留 Valve 签名,其他厂商已签名的插件请用 --keep-signed <文件名> 指定。脚本会在 Unity Hub 目录和 Spotlight 中查找项目的编辑器版本;如果找不到,请用 --unity 指定 Unity 可执行文件,或设置环境变量 OZERO_UNITY_PATH。全部选项可通过 --help 查看。
Entitlement
自动签名和脚本会自动选择。使用 --entitlements 传入自己的文件或不使用 OZero 工具签名时,请按此表设置。
| Entitlement | IL2CPP(推荐) | Mono | 另外使用 Steamworks 等厂商已签名插件时 |
|---|---|---|---|
| com.apple.security.cs.allow-jit | — | 需要 | — |
| com.apple.security.cs.disable-library-validation | — | — | 需要 |
| com.apple.security.cs.allow-dyld-environment-variables | 请勿添加 | 请勿添加 | 请勿添加 |
| com.apple.security.get-task-allow | 请勿添加 | 请勿添加 | 请勿添加 |
allow-dyld-environment-variables 判定为 macos_unsafe_dyld_state;允许本机任意进程附加调试器的 get-task-allow 判定为 macos_debuggable_signature。存在 disable-library-validation 时,OZero 不会拦截,只记录警告日志。无需使用范围更广的 disable-executable-page-protection。
应用仍然退出时:按消息查看原因和解决方法
| 消息 | 原因 | 解决方法 |
|---|---|---|
| macos_unsafe_dyld_state | 应用在没有 hardened runtime 的情况下运行(例如直接发布了 Unity 构建),或者带有 allow-dyld-environment-variables。 | 开启自动签名或使用脚本签名。如果使用自己的 entitlement 文件,请删除该项。 |
| macos_debuggable_signature | 应用带有 get-task-allow。 | 从 entitlement 文件中删除该项后重新签名。 |
| macos_outer_signer_mismatch | 重新生成完整性文件后,外层 .app 又用其他证书或 ad-hoc 重新签名。 | 使用自动签名重新构建,或对该应用重新运行脚本。外层 .app 只用相同的 Developer ID 签名一次。 |
| aggregate hash mismatch | 重新生成完整性文件后又签名或修改了文件,或者跳过了重新生成。 | 使用自动签名重新构建,或对该应用重新运行脚本。之后不要修改应用内的文件。 |
| Library not loaded … different Team IDs | 缺少 disable-library-validation,macOS 未加载 Steamworks 等厂商已签名插件。这发生在 OZero 启动之前。 | 使用自动签名或脚本;保留厂商签名时,两者都会自动添加该 entitlement。自行签名时请将其加入 entitlement 文件。 |
| macOS nested signing failed (OZeroMacSign.sh exit …) | codesign 无法使用钥匙串:构建通过 SSH 或在锁定的 CI 会话中运行,或者未安装 Developer ID Application 证书。Console 中会显示脚本输出。 | 在 Mac 的已登录会话中构建,或在 CI 作业中解锁钥匙串(security unlock-keychain)。可用 security find-identity -v -p codesigning 检查证书。 |
| Notarize is on but no notary keychain profile is set | 签名设置中开启了 Notarize,但公证钥匙串配置文件为空。构建会在签名前停止。 | 使用 xcrun notarytool store-credentials <名称> 创建配置文件并填入 Notary keychain profile,或关闭 Notarize。 |
| more than one identity matches | 钥匙串中有多个符合条件的证书。 | 在 Certificate 列表中选择一个证书,或将输出列表中的一个 SHA-1 填入 Advanced > Identity (SHA-1 or name)(脚本使用 --identity)。 |
不使用脚本自行签名(CI 或自有流水线)
自动签名和脚本按以下顺序执行三个步骤。在自有流水线中签名时也请保持此顺序:在第 3 步之后重新签名任何内容,都需要从头重做三个步骤。
- 使用
codesign --force --options runtime --timestamp为Contents内的所有.dylib、.bundle和.framework签名。Valve 的libsteam_api保留 Valve 原有签名。 - 重新生成 OZero 完整性文件。签名会改变 OZero 在构建时计算哈希的插件文件,因此省略此步骤会导致应用以
0x0A退出。使用生成该构建的项目以 batch mode 运行 Unity,并将OZERO_MACOS_SIGNED_APP_PATH设为.app路径(见下方命令)。IL2CPP 和 Mono 构建使用相同的步骤。重新生成时还会记录已签名内部代码(GameAssembly.dylib,Mono 为UnityPlayer.dylib)的 Team ID。 - 在不使用
--deep的情况下,用相同的 Developer ID 和下表中的 entitlement 为外层.app签名一次,然后进行公证和 staple。如果外层签名的 Team ID 与记录值不同或为 ad-hoc,OZero 会以macos_outer_signer_mismatch(0x0A)关闭应用。此检查用于阻止复制发行版后只替换外层签名、添加权限或注入库的副本。
OZERO_MACOS_SIGNED_APP_PATH="/path/to/YourGame.app" \
"/path/to/Unity.app/Contents/MacOS/Unity" -batchmode -nographics -quit \
-projectPath "/path/to/YourProject" \
-executeMethod OZeroSDK.Security.Editor.OZeroMacOSPostSignIntegrityRebaker.RebakeAfterNestedSigning \
-logFile rebake.log
Windows / Linux:代码签名与完整性文件重新生成
OZeroSecurity.dll、OZeroSecurity64.dll、OZeroPCStoreBridge.dll、OZeroPCStoreBridge64.dll,包括 Pro 软件包)带有 OZero Security Inc 名义的代码签名和时间戳。右键单击文件,打开属性 > 数字签名,确认签名者为 OZero Security Inc 且带有时间戳,并在签名详细信息中确认签名有效。如果没有签名或显示为其他发布者,则不是正版软件包,请从客户门户或购买的商店重新下载。
如果不自行对 Windows 或 Linux 构建进行代码签名,请跳过以下步骤。如果在构建后对可执行文件或原生模块签名,签名后必须重新生成 OZero 完整性文件,否则游戏会在启动时因完整性错误(0x0A)退出。
- 照常构建后,为可执行文件和您自己的 DLL 或
.so签名。OZero DLL 已经签名,无需再次签名。发布构建需要操作系统信任的证书,自签名测试证书无法通过。 - 用下面的命令按已签名的输出重新生成完整性文件,也可以在编辑器中使用 Tools > OZero Security > Re-bake Integrity After Code Signing (Windows or Linux player)...。
- 直接打包并发布。完整性文件位于
<Game>_Data/StreamingAssets下,不属于签名对象。如果使用服务器验证,请登记重新生成时输出的 manifest 哈希。
set OZERO_SIGNED_PLAYER_PATH=C:\dist\YourGame.exe
"C:\Program Files\Unity\Hub\Editor\<version>\Editor\Unity.exe" -batchmode -nographics -quit ^
-projectPath "C:\path\to\YourProject" ^
-executeMethod OZeroSDK.Security.Editor.OZeroStandalonePostSignIntegrityRebaker.RebakeAfterSigning ^
-logFile rebake.log
Assets/OZeroSecurity/Plugins)签名:构建时会与 OZero 签名的 Variant 清单中的哈希进行比对,原始 DLL 被修改后构建将停止。
自定义构建脚本:读取和修改安全设置(高级)
OZero 在构建开始阶段(回调顺序 50)将安全设置烘焙为 oz_cfg.bin。设置位于 Assets/ 之外的 Project Settings 中,因此构建期间不会移动或恢复任何内容,也不会有明文设置资源进入玩家包。无论烘焙前后、在哪个回调顺序,OZeroSecurityConfig.Instance 都会返回项目设置。WebGL 构建使用构建时生成的资源(构建后删除),而不是明文资源。
- 在编辑器代码中用
OZeroSecurityConfig.Instance读取值,callbackOrder没有限制。 - 要通过脚本(CI 或构建流水线)修改设置,请使用
OZeroSDK.Security.OZeroSecurityProjectSettings:项目没有设置时Load()返回null,LoadOrCreate()会在缺失时以 Standard 预设创建,Save()负责写入。可以通过new SerializedObject(...)等方式编辑返回的对象;即使尚未保存,构建也会烘焙内存中的值。 DeveloperSecret和 manifest 签名私钥路径是UserSettings/OZeroSecuritySecrets.json中按用户保存的值。在构建机器上请设置OZERO_DEVELOPER_SECRET和OZERO_MANIFEST_SIGNING_PRIVATE_KEY_PATH;设置的环境变量优先于已保存的值,且不会写回文件。- 如果
Assets/下仍有 SDK 1.1 或更早版本的OZeroSecurityConfig资源,构建会停止。请用 Check Setup(Import 或 Auto Setup)导入,或在批处理模式下使用-executeMethod OZeroSDK.Security.Editor.OZeroLegacyConfigImporter.ImportForBatchMode。 - 在线附加组件的许可证设置也是同样的方式:保存在
ProjectSettings/OZeroLicenseSettings.asset中(没有该文件时为 Standard、不使用服务器的构建),在脚本中通过OZeroSDK.Security.License.OZeroLicenseProjectSettings(Load、LoadOrCreate、Save、Reload)修改。它会以构建时生成的加密资源放入玩家包,构建后删除。Assets/下残留旧的OZeroLicenseConfig资源同样会使构建停止;请通过 Check Setup(Old OZeroLicenseConfig asset found)或批处理模式的-executeMethod OZeroSDK.Online.Editor.OZeroLegacyLicenseImporter.ImportForBatchMode导入。
Android ProGuard / R8 设置
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/OZeroSecurity/Plugins/Android/arm64-v8a下。如果您要分发 32 位版本,也请保留armeabi-v7a。 - 如果您使用 Build Integrity > Check Platform Native 和 Android SHA Keys,请务必在应用 Minify/R8 后,使用最终签名的 APK 或 AAB 进行测试。调试密钥库的指纹与发布密钥库的指纹是不同的。
详细:为何需要这些规则以及 Android 原生配置
OZero Security 不需要单独的 Java SDK 包。但是,如果您的 Android 部署版本启用了 Minify、ProGuard 或 R8,则必须保留 Unity Java 桥接以及项目中使用的自定义 Android 桥接类。这样才能确保获取包信息、安装来源、APK 签名证书检查和启动资产加载所需的 JNI 调用在混淆后依然稳定运行。
- 如果只有在开启 Minify 之后,Android 日志中才出现 JNI 查找失败、安装来源检测失败或 APK 签名检查结果为空的情况,请先检查您的自定义桥接的 keep 规则。
- 对于商店分发的构建版本,请基于 IL2CPP、Release 签名、
Android Sha Keys中注册的预期 Android SHA-256 指纹,并在至少一台真机上进行干净运行测试后再做判断。
原生核心与启动 provider:软件包为每个 ABI 同时包含 libOZeroSecurity.so 和静态核心 libOZeroSecurity.a,构建时 OZero 会按 Scripting Backend 选择其一,因此无需修改插件导入设置。IL2CPP 会把 libOZeroSecurity.a 链接进 libil2cpp.so,并排除 .so 和启动 provider:APK 中没有 libOZeroSecurity.so,OZero 代码包含在 libil2cpp.so 的哈希中。Mono 会包含 libOZeroSecurity.so 和 Plugins/Android/OZeroSecurityBoot.androidlib:Unity 会将该库作为子项目加入导出的 Gradle 项目,合并后的 manifest 中会添加 provider com.ozerosecurity.boot.OZeroBootProvider(exported=false,authority 为 <applicationId>.ozerosecurity.boot),它会在应用进程启动时加载 .so。即使使用自定义 main manifest 或 Gradle 模板,也不要移除该库和 provider。如果 Mono 构建无法加载原生库(包括缺少设备 ABI 对应的 .so),应用会在启动后立即退出。
5
安全设置 (OZeroSecurityConfig)
导入包后会以 Standard 预设创建安全设置。请在 Edit > Project Settings > OZero Security 中编辑,Window > OZero Security > Config & Dashboard 也会打开同一页面。共享值保存在 ProjectSettings/OZeroSecuritySettings.asset 中,不含机密,请提交到版本控制。DeveloperSecret 和 manifest 签名私钥路径是按用户保存的值,存放在 UserSettings/OZeroSecuritySecrets.json 中,不纳入版本控制。在运行时和编辑器脚本中用 OZeroSecurityConfig.Instance 读取;若要通过脚本修改,请参见自定义构建脚本。页面顺序为 Quick Preset、Project & Advanced(Common Settings、Editor Play Mode)、Detection Response(Global Threat Response、Player Notice (Managed UI))、Protection Modules。所有部分初始均为折叠状态,请点击标题展开后再设置。Security Termination Notice 策略位于 Detection Response 下的 Player Notice (Managed UI) 中。
通用设置
这些是所有安全模块共享的顶级设置。本表中的默认值以代码中定义的序列化初始值为准。在 Security Config 中,它们位于 Project & Advanced 组的 Common Settings 下,分为 Keys & Storage、Build Checks (Preflight) 和 Logs & Diagnostics。
| 字段 | Type | 默认值 | 说明 |
|---|---|---|---|
| developerSecret | string | "" | 用于保护 OZeroSV_File 和 OZeroSafePlayerPrefs 数据的项目级 secret。请使用 Config Dashboard 中的 Generate Secure Secret 按钮生成,不要手动随意输入字符串。每个游戏应使用不同值,并避免出现在文档、日志或公开仓库中。发布后如果更改该值,新版本将无法解密旧版本保存的受保护数据。该值按用户保存在 UserSettings/OZeroSecuritySecrets.json 中,不纳入版本控制;构建本项目的所有机器都需要相同的值,请用密码管理器共享,或在构建机器上设置 OZERO_DEVELOPER_SECRET。 |
| enableLog | bool | true | 开启 Enable Debug Logs 会将 SDK 初始化、设置加载、许可证/遥测流程、检测事件等内部状态输出到 Unity Console 和 Player 日志中。这在开发/QA 阶段有助于查明原因,但可能暴露检测流程和模块状态,建议在生产构建中关闭。 |
| enableFailureDiagnostics | bool | false | 开启 Enable Failure Diagnostics 时,如果发生安全违规,本地诊断文件会保存到 Application.persistentDataPath。因文件内可能包含模块名、哈希、设备状态、安装包名以及部分运行时配置,建议仅在 QA 或客户服务支持时开启。按平台的默认保存位置:
|
developerSecret。该值按用户保存在 UserSettings/OZeroSecuritySecrets.json 中,不纳入版本控制,而构建本项目的所有机器都必须使用相同的值:请用密码管理器共享,或在构建机器上设置环境变量 OZERO_DEVELOPER_SECRET(设置后优先于已保存的值)。发布后不要更改。更改该值后,现有用户的 OZeroSV_File 文件将无法读取并返回 null(记录为密钥变更,不判定为篡改),OZeroSafePlayerPrefs 的值会回到默认值。除非游戏迁移或重置存档数据,否则用户将丢失存档。
Player Notice (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 > Managed Verification Text。
RuntimeNoticeReady 后适用。只显示安全的 OZ-SEC-* 代码,不暴露内部检测原文。
oz_boot.bin。缺失、格式错误、未签名、不匹配或旧 ABI 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 值会自动迁移。 |
| Security Config > Pro - License & Online Services > Online Verification UI > Ui Policy (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。 |
| Security Config > Pro - License & Online Services > Online Verification UI > Retry Timeout Seconds (Pro) | int | 15 | 用户点击 Retry 后,进入 retry-timeout 状态前等待的最长时间。用于避免玩家无限期停留在验证等待状态。 |
| Security Config > Pro - License & Online Services > Online Verification UI > Online Required Timeout Seconds (Pro) | int | 120 | 显示需要联网对话框后,在应用所配置 timeout action 前等待的最长时间。 |
| Security Config > Pro - License & Online Services > Online Verification UI > Timeout Action (Pro) | enum | BlockSession | 验证未能在限定时间内恢复时的处理方式:继续显示对话框、阻止受保护会话、终止应用,或仅调用 callback 以便自定义流程处理。 |
| Security Config > Pro - License & Online Services > Online Verification UI > Auto Retry When Network Restored (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 中打开 Edit > Project Settings > OZero Security(或 Window > OZero Security > Config & Dashboard),然后开启 Common Settings > Logs & Diagnostics > 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。 |
| macOS 构建在启动约 10 秒后以 BuildIntegrity (0x0A) 退出。 | 应用为 ad-hoc 签名、缺少 hardened runtime、带有 allow-dyld-environment-variables 或 get-task-allow、重新签名后未重新生成完整性文件,或外层 .app 使用了与内部代码不同的证书签名。 | 请按照 macOS:Developer ID 签名与公证 使用自动签名或脚本签名。Player 日志会显示原因(macos_unsafe_dyld_state、macos_debuggable_signature、macos_outer_signer_mismatch 或 aggregate hash mismatch),该节末尾的表格列出了各原因的解决方法。 |
| Windows Standalone 在干净 PC 上立即退出。 | OZero 原生插件可能未包含在构建输出中,或未能加载。 | 请确认 OZero 原生插件(OZeroSecurity64.dll 等)已包含在构建输出中,并查看 Player.log 中 DllNotFoundException 指向的文件。OZero 插件不需要 VC++ 运行时,但游戏中的其他插件可能需要。 |
构建因 OZeroSecurityConfig asset(s) found in the project 而停止。 | Assets/ 下仍有 SDK 1.1 或更早版本的 OZeroSecurityConfig 资源。它可能以明文形式发布,因此构建会停止。 | 打开 Window > OZero Security > Check Setup,在 Old OZeroSecurityConfig asset found 中点击 Import(也可以使用 Auto Setup)。设置值会移到 Project Settings,资源会被删除。参见自定义构建脚本。 |
构建因 OZeroLicenseConfig asset(s) found in the project 而停止。 | Assets/ 下仍有 SDK 1.1 或更早版本的 OZeroLicenseConfig 资源。它可能以明文形式发布,因此构建会停止。 | 打开 Window > OZero Security > Check Setup,在 Old OZeroLicenseConfig asset found 中点击 Import(也可以使用 Auto Setup)。许可证设置会移到 ProjectSettings/OZeroLicenseSettings.asset,资源会被删除。 |
增量构建(例如 Android)因 OZero could not confirm its startup sentinel in these scenes, which Unity reused from the build cache 消息而停止。 | Unity 未经处理就从构建缓存中复用了未更改的场景,而没有 OZero 曾在该场景内容中添加启动 sentinel 的记录(例如更新 SDK 后的第一次增量构建)。 | 请使用 Clean Build 构建一次(Build Settings > Clean Build,或 BuildOptions.CleanBuildCache)。OZero 会将处理过的场景记录在 Library/OZeroSecurity/StartupSentinelScenes.txt 中,之后的增量构建即可通过。如果消息为 could not add its startup sentinel to any built scene,请在构建中添加至少一个已启用的场景。 |
| 在编辑器中检测到后没有任何反应,或 Play Mode 自行停止。 | Editor Play Mode Protection 设置(Off 或 Test)。 | Off(默认)在编辑器中只记录检测日志。要执行已配置的响应,请改为 Test。在 Test 下,玩家中会导致退出的检测会在宽限时间后停止 Play Mode(日志 Editor Play Mode will stop in N ms)。参见编辑器 Play Mode。 |
| 正常 overlay 或录屏工具触发 Injection 事件。 | Injection/Hooking 正在观察已加载模块。 | 请先在 Failure Diagnostics 或 Pro 门户确认检测到的模块名、哈希和签名者。若是正常程序,Pro 客户在门户(注入 → 检测到的模块)中点击该行的允许,下次列表刷新时即对所有设备生效。Standard/Pro 客户将哈希加入 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 编辑器中也执行验证。由于 Editor Play Mode Protection 为 Off(默认)时编辑器中不启用检测器,此项仅在 Test 时生效。对测试有帮助,建议在常规开发期间关闭。 |
| 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 验证已编译程序集的哈希值(启动时及定期重新检查的运行时检查)。此选项不会关闭原生启动聚合检查:桌面播放器在读取配置之前总会验证已发布的二进制文件,因此程序集被篡改时仍会在启动阶段安全退出。该聚合包含哪些 IL2CPP 文件由 il2cppHash* 字段决定。 |
| 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。签名会改变执行文件的哈希,因此签名后必须重新生成完整性文件(参见 Windows / Linux:代码签名与完整性文件重新生成)。否则构建会以 Unexpected or modified native module 退出。 |
| blockVirtualMachine (Windows) | bool | false | 阻止游戏在虚拟机内部运行。仅限 Windows。 |
| blockHyperV (Windows) | bool | false | 拦截 Hyper-V VMBus 信号。该检查无法区分 Hyper-V 来宾与作为 Hyper-V 根分区运行的主机 PC,因此启用了 WSL2、Docker Desktop、Windows Sandbox、内核隔离(VBS)或 Hyper-V 角色的用户也会一并被阻止。公开发布时请保持关闭,仅在受控环境中使用。 |
| blockNetworkProxies (Windows) | bool | false | 检测疑似网络代理、数据包检测或流量分析工具的运行信号。用于竞技型构建,但请事先测试是否存在误报可能。 |
| blockReverseEngineeringTools (Windows) | bool | false | 检测疑似逆向工程或调试工具的运行信号。请在开发/QA 环境与线上环境分别进行验证。 |
| blockSystemMonitorTools (Windows) | bool | false | 检测疑似进程/系统监控工具的运行信号。请注意普通用户环境中可能存在的误报情况。 |
| il2cppHashGameAssembly | bool | true | 对 IL2CPP 桌面构建(Windows、macOS、Linux)的 GameAssembly 二进制文件(.dll/.dylib/.so)进行哈希验证。这是 IL2CPP 文件保护的最低建议项。 |
| il2cppHashGlobalGameManagers | bool | false | 将 globalgamemanagers 加入 IL2CPP 桌面文件哈希范围。代码默认关闭,但 Standard 和 Strict 预设会开启。自 SDK 1.2.0 起,在 Windows、macOS、Linux 上,无论此选项如何,globalgamemanagers 和 globalgamemanagers.assets 都会作为播放器启动文件始终参与哈希。 |
| il2cppHashSharedAssets | bool | false | 将 sharedassets* 文件加入 IL2CPP 桌面文件哈希范围。代码默认关闭,但 Standard 和 Strict 预设会开启。 |
| il2cppHashSceneFiles | bool | false | 将 level* 等 Unity 场景文件加入 IL2CPP 桌面文件哈希范围。代码默认关闭,但 Standard 和 Strict 预设会开启。 |
| il2cppHashResourcesAssets | bool | false | 将 resources.assets 添加到 IL2CPP 验证范围内。有助于 Strict 模式,但请先测试补丁流程。 |
| il2cppAdditionalWatchedFiles | List<string> | — | 当项目包含额外的原生 payload 时,以相对于 _Data 文件夹(macOS 为 Contents/Resources/Data)的路径指定需要受监控的 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)。启用了 checkPlatformNative 的发布构建在此列表为空时会被拒绝构建;只有在关闭 checkPlatformNative 或开发构建时才会跳过签名检查。在 Android 上,场景和资源文件(globalgamemanagers、sharedassets*、level*、resources.assets)不会被哈希,只有原生库和 IL2CPP 元数据会被哈希。因此,阻止篡改资源后重新打包的 APK 依赖于此签名检查,发布构建请务必设置。 使用 Google Play 应用签名时,构建由上传密钥签名,Play 为用户安装的 APK 会用应用签名密钥重新签名。请同时添加两个密钥的 SHA-256(Play Console › 设置 › 应用签名)。只添加上传密钥时构建可以通过,但所有从 Play 安装的用户都会签名检查失败。 |
| expectedBundleIds (iOS) | List<string> | — | 允许的 iOS Bundle ID 列表。留空则跳过 Bundle ID 检查。 |
| excludedAssemblies | List<string> | — | 要从哈希验证中排除的程序集名称列表(不带 .dll 扩展名)。用于在运行时发生改变的程序集(例如生成的代码)。 |
| Security Config > Build Integrity > Pro - Online > Check Integrity With Server (Pro) | bool | false | 开启 Pro 的 nonce → attest 流程。本地 Build Integrity 通过后,SDK 会向 OZero 提交构建完整性证据。与 attestationVerificationMode=OZeroManaged、optional OZero TMP dialog package 和内置 Managed Verification UI 一起使用时,面向用户的验证流程不需要游戏代码。预设值:Low 关闭,Standard 开启(OZeroManaged),Strict 开启(CustomerGameServer)。新建的 Online 设置以 Standard 预设的值开始,字段本身的默认值仍为关闭。开启后,服务器会判定构建,检测到注入或挂钩时拒绝验证,为游戏服务器签发 OZA 令牌,并在运行期间再次确认。关闭时,门户的设备和 Variant 封禁仍会生效;若 Build Integrity 已开启,已签名的构建与版本封禁列表也会在启动时生效。但新添加的构建或版本封禁要到下次启动才会生效。Pro 下此值关闭时,Check Setup 会显示 Info 项 Pro server attestation is off,但不会阻止构建。开启并使用默认 Built-In 确认 UI 时需要 TMP dialog 包,Auto Setup 会导入它。 |
| Security Config > Build Integrity > Pro - Online > Attestation Verification Mode (Pro) | enum | OZeroManaged | 选择由谁完成最终验证。默认值以及 Low、Standard 安全预设使用 OZeroManaged,由 SDK 向 OZero 请求验证结果。Strict 预设使用 CustomerGameServer:游戏服务器提供绑定信息,客户端调用 RequestGameServerAttestation(...),再由游戏服务器验证 OZA 令牌。此值仅在 checkIntegrityWithServer 开启时生效;Standard 和 Strict 预设会开启它,Low 预设会关闭它。若许可证等级不是 Pro,无论应用哪个预设都会保持 OZeroManaged。 |
| Security Config > Build Integrity > Pro - Online > Attestation Network Policy (Pro) | enum | RequireOnlineRevalidation | 目前只支持 RequireOnlineRevalidation。无法完成或刷新服务器验证时,SDK 会进入 OnlineRequired 状态,要求重新联网验证。不提供接受未验证离线会话的选项。 |
| manifestSigningPublicKey | string | "" | 用于验证已签名 Build Integrity manifest 的 Base64 RSA-2048 公钥。请在 Window > OZero Security > Config & Dashboard 中通过 Generate Key Pair 生成。它不同于 Online Services > Server Connection > Server Public Key Hex 中填写的客户门户服务器密钥。 |
| requireManifestSignature | bool | false* | Standard 和 Strict 预设会开启此项(Standard 自 SDK 1.2.0 起)。在发布版播放器构建中,manifest 签名验证会被强制开启,因此请在发布前生成密钥对。 |
| manifestSigningPrivateKeyPath | string | "" | 构建期间用于 manifest 签名的私钥 PEM 路径。留空时,OZero 使用 [ProjectRoot]/OZeroSigningKeys/manifest_private_key.pem。该路径仅用于 Editor,不会包含在 player build 中。这是按用户保存在 UserSettings/OZeroSecuritySecrets.json 中的值,不纳入版本控制;构建机器可以改用环境变量 OZERO_MANIFEST_SIGNING_PRIVATE_KEY_PATH。 |
平台说明(门户封禁):Android 构建不生成 manifest hash,因此构建哈希封禁不适用,只有版本封禁生效。WebGL 不会在本地检查已签名的封禁列表。版本封禁会用同一列表比较 SDK 版本和应用版本,且不区分平台,因此被封禁的版本对所有平台生效。
使用默认 UI 的 OZero 服务器 attestation
无代码流程:开启 Security Config > Build Integrity > Pro - Online > Check Integrity With Server,将 Attestation Verification Mode 设为OZeroManaged,先 import optional OZero TMP dialog package,并让 Security Config > Pro - License & Online Services > Online Verification UI > Ui Policy 保持使用内置对话框。SDK 会获取 nonce、提交 attest 证据、向 OZero 请求托管会话判定,并通过默认对话框显示需要联网、重试、timeout、警告或阻止状态。如果要替换默认 UI、不使用 TextMeshPro,或添加自有 telemetry/session gate,请注册 RegisterUserManagedVerificationStateCallback。
使用 CustomerGameServer 时,游戏服务器使用服务器 API 密钥调用 POST /v1/validate 检查每个令牌。完整流程和示例见 API 参考中的使用服务器 API 密钥集成游戏服务器。
💡 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) 可能仍在进行中。 |
| OZeroOnlineAttestation.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 签名密钥对。自 SDK 1.2.0 起,聚合哈希(oz_ahash)在所有预设下始终包含播放器启动文件:Windows、macOS、Linux(Mono 与 IL2CPP)为 RuntimeInitializeOnLoads.json、ScriptingAssemblies.json、globalgamemanagers 和 globalgamemanagers.assets;Android 在原生库和 global-metadata.dat 之外再加上 RuntimeInitializeOnLoads.json 和 ScriptingAssemblies.json。在桌面平台上,Compression Method 为 LZ4/LZ4HC 时,globalgamemanagers、场景和资源会被打包成 data.unity3d 一个文件;此时只要该文件存在,就会代替单独的文件始终参与哈希。即使文件较大,SHA-256 计算在启动时也大约每 GB 1 秒。
构建时,OZero 会使用私钥为 manifest 签名。私钥不会包含在 player build 中,构建中只包含存储在安全设置中的公钥。运行时,SDK 会用公钥确认 manifest 签名。如果签名有效,SDK 会认为“构建时生成的 manifest 仍保持原样”,并以该 manifest 作为 assembly/file hash 检查的可信基准。如果签名缺失或不匹配,则 manifest 可能已被替换或编辑,因此 Build Integrity 会将其视作失败。之后的处理会按照 Response Settings 执行,例如记录日志、触发回调或关闭应用。
生成密钥对
- 在 Unity 编辑器中打开 Window > OZero Security > Config & Dashboard。
- 在该页面中展开 Build Integrity 部分。
- 勾选 Require Manifest Signature 复选框。
- 点击 Generate Key Pair 按钮。
- OZero 会生成用于 Build Integrity manifest signing 的密钥对。公钥存储在安全设置(Project Settings)中,私钥 默认保存在以下路径(该路径本身按用户保存在 User Settings 中):
[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。
验证现有密钥对
如果要确认磁盘上的私钥是否与 安全设置中保存的公钥一致,请点击 Validate Key Pair。OZero 会从 PEM 重新推导公钥,并与 设置中的公钥比较。如果不匹配,请还原对应的私钥,或生成新的密钥对,然后进行 clean build。
- 当私钥丢失或泄露时。
- Validate Key Pair 报告不匹配(密钥未同步)时。
- 作为既定安全策略的一部分,有意轮换密钥时。
重新生成后,已经发布的旧构建仍会使用自身内置的密钥和 manifest 继续运行。下一次更新构建必须让新的公钥、fingerprint、manifest 和 signature 作为同一套匹配数据使用。请重新生成 integrity manifest,并通过 clean build 发布。
双指纹密钥轮换
点击 Generate Key Pair 按钮后,公钥会保存在安全设置中,同时识别该密钥的 fingerprint 也会记录在 Assets/OZeroSecurity/Scripts/Security/BuildIntegrity/OZeroManifestTrustAnchor.cs 中。运行时 SDK 会先确认 设置中的公钥是否匹配受信任的 fingerprint,然后才接受已签名的 manifest。
ExpectedPublicKeyFingerprintHex 是当前构建使用的密钥 fingerprint。PreviousPublicKeyFingerprintHex 是只在密钥轮换期间使用的辅助槽位。它不能替代 manifest 重新生成,也不是服务端密钥管理功能。安全设置中的公钥、fingerprint、manifest 和 signature 仍然必须基于同一把密钥组成一套匹配数据。
安全的密钥轮换步骤
- 备份当前指纹。 打开
Assets/OZeroSecurity/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、安全设置中的公钥、OZeroManifestTrustAnchor 中的 fingerprint、生成出的 manifest/signature 文件。
ExpectedPublicKeyFingerprintHex 可能为空。发布构建在缺少 fingerprint 时会为了安全直接失败。解决:执行 Generate Key Pair,等待 Unity 重新编译完成后制作 clean build。如果该值已经存在,请执行 Validate Key Pair,并清空构建输出文件夹后重新构建。
当 Project Settings > OZero Security > Build Integrity > Manifest Signing Public Key 为空时触发。解决:打开 Window > OZero Security > Config & Dashboard,在 Build Integrity 区域点击 Generate Key Pair,然后重新构建。
当 安全设置的公钥与 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 进行确认。
Install Source (Android Only) 设置
限制游戏仅在通过已批准的商店或途径安装时才能运行。这有助于防止侧载或重新打包的 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。仅在需要区域商店分发并完成真机测试后使用。 |
| Security Config > Install Source (Android Only) > Pro - Online > Check Installer With Server (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 Anti-Piracy (PC Only) 设置
对在 Steam 发售的 PC 端查明 Steam 调用通道、App ID、权限情况、发布档设定。Standard 是局端核实,要想通过 Steam 取得牢固权证唯有依赖 Pro 级别之内的 Steam Attestation 来供给。
OZERO_STEAMWORKS_SDK)。可选:Required DLC App Ids 仅在没有特定 DLC 就无法游玩时填写。
高级(大多数情况下留空):Validate Steam Api Dll Hash 和 Expected Steam Api Dll Sha256 Hashes 在所有预设中均为关闭,关闭时不会使用哈希列表。即使不填写哈希,构建时复制的
steam_api 哈希也会自动写入已签名的构建完整性信息,由 Build Integrity 检测替换;Require Valve Signed Steam Api(Standard 和 Strict 预设中开启)会在 Windows 和 macOS 上检查 Valve 签名。其余项目保持预设值即可。Security Config 中该部分顶部的提示框也会显示相同内容,将鼠标悬停在字段上可查看更改该值的效果。Steamworks SDK 文件夹与库的自动复制
在 Project Settings > OZero Security > Steam Anti-Piracy (PC Only) > SDK Folder(或 Check Setup 的 Steam 设置栏)中选择 Steamworks SDK 文件夹(sdk 文件夹或其上级文件夹)。构建时 OZero 会将目标平台对应的 Steam API 库自动复制到构建输出中,无需手动复制。
| 平台 | SDK 文件夹中的文件 | 复制到 |
|---|---|---|
| Windows x64 | redistributable_bin/win64/steam_api64.dll | steam_api64.dll (可执行文件旁), <Game>_Data/Plugins/x86_64/ |
| Windows x86 | redistributable_bin/steam_api.dll | steam_api.dll (可执行文件旁), <Game>_Data/Plugins/x86/ |
| Linux x64 | redistributable_bin/linux64/libsteam_api.so | libsteam_api.so (可执行文件旁), <Game>_Data/Plugins/x86_64/ |
| macOS | redistributable_bin/osx/libsteam_api.dylib | <Game>.app/Contents/MacOS/libsteam_api.dylib |
构建开始前,OZero 会检查库的文件格式、架构以及支持的 Steamworks 版本(SteamApps v008–v009, SteamUtils v009–v010, SteamUser v022–v023),不匹配时构建会停止。复制发生在写入完整性 manifest 之前,因此 Steam 库同样受完整性检查保护。为 macOS 构建签名时,libsteam_api.dylib 上的 Valve 签名会保持不变(macOS 签名)。
其他 PC 与 CI:所选文件夹只保存在该 PC 的 Editor 设置中,不会写入项目文件。在其他 PC 或 CI 上,请重新选择文件夹,或将环境变量 OZERO_STEAMWORKS_SDK 设为 SDK 文件夹路径。两者都没有时,构建会失败并显示说明消息。
仅限桌面:Steam Anti-Piracy 只在 Windows、macOS、Linux 播放器中运行。在开启状态下构建 Android、iOS 或 WebGL 时,构建只会留下警告并继续,该应用不会运行 Steam 检查,也不会包含 Steam 库。此时 Check Setup 会显示 Info 项 Steam Anti-Piracy is skipped for this build target。
与 Steamworks.NET 等封装库一起使用时:如果封装插件放入构建输出的 steam_api 库与所选 SDK 的不同,OZero 会用 SDK 中的库替换它,并在构建日志中留下警告。请将封装库构建时所用的同一 Steamworks SDK 版本指定为 SDK Folder。
| 字段 | 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 而非执行文件独立启航。需厘清研发内部自测操作与外发政策。直接启动并请求 Steam 重新启动游戏时,当前实例会静默退出,由 Steam 启动的实例继续运行。 |
| requireSteamApiInit | bool | true | 探察 Steam API 奠定起手成效。研发时期直接试玩因 Steamworks 配定生变有败局几率,决断还得以确实的 Steam 散播流为基准。 |
| requireSubscribedCurrentApp | bool | true | 核准当前挂号的 Steam 主能否掌握此件及赋予进入权限。此是机内考核,至于服端盘查必得用 Pro 解决。 |
| requiredDlcAppIds | List<int> | — | 必需 DLC 的 Steam App ID 列表(最多 10 个,超过则构建失败)。只要账户未拥有列表中的任一 DLC,就按与未购买本体相同等级的授权失败(required_dlc_missing)处理。判断依据是是否拥有(Steam BIsSubscribedApp),而不是是否已安装。Steamworks 合作伙伴(开发商)账户可能被报告为拥有自家 DLC,因此请用没有该 DLC 的普通账户测试未拥有的情况。用于解锁内容的可选 DLC 不要加入此列表,请用 QueryDlcOwnership 检查。 |
| blockSteamAppIdTxtInRelease | bool | true | steam_appid.txt 是仅用于不通过 Steam 进行本地启动测试的开发文件。如果它留在发布构建中,可能允许绕过 Steam 启动,因此 OZero 会将其视为违规。发布前请从项目根目录和构建输出中移除它。 |
| validateSteamApiDllHash | bool | false | 可选·高级,默认关闭(所有预设)。开启后会将 Steam API 文件哈希与 expectedSteamApiDllSha256Hashes 列表比较,只允许列表中的文件。列表为空时构建会失败,每次升级 Steamworks SDK 都需要更新列表。大多数游戏保持关闭即可。 |
| allowFamilySharing / allowFreeWeekend / allowTimedTrial | bool | true | 控制是否允许 Steam 家庭共享、免费周末以及限时试用权限状态。 注意:当前 SDK 的发布版本构建中不应用这些策略检查。保留已设置的值不会导致错误。 |
| requireSteamBuildIdNonZero | bool | false | 如果 Steam 返回的 Build ID 为 0,或无法取得 Build ID,则按违规处理。请先确认 Steam depot/build 发布流程能正确取得 Build ID 后再开启。 |
| expectedSteamApiDllSha256Hashes | List<string> | — | 可选·高级。仅在开启 validateSteamApiDllHash 时使用的 Steam API 文件允许 SHA-256 列表;关闭时(默认)不会使用。若开启,请填写所发布的每个 Steamworks SDK 版本和平台的哈希,并在每次升级 SDK 时更新列表。 |
| requireValveSignedSteamApi | bool | true | 确认 Steam API 再分发文件与 Valve 的平台身份一致:Windows 检查代码签名者,macOS 检查 Valve Team ID。在 Linux 等无法检查签名的平台上,此检查只记录警告并通过;此时 Build Integrity 仍会通过构建完整性信息中的 steam_api 哈希检测替换。若还要用 SHA-256 列表固定文件,可使用高级选项 validateSteamApiDllHash。 |
| 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 | OZeroPCStoreDetectionAction | Callback | 决定 Steam 验证失败时 SDK 在本地处理到什么程度。Observe 只记录日志和诊断,Callback 会调用游戏回调,Block 会把该事件作为阻止策略处理。应用是否真正退出仍由 Global Threat Response 设置决定。 |
| reportViolationToCallback | bool | true | 在 Callback 模式下 Steam 验证失败时,是否把事件发送到普通 OZero 安全回调。只有在 QA 构建中想保留日志但不触发游戏回调时才关闭。 |
| forceSteamAntiPiracyObserveOnly | bool | false | 临时将 Steam 验证失败只记录为日志和诊断信息,不触发回调或阻止行为。仅用于测试特殊 Steam 启动环境,发布前请重新关闭。 |
| Security Config > Steam Anti-Piracy (PC Only) > Pro - Online > Check Steam DRM With Server (Pro) | bool | false | Pro 专用。启用基于服务器证据的 Steam DRM 验证。需要用服务器证据确认 Steam 所有权,而不是只依赖本地 Steamworks 状态时使用。 |
| Security Config > Steam Anti-Piracy (PC Only) > Pro - Online > Steam Drm Verification Mode (Pro) | OZeroPCStoreDrmVerificationMode | 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 的结构。 |
| Security Config > Steam Anti-Piracy (PC Only) > Pro - Online > Steam Drm Network Policy (Pro) | OZeroPCStoreDrmNetworkPolicy | RequireOnlineRevalidation | 需要最新服务器再验证但网络或服务器不可用时,将进入需要联网状态。与 OZeroManaged 和已 import 的内置 UI package 一起使用时,会自动显示重试流程。 |
Steam 保护的执行级别
只有当 Detection Action 为 Callback 或 Block,且 Response > Force Quit On Detection 已开启时,Steam 违规才会导致游戏终止。该执行级别在构建时封存到 boot 元数据中,终止由原生代码直接执行。如果构建输出中的封存值与安全设置不一致,构建 preflight 会失败,请重新构建。
当设置为 Observe 或 Off、关闭 Force Quit On Detection、开启 forceSteamAntiPiracyObserveOnly、关闭 requireSteamApiInit 或使用 Low 预设时,违规只会被记录,游戏不会终止。这些配置也是允许的,但 Check Setup 的 Steam enforcement 项会显示警告,构建时控制台也会输出警告。若配置会导致终止,同一项会显示 “terminates on detection”。
在在线附加组件中,通过 Check Steam DRM With Server 由服务器判定 Steam 所有权的配置,将按服务器判定流程处理而不是原生终止,本地 Steam 违规会作为观察记录。
OZeroManaged 时,受保护的首次启动会通过 OZero 服务器在线取得有效 activation token。之后,只要 cache token 有效且离线宽限期仍然存在,就可以允许离线运行。如果令牌已过期、宽限期已结束,或需要重新验证时用户处于离线状态,import OZeroSecurity_BuiltInDialog_TMP.unitypackage 后的默认 Managed Verification UI 会自动处理需要联网提示、Retry 按钮、retry timeout 和阻止状态。
OZero 服务器托管的 Steam DRM
开启 Security Config > Steam Anti-Piracy (PC Only) > Pro - Online > Check Steam DRM With Server,将 Steam Drm Verification Mode 设置为 OZeroManaged,import OZeroSecurity_BuiltInDialog_TMP.unitypackage,并让 Security Config > Pro - License & Online Services > Online Verification UI > Ui Policy 保持使用内置对话框。SDK 会把 Steam auth ticket 发送到 OZero,管理 activation token cache,并通过默认 UI 显示需要联网、重试、timeout、警告或阻止状态。保留默认 UI 时,不需要编写 callback 或 retry 代码。如果 Pro 激活本身被拒绝(例如 Product Name 未登记),Steam 消息会以 Pro activation was not granted … Cause: <激活错误代码> 显示原因。请先解决该激活错误(参见许可证问题解决)。
客户游戏服务器 Steam 验证
开启 Security Config > Steam Anti-Piracy (PC Only) > Pro - Online > Check Steam DRM With Server,并将 Steam Drm Verification Mode 设置为 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 分钟有效,只能使用一次,并绑定到准确的许可证和设备。
在游戏中解除绑定
当玩家正当更换设备时,游戏可以通过 RequestReset 清除已保存的指纹。在玩家构建中,SDK 会询问通过 OZeroDeviceBindingDetector.SetResetAuthorizer 注册的授权回调,只有回调返回 true 时才会解除。解除后 SDK 会在同一会话中立即重新绑定当前设备,无需重启。
using OZeroSDK.Security;
// Register once at startup.
OZeroDeviceBindingDetector.SetResetAuthorizer(request =>
{
// Approve only after your own check succeeds, for example a recent
// account re-authentication or a support code your server verified.
return MyAccountService.HasVerifiedResetFor(request.Reason);
});
// Later, from your account recovery or support screen:
bool cleared = OZeroDeviceBindingDetector.Instance?.RequestReset("ticket-1234") ?? false;
true 的回调会让进程中的任何代码(包括注入的代码)都能清除绑定。请仅在完成实际验证(例如账号重新认证或经服务器确认的支持码)后返回 true。Demo_DeviceBinding 和 Demo_OZeroSecurity 示例仅为演示而批准所有请求,请勿将该回调直接用于游戏。
在玩家构建中,如果既没有授权回调也没有有效的服务器 Reset Token,解除会被拒绝并记录错误日志。在编辑器中始终会解除。RequestReset 和授权回调会在调用线程上同步执行。ClearStoredFingerprint(token) 仍可使用:配置了 Pro 许可证和服务器设置(服务器 URL、许可证密钥)时,非空令牌会在后台作为服务器 Reset Token 验证,因此解除会在调用返回之后发生;需要结果时,请在主线程上 await ClearStoredFingerprintAsync(token)(见下方流程)。其他情况下由授权回调判断,令牌通过 request.AuthorizationToken 传入。
Standard 游戏的客服处理指南
Standard 没有 OZero 服务器,因此无法使用门户的 Reset Token。请将游戏的客服确认流程直接接入授权回调(SetResetAuthorizer)。从下列方法中选择一种;无论哪种方法,都请按照上方警告,仅在确认通过后让回调返回 true。
A. 游戏有自己的服务器(账号系统)— 推荐
- 客服在后台为玩家账号签发一次性支持码。
- 玩家在游戏界面中输入该代码。
- 游戏先在自己的服务器上异步验证代码。
- 验证通过后,游戏调用
RequestReset,回调只检查刚才的验证是否通过。
玩家重新登录(平台重新认证)后允许解除的方式也是同样的结构。授权回调同步执行,因此请先完成异步验证,再调用 RequestReset。
bool _supportCodeVerified;
void Start()
{
// The authorizer runs synchronously: it only reports whether the check just passed.
OZeroDeviceBindingDetector.SetResetAuthorizer(_ => _supportCodeVerified);
}
async void OnSubmitSupportCode(string code)
{
// Verify the one-time code issued by your support team on your own backend.
_supportCodeVerified = await MyBackend.VerifyResetCodeAsync(accountId, code);
try
{
bool cleared = OZeroDeviceBindingDetector.Instance.RequestReset("cs-support");
ShowResetResult(cleared);
}
finally
{
_supportCodeVerified = false;
}
}
B. 没有服务器的离线游戏 — 签名码方式
- 游戏显示请求码,例如将一次性随机数和游戏自身的存档或玩家 ID 简短编码后的值。
- 客服返回用只有开发商持有的私钥签名的响应码。
- 游戏用内置的公钥验证响应,只有签名有效时回调才允许解除。请丢弃用过的随机数,防止同一响应被再次使用。
GetCurrentFingerprintHash();它是调试辅助方法,不应在正式代码中显示或发送。
// Pseudocode. Game: build a request code that the player sends to support.
nonce = RandomBytes(8) // keep until the reset is done, then discard
requestCode = ShortEncode(nonce + yourSaveOrPlayerId)
// Support tool on your side. The private key never ships with the game.
responseCode = Sign(studioPrivateKey, requestCode) // Ed25519 or ECDSA
// Game: verify with the public key built into the game, then reset.
OZeroDeviceBindingDetector.SetResetAuthorizer(_ => _signedCodeVerified)
_signedCodeVerified = Verify(embeddedPublicKey, requestCode, responseCode)
OZeroDeviceBindingDetector.Instance.RequestReset("cs-offline")
_signedCodeVerified = false
Discard(nonce)
C. 清除应用数据或重新安装
绑定记录保存在应用的本地存储(PlayerPrefs)中。玩家清除应用数据或重新安装应用后,下次启动时会作为新设备注册。本地存档也可能丢失,因此仅在有服务器或云存档的游戏中将此作为客服答复。
D. 减少咨询
将 hardwareChangeTolerance 设为 1 或以上(Standard 预设为 1,Strict 为 0)时,即使因系统更新或隐私设置变更导致某一个识别值变化,也不会拦截,而是重新注册设备。
Pro:请使用门户 Reset Token 和 ClearStoredFingerprintAsync(token)。参见下方的 Reset Token 支持流程。
Reset Token 支持流程(Pro)
- 先通过正常客服流程确认玩家账号,以及为什么需要重置设备。
- 进入 Customer Portal → Device Binding,选择目标设备并发放 Reset Token。
- 令牌只能通过已认证的支持渠道发送。不要留在公开聊天、截图或长期保存的工单中。
- 游戏或支持界面应在主线程上通过
await OZeroDeviceBindingDetector.Instance.ClearStoredFingerprintAsync(token.Trim())传入令牌,并检查返回的bool。ClearStoredFingerprint(token.Trim())也可使用,但服务器检查在后台进行,解除会在调用返回之后发生。 - SDK 接受令牌后,会在同一会话中立即重新绑定当前设备,无需重启。
| 字段 | Type | 默认值 | 说明 |
|---|---|---|---|
| Activate Device Binding | bool | true | 开启 Device Binding 检查。开启后,SDK 会在启动时创建本地设备指纹,并与已保存的值比较。 如果存在已保存的指纹项但无法解密(被篡改),将按 Device Binding 违规(StorageTampered)处理。如果没有该项(例如清除了应用数据),则会重新注册。 |
| hardwareChangeTolerance | int (0–3) | 1 | 决定设备指纹中的多少项发生变化时仍视为同一设备。0 表示严格,3 对较大的硬件/OS 变化更宽容。默认值 1 用于吸收较小的 OS 或固件变化。 |
| storageKey | string | "ozero_dfp" | SDK 用来保存加密本地设备指纹的 PlayerPrefs 键。只有当游戏已经使用相同键时才需要修改。 |
| Security Config > Device Binding > Pro - Online > Verify Device With Server (Pro) | bool | false | 仅限 Pro。首次使用时通过 /v1/device/register 注册设备指纹,后续启动时通过 /v1/device/verify 与服务器记录比较。网络或服务器故障本身不会阻止游戏,但服务器明确返回 DEVICE_BLOCKED、FINGERPRINT_MISMATCH 等拒绝时,会作为 Device Binding 违规处理。 |
Speed & Time Hack 设置
对比 Unity 时间、原生基准时钟、后台计时器以及 Web/Pro 服务器时间,用来检测加速或减速游戏的操控,以及设备时钟变化。WebGL 中无法使用原生时钟对比:启动日志会说明这一点,浏览器时钟篡改(Date.now / performance.now)改由 Injection & Hooking 的 WebGL 运行时检查检测。
| 字段 | 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/服务器时间重新比较的周期。设置过短会增加网络请求。 |
| webSyncIdleInterval | float | 60 s | 近期没有时间异常时的 Web 时间比较周期。在本地时间异常或 Web 时间检查失败后的 5 分钟内改用 webSyncInterval。设为 0 时始终使用 webSyncInterval。Strict 预设为 15 秒。 |
| webRatioTolerance | float | 0.15 | 设备时间与 Web/服务器时间之间允许的正常差异比例。请结合地区网络延迟在真机上验证。 |
| timeOffsetTolerance | float | 60 s | 运行期间设备时钟回拨超过此秒数,或与网络/服务器时间的偏差相对首次测量变化达到此值时,视为时间篡改。启动时就已不准的设备时钟不视为篡改。判定基准以正在运行的进程为单位,因此在更改时钟会导致应用重启的移动平台(例如 iOS 设置应用)或 -batchmode 无界面播放器中,本地回拨判定不会生效。基于时间的奖励请以服务器时间(OZeroTime 签名时间)为准处理。 |
| 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 地址列表。建议加入两个以上由您运营或信任的地址,避免一个地址被阻断后检查就停止。 Standard 和 Strict 预设会填入第三方主机 https://www.apple.com/ 和 https://www.bing.com/(不是 OZero 服务器)。 |
| minSuccessfulEndpoints | int | 2 | 一次检查中,至少有多少个地址返回有效 Date 标头后才信任该时间。不要设置得比已配置地址数量更大。 |
| maxConsecutiveFailures | int | 6 | Web/服务器时间检查连续失败达到此次数后,执行 onWebTimeUnavailable 策略。它用于给临时网络故障留出缓冲,不会马上当作安全违规。 |
| onWebTimeUnavailable | WebTimeUnavailablePolicy | WarnOnly | 多次无法确认 Web/服务器时间时的处理策略。WarnOnly 只记录警告并继续运行。Strict 会把反复失败视为可疑情况并触发 SpeedHack 回调。Silent 不记录日志,除特殊测试外不要使用。 |
| 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 倍后再触发回调。 |
| Security Config > Speed & Time Hack > Pro - Online > Enable Remote Speed Hack Config (Pro) | bool | true | 从 Pro 服务器获取 Speed & Time Hack 设置。新设置默认为 true;安全预设仅在许可证等级为 Pro 时开启,在 Standard 中关闭。只有在门户中设置了值的项目才会覆盖本地值,这不仅包括阈值,也包括 detectSlowHack、enableTimeScaleDetection、enableThreadTimerCheck、enableSignedServerTime 等检测开关。门户中留空的项目保持本地值。关闭此选项或无法连接服务器时,仅使用本地设置。 |
| remoteSpeedHackConfigInterval | float | 300 s | 从 Pro 服务器重新获取 Speed & Time Hack 远程设置的周期。0 表示只在应用启动时获取一次,正数表示定期检查新策略。 |
| remoteSpeedHackConfigJitterPercent | float | 20% | 让多个设备不要同时请求 Pro 远程设置,而是把设置刷新时间稍微分散开。 |
| Security Config > Speed & Time Hack > Pro - Online > Enable Signed Server Time (Pro) | bool | false | Pro 激活可用时,优先使用签名的 /v1/time 响应作为可信时间源。请在服务器密钥和时间端点配置并测试完成后再开启。 |
useWebTimeValidation)后,SDK 会向 webTimeUrls 中的地址发送 HTTPS HEAD 请求,仅读取 Date 头用于时间比较。Standard 和 Strict 预设会填入第三方主机 https://www.apple.com/ 和 https://www.bing.com/。这些不是 OZero 服务器,OZero 不会在请求中加入个人数据。如果游戏不得连接其他主机,请换成你自己运营的地址,或关闭网络时间校验。
使用 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 远程配置下载和遥测数据发送。速度限制、距离容差、穿墙检查和加速度检查都在各自的 OZeroPhysicsHackDetector 组件上配置。请把组件添加到玩家预制体,根据角色或载具的真实移动规则调整数值,并在生成后调用 Initialize(playerId)。| 字段 | Type | 默认值 | 说明 |
|---|---|---|---|
| physicsHack.useGlobalPhysicsHack | bool | true | 所有 Physics Hack 组件的全局开关。只有在确实想停止全部物理移动检查时才关闭。 |
| Security Config > Pro - License & Online Services > Telemetry > Enable Remote Physics Config (Pro) | bool | true | Pro 在应用启动时获取全局 PhysicsHack 设置。新设置默认为 true;安全预设仅在许可证等级为 Pro 时开启,在 Standard 中关闭。只有在门户中设置的项目(useGlobalPhysicsHack、telemetryThrottlePerMinute)会覆盖本地值,且服务器只能关闭遥测,不能开启。关闭或无法连接服务器时,使用本地设置。每个角色的移动阈值需另行设置。代码中可通过只读属性 EnableRemotePhysicsConfig 查看当前值。 |
| Security Config > Pro - License & Online Services > Telemetry > Enable Server Telemetry (Pro) | bool | true | 仅限 Pro,默认开启。除非项目将其关闭,否则会发送一般安全事件与详细 PhysicsHack telemetry。关闭后将停止发送新的 telemetry;服务器策略可以关闭传输,但在本地已关闭时不能启用传输。 |
| Security Config > Pro - License & Online Services > Telemetry > Telemetry Throttle Per Minute (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 检查;开发构建会使用更偏诊断的宽松流程;在编辑器 Play Mode 中,仅当 Editor Play Mode Protection 为 Test 时才检查。 |
| injectionWhitelistEntries | OZeroInjectionWhitelistEntry[] | empty | 所有 tier 都可以使用的本地可信模块列表。每个条目通过模块文件的 SHA-256 hash 和可选 signer fingerprint,允许一个特定文件。只添加随游戏一起分发,或通过 QA、诊断文件、Pro telemetry、OZero 支持团队确认过的模块。 |
| Security Config > Injection & Hooking > Pro - Online > Enable Server Whitelist (Pro) | bool | false | Pro 获取已签名的模块允许与禁止列表。只需使用 enableServerWhitelist 开关,允许列表和禁止列表的有效期会分别管理。允许的模块按静默方式处理,明确禁止的模块按拦截策略处理。只缓存已验证的列表,离线时仅使用仍在有效期内的判定。默认值为 false。 |
| Security Config > Injection & Hooking > Pro - Online > Enable Injection Scoped Exact (Pro) | bool | false | 不再下载完整列表,而是按哈希逐个向服务器查询观察到的模块。尚未提供:OZero 服务器尚未开启此查询功能,开启后不会生效,SDK 会继续使用常规服务器白名单。请保持关闭。 |
| Security Config > Injection & Hooking > Pro - Online > Enable Injection Bloom Advisory (Pro) | bool | false | 使用已签名的全局模块索引确定查询优先级(仅供参考)。尚未提供:全局索引根密钥尚未公开,与 enableInjectionScopedExact 同时开启会以 root_unconfigured 导致构建失败。请保持关闭。 |
| serverWhitelistRefreshInterval | float | 0 s | 应用运行期间定期获取服务器列表的间隔。0 仅停止定期轮询,启动时获取和到期前的自动更新仍会执行。 |
| requireSignerForNativeWhitelist | bool | false | 要求 Windows PE 和 Apple Mach-O whitelist 条目除了文件 hash 外,还必须包含 signer fingerprint。这样更强,但只有在能收集准确 signer 值时才建议开启。Android/Linux 共享库通常按 hash-only 处理。 |
| Security Config > Injection & Hooking > Pro - Online > Enable Remote Injection Config (Pro) | bool | true | 从 Pro 服务器获取 Injection 设置。新设置默认为 true;安全预设仅在许可证等级为 Pro 时开启,在 Standard 中关闭。只覆盖服务器上明确设置的项目;关闭后仅使用本地设置。服务器白名单的获取由 enableServerWhitelist 单独控制。 |
| remoteInjectionConfigInterval | float | 300 s | 重新检查 Pro 远程 Injection 设置的周期。0 表示只在应用启动时获取一次。 |
| enableWindowsModuleIdentityScan | bool | true | 在所有原生平台上,通过文件哈希识别启动后出现的每个模块,Windows/macOS 还使用签名者指纹(字段名沿用旧名 Windows)。iOS/macOS 将应用包内的映像视为游戏自身代码,Android/Linux 通过哈希识别共享库。参见检测器如何识别模块。 在 macOS 上,由与应用不同的签名者签名的包内镜像会被视为注入候选。 |
| enableWindowsSystemModuleScan | bool | true | 检查从 Windows 的 System32、SysWOW64 和 WinSxS 目录加载到游戏中的模块。启用后,不会仅因为模块或挂钩目标位于系统目录就信任它。Low: false;Standard/Strict: true。模块识别还需要启用 enableWindowsModuleIdentityScan。内联挂钩扫描由单独的选项控制。 |
| scanIntervalSeconds | float | 2 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 检查的开关。 |
发现不熟悉的系统模块时,不会立即关闭游戏,而是先记录以供查看。重复出现或文件没有嵌入式签名,都不会单独触发拦截。现有允许规则仍然有效;通过文件哈希明确禁止的模块仍会按策略拦截。请在 Unity 的 Injection 设置中调整此选项,Pro 门户不支持远程修改。此选项不影响其他平台。
检测器如何识别模块
检测器每隔几秒遍历游戏进程中加载的全部模块,并与启动时的集合比较。新模块通过文件哈希识别,在 Windows 和 macOS 上还通过签名证书指纹识别。规则只看模块本身而不看安装目录名称,因此正常程序不会因安装路径而被误判。
| 平台 | 视为游戏自身代码的标准 |
|---|---|
| Windows | 游戏自身的 native 模块通过已签名的构建完整性哈希列表确认,不会仅凭所在目录获得信任。enableWindowsSystemModuleScan=false 时,System32、SysWOW64、WinSxS 中的模块不参与身份检查。启用后,即使系统模块在游戏启动时就已加载,也会进行检查。不熟悉的系统模块会先记录以供查看;通过文件哈希明确禁止的模块仍会按策略拦截。现有哈希和签名者允许规则,以及已验证厂商模块的静默处理保持不变。游戏目录中不在签名列表内的 native 文件会在启动时视为 Build Integrity 违规。 |
| iOS / macOS | 属于应用自身 .app 包的映像以及 Apple 平台映像。构建时无需生成任何产物。需要 iOS 14.0 或更高,低于该版本时 Editor preflight 会失败。 但在 macOS 上,只有由与应用相同签名者(GameAssembly.dylib・UnityPlayer.dylib 的签名)签名的包内镜像才被视为游戏代码。ad-hoc、未签名或由其他签名者签名的镜像,即使位于包内也按注入候选(bundle_signer_mismatch)处理;Valve 签名的 libsteam_api.dylib 除外。应用本身为 ad-hoc 签名时不进行比较。 |
| Android / Linux | 启动时通过与运行时相同规则的共享库、系统和引擎运行时库、Steam 覆盖层,以及应用自身的库。Linux 上通过构建签名的清单识别(不看目录位置),Android 上通过只有操作系统能写入的已安装包目录识别应用自身的库。Android 系统 WebView 提供方也视为平台组件。不属于任何文件的可执行内存也在同一次扫描中上报,但只做累积判定,不会单独终止。Linux 宿主层和 Proton 遵循上文 Build Integrity 策略(默认 Monitor)。 |
每次检查按检测族返回三种结果之一:已检测、扫描后正常或尚未到检查时间。只有真实样本才会改变计数;未执行的检查不会清除先前的检测,完全无法执行的检查会作为扫描器健康问题上报,而不是正常结果。
确认检测后的行为。开启 Force Quit On Detection(Standard、Strict 预设的默认值)时,确认的注入模块会在回调宽限时间结束后关闭游戏,即使 OZeroSecurityReceiver 或回调只记录日志也是如此;在 Windows 上由原生模块监视直接关闭。若只想在游戏代码中处理注入,请关闭 Force Quit On Detection(Low 预设)并在 Receiver 中响应。
Pro 构建。向 Pro 构建注入未签名模块后,许可证服务器会拒绝下一次在线检查(约每 4 分钟一次)。该拒绝属于 Build Integrity 失败,无论 Force Quit On Detection 或回调如何设置,都会关闭游戏。若模块合法,请在门户中允许。
Pro:在门户中允许或阻止检测到的模块
启用 enableServerWhitelist 后,Pro 构建会获取一份签名列表,合并项目在门户中的判定与 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 数据加密密钥的基准值。若发布后更改此值,新版本将无法读取旧版本保存的受保护数据。OZeroSV_File 会检测到密钥变更,返回 null 并触发 OZeroSV_File.OnKeyMismatch 事件(不作为安全违规处理);OZeroSafePlayerPrefs 返回默认值。要保留用户数据,游戏需要自行迁移或重置。
4
游戏内变量保护(Secure Types)
Secure Types 会将常规 C# 变量类型替换为加密类型。因为值存储在 Native C++ 堆中,内存扫描器和内存编辑器即使扫描内存也无法找到。只需更改类型名称,其余代码即可照常运行。
示例代码
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 / Environment tamper | The notice label tells which one. Build integrity: check whether the build, manifest, signing information, native variant, or packaged files changed after the release build was prepared. Environment tamper: a debugger, a cheat or reverse-engineering tool process, a VM or emulator, or root/jailbreak was detected; check what was running on the device. |
| 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 / Environment tamper | Build integrity validation failed, or a build integrity runtime-environment check (debugger, cheat or reverse-engineering tool process, VM or emulator, root/jailbreak) detected a finding. |
| 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. |
| 0x0BADB007 | 启动检查 | Boot-ACK 超时:托管层 OZero 引导程序未在规定时间内确认启动完成。 |
| 0x0BADB008 | 启动检查 | 加载锚点超时:OZero 原生库已加载,但 OZero 引导程序未在 90 秒内启动。构建中的 OZero 启动项很可能已被删除或修改。 |
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. |