⏱️ Lectura: 10 min

Every image you generate with Microsoft Paint’s local artificial intelligence carries a hidden 16-byte unique identifier that you never authorized and cannot turn off.

📑 En este artículo
  1. TL;DR
  2. What happened
  3. Context and history
  4. How the invisible watermark works
  5. How to check it yourself
  6. Impact and privacy
  7. What’s next
  8. Frequently Asked Questions
    1. What is a GUID and why does Paint use it as a watermark?
    2. Can I turn off Paint’s invisible watermark?
    3. Does this also affect the Windows Photos app?
    4. What file formats preserve the C2PA watermark?
    5. Is Paint’s image generation really local?
    6. How can I check if an image has C2PA metadata?
  9. References

A reverse engineer going by Xusheng Li documented how Paint and the Windows Photos app turn that GUID into an invisible watermark embedded directly in the pixels of every image, even when generation happens entirely on the device, without touching the cloud.

TL;DR

  • Microsoft Paint generates images with AI in the cloud and also locally, using its own ONNX models.
  • Before generating, Paint sends the prompt to a remote Microsoft server for moderation.
  • The server returns, along with the moderated prompt, a GUID that identifies the request.
  • Paint embeds that GUID as an invisible watermark in the pixels via WmkWriteWatermark in Watermarker.dll.
  • The invisible watermark is independent of the visible Copilot logo: turning off one does not turn off the other.
  • The payload must be exactly 16 bytes long; if the write fails, Paint turns the entire generation into an error.
  • On Copilot+ PCs inference runs on the local NPU, but prompt moderation still travels to a server.
  • Microsoft already states that Paint adds C2PA metadata and only allows saving in PNG, JPEG, GIF, or the .paint format.

What happened

The investigation started as personal curiosity. Xusheng Li had previously audited poorly documented Windows features, like UCPD and WHESCVC, and wanted to understand how Paint generates images with AI. He expected to find a simple call to a cloud API. Instead, he connected Binary Ninja MCP to a code agent (Codex) and discovered that Microsoft packages full AI models inside the app itself, in the folder C:\Program Files\WindowsApps\Microsoft.Paint_11.2605.71.0_x64__8wekyb3d8bbwe\PaintApp\.

There, four files with the .onnxe extension appear: seg.onnxe (23.1 MB), inseg_enc.onnxe (28.0 MB), inseg_dec.onnxe (16.5 MB), and mager.onnxe (302.4 MB), as detailed in the original investigation. The format was already partly known: applying an XOR with the string “Microsoft_2023” against the file produces a valid ONNX model that passes the format’s standard verification.

While tracing Paint’s calls, Li found Watermarker.dll, a 1.67 MB file. He already knew Paint has an option to add a visible Copilot logo in the corner of the generated image, so the file size caught his attention: that feature alone doesn’t justify such a large binary. The final trigger was Claude Code’s announcement about text watermarks, which led him to wonder whether Paint did something similar but hidden in the pixels.

Microsoft Paint folder with encrypted ONNX models and Watermarker.dll
Watermarker.dll weighs 1.67 MB: too much for just a visible logo. Foto de Tim Graf en Unsplash

Context and history

The discovery of the .onnxe format wasn’t entirely new: the community already knew it was solved with a simple XOR against the key “Microsoft_2023”. What Li contributed was finding the rest of the logic: segapi.dll stores a small key registry where version 1.0.80-main uses that literal string, but version 1.0.81-main already rotates to an alphanumeric key of 4,096 bytes. The encryption algorithm never changed, only the key, suggesting Microsoft treats this as light obfuscation rather than real cryptographic protection.

The broader context is the race to mark AI-generated content. The Coalition for Content Provenance and Authenticity (C2PA), backed by Adobe, Microsoft, Intel, and other companies, defines a metadata standard for declaring an image’s origin. Microsoft already publicly acknowledges that Paint adds C2PA metadata to AI-generated images, which is why it limits saving those images to formats that preserve that metadata: PNG, JPEG, GIF, and the native .paint format. What isn’t publicly documented, according to the investigation, is the second layer: the GUID embedded pixel by pixel.

How the invisible watermark works

The full path, from the moment you hit “generate” until the image is saved to disk, passes through several layers of the app before reaching the final file:

flowchart TD
A["CocreatorViewModel::GenerateImageAsync"] --> B["StableDiffusionHelpers::GenerateAsync"]
B --> C["ImageGenerator on the local NPU"]
C --> D["Safety and moderation checks"]
D --> E["Paint::AI::AddWatermark"]
E --> F["Watermarker.dll: WmkWriteWatermark"]
F -->|"success"| G["Final image with embedded GUID"]
F -->|"failure"| H["Generation turns into an error"]

CocreatorViewModel::GenerateImageAsync calls Paint::AI::StableDiffusionHelpers::GenerateAsync, which in turn invokes Microsoft.ImageCreation.ImageGenerator. That’s where inference happens: on devices with an NPU, the model runs locally using the .onnxe files described above. The result goes through a safety and moderation check before reaching Paint::AI::AddWatermark, which finally calls WmkWriteWatermark inside Watermarker.dll.

Reproducing the decryption of the older .onnxe format is simple with a short script:

with open("seg.onnxe", "rb") as f:
    data = f.read()

key = b"Microsoft_2023"
decrypted = bytes(b ^ key[i % len(key)] for i, b in enumerate(data))

with open("seg.onnx", "wb") as f:
    f.write(decrypted)

This script reproduces the minimal algorithm that segapi.dll uses for the version 1.0.80-main key: a byte-by-byte XOR against the string “Microsoft_2023”. The result is a standard .onnx file.

The most revealing part is the signature of the function that writes the invisible watermark, according to the binary analysis of the original code:

int WmkWriteWatermark(
    uint8_t* output_pixels,
    const uint8_t* payload,
    size_t payload_length,
    int width,
    int height,
    int stride,
    const uint8_t* input_pixels,
    int pixel_format) {

    if (payload_length < 16) return -6;
    if (payload_length > 16) return -5;

    std::vector<uint8_t> message;
    for (size_t i = 0; i < 16; i++) {
        message.push_back(payload[i]);
    }
    // the 16-byte GUID is encoded starting here, pixel by pixel
}

The function requires a payload of exactly 16 bytes, the size of a GUID, and uses two different error codes depending on whether there’s too much or too little content. Interestingly, the loop that copies the payload ignores the payload_length parameter and uses a fixed limit of 16 iterations regardless.

Signature of the WmkWriteWatermark function that requires a 16-byte payload
The payload must be exactly 16 bytes: the size of a GUID. Foto de Liam Briese en Unsplash

WatermarkWhat it containsCan it be turned off?Where it lives in the code
Visible (perceptible)Copilot logo overlaid in the cornerYes, with the “Never” setting in WatermarkSettingAddPerceptibleWatermark
Invisible16-byte GUID issued by the moderation serverNo setting turns it offWmkWriteWatermark in Watermarker.dll

ModelSizeNodesFunction
seg.onnxe23.1 MB1,094Image segmentation
inseg_enc.onnxe28.0 MB1,014Encoder, generates image_embeddings
inseg_dec.onnxe16.5 MB1,133Mask decoder
mager.onnxe302.4 MB15,284Main image generation model

⚠️ Heads up: turning off the visible Copilot logo in Paint’s settings does not turn off the invisible watermark. They are two separate features, controlled by separate code, and only one has a switch in the interface.

How to check it yourself

On Windows you can locate Paint’s encrypted models with PowerShell:

Get-ChildItem "C:\Program Files\WindowsApps\Microsoft.Paint_*\PaintApp\" -Recurse -Filter *.onnxe

That command lists the four .onnxe files described above. With the Python script from the previous section you can decrypt seg.onnxe and validate the result against onnx.checker.check_model() from the official ONNX library.

To check the C2PA metadata of an image you generated with Paint, use exiftool on any operating system:

# Windows (PowerShell)
.\exiftool.exe .\generated-image.png

# macOS (Homebrew)
brew install exiftool
exiftool generated-image.png

# Linux (apt)
sudo apt install libimage-exiftool-perl
exiftool generated-image.png

If the image has C2PA metadata you’ll see a JUMBF block or references to “C2PA” in the output. Watch out for the limitation of this method: exiftool reads metadata declared in the file container, not the 16-byte GUID that, according to Li’s investigation, lives encoded in the pixels themselves and not in a metadata chunk readable with standard tools.

Impact and privacy

The most direct implication is that “local generation” isn’t synonymous with “no network.” The prompt always travels to a Microsoft moderation server before the NPU draws a single pixel, and that server is what issues the GUID that ends up embedded in the image. For a user who explicitly chooses the local model with privacy in mind, that network dependency usually isn’t documented as clearly as the visible logo setting.

One question remains unanswered for now: it isn’t publicly confirmed whether Microsoft correlates that GUID, on the server side, with the account or session that requested it. The original investigation documents how the watermark is generated and written, but doesn’t go as far as proving what Microsoft does with that correlation once issued. It’s a real limitation of the analysis, not a confirmed fact, and it’s worth clarifying before drawing stronger conclusions.

💭 Key point: on Copilot+ PCs image inference runs on the NPU without leaving the device, but prompt moderation is still a round trip to a Microsoft server. “Local” describes where the model runs, not where the prompt ends up.

What’s next

It’s expected that privacy researchers and specialized media will replicate this analysis on the Windows Photos app, which according to the investigation itself shares the same watermarking infrastructure. Microsoft is also likely to eventually publish more explicit documentation about the invisible GUID, given that it already acknowledges using C2PA metadata in Paint. The underlying pattern is broader than a single product: between Google’s SynthID and Claude Code’s announcements about text watermarks, the industry is moving toward invisible provenance markers that users can’t control from a settings menu, even though they can control the visible ones.

📖 Summary on Telegram: See summary

Try it yourself: run the PowerShell command from the previous section on your own Paint installation and confirm whether your .onnxe files already use the rotated 4,096-byte key.

Frequently Asked Questions

What is a GUID and why does Paint use it as a watermark?

A GUID (globally unique identifier) is a 16-byte string that a system generates to identify something unambiguously. In this case, Microsoft’s moderation server issues a GUID for every image generation request, and Paint embeds it in the pixels of the resulting image as an invisible watermark.

Can I turn off Paint’s invisible watermark?

There’s no visible setting for that. The only control that exists in the interface is for the visible Copilot logo, which is a different code feature (AddPerceptibleWatermark) from the one that writes the invisible GUID (WmkWriteWatermark).

Does this also affect the Windows Photos app?

Yes. Xusheng Li’s investigation points out that Photos shares this remote moderation and watermarking logic with Paint, although the technical focus of the binary analysis was centered on Paint.

What file formats preserve the C2PA watermark?

Paint limits saving AI-generated images to PNG, JPEG, GIF, and its native .paint format, precisely because those are the formats that can retain the declared C2PA metadata.

Is Paint’s image generation really local?

Model inference can indeed run on the local NPU on a Copilot+ PC. But the prompt is always sent first to a remote Microsoft server for moderation, and that same server is what issues the GUID that ends up embedded in the image.

How can I check if an image has C2PA metadata?

With exiftool, running exiftool generated-image.png from Windows, macOS, or Linux, you’ll see a C2PA or JUMBF metadata block if the image includes one. The GUID embedded in the pixels, however, doesn’t show up with this tool.

References

📱 Enjoying this content? Follow @programacion on Telegram for daily tech content in Spanish: quick summaries, fresh content every day. @programacion

Imagen destacada: Foto de Shubham Dhage en Unsplash

Categories: Noticias Tech

Javier Alarcón

Infrastructure engineer specializing in networking, Linux systems, Kubernetes, and cloud architectures. Covers hardware, networking, observability, and engineering practices for production teams.

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.