· 9 min read

Paint Generates Images on Your NPU. It Still Asks Microsoft for a GUID First, and Bakes It Into the Pixels.

Microsoft Paint's Cocreator feature generates images on your device's NPU. Before it generates anything, it sends your prompt to a Microsoft server for moderation, and that server returns a 16-byte GUID. Paint then encodes that GUID into the image pixels and signs the same value into the file's C2PA manifest. Xusheng Li, a software developer at Vector 35, published the reverse engineering on August 20, and The Register covered it this morning. The visible watermark setting in Paint has three options, Never, Always and Ask every time, and none of them touch this.

What the reverse engineering actually found

Li started by poking at Paint out of curiosity and found that Microsoft ships local Stable Diffusion models inside the app, stored as .onnxe files that are ONNX models XORed with a key. The interesting file was not a model. It was Watermarker.dll, at 1.67 MB, which is a lot of binary for what Paint documents as a small visible Copilot logo in the corner.

The visible logo is handled by a function called AddPerceptibleWatermark. Separately there is WmkWriteWatermark, and tracing its callers shows it runs immediately after local image generation. Its payload parameter has to be exactly 16 bytes, with distinct error codes for too short and too long. The wrapper in PaintAIManager.dll has a symbolized signature that gives the game away: the parameter is typed as winrt::guid.

The encoder builds an 18-byte message, a 0x4c marker followed by the GUID followed by a checksum byte, expands it to 144 bits, and requires each bit to be placed at least three times across selected image blocks. It needs the image to be at least 192 by 192 pixels. Li had a test harness call the function directly on a synthetic 512 by 512 image: 193,376 of the 262,144 pixels came back changed.

Working backwards from there, the GUID comes off the network. Before local generation runs, AIServices.dll posts the prompt and style to a paint-cocreator/moderate-prompt endpoint on an Azure Front Door host. The response carries four fields: revisedPrompt, promptGenerationId, watermarkId, and containsHumanReference. The watermarkId is what ends up in your pixels.

One more detail deserves attention. Paint sends the previous promptGenerationId as lastPromptGenerationId on the next moderation request. Successive generations in a session are explicitly chained together on the server side.

The GUID appears twice, and only one copy is easy to remove

Paint also attaches C2PA Content Credentials to the saved file. Li pulled the PNG apart and found an 18,979-byte caBX chunk sitting right after IHDR, containing a signed manifest. Inside it, a c2pa.soft-binding assertion names the algorithm com.microsoft.invismark.1 and records a value. That value is the same GUID that was quantized into the pixels.

This is the part people are going to gloss over, so I want to be blunt about it. C2PA calls this a soft binding on purpose. The whole design goal is that the identifier survives when the file-level manifest does not. Run the PNG through a converter, screenshot it, re-encode it, strip every metadata chunk you can find, and you have removed the manifest copy. The pixel copy is what the soft binding exists to preserve.

Microsoft cryptographically signed that assertion. The manifest declares the generator as Microsoft Responsible AI Provenance, the AI system as Azure OpenAI ImageGen, and the action as c2pa.watermarked.

Why Paint needs a local watermarker at all

The two generation paths explain the architecture. Image Creator runs in Microsoft's cloud, so watermarking and provenance packaging can happen server-side and Paint just receives a finished file. Cocreator on a Copilot+ PC runs inference on the NPU, so there is no server holding the pixels at the moment they are created. Paint has to do the embedding itself, which is why Watermarker.dll ships at all.

That also explains a behaviour I found genuinely revealing. If WmkWriteWatermark fails, Paint turns the whole generation into an error rather than handing you an unwatermarked image. Photos, which uses the same DLL for its Image Creator and Restyle Image features, logs the failure and appears to return the image anyway. Same component, two different judgments about whether the mark is mandatory.

There is a smaller tell in the save dialog. Saving an AI result from Paint offers PNG only, and after the result is applied to the canvas you get PNG, JPEG, GIF and Paint's own .paint. BMP, the format Paint has shipped since forever, is gone. The C2PA specification calls out BMP as unable to embed manifest data inline, so allowing it would drop the manifest on export.

What this means if you ship generated images

I generate a fair number of throwaway images for posts and product screenshots, and my working assumption for anything from a hosted tool has been that the vendor knows what I asked for. That assumption was already correct here, and the moderation call would be true even if there were no watermark. What is new is that the identifier travels with the artifact after it leaves the vendor's systems.

So the practical version. If you are producing images in Paint or Photos and shipping them as part of a product, marketing site, or client deliverable, every file carries a per-generation identifier that Microsoft issued and that is chained to your other generations in the same session. Whether that matters depends entirely on whether you would be comfortable with someone matching two images you published to the same account and session. For most indie work, that is a shrug. For anything where the pseudonymity of the output matters, it is not.

If you want out, the answer is the boring one The Register landed on: run an open weight model locally with open source tooling. Nothing about that is hard in 2026, and it removes the moderation round trip along with the GUID.

The honest take

I want to be careful not to overstate this, because the load-bearing claim is not proven. Li documented that a server issues the GUID and that the GUID lands in the pixels. He did not demonstrate that Microsoft maintains a mapping from GUID to user account, and nobody has. The company's own support pages do say it collects user and device identifiers alongside prompts for abuse prevention, which makes such a mapping plausible, but plausible is not the same as shown. Microsoft did not respond to The Register's request for comment before publication.

There is also a defensible reason for all of this to exist. The EU's Code of Practice on Transparency of AI-generated Content took effect on August 2, and requires signatories to mark AI content in a machine-readable way. Microsoft is a C2PA founder and went past the floor the rules set. The rules ask for a detectable mark. They do not ask for a prompt-specific identifier, and that gap is the actual story.

My criticism is narrow and I will stand behind it: this is a disclosure failure, not a scandal. Microsoft documents that Paint adds C2PA metadata. It does not explain that the metadata contains an identifier that is also physically present in the pixels, or that a "local" generation path still requires a network round trip that issues that identifier. Users who read the support page carefully would still not know. That is worth fixing, and it took a reverse engineer with a disassembler to surface it.

Author

Sources

Stay in the Loop

Get new posts delivered to your inbox. No spam, unsubscribe anytime.

Newsletter coming soon. Set PUBLIC_CONVERTKIT_FORM_ID in .env to activate.

Related Posts