Code Stage Documentation / Obfuscator

Code Stage Obfuscator manual

Code Stage Obfuscator renames types, methods, fields, properties and events in selected managed C# assemblies during a Unity player build. Configure it in Project Settings > Code Stage > Obfuscator.

Symbol renaming makes decompiled code harder to read. It does not encrypt strings, add anti-tamper checks or prevent reverse engineering.

Quick start

  1. Import the package and let Unity resolve the declared dependency, com.unity.nuget.mono-cecil 1.11.4.
  2. Open Project Settings > Code Stage > Obfuscator. In General, use the status-banner checkbox to disable obfuscation. Make a comparison build and check your game's normal behavior.
  3. Enable obfuscation and select Minimal for the first obfuscated build. New settings use Moderate by default. Leave all seven Default Protections enabled.
  4. Review Assembly Filter. Start with your own gameplay assemblies; exclude third-party code whose names must remain stable. See assembly selection.
  5. Add Skip Rules for custom reflection, save formats, external APIs and methods referenced by name in the Inspector or animation events.
  6. Build the player normally. Check Build Log and the Unity build log, then run the same checks as in the comparison build.
  7. Keep ProjectSettings/CodeStageObfuscator.json in source control. Archive the exact successful release's mapping privately with the player and its build evidence.

Obfuscation runs through Unity's build pipeline. Editor Play Mode does not exercise the obfuscated player assemblies.

Assembly selection

SettingBehavior
Included AssembliesAssembly names to process. The default includes Assembly-CSharp.
Auto-Include Asmdef AssembliesEnabled by default. Adds detected user assemblies to the included set. Disable it to select assemblies explicitly.
Excluded AssembliesRemoves names from the included and automatically detected set. Exclusions take precedence.
Detect AssembliesRefreshes the detected list shown in the settings window.
Effective Assembly ListThe resulting selection after automatic inclusion and exclusions.

Assembly names are matched case-insensitively. Surrounding whitespace and a trailing .dll are ignored. Assembly filters use names, not wildcard patterns. Only selected assemblies present in the player build are processed.

If the Unity build log says No matching player assemblies found. Skipping obfuscation., that build was not obfuscated. The settings window can still show an older report; check its timestamp.

Obfuscation tiers

TierRename candidates
MinimalPrivate members and types within the tier's visibility scope.
ModeratePrivate and internal members, and types outside the public/protected visibility scope.
AggressiveAll visibility levels, subject to skip rules, protections and structural exclusions.

Aggressive does not disable Skip Public API Surface. Constructors, virtual/abstract methods, explicit interface implementations and other structural cases can remain unchanged regardless of tier. Rename counts can therefore be lower than the number of symbols at a chosen visibility level.

Change one setting at a time, rebuild and test the player. A higher tier increases the set of candidates; it does not guarantee that every symbol will be renamed.

Skip rules and exclusion attributes

Each rule has a Kind, Pattern and enabled checkbox. Disabled and empty rules are ignored. Patterns match whole names case-insensitively: * matches zero or more characters, and ? matches one character.

KindMatchesExample
TypeType name or full type name.Game.SaveData
NamespaceThe namespace of a type or member.Game.Save; add Game.Save.* for child namespaces.
MemberMember name, full signature or qualified type/member name.Game.SaveData.Load or Game.SaveData.*

A Type rule preserves the type name; it does not preserve all its members. Add a Member rule such as Game.SaveData.* as well when both the type and its members must retain their names. A Namespace rule applies to both types and members in the matched namespace.

Alternatively, place this attribute on the type to exclude its name and members:

[System.Reflection.Obfuscation(Exclude = true, ApplyToMembers = true)]

Place [System.Reflection.Obfuscation(Exclude = true)] on an individual member to preserve that member. Use explicit rules or attributes for names stored in configuration files, loaded at runtime, or called from another assembly that is not processed.

Default protections

All seven switches are enabled by default. They preserve recognized patterns; they cannot discover every dependency on a name.

SettingPreservesLimit
Skip Unity CallbacksKnown callback names on MonoBehaviour and ScriptableObject types.Custom methods called by name need explicit rules.
Skip Serialized FieldsFields with SerializeField/SerializeReference, and selected public fields on Unity behavior or Serializable types.Custom save formats and name-based serializers need separate review.
Skip Unity Event MethodsKnown collision, trigger, mouse, audio and animator callback names.It does not discover every custom UnityEvent or animation-event target.
Skip Reflection-Dependent CodeRecognized names in simple string-based call patterns.Dynamic strings and arbitrary reflection need explicit rules.
Skip Networking RPCsMembers with recognized RPC/sync attribute names.Attribute recognition does not establish compatibility with an entire networking framework.
Skip Serialization AttributesSymbols with recognized Unity, Newtonsoft JSON, DataContract and XML serialization attributes.Recognition does not cover every serializer or configuration.
Skip Public API SurfacePublic/protected API names on externally visible types.Internal or private names used outside the processed assemblies can still need rules.

The reflection scan recognizes nearby string literals for calls such as SendMessage, Invoke, StartCoroutine and GetType. It does not evaluate runtime concatenation or strings loaded from assets, and it does not cover arbitrary GetMethod/GetField calls. Add rules even if a case looks similar to a recognized pattern.

Disabling a protection can change behavior that relies on original names. Rebuild and test after changing a protection, tier or assembly filter.

Build log

Build Log shows the last saved report: timestamp, renamed type/method/field/property/event counts, skipped count, warnings, errors and mapping path. Use Copy to Clipboard to copy it or Clear Log to remove it.

Read warnings about string references and add skip rules where a name must stay stable. Review the Unity build result separately. A report describes the obfuscation stage and may remain after a later player-build failure.

Private mappings

Each run writes ProjectSettings/CodeStageObfuscatorMappings/<run-id>.json. Its path appears in the Unity build log and Build Log. The file records original and obfuscated symbol identities, a unique build ID, Unity version, output path and assembly module IDs.

HashIdentifies
originalSha256The assembly before obfuscation.
obfuscatedSha256The assembly immediately after obfuscation. Unity can modify it later during stripping.
playerSha256The final DLL in a successful Windows managed player's Managed folder, when that output is available. It remains empty for unsupported layouts or outputs without a managed DLL.

Keep mappings private. They expose original names. Exclude ProjectSettings/CodeStageObfuscatorMappings/ from source control and public distributions. The package does not copy mappings into the player.

Archive the exact release's mapping with its build ID, final player and symbols. Where playerSha256 is populated, compare it with the retained player's DLL, for example with PowerShell Get-FileHash -Algorithm SHA256. When no final DLL hash is available, retain the build ID and mapping together with the release evidence. A trace alone cannot identify a mismatched mapping.

A mapping marked completed confirms that obfuscation finished, not that the entire player build succeeded. Losing the mapping loses that run's recorded original names.

Decode a managed stack trace

  1. Open Tools > Code Stage > Obfuscator > Decode Stack Trace, or click Decode Stack Trace in Build Log.
  2. Select the exact build's JSON through Mapping file or Browse. Check its build ID and retained assembly evidence.
  3. Paste the managed trace into Obfuscated trace and click Decode.
  4. Read the restored-frame count and result, then use Copy decoded trace if needed.

The decoder restores qualified type and method names in managed Type.Method(...) and Type:Method(...) frames. It retains generic arguments and existing source-location text. Unmatched or ambiguous frames remain unchanged; it does not guess between assemblies with identical obfuscated qualified names.

Native IL2CPP addresses require symbolication with that build's platform symbols before a managed-name workflow can help. This decoder does not decode native addresses, recover absent file/line information or rewrite arbitrary reflection strings.

Interrupted builds

The pipeline backs up target DLLs and existing symbol files before modifying them. Backup failure leaves originals intact. A later processing failure attempts restoration and reports the preserved backup directory; a failed restore requires manual recovery from that directory.

Each DLL replacement is atomic. Several DLLs and symbol files are not one transaction. Editor termination, an OS crash or a full disk can interrupt recovery.

Backups are temporary under Temp/ObfuscatorBackup_<timestamp>/ and are deleted after successful obfuscation, before the complete player build finishes. Keep separate release archives. Automatic recovery after an editor restart is not provided.

Compatibility and project validation

The package declares Unity 2022.3 as its minimum editor version. Validate your editor, scripting backend and target platform with the actual built player before relying on a release configuration.

System.Text.Json contracts are not validated, and its serialization attributes are not recognized by the attribute protection. Exclude contract types and their members with explicit skip rules or exclude their assembly. Android and iOS device compatibility remains unverified.

Networking attribute recognition is not a framework compatibility guarantee. Test the framework and version used by your project, including actual RPC/sync behavior after renaming. Addressables, AssetBundles and code loaded or referenced by name may need Type and Member rules or an excluded assembly; this manual makes no blanket compatibility claim for them.

Check scene and prefab behavior, Inspector callbacks, save/load, reflection, external API calls and any integration that depends on names. Keep a successful comparison build and test the obfuscated player on the target devices.

For support, use Code Stage contact or Discord. Include the Unity version, platform, backend and relevant build-log errors. Share private mappings only through a private support channel.