2026-06-05 — Multi-select trusted list, running process picker, import/export GUI, .exe auto-normalize, expanded layout

VaultGuard (vg.exe) is a complete rewrite of a 12-year-old Qt/C++ folder-protection suite in pure x64 MASM — zero CRT, native WinAPI only, Windows 11 Dark Mode + Mica + system tray. The same binary acts as a full Win32 GUI, a scriptable CLI, or a Windows service depending on its arguments. It communicates with vg.sys, a kernel-mode FSFilter Content Screener minifilter signed by PROMOSOFT CORPORATION (2014), which loads on Windows 11 26H1 via Microsoft's backward-compatibility mechanism for cross-signed drivers predating 29 July 2015.

VaultGuard — Kernel-Backed Folder Protection for Windows

VaultGuard


**Pure x64 MASM application — zero CRT, native WinAPI, Windows 11 Dark Mode + Mica + system tray** *Communicates with a kernel minifilter driver via reverse-engineered IOCTL codes* *Under 100 KB binary — GUI with flag checkboxes, system tray, service, autostart, or fully scriptable CLI*

Table of Contents


Overview

VaultGuard is an access-control tool for Windows directories and files, backed by a kernel-mode driver of type Minifilter (FSFilter Content Screener). The project is a ground-up rewrite of the original 12-year-old VaultGuard (Qt/C++, split GUI + CLI) into pure x64 MASM assembly. The primary design goals are:

Goal Implementation
Zero CRT No msvcrt, ucrtbase, or vcruntime. All memory, string, and file operations go through native Win32 API directly
Minimal footprint Compiled vg.exe is under 100 KB — the original Qt/C++ suite was ~8 MB
Modern UX (Windows 11) Dark Mode, Mica material (DWM API), PerMonitorV2 DPI scaling, system tray, flicker-free rendering
Dual-head binary Same binary: no arguments → rich GUI; recognized argument → scriptable CLI or service mode
Persistent startup Task Scheduler logon task (/rl highest) or Windows service (DEMAND_START), both elevated, no UAC prompt
Driver backward compatibility Original vg.sys signed by PROMOSOFT CORPORATION (2014 certificate) loads correctly on Windows 11 26H1 via Microsoft's cross-signed driver legacy mechanism

Architecture

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 CLI| 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]

GUI Reference

Launching vg.exe without arguments (or with an unrecognized argument) starts the graphical interface. Because the application installs and communicates with a kernel driver, it requires Administrator privileges (enforced via requireAdministrator in the manifest).

Main Window

The main window (class VGMainWnd) has a fixed size of 700 × 550 pixels and uses a native Win32 window with a Mica backdrop and a dark title bar when the system is in Dark Mode.

Driver and protection state is embedded dynamically in the window title:

VaultGuard | Driver: STOPPED   | Protection: OFF
VaultGuard | Driver: TRANSIENT | Protection: ON

This state refreshes every 2 seconds via WM_TIMER and after every user action.

System Tray

Action Behavior
Shift+Minimize Hides window to system tray
/tray switch Starts directly as tray-only (hidden window)
Double-click tray icon Restores main window
Right-click tray icon Context menu: Restore / Exit
TaskbarCreated broadcast If Explorer restarts (crash or logon race), VaultGuard re-registers its tray icon automatically

UIPI note: When launched elevated via Task Scheduler (/rl highest), ChangeWindowMessageFilterEx is called during WM_CREATE to allow the TaskbarCreated registered 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.

Protected Folders Panel

The upper section contains a ListView of protected paths.

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) Clicking any flag cell immediately toggles the checkbox and sends an IOCTL update to the driver
Drag & Drop Accepts folders and files from Explorer; .lnk shortcuts resolved via COM IShellLink; .exe files dropped here are added as protected items

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

Trusted Processes Panel

The lower section defines processes that bypass all driver protections.

Control Behavior
Edit box Enter the process executable name (e.g. totalcmd64.exe)
[Add] button Strips path prefix (e.g. C:\dir\app.exeapp.exe), appends .exe if extension missing, lowercases → IoctlAddTrusted + ConfigSaveTrusted
[Add running] button Opens "Select running process" dialog (procpicker.asm) — Ctrl/Shift multi-select — adds all selected processes at once
[Remove] button Multi-select: ConfigRemoveTrusted per selected item → IoctlRemoveTrusted(empty)ConfigLoad (reloads remaining) → RefreshLists
[Export] button GuiExportConfig — opens Save dialog → writes .vgc file (UTF-16LE, [Paths]/[Trusted] sections)
[Import] button GuiImportConfig — opens Open dialog → parses .vgcConfigLoad + RefreshLists
Drag & Drop Drop an .exe file or .lnk shortcut onto this panel — executable name extracted and added as trusted process directly

Note: Removing sends an empty IoctlRemoveTrusted that wipes the entire active trusted list in the driver. ConfigLoad immediately reloads all remaining registry entries. The driver provides no per-item removal IOCTL.


CLI Reference

CLI mode is activated when vg.exe is launched with at least one recognized argument.

vg.exe /?
vg.exe /enumitems    <outfile.csv>
vg.exe /enumtrusted  <outfile.csv>
vg.exe /protection   on | off
vg.exe /setitem      <path>  Hidden|Locked|Read-only|No-execution|Disabled
vg.exe /settrusted   <name.exe>  Enabled|Disabled
vg.exe /tray
vg.exe /autostart    on | off
vg.exe /service      install | uninstall
vg.exe /driver       install [manual | auto]
vg.exe /driver       uninstall
vg.exe /driver       start | stop
vg.exe /driver       startup manual | auto
vg.exe /uninstall
/p <password>  — parsed and silently ignored (driver has no password layer)

Command Summary

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 uninstall Stop and remove clrcd; delete extracted vg.sys best-effort vg.exe /driver uninstall
/driver start\|stop Runtime start or stop of clrcd (current session) vg.exe /driver start
/driver startup manual\|auto Change clrcd start type via ChangeServiceConfigW (next boot) — running driver is not stopped vg.exe /driver startup auto
/uninstall Full cleanup: disable protection, remove app service, remove driver service/file, delete HKCU config vg.exe /uninstall

Protection Modes for /setitem

Mode Effect
Hidden Directory becomes invisible in Explorer and dir listings
Locked All access attempts return ACCESS_DENIED
Read-only FILE_WRITE_DATA and DELETE bits stripped from DesiredAccess
No-execution Execute bits stripped from DesiredAccess
Disabled Path remains in registry with flags=0; inactive in driver

CSV Output Formats

/enumitems output:

<BOM>Path,Hidden,Locked,ReadOnly,NoExec\r\n
C:\temp\aaa,1,0,0,0\r\n

/enumtrusted output:

<BOM>Application\r\n
totalcmd64.exe\r\n

Both files are written in UTF-16LE with BOM. /enumitems reads from registry (HKCU\Software\VG\Paths), not from the IOCTL buffer.

Exit Codes

Code Meaning
0 Success
1 Unknown switch, bad argument, or driver error

Every CLI exit path goes through _CliFinish(code), which injects a VK_RETURN keystroke via WriteConsoleInputW so the CMD prompt reappears without waiting for Enter.


Service & Autostart

Windows Service (/service install)

vg.exe /service install
vg.exe /service uninstall

Registers vg.exe as a Windows service named VaultGuard:

  • Start type: SERVICE_DEMAND_START — manual start (immediately started after creation via StartServiceW)
  • Binary path: "<full path to vg.exe>" /svcstart
  • On install: service is created and immediately started via StartServiceW
  • On uninstall: service is stopped and deleted

The internal /svcstart switch is dispatched by CliDispatch_SvcStartStartServiceCtrlDispatcherW. 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.

Logon Autostart (/autostart on)

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
  • 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\Run entries are silently skipped for processes with requireAdministrator manifest.

Driver Service (/driver ...)

vg.exe /driver install
vg.exe /driver install auto
vg.exe /driver uninstall
vg.exe /driver start
vg.exe /driver stop
vg.exe /driver startup manual
vg.exe /driver startup auto

Manages the real protection service, clrcd, through WinAPI/SCM calls only:

  • install extracts vg.sys, creates the kernel driver service and leaves it SERVICE_DEMAND_START by default
  • install auto creates the service and changes start type to SERVICE_AUTO_START
  • start / stop call StartServiceW / ControlService (current session)
  • startup manual / startup auto call ChangeServiceConfigW for the existing clrcd service — SCM rewrites HKLM\SYSTEM\CurrentControlSet\Services\clrcd\Start on the fly; a running driver is not stopped
  • uninstall stops and deletes clrcd, then deletes %SystemRoot%\System32\drivers\vg.sys best-effort

Full Cleanup (/uninstall)

vg.exe /uninstall

Single-shot teardown of every persistent VaultGuard footprint:

  1. Disable protection (IoctlSetActive(0)) if driver reachable
  2. Stop + delete app service VaultGuard via SCM
  3. Stop + delete driver service clrcd via SCM
  4. Delete %SystemRoot%\System32\drivers\vg.sys best-effort
  5. Remove HKCU\Software\VG registry tree (Paths, Trusted)

All steps are best-effort — stale partial installs can be flushed in one pass even if individual steps fail.


Use Cases

Scenario 1: Lock a Folder and Allow a Specific Application

vg.exe /protection on
vg.exe /setitem "C:\Private" Locked
vg.exe /settrusted totalcmd64.exe Enabled

vg.exe /enumitems   items.csv
vg.exe /enumtrusted trust.csv

# Revoke when done
vg.exe /settrusted totalcmd64.exe Disabled

Scenario 2: Hide a Folder from Explorer

vg.exe /setitem "C:\Secret" Hidden

The folder disappears from Explorer, dir, and all directory enumeration APIs.

Scenario 3: Read-Only Archive

vg.exe /setitem "C:\Backups" Read-only

The driver strips FILE_WRITE_DATA and DELETE bits at the kernel level. Trusted processes can still write.

Scenario 4: Persistent Elevated Tray App at Logon

vg.exe /autostart on
# Task Scheduler entry created. VaultGuard starts at logon to tray, elevated, no UAC prompt.
vg.exe /autostart off   # remove

Scenario 5: Boot-Time Windows Service

vg.exe /service install
# Service VaultGuard created (DEMAND_START) and immediately started.
vg.exe /service uninstall

Scenario 6: Scripted Status Check

vg.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)"

Module Analysis

The source tree is organized into 19 MASM source files plus includes. Each file has a single clearly defined responsibility.

main.asm — Entry Point & Globals

Entry point: mainCRTStartup

Startup sequence:

  1. GetStdHandle(STD_OUTPUT_HANDLE) + GetFileTypeAttachConsole(-1) if no TTY
  2. GetCommandLineWCommandLineToArgvW
  3. argc >= 2CliDispatch(argv[1], argv, argc); returns 0 (unknown) or argc < 2 → 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

window.asm — Window Skeleton

Contains exclusively MainWndProc and CreateMainWindow. Fixed 700 × 550 px, class VGMainWnd.

On creation, calls RegisterWindowMessageW("TaskbarCreated") and stores the dynamic ID in g_wmTaskbarCreated (PUBLIC) — used in MainWndProc and to unlock the UIPI filter in _OnCreate.

Handled WM messages:

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 — tray icon mouse events
WM_DROPFILES _OnDropFiles (drop.asm)
WM_NOTIFY _OnNotify (handlers.asm) — flag checkboxes
WM_COMMAND _OnCommand (handlers.asm) — button clicks
WM_TIMER UpdateStatusBar every 2 seconds
WM_SETTINGCHANGE _ReadDarkMode + ApplyDarkMode + _ApplyThemeColors + InvalidateRect
WM_ERASEBKGND FillRect(g_hBrushBg)
WM_CTLCOLORSTATIC Dark mode: SetBkMode(OPAQUE) + colors + return g_hBrushBg
TaskbarCreated _TrayAdd — re-register tray icon after Explorer restart

layout.asm — Control Creation

_OnCreate(rcx=hwnd) creates all widgets in a single pass:

[y=  8] Toggle button (x=182)
[y=  8] [Add path...]  (x=364)  +  [Remove selected]  (x=504)
[y= 10] "Protected files/folders" header (x=20)
[y= 40] ListView Paths (w=624, h=220): columns Path/H/L/R/X — multi-select, checkboxes
[y=278] "Allowed apps (trusted)" header (x=20)
[y=276] Trusted edit box (x=182)  +  [Add] (x=364)  +  [Add running] (x=440)  +  [Remove] (x=556)
[y=308] ListView Trusted (w=624, h=140, ~6 rows): 1 column Process name — multi-select
[y=454] [Export] (x=20)  +  [Import] (x=115)
[y=482] Author/copyright label (x=20, w=624, centered)

Key initialization calls:

  • InitCommonControlsEx(ICC_LISTVIEW_CLASSES)
  • DragAcceptFiles(TRUE)
  • ChangeWindowMessageFilterEx for WM_DROPFILES, WM_COPYDATA, WM_COPYGLOBALDATA, and TaskbarCreated — all MSGFLT_ALLOW

UIPI fix: The TaskbarCreated filter is required when running at High Integrity Level (elevated via Task Scheduler). Without it, the TaskbarCreated broadcast from Medium-IL Explorer is silently blocked, and the tray icon never reappears after logon.


tray.asm — System Tray

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_LBUTTONDBLCLKShowWindow(SW_RESTORE) + SetForegroundWindow; right-click → context menu (Restore / Exit)

WM_TRAY is WM_APP + 1. _TrayAdd sets uCallbackMessage = WM_TRAY so all tray icon mouse events are routed to MainWndProc.


theme.asm — Dark Mode & Colors

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

handlers.asm — Commands & Status

_OnCommand dispatch by control ID:

IDC Action
IDC_BTN_TOGGLE IoctlSetActive(!g_protActive)
IDC_BTN_ADD_PATH SHBrowseForFolderW → stage as g_pendingPathRefreshLists
IDC_BTN_REM_PATH Multi-select loop → IoctlAddPath(0) + ConfigRemovePath + LVM_DELETEITEM
IDC_BTN_ADD_TRUSTED Strip path prefix, append .exe if missing, lowercase → IoctlAddTrusted + ConfigSaveTrusted
IDC_BTN_ADD_RUNNING ShowProcPicker(hwnd) → on return > 0: RefreshLists
IDC_BTN_REM_TRUSTED Multi-select loop → ConfigRemoveTrusted per item; IoctlRemoveTrusted(empty) + ConfigLoad + RefreshLists
IDC_BTN_EXPORT GuiExportConfig(hwnd) — writes .vgc file via GetSaveFileNameW
IDC_BTN_IMPORT GuiImportConfig(hwnd) + ConfigLoad + RefreshLists

_OnNotify — NM_CLICK on Protected Paths ListView: col 1–4 → toggle flag bit → IoctlAddPath + ConfigSavePath + RefreshLists.

UpdateStatusBarEnsureDriverReadyIoctlGetStatus → updates title bar and toggle button text.

RefreshListsIoctlEnumPaths + IoctlEnumTrustedLVM_DELETEALLITEMS_LvInsertItem for each entry.


procpicker.asm — Running Process Picker

ShowProcPicker(rcx=hwndOwner)eax = count of processes added

Opens a modal dialog snapped flush against the right edge of the main window.

Feature Description
Process list Snapshot via CreateToolhelp32Snapshot + Process32FirstW/NextW; system processes filtered
Multi-select Ctrl+Click / range select; all selected entries submitted at once
Submit [OK] / double-click: EnsureDriverReadywcs_ascii_lower_inplaceIoctlAddTrusted + ConfigSaveTrusted per item → EndDialog(count)
Dark mode Full dark: WM_ERASEBKGND + WM_CTLCOLORSTATIC/BTN handlers + _SetLvColors for ListView
Positioning DwmGetWindowAttribute(DWMWA_EXTENDED_FRAME_BOUNDS=9) on both windows; compensates for invisible DWM shadow border to place dialog flush-right of main window at pixel precision

impexp.asm — GUI Config Import/Export

Procedure Description
GuiExportConfig(rcx=hwndOwner) GetSaveFileNameW (filter *.vgc) → creates file → writes UTF-16LE BOM + [Paths] section (path=decimal flags per line) + [Trusted] section (name=1 per line) → confirmation MessageBoxW
GuiImportConfig(rcx=hwndOwner) GetOpenFileNameW → reads .vgc file → parses [Paths]/[Trusted] sections → RegSetValueExW per entry; caller calls ConfigLoad + RefreshLists afterward

File format (.vgc, UTF-16LE with BOM):

[Paths]
C:\Private=2
C:\Secret=1
[Trusted]
totalcmd64.exe=1

drop.asm — Drag & Drop

_OnDropFiles(rcx=HDROP, rdx=hMainWnd) — handles WM_DROPFILES:

  1. DragQueryFileW(0) → first dropped path into g_pathBuf
  2. Detects drop target: if cursor Y-position is over Trusted panel → route to trusted add
  3. Last 4 chars are .lnkResolveLnkPathGetLongPathNameW
  4. Trusted panel drop: extracts filename component → lowercase → IoctlAddTrusted + ConfigSaveTrusted
  5. Paths panel drop: auto-commits prior g_pendingPath, stores resolved path in g_pendingPathRefreshLists
  6. DragFinish

ResolveLnkPath(rcx=.lnk path, rdx=out buf):

CoInitializeCoCreateInstance(CLSID_ShellLink)QueryInterface(IID_IPersistFile)IPersistFile::LoadIShellLinkW::GetPath → full COM release chain → CoUninitialize


service.asm — Windows Service Runtime

Procedure Description
_CliServiceInstall Opens SCM → CreateServiceW (DEMAND_START, binary="<exe>" /svcstart) → StartServiceW
_CliServiceUninstall Opens SCM → ControlService(STOP)DeleteService
_SvcStart Builds SERVICE_TABLE_ENTRYW[2] on stack → StartServiceCtrlDispatcherWExitProcess(0)
_SvcMain CreateEventW(manual-reset)RegisterServiceCtrlHandlerExWSetServiceStatus(RUNNING, accepts=STOP\|SHUTDOWN\|PRESHUTDOWN)WaitForSingleObject(INFINITE)SetServiceStatus(STOPPED)
_SvcCtrlHandler STOP/SHUTDOWN/PRESHUTDOWNSetServiceStatus(STOP_PENDING)SetEvent(stop_event)

SERVICE_ACCEPT_PRESHUTDOWN (0x100) requires RegisterServiceCtrlHandlerExW (extended variant).


driver_scm.asm — clrcd SCM Lifecycle

Service Control Manager glue for the kernel driver service clrcd.

Property Value
Service name clrcd
Display name Vault Guard Driver
Type SERVICE_KERNEL_DRIVER
Start SERVICE_DEMAND_START (default) — switchable to SERVICE_AUTO_START
Dependency FltMgr\0\0
Image path \SystemRoot\system32\drivers\vg.sys
Procedure Description
InstallDriver ExtractDriver()CreateServiceW (FSFilter Content Screener, Altitude 389991) → writes Instances subkey
StartDriver StartServiceW; treats ERROR_SERVICE_ALREADY_RUNNING and ERROR_ALREADY_EXISTS as success
StopDriver ControlService(SERVICE_CONTROL_STOP)
UninstallDriver StopDriverDeleteService
SetDriverStartType(ecx=startType) ChangeServiceConfigW — atomically updates HKLM\...\clrcd\Start; running driver not stopped
DeleteDriverFile Best-effort DeleteFileW on %SystemRoot%\system32\drivers\vg.sys
IsDriverInstalled OpenServiceW probe → boolean

device.asm — Device Handle + EnsureDriverReady

Owns the kernel device handle and the install-on-demand bootstrap.

Property Value
Device path \\.\BE79F7D853E643089D51EDCDA79805C4
Procedure Description
OpenDevice CreateFileW("\\.\BE79...", GENERIC_READ\|GENERIC_WRITE, ...) — returns handle or INVALID_HANDLE_VALUE
CloseDevice CloseHandle + clears cached handle
EnsureDriverReady OpenDevice → on fail → InstallDriverStartDriverOpenDevice retry → returns boolean ready flag

ioctl.asm — DeviceIoControl Wrappers

Thin marshalling layer over DeviceIoControl for every IOCTL the driver exposes.

IOCTL Codes (reverse-engineered from vg.sys)

Constant Value Description
IOCTL_VG_ADD_PATH 0x9C402400 Add/update path (flags=0 → Disabled)
IOCTL_VG_ENUM_PATHS 0x9C402404 Retrieve protected paths list
IOCTL_VG_ADD_TRUSTED 0x9C402408 Add trusted process
IOCTL_VG_REMOVE_TRUSTED 0x9C402408 Empty input (size=0) clears entire list
IOCTL_VG_ENUM_TRUSTED 0x9C40240C Retrieve trusted processes list
IOCTL_VG_SET_ACTIVE 0x9C40241C Enable/disable protection (DWORD, 4 bytes)
IOCTL_VG_GET_STATUS 0x9C402420 Retrieve VG_STATUS (16 bytes)
IOCTL_VG_CLEAR_ALL 0x9C402424 Full reset

IoctlAddPath always sends the fixed SF_ORIG_PATH_INPUT_SIZE (0x6414) buffer regardless of actual path length — smaller buffers cause the driver to accept the IOCTL but silently skip the rule.


config.asm — Registry Persistence

Procedure Description
ConfigLoad Enumerates PathsIoctlAddPath each; TrustedIoctlAddTrusted each; IoctlSetActive(1)
ConfigSavePath(rcx=path, rdx=flags) RegCreateKeyExWRegSetValueExW
ConfigRemovePath(rcx=path) RegOpenKeyExWRegDeleteValueW
ConfigSaveTrusted(rcx=name_lowercase) RegCreateKeyExWRegSetValueExW(name, 1)
ConfigRemoveTrusted(rcx=name) RegOpenKeyExWRegDeleteValueW

cli.asm — Command-Line Interface

Switch comparison uses wcscmp_ci — ASCII case-insensitive wide compare, no CharLowerW. All switches work regardless of capitalization.

Every exit path goes through _CliFinish(code)ConsoleSendEnter() (injects VK_RETURN via WriteConsoleInputW) so the CMD prompt reappears immediately.

Switch dispatch order: /?/service/driver/uninstall/enumitems/enumtrusted/protection/setitem/settrusted/svcstart/tray/autostart → unknown (return 0 → GUI)

RunCmdAndWait(rcx=lpCommandLine) — helper used by /autostart: launches process hidden (CREATE_NO_WINDOW), waits for exit, returns exit code.


export.asm — CSV Export

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 → ExitProcess(0)
_CliEnumTrusted(rcx=outfile) Verifies driver ready → opens file → writes BOM + header → enumerates HKCU\Software\VG\Trusted → one name per row → ExitProcess(0)
_WriteBytes (private) WriteFile wrapper
_WriteWStr (private) Wide string → wcslen_p_WriteBytes

CSV is written from registry (authoritative persisted state), not from the driver IOCTL buffer which can lag after flag-change operations.


res.asm — Driver Extraction (FDI)

The driver vg.sys is embedded inside vg.exe as a resource: LZX CAB appended to the ICO file header.

  1. FindResourceW(NULL, IDR_DRIVER=102, RT_RCDATA=10) → resource pointer
  2. LockResource → raw bytes; CAB starts at offset 1078 bytes
  3. All FDI callbacks operate in memory — no temp files on disk
  4. FDICopy → heap buffer → WriteFile to %SystemRoot%\system32\drivers\vg.sys

CAB is packed at ~42% of original size via LZX compression.


strutil.asm — String Utilities

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 not a console
WideWriteLn rcx=str WideWriteConsole(stdout, str) + CRLF
ConsoleSendEnter Injects VK_RETURN via WriteConsoleInputW

listview.asm — ListView Wrappers

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

Driver Communication Protocol

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]

IOCTL Buffer Formats

IoctlAddPath(rcx=flags, rdx=DOS path):

  • QueryDosDeviceW("C:") → NT prefix \Device\HarddiskVolume3
  • Buffer: [0..3] = DWORD flags, [4..] = NT path as WCHAR
  • nInBufSize = 0x6414 (fixed — smaller buffer causes driver to accept but not apply the rule)

IoctlAddTrusted(rcx=process name lowercase):

  • Buffer g_ioBuf: [0..3] = 0, [4..] = WCHAR process name
  • nInBufSize = 0xD94 (fixed record, padded with zeros)

IoctlRemoveTrusted:

  • nInBufSize = 0 → driver clears entire active trusted list
  • Caller must call ConfigLoad afterward to reload remaining entries

IoctlGetStatus → output buffer VG_STATUS (16 bytes):

Offset Type Description
0 BYTE IsActive (protection enabled)
4 DWORD PathCount
8 DWORD TrustedCount
12 DWORD Version

Protection Flags

Flag Hex Driver behavior
VG_FLAG_HIDDEN 0x01 PreCreateSTATUS_OBJECT_NAME_NOT_FOUND; directory listing entry removed
VG_FLAG_LOCKED 0x02 PreCreateSTATUS_ACCESS_DENIED
VG_FLAG_READONLY 0x04 Strips FILE_WRITE_DATA and DELETE from DesiredAccess
VG_FLAG_NOEXEC 0x08 Strips execute bits from DesiredAccess

Flags combine as bitmask: Hidden + Locked = 0x03, Hidden + Locked + Read-only = 0x07. Disabled = 0x00 means path stored in registry, inactive in driver.


Registry Layout

HKEY_CURRENT_USER\
└── Software\
    └── VG\
        ├── Paths\
        │     "C:\Private\Data"   REG_DWORD  0x00000003   (Hidden + Locked)
        │     "C:\Projects\Work"  REG_DWORD  0x00000004   (Read-only)
        │     "C:\Temp\Archive"   REG_DWORD  0x00000000   (Disabled)
        └── Trusted\
              "totalcmd64.exe"    REG_DWORD  0x00000001
              "explorer.exe"      REG_DWORD  0x00000001

ConfigLoad enumerates both subkeys and reloads full configuration into the driver. The driver holds no persistent state across reboots.


Build System

Property Value
Assembler ml64.exe — MASM x64 (auto-detected via vswhere.exe)
Standard x64 MASM, zero CRT
Output bin\vg.exe (under 100 KB)
Subsystem Windows GUI — CLI attaches/allocates console at runtime
Build script build.ps1 — 4 steps, verifies imports via dumpbin

Build Steps

.\build.ps1
[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_scm device ioctl config cli export service theme listview handlers drop tray layout procpicker impexp 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

-SkipRC skips steps 0 and 1 (requires pre-existing vg.res). Intermediates removed on completion.


Project Structure

VaultGuard\
├── x64/
│   ├── consts.inc       EQU constants: IOCTL codes, flags, struct offsets, control IDs
│   ├── globals.inc      EXTRN declarations for globals from main.asm
│   ├── main.asm         Entry point, global data, message loop
│   ├── window.asm       MainWndProc + CreateMainWindow
│   ├── layout.asm       _OnCreate: all controls + UIPI message filters
│   ├── tray.asm         System tray: _TrayAdd, _TrayRemove, _OnTrayMsg
│   ├── theme.asm        Dark mode, Mica, ListView colors
│   ├── handlers.asm     _OnCommand, _OnNotify, UpdateStatusBar, RefreshLists
│   ├── procpicker.asm   ShowProcPicker — running process picker dialog
│   ├── impexp.asm       GuiExportConfig, GuiImportConfig — .vgc file import/export
│   ├── drop.asm         WM_DROPFILES + ResolveLnkPath (IShellLink COM) + trusted panel routing
│   ├── service.asm      Windows service runtime: _SvcStart, _SvcMain, _SvcCtrlHandler
│   ├── listview.asm     _LvAddColumn, _LvInsertItem, _LvGetItemText
│   ├── driver_scm.asm   clrcd SCM lifecycle (Install/Start/Stop/Uninstall/SetStartType)
│   ├── device.asm       Device open/close + EnsureDriverReady
│   ├── ioctl.asm        DeviceIoControl wrappers + path/trusted marshalling
│   ├── config.asm       ConfigLoad/Save/Remove for Paths and Trusted (registry)
│   ├── cli.asm          CliDispatch + RunCmdAndWait + _CliAutostart
│   ├── export.asm       _CliEnumItems, _CliEnumTrusted — CSV export
│   ├── strutil.asm      String utilities + WideWriteLn/WideWriteConsole
│   └── res.asm          ExtractDriver — FDI decompression of CAB from icon
├── tests/
│   └── cli_test.ps1     84 regression checks (CLI, driver lifecycle, registry, CSV, no GUI required)
├── IcoBuilder/
│   ├── vg.sys           Original driver (source for packaging)
│   └── 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

Regression Tests

tests/cli_test.ps1 contains 84 regression checks (CLI interface, no GUI required).

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

Known Limitations

Item Status
wcscmp_ci ASCII only (A-Z). Non-ASCII paths use case-sensitive comparison
COM apartment CoInitialize/CoUninitialize at every .lnk resolution; safe for GUI use
Password mode /p parsed and silently ignored; driver has no password layer
Light mode GUI works; ListView colors fall back to system defaults
Trusted list removal No per-item IOCTL — driver only supports clearing entire list + full reload
Multi-file drop Only first dropped file processed per WM_DROPFILES; others discarded
Service + GUI Service mode runs full GUI; no headless-only service build

License

MIT License — full text in LICENSE.md.

WARNING: This tool installs a kernel-mode driver (vg.sys) and requires Administrator privileges. The original vg.sys is the property of PROMOSOFT CORPORATION. Use on systems you own or have permission to modify.


Last updated: 2026-06-05