OZeroSDK Manual - Unity Native C++ Security Setup Guide
OZero Security
文档

入门

本指南将逐步引导您从安装 OZero Security 到应用您的第一个安全功能。无需安全专业知识,约 3 分钟即可完成基本设置。

约3分钟 · Unity 2020.3 LTS+ · iOS 12.0+ / Android API 21+

概述

OZero Security 通过在 Native C++ 层运行安全逻辑来保护 Unity 游戏,而普通黑客工具很难访问该层。只需从 Unity 编辑器中的仪表板激活模块即可激活核心保护功能。无需设置场景或编写单独的代码。

内置保护功能:
  • 构建完整性检查(应用程序篡改检测)
  • 速度黑客和时间黑客检测
  • 内存注入监控
  • 加密的游戏内变量类型(安全类型)
  • 加密的保存文件和玩家偏好设置
免接线自动引导与配置加密
  • 激活的检测器会在 SDK 启动时自动启动 - 无需场景部署或样板调用。
  • 安全设置在构建过程中受到保护,因此纯文本设置不会暴露给一般玩家分发。
  • 本机 C++ 运行时防护在托管 Unity 代码之外提供了额外的验证层。
  • 威胁响应通过 SDK 策略和 Pro 遥测进行集中处理,使现场调查变得容易,而无需暴露实施细节。

1 包导入

打开 Unity 编辑器并导入 OZero Security 包。您可以通过 Unity 包管理器或双击 .unitypackage 文件导入它。

当出现 Import Unity Package 对话框时,单击 Import 并选中所有项目。必要的脚本、本机插件和编辑器工具会自动添加。

Import Unity Package dialog — replace with actual screenshot
导入后,Unity 可能需要一些时间来编译脚本。等待右下进度条消失,继续下一步。

2 打开仪表板

导入完成后,从 Unity 菜单栏打开 OZero Security Dashboard:

OZero Security → Security → Config & Dashboard
Unity menu bar showing OZero Security → Security → Config & Dashboard — replace with actual screenshot

将打开自定义编辑器窗口。这是控制所有 OZero 安全模块的中央面板。 Hairaki 项目不需要被触及。

3 模块激活

在仪表板中,您可以看到安全模块和切换开关的列表。激活您想要使用的模块。以下是推荐的启动配置。

OZero Security Dashboard showing module toggles — replace with actual screenshot

选择安全预设

首先选择预设,然后调整各个模块以适合您的项目。对于一般的实时游戏,我们建议从 Standard 开始。保护、性能和误报可能性的最佳平衡。

预设 推荐用途 适用政策摘要
Low 原型、开发构建、初始 QA 仅保留轻度核心检查。放宽平台原生检查和杀掉策略,防止测试环境过早被封锁。
Standard 大多数启动游戏的推荐默认值 打开核心保护集、启动时验证、运行时重新验证、模拟器检查以及推荐的 IL2CPP 文件覆盖率。这是一种平衡兼容性和保护级别的配置。
Strict 高风险的实时服务、PvP、竞争性构建 应用最广泛的覆盖范围,并将更多的失败视为致命违规行为。请在申请前充分测试平台、签名、店铺分发流程。
模块 功能说明 推荐还是不推荐?
Build Integrity Validator 检测应用程序二进制篡改 受到推崇的
Speed Hack Detector 时间操纵作弊检测 受到推崇的
Injection Detector 内存挂钩工具监控 受到推崇的
Install Source Validator 阻止非法 APK(仅限 Android) 选择

无需按单独的“自动设置”按钮。只需在仪表板上检查要使用的模块并保存 OZeroSecurityConfig 资产即可。当玩家运行时,SDK 在开始游戏之前自动准备活动检测器。

没有单独的 自动设置 按钮 — 模块激活是完全自动的。切换所需的模块并保存 OZeroSecurityConfig 资源,所有活动模块将在下次运行时自动创建。无需场景放置或按钮单击。

许可模式 — 标准版/增强版/专业版

OZero Security 分为三个级别:标准版、增强版和专业版。标准是使用常见本机模块的完全无服务器配置。此外,还添加了特定于应用程序的本机变体包和清单/捆绑包 ID 绑定,而无需运行时服务器。 Pro 包括 Plus 配置并支持云遥测、签名时间、远程策略、服务器验证和设备限制等操作功能。

下面的矩阵从开发人员的角度总结了 SDK 在运行时如何按层实际运行。如需完整功能比较,请查看首页许可模式对照表
物品 标准 专业版
许可证密钥 -(不存在) OZ-PLS-XXXX ×6 OZ-PRO-XXXX ×6
启动时网络 完全没有——完全离线 不需要运行时服务器 - 只需从门户下载变体 每个设备在 POST /v1/activate 之后缓存一次
10个保护模块 激活所有 10 个保护模块 共10个(本地保护模块与标准相同) 全部 10 个(与标准相同)
原生变体 公共本机模块 应用程序特定变体 + 清单绑定 包括
云遥测 关闭(安静无动作) 关闭(无服务器) 开启 — 将威胁事件发送至 /v1/telemetry
签名时间(防止时钟篡改) 关闭 — WebTime 仅使用 HTTPS HEAD 关闭 — 与标准相同 开启 — 使用签名的 /v1/time 响应。
每个设备限制 无限制(无钥匙,无强迫) 项目特定许可证,无运行时设备限制 基本5个单位/可调节
源代码访问 仅托管 C# 仅托管 C# 仅托管 C#
如果您今天以标准版启动并稍后升级到 Plus 或 Pro,您将保留您的游戏代码。 Plus 添加了 Variant 清单和本机包,Pro 添加了基于 OZeroLicenseConfig 的服务器功能。

注册 Plus/Pro 许可证密钥

标准层不需要任何单独的设置。 Plus 或 Pro 添加一项 ScriptableObject 资源并粘贴您购买后收到的密钥。 Plus 密钥用于项目特定的变体验证和门户下载,Pro 密钥还用于运行时服务器激活。

1. 创建设置资产

打开 OZero 安全配置 & 仪表板(菜单:OZero Security → Security → Config & Dashboard),然后单击 License & Server 部分中的 Create OZeroLicenseConfig 按钮。资源将自动创建在正确的 Resources/ 文件夹中,因此您无需手动移动它们。

或者,您可以在“项目”窗口中手动将其创建为 Assets → Create → OZero → License Config。但是,您必须手动检查资产是否位于 Resources/ 文件夹下,并且名称是否准确为 OZeroLicenseConfig(区分大小写)。

2. 填写检查员字段

场地 必需的? 解释
tier 所有级别 从下拉列表中选择 StandardPlusProStandard 使用通用本机模块。 Plus 需要绑定到项目的变体清单。 Pro 包括 Plus 并启用服务器功能。
licenseKey 增强版/专业版 这是您通过电子邮件收到的密钥。 Plus 键使用格式 OZ-PLS-...,Pro 键使用格式 OZ-PRO-...。如果留空,SDK 将作为标准/无服务器运行,即使级别是 Plus 或 Pro。
appIdentifier 自动转移 激活后,SDK 会将 Unity 的 Application.identifier 传输到 appIdentifier。服务器将此值与许可证中注册的捆绑包/包 ID 进行比较,因此播放器设置中的标识符必须与颁发的密钥匹配。
serverBaseUrl 增强版/专业版 默认 https://api.ozero.security — 保持原样,除非 OZero 支持在单独的端点上指示您。在发布版本中,它必须是 HTTPS,并且尾部斜杠会自动删除。
serverPublicKeyHex 增强版/专业版 OZero 许可证服务器的 32 字节数字签名公钥(64 个十六进制字符)。由 SDK 用于验证激活令牌的签名(第 1.5 阶段/N-1 强化)。如果留空,签名验证将被静默跳过——仅允许过渡版本,生产应始终填充此值。请检查您的购买电子邮件以获取确切的十六进制字符串。
tokenTtlSeconds 选择 离线时信任缓存的激活结果的秒数。默认 604800(7 天)。过期后,SDK 将继续运行,但仅服务器功能(遥测/签名时间)将被关闭,直到下次成功激活。
offlineProPolicyMode 增强版/专业版 确定设备离线时如何强制执行签名的 Pro Portal 阻止策略。建议值为 ApplyCachedBlockPolicies。仅对纯在线游戏使用 RequireFreshPolicy,当政策过时时应拒绝这些游戏。 IgnoreCachedBlockPolicies 是较旧的兼容模式,不建议用于实时构建。
activationTimeoutSeconds 选择 /v1/activate 等待响应的最大秒数。如果超过此时间,SDK 将回退到缓存的权利。默认 6.0。 activate 调用永远不会阻止场景加载 - 该值仅确定后台 Task 在记录超时之前等待的时间。
enableLog 选择 如果为 true,许可证流事件将通过 OZeroSecLog 记录(缓存命中/激活成功/超时/数字签名不匹配)。默认为真;关闭以在启动时静音日志。

3. 构建和验证

无需更改代码。应用启动时,OZeroLicenseBootstrap 自动链接到 [RuntimeInitializeOnLoadMethod(AfterAssembliesLoaded)] 以读取资源。第一次运行时,检查播放器日志中是否有类似 [OZeroLicense] activated; tier=pro caps=5 的行。如果您有 Pro 设置并看到 [OZeroLicense] Standard / serverless mode.,请仔细检查该资源是否位于 Resources/ 文件夹下并命名为 OZeroLicenseConfig(无扩展名/重命名后缀)。

您的许可证密钥不是秘密的 - 即使它被泄露,所需要的只是有人耗尽服务器强制执行的每台设备的限制。因此,资产包含在构建中并且(故意)未加密。将您的密钥视为产品代码,而不是密码。

服务器激活流程

当您启动应用程序时,激活会在后台进行。 SDK 永远不会阻止场景加载 - 即使发生 6 秒超时,它也会使用缓存的权利(或标准回退),因此播放器永远不会在黑屏上等待 HTTP。

启动顺序

  1. 当应用程序启动时,SDK 会自动初始化许可证运行时。
  2. Standard 和 Plus 继续运行,无需任何运行时服务器调用。
  3. Pro 在后台发送轻量级激活请求。
  4. 成功激活后,遥测、签名时间和证明等 Pro Server 功能将可用。
  5. 如果发生激活失败、过期或超时,它将继续以标准模式运行,而不向玩家显示任何错误。

数据在网络上流动

激活请求是小型 JSON POST。必填字段为 licenseKeydeviceIdsdkVersionplatform。仅当 Unity 提供非空值时,才会添加 appIdentifiercompanyNameproductNamewebglOrigin。设备标识符源可以用 OZeroLicenseRuntime.DeviceIdProvider 覆盖。

POST https://api.ozero.security/v1/activate
Content-Type: application/json

{
  "licenseKey":    "OZ-PRO-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX",
  "deviceId":      "<DeviceIdProvider result; default SystemInfo.deviceUniqueIdentifier>",
  "sdkVersion":    "<OZeroSdkVersion.ManagedVersion>",
  "platform":      "<android | ios | windows | macos | linux | webgl | ...>",
  "appIdentifier": "<Application.identifier>",
  "companyName":   "<Application.companyName>",
  "productName":   "<Application.productName>",
  "webglOrigin":   "<WebGL origin only>"
}

服务器响应 JSON,其中包含 JWT 格式的 signedTokenheader.payload.signature,base64url,无填充)、确定的 tiercapabilities 数组、serverFeaturesEnabled 标志和(可选)expiresAt unix-ms。 SDK 仅在通过使用 serverPublicKeyHex 调用 OZerodigital signature.VerifySignature 来验证 signedToken 后才接受权利 - 其他所有内容均会失败关闭。

优雅降级

服务器维护、网络超时、许可证过期/暂停/撤销以及捆绑包不匹配不被视为操纵。此状态将仅限专业版的功能降级为标准/无服务器模式,以保留玩家会话。相反,已确认的操作(例如构建不匹配、阻止构建、注入和调试器)遵循既定的威胁响应策略。

如果您想验证实时激活是否确实成功(而不仅仅是信任缓存),请在您自己的引导程序中调用 await OZeroLicenseRuntime.Initialize(); 并查看 OZeroLicenseRuntime.Entitlement?.cachedAtMillis。如果该值在最近几秒内,则服务器刚刚响应;如果较旧,则服务器在缓存基础上运行。

离线操作

OZero 的设计目的是让玩家无论网络状况如何都能始终玩游戏。确切的行为因层而异:

标准 — 始终离线

服务器永远不会被调用。所有 10 个检测器模块均可正常运行且性能不会下降。许可的运行时设置为 IsServerless = trueHasEntitlement = falseOZeroTelemetryReporter 附加但对所有事件均无操作。

Pro — TTL 内可离线使用

在给定设备上首次成功执行 /v1/activate 后,权利将缓存在 PlayerPrefs 中(使用Device Binding密钥加密 — 请参阅下面的缓存安全性)。在后续运行中,在发起网络调用之前会同步读取 OZPH_2__ 缓存,因此即使播放器离线,SDK 也知道自己处于哪一层以及解锁了哪些功能。

对于激活时记录的 cachedAtMillis 中的 tokenTtlSeconds,缓存的权利受到信任。默认 7 天 (604800)。如果设备持续离线且 TTL 过期,则缓存将被丢弃,并且 SDK 将降级为标准级别,直到下次成功激活。探测器继续运行——仅关闭遥测传输和签名时间验证。

Pro — 签署离线阻止策略

优雅降级是针对许可证、网络和服务器故障的操作。这并不意味着门户中明确阻止的构建允许离线。如果 Pro 激活成功,服务器会将当前门户阻止策略签署到激活令牌中,并且 SDK 会将该策略与权利分开缓存。

在推荐的 ApplyCachedBlockPolicies 模式下,即使在签名策略生效时播放器在飞行模式下运行,被阻止的清单哈希、SDK 版本和应用程序版本也将继续被构建完整性拒绝。策略遵循签名令牌 TTL,因此在门户中更改阻止规则后需要进行一次在线激活。

尚未收到 Pro 激活的首次运行设备将完全离线,并且不知道门户阻止规则。对于销售/经济流很重要的直播游戏,请将服务器端会话/OZA 验证作为核心游戏或产品处理部分的最终网关。
情况 探测器 遥测 签约时间
在线,刚刚激活 全部 10 项开启
离线,缓存 < TTL 全部 10 项开启 队列(下次在线时发送) WebTime 回退到 HTTPS HEAD
离线,缓存 > TTL 全部 10 项开启 关(降级) 关(降级)
无缓存(首次运行+离线) 全部 10 项开启 关闭直到第一次在线运行 关闭直到第一次在线运行
脱机发生的遥测事件不会缓冲到磁盘 - 它们会被丢弃在警告级别的单行日志中。这是设计使然:缓冲提示本身成为调制表面。下次在线正常检测就会正常发送。

故障排除

所有许可事件均通过 OZeroSecLog 记录,默认情况下路由到 Unity 中的 Debug.Log(可在 OZeroSecurityConfig 中设置)。使用 [OZeroLicense] 前缀过滤播放器日志以查看启动时发生的情况,并使用 [OZeroTelemetry] 进行报告。常见症状及解决方法:

症状(日志行) 可能的原因 要检查什么
[OZeroLicense] Standard / serverless mode. 资产丢失或等级=标准或空密钥 确保 OZeroLicenseConfig.asset 位于 Resources/ 文件夹下,并且具有准确的名称 OZeroLicenseConfigtier=Pro 和非空 licenseKey
/v1/activate failed: 401: invalid_key 关键拼写错误或不正确的层级前缀 请粘贴原始购买电子邮件中的密钥。格式区分大小写,必须包含破折号,并且必须以 OZ-PRO- 为前缀。
/v1/activate failed: 403: bundle_mismatch appIdentifier 不匹配 服务器端许可证记录将密钥绑定到特定的捆绑包/包 ID。检查播放器设置中的 Application.identifier 是否与许可证中注册的 ID 匹配。
activation token signature verification failed serverPublicKeyHex 或 MITM 无效 将资产的十六进制字符串与购买电子邮件中的标准公钥进行比较 - 所有 64 个十六进制字符必须匹配。如果正确但仍然失败,请怀疑公司代理/MITM 设备并尝试直接移动数据连接。
/v1/activate timed out 网络延迟/防火墙 SDK 自动回退到缓存 — 这只是首次运行/无缓存时的问题。确保您的设备可以达到https://api.ozero.security/health;如果在高延迟区域操作,请增加 activationTimeoutSeconds
cached entitlement past TTL; clearing. 玩家离线 > TTL 这很正常。探测器继续运行;遥测/签名时间将关闭,直到下一次在线运行。如果您的用户经常离线玩游戏超过 7 天,请增加 tokenTtlSeconds
cache hit; tier=pro on a different machine than expected 更改设备指纹 如果 SystemInfo.deviceUniqueIdentifier 发生更改(某些 Android ROM/操作系统重置),v2 缓存将变得不可读,SDK 只需重新启用它。无需用户操作 - 它将在您下次在线运行时恢复。
如果您担心部署日志文件中正在运行的检测器被泄露,请不要在 enableLog = true 状态下部署构建。许可证流日志公开了层/功能状态,这很好,但也成对禁用全局 OZeroSecurityConfig.enableLog 以保持检测器的内部逻辑安静。

项目设置

在调整各个安全模块之前,请首先检查 Unity 播放器设置,这会影响本机插件、商店构建和平台验证。

最低构建目标

平台 最低目标 解释
iOS 12.0+ 对于 iOS 版本,请将 Project Settings > Player > iOS > Target minimum iOS Version 设置为 12.0 或更高。 OZero 不会强制您覆盖此 PlayerSettings 值,因此您可以使其与应用程序的支持政策保持一致。
Android API 21+ 对于 Android 版本,请将 Project Settings > Player > Android > Minimum API Level 设置为 Android 5.0 Lollipop (API level 21) 或更高版本。我们建议使用 IL2CPP 和 ARM64 进行商店发布版本。

Android ProGuard/R8 设置

OZero Security 不需要单独的 Java SDK 包。但是,如果您在 Android 发行版本中启用了 Minify、ProGuard 和 R8,则必须保留 Unity Java 桥和项目使用的任何自定义 Android 桥类。这确保了包信息、Install Source、APK 签名证书验证和启动资产加载所需的 JNI 调用即使在混淆后也能可靠运行。

在 Unity 中,打开 Project Settings > Player > Android > Publishing Settings 如果您已启用 Minify Release,还要启用 Custom ProGuard File 并将以下规则添加到 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.** { *; }

5 OZeroSecurityConfig 设置

OZeroSecurityConfig 导入包时包含 ScriptableObject 资源。通过在“项目”窗口中选择所有安全模块设置来查看和调整它们。在运行时,它被作为 OZeroSecurityConfig.Instance 访问。

OZeroSecurityConfig Inspector panel — replace with actual screenshot

常规设置

这是全局适用于所有模块的顶级字段。

场地 Type 默认 解释
developerSecret string 用于派生加密密钥的唯一密码。将其设置为您的游戏独有的值并保密。如果发布后进行更改,现有的保存数据将变得不可读。
enableLog bool false 启用调试日志 将向 Unity 控制台和播放器日志输出内部状态,例如 SDK 初始化、设置加载、许可证/遥测流程和检测事件。这对于开发/QA 阶段的根本原因识别很有用,但建议在生产版本中关闭,因为它可能会暴露检测流和模块状态。
enableFailureDiagnostics bool false 发生安全违规时,打开 启用故障诊断 会将本地诊断文件保存到 Application.persistentDataPath。该文件可能包含模块名称、哈希、设备状态、安装包名称和一些运行时设置,因此我们建议仅在 QA 或客户支持会话时打开它。

按平台划分的默认存储位置:
  • Windows: %USERPROFILE%\AppData\LocalLow\CompanyName\ProductName
  • macOS:~/Library/Application Support/CompanyName/ProductName
  • Linux:~/.config/unity3d/CompanyName/ProductName
  • Android:/storage/emulated/0/Android/data/package.name/files
  • iOS:应用程序中的Documents文件夹sandbox
  • WebGL:基于浏览器 IndexedDB /idbfs Repository
重要提示: 请务必在首次发布之前设置您自己的 Developer Secret。不要使用默认值,发布后也不要更改它们。

响应设置

控制安全模块生成检测事件时的行为。

场地 Type 默认 解释
forceQuitOnDetection bool true forceQuitOnDetection 确定在检测到已确认的威胁时是否应自动终止应用程序。默认情况下,在“标准”和“严格”预设中,即使托管代码回调已修补或丢失,内部后备接收器也可以强制执行响应策略。仅在受控 QA 流程中将其关闭,其中必须观察检测情况而不终止。

构建完整性验证器设置

配置构建完整性检查器以检测修改的游戏文件、调试器附件和异常执行环境。

场地 Type 默认 解释
Activate Build Integrity checkbox On 启用构建完整性模块。
validateOnStartupbooltrue启动游戏后立即运行构建完整性检查。
validateInEditorboolfalseUnity 编辑器还运行验证(对于测试很有用,但建议在正常开发期间关闭)。
enablePeriodicValidationbooltrue即使游戏正在运行,也会重复完整性验证。我们建议您打开它,除非您正在构建一个启动时一次性验证就足够的版本。
periodicCheckIntervalfloat300 s游戏运行时定期安全检查之间的时间间隔(以秒为单位)。
periodicCheckJitterPercentfloat35%在验证周期中添加随机抖动使得攻击者很难预测准确的验证时间。例如,300 秒处的 35% 抖动将大约运行 195 到 405 秒。
timingAnomalyConsecutiveRequiredint7确定基于时序的异常信号必须累积多少次才能被视为调试器时序漂移。强调试器信号可能会导致它立即失败。
timingAnomalyWindowSecondsfloat900 s用于累积异常信号的基于时间的时间窗口。越长越宽松,Strict 使用较短的窗口。
timingAnomalyFrameHitchSuppressionSecondsfloat20 s在发生大帧故障(例如场景加载、着色器编译、GC 和操作系统调度延迟)后,在一段时间内放松基于时序的调试器检查。
checkAssemblyHashbooltrue作为构建完整性检查的一部分,已编译的 DLL 程序集的哈希值将得到验证。
checkDebuggerbooltrue在运行时检测调试器是否附加到进程。
failOnDebugBuildboolfalse将 Unity 调试版本视为违规(建议用于发布版本)。
checkPlatformNativebooltrue检查是否有迹象表明它正在模拟器上或异常环境中运行。
failIfManifestMissingbooltrue如果 OZeroIntegrityManifest.ozero 文件不存在,则视为违规。保持启用状态以防止攻击者通过删除清单来绕过完整性检查。
failIfAssemblyHashBlobMissingbooltrue如果清单中没有捆绑任何程序集哈希 blob,则这将被视为违规。
requireCodeSignature (Windows)boolfalse主要可执行文件必须经过代码签名。仅限 Windows。
blockVirtualMachine (Windows)boolfalse防止游戏在虚拟机内运行。仅限 Windows。
blockHyperV (Windows)boolfalse阻止 Hyper-V VMBus 信号传输。 WSL2、Docker Desktop 和 Windows Sandbox 用户也可能被阻止,因此请谨慎使用,并且仅在受控环境中使用。
blockNetworkProxies (Windows)boolfalse检测网络代理/数据包检查工具,例如 Wireshark、Fiddler、Charles 和 HTTPDebugger。在竞争性构建中使用它,但测试可能的误报。
blockReverseEngineeringTools (Windows)boolfalse检测逆向工程工具执行信号,例如 ILSpy、dotPeek、Ghidra、WinDbg 和 IDA Pro。
blockSystemMonitorTools (Windows)boolfalse检测Process Explorer、Process Monitor、Process Hacker等系统监控工具。考虑高级用户误报的可能性。
il2cppHashGameAssemblybooltrue在 Windows IL2CPP 版本中对 GameAssembly.dll 进行哈希验证。这是 IL2CPP 文件保护的最低建议。
il2cppHashGlobalGameManagersbooltrueglobalgamemanagers 添加到 IL2CPP 清单验证范围。
il2cppHashSharedAssetsbooltruesharedassets* 文件添加到 IL2CPP 验证范围。
il2cppHashSceneFilesbooltruelevel* 等 Unity 场景文件添加到 IL2CPP 验证范围。
il2cppHashResourcesAssetsboolfalseresources.assets 添加到 IL2CPP 验证范围。对于严格模式很有用,但首先测试补丁流程。
il2cppAdditionalWatchedFilesList<string>如果您的项目包含单独的本机有效负载,请指定要监视的其他 Windows IL2CPP 输出文件。
blockEmulator (Android)booltrue仅适用于安卓。将模拟器或不受支持的运行时信号视为完整性违规。我们建议在 QA/模拟器测试期间采用宽松的策略,而在生产构建时采用更严格的策略。
blockSystemRwMount (Android)booltrue仅适用于安卓。如果系统分区可写或根挂载状态可见,则将其视为完整性违规。
androidShaKeysList<string>预期 APK 签名证书的 SHA-256 指纹列表。如果安装的 APK 未使用这些密钥之一进行签名,验证将失败(仅限 Android)。
expectedBundleIds (iOS)List<string>允许的 iOS Bundle ID 列表。如果留空,将跳过 Bundle ID 检查。
excludedAssembliesList<string>要从哈希验证中排除的程序集名称列表(不包括 .dll 扩展名)。用于在运行时更改的程序集(例如生成的代码)。
checkIntegrityWithServer (Pro)boolfalse开启 Pro 服务器证明(attestation)。客户端请求 nonce 并提交完整性证据后,会收到一个已签名的 OZA 令牌,可由游戏服务器或 OZero 托管验证(Managed Verification)进行校验。在较新的服务器上,nonce 会与 manifest hash、platform、SDK version 和应用标识信息绑定,以减少重放与证据替换。
enableManagedVerification (Pro)boolfalse为没有自己的游戏服务器的团队提供委托验证选项。 OZero 服务器再次验证颁发的 OZA 令牌,并返回允许/警告/阻止判决和简短的托管会话。 SDK 在托管会话过期之前自动尝试重新验证托管会话。 checkIntegrityWithServer 必须打开。
requireManagedVerification (Pro)boolfalse打开后,托管验证网络故障也被视为验证失败。一般来说,我们建议保留它并让它悄悄降级到标准。
attestRefreshLeadSeconds (Pro)float300 s确定 OZA 令牌在到期之前尝试续订的秒数。如果0,则不使用到期前的预续订。
attestRefreshCooldownSeconds (Pro)float30 s证明 续订尝试之间的最短等待时间。
manifestSigningPublicKeystring如果启用 requireManifestSignature,则用于验证清单签名的 RSA 公钥 (PEM)。
requireManifestSignatureboolfalse验证完整性清单本身是否使用 manifestSigningPublicKey 进行签名。
manifestSigningPrivateKeyPathstringOZeroSigningKeys用于在构建时进行清单签名的私钥 PEM 路径。这仅供编辑器使用,不包含在播放器版本中。
可以从 OZero 管理/策略端打开 Pro Strict Attestation。在此模式下,随机数与提交的清单哈希、平台、SDK 版本和应用程序标识信息相关联,并且仅当注册的构建版本和强完整性证据都存在时,服务器才会颁发 OZA 令牌。

Events

Event Description
OnValidationPassed 当本地完整性检查完成且没有违规时发生。专业服务器认证可能仍在进行中。
OnAttestationPassed 仅在 Pro OZA 证明(attestation)令牌实际签发后才会触发。游戏服务器登录、PvP、排名和货币流程,请先用本事件或AttestationToken.IsValid(nowMillis)
OnValidationFailed 当完整性检查检测到违规时发生。全局 onHackDetected 事件也会与 ModulationType.BuildIntegrity 一起触发。

生成 RSA 签名密钥(构建完整性)

如果启用 Build Integrity 并打开 Require Manifest Signature,OZero 将使用公钥签名非对称签名来验证完整性清单未被篡改。在创建发布版本之前,您必须在 Unity 编辑器中生成密钥对。

清单签名的工作原理

在构建时,OZero 使用私钥 OZPH_2__ 签署程序集清单,并将生成的签名包含在构建中。在运行时,使用存储在 OZeroSecurityConfig 中的 公钥 来验证签名。如果签名不匹配(即清单或二进制文件已被篡改),游戏将认为这是篡改并根据响应设置进行响应。

Build Integrity Manifest Signing UI

生成密钥对

  1. 在 Unity 编辑器的“项目”窗口中选择 OZeroSecurityConfig
  2. 在检查器中,展开 Build Integrity 部分。
  3. 激活 需要清单签名 复选框。
  4. 单击生成密钥对按钮。
  5. OZero 生成公钥签名密钥对。 公钥直接写入OZeroSecurityConfig(存储在资产中)。 私钥存储在以下路径:
    [ProjectRoot]/OZeroSigningKeys/manifest_private_key.pem
  6. 将出现一个确认对话框,显示私钥位置。单击 确定 关闭。
⚠ 重要 — 私钥安全
  • 私钥文件存储在 Assets/ 文件夹 external 中,以便 Unity 不会将其包含在构建中。切勿在 Assets/ 内移动。
  • 立即将 OZeroSigningKeys/ 添加到 .gitignore。将您的私钥提交给版本控制是一个严重的安全风险。
  • 将您的私钥备份到安全的离线位置(加密的 USB 驱动器、密码管理器等)。拥有此文件的任何人都可以伪造有效的清单。
  • 如果您丢失了私钥,您将需要生成新的密钥对。您的新公钥将自动包含在下一个版本中。所有以前发布的版本都将无法使用新公钥进行清单验证,因此必须部署更新。

自定义私钥路径(CI/CD & 团队环境)

您可以在检查器中 Manifest Signing — Editor Only 下的 Manifest Signing Private Key Path 字段中指定私钥的备用位置。这在两种情况下很有用:

  • CI/CD Pipeline — 将私钥存储为 CI 机密并在构建时注入路径可以使构建机器不必将密钥永久保留在磁盘上。
  • 团队环境 — 将密钥存储在共享秘密服务器或秘密管理器上,并将该字段指定为安装路径。只有运行发布版本的团队成员才需要访问权限。

验证现有密钥对

要验证磁盘上的私钥是否与 OZeroSecurityConfig 中存储的公钥匹配,请单击 验证密钥对 按钮。 OZero 使用您的私钥签署一个小型测试负载,并使用您的公钥对其进行验证。如果匹配,您将收到一条成功消息,如果不匹配,您将必须创建一个新的对。

何时重新生成密钥对
  • 如果您的私钥丢失或泄露。
  • 验证密钥对报告不匹配(密钥不同步)。
  • 当密钥作为计划安全策略的一部分有意轮换时。

重新生成后,新的公钥将自动包含在下一个版本中。以前发布的版本将无法使用新公钥进行清单验证,因此必须部署更新。

双指纹不间断按键旋转

“生成密钥对”按钮同时将公钥存储在两个位置 - OZeroSecurityConfig(资产中的 ScriptableObject)和 Assets/OZeroSDK/Scripts/Security/BuildIntegirity/OZeroManifestTrustAnchor.cs 中的 SHA-256 指纹常量。在运行时,SDK 首先检查资产的公钥是否与程序集中嵌入的指纹匹配,然后再验证 RSA 签名。如果不匹配,它将拒绝清单本身。这种固定是阻止“重新打包攻击,即攻击者同时用自己的密钥替换资产和 .sig”的关键。

两个指纹槽

OZeroManifestTrustAnchor.cs 中有两个 const 槽 — ExpectedPublicKeyFingerprintHex (当前生产指纹)和 PreviousPublicKeyFingerprintHex (之前的指纹,仅在旋转时填充,但通常为空)。如果实际 SPKI 哈希与 匹配,则 Matches() 验证函数返回 true,并通过恒定时间 XOR 比较防止定时攻击。这种双槽模型就是主页上宣传的“不间断密钥轮换”的现实——新版本同时信任新密钥(预期)和旧密钥(先前),这样在宽限期内,旧版本仍然可以验证旧清单并同时推送新版本。

轮换程序(手动但仅需 5 分钟)

  1. 打开当前指纹备忘录. Assets/OZeroSDK/Scripts/Security/BuildIntegirity/OZeroManifestTrustAnchor.cs并将ExpectedPublicKeyFingerprintHex的当前值复制到剪贴板(或临时备忘录)。
  2. 移至上一个插槽。 在同一文件中,将步骤 1 中复制的值粘贴到 PreviousPublicKeyFingerprintHex(通常为 "")并保存。
  3. 生成新密钥对。 OZeroSecurityConfig 在检查器中,单击 生成密钥对。针对“现有清单将失效”警告,选择 Regenerate。新的私钥被写入 PEM,新的公钥被更新到资产,新的指纹被自动注入到 ExpectedPublicKeyFingerprintHex 中。步骤 2 中填写的 PreviousPublicKeyFingerprintHex 将保持不变。
  4. 构建+发布新的 SDK 版本。推送到常用补丁/商店渠道。此版本中的新清单将使用新的私钥进行签名。
  5. Fleet 等待翻转。 在宽限期内,新版本信任两个孩子,旧版本按原样验证其旧清单(仅按预期设置旧指纹)。两边都没有破损。现场比赛通常需要 1 到 4 周。
  6. 上一个 空槽。 确认遥测中旧版本的份额为 0% 后,恢复到 PreviousPublicKeyFingerprintHex = "" 并再次发布 SDK。旋转完成。
为什么“不需要强制更新”

旧的构建二进制文件已经附带了使用旧密钥签名的旧清单,并且旧指纹卡在预期插槽中。即使它不知道有关新密钥的任何信息,它也会按原样工作,因为它使用自己的密钥验证其清单。新版本需要上一个插槽的原因只有一个——当玩家在轮换期间强制重新安装或回滚到旧版本时,旧的缓存清单仍然需要使用新版本的信任锚进行验证。一旦旧版本从舰队中消失,您可以清空“上一个版本”以获取下一个版本。

故障排除

下面的五个症状都源于相同的防御堆栈 - 构建时 PreBuild 验证 (OZeroBuildPreflightValidator)、启动时锚点防护 (OZeroManifestTrustAnchor.ValidateConfigurationOrFail) 和运行时验证 (OZeroBuildIntegrityValidator.VerifyManifestSignature)。所有情况下的解决方案都是重新同步三个输出(私钥 PEM、OZeroSecurityConfig 中的公钥和 OZeroManifestTrustAnchor 中的指纹)。

症状 1 — 发布版本在执行时立即退出,原因代码为 0x0E (TRUST_ANCHOR_MISSING)

ExpectedPublicKeyFingerprintHex 是一个空字符串。编辑器和开发版本仅通过警告,但发布版本在接受不受信任的清单之前立即退出。 解决方案:如果在编辑器中运行生成密钥对一次,指纹将自动注入const。如果即使填写了 const,它仍然退出,则程序集哈希 blob (oz_ahash.bin) 可能已过时,因此请在干净的状态下重建。

症状 2 — 在构建开始之前,构建中止并显示“没有配置清单签名公钥

PreBuild 验证程序(callbackOrder 51)发现 OZeroSecurityConfig.Integrity.ManifestSigningPublicKey 为空。 解决方案: 在检查器中打开 OZeroSecurityConfig → 展开 Build Integrity → 单击 Generate Key Pair 并重建。请注意,在 Android / iOS(使用操作系统级别签名)和 IL2CPP Standalone(本身没有每 DLL 文件)上会自动跳过此验证,因此缺少密钥只会阻止 Mono Standalone 目标。

症状 3 — 运行时日志:“Manifest 签名公钥与固定的信任锚指纹不匹配 — APK 似乎已使用攻击者控制的密钥重新打包

OZeroSecurityConfig 公钥字符串的哈希值与 ExpectedPublicKeyFingerprintHexPreviousPublicKeyFingerprintHex 都不匹配。在正常构建中,两个输出由于手动编辑而未对齐。 解决方案: 在编辑器中,运行 Tools → OZero → Validate Manifest Signing Keys — 如果密钥对一致,该工具将自动重写 const。如果您报告不匹配,请使用 Generate Key Pair 颁发新的密钥对。如果您在修改后的二进制文件上看到此消息,则这是一个预期的失败关闭信号 - 您应该检查 APK / .exe 是否已重新打包。

症状 4 — 旧版本用户在轮换后无法通过清单验证

您错过了轮换过程的第 2 步。新的 SDK 版本随 PreviousPublicKeyFingerprintHex = "" 一起发布,防止使用缓存的旧版本强制重启(或通过强制重新安装获取新的旧清单)的玩家无法使用自己的信任锚验证清单。 已解决:立即使用 PreviousPublicKeyFingerprintHex 的旧指纹部署修补程序 SDK 版本 — 随后的清单获取将被验证为两个孩子之一。

症状 5 — 本地构建成功,但 CI 构建显示“未找到私钥”或使用错误的密钥签名

CI 机器没有 OZeroSigningKeys/manifest_private_key.pem 通常被 .gitignore 阻塞。 解决方案:将 PEM 存储在 CI 机密(GitHub Actions Secret、GitLab 变量、Jenkins 凭证等)中,并配置管道以在 Unity 构建步骤之前将其恢复到 OZeroSigningKeys/manifest_private_key.pem。或者,指定 OZeroSecurityConfigManifest Signing Private Key Path 作为 CI 运行程序可以挂载的路径(例如机密管理器挂载)。所有 CI 运行者必须使用相同的 PEM ——即使公钥相同,如果 PEM 不同,签名也会不同。

设置Install Source

限制游戏仅在从批准的商店或目录安装时运行。对于防止旁加载或重新打包 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 允许通过华为应用市场安装。
allowOneStore bool false 允许通过 One Store(韩国)进行安装。
allowXiaomiGetApps bool false 允许通过小米 GetApps 安装。
allowOppoAppMarket bool false 允许通过 OPPO 应用市场安装。
allowVivoAppStore bool false 允许通过 Vivo App Store 安装。
allowADB bool false 允许通过 Android 调试桥 (ADB) 安装。仅出于内部测试目的启用。
allowDetectionFailedboolfalse当 Android 安装程序包查找或 JNI 调用失败时允许游戏执行。我们建议在发布版本中将其关闭。
allowUnknownSourcesboolfalse允许不在默认或自定义白名单中的Install Source。仅在需要本地商店响应时进行测试和使用。
enableServerSync (Pro)boolfalse将检测到的安装程序包发送到OZero服务器以执行白名单验证和审计日志。仅适用于专业版。
customAuthorizedPackages List<string> 允许的其他安装程序包名称(例如 com.yourcompany.launcher)。
reportViolationToCallback bool true 如果检测到未经授权的Install Source,则会向服务器发送报告。
logRawInstallerPackage bool true 将原始安装程序包名称记录到控制台。这在开发阶段为您的自定义商店确定正确的包名称时非常有用。

Steam Anti-Piracy设置

检查通过 Steam 分发的 PC 构建中的 Steam 启动路径、App ID、授权状态和发布配置。Standard 执行本地验证;基于 Steam 服务器证据的更强所有权验证由 Pro Server Steam Attestation 提供。

场地 Type 默认 解释
Activate Steam Anti-PiracycheckboxOffSteam Anti-Piracy 的启用复选框。由于 Steam App ID 和分发流程因项目而异,默认关闭。请先在 observe/QA 模式确认状态,再收紧发布构建策略。
expectedSteamAppIdint0项目的 Steam App ID。如果本地检查使用了开发用 AppID 480,请在发布前替换为真实 App ID。
requireSteamLaunchbooltrue检查游戏是否通过 Steam 启动,而不是直接运行可执行文件。请将开发阶段的直接运行测试与发布策略分开。
requireSteamApiInitbooltrue检查 Steam API 初始化是否成功。本地开发启动可能因 Steamworks 设置而失败,因此最终判断应通过实际 Steam 分发路径验证。
requireSubscribedCurrentAppbooltrue检查当前 Steam 账号是否拥有或有权使用该应用。这是本地验证;服务器侧所有权验证由 Pro 处理。
requiredDlcAppIdsList<int>需要由要保护的 DLC 流拥有的 DLC 应用程序 ID 列表。
blockSteamAppIdTxtInReleasebooltrue如果发布构建中仍包含开发用 steam_appid.txt 文件,则判定失败。该文件仅用于本地开发检查,不应随产品分发。
validateSteamApiDllHashboolfalse根据已知的 SHA-256 值验证 Steam API DLL 哈希值。
allowFamilySharing / allowFreeWeekend / allowTimedTrialbooltrue控制是否允许 Steam 家庭共享、免费周末和定时试用权限状态。
enableServerSteamAttestation (Pro)boolfalse仅 Pro。向 OZero 服务器提交 Steam 授权证据,以获得比本地客户端结果更强的服务器侧所有权验证。

Device Binding设置

使用硬件指纹将您的游戏帐户绑定到您的第一台设备。检测到不同硬件的帐户转移。

商业运营目的

Device Binding 最适合作为 Pro 运营控制层:它将许可证与服务器注册的硬件指纹关联,使团队能够隔离高风险设备、降低随意共享许可证的价值,并在不关闭整个许可证的情况下支持合法的设备更换。

阻止重复风险设备

请在查看 SpeedHack、Injection、Install Source 或 Build Integrity 等安全事件证据后使用。被阻止的设备会在下一次激活、注册、验证(verify)或策略(policy)检查中收到 DEVICE_BLOCKED

支持合法用户重置

手机更换、OS 重装、硬件更换或正常指纹不匹配时使用 Reset Token。该令牌有效期 5 分钟、只能使用一次,并绑定到准确的许可证和设备。

控制设备槽位

对于 Pro 许可证,maxDevices 限制可注册的活动服务器指纹数量。被阻止的指纹不应视为可重复信任的槽位;请仅在客服验证后执行重置或删除。

Reset Token 支持流程

  1. 通过正常客服流程确认玩家账号和重置原因。
  2. 打开 Customer Portal → Device Binding,找到目标设备并签发 Reset Token。
  3. 仅通过已认证的客服渠道发送令牌。不要将其保存在长期公开聊天或截图中。
  4. 游戏或客服 UI 应将令牌传给 OZeroDeviceBindingDetector.Instance?.ClearStoredFingerprint(token.Trim())
  5. SDK 消费令牌后,重启应用或重新进入受保护的启动流程,使当前硬件指纹重新注册。
不要将 Device Binding 宣传为完美的硬件身份保证。OS 重置、隐私设置变化或硬件更换后,平台标识符可能会改变。对于高价值在线游戏,请将其视为提高绕过成本和运营控制的层,并与 Build Integrity、Injection、SpeedHack、Install Source 和服务器验证一起使用。
场地 Type 默认 解释
Activate Device Binding bool true 激活Device Binding模块。
hardwareChangeTolerance int (0–3) 1 触发违规之前允许的硬件组件更改数量。 0 = 严格(无变化),3 = 宽松(更换主要硬件)。 1 是允许次要操作系统或固件更新的级别。
storageKey string "ozero_dfp" 存储加密设备指纹的 PlayerPrefs 密钥。仅当与游戏中现有的键冲突时才进行更改。
enableServerSync bool false 将设备指纹与后端服务器同步,用于服务器端验证。
maxDevices (Pro)int0记录预期服务器端设备限制的值。单独更改此本地值不会增加操作限制,因为实际限制是根据服务器许可证历史记录强制执行的。

速度黑客检测器设置

通过将 Unity Time.realtimeSinceStartup 与本机平台计时器和可选的受信任 Web 时间源进行比较来检测时间刻度操纵 (speedhacks)。

场地 Type 默认 解释
Activate Speed & Time Hack bool true 激活速度黑客检测器模块。
autoStart bool true 游戏运行时自动开始检测。如果禁用,则必须使用 OZeroSpeedHackDetector.StartDetection() 手动启动。
checkInterval float 1.0 s 检测器采样并比较计时器的频率(以秒为单位)。
requiredDetections int 3 触发违规之前所需的连续异常样本数。增加该值以减少不稳定设备上的误报。
ratioTolerance float 0.15 Unity 时间和本机计时器之间允许偏差 (±15%)。超出此范围的样本被视为异常值。
maxAllowedRatio float 4.0 允许的最大时间率。如果超过该值,则无论 requiredDetections 值如何,都会立即被判定为速度黑客。
detectSlowHack bool true 除了速度增加之外,它还检测减慢时间的工具(1.0 - 低于容差的比率)。
enableTimeScaleDetection bool true 检测内存编辑器等以编程方式设置的异常Time.timeScale值。
hackDetectMultiplier float 1.3 即使在达到 requiredDetections 之前,超过 maxAllowedRatio × hackDetectMultiplier 的样本也会被计为检测。
enableThreadTimerCheck bool true 它使用后台线程计时器作为附加参考时钟,使得仅影响主线程的黑客变得更加困难。
useWebTimeValidation bool false 定期从 webTimeUrl 检索当前时间并将其与设备时钟进行比较以检测设备级时间操作。
webTimeUrl string 可靠时间 API 端点 URL(需要返回 Unix 时间戳或 RFC 2616 日期标头)。 useWebTimeValidation 激活时需要。
webSyncInterval float 15 s 与 Web 时间服务器同步的频率(以秒为单位)。
webRatioTolerance float 0.15 设备时间和网络服务器时间之间允许偏差(±15%)。
timeOffsetTolerance float 60 s 触发违规之前允许设备和 Web 服务器之间的绝对时钟偏移的秒数。
focusIgnoreTime float 4 s 应用重新获得焦点后立即忽略的秒数。防止操作系统暂停应用程序时发生误报。
loadingGraceTime float 6 s 启动时忽略检测结果的宽限时间(以秒为单位)。防止场景加载期间可能发生的误报。
lagSpikeIgnore float 0.5 s 帧增量超过该值的样本将被丢弃(被视为真正的滞后尖峰,而不是时间操纵)。
buildFailIfTimeScaleTamperedbooltrue如果受保护的代码似乎在策略之外更改了 Time.timeScale,则会导致构建/验证阶段失败。
timeScaleTamperExemptionsList<string>有意更改 Time.timeScale 的类/方法模式(例如暂停或子弹时间)将被注册为异常。
webTimeUrlsstring[]您可以指定多个可信时间端点。通过要求多次成功响应来提高网络时间可靠性。
minSuccessfulEndpointsint2使用多个网络时间端点时必须成功的最小响应数。
maxConsecutiveFailuresint6确定发生多少次连续的网络时间查找失败以应用不可用策略。
onWebTimeUnavailableenumWarnOnly这是在无法访问可信网络时间时应用的策略。测试您的网络状况,然后增加到严格模式。
detectTimeHackbooltrue除了速度比检查之外,还打开设备时钟和网络时间操作检查。
webSyncJitterPercentfloat20%通过向网络时间同步周期添加随机抖动来避免可预测的轮询。
sustainedLagThresholdfloat0.15这是将重复的慢帧分类为持续滞后而不是作弊证据的标准。
sustainedLagGraceDurationfloat3 s被归类为持续滞后时应用的宽限期。
overloadStrictMultiplierint3在重复异常计时条件后应用更严格的过载处理标准的倍数。
enableRemoteSpeedHackConfig (Pro)boolfalsePro 服务器策略允许在不更改客户端代码的情况下覆盖 Speed & Time Hack 中的某些阈值。
remoteSpeedHackConfigIntervalfloat300 s这是接收新的远程速度和时间黑客设置的循环。
enableSignedServerTime (Pro)boolfalse如果可用,请使用签名的服务器时间来防止伪造未签名的响应。

物理黑客探测器

组件方法 - 不包含在 OZeroSecurityConfig 中。
OZeroPhysicsHackDetector 专为需要每个玩家对象独立检测阈值的多人游戏/MMORPG 游戏而设计。将组件直接添加到播放器预制件中,并在检查器中基于每个对象配置设置。通过在生成逻辑中调用 Initialize(playerId) 来启用检测。
场地 Type 默认 解释
maxAllowedSpeed float 15 u/s 允许的最大移动速度(每秒单位数)。调整到游戏中允许的最大合法玩家速度。
distanceTolerance float 2.0 u 额外的距离容差可补偿网络延迟或物理误差。
obstacleLayer LayerMask 代表墙壁和障碍物的图层蒙版。用于通过线路广播检测墙壁黑客行为。
checkInterval float 0.05 s 物理验证器对玩家位置和速度进行采样的频率(以秒为单位)。
maxDeltaTimeCap float 0.1 s 距离计算中使用的增量时间的上限。防止欺诈性传送被滞后峰值掩盖。

注入检测器设置

检测未经授权的代码注入,包括程序集注入、DLL 劫持和 IL2CPP 修补。

场地 Type 默认 解释
Activate Injection & HookingcheckboxOn启用代码注入检测器模块。检测逻辑完全在Native C++层运行,不需要额外的配置。
enableServerWhitelist (Pro)boolfalse从服务器下载受信任的本机模块策略,以集中允许批准的覆盖或合作伙伴模块。
serverWhitelistRefreshIntervalfloat0 s服务器管理白名单策略更新周期。如果 0,则仅在启动或策略引导时更新。
requireSignerForNativeWhitelistboolfalse除了文件哈希之外,本机白名单条目还需要签名者标识。需要更强但更清晰的模块签名数据。
enableRemoteInjectionConfig (Pro)booltruePro 服务器策略允许您调整注入阈值和探测配置,而无需更改客户端代码。
remoteInjectionConfigIntervalfloat300 s这是接收新的远程注入策略的周期。
enableWindowsModuleIdentityScanbooltrue扫描加载的 Windows 本机模块并将哈希/签名者信息与信任策略进行比较。
scanIntervalSecondsfloat1 s这是注入探针的默认运行时扫描周期。
windowsModuleIdentityScanIntervalSecondsfloat5 s这是一个相对繁重的 Windows 模块识别扫描的单独周期。
scanJitterPercentfloat20%扫描时序中添加了抖动,使得探测时序难以预测。
enableExecutablePrivateMemoryScanbooltrue检测注入的 shellcode 或运行时修补程序经常使用的可执行私有内存区域。
enableInlineHookScanboolfalse检测已知代码入口点的可疑内联补丁。此项在严格模式下进行兼容性测试后开启。
enableThreadStartAddressScanbooltrue检查线程起始地址是否是受信任模块之外的可疑位置。
enableExternalProcessHandleScanbooltrue检测外部进程是否持有游戏进程的可疑句柄。
detectionConfidenceThresholdint70这是组合多个注入信号时确认违规的最低置信度分数。
enableWebRuntimeTamperScan (WebGL)booltrue仅适用于 WebGL 的轻量级浏览器篡改检查。由于它不受 Native C++ 保护,因此将其视为有限的客户端信号收集。
WebGL probesboolstrue控制在 WebGL 构建中检查 DevTools、时钟钩子、网络钩子、WASM 钩子、存储钩子和加密钩子。

PlayerPrefs 加密

要保护存储在 Unity PlayerPrefs 中的数据(设置、用户首选项等),请将 PlayerPrefs 替换为 OZeroSafePlayerPrefs。 API 是一样的。

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);
重要:发布后请不要更改 developerSecretdeveloperSecret 是用于保护 OZeroSafePlayerPrefsOZeroSV_File 数据的基准值之一。发布后更改此值,旧版本保存的受保护数据可能无法再打开。

4 保护游戏内变量(安全类型)

安全类型用加密类型替换常规 C# 变量类型。该值存储在 Native C++ 堆中,因此 Cheat Engine 等工具无法通过扫描内存找到它。只需更改类型名称,其余代码将按原样运行。

Unity Inspector showing OZeroSV_Int and OZeroSV_Float fields — replace with actual screenshot

支撑类型

现存的 应用OZero后
intOZeroSV_Int
longOZeroSV_Int64
uintOZeroSV_UInt
ulongOZeroSV_UInt64
shortOZeroSV_Short
ushortOZeroSV_UShort
byteOZeroSV_Byte
floatOZeroSV_Float
doubleOZeroSV_Double
decimalOZeroSV_Decimal
boolOZeroSV_Bool
stringOZeroSV_String
Vector2OZeroSV_Vector2
Vector3OZeroSV_Vector3
byte[]OZeroSV_Buffer

示例代码

// ── Before: plain C# variable ──────────────────────────────
using UnityEngine;

public class PlayerStats : MonoBehaviour
{
    public int   gold  = 0;
    public float speed = 5.0f;
    public int   hp    = 100;
}

// ── After: OZero Secure Types (only the type name changes) ──
using UnityEngine;
using OZeroSDK.Security;

public class PlayerStats : MonoBehaviour
{
    public OZeroSV_Int   gold  = 0;
    public OZeroSV_Float speed = 5.0f;
    public OZeroSV_Int   hp    = 100;

    // All arithmetic works exactly as before — no code changes needed
    void TakeDamage(int dmg) => hp -= dmg;
    void AddGold(int amount) => gold += amount;
}
安全类型使用 OZeroSDK.Security 命名空间。将 using 指令添加到声明该类型的脚本中。

7 安全事件处理(可选)

默认情况下,当 OZero 检测到威胁时,它会终止应用程序或仅记录该应用程序,具体取决于您设置的响应策略。如果您需要直接显示警告屏幕、发送您自己的服务器日志或在终止前执行保存过程,您可以注册回调。回调通过 OZeroSecurityEvent 传输调制类型、Abort Code、消息密钥、详细消息和计划终止。

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.
        }
    }
}

API 参考中记录了 OZeroSecurityEventModulationTypeOZeroAbortCode 的完整列表。

如果 evt.WillAbort 为 true,根据当前响应策略,应用程序将在回调后终止。此时,只需简单地执行您自己的分析刷新或保存处理。
4路独立防御

当检测到妥协时,(1) OZeroSecurityManager.BuiltinResponse 默认处理程序(始终运行),(2) 用户 OZeroSecurityReceiver.HandleModulation 和检查器 onHackDetected 事件(场景级选择挂钩),(3) OZeroInternalFallbackReceiver(即使在接收器缺失的情况下也强制执行 Response.ForceQuitOnDetection 策略),(4) 本机 OZ_ReportViolation(完全绕过托管补丁区域的 C++ 导出)- 四个路径独立激活。即使一条路径被打补丁或禁用,整个 SDK 也不会被静音。

下一步

OZero 的核心保护功能现已上线。查看 API 参考以获取有关所有类、方法和配置选项的详细信息。