Skip to content

Unique Obfuscation Identity System — per-build and per-project fingerprint elimination #69

Open

Description

@mcpolo99

Goal

Make every ConfuserEx build and every obfuscation run produce unique output that cannot be identified by generic deobfuscation tools or AV heuristics. Two levels of uniqueness that stack for maximum stealth.

The Problem

Every ConfuserEx-obfuscated assembly carries identical fingerprints — same namespace (System.Core.Internal), same magic constants (0x3dbb2819), same watermark (ProtectedByAttribute), same algorithm patterns. Tools like de4dot pattern-match these known values. AV heuristics flag the same byte sequences. Every user's output looks identical.

The Solution: Two-Level Uniqueness

Level 1 — Source Identity Script (uniquify.py)

A setup script that makes your ConfuserEx build unrecognizable as ConfuserEx. Run once per environment.

Targets:

Fingerprint Default Value Randomized
Runtime namespace System.Core.Internal Derived from seed
Runtime type names Constant, Resource, Compressor, etc. Derived from seed
Runtime DLL name System.Core.Runtime.dll Derived from seed
Product string "Protected" User's choice
Default magic constants 0x3dbb2819, 0x3ddb2819, etc. Derived from seed
Process name checks "dnspy" Expanded/obfuscated
Watermark attribute name ProtectedByAttribute User's choice or random

Script features:

  • --seed "phrase" — deterministic, reproducible
  • --dry-run — show changes without modifying
  • --test — build + run tests after modification
  • --restore — revert to defaults

Level 2 — Per-Project Obfuscation Identity (runtime)

Randomize cryptographic constants per obfuscation run. Works on stock or script-customized builds.

Targets:

Fingerprint Current After
Anti-tamper feedback constant 0x3dbb2819 hardcoded Derived from project seed
Compressor feedback constant 0x3ddb2819 hardcoded Derived from project seed
Hash constants 0x6fff61 / 0x5e3f1f hardcoded Derived from project seed
CFGCtx multiplier 0x21412321 hardcoded Derived from project seed
XorShift parameters 12/25/27 hardcoded Picked from valid set per seed
Prime moduli 0x143fc089 etc. hardcoded Picked from valid prime set per seed

Implementation: Convert hardcoded constants to Mutation.KeyI* placeholders. The MutationHelper system already injects per-run values — minimal new infrastructure needed.

Watermark Configurability

Currently hardcoded (always on or off via source edit). Should be project-level:

<!-- Disable watermark -->
<protection id="watermark" action="remove" />

<!-- Custom watermark -->
<protection id="watermark">
  <argument name="text" value="MyCompany Security" />
  <argument name="attributeName" value="SecurityStampAttribute" />
</protection>

Default: off (no fingerprint unless explicitly enabled).

How The Levels Stack

Setup de4dot AV heuristics Per-project analysis
Stock ConfuserEx Works Flags it Easy
Level 2 only Constant matching fails Reduced Needed per project
Level 1 only Name detection fails Fails Harder
Level 1 + Level 2 Completely fails Fails Needs reverse engineering per build AND per project

Out of Scope (Hard Fingerprints)

These require architectural redesign, not randomization:

  • JIT hook technique (hooking ICorJitCompiler::compileMethod)
  • Anti-dump PE header zeroing behavior
  • LZMA decompressor algorithm structure
  • Reference proxy DynamicMethod architecture

Documented for awareness but not part of this feature.

Implementation Order

  1. Watermark configurability — quick win, project-level on/off with custom text
  2. Level 2 — convert magic constants to mutation keys, per-run randomization
  3. Level 1 — the uniquify.py script for source-level identity change

Related Issues

Activity

  1. mcpolo99 commented on Jul 4, 2026

    @mcpolo99
    OwnerAuthor

    Progress update — Phases 1 & 2 landed on develop

    Two deterministic-identity changes are merged:

    Phase 1 — Watermark (opt-in + configurable) — merged in #97 (ee3a56c)

    • The ConfusedByAttribute watermark is no longer force-inserted. It's now a normal opt-in protection, default off, with text and attributeName parameters.
    • This removes the feat(ci): Add GitHub Actions CI, fix CodeQL, update Dependabot #1 surviving output fingerprint (the de4dot/AV signature that identifies the tool by name+version in the output assembly).

    Phase 2 — Level-2 constant randomization — merged in #98 (b2a44ff)

    • Anti-tamper feedback 0x3dbb2819 → random per run (Normal + Anti modes; injected via mutation key, validated by AntiTamper.Test).
    • CFG state multiplier 0x21412321 → random odd value per run (Constants protection; runtime CFGCtx ctor IL rewritten + obfuscator CFGState threaded, validated by 270_EnumArrayConstantProtection.Test + 193_ConstantsInlining.Test).

    Both round-trip through obfuscate → run → assert, so a broken constant-sync fails the tests immediately.

    Correction to the original fingerprint list

    • The watermark attribute is ConfusedByAttribute, not ProtectedByAttribute.
    • The runtime namespace is Confuser.Runtime, not System.Core.Internal.
    • Most "Level 1" runtime type/namespace fingerprints never reach the protected output — the renamer already sets injected helper types to Namespace="" + a random name per build. Level 1 is really about the tool binary, so it's reprioritized below Level 2.

    Remaining (this issue stays open)

    • Compressor feedback 0x3ddb2819 (needs Decrypt signature change + LZMA-test validation)
    • Compressor LCG constants 0x6fff61 / 0x5e3f1f
    • Prime moduli 0x143fc089 / 0x444d56fb / 0x8a5cb7 (curated valid-prime set)
    • XorShift shifts 12/25/27 and anti-tamper rotation shifts 5/3/7/11 (curated valid-triple sets)
    • Level 1 uniquify.py (source-level tool identity)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions