# Midas Agent A Windows troll implant, hows images, pops message boxes, changes the wallpaper and shakes the screen. **Windows only.** The command surface is built on Win32 — moving another application's window, warping the pointer, `BlockInput`, `PlaySoundW`. An earlier revision had an X11 backend, but a Wayland compositor forbids most of what this implant does to *other* applications, so the Linux build was largely decorative. Building for any other target now fails with an explanation rather than producing something that silently does nothing. ## Configuring Set these in the environment at **compile** time. `option_env!` bakes them into the binary, so the C2 URL and XOR key are not visible in the process environment, and the values are XOR-encoded like every other string constant so they do not appear in `strings` output either. |Variable|Default|Purpose| |---|---|---| |`MIDAS_SERVER`|`http://127.0.0.1:3000/`|C2 base URL| |`MIDAS_KEY`|`asdf`|XOR key; must be in the server's keys list| |`MIDAS_REG_FAIL_LIMIT`|`10`|Registration attempts before giving up| |`MIDAS_CHECKIN_SLEEP`|`10`|Seconds between check-ins| |`MIDAS_CHECKIN_JITTER`|`5`|Jitter range in seconds, applied to the sleep| |`MIDAS_LOG`|unset|Set to anything but `0` for stderr diagnostics| ## Compiling ```bash # From Windows, for the usual target: MIDAS_SERVER=https://c2.example.com/ MIDAS_KEY=... \ cargo build --release --target x86_64-pc-windows-msvc # Cross-compiling from Linux (needs a mingw-w64 linker): MIDAS_SERVER=https://c2.example.com/ MIDAS_KEY=... \ cargo build --release --target x86_64-pc-windows-gnu ``` ## Commands Every argument is required. Where behaviour is genuinely optional it is expressed with a **sentinel**, listed in the description: the Web UI drops blank inputs from the argument array, so an omitted argument silently shifts every later one down a slot rather than falling back to a default. |Command|Description|Arguments| |---|---|---| |`sysinfo`|Report OS, elevation, screen size and overlay state|| |`listoverlays`|List running overlays with their id and age|| |`stopall`|Close every running overlay and stop any playing sound|| |`kill`|Stop the implant|| |`getsleep`|Get the current sleep time and jitter|| |`setsleep`|Set the sleep time between check-ins|seconds: int, jitter: int| |`fetchimage`|Download a file to the implant's cache and return its path|url: str, filename: str| |`msgbox`|Show a message box|title: str, body: str, icon: str, sound: bool| |`showimage`|Show an image over the desktop|path: str, mode: str, seconds: int, topmost: bool| |`setwallpaper`|Set the desktop wallpaper|path: str| |`getwallpaper`|Report the current wallpaper path so it can be restored|| |`shakescreen`|Shake a window back and forth|seconds: int, amplitude: int, interval: int, target: str| |`fakebsod`|Fullscreen fake blue screen with a progress bar|stopcode: str, percent: int, seconds: int| |`fakeupdate`|Fullscreen fake "working on updates" screen|title: str, percent: int, seconds: int| |`faketoast`|Fake desktop notification|title: str, body: str, seconds: int| |`titlechange`|Retitle the foreground window|title: str| |`gettitle`|Report the foreground window's current title|| |`toggletaskbar`|Hide or show the taskbar|state: str| |`scare`|Fullscreen image plus sound at once|image: str, sound: str, seconds: int| |`spook`|Fullscreen animated static, matrix rain or scanlines|mode: str, seconds: int| |`playsound`|Play a sound file|path: str, loop: bool| |`volume`|Change the system playback volume|action: str, level: int| |`cursor`|Hide, freeze or move the mouse pointer|action: str, seconds: int| |`desktopnote`|Draw a large text note on the desktop|text: str, x: int, y: int, size: int| |`fontsize`|Scale the system UI font|percent: int| |`minimizeall`|Minimise or restore every window|state: str| |`openurl`|Open a URL in the default browser|url: str, background: bool| |`lockinput`|Block keyboard and mouse input; `0` holds until `stopall`|seconds: int| ## Sentinels |Argument|Sentinel|Meaning| |---|---|---| |`showimage` / `spook` / `fakebsod` / `fakeupdate` `seconds`|`0`|Do not auto-close; stays until `stopall`| |`scare` `sound`|`none`|Image only, no sound| |`volume` `action`|`set`|Uses the `level` argument; other actions ignore it| |`shakescreen` `target`|`foreground`|Foreground window; `all` for every top-level, `mouse` to shake only the window under the pointer| |`minimizeall` / `toggletaskbar` `state`|`toggle`|Flip to the opposite of the current state. `minimizeall` moves every window one way together, rather than leaving a mix that neither `minimize` nor `restore` could describe afterwards| |`cursor` `seconds`|capped at 60|Ignores anything longer| |`lockinput` `seconds`|`0`|Do not time out; holds until `stopall`. Any interruption needs `stopall` - `sysinfo` reports an active hold as `input lock: active for …s`| |`shakescreen` `seconds`|capped at 30|| ## Platform notes - **Overlays have two backends.** `eframe` is tried first and needs a working OpenGL 3.2+ context; where that is unavailable — a VM with 3D acceleration off, an RDP session, which offers only OpenGL 1.1 — eframe fails during context creation, before its first frame, and does so with a panic from inside glutin. Midas then falls back to a GDI backend that needs no graphics driver at all, and remembers the choice so the dead one is not retried on every command. The fallback also covers a backend that survives startup but dies on its first command: the send itself is the liveness test, so a dead backend is retired at the point it would have failed a command and the same command goes out again on the other one. `sysinfo` reports which is live as `overlay host: running (eframe|gdi)`, along with the real reason if neither came up. - **Panics are reportable, not just stderr noise.** The default hook names only the thread — always one of Midas' own — so every distinct failure printed the same line. The implant installs its own hook that keeps the last few panics and `sysinfo` reports them as `panic: ::`. On a target nobody is sitting at, a stderr line is nowhere to look; this is how the reason a backend died reaches the operator. - **The GDI backend is a transcription, not a port.** Same layouts, same colours, same system font, drawn by hand into a DIB and composited by the window manager through `UpdateLayeredWindow`. What is lost: rounded corners, subpixel layout, and the GPU. What is kept: all seven overlays work, including animated `spook`, and the notification and desktop note are still click-through everywhere they are transparent, because a layered window is hit-tested against its own alpha. - **Text is the one thing GDI is used for.** Everything else is a hand-rolled rasteriser, because GDI cannot write a meaningful alpha channel into a 32-bit DIB. Text is rendered over a sentinel colour and the distance from it is recovered as per-pixel coverage, so antialiasing survives the round trip. Grayscale antialiasing is requested rather than ClearType, since ClearType's subpixel colours would defeat both that and the compositing. - **The overlay host restarts.** It is started lazily by the first visual command and re-created if the backend dies, so a transient failure does not disable overlays for the rest of the implant's life. - **`lockinput`** uses `BlockInput`, which requires an elevated process. An unelevated implant reports the refusal in its own log rather than pretending to have locked anything. `sysinfo` reports the elevation state so this is predictable up front. `0` holds until `stopall`, and `sysinfo` reports an active hold as `input lock: active for …s`. - **No interactive desktop** — a service, a scheduled task, or session 0 — means every window command fails. `sysinfo` reports `desktop: false` in that case. - **`fontsize`** writes `HKCU\Control Panel\Desktop\LogPixels` and broadcasts `WM_SETTINGCHANGE`, so it takes effect for new windows only. - **`setwallpaper` / `getwallpaper`** go through `SystemParametersInfoW` with `SPI_SETDESKWALLPAPER`. - **Overlays** are drawn rather than borrowed from native controls, because they have to be faked from scratch anyway — the BSOD, the update screen, the animated static. `msgbox` is the one exception and uses a real `MessageBoxW`, so it gets the genuine system styling. ## Design notes - **One GUI thread, many viewports — under eframe.** winit permits one `EventLoop` per process, so "a thread per overlay" cannot work. A single long-lived thread owns the one event loop and each overlay is an egui *viewport* within it. - **A thread and a window per overlay — under GDI.** That shape is illegal for eframe and is exactly why the fallback is not a second egui app: it uses ordinary top-level windows with ordinary message loops, so the one-event-loop constraint does not apply. - **Every command acks immediately.** The Web UI abandons a task after 90 seconds (`TASK_TIMEOUT_MS` in `webui/assets/js/agents.js`). Tombili runs commands inline on its check-in loop, so a 30-second shake there would stall check-ins and be reported as a timeout even though it worked. Midas hands work to the GUI thread or a worker thread and returns a status line immediately. - **Blocking calls go on worker threads.** `MessageBoxW` is modal, a key press grab has to hold, `lockinput` has to be undone after a delay or a `stopall`. All of them run through `gui::detach`, so a `lockinput` — timed or continuous — does not hold up the check-in loop. - **`stopall` is immediate.** It bumps an epoch, releases any input hold, sets cancel tokens; overlays check a deadline on their own thread, and `PlaySoundW` is ended by re-issuing it with a null sound, which is the only way to stop it. - **`stopall` does not undo everything.** Wallpaper, title, taskbar, font scale and volume are deliberately left changed — that is the joke. `getwallpaper` and `gettitle` exist so the original state can be read back afterwards. The one thing it must undo is input: a continuous `lockinput` has no other way off, so unblocking it is part of the same cleanup. - **Win32 is declared by hand**, not through the `windows` crate. The declarations are plain `extern "system"`, which keeps the dependency list and the binary small and links identically under the MSVC and GNU targets.