Nghiên cứu
Phát hiện Microsoft Paint và Photos tự động chèn watermark ẩn vào ảnh AI
(giờ Việt Nam)
Tóm tắt AI
Các kỹ sư phát hiện MS Paint và ứng dụng Photos tự động nhúng mã GUID ẩn vào pixel của ảnh tạo bởi AI. Mã này giúp Microsoft truy xuất nguồn gốc hình ảnh mà người dùng không hề hay biết.
Bản dịch AI

Kỹ thuật đảo ngược (reverse engineering) tiết lộ cách Paint và Photos nhúng một GUID do máy chủ cấp vào các pixel của hình ảnh được tạo bởi AI ngay trên thiết bị.
Tóm tắt (TL;DR)
Một cái nhìn đầy tò mò về Microsoft Paint
Nghiên cứu này bắt đầu từ sự tò mò của tôi về Paint. Gần đây, tôi đã đạt được một số thành công khi tìm hiểu các tính năng ít được khám phá trên Windows như UCPD, WHESCVC, và tôi đã biết từ lâu rằng Microsoft đã thêm hàng loạt tính năng AI vào ứng dụng Paint. Tôi không biết liệu có ai thực sự sử dụng Paint + AI để tạo ảnh hay không, nhưng tôi muốn xem chính xác quy trình tạo ảnh đó hoạt động như thế nào.
Trước khi bắt đầu, tôi cứ ngỡ rằng nó chỉ đơn giản là gọi một API từ xa để thực hiện việc tạo ảnh. Tuy nhiên, sau khi thiết lập Binary Ninja MCP với Codex và bắt đầu phân tích, tôi sớm nhận ra rằng Microsoft thực sự đã tích hợp các mô hình cục bộ vào Windows như một phần của Copilot.
Ứng dụng Paint nằm ở đường dẫn sau (vâng, tất cả chúng hiện nay đều là Windows Apps):
Và có bốn tệp mô hình rõ ràng với phần mở rộng.onnxe:
Định dạng của seg.onnxe đã được biết đến trước đó, nghĩa là khi nó được XOR với chuỗi Microsoft_2023, nó sẽ trở thành một tệp ONNX bình thường. Tuy nhiên, định dạng của ba tệp.onnxe còn lại ban đầu trông có vẻ khác biệt.
Hóa ra Microsoft không thay đổi thuật toán mà chỉ thay đổi khóa. segapi.dll chứa một registry khóa nhỏ:
Sau khi giải mã, onnx.checker.check_model hoạt động trên tất cả các tệp này:
Một hình mờ (watermark) hiển thị
Trong khi xem xét các tệp này, tôi tìm thấy một tệp Watermarker.dll:
Điều này không quá ngạc nhiên đối với tôi, vì trong khi tương tác với ứng dụng Paint, tôi đã phát hiện ra nó có một cài đặt để nhúng hình mờ hiển thị vào hình ảnh mà nó tạo ra:
Hình mờ hiển thị chỉ là một logo Copilot nhỏ ở góc dưới bên phải của hình ảnh, điều này hoàn toàn bình thường.
Sau đó, chẳng hiểu sao tôi quyết định yêu cầu AI phân tích tệp DLL này để xem liệu nó có nhúng thêm hình mờ ẩn hay không. Đây là một phần trực giác của tôi với tư cách là một kỹ sư đảo ngược, vì tệp này có dung lượng 1,67 MB, một kích thước bất thường đối với một chức năng đơn giản như vậy (thực tế là hình mờ hiển thị thậm chí không cần một tệp DLL riêng). Rõ ràng, thông báo về watermark văn bản của Claude Code gần đây cũng góp phần khiến tôi nghĩ đến khả năng này.
Một hình mờ ẩn
Để bắt đầu, hình mờ hiển thị được thêm vào bởi AddPerceptibleWatermark:
Sau đó còn có một hàm WmkWriteWatermark khác:
Truy vết cây gọi hàm (call tree), chúng ta có thể thấy WmkWriteWatermark được gọi sau khi tạo ảnh bằng Stable Diffusion cục bộ. Và nếu WmkWriteWatermark thất bại, Paint sẽ chuyển toàn bộ quá trình tạo ảnh thành một lỗi thay vì trả về hình ảnh mà không có watermark:
Sau đó, thật tự nhiên khi đặt câu hỏi payload (dữ liệu truyền tải) đầu vào thực sự là gì. Rất nhanh chóng, rõ ràng là nó phải có kích thước 16 byte:
Tôi thấy buồn cười là mã nguồn sử dụng hai mã lỗi khác nhau khi payload quá ngắn hoặc quá dài. Hàm này sau đó bỏ qua tham số độ dài và sử dụng một vòng lặp được mã hóa cứng (hard-coded) khi sao chép payload:
Chúng ta vẫn chưa biết payload 16 byte đó là gì, nhưng như sẽ thấy sau, đó chính là một GUID! WmkWriteWatermark không nhúng trực tiếp GUID. Trình bao bọc (wrapper) của nó xây dựng một thông điệp 18 byte (144-bit) như sau:
Bộ mã hóa cốt lõi làm tròn các kích thước hình ảnh khả dụng xuống bội số của tám và giữ 144 bộ đếm, mỗi bộ đếm cho một bit. Nó yêu cầu mỗi bit phải được đặt ít nhất ba lần.
Bản thân bộ mã hóa có thể được tóm tắt như sau:
Vòng lặp nhúng thực hiện các thay đổi lượng tử hóa nhỏ trên các khối hình ảnh được chọn. Nó chứa các phép toán ma trận 3x5 và một quy trình phân rã ma trận, đồng thời sử dụng các hằng số bao gồm 24.0, 0.25, 0.5 và 0.2. Điều này trông giống như một hình mờ kiểu SVD, thích ứng theo nội dung trong miền khối.
Tôi không phải là chuyên gia về hình mờ hình ảnh, nhưng có một điều rõ ràng – đây là một hình mờ ẩn! AI thậm chí đã viết một số mã để gọi trực tiếp hàm này và thử nghiệm nó với một hình ảnh BGRA 512x512 tổng hợp – 193.376 trên tổng số 262.144 pixel đã thay đổi sau khi thêm hình mờ.
Điều đó dẫn đến câu hỏi tiếp theo. Đầu vào của hình mờ đến từ đâu?
Một GUID từ quá trình kiểm duyệt prompt từ xa
Tại ranh giới của WmkWriteWatermark, payload chỉ là một con trỏ và một độ dài. Biết rằng nó phải là 16 byte là một manh mối, nhưng có rất nhiều thứ có thể là 16 byte. Do đó, tôi bắt đầu truy ngược lại các hàm gọi nó. Trình bao bọc trực tiếp trong PaintAIManager.dll có chữ ký được ký hiệu như sau:
winrt::guid, ôi chao! Bây giờ chúng ta biết rằng payload hình mờ 16 byte thực sự là một GUID.
Truy vết sâu hơn về nguồn gốc, chúng ta thấy rằng GUID thực sự đến từ một yêu cầu mạng. Trước khi Paint chạy mô hình tạo ảnh cục bộ, AIServices.dll gửi prompt và phong cách đến:
Yêu cầu này ở định dạng JSON và chứa ít nhất các trường sau:
Trình phân tích phản hồi mong đợi:
Phân tích tĩnh thì tốt, nhưng đến thời điểm này tôi muốn thấy một phản hồi thực tế từ máy chủ. Tôi đã sử dụng lại phiên xác thực của chính Paint và gửi prompt sau thông qua điểm cuối kiểm duyệt (moderation endpoint):
Máy chủ trả về HTTP 200:
Tôi cũng đã thử với prompt "a portrait of a smiling person wearing a blue hat" (chân dung một người đang cười đội mũ xanh). Lần này phản hồi chứa một cặp GUID khác và containsHumanReference là true. Do đó, trường này là sự phân loại từ phía máy chủ về việc liệu prompt có đề cập đến con người hay không. Paint phân tích và lưu trữ nó cùng với các ID, mặc dù tôi không tìm thấy bằng chứng nào cho thấy nó tự kiểm soát bước nhúng hình mờ.
ParseModerateResponse phân tích cả hai chuỗi ID dưới dạng GUID và từ chối các giá trị bằng 0 với InvalidPromptGenerationId hoặc InvalidWatermarkId. watermarkId của máy chủ chính là thứ trở thành một phần của hình ảnh được tạo:
Nói cách khác, "được tạo cục bộ" không có nghĩa là toàn bộ hoạt động đều diễn ra cục bộ. Microsoft nhận và kiểm duyệt prompt, sau đó cấp GUID duy nhất mà Paint nhúng vào hình ảnh được tạo cục bộ. Paint cũng gửi promptGenerationId trước đó dưới dạng lastPromptGenerationId với yêu cầu kiểm duyệt tiếp theo, cho phép các yêu cầu kế tiếp được liên kết một cách rõ ràng.
Câu chuyện này còn một phần nữa. Paint không chỉ thay đổi các pixel. Nó còn đính kèm C2PA Content Credentials vào tệp đã lưu. Mã chịu trách nhiệm cho việc này nằm trong ProvenanceHelper.dll, được hỗ trợ bởi provenancesdk.dll.
Bài viết được AI dịch và tổng hợp tự động từ Hacker News Nổi bật (buzzing.cc bản dịch tiếng Trung). Liên kết bài gốc ở phía trên. Dữ liệu đồng bộ qua API công khai được ghi nguồn tại AI HOT (canonical) ↗. AIHOT.vn luôn dẫn nguồn đầy đủ — nếu bạn thấy điểm cần chỉnh sửa, hãy gửi ý kiến tại trang phản hồi.