A full-stack Windows kernel security laboratory for studying process hiding, driver callbacks, SSDT monitoring, LSASS protection, registry persistence, WMI reconnaissance, HVCI bypass simulation, and Bring-Your-Own-Vulnerable-Driver (BYOVD) attack patterns.
Built with Python/PySide6 (desktop UI), C (kernel drivers), C++ (driver loader), and PowerShell (VM agent). All components communicate over HTTP between a host analysis machine and a Windows 10 VMware guest.
"You can't defend what you don't understand."
This project exists to close the gap between offensive technique and defensive
visibility. Every attack scenario has a corresponding detection path — the attacker
hides a process via DKOM, the defender cross-checks the EPROCESS linked list every 3
seconds and flags the anomaly. The attacker writes to a registry Run key, the defender's
CmRegisterCallback fires and sets a PERSISTENCE flag.
The lab is designed as an observable, measurable, replayable environment. Every
single action — launching a driver, hiding a process, querying WMI, even a failed
attempt — is logged with an explicit source (defender / attacker / system) and
severity level. The activity log is the single source of truth.
No mock data. No simulation. The kernel drivers are real. The IOCTLs are real. The
DKOM unlinking actually removes the EPROCESS from ActiveProcessLinks. The defender
actually calls ZwQuerySystemInformation from a kernel DPC and diffs the results.
┌─────────────────────────────────────────────────────────────────┐
│ HOST (Analysis Machine) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌────────────┐ ┌─────────────┐ │
│ │ Dashboard │ │ File Mgr │ │ Screenshots│ │ Terminal │ │
│ │ CPU/GPU/ │ │ Dual-pane │ │ VM capture │ │ CMD + PS │ │
│ │ Memory │ │ Host ↔ VM │ │ + gallery │ │ remote │ │
│ └─────┬─────┘ └─────┬─────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │ │
│ ┌─────┴──────────────┴──────────────┴────────────────┴──────┐ │
│ │ SECURITY TAB │ │
│ │ Drivers · Autoboot · Attacker · Detection · Device Guard │ │
│ │ Defender Stats · Attacker Stats · Process Table · Log │ │
│ └────────────────────────┬─────────────────────────────────┘ │
│ │ HTTP :4444 │
│ ┌────────────────────────┴─────────────────────────────────┐ │
│ │ Python Backend (6 providers, 27+ slots, 18+ signals) │ │
│ │ StatsProvider · SecurityProvider · FileModel · etc. │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
HTTP :4444
│
┌─────────────────────────────────────────────────────────────────┐
│ WINDOWS 10 VMware Guest │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ vm_agent.ps1 │ │ defender.sys │ │ attacker.exe │ │
│ │ PowerShell │ │ ObCallbacks │ │ Persist · LSASS dump │ │
│ │ HTTP server │ │ CmCallbacks │ │ Beacon · WMI · HVCI │ │
│ │ 19 endpoints │ │ PsCallbacks │ │ spoof · BYOVD bypass │ │
│ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────┘ │
│ │ │ │ │
│ ┌──────┴─────────────────┴──────────────────────┴───────────┐ │
│ │ Kernel Space │ │
│ │ hider.sys (DKOM) · filehider.sys (minifilter) │ │
│ │ sysinfo.sys (HW info) · kvc.sys (BYOVD loader) │ │
│ └────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
laboratory_analyzer/
├── sysmon/ # Main monitoring application
│ ├── app/
│ │ ├── main.py # PySide6 entry point
│ │ ├── backend.py # 1582 lines — all providers & logic
│ │ └── ui/ # 7 QML files
│ │ ├── main.qml # 5-tab layout
│ │ ├── Dashboard.qml # Gauges, cores, top processes
│ │ ├── Security.qml # Drivers, attacker, detection, log
│ │ ├── FileManager.qml # Dual-pane file browser
│ │ ├── Terminal.qml # CMD + PowerShell remote
│ │ ├── ScreenshotsView.qml
│ │ └── CircularGauge.qml # Canvas-based gauge widget
│ ├── agent/
│ │ └── vm_agent_new.ps1 # PowerShell HTTP agent (438 lines)
│ ├── lab.py # Unified CLI (845 lines)
│ ├── tests/ # 15 test scripts
│ ├── json/ # Test data & payload samples
│ └── screenshots/ # Captured VM screenshots
│
├── drivers_lab/ # Kernel drivers (C)
│ ├── 11_sysinfo/ # sysinfo.sys — IOCTL-based HW info
│ ├── 12_attacker/ # attacker.exe + supporting tools
│ │ ├── attacker_exe/ # C source for attacker payload
│ │ ├── hider/ # hider.sys — DKOM process hiding
│ │ ├── filehider/ # filehider.sys — minifilter file hide
│ │ └── hvci_hook/ # hvci_hook.dll — HVCI spoof injection
│ ├── 13_defender/ # defender.sys — kernel security monitor
│ └── 14_test_payload/ # test_payload.sys — unsigned test driver
│
└── rtcore/ # Driver loader / BYOVD utility (C++)
└── driver_loader_cli_kvc/ # DSE bypass loader
Real-time system vitals from the VM — CPU/GPU/Memory gauges, per-core utilization bars, kernel driver status, and top processes by CPU.
┌──────────┐ ┌──────────┐ ┌──────────┐
│ CPU 45% │ │ GPU 12% │ │ MEM 54% │
│ ◔ gauge │ │ ◑ gauge │ │ ◕ gauge │
└──────────┘ └──────────┘ └──────────┘
┌──────────────────────────────────────┐
│ Core 0: ████████░░ 42% │
│ Core 1: ██████░░░░ 28% │
│ Core 2: ██████████ 55% │
│ Core 3: ████░░░░░░ 18% │
└──────────────────────────────────────┘
Dual-pane browser — left pane shows the remote VM filesystem, right pane shows the host. Upload files from host to VM, download from VM to host, with progress tracking.
One-click VM desktop capture. Screenshots are saved locally and displayed in a scrollable gallery.
Remote CMD and PowerShell interface. Commands are wrapped to track the current working
directory (CWD), which is displayed in the UI. Output is syntax-colored: green for
success, red for errors.
The Security tab is the heart of the lab. It contains:
- Drivers panel: Start/stop
defender.sysandhider.syswith one click - Autoboot: Configure all 4 components (defender, hider, filehider, attacker) to auto-start at system boot via registry + scheduled tasks
- Attacker panel: Launch/Kill attacker.exe; DKOM hide/unhide; file hide/unhide; 8 attack modes (persist, LSASS dump, beacon, WMI recon, HVCI spoof, defender spoof, all attacks, HVCI bypass)
- Detection pills: Real-time status for defender ON/OFF, SSDT HOOKED/CLEAN, hidden process count, callback count, PERSIST detected, LSASS access, WMI activity, registry spoof
- Device Guard / VBS status: VBS state, running services, VM isolation
- Defender stats: Uptime, scan count, DKOM/LSASS/PERSIST/WMI/regspoof/stealth/ kwrite/bypass detection counters
- Tracked process table: Every process the defender monitors, with status badges (DKOM_HIDDEN, LSASS DUMP, PERSIST, WMI RECON, etc.) and per-row KILL button
- Activity log: Filterable by source (ALL/DEFENDER/ATTACKER/SYSTEM) and level (ALL/DETECT/WARN/ERROR/OK), auto-scrolling, color-coded
The host UI runs on Qt's main thread, but all HTTP calls to the VM are blocking. A custom signal bridge ensures background threads can safely update the GUI:
class _Bridge(QObject):
_sig = Signal(object)
def __init__(self):
super().__init__()
self._sig.connect(self._run) # AutoConnection → QueuedConnection
def _run(self, fn):
fn()
def post(self, fn):
self._sig.emit(fn)Background threads call _bridge.post(lambda: self.label.setText("done")) and Qt
delivers it on the main event loop via QueuedConnection.
The defender driver tracks every process by PID. Every 3 seconds a kernel DPC
cross-checks the EPROCESS linked list via ZwQuerySystemInformation. If a tracked
PID is missing from the system list but the driver's internal record still shows it as
alive → DKOM_HIDDEN.
# In SecurityProvider._on_refresh_result()
if new_hidden > self._prev_hidden:
delta = new_hidden - self._prev_hidden
self._stat_dkom_events += delta
hidden_names = [p["name"] for p in new_procs if p["status"] == "DKOM_HIDDEN"]
self._log_add(
f"DKOM HIDDEN detected: {', '.join(hidden_names)}",
"detect", "defender")The hider.sys driver unlinks an EPROCESS from the kernel's doubly-linked list:
// hider.c — unlink EPROCESS from ActiveProcessLinks
PLIST_ENTRY target = (PLIST_ENTRY)((PUCHAR)eproc + ActiveProcessLinksOffset);
PLIST_ENTRY prev = target->Blink;
PLIST_ENTRY next = target->Flink;
prev->Flink = next; // bridge over the target
next->Blink = prev; // — the process vanishes from Task Manager
target->Flink = target; // isolate the entry
target->Blink = target;The Security tab polls 3 endpoints in parallel using ThreadPoolExecutor:
def _worker():
results = [None, None, None]
def fetch(idx, path, body=None):
try:
results[idx] = _vm_post(path, body, timeout=BG_TIMEOUT)
except Exception:
results[idx] = None
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as ex:
f1 = ex.submit(fetch, 0, "/shell", {"cmd": "sc query drv_defender"})
f2 = ex.submit(fetch, 1, "/shell", {"cmd": "sc query drv_hider"})
f3 = ex.submit(fetch, 2, "/security")
concurrent.futures.wait([f1, f2, f3])The agent runs inside the VM, exposing kernel driver IOCTLs over HTTP:
# P/Invoke to communicate with kernel drivers
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public class SysInfoDriver {
[DllImport("kernel32.dll")] public static extern IntPtr CreateFile(...);
[DllImport("kernel32.dll")] public static extern bool DeviceIoControl(...);
[DllImport("kernel32.dll")] public static extern bool CloseHandle(IntPtr);
}
"@
function Get-SysInfoDriverData {
$h = [SysInfoDriver]::CreateFile("\\.\SysInfo", ...)
$outBuf = [Marshal]::AllocHGlobal(40)
[SysInfoDriver]::DeviceIoControl($h, 0x222400, [IntPtr]::Zero, 0, $outBuf, 40, ...)
# Read struct: totalKB, availKB, cpus, uptimeSeconds
[Marshal]::FreeHGlobal($outBuf)
[SysInfoDriver]::CloseHandle($h)
}Every event carries a source field enabling filtered views:
def _log_add(self, msg, level="info", source="system", _emit=True):
ts = datetime.datetime.now().strftime("%H:%M:%S")
lines = [l.strip() for l in msg.splitlines() if l.strip()]
msg = " · ".join(lines) if lines else msg
entry = {"level": level, "time": ts, "text": msg, "source": source}
self._log.append(entry)
if len(self._log) > 500:
self._log = self._log[-500:]
if _emit:
self.logChanged.emit()| Attack Technique | Driver Mechanism | Detection Method | Flag |
|---|---|---|---|
| DKOM process hiding | hider.sys unlinks EPROCESS | ZwQuerySystemInformation cross-check (3s DPC) | DKOM_HIDDEN |
| File hiding | filehider.sys minifilter (ZwQueryDirectoryFile) | Independent directory enumeration | FILE_HIDDEN |
| LSASS credential dump | OpenProcess(VM_READ) on lsass.exe | ObRegisterCallbacks (OB_OPERATION_HANDLE_CREATE) | LSASS_ACCESS |
| Registry persistence | RegSetValue on Run key | CmRegisterCallback bait key monitoring | PERSISTENCE |
| WMI reconnaissance | COM/WMI queries via services.exe | ObRegisterCallbacks handle open on PID 4 | WMI_ACTIVITY |
| Registry spoof (Defender/HVCI) | Tampered DeviceGuard/Defender keys | CmRegisterCallback path filter | REG_SPOOF |
| Stealth folder hide | filehider.sys folder filter | Explorer directory listing cross-check | STEALTH_HIDE |
| Kernel write / BYOVD | DCMD_KERNEL_WRITE to ci.dll g_CiOptions | Defender.sys tracks requesting PID | KERNEL_WRITE |
| SSDT hooking | Inline hook on NtQuerySystemInformation | Kernel memory integrity check | SSDT_HOOKED |
| Endpoint | Method | Purpose |
|---|---|---|
/ping |
GET | Connectivity check, returns hostname + user |
/stats |
POST | CPU, GPU, memory, processes, uptime, driver info |
/security |
POST | Defender report: processes, hidden count, SSDT, callbacks |
/shell |
POST | Execute CMD command, return output |
/ps |
POST | Execute PowerShell, return output |
/drives |
POST | List logical drives |
/listdir |
POST | Directory listing with metadata |
/open |
POST | Open file/folder with default handler |
/upload |
POST | Base64 file upload from host to VM |
/download |
POST | Base64 file download from VM to host |
/screenshot |
POST | Capture VM desktop as PNG (base64) |
/hidepid |
POST | DKOM hide process by PID via hider.sys |
/unhidepid |
POST | Restore EPROCESS link |
/hidefile |
POST | Hide file from directory listing via filehider.sys |
/unhidefile |
POST | Remove file hiding rule |
/killpid |
POST | Force-kill process via IOCTL |
/procinfo |
POST | Detailed process info (path, modules, threads) |
/deviceguard |
POST | Win32_DeviceGuard WMI data |
/attacker/bypasshvci |
POST | DSE/KMCI bypass via KERNEL_WRITE |
The project includes 15 test scripts covering every layer:
# Quick headless tests (no VM, no PySide6)
python3 sysmon/tests/test_all_headless.py # 315 assertions — pure logic
python3 sysmon/tests/test_security_comprehensive.py # 400 assertions — Security tab
# Full GUI tests (Windows + PySide6 + VM required)
python sysmon/tests/test_all.py # 155+ assertions — full stack
python sysmon/tests/test_provider.py # Stats + Security provider
python sysmon/tests/test_full.py # All providers + actions
python sysmon/tests/test_terminal.py # Terminal CWD tracking
python sysmon/tests/test_autoboot.py # Autoboot setup/disable/verify
python sysmon/tests/test_log_source.py # Log source/level filtering
python sysmon/tests/test_bridge.py # Cross-thread bridge
python sysmon/tests/test_attack_defend.py # Full attack/defend scenario
# CLI test suite
python sysmon/lab.py test-ep # All endpoint diagnostics
python sysmon/lab.py test-attack # Attack/defend scenarioPrerequisites:
- Windows 10 VMware VM at
192.168.10.128with bridged networking - Python 3.10+ with PySide6 (
pip install PySide6) - Kernel drivers built with EWDK (Enterprise WDK)
- VM agent deployed to
C:\drivers_test\on the guest
Quick start:
# On the VM — start the agent
powershell -File C:\drivers_test\vm_agent_new.ps1
# On the host — launch the GUI
python C:\laboratory_analyzer\sysmon\app\main.py
# Or use the CLI
python C:\laboratory_analyzer\sysmon\lab.pyThis project is for educational and security research purposes only. The kernel drivers and BYOVD techniques demonstrated here should only be used in isolated lab environments with explicit authorization.
"Know the attack. See the defense. Understand the kernel."