Table of Contents

Code integrity and platform helpers

Anti-cheat solutions work best when they are part of a broader hardening strategy. Use the tools below to make reverse engineering harder, validate that the shipped code matches your trusted build, and leverage platform APIs for additional safety nets.

Harden your builds

ACTk does not obfuscate or encrypt your managed assemblies on its own. Combine it with additional hardening layers to slow down reverse engineers and keep managed injections out of your builds.

  • Prefer IL2CPP over Mono so your scripts are compiled into native binaries. IL2CPP removes the intermediate IL that dnSpy and similar tools rely on, blocks managed assembly injection, and forces attackers to use native disassemblers instead.
  • Remember that IL2CPP still emits metadata for reflection. Attackers can recover class and method names with tools such as IL2CPP Dumper. Run a Unity-aware obfuscator before the IL2CPP step so symbol names become meaningless in the generated metadata.
  • Evaluate native protectors when your threat model includes professional cheat makers. Solutions like Denuvo or VMProtect add runtime checks, while Unity-focused tools such as Mfuscator can encrypt IL2CPP metadata and add additional anti-tamper layers.
  • Use CodeHashGenerator to confirm the shipped build matches the hashes produced in CI. Pair the client-side check with server validation for sensitive game flows.

Code hash pipeline

ACTk validates build integrity by generating hashes both in the editor and at runtime. This system helps ensure your shipped code matches your trusted build and hasn't been tampered with.

Note

Only Android and Windows PC builds are supported so far.

How hash generation works

Hash generation produces two types of hashes:

  • Per-file hashes: Individual hashes for each file in the build
  • Summary hash: A single hash derived from all per-file hashes

Both editor (via CodeHashGeneratorPostprocessor) and runtime (via CodeHashGenerator) operations produce these same hash types, though summary hashes may differ in some cases (e.g., Android App Bundle splits with platform-specific files).

Generate trusted hashes during builds

CodeHashGeneratorPostprocessor hooks into the build pipeline and produces reference hashes for each generated file. Enable Generate code hash on build completion in the settings window to automatically run it after every build.

You can also manually calculate external build hashes using:

  • Menu item: Tools > Code Stage > Anti-Cheat Toolkit > Calculate external build hashes
  • Code: CodeHashGeneratorPostprocessor.CalculateExternalBuildHashes(buildPath, printToConsole)

Validate builds at runtime

CodeHashGenerator computes hashes on the player device using two approaches:

Async approach (recommended):

// Note: This must be called from an async method
var result = await CodeHashGenerator.GenerateAsync();
if (result.Success)
    Debug.Log($"Summary Hash: {result.SummaryHash}");

Event-based approach:

CodeHashGenerator.HashGenerated += OnHashGenerated;
CodeHashGenerator.Generate();

private void OnHashGenerated(HashGeneratorResult result)
{
    if (result.Success)
        Debug.Log($"Summary Hash: {result.SummaryHash}");
}
  1. Compare summary hashes first - if they match, the build is likely intact
  2. If summary hashes differ, check per-file hashes against your pre-generated whitelist
  3. Treat unknown hashes as build alteration triggers while ignoring absent files
  4. Implement server-side validation for maximum security - send hashes to your server for comparison against trusted whitelist

CI/CD integration

For CI environments (like GitHub Actions) that don't properly wait for build completion:

// Use synchronous generation instead of relying on HashesGenerated event
CodeHashGeneratorPostprocessor.CalculateExternalBuildHashes(buildPath, printToConsole);

Platform considerations

  • Android App Bundles: Summary hashes may differ per device split due to platform-specific files
  • Per-file hashes: Should remain consistent between editor and runtime builds
  • Server validation: Recommended for production use to prevent client-side tampering

For detailed API reference and examples, see CodeHashGenerator API documentation.

Example implementations

  • Basic usage: See GenuineChecksExamples.cs in Examples/API Examples/Scripts/Runtime/UsageExamples/
  • Advanced validation: See GenuineValidatorExample.cs in Examples/Code Genuine Validation/Scripts/Runtime/
  • DOTS integration: See UIActionSystem.cs in Examples/DOTS ECS Examples/Scripts/UI/Systems/

App installation source validation

AppSourceValidator (namespace CodeStage.AntiCheat.Genuine) tells you which store the app was installed from, with one API that works on both Android and iOS. The common use is to branch a single binary on its install source, for example to pick which In-App Purchase SDK to load or to flag installs from outside your trusted stores.

using CodeStage.AntiCheat.Genuine;

if (AppSourceValidator.IsInstalledFromOfficialStore())
{
    // App Store on iOS, Google Play on Android
}
else if (AppSourceValidator.IsInstalledFromEpicGamesStore())
{
    // Epic Games Store on either platform
}
else
{
    // a sideloaded build, another marketplace, or not resolved yet
}

GetAppSource() returns the full picture as a never-null AppSourceInfo: the platform, the raw installer or marketplace id, IsOfficialStore, and the platform-specific Android / Apple enums.

var info = AppSourceValidator.GetAppSource();
Debug.Log($"{info.Platform}: official store = {info.IsOfficialStore}, id = {info.RawSourceId}");

Per-platform details

When you need more than the cross-platform view, the typed getters return false off their platform, so a missed check can't be mistaken for a real result:

// Android: installer package + detected store
if (AppSourceValidator.TryGetAndroidSource(out var android))
    Debug.Log($"{android.DetectedSource} (installer: {android.PackageName})");

// iOS: marketplace bundle id + signing environment
if (AppSourceValidator.TryGetAppleSource(out var apple))
    Debug.Log($"{apple.DetectedSource} (env: {apple.Environment})");
  • Android (AndroidAppSource, resolved synchronously from the installer package): Google Play, Amazon, Huawei AppGallery, Samsung Galaxy Store, Epic Games Store, the on-device package installer (PackageInstaller, a sideloaded APK), or Other.
  • iOS (AppleAppSource): App Store, Epic Games Store, other alternative marketplaces, TestFlight, Web Distribution, Xcode builds, and enterprise/education distribution.

iOS specifics

  • Naming a marketplace needs iOS 17.4+ (MarketplaceKit AppDistributor). Below that ACTk falls back to the StoreKit transaction environment (iOS 16+), then the legacy receipt name, which can only separate the App Store from TestFlight.
  • Resolution is asynchronous at launch, so an early call may report AppleAppSource.Unknown. Poll AppSourceValidator.IsResolved (always true off iOS) or query a bit later.
  • Alternative marketplaces exist only where Apple allows them, currently the EU; elsewhere installs report as the App Store.
  • ACTk's build postprocessor adds StoreKit.framework and weak-links MarketplaceKit.framework (Xcode 15.3+ / iOS 17.4 SDK) to the generated Xcode project, so there's no manual Xcode setup, and the app still launches on older iOS.
  • AppDistributor.current can misbehave on the Simulator, so test on a device.

Keep critical checks on the server

The detected source is a plain runtime value, not a signed one, so a jailbroken or rooted device can spoof it. It's fine for choosing an IAP SDK or showing a soft warning; back anything security-critical with a server-side check.

Android-only entry point

If you only ship to Android you can call CodeStage.AntiCheat.Genuine.Android.AppInstallationSourceValidator directly (GetAppInstallationSource(), IsInstalledFromGooglePlay()). AppSourceValidator wraps it, so prefer the cross-platform API unless you specifically want the Android-only one.

Reference and examples

Android screen recording blocker

AndroidScreenRecordingBlocker toggles the system-wide flag that prevents screenshots and screen recording on most stock ROMs. Use it to frustrate basic bots and discourage tool-assisted speed runs on non-rooted devices.

Basic Usage

AndroidScreenRecordingBlocker.PreventScreenRecording();
AndroidScreenRecordingBlocker.AllowScreenRecording();

Advanced Features

  • State-based control: Block recording during sensitive gameplay, allow in menus
  • Platform detection: Use #if UNITY_ANDROID && !UNITY_EDITOR for Android-only code
  • Lifecycle management: Always allow recording when app is paused or destroyed

For detailed API reference, see AndroidScreenRecordingBlocker API documentation.

Example implementation

See AndroidExamples.cs in Examples/API Examples/Scripts/Runtime/UsageExamples/ for complete usage examples.

Tip

Blocking is best-effort. Custom ROMs and rooted devices can bypass the flag, and the app preview disappears from the Android task switcher while prevention is active.