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 编辑器内的仪表板中激活模块,即可运行核心保护功能。无需场景设置或编写额外代码。

内置保护功能:
  • 构建完整性检查(应用篡改检测)
  • 速度黑客及时间黑客检测
  • 内存注入监控
  • 加密的游戏内变量类型(Secure Types)
  • 加密的存档文件及 PlayerPrefs
无需代码连接的自动引导与 Config 加密
  • 激活的检测器将在 SDK 启动时自动准备就绪。无需在场景中放置独立对象或编写重复的初始化代码。
  • 安全设置在构建过程中受到保护,因此明文设置不会暴露在一般玩家的发布版本中。
  • Native C++ 运行时守卫在托管 Unity 代码之外提供了额外的验证层。
  • 检测结果可通过回调、日志和 Pro 遥测进行确认,便于在测试和运营中追踪原因。

1 导入包

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

当出现 Import Unity Package 对话框时,在勾选所有项目的情况下点击 Import。所需的脚本、原生插件和编辑器工具将自动添加。

Import Unity Package dialog with all OZeroSDK files selected
在导入之前,请退出 Unity 播放模式,如果可能,请重启一次编辑器后继续。如果运行中的播放器已加载 OZero DLL,Windows 会锁定该 DLL 文件,可能导致导入或覆盖失败。导入后 Unity 会重新编译脚本,请等待右下角的进度条消失后再进行下一步。

2 打开仪表板

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

OZeroSDK -> Security -> Config Dashboard
OZero Security Config Dashboard opened in the Unity Editor

打开 Config Dashboard 后,可以在此屏幕上一次性管理预设、安全模块、许可证和构建完整性设置。无需在 Project 窗口中直接查找并修改文件。

Unity 编辑器菜单说明

OZeroSDK > Security 菜单集中提供 SDK 设置和诊断命令。安全预设有意放在 Config & Dashboard 中应用,而不是放在顶部菜单中,这样您可以在保存前确认实际变更的设置。

菜单 作用 使用场景
OZeroSDK > Security > Config & Dashboard打开或创建主 OZeroSecurityConfig 资产。首先在这里应用预设、启用模块、配置响应策略并检查构建完整性设置。
OZeroSDK > Security > License > Settings打开或创建 OZeroLicenseConfig用于输入 Standard、Plus 或 Pro 许可证设置、服务器端点和遥测选项。
OZeroSDK > Security > Tools > Check Setup在编辑器中诊断常见的项目和发布配置问题。在导入包后、发布构建前,或模块行为不符合预期时运行。
OZeroSDK > Security > Tools > Check Integrity Manifest打开 OZero 完整性清单并显示解码后的内容。用于构建完整性故障排查或支持调查。
OZeroSDK > Security > Tools > Bake Security Config BlobStreamingAssets 中重新生成受保护的安全配置 blob。用于高级调试或 CI 工作流。普通播放器构建会自动 bake。
OZeroSDK > Security > Tools > Bake Assembly Hash为最近一次构建输出重新生成 oz_ahash.bin仅在未重新运行完整构建流水线、只进行了代码重建之后使用。
OZeroSDK > Security > Tools > Keystore SHA Extractor从 Android keystore 中提取 SHA-1 和 SHA-256 指纹。在填写 Build Integrity 的 Android 签名指纹时使用。
OZeroSDK > Security > Tools > Add Debug SHA Key to Config查找 Android debug keystore,并将其 SHA-256 指纹加入 config。仅用于本地 Android 调试构建。发布构建应使用发布签名密钥指纹。
OZeroSDK > Security > Tools > Steam Anti-Piracy > Scan Steam Redistributable扫描 PC 构建或插件文件夹中的 Steam redistributable 文件和已知 emulator artifact。在 Steam 发布打包前,或检查是否包含可疑 Steam 文件时使用。
OZeroSDK > Security > Tools > Check Time.timeScale Usage扫描可能与 OZero 时间保护冲突的直接 Time.timeScale 写入代码。在发布前,或 Speed & Time Hack 模块报告项目侧 time-scale 策略问题时运行。

3 激活模块

在仪表板内可以查看安全模块列表及切换开关。请激活您要使用的模块。以下是推荐的初始配置。

OZero Security Config Dashboard in the Unity Editor

选择安全预设

首先选择一个预设,然后根据项目进行个别模块调整。对于一般的实时在线游戏,建议从 Standard 开始。这能在保护级别、性能和误报率之间取得最佳平衡。

预设 推荐用途 适用策略摘要
Low 原型、开发版本、初期 QA 仅保留轻量的核心检查。放宽平台原生检查及强制关闭策略,以免测试环境过早被封锁。
Standard 大多数发售游戏的推荐默认值 启用核心保护套件、启动验证、运行时再验证、模拟器检查以及推荐的 IL2CPP 文件覆盖。是一个平衡了兼容性与保护级别的配置。
Strict 高风险实时服务、PvP、竞技类构建 采用最广泛的覆盖范围,将更多的失败视为致命违规。请在充分测试平台、签名及商店发布流程后再采用。
模块 功能说明 是否推荐
Build Integrity Validator 检测应用二进制是否被篡改 推荐
Speed Hack Detector 检测时间操控作弊 推荐
Injection Detector 监控内存 Hook 工具 推荐
Install Source Validator 拦截非法 APK(仅限 Android) 选择

只需在仪表板中开启要使用的模块并保存 OZeroSecurityConfig 资产即可。播放器启动时,SDK 会自动准备已激活的模块。

无需在场景中放置独立对象。开启所需模块并保存设置后,下次运行时该模块将自动初始化。

许可证模型 — Standard / Plus / Pro

OZero Security 提供 Standard、Plus、Pro 三种等级。Standard 模式不与服务器联动,仅使用本地保护功能。Plus 模式在此基础上加入了各项目特定的 Native Variant 包及清单 / Bundle ID 绑定。Pro 模式包含 Plus 的配置,并增加了遥测、签名验证的服务器时间、远程策略、服务器校验以及设备限制等运营功能。

下方的矩阵概括了从开发者的角度来看,SDK 在运行时对各个层级的实际表现情况。完整的特性对比请参考首页的 许可证模式对比表
项目 Standard Plus Pro
许可证密钥 — (无) OZ-PLS-XXXX ×6 OZ-PRO-XXXX ×6
启动时网络连通 无需 — 可离线执行 无需运行时服务器 — 仅从门户下载 Variant 即可 每设备进行 1 次 POST /v1/activate 后缓存
10 个保护模块 全部 10 个保护模块激活 全部 10 个(本地保护模块与 Standard 相同) 全部 10 个(与 Standard 相同)
原生 Variant 共用的原生模块 各应用特定的 Variant + 清单绑定 包含
云端遥测 不发送 关闭 (无服务器) 开启 — 将威胁事件发至 /v1/telemetry
签名时间 (防时钟篡改) 关闭 — WebTime 仅使用 HTTPS HEAD 关闭 — 与 Standard 相同 开启 — 使用签名的 /v1/time 响应
设备限制 无限制 (无密钥,不强制) 归属项目的许可证,无运行时设备限制 默认 5 台 / 可调节
源代码权限 仅托管 C# 仅托管 C# 仅托管 C#
即使您今日使用 Standard 发布,之后再升级至 Plus 或 Pro,游戏逻辑的执行代码也可原样保留。Plus 会增加 Variant 清单和原生包,而 Pro 会再加入基于 OZeroLicenseConfig 的服务器功能。

Plus / Pro 许可证密钥注册

Standard 级别无需额外设置。Plus 或 Pro 需要添加 1 个 ScriptableObject 资产并将购买后获得的密钥粘贴进去。Plus 密钥用于分项目的 Variant 校验与门户下载,而 Pro 密钥还会被用于运行时服务器激活。

1. 创建设置资产

打开 Config Dashboard (菜单: OZeroSDK → Security → Config Dashboard) ,在 License & Server 栏目点击 Create OZeroLicenseConfig 按钮。资产会自动生成到正确的 Resources/ 文件夹,无需手动移动。

您也可以在 Project 窗口通过 Assets → Create → OZero → License Config 手动生成。但必须自行确保资产位于 Resources/ 文件夹下并且名称准确为 OZeroLicenseConfig (区分大小写)。

2. 填写 Inspector 字段

字段 是否必需 说明
tier 所有层级 在下拉菜单中选择 StandardPlusPro 之一。Standard 使用基本原生模块。Plus 需要与项目相连的 Variant manifest。Pro 在 Plus 基础上提供服务器运营功能。
licenseKey Plus / Pro 收到的邮件中的密钥。Plus 密钥采用 OZ-PLS-... 格式,Pro 采用 OZ-PRO-... 格式。如果将密钥留空,即使选了 Plus 或 Pro 也将不启用服务器功能,按 Standard 方式运行。
appIdentifier 自动传输 SDK 激活时会将 Unity 的 Application.identifier 作为 appIdentifier 传送。服务器会将此值与许可证绑定的 Bundle/Package ID 进行比对,所以 Player Settings 的 Identifier 必须与签发的密钥匹配。
serverBaseUrl Plus / Pro 默认值 https://api.ozero.security — 除非 OZero 支持团队给出了专门的 endpoint 否则请保留原状。在发布构建中必须为 HTTPS 且会自动移除结尾斜杠。
serverPublicKeyHex Plus / Pro 用于验证 OZero 许可证服务器响应签名的公钥。请直接粘贴购买邮件中的 64 位 hex 值。建议发布版本中不要将此项留空。
tokenTtlSeconds 可选 在离线状态下能信任缓存激活结果的秒数。默认值 604800 (7 天)。过期后 SDK 仍可继续运行,但至下一次成功激活前,需要连网的专属功能 (遥测/签名时间) 会被关闭。
offlineProPolicyMode Plus / Pro 当设备离线时应如何应用带有签名的 Pro 门户屏蔽策略。推荐使用 ApplyCachedBlockPolicies。只有对必须阻止旧版策略的纯在线游戏才使用 RequireFreshPolicyIgnoreCachedBlockPolicies 仅是针对特殊测试场景的选项,不建议正式版本使用。
activationTimeoutSeconds 可选 等待 /v1/activate 响应的最长时间 (秒)。如果超时则 SDK 会应用缓存的激活信息或回退至 Standard 继续运行。默认 6.0 秒。
enableLog 可选 为 true 时将使用 OZeroSecLog 输出如调用缓存、成功激活、超时、签名不符等步骤日志。如果不希望在发布版显示此类日志可关闭。

3. 构建与验证

无需修改代码。应用启动时 SDK 将自动读取 OZeroLicenseConfig 资产。首次执行后,请在 Player 日志中寻找类似于 [OZeroLicense] activated; tier=pro caps=5 的输出。如果配置为了 Pro 却出现了在 Standard 模式下运行的日志,请再三确认资产是否放在 Resources/ 目录下且名为确切的 OZeroLicenseConfig

许可证密钥不是机密信息 — 即便外流,也只代表有人能消耗您在服务器端受到强制限制的每设备限额而已。这也是资产不加密并捆绑至构建的(刻意)设计。请把它当做产品序列号而非密码来对待。

服务器激活流程

激活会在应用开启的后台进行。即使服务器响应延迟或临时失败也不会阻断场景加载,并且有缓存即使用之。

引导序列

  1. SDK 将在应用启动时自动初始化许可证的运行时。
  2. Standard 与 Plus 将在无运行时服务器调用的状态下继续进行。
  3. Pro 将在后台发出轻量的激活请求。
  4. 一旦成功激活,遥测、签名时间以及认证(attestation)等 Pro 服务器功能将变为可用。
  5. 发生激活失败、过期或超时等情况,它也会以不提示给玩家的形式继续在 Standard 模式下运行。

网络传输数据

激活请求是一个短小的 JSON POST。必须字段是 licenseKeydeviceIdsdkVersion 以及 platformappIdentifiercompanyNameproductNamewebglOrigin 会在 Unity 端给出非空值时加入。设备 ID 数据来源可借助 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 格式返回带签名的令牌、确定的层级等级、可使用的权限列表以及过期时间。SDK 会先用提供的公钥来校验响应的签名,而后才会去激活 Pro 功能。

无法使用服务器功能时

服务器维护、网络超时、许可证的过期/暂停/废除或 Bundle ID 不符等问题不会被草率地认定为黑客攻击。在此类情况下,它将停用仅适用于 Pro 的功能而继续常规的防御功能。相对的,明显遭受如版本失配、被封禁、注入以及调试工具等篡改的场合,将遵循已设定的应对策略行动。

若想验证直播的激活是否确实成功 (而不只是依赖缓存信任),可以在自有的引导程序中调用 await OZeroLicenseRuntime.Initialize();,然后查询 OZeroLicenseRuntime.Entitlement?.cachedAtMillis 的值。如果数值落在几秒前则代表服务器刚作答,否则是在以缓存基础运行。

离线操作

OZero 被设计成无论网络条件如何,都能让玩家运行游戏。具体行为受级别的制约:

Standard — 永远支持离线

Standard 只负责本地防御且无需请求激活或遥测。在不连网时可以照常启动,这也就意味着不能享有特供 Pro 的功能。

Pro — TTL 内的离线运行支持

当某设备成功运行第一次 /v1/activate 后,Pro 激活数据就会保留在 PlayerPrefs (借设备绑定的钥匙加密 — 请参阅下方的缓存保障)。在随后的启用中它将在触发联网前读取留存数据,如此玩家即使断网,SDK 也能获取属于何种层级或拥有何等 Pro 功能的状况。

存下的 Pro 信息在其激落时点起算到指定的 tokenTtlSeconds 前会被采信。默认 7 天 (604800)。当持续断网走完了此时限,被保留的情报会被销毁,直到下次重新完成激活并被转作 Standard 水平运作。探测器则照常,而只有遥测和验证签名时间相关步骤将处于闭塞状态。

Pro — 具备签名的离线拦截策略

遭受连接失效仍带普通保护运行和默许于系统中被拉黑封杀的组件是截然不同的。倘使 Pro 获取成功,服务器会颁发以含签约格式返回的现行屏蔽策略供 SDK 存留。

采用建议的 ApplyCachedBlockPolicies 模式,凡是带有合格签名的策略存续期间,开启着飞行模式运行依旧对已被制裁的散列、SDK版本或是产品版本进行有效的阻挠。因为效能受制于附属时效,若需改变门户拦截规则,必须使之在线认证一回。

从未取得过初轮激活而离线登入的用户,无从知晓最新的门户限制。依赖变现的在线项目应在关键流程上布下验证游戏过程或通货相关的最终关卡。
状态 探测器 遥测 Signed Time
连线,刚完成激活 10 项皆起作用 开启 开启
离线且 Cache < TTL 10 项皆起作用 暂缓发列 (待连网时传送) WebTime 协议为依归
离线且 Cache > TTL 10 项皆起作用 停顿 (下调标准) 停顿 (下调标准)
无缓存 (首次游玩亦离线) 10 项皆起作用 中止,及至顺利上网 中止,及至顺利上网
在离线时期引发的事件是不会写进硬盘日志缓存的 — 只给出一次级别报备即遭摒除。那是经过深思熟虑的:排队的机制反成软肋,只当在重拾接驳后的合规探取才将发出汇报。

故障排除

大部分许可事件均被安排由 OZeroSecLog 将讯息打进原生的 Debug.Log 中 (借 OZeroSecurityConfig 定制)。于终端中依 [OZeroLicense] 探寻可洞悉起因,另外利用 [OZeroTelemetry] 查询回报实况。以下为典型情况以及相关应对措施:

表象 (输出字句) 可能缘由 核实项
[OZeroLicense] Standard / serverless mode. 欠缺预设又或指明 tier=Standard,或者密钥一片空白 理应令 OZeroLicenseConfig.asset 长驻 Resources/ 深层,名确为该字、层级定在 tier=Pro 且持有妥当未亏的 licenseKey
/v1/activate failed: 401: invalid_key 键入偏差与或阶层前缀用误 回到邮件原始档里头重新拷贝密钥;除了大小写明察秋毫以外,切莫遗漏划线以及确凿开头须是 OZ-PRO-
/v1/activate failed: 403: bundle_mismatch appIdentifier 未比对成功 系统远端的发放是以专户、专属 Bundle ID 绑约的形式记录。烦请再三检阅 Player Settings 里的属性配置和当时颁赐的资讯同处。
activation token signature verification failed serverPublicKeyHex 填写出错或逢受 MITM 对照从电邮取得的原生公钥把 64 码完整对碰,如若仍落得折戟的田地即可高度确信受制于企业拦网或者代理服务了。
/v1/activate timed out 局域延迟及防范墙 除非初启动又或苦无纪录方会发作,不然 SDK 有能力自觉倚重过往资料。查清主机否能顺利联络上 https://api.ozero.security/health 并在跨地域慢网下抬高 activationTimeoutSeconds 容限。
cached activation past TTL; clearing. 玩家断连时日大于 TTL 纯属常态;检测手段不变,惟遥测记录与验证授期会按规矩暂停,至玩家上架前停息,当遇到远行常客可提升 tokenTtlSeconds 放宽。
cache hit; tier=pro on a different machine than expected 机体生物鉴别转移 当出现类似操作系统换代等带来标识代码变换情况,那过去储备的信息顿失意义所以只待联机重新执行取权流程。
一旦决定上线便理应把 enableLog 关闭。研发日志可能显露防御手法甚至状态详情供人刺探,即便对内品保管用,对大众发行可是多此一举。

项目设置

在调整各个安全模块之前,请先检查影响原生插件、商店构建和平台验证的 Unity Player Settings。

最低构建目标

平台 最低目标 说明
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。
运行 Windows Standalone 构建的 PC 必须安装 Microsoft Visual C++ Redistributable 2015-2022 (x64)。如果缺少此运行时,Windows 可能无法加载 OZero 原生插件,导致应用在启动后立即退出。如果通过 Steam、启动器或独立安装程序分发,请配置将 VC++ Redistributable 作为必备组件一起安装。

Android ProGuard / R8 设置

OZero Security 不需要单独的 Java SDK 包。但是,如果您的 Android 部署版本启用了 Minify、ProGuard 或 R8,则必须保留 Unity Java 桥接以及项目中使用的自定义 Android 桥接类。这样才能确保获取包信息、安装来源、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 资产会在包导入时包含。在 Project 窗口中选择它即可查看并调整所有安全模块设置。在运行时通过 OZeroSecurityConfig.Instance 进行访问。

OZeroSecurityConfig Inspector panel in the Unity Editor

Common Setting

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

字段 Type 默认值 说明
developerSecret string 用于派生加密密钥的唯一密码短语。请为游戏设置一个唯一值并保密。发布后更改会导致现有存档数据无法读取。
enableLog bool false 开启 Enable Debug Logs 会将 SDK 初始化、设置加载、许可证/遥测流程、检测事件等内部状态输出到 Unity Console 和 Player 日志中。这在开发/QA 阶段有助于查明原因,但可能暴露检测流程和模块状态,建议在生产构建中关闭。
enableFailureDiagnostics bool false 开启 Enable Failure Diagnostics 时,如果发生安全违规,本地诊断文件会保存到 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:App 沙盒的 Documents 文件夹
  • WebGL:基于浏览器 IndexedDB 的 /idbfs 存储
重要: 首次发布前必须设置唯一的 Developer Secret。不要使用默认值,发布后请勿更改。

Global Threat Response

决定确认安全威胁后游戏的响应方式。在测试期间请先检查回调和日志,在实际部署构建中请根据项目的运营策略选择给玩家的提示时间和终止策略。

字段 Type 默认值 说明
forceQuitOnDetection bool true forceQuitOnDetection 决定在检测到确切威胁时 SDK 是否自动终止游戏。关闭后只检查回调和日志,便于 QA,但在实际部署构建中可能导致被绕过的客户端继续运行,请慎重选择。
fatalCallbackGraceSeconds float 10 fatalCallbackGraceSeconds 是在检测到威胁后,游戏侧安全回调向玩家显示提示 UI 的最长容许时间。默认值为 10 秒。设置为 0 时会恢复之前的无提示直接退出行为。

构建完整性验证器设置

配置构建完整性检查器,以检测被修改的游戏文件、调试器连接以及异常的运行环境。

字段 Type 默认值 说明
Activate Build Integrity checkbox On 激活 Build Integrity 模块。
validateOnStartupbooltrue游戏启动后立即执行构建完整性检查。
validateInEditorboolfalse在 Unity 编辑器中也执行验证(对测试有帮助,建议在常规开发期间关闭)。
enablePeriodicValidationbooltrue在游戏运行期间也循环进行完整性验证。如果您的构建不足以通过启动时的单次验证,建议开启此功能。
periodicCheckIntervalfloat300 s游戏运行中执行定期安全检查的间隔(秒)。
periodicCheckJitterPercentfloat35%给验证周期添加随机变化,使检查时机更难被预测。公开文档中未提供具体周期与范围,推荐使用默认值。
timingAnomalyConsecutiveRequiredint7确定基于时间的异常信号需要累积多少次才判定为调试器时序漂移。强烈的调试器信号可能仍会导致立即失败。
timingAnomalyWindowSecondsfloat900 s用于累加时间异常信号的时间窗。越长容错越高,Strict 使用更短的时间窗。
timingAnomalyFrameHitchSuppressionSecondsfloat20 s在发生了如场景加载、着色器编译、GC 或 OS 调度延迟等大规模帧卡顿后,在一定时间内放宽对基于时间的调试器检测。
checkAssemblyHashbooltrue作为构建完整性检查的一部分,验证已编译 DLL 程序集的哈希值。
checkDebuggerbooltrue在运行时检测进程是否连接了调试器。
failOnDebugBuildboolfalse将 Unity 的 Debug 构建视为违规(建议用于发布构建)。
checkPlatformNativebooltrue检查是否有在模拟器或异常环境中运行的迹象。
failIfManifestMissingbooltrue如果 oz_manifest.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检测疑似网络代理、数据包检测或流量分析工具的运行信号。用于竞技型构建,但请事先测试是否存在误报可能。
blockReverseEngineeringTools (Windows)boolfalse检测疑似逆向工程或调试工具的运行信号。请在开发/QA 环境与线上环境分别进行验证。
blockSystemMonitorTools (Windows)boolfalse检测疑似进程/系统监控工具的运行信号。请注意普通用户环境中可能存在的误报情况。
il2cppHashGameAssemblybooltrue对 Windows IL2CPP 构建的 GameAssembly.dll 进行哈希验证。这是 IL2CPP 文件保护的最低建议项。
il2cppHashGlobalGameManagersbooltrueglobalgamemanagers 添加到 IL2CPP 清单验证范围内。
il2cppHashSharedAssetsbooltruesharedassets* 文件添加到 IL2CPP 验证范围内。
il2cppHashSceneFilesbooltruelevel* 之类的 Unity 场景文件添加到 IL2CPP 验证范围内。
il2cppHashResourcesAssetsboolfalseresources.assets 添加到 IL2CPP 验证范围内。有助于 Strict 模式,但请先测试补丁流程。
il2cppAdditionalWatchedFilesList<string>当项目包含额外的原生 payload 时,指定需要受监控的 Windows IL2CPP 额外输出文件。
blockEmulator (Android)booltrue仅限 Android。将模拟器或不支持的运行时信号视为完整性违规。建议在 QA/模拟器测试期间放宽,而在生产构建中使用更严格的策略。
blockSystemRwMount (Android)booltrue仅限 Android。若系统分区处于可写状态,或有类似的 root mount 状态,将被视为完整性违规。
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 并提交完整性证据后,获取可由游戏服务器或 OZero 服务器校验的签名 OZA 令牌。服务器会将 nonce 与 manifest hash、platform、SDK version 以及应用标识信息绑定在一起,减少被复用/替换证据的可能。
enableManagedVerification (Pro)boolfalse为没有自有游戏服务器团队准备的委托验证选项。它让 OZero 服务器重新核对下发的 OZA 令牌,返回允许/警告/屏蔽结果与短期会话。SDK 会在会话到期前自动尝试再验证。需要 checkIntegrityWithServer 处于开启状态。
requireManagedVerification (Pro)boolfalse开启后,若委托验证发生网络失效亦会被认作验证失败。一般建议关闭,令其可自动切换至普通(Standard)级保护。
attestRefreshLeadSeconds (Pro)float300 s确定在 OZA 令牌失效前几秒去尝试续约。如果设 0 即不使用提前更新。
attestRefreshCooldownSeconds (Pro)float30 sAttestation 更新尝试间的最低等待用时。
manifestSigningPublicKeystring启用 requireManifestSignature 时,用于验证清单签名的 RSA 公钥 (PEM)。
requireManifestSignatureboolfalse验证完整性清单本身是否通过 manifestSigningPublicKey 进行签名。
manifestSigningPrivateKeyPathstringOZeroSigningKeys构建期间使用的私钥 PEM 路径以签署清单。它仅在 Editor 出现并不在用户构建版内。
Pro Strict Attestation 可以在 Pro 策略 (Portal) 里设置。此模式下,nonce 绑定至提交的 manifest hash、platform、SDK version 与应用标识,并且只有在具备已注册构建版本和强完整性证据的情况下,服务器才会发下 OZA 令牌。

Events

Event Description
OnValidationPassed 当本地完整性检查顺利完成而无违规时发生。此时 Pro 服务器证明 (attestation) 可能仍在进行中。
OnAttestationPassed 仅在实际颁发了 Pro OZA 证明 (attestation) 令牌后触发。请使用此事件或 AttestationToken.IsValid(nowMillis) 来校验,只有通过后才能进行游戏服务器登录、PvP、排名、货币结算等流程。
OnValidationFailed 当完整性检查检测到违规时触发。同时也会随着 ModulationType.BuildIntegrity 触发全局的 onHackDetected 事件。

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

如果启用了 构建完整性 (Build Integrity) 并且开启了 Require Manifest Signature,OZero 会使用基于公钥的签名来验证完整性清单是否未被更改。在制作发布构建之前,必须在 Unity 编辑器中生成密钥对。

清单签名运作方式

在构建时,OZero 会使用 私钥 对程序集清单进行签名,并将生成的签名嵌入到构建中。在运行时,它会使用保存在 OZeroSecurityConfig 中的 公钥 来验证签名。如果签名不匹配 — 即清单或二进制文件已被篡改 — 游戏会将其视为篡改并根据响应设置进行处理。

Build Integrity Manifest Signing UI in the Unity Editor

生成密钥对

  1. 在 Unity 编辑器的 Project 窗口中选择 OZeroSecurityConfig
  2. 在检查器 (Inspector) 中展开 Build Integrity 选项卡。
  3. 勾选 Require Manifest Signature 复选框。
  4. 点击 Generate Key Pair 按钮。
  5. OZero 将生成签名密钥对。公钥 存储在 OZeroSecurityConfig 中,私钥 则保存在以下路径:
    [ProjectRoot]/OZeroSigningKeys/manifest_private_key.pem
  6. 将显示指示私钥位置的确认对话框。点击 OK 关闭它。
⚠ 重要提示 — 私钥安全
  • 私钥文件存储在 Assets/ 文件夹 外部,以防止 Unity 将其打包进构建中。切勿将其移入 Assets/ 中。
  • 请立即将 OZeroSigningKeys/ 添加到 .gitignore 中。将私钥提交到版本控制中是严重的安全隐患。
  • 请将私钥备份到安全的离线位置(如加密 USB 驱动器、密码管理器等)。任何拥有此文件的人都可以伪造有效的清单。
  • 如果您丢失了私钥,必须重新生成一个新的密钥对。新公钥将自动包含在下一次构建中。以前分发的所有构建都会因为新公钥而导致清单验证失败,因此您必须部署更新。

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

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

  • CI/CD 管道 — 将私钥保存为 CI Secret,并在构建时注入路径,这样构建机器就不需要将密钥永久保存在磁盘上。
  • 团队环境 — 将密钥保存到共享的安全服务器或机密管理器中,并将该字段指定为挂载路径。只需确保运行发布构建的团队成员具有访问权限即可。

验证现有密钥对

要验证磁盘上的私钥与存储在 OZeroSecurityConfig 中的公钥是否匹配,请点击 Validate Key Pair 按钮。OZero 会用私钥对一个小测试载荷签名,然后用公钥进行验证。如果匹配,将显示成功消息;如果不匹配,您需要生成一对新密钥。

何时应重新生成密钥对
  • 当私钥丢失或泄露时。
  • Validate Key Pair 报告不匹配(密钥未同步)时。
  • 作为既定安全策略的一部分,有意轮换密钥时。

重新生成后,新的公钥将自动包含在下一次构建中。此前分发的构建使用新公钥将导致清单验证失败,因此必须部署更新。

双指纹无中断密钥轮换

点击 Generate Key Pair 按钮后,公钥不仅会保存在 OZeroSecurityConfig 中,同时识别相同密钥的 fingerprint 也会记录在 OZeroManifestTrustAnchor.cs 中。运行时将先验证这两个值是否匹配,然后再验证清单的签名。如果二者不匹配,它将不再信任该清单。

当前密钥与旧密钥

OZeroManifestTrustAnchor.cs 中,您可以记录当前使用的密钥 fingerprint,以及仅在密钥轮换期间临时允许的旧密钥 fingerprint。平时只使用当前密钥,仅在更换新密钥时才会暂时填入旧密钥槽。通过这种方式,您可以减少在部署新版本期间现有构建用户突然验证失败的情况。

密钥轮换步骤

  1. 备份当前指纹。 打开 Assets/OZeroSDK/Scripts/Security/BuildIntegirity/OZeroManifestTrustAnchor.cs,复制 ExpectedPublicKeyFingerprintHex 的值。
  2. 注册到旧密钥槽。 在同一文件中,将第 1 步复制的值粘贴到 PreviousPublicKeyFingerprintHex 中并保存。
  3. 生成新密钥对。OZeroSecurityConfig 检查器中点击 Generate Key Pair,在弹出的确认窗口中继续操作。新公钥与新指纹将被自动更新。
  4. 部署新版本构建。 新的清单将使用新的私钥进行签名。
  5. 预留更新过渡期。 在新旧版本共存期间,需同时允许使用旧密钥。对于在线游戏,通常设定在 1 至 4 周左右。
  6. 清空旧密钥槽。 当旧版本的使用量足够低后,将代码改回 PreviousPublicKeyFingerprintHex = "",再次发布后轮换即告完成。
为何需要旧密钥槽

在实时在线服务中,并非所有用户都会在同一时间迁移到新版本。通过暂时保留旧密钥槽,可以减少新旧构建共存期间引发的清单验证失败。经过充分的过渡期后,再清空旧密钥槽即可。

故障排除

大部分与密钥相关的错误,均因私钥、OZeroSecurityConfig 中的公钥以及 OZeroManifestTrustAnchor 中的 fingerprint 三者不匹配所致。请按顺序检查以下项目。

症状 1 — 部署构建在运行后立即退出

ExpectedPublicKeyFingerprintHex 可能为空。解决:在 Unity 编辑器中运行一次 Generate Key Pair,然后进行 Clean Build。若值已填写但仍失败,可能是因为以前构建的产物残留,请清空构建文件夹后重新构建。

症状 2 — 构建刚开始前就提示 "no manifest signing public key is configured" 并中断

OZeroSecurityConfig.Integrity.ManifestSigningPublicKey 为空时触发。解决:在检查器中打开 OZeroSecurityConfig,在 Build Integrity 选项卡中点击 Generate Key Pair,再重新构建。

症状 3 — 运行时日志:"Manifest signing public key does NOT match the pinned trust anchor fingerprint — APK appears to have been repacked with attacker-controlled keys"

OZeroSecurityConfig 的公钥与代码中记录的 fingerprint 不匹配。解决:在 Unity 编辑器中运行 Tools → OZero → Validate Manifest Signing Keys。如果工具报告不匹配,请使用 Generate Key Pair 重新签发密钥对后再重新构建。如果该提示出现在分发版中,则还应检查 APK 或可执行文件是否被重新打包过。

症状 4 — 轮换后,老构建版本的用户遭遇清单验证失败

极有可能是因为在部署新密钥之前,未将旧 fingerprint 填入 PreviousPublicKeyFingerprintHex 中。解决:发布一个将旧指纹放入 Previous 槽的热修复版本,并在经过充分过渡期后再将其清空。

症状 5 — 本地构建成功,但 CI 构建时提示 "private key not found" 或使用错误的密钥签名

当 CI 机器上缺失 OZeroSigningKeys/manifest_private_key.pem 时发生。解决:将私钥保存在 CI Secret 中,并在 Unity 构建前恢复至相同路径;或者在 OZeroSecurityConfigManifest Signing Private Key Path 中指定 CI 可访问的安全路径。所有发版机器必须使用相同的私钥。

安装来源设置

限制游戏仅在通过已批准的商店或途径安装时才能运行。这有助于防止侧载或重新打包的 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) 安装。仅供内部测试时启用。
allowDetectionFailedboolfalse如果因 Android installer package 的提取失误或者 JNI 调取出错都任其通行游戏。发布版本建议将其闭锁。
allowUnknownSourcesboolfalse允许并非出自官方列表或自订许可列表中的源头途径。遇到必要对应地方商店时再行开启测试使用。
enableServerSync (Pro)boolfalse将探测所得的 installer package 送上 OZero 检查库校验并且建档,专属 Pro 级别。
customAuthorizedPackages List<string> 额外允许的安装程序包名称(例:com.yourcompany.launcher)。
reportViolationToCallback bool true 若检测到未被许可的安装来源,则向服务器发送报告。
logRawInstallerPackage bool true 在控制台中输出原始安装程序包名的日志。在开发阶段确定自定义商店的正确包名时非常有用。

Steam 防盗版配置

对在 Steam 发售的 PC 端查明 Steam 调用通道、App ID、权限情况、发布档设定。Standard 是局端核实,要想通过 Steam 取得牢固权证唯有依赖 Pro 级别之内的 Steam Attestation 来供给。

字段 Type 默认值 说明
Activate Steam Anti-PiracycheckboxOff启动 Steam 防盗版侦察的勾选位。有鉴于 Steam App ID 及铺货形态随项目多变,初始定作 Off。理应首先处 observe/QA 情境查其状,尔后针对发行端上紧限制。
expectedSteamAppIdint0项目的 Steam App ID 标识。设使之前倚仗除错用 AppID 480 则应在交差前必定修正为正当的 App ID。
requireSteamLaunchbooltrue断明是否借由 Steam 而非执行文件独立启航。需厘清研发内部自测操作与外发政策。
requireSteamApiInitbooltrue探察 Steam API 奠定起手成效。研发时期直接试玩因 Steamworks 配定生变有败局几率,决断还得以确实的 Steam 散播流为基准。
requireSubscribedCurrentAppbooltrue核准当前挂号的 Steam 主能否掌握此件及赋予进入权限。此是机内考核,至于服端盘查必得用 Pro 解决。
requiredDlcAppIdsList<int>在进行保护的 DLC 流程中,必须持有的 DLC App ID 清单。
blockSteamAppIdTxtInReleasebooltrue如果在发布构建中残留了开发用的 steam_appid.txt 文件,则作失败处理。此文件仅供本地开发确认使用,请勿向客户分发。
validateSteamApiDllHashboolfalse通过将 Steam API DLL 的哈希值与已知的 SHA-256 值进行比较来进行验证。
allowFamilySharing / allowFreeWeekend / allowTimedTrialbooltrue控制是否允许 Steam 家庭共享、免费周末以及限时试用权限状态。
enableServerSteamAttestation (Pro)boolfalse仅限 Pro。将 Steam 权限证据提交到 OZero 服务器,执行比客户端本地结果更强的服务器端所有权验证。

设备绑定设置

使用硬件指纹将游戏账号与首次运行的设备绑定。检测账号向其他硬件的转移。

商用运营目的

设备绑定作为 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 消耗令牌后,重启应用或重新进入保护初始化流程,即可重新注册当前的硬件指纹。
请勿将设备绑定宣传为完美的硬件身份保证。在 OS 重置、隐私设置更改或硬件更换后,平台标识符有可能会改变。在商业在线游戏中,应将其作为提高绕过代价的运营控制层,配合 Build Integrity、Injection、SpeedHack、Install Source 及服务器验证一起使用。
字段 Type 默认值 说明
Activate Device Binding bool true 激活 Device Binding 模块。
hardwareChangeTolerance int (0–3) 1 触发违规前允许的硬件组件变更次数。0 = 严格(不可更改),3 = 宽松(主要硬件更换)。1 属于允许小幅度 OS 或固件更新的级别。
storageKey string "ozero_dfp" 存储加密设备指纹的 PlayerPrefs 键。仅在与游戏内现有键冲突时才需修改。
enableServerSync bool false 将设备指纹与后端服务器同步,以进行服务器端验证。
maxDevices (Pro)int0这是一个记录预期服务器端设备限制的文档值。实际限制由服务器许可证记录强制执行,仅更改此本地值并不能提升运营限制。

速度黑客检测器设置

通过将 Unity 的 Time.realtimeSinceStartup 与原生平台计时器,甚至可选的受信任 Web 时间源进行对比,以检测时间缩放操控 (速度黑客)。

字段 Type 默认值 说明
Activate Speed & Time Hack bool true 激活 Speed Hack Detector 模块。
autoStart bool true 游戏执行时自动开始检测。如果禁用,需通过 OZeroSpeedHackDetector.StartDetection() 手动启动。
checkInterval float 1.0 s 检测器对计时器进行采样比对的间隔(秒)。
requiredDetections int 3 触发违规前所需的连续异常样本数量。调高此值可减少在不稳定设备上的误报。
ratioTolerance float 0.15 用于确认 Unity 时间流速与原生计时器是否出现异常偏差的容差范围。请先使用默认值,并在实际游戏环境中确认有无误报后再作调整。
maxAllowedRatio float 4.0 用于快速捕获严重超出正常范围的时间流速的上限值。一般建议保留默认值。
detectSlowHack bool true 除加速工具外,还会检测用来放慢时间的工具(低于 1.0 减去容差的比例)。
enableTimeScaleDetection bool true 检测被内存修改器等通过编程方式设置的异常 Time.timeScale 值。
hackDetectMultiplier float 1.3 用于更快汇总高强度可疑信号的校正系数。值越低越敏感,请在 QA 后调整。
enableThreadTimerCheck bool true 使用后台线程计时器作为额外的参考时钟,使只影响主线程的黑客手段变得更困难。
useWebTimeValidation bool false 定期从 webTimeUrl 获取当前时间并与设备时钟比较,以检测设备级别的时间篡改。
webTimeUrl string 受信任的 Web 时间 API 终端 URL(必须返回 Unix 时间戳或 RFC 2616 Date 头部)。启用 useWebTimeValidation 时必填。
webSyncInterval float 15 s 与 Web 时间服务器同步的时间间隔(秒)。
webRatioTolerance float 0.15 用于判断设备时间与 Web 服务器时间差异的容差范围。请综合考虑网络延迟和地区环境,从默认值开始验证。
timeOffsetTolerance float 60 s 在触发违规之前允许的设备和 Web 服务器之间的绝对时钟偏移量(秒)。
focusIgnoreTime float 4 s 应用重新获得焦点后忽略检测的秒数。可防止操作系统暂停应用时产生的误报。
loadingGraceTime float 6 s 用于在应用启动或场景加载等可能出现正常延迟的阶段,减少误报的初始缓冲时间。
lagSpikeIgnore float 0.5 s 帧时间差超过此值的样本将被丢弃(视为实际的卡顿,而非时间操控)。
buildFailIfTimeScaleTamperedbooltrue一种保护措施,如果受保护的代码疑似在策略之外更改 Time.timeScale,则在构建/验证阶段触发失败。
timeScaleTamperExemptionsList<string>将诸如暂停或子弹时间等有意更改 Time.timeScale 的类/方法模式注册为例外。
webTimeUrlsstring[]您可以指定多个受信任的时间端点。要求有多个成功的响应以提高 Web 时间的可靠性。
minSuccessfulEndpointsint2使用多个 Web 时间端点时必须成功响应的最小数量。
maxConsecutiveFailuresint6决定连续多少次 Web 时间查询失败将触发不可用策略。
onWebTimeUnavailableenumWarnOnly无法访问受信任的 Web 时间时应用的策略。在测试网络环境后请提升至严格模式。
detectTimeHackbooltrue除速度比率检查外,还开启设备时钟和 Web 时间操控检查。
webSyncJitterPercentfloat20%为 Web 时间同步周期添加随机偏差,使得查询时机难以预测。
sustainedLagThresholdfloat0.15用于将反复出现的慢帧分类为持续卡顿而非作弊证据的标准。
sustainedLagGraceDurationfloat3 s在判断为持续卡顿或加载延迟期间,用于减少误报的缓冲时间。
overloadStrictMultiplierint3在反复出现异常时序条件后,更严格地应用 overload 处理标准的乘数。
enableRemoteSpeedHackConfig (Pro)boolfalse允许通过 Pro 服务器策略远程调整部分 Speed & Time Hack 阈值。
remoteSpeedHackConfigIntervalfloat300 s获取新远程 Speed & Time Hack 设置的周期。
enableSignedServerTime (Pro)boolfalse如果可用,使用签名的服务器时间,防止伪造无签名响应。

防止长时间加载时 Watchdog 意外终止应用

OZero 的原生 Watchdog 会通过定期 heartbeat 确认 Unity 主线程是否仍在正常运行。在发布构建中,非 Android 平台大约使用 6 秒 heartbeat deadline,Android 在启动宽限后大约使用 10 秒 deadline。如果在该时间内没有收到 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);
}
注意:不要把 Loading Grace 当作变通手段使用。

Loading Grace 只会有限延后 Watchdog deadline。不要把它滥用为无限循环或 keep-alive 机制,用来绕过正常监控并让应用持续运行。

如果一个大型阻塞操作可能超过最大 grace window,请把工作拆成多个较小的 chunk,并按步骤处理。

或者使用 Unity 的 async/await、Coroutine 等异步加载流程,避免主线程长时间完全阻塞。

物理黑客检测器

组件方式 — 不包含在 OZeroSecurityConfig 中。
OZeroPhysicsHackDetector 专为需要为每个玩家对象设置独立检测阈值的多人游戏/MMORPG 游戏而设计。将组件直接添加到玩家预制件上,并在检查器中为每个对象配置设置。在生成逻辑中调用 Initialize(playerId) 来启用检测。
字段 Type 默认值 说明
maxAllowedSpeed float 15 u/s 允许的最大移动速度(Unity 单位/秒)。请根据游戏中允许的合法最高玩家速度进行微调。
distanceTolerance float 2.0 u 用于补偿网络延迟或物理计算误差的额外距离容差。
obstacleLayer LayerMask 代表墙壁和障碍物的层遮罩。用于通过射线投射检测穿墙行为。
checkInterval float 0.05 s 物理验证器对玩家位置和速度进行采样的频率(秒)。
maxDeltaTimeCap float 0.1 s 用于距离计算的增量时间上限。可防止因卡顿导致非法传送被掩盖。

注入检测器设置

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

字段 Type 默认值 说明
Activate Injection & HookingcheckboxOn启用 Code Injection Detector 模块。检测逻辑完全在 Native C++ 层运行,无需额外配置。
enableServerWhitelist (Pro)boolfalse从服务器下载信任模块列表,以允许获批的覆盖层或合作伙伴模块。
serverWhitelistRefreshIntervalfloat0 s服务器管理的白名单策略更新周期。如果为 0,则仅在启动或策略初始化时更新。
requireSignerForNativeWhitelistboolfalse对原生白名单项目不仅要求文件哈希,还要求签名者识别信息。保护更强,但需要干净的模块签名数据。
enableRemoteInjectionConfig (Pro)booltrue允许通过 Pro 服务器策略远程调整 Injection 检查阈值和检查配置。
remoteInjectionConfigIntervalfloat300 s获取新远程 Injection 策略的周期。
enableWindowsModuleIdentityScanbooltrue扫描已加载的 Windows 原生模块,并将哈希/签名者信息与信任策略进行比较。
scanIntervalSecondsfloat1 sInjection 检查的基本执行周期。
windowsModuleIdentityScanIntervalSecondsfloat5 s相对繁重的 Windows 模块识别扫描的独立周期。
scanJitterPercentfloat20%为检查周期添加随机偏差,使得检查时机难以预测。
enableExecutablePrivateMemoryScanbooltrue检测 injected shellcode 或运行时 patcher 经常使用的可执行私有内存区域。
enableInlineHookScanboolfalse检测已知代码入口点的可疑内联补丁。这是在 Strict 模式下经过兼容性测试后开启的项目。
enableThreadStartAddressScanbooltrue检查线程启动地址是否位于信任模块之外的可疑位置。
enableExternalProcessHandleScanbooltrue检测外部进程是否持有游戏进程的可疑句柄。
detectionConfidenceThresholdint70综合多个 Injection 信号时,确定为违规所需的最低置信度分数。
enableWebRuntimeTamperScan (WebGL)booltrue仅适用于 WebGL 的轻量级浏览器篡改检查。因其不是 Native C++ 保护,故仅作为辅助信号使用。
WebGL probesboolstrue控制在 WebGL 构建中的 DevTools、时钟操控、网络 Hook、WASM Hook、存储 Hook 以及加密 API Hook 检查。

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 游戏内变量保护(Secure Types)

Secure Types 会将常规 C# 变量类型替换为加密类型。因为值存储在 Native C++ 堆中,诸如 Cheat Engine 的工具即使扫描内存也无法找到。只需更改类型名称,其余代码即可照常运行。

Unity Inspector showing OZeroSV_Int and OZeroSV_Float fields

示例代码

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 后
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
Secure Types 使用 OZeroSDK.Security 命名空间。请在声明该类型的脚本中添加 using 指令。

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

OZeroSecurityEventModulationType 以及 OZeroAbortCode 的完整列表已记录在 API 参考中。

如果 evt.WillAbort 为 true,按照当前响应策略,应用将在回调之后退出。在这种情况下,请只执行简短的自定义 analytics flush 或是保存操作。
检测事件的分发流程

当检测到威胁时,OZero 会首先应用 SDK 基础的响应策略,并将同样的安全事件派发给项目中注册的回调和 Inspector 事件。如果希望直接显示警告画面或留下服务器日志,请注册回调。关于应用是否退出,可以通过 Config Dashboard 的 Response 设置以及 evt.WillAbort 的值来确认。

下一步

现在 OZero 的核心防线已经在为您保驾护航了。翻查文档,全面探究每个接口,类、或控制选项的真谛。