Pure x64 MASM folder-protection for Windows — kernel minifilter, GUI + CLI, zero CRT, < 100 KB
Reverse-engineered IOCTL layer communicating with
vg.sys, a kernel FSFilter Content Screener signed by PROMOSOFT CORPORATION (2014), which loads on Windows 11 26H1 via the legacy cross-signed driver compatibility mechanism.
- Origin
- Overview
- Architecture
- Protection Flags
- GUI Reference
- CLI Reference
- Service & Autostart
- Use Cases
- Driver Communication
- Registry Layout
- Module Analysis
- Building from Source
- Regression Tests
- Known Limitations
- Project Layout
- License
This project started with a private message from BetaTesta on MyDigitalLife forums. He sent me the original Secure Folders binary — a Qt-based folder-protection suite — and asked whether I'd consider building something similar.
The binary turned out to be a real find. Inside it I found a kernel-mode FSFilter minifilter (vg.sys) signed in March 2014 by PROMOSOFT CORPORATION. Microsoft's backward-compatibility policy loads cross-signed drivers timestamped before July 29, 2015 on Windows 10/11 without any WHQL or test-signing requirement. The driver just worked — on Windows 11 26H1, build 28000, without any patches or bypass tricks.
I reverse-engineered the IOCTL interface from the binary, extracted the driver, and rebuilt the entire userland from scratch in pure x64 MASM. During development I spent considerable time debugging driver interactions — tracing IOCTL responses, watching pool allocations, testing repeated load/unload cycles. Despite being 12 years old, vg.sys held up extremely well: no pool leaks, no dangling references, no stale device objects. The cleanup paths are correct. In the kernel world, that's not a given.
The result is VaultGuard — under 100 KB, zero CRT, the same binary as full Win32 GUI and fully scriptable CLI.
Thanks to BetaTesta for the idea, the original binary, and the beta testing.
Ground-up rewrite of a 12-year-old Qt/C++ folder-protection suite (~8 MB) in pure x64 MASM assembly. The same binary runs as a full Win32 GUI application or a scriptable CLI tool.
| Property | Value |
|---|---|
| Binary size | < 100 KB |
| Original (Qt/C++) | ~8 MB — over 80× larger |
| CRT dependencies | Zero |
| Linked DLLs | kernel32 user32 advapi32 shell32 ole32 dwmapi gdi32 comctl32 uxtheme cabinet |
| UI | Win32 — Dark Mode, Mica (DWM), PerMonitorV2 DPI, system tray |
| Driver | vg.sys — FSFilter Content Screener, Altitude 389991 |
| Driver service name | clrcd |
| VaultGuard service name | VaultGuard (optional, manual-start by default) |
| Registry | HKCU\Software\VG\Paths · HKCU\Software\VG\Trusted |
| Requires | Windows 11 x64, Administrator |
| Driver signature | Signed March 18, 2014 — loads via legacy cross-sign compatibility, no test-signing needed |
Run from an elevated command prompt (Administrator). Driver installs itself on first run.
GUI — double-click vg.exe or run without arguments:
- Drag a folder or file from Explorer onto the Protected Paths panel → row appears instantly
- Drag a
.lnkshortcut → resolved to real target automatically viaIShellLink - Drag an
.exefile onto the Trusted Apps panel → executable name extracted and added as trusted process - Click Hidden / Locked / Read-only / No run checkbox → flag applied to kernel driver immediately
Ctrl+Clickmultiple rows → click Remove selected to delete them all at once- Add a process name to Allowed apps → that process bypasses all driver protections
Shift+Minimize→ window hides to system tray- Title bar shows live driver + protection state:
Driver: TRANSIENT | Protection: ON
CLI — same binary, scriptable:
vg.exe /protection on
vg.exe /setitem "C:\Private" Locked
vg.exe /settrusted totalcmd64.exe Enabled
vg.exe /enumitems out.csv
vg.exe /enumtrusted trust.csv
vg.exe /tray start minimized to system tray
vg.exe /autostart on register Task Scheduler logon entry
vg.exe /service install register app service as manual-start
vg.exe /driver install auto install clrcd and set automatic driver start
vg.exe /uninstall stop and remove services, driver and config
vg.exe /?
Everything above — the GUI, the CLI, the COM .lnk resolver, the FDI driver extractor, the SCM installer, the IOCTL layer, the registry persistence, the Windows service runtime, the tray icon — is written in pure x64 MASM assembly. Zero CRT. Zero runtime. Every byte deliberate.
flowchart TD
A[vg.exe launched] --> B{argc >= 2?}
B -->|Yes| C[CliDispatch argv]
B -->|No| D[CreateMainWindow GUI]
C --> E{Known switch?}
E -->|/service| SVC[_CliServiceInstall/Uninstall]
E -->|/driver| DRV[_CliDriver clrcd lifecycle]
E -->|/uninstall| UN[_CliFullUninstall cleanup]
E -->|/autostart| AS[_CliAutostart schtasks.exe]
E -->|/tray| TR[g_startMinimized=1 → GUI]
E -->|/svcstart| SS[_SvcStart → StartServiceCtrlDispatcherW]
E -->|other| F[CLI command handler]
E -->|unknown| D
D --> G[WM_CREATE: _OnCreate layout]
G --> H[SetTimer 2000ms]
H --> I[Message Loop]
I --> J{WM_MESSAGE}
J -->|WM_COMMAND| K[_OnCommand handlers.asm]
J -->|WM_NOTIFY| L[_OnNotify flag checkboxes]
J -->|WM_DROPFILES| M[_OnDropFiles drop.asm]
J -->|WM_TIMER| N[UpdateStatusBar]
J -->|WM_SIZE minimized+Shift| TY[_TrayAdd tray.asm]
J -->|WM_TRAY| TM[_OnTrayMsg tray.asm]
J -->|TaskbarCreated| TA[_TrayAdd re-register]
J -->|WM_SETTINGCHANGE| O[_ReadDarkMode + ApplyDarkMode]
K --> P[EnsureDriverReady]
F --> P
P --> Q{Device open?}
Q -->|Yes| R[IOCTL DeviceIoControl]
Q -->|No| S[InstallDriver: FDI extract + CreateServiceW]
S --> T[StartDriver: SCM StartServiceW]
T --> R
R --> U[ConfigSave/Load: HKCU registry]
| Flag | CLI mode | Hex | Driver behavior |
|---|---|---|---|
| Hidden | Hidden |
0x01 |
Folder invisible — STATUS_OBJECT_NAME_NOT_FOUND + removed from dir listings |
| Locked | Locked |
0x02 |
All access → STATUS_ACCESS_DENIED |
| Read-only | Read-only |
0x04 |
Strips FILE_WRITE_DATA and DELETE from DesiredAccess |
| No execute | No-execution |
0x08 |
Strips execute bits from DesiredAccess |
| Disabled | Disabled |
0x00 |
Path stored in registry, inactive in driver |
Flags combine as a bitmask: Hidden + Locked = 0x03, Hidden + Locked + Read-only = 0x07, etc.
Launch vg.exe without arguments. Fixed 680 × 472 px window.
Class VGMainWnd. Driver and protection state is embedded in the window title (refreshed every 2 seconds by WM_TIMER):
VaultGuard | Driver: STOPPED | Protection: OFF
VaultGuard | Driver: TRANSIENT | Protection: ON
Dark Mode and Mica backdrop follow the system theme automatically via WM_SETTINGCHANGE.
Shift+Minimize— hides window and adds tray icon/trayswitch — starts directly as a tray-only process (hidden window)- Double-click tray icon — restores main window
- Right-click tray icon — context menu (Restore / Exit)
TaskbarCreatedmessage — if Explorer restarts (crash or logon race), VaultGuard re-registers its tray icon automatically
UIPI note: When launched elevated via Task Scheduler (
/rl highest),ChangeWindowMessageFilterExis called duringWM_CREATEto allow theTaskbarCreatedregistered message (ID ≥0xC000) to cross the integrity boundary from Medium-IL Explorer into the High-IL process. Without this, the tray icon would not appear after logon.
| Control | Behavior |
|---|---|
| [Add path...] button | Opens SHBrowseForFolderW native folder browser |
| [Remove selected] button | Removes all selected entries (multi-select supported) |
| Flag columns (H / L / R / X) | Click any flag cell → toggles checkbox + sends IOCTL update to driver immediately |
| Drag & Drop | Accepts folders and files from Explorer; .lnk shortcuts resolved via COM IShellLink; .exe files dropped here add full path as protected item |
Columns:
| Column | Flag | Hex |
|---|---|---|
| Path | — | — |
| Hidden (H) | VG_FLAG_HIDDEN |
0x01 |
| Locked (L) | VG_FLAG_LOCKED |
0x02 |
| Read-only (R) | VG_FLAG_READONLY |
0x04 |
| No run (X) | VG_FLAG_NOEXEC |
0x08 |
Processes in this list bypass all driver protections — Hidden/Locked/Read-only rules do not apply to them.
| Control | Behavior |
|---|---|
| Edit box | Enter process executable name (e.g. totalcmd64.exe) |
| [Add] button | Normalizes to lowercase → IoctlAddTrusted + ConfigSaveTrusted |
| [Remove] button | Clears entire driver trusted list → removes registry entry → reloads remaining entries via ConfigLoad |
| Drag & Drop | Drop an .exe file or .lnk shortcut onto this panel — executable name extracted and added directly as trusted process |
Note: Removing a trusted process sends an empty
IoctlRemoveTrustedthat wipes the entire active trusted list in the driver.ConfigLoadimmediately reloads all remaining registry entries. The driver provides no per-item removal IOCTL.
Run from an elevated command prompt (Administrator):
vg.exe /?
vg.exe /protection on | off
vg.exe /setitem <path> Hidden | Locked | Read-only | No-execution | Disabled
vg.exe /settrusted <name> Enabled | Disabled
vg.exe /enumitems <out.csv>
vg.exe /enumtrusted <out.csv>
vg.exe /tray
vg.exe /autostart on | off
vg.exe /service install | uninstall
vg.exe /driver install [manual | auto]
vg.exe /driver start | stop | manual | auto
vg.exe /driver uninstall
vg.exe /uninstall
| Command | Description | Example |
|---|---|---|
/? -h --help |
Print help to stdout | vg.exe /? |
/protection on|off |
Enable or disable global protection | vg.exe /protection on |
/setitem <path> <mode> |
Set protection flags for a path | vg.exe /setitem "C:\Data" Locked |
/settrusted <name> <state> |
Add or remove a trusted process | vg.exe /settrusted cmd.exe Enabled |
/enumitems <file.csv> |
Export protected paths as UTF-16LE CSV | vg.exe /enumitems out.csv |
/enumtrusted <file.csv> |
Export trusted processes as UTF-16LE CSV | vg.exe /enumtrusted trust.csv |
/tray |
Start minimized to system tray | vg.exe /tray |
/autostart on|off |
Register/remove Task Scheduler logon entry | vg.exe /autostart on |
/service install|uninstall |
Register/remove VaultGuard Windows service | vg.exe /service install |
/driver install [manual|auto] |
Install clrcd driver service; default start type is manual |
vg.exe /driver install auto |
/driver start|stop |
Start or stop the clrcd driver service |
vg.exe /driver start |
/driver manual|auto |
Change clrcd startup type via ChangeServiceConfigW |
vg.exe /driver auto |
/driver uninstall |
Stop and remove clrcd; delete extracted vg.sys best-effort |
vg.exe /driver uninstall |
/uninstall |
Full cleanup: disable protection, remove app service, remove driver service/file, delete HKCU config | vg.exe /uninstall |
/enumitems — UTF-16LE with BOM:
Path,Hidden,Locked,ReadOnly,NoExec
C:\Private,0,1,0,0
/enumtrusted — UTF-16LE with BOM:
Application
totalcmd64.exe
| Code | Meaning |
|---|---|
0 |
Success |
1 |
Unknown switch / bad argument / driver error |
vg.exe /service install
vg.exe /service uninstall
Registers vg.exe itself as a Windows service named VaultGuard:
- Start type:
SERVICE_DEMAND_START— manual by default - Binary path:
"<full path to vg.exe>" /svcstart - On install: service is created and immediately started via
StartServiceW - On uninstall: service is stopped (if running) and deleted via
DeleteService
The internal /svcstart switch is dispatched by CliDispatch → _SvcStart → StartServiceCtrlDispatcherW. The SCM thread runs the standard Win32 service lifecycle:
_SvcMain → RegisterServiceCtrlHandlerExW → SetServiceStatus(RUNNING)
→ WaitForSingleObject(stop_event, INFINITE)
→ on STOP/SHUTDOWN/PRESHUTDOWN: SetEvent → SetServiceStatus(STOPPED)
Accepted controls: SERVICE_CONTROL_STOP, SERVICE_CONTROL_SHUTDOWN, SERVICE_CONTROL_PRESHUTDOWN.
The service mode runs the full GUI (window + message loop) — the same binary, no separate service-only build. The SCM thread acts as a watchdog; the main thread runs the normal Win32 message loop.
vg.exe /driver install
vg.exe /driver install auto
vg.exe /driver manual
vg.exe /driver auto
vg.exe /driver start
vg.exe /driver stop
vg.exe /driver uninstall
Manages the real protection service, clrcd, through WinAPI/SCM calls only:
installextractsvg.sys, creates the kernel driver service and leaves itSERVICE_DEMAND_STARTby defaultinstall autocreates the service and changes start type toSERVICE_AUTO_STARTmanual/autocallChangeServiceConfigWfor the existingclrcdservicestart/stopcallStartServiceW/ControlServiceuninstallstops and deletesclrcd, then deletes%SystemRoot%\System32\drivers\vg.sysbest-effort
vg.exe /uninstall
Disables live protection if the device is open, removes the optional VaultGuard app service, stops/deletes clrcd, deletes the extracted driver file best-effort, and removes HKCU\Software\VG.
vg.exe /autostart on
vg.exe /autostart off
Registers a Task Scheduler logon task:
schtasks.exe /create /f /sc onlogon /rl highest /tn VaultGuard
/tr "\"<exe>\" /tray"
Key properties:
/rl highest— runs elevated (High Integrity Level) without UAC prompt at logon/sc onlogon— fires once per user logon session/tray— launches directly to system tray (no window flash)- Battery restrictions cleared after creation:
DisallowStartIfOnBatteries:$false,StopIfGoingOnBatteries:$false
This is the only officially supported Microsoft method for silent elevated autostart on Windows 10/11.
HKCU\Runentries are silently skipped by Windows for processes withrequireAdministratormanifest.
vg.exe /protection on
vg.exe /setitem "C:\Private" Locked
# Allow Total Commander through
vg.exe /settrusted totalcmd64.exe Enabled
# Verify
vg.exe /enumitems items.csv
vg.exe /enumtrusted trust.csv
# Revoke when done
vg.exe /settrusted totalcmd64.exe Disabledvg.exe /setitem "C:\Secret" HiddenThe folder disappears from Explorer, dir, and all directory enumeration APIs. Processes that know the full path can still address it — combine with Locked to block those too (use the GUI checkboxes to set multiple flags per path).
vg.exe /setitem "C:\Backups" Read-onlyThe driver strips FILE_WRITE_DATA and DELETE bits at the kernel level. Trusted processes can still write normally.
vg.exe /autostart on
# Task Scheduler entry created. VaultGuard starts at logon to tray, elevated, no UAC prompt.
# To remove:
vg.exe /autostart offvg.exe /service install
# Service VaultGuard created (DEMAND_START) and started immediately.
# To remove:
vg.exe /service uninstallvg.exe /enumitems C:\temp\items.csv
$rows = Import-Csv C:\temp\items.csv -Encoding Unicode
$locked = $rows | Where-Object { $_.Locked -eq '1' }
Write-Host "Locked paths: $($locked.Count)"Import-Csv -Encoding Unicode reads UTF-16LE correctly on both Windows PowerShell 5.1 and PowerShell 7.
flowchart TD
UA[User action: GUI or CLI] --> EDR[EnsureDriverReady]
EDR --> OD[OpenDevice: \\.\BE79F7D8...]
OD -->|success| IOCTL
OD -->|fail| ID[InstallDriver: FDI extract + CreateServiceW]
ID --> SD[StartDriver: SCM StartServiceW]
SD --> OD2[OpenDevice retry]
OD2 --> IOCTL
IOCTL --> IAP[IoctlAddPath flags + NT path]
IAP --> QDD[QueryDosDeviceW C: → Device/HarddiskVolumeN]
QDD --> DIO[DeviceIoControl 0x9C402400, buf 0x6414 bytes]
DIO --> CSP[ConfigSavePath: HKCU/Software/VG/Paths/path = REG_DWORD flags]
CSP --> HIDE[Folder disappears from Explorer / returns ACCESS_DENIED]
RT[Remove trusted: GUI or CLI] --> IRT[IoctlRemoveTrusted empty input clears ALL]
IRT --> CRT[ConfigRemoveTrusted: delete registry entry]
CRT --> CL[ConfigLoad: reload remaining entries from registry into driver]
Reverse-engineered IOCTL codes for vg.sys:
| IOCTL | Code | Notes |
|---|---|---|
IOCTL_VG_ADD_PATH |
0x9C402400 |
flags=0 → Disabled; nInBufSize fixed at 0x6414 |
IOCTL_VG_ENUM_PATHS |
0x9C402404 |
|
IOCTL_VG_ADD_TRUSTED |
0x9C402408 |
record size 0xD94, process name at +4 |
IOCTL_VG_REMOVE_TRUSTED |
0x9C402408 |
empty input (size=0) clears entire list |
IOCTL_VG_ENUM_TRUSTED |
0x9C40240C |
|
IOCTL_VG_SET_ACTIVE |
0x9C40241C |
DWORD (4 bytes) |
IOCTL_VG_GET_STATUS |
0x9C402420 |
16-byte VG_STATUS struct |
IOCTL_VG_CLEAR_ALL |
0x9C402424 |
Full reset |
Device path: \\.\BE79F7D853E643089D51EDCDA79805C4
vg.sys is embedded inside vg.exe as a resource — an LZX CAB appended after the first 1078 bytes of an ICO file header, extracted at install time via FDI (cabinet.lib, no temp files on disk during extraction).
HKEY_CURRENT_USER\Software\VG\
├── Paths\
│ "C:\Private\Data" REG_DWORD 0x00000002 (Locked)
│ "C:\Secret" REG_DWORD 0x00000001 (Hidden)
│ "C:\Temp\Archive" REG_DWORD 0x00000000 (Disabled)
└── Trusted\
"totalcmd64.exe" REG_DWORD 0x00000001
"explorer.exe" REG_DWORD 0x00000001
ConfigLoad enumerates both keys and reloads the full configuration into the driver.
The driver holds no persistent state across reboots.
17 MASM source files, each with a single defined responsibility.
Entry point: mainCRTStartup
Startup sequence:
GetStdHandle(STD_OUTPUT_HANDLE)+GetFileType→AttachConsole(-1)if no TTYGetCommandLineW→CommandLineToArgvW→lea rdx, [rsp+20h]as&argcargc >= 2→CliDispatch(argv[1], argv, argc); returns 0 (unknown) orargc < 2→ start GUI
Public globals:
| Symbol | Type | Description |
|---|---|---|
g_hInstance |
dq |
Process HINSTANCE |
g_hwndMain |
dq |
Main window handle |
g_hwndLvPaths |
dq |
ListView "Protected Paths" |
g_hwndLvTrusted |
dq |
ListView "Trusted Processes" |
g_hwndBtnToggle |
dq |
Toggle button |
g_hDevice |
dq |
Handle to \\.\BE79F7D853E643089D51EDCDA79805C4 |
g_hFontMain, g_hFontSmall |
dq |
GDI font handles |
g_hBrushBg |
dq |
Background brush (0x202020 in dark mode) |
g_isDarkMode |
dd |
1 = dark mode active |
g_startMinimized |
dd |
1 = start hidden to tray (/tray switch) |
g_driverInstalled, g_driverRunning, g_protActive |
dd |
Driver state flags |
g_ioBuf |
65536 B |
IOCTL enumeration buffer (64 KB) |
g_pathBuf, g_tempBuf, g_statusBuf |
520 W |
Wide-character scratch buffers |
Contains exclusively MainWndProc and CreateMainWindow. Fixed size 680 × 472 px, class VGMainWnd.
On creation, calls RegisterWindowMessageW("TaskbarCreated") and stores the dynamic message ID in g_wmTaskbarCreated — used both in MainWndProc and to unlock the UIPI filter in _OnCreate.
| Message | Action |
|---|---|
WM_CREATE |
_OnCreate (layout.asm) |
WM_DESTROY |
KillTimer, DeleteObject (fonts + brush), PostQuitMessage(0) |
WM_CLOSE |
DestroyWindow |
WM_SIZE (minimized + Shift held) |
_TrayAdd — hide to system tray |
WM_TRAY |
_OnTrayMsg — handle tray icon mouse events |
WM_DROPFILES |
_OnDropFiles (drop.asm) |
WM_NOTIFY |
_OnNotify (handlers.asm) — flag checkboxes in the ListView |
WM_COMMAND |
_OnCommand (handlers.asm) — button clicks |
WM_TIMER |
UpdateStatusBar every 2 seconds |
WM_SETTINGCHANGE |
_ReadDarkMode + ApplyDarkMode + _ApplyThemeColors + InvalidateRect |
WM_ERASEBKGND |
FillRect(g_hBrushBg) — paints client area with theme background |
WM_CTLCOLORSTATIC |
Dark mode: SetBkMode(OPAQUE) + colors + returns g_hBrushBg |
TaskbarCreated |
_TrayAdd — re-registers tray icon after Explorer restart |
_OnCreate(rcx=hwnd) creates all widgets in a single pass:
[y= 8] Toggle button
[y= 10] "Protected files/folders" header + [Add path...] + [Remove selected]
[y= 40] ListView Paths (h=220): columns Path/H/L/R/X
LVS_REPORT|LVS_SHOWSELALWAYS — multi-select enabled
[y=278] "Allowed apps (trusted)" header
[y=276] Trusted edit box + [Add] + [Remove]
[y=308] ListView Trusted (h=80, ~3 rows): 1 column Application
[y=396] Author/copyright static label (centered)
Key calls: InitCommonControlsEx(ICC_LISTVIEW_CLASSES), DragAcceptFiles(TRUE), ChangeWindowMessageFilterEx for WM_DROPFILES / WM_COPYDATA / WM_COPYGLOBALDATA / TaskbarCreated (all MSGFLT_ALLOW — required for High-IL process to receive drops and shell messages from Medium-IL Explorer), SetTimer(TIMER_STATUS_ID, 2000).
| Procedure | Description |
|---|---|
_TrayAdd(rcx=hwnd) |
Shell_NotifyIconW(NIM_ADD) with icon handle and tooltip "VaultGuard" |
_TrayRemove(rcx=hwnd) |
Shell_NotifyIconW(NIM_DELETE) |
_OnTrayMsg(rcx=hwnd, rdx=lParam) |
WM_LBUTTONDBLCLK → ShowWindow(SW_RESTORE) + SetForegroundWindow; right-click → context menu (Restore / Exit) |
WM_TRAY is a user-defined message (WM_APP + 1). _TrayAdd sets uCallbackMessage = WM_TRAY so all tray icon mouse events are routed to MainWndProc.
| Procedure | Description |
|---|---|
_ReadDarkMode |
Reads AppsUseLightTheme registry value; sets g_isDarkMode |
ApplyDarkMode(rcx=hwnd) |
DwmSetWindowAttribute(DWMWA_USE_IMMERSIVE_DARK_MODE) + Mica via DWMSBT_MAINWINDOW |
_SetLvColors |
SetWindowTheme("DarkMode_Explorer") + ListView color messages |
_ApplyThemeColors |
Recreates g_hBrushBg; calls _SetLvColors for both ListViews |
_OnCommand dispatch by control ID:
| IDC | Action |
|---|---|
IDC_BTN_TOGGLE |
IoctlSetActive(!g_protActive) |
IDC_BTN_ADD_PATH |
SHBrowseForFolderW → stage as g_pendingPath → RefreshLists |
IDC_BTN_REM_PATH |
Multi-select loop: LVM_GETNEXTITEM(-1, LVNI_SELECTED) → IoctlAddPath(0) + ConfigRemovePath + LVM_DELETEITEM; repeat until no more selected |
IDC_BTN_ADD_TRUSTED |
Lowercase → IoctlAddTrusted + ConfigSaveTrusted |
IDC_BTN_REM_TRUSTED |
ConfigRemoveTrusted + IoctlRemoveTrusted(empty) + ConfigLoad (reloads remaining) + RefreshLists |
_OnNotify — NM_CLICK on Protected Paths ListView: col 1–4 → toggle flag bit → IoctlAddPath + ConfigSavePath + RefreshLists.
UpdateStatusBar — EnsureDriverReady → IoctlGetStatus → updates title bar and toggle button text. Repaint suppressed when state has not changed.
RefreshLists — IoctlEnumPaths + IoctlEnumTrusted → LVM_DELETEALLITEMS → _LvInsertItem for each entry.
_OnDropFiles(rcx=HDROP, rdx=hMainWnd):
DragQueryFileW(0)→ first dropped path intog_pathBuf- Detects drop target: if cursor Y-position is over the Trusted panel → route to trusted add path
- Last 4 chars are
.lnk(viawcscmp_ci) → callResolveLnkPath→GetLongPathNameW(canonical case) - Trusted panel drop: extracts filename component from path → lowercase →
IoctlAddTrusted+ConfigSaveTrusted - Paths panel drop: auto-commits any prior
g_pendingPathto registry viaConfigSavePath(flags=0)before overwriting; stores resolved path ing_pendingPath→RefreshLists DragFinish
ResolveLnkPath(rcx=.lnk path, rdx=out buf):
CoInitialize → CoCreateInstance(CLSID_ShellLink) → QueryInterface(IID_IPersistFile) → IPersistFile::Load → IShellLinkW::GetPath → full COM release chain → CoUninitialize
| Procedure | Description |
|---|---|
_CliServiceInstall |
Opens SCM → CreateServiceW (name=VaultGuard, DEMAND_START, binary="<exe>" /svcstart) → StartServiceW |
_CliServiceUninstall |
Opens SCM → OpenServiceW → ControlService(STOP) → DeleteService |
_SvcStart |
Builds SERVICE_TABLE_ENTRYW[2] on stack → StartServiceCtrlDispatcherW → ExitProcess(0) |
_SvcMain |
CreateEventW(manual-reset) → RegisterServiceCtrlHandlerExW → SetServiceStatus(RUNNING, accepts=STOP|SHUTDOWN|PRESHUTDOWN) → WaitForSingleObject(INFINITE) → SetServiceStatus(STOPPED) |
_SvcCtrlHandler |
STOP/SHUTDOWN/PRESHUTDOWN → SetServiceStatus(STOP_PENDING) → SetEvent(stop_event) |
SERVICE_ACCEPT_PRESHUTDOWN (0x100) is registered via RegisterServiceCtrlHandlerExW (the extended variant) to receive clean shutdown notification before system-level services stop.
| Property | Value |
|---|---|
| Service name | clrcd |
| Display name | Vault Guard Driver |
| Type | SERVICE_KERNEL_DRIVER |
| Start | SERVICE_DEMAND_START |
| Dependency | FltMgr\0\0 |
| Device path | \\.\BE79F7D853E643089D51EDCDA79805C4 |
| Procedure | Description |
|---|---|
driver_scm.asm |
InstallDriver, StartDriver, StopDriver, UninstallDriver, SetDriverStartType, DeleteDriverFile |
device.asm |
OpenDevice, CloseDevice, EnsureDriverReady |
ioctl.asm |
IoctlSetActive, path/trusted add-remove, enum, status, clear-all wrappers |
driver_consts.inc |
Shared driver-layer symbol declarations (clrcd, device path, status/drive buffers) |
| Procedure | Description |
|---|---|
ConfigLoad |
Enumerates Paths → IoctlAddPath each; enumerates Trusted → IoctlAddTrusted each; calls IoctlSetActive(1) |
ConfigSavePath(rcx=path, rdx=flags) |
RegCreateKeyExW → RegSetValueExW(path, flags) |
ConfigRemovePath(rcx=path) |
RegOpenKeyExW → RegDeleteValueW |
ConfigSaveTrusted(rcx=name_lowercase) |
RegCreateKeyExW → RegSetValueExW(name, 1) |
ConfigRemoveTrusted(rcx=name) |
RegOpenKeyExW → RegDeleteValueW |
ConfigDeleteAll |
Deletes Paths, Trusted, then HKCU\Software\VG via RegDeleteKeyW |
Switch comparison uses wcscmp_ci — fast ASCII case-insensitive wide-string compare, no CharLowerW. All switches work regardless of capitalization.
Every exit path goes through _CliFinish(code) → ConsoleSendEnter() (injects VK_RETURN via WriteConsoleInputW) so CMD prompt reappears immediately without waiting for Enter.
Switch dispatch order: /? → /service → /driver → /uninstall → /enumitems → /enumtrusted → /protection → /setitem → /settrusted → /svcstart → /tray → /autostart → unknown (return 0 → GUI mode)
Owns all enumeration and file-writing logic for /enumitems and /enumtrusted.
| Procedure | Description |
|---|---|
_CliEnumItems(rcx=outfile) |
Opens file → writes UTF-16LE BOM + CSV header → enumerates HKCU\Software\VG\Paths → writes path + 4 flag columns per row → closes file → ExitProcess(0) |
_CliEnumTrusted(rcx=outfile) |
IoctlEnumTrusted (verifies driver ready) → opens file → writes UTF-16LE BOM + header → enumerates HKCU\Software\VG\Trusted → writes one name per row → ExitProcess(0) |
_WriteBytes (private) |
WriteFile wrapper |
_WriteWStr (private) |
Wide string → wcslen_p → _WriteBytes |
CSV is written from the registry (authoritative persisted state) rather than the driver's enumeration buffer, which can lag after flag-change operations.
The driver vg.sys is embedded inside vg.exe as a resource: an LZX CAB appended to the ICO file header.
FindResourceW(NULL, IDR_DRIVER=102, RT_RCDATA=10)→ resource pointerLockResource→ raw bytes; CAB starts at offset 1078 bytes- All FDI callbacks operate in memory — no temp files on disk
FDICopy→ heap buffer →WriteFileto%SystemRoot%\system32\drivers\vg.sys
CAB is packed at ~42% of original size via LZX compression.
| Procedure | Signature | Description |
|---|---|---|
wcslen_p |
rcx=s → rax=count |
Wide strlen |
wcscpy_p |
rcx=dst, rdx=src → rax=dst |
Wide strcpy |
wcscat_p |
rcx=dst, rdx=src → rax=dst |
Wide strcat |
wcscmp_ci |
rcx=a, rdx=b → rax=0/nonzero |
Case-insensitive wide compare (ASCII A-Z only) |
wcs_ascii_lower_inplace |
rcx=s |
Lowercases A-Z in place |
IntToDecW |
rcx=val, rdx=buf → rax=ptr |
DWORD → wide decimal string |
IntToHexW |
rcx=val, rdx=buf → rax=ptr |
DWORD → 8-char wide hex string |
WideWriteConsole |
rcx=handle, rdx=str |
WriteConsoleW; falls back to ANSI WriteFile if handle is not a console |
WideWriteLn |
rcx=str |
WideWriteConsole(stdout, str) + CRLF |
ConsoleSendEnter |
— | Injects VK_RETURN via WriteConsoleInputW |
| Procedure | Signature | Description |
|---|---|---|
_LvAddColumn |
rcx=hwnd, rdx=idx, r8=width, r9=text |
LVM_INSERTCOLUMNW |
_LvInsertItem |
rcx=hwnd, rdx=row, r8=col, r9=text |
LVM_INSERTITEMW / LVM_SETITEMW |
_LvGetItemText |
rcx=hwnd, rdx=row, r8=col, r9=buf |
LVM_GETITEMTEXTW |
Requires Visual Studio with MASM x64 toolchain (auto-detected via vswhere.exe):
.\build.ps1 # full build — steps 0 through 4
.\build.ps1 -SkipRC # skip step 0 (icon packaging) and step 1 (rc.exe)Build steps:
[0] makecab IcoBuilder\vg.sys → LZX CAB → prepend 1078 B ICO header → ICON\vg.ico
[1] rc.exe /c65001 vg.rc → vg.res
[2] ml64.exe /c /Cp /Cx /Zi
(strutil res driver config cli export service theme listview handlers drop tray layout window main)
[3] link.exe /SUBSYSTEM:WINDOWS /NODEFAULTLIB /MANIFEST:EMBED /MANIFESTUAC:requireAdministrator
Libs: kernel32 user32 advapi32 shell32 ole32 dwmapi gdi32 comctl32 uxtheme cabinet
[4] dumpbin — verify: no CRT imports, allowed DLL set only
Intermediates (*.obj, *.res, *.pdb) are deleted on completion.
tests/cli_test.ps1 — 84 regression checks, CLI interface only, no GUI required.
Requires bin\vg.exe and Administrator context; the script installs/starts/removes clrcd as needed.
powershell -ExecutionPolicy Bypass -File tests\cli_test.ps1
# -KeepOutput preserves CSV output files in tests\out\| Group | Tests | What is verified |
|---|---|---|
| Help | 1 | /? output on stdout |
| setitem flags | 4 | Each flag individually → registry value |
| setitem Disabled | 1 | Path in registry with value 0 |
| enumitems CSV | 3 | File content, rows, flag bits |
| settrusted + enumtrusted | 4 | Registry and CSV |
| settrusted Disabled | 2 | Entry removed; second entry unaffected |
| protection on/off | 2 | Exit code 0; bad argument → exit code 1 |
| error cases | 3 | Missing args, bad mode → exit code 1 |
| registry consistency | 13 | Remove-one-trusted: remaining entry survives |
All tests capture output via Start-Process -RedirectStandardOutput, which exercises the WriteConsoleW → WriteFile ANSI fallback path in WideWriteConsole.
| Item | Status |
|---|---|
wcscmp_ci |
ASCII only (A-Z). Paths with non-ASCII characters use case-sensitive comparison — sufficient for NT paths and CLI switches |
| COM apartment | CoInitialize/CoUninitialize at every .lnk resolution; safe for GUI usage |
| Password mode | /p is parsed and silently ignored; driver has no password enforcement |
| Light mode | GUI works in light mode; ListView colors fall back to system defaults |
| Trusted list removal | No per-item IOCTL — driver only supports clearing the entire list, requiring a full reload cycle |
| Multi-file drop | Only the first dropped file/folder is processed per WM_DROPFILES; multi-file drops discard all but the first |
| Service + GUI | In service mode the full GUI is active; no headless/service-only mode |
VaultGuard/
├── x64/
│ ├── consts.inc IOCTL codes, flags, struct offsets, control IDs
│ ├── globals.inc EXTRN declarations
│ ├── main.asm Entry point, globals, message loop
│ ├── window.asm MainWndProc + CreateMainWindow
│ ├── layout.asm _OnCreate — all controls, UIPI message filters
│ ├── theme.asm Dark mode, Mica, ListView colors
│ ├── handlers.asm _OnCommand, _OnNotify, UpdateStatusBar, RefreshLists
│ ├── drop.asm WM_DROPFILES + ResolveLnkPath (IShellLink COM) + trusted panel routing
│ ├── tray.asm System tray: _TrayAdd, _TrayRemove, _OnTrayMsg
│ ├── listview.asm ListView wrappers
│ ├── driver_scm.asm clrcd SCM lifecycle + driver file cleanup
│ ├── device.asm Device open/close + EnsureDriverReady
│ ├── ioctl.asm DeviceIoControl wrappers + path marshalling
│ ├── driver_consts.inc shared driver-layer declarations
│ ├── config.asm ConfigLoad/Save/Remove/DeleteAll (registry)
│ ├── cli.asm CliDispatch — CLI interface + RunCmdAndWait + _CliAutostart
│ ├── export.asm _CliEnumItems, _CliEnumTrusted — CSV export
│ ├── service.asm Windows service runtime — _SvcStart, _SvcMain, _SvcCtrlHandler
│ ├── strutil.asm String utilities + WideWriteLn/WideWriteConsole
│ ├── res.asm ExtractDriver — FDI decompression of CAB from icon
│ ├── vg.rc ICON 101 + RCDATA 102 (both = ICON/vg.ico)
│ └── vg.manifest requireAdministrator, Win11 GUID, perMonitorV2
├── tests/
│ └── cli_test.ps1 CLI, driver lifecycle, registry, CSV, enforcement, uninstall tests
├── IcoBuilder/
│ ├── vg.sys Third-party driver — PROMOSOFT CORPORATION (2014)
│ └── vg.ico Base icon (ICO header used as CAB wrapper)
├── images/
│ └── VaultGuard.jpg Main window screenshot
├── build.ps1 Build script — auto-detects VS + SDK via vswhere.exe
└── LICENSE.md
Source code (x64/*.asm, x64/*.inc, x64/vg.rc, x64/vg.manifest, build.ps1,
tests/, IcoBuilder/vg.ico) — MIT License.
See LICENSE.md.
IcoBuilder/vg.sys — property of PROMOSOFT CORPORATION.
This kernel minifilter driver binary was signed under a cross-signing certificate in 2014.
It is included here solely to enable building and running VaultGuard on supported systems.
All rights to vg.sys remain with PROMOSOFT CORPORATION.
Author: Marek Wesołowski (WESMAR)
Contact: marek@wesolowski.eu.org
GitHub: https://github.com/wesmar/VaultGuard
