MS Paint and Photos inivisibly watermark even locally generated output with GUID
Reverse engineering reveals how Paint and Photos embed a server-issued GUID into the pixels of locally generated AI images. This research started with my curiosity about Paint. I recently had some success looking into less-explored Windows features like UCPD , WHESCVC , and I have long known that Microsoft added a bunch of AI features into the Paint app. I do not know if anyone actually uses Paint + AI to generate images, but I wanted to see how exactly the image generation works. Before I started, I expected that it simply called a remote API to do the image generation. However, after I set up Binary Ninja MCP with Codex and started the analysis, I soon realized that Microsoft actually shipped local models in Windows as part of Copilot. The Paint App is sitting in the following path (yes, they are all Windows Apps now): seg.onnxe 23.1 MB inseg_enc.onnxe 28.0 MB inseg_dec.onnxe 16.5 MB mager.onnxe 302.4 MB The format of seg.onnxe was previously known , i.e., when it is XORed with the string Microsoft_2023 , it becomes a normal ONNX file.
However, the format of the other three .onnxe files initially looked different. It turned out that Microsoft had not changed the algorithm, only the key. segapi.dll contains a small key registry: ps_enc_key.1.0.80-main -> "Microsoft_2023" ps_enc_key.1.0.81-main -> a 4,096-byte alphanumeric string After decryption, onnx.checker.check_model() works on all of them: While walking through these files, I found a Watermarker.dll : This is not super surprising to me, because while I interacted with the Paint app, I already discovered that it has a setting to embed a visible watermark to the image that it produces: The visible watermark is just a small Copilot logo at the bottom right of the image, which is totally normal. Then, out of nowhere, I decided to ask AI to analyze the DLL and see if it could also be embedding an invisible watermark. This is part of my intuition as a reverse engineer, because the file is 1.67 MB in size, which is unusually large for such trivial functionality (arguably, the visible watermark does not even require a separate DLL). Apparently, the recent Claude Code text-watermark announcement also played a role in prompting me to think about this possibility. To begin with, the visible watermark is added by Add Perceptible Watermark : CPBDoc::Save(...) | `-- perceptible-watermark save helper(bitmap, Watermark Setting) | +-- Watermark Setting::Never | `-- return the original bitmap | +-- Watermark Setting::Ask Every Time | `-- show the Yes / No confirmation popup | +-- No: return the original bitmap | `-- Yes: continue | `-- Always or confirmed Yes +-- Paint::AI::Get Perceptible Watermark Svg() `-- Paint::AI::Add Perceptible Watermark(bitmap, SVG stream) `-- composite the visible Copilot logo Then there is also a different Wmk Write Watermark function: Watermarker.dll !
Wmk Write Watermark ( output_pixels, payload, payload_length, width, height, stride, input_pixels, pixel_format); Tracing the call tree, we can see Wmk Write Watermark is called after a local Stable Diffusion image generation. And if Wmk Write Watermark fails, Paint converts the entire generation into an error rather than returning the image without it: Cocreator View Model::Generate Image Async(...) | `-- Paint::AI::Stable Diffusion Helpers::Generate Async(..., watermark Id, ...) | `-- Microsoft. Image Creation. Image Generator | `-- NPU-generated image result | +-- output safety/moderation checks | +-- Paint::AI::Add Watermark(bitmap, watermark Id) | | | `-- Watermarker.dll! Wmk Write Watermark(...) | | | +-- success: return the watermarked bitmap | `-- failure: turn generation into an error | `-- construct successful Stable Diffusion Result Then it is natural to ask what the incoming payload actually is. It quickly becomes apparent that it must be 16 bytes: if (payload_length 16 ) return - 5 ; It is funny to me that the code is using two different error codes when the payload is too short or too long.
โก Effects Interpreter
๐World Economy
- โถInvestors abroad often reprice their bets when this kind of news lands.
- โถThe ripples can spread across borders, nudging growth forecasts here and there.
๐๏ธLocal Economy
- โถLocal suppliers who import goods could pass on any change in costs.
- โถPrices at your local shops could feel a gentle, indirect squeeze from this.
๐ฆRates & Banks
- โถYour loan or mortgage rate is more likely to drift than to lurch here.
- โถBanks tend to wait and see before nudging the rates they offer.
โค๏ธHealth
- โถDay-to-day stress can creep up if this starts touching familiar routines.
- โถCommunity wellbeing might dip a little while people wait for clarity.
๐ทWealth
- โถLong-term savers usually ride out these small bumps just fine.
- โถAny hit to your money is more likely a ripple than a wave.
๐ Housing
- โถMortgage deals could edge around if lenders read the wider mood.
- โถBuyers and renters may notice only a gentle drift, if anything at all.