DEV Community

Zero Heartbeat
Zero Heartbeat

Posted on Originally published at delta1labs.com

Watermarking .NET assemblies to trace who leaked a build

Obfuscation answers the question "can someone read my code?" It does nothing for a different and often more painful question: "someone leaked my software — who?" When a build you sold to one customer shows up on a forum, a file-sharing site, or a competitor's machine, obfuscation cannot point back at the source. Watermarking can. A watermark is a covert, per-licensee marker baked into each copy so that a leaked assembly can be traced to the account it was issued to. It is a different tool for a different job, and it pairs naturally with both obfuscation and licensing.

Attribution, not confidentiality

It helps to be precise about what a watermark is for. Obfuscation raises the cost of understanding your code. A watermark raises the cost of leaking it by making the leak attributable. The two are orthogonal: an obfuscated build with no watermark is unreadable but anonymous — a copy on a torrent tells you nothing about who put it there. A watermarked build that isn't obfuscated is perfectly readable but traceable. You want both: hard to understand and traceable back to the licensee if it escapes.

That reframing matters because it changes the success criterion. Obfuscation "works" if an attacker gives up trying to read the code. A watermark "works" if, given a leaked copy, you can reliably say this build was issued to account X. Nothing about the watermark needs to stop the leak from being usable — it needs to make the leaker identifiable, which is a deterrent in itself once customers know builds are individually marked.

What a good watermark needs to be

Not every "hidden string" is a watermark. A usable one has four properties:

  • Unique per licensee. Each customer's build (or each activated copy) carries a different marker — typically a per-account identifier, or an opaque token that maps to one in your records. Two customers must never share a marker, or attribution is lost.
  • Covert. It should not be an obvious CustomerId = "12345" string sitting in the metadata, which is the first thing anyone would find and delete. A good marker is not visibly a marker — it blends into structures that normally carry noise.
  • Robust to casual handling. It must survive the ordinary things that happen to a file in transit: copying, zipping, renaming, moving between machines. It does not need to survive a determined, watermark-aware attacker (nothing does), but it must not fall out from simply being passed along.
  • Deterministically verifiable. Given a suspect copy, you must be able to extract the marker mechanically and match it against your issuance records with no guesswork. Attribution that relies on "it looks like theirs" is not attribution.

The tension is between covert and robust: the more places you redundantly encode the marker, the harder it is to remove, but the more surface there is to notice. The practical answer is redundancy in several unobtrusive locations, so stripping one copy doesn't destroy the mark.

Where the mark can live

A .NET assembly has many places that tolerate per-build variation without changing behaviour, which is exactly where a watermark belongs. The point is not any single hiding spot but that the marker is woven into structures that normally vary from build to build, so its presence isn't conspicuous:

// The marker is derived, not a plaintext ID — an opaque value that maps
// back to a licensee only through your issuance records.
// e.g. marker = HMAC(issuanceSecret, licenseeId)  → a 128-bit token
Enter fullscreen mode Exit fullscreen mode

The guiding idea is to encode that opaque token into incidental, behaviour-neutral parts of the build rather than into a field that screams "identity." Because the token is derived rather than a literal account number, even someone who finds one copy of it learns nothing about the scheme or other customers, and it only resolves to a licensee through records only you hold. A well-designed obfuscator can carry the marker through its transforms so that watermarking and protection happen in one pass rather than as a bolt-on step that an attacker can diff against an unmarked build.

Verifying a leak

The workflow when a suspect copy surfaces is mechanical, which is the whole point. You run the extraction over the file, recover the embedded token, and look it up in the issuance records that map tokens to accounts. Because the token was derived deterministically (for example as an HMAC over the licensee id with a secret only you hold), you can also re-derive the expected token for any account and confirm the match, rather than trusting a lookup table alone. The result is a defensible statement — this binary carries the marker issued to account X on date Y — not a hunch.

This is also where watermarking and licensing reinforce each other. If each activation already binds a copy to a licensee, the activation identity is a natural seed for the watermark, and a leaked build's marker lines up with the same account your licensing records already track. Protection, activation, and attribution end up telling one consistent story about where a build came from.

Honest limits

A watermark is a deterrent and a tracing tool, not DRM. Three limits are worth stating plainly. First, it does not prevent anything — a leaked build still runs; the watermark only tells you whose it was. Second, it is not unremovable: an attacker who knows the marker exists, knows where it lives, and is willing to spend effort can strip or corrupt it, which is why redundancy and covertness matter and why you should not oversell it. Third, attribution is only as trustworthy as your issuance records and the secrecy of your derivation key; if those leak, so does the scheme's value.

Within those limits it earns its place. Most leaks are not sophisticated de-watermarking operations — they are a licensee quietly handing a build to someone who shouldn't have it, often not realizing the copy is individually marked. A covert, redundant, deterministically-verifiable watermark catches exactly that case and, once customers know builds are marked, discourages it in the first place. Obfuscate so the code is hard to read, watermark so a leak has a name, and license so each copy is bound to an account — three layers, three jobs, one coherent answer to "what happened to my software after it left the building."

Top comments (0)