Skip to content

wesmar/VaultGuard

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

VaultGuard

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.

VaultGuard main window


Table of Contents


Origin

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.

Forum thread: https://forums.mydigitallife.net/threads/vaultguard-%E2%80%94-folder-file-protection-in-pure-x64-assembly-60-kb-no-crt-win11-mica.90355/

Thanks to BetaTesta for the idea, the original binary, and the beta testing.


Overview

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

Quick Start

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 .lnk shortcut → resolved to real target automatically via IShellLink
  • Drag an .exe file 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+Click multiple 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.


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

Protection Flags

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.


GUI Reference

Launch vg.exe without arguments. Fixed 680 × 472 px window.

Main 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.

System Tray

  • Shift+Minimize — hides window and adds tray icon
  • /tray switch — starts directly as a tray-only process (hidden window)
  • Double-click tray icon — restores main window
  • Right-click tray icon — context menu (Restore / Exit)
  • TaskbarCreated message — 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

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

Trusted Processes Panel

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

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

CSV Output Formats

/enumitems — UTF-16LE with BOM:

Path,Hidden,Locked,ReadOnly,NoExec
C:\Private,0,1,0,0

/enumtrusted — UTF-16LE with BOM:

Application
totalcmd64.exe

Exit Codes

Code Meaning
0 Success
1 Unknown switch / bad argument / driver error

Service & Autostart

Windows Service (/service install)

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_SvcStartStartServiceCtrlDispatcherW. 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.

Driver Service (/driver ...)

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:

  • 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
  • manual / auto call ChangeServiceConfigW for the existing clrcd service
  • start / stop call StartServiceW / ControlService
  • uninstall stops and deletes clrcd, then deletes %SystemRoot%\System32\drivers\vg.sys best-effort

Full Uninstall (/uninstall)

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.

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


Use Cases

Lock a folder, allow one trusted process

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 Disabled

Hide a folder from Explorer

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

The 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).

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 normally.

Set up persistent elevated autostart

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

# To remove:
vg.exe /autostart off

Install as a Windows service (manual start)

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

# To remove:
vg.exe /service uninstall

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

Import-Csv -Encoding Unicode reads UTF-16LE correctly on both Windows PowerShell 5.1 and PowerShell 7.


Driver Communication

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

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


Registry Layout

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.


Module Analysis

17 MASM source files, each with a single defined responsibility.

main.asm — Entry Point & Globals

Entry point: mainCRTStartup

Startup sequence:

  1. GetStdHandle(STD_OUTPUT_HANDLE) + GetFileTypeAttachConsole(-1) if no TTY
  2. GetCommandLineWCommandLineToArgvWlea rdx, [rsp+20h] as &argc
  3. argc >= 2CliDispatch(argv[1], argv, argc); returns 0 (unknown) or argc < 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

window.asm — Window Skeleton

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

layout.asm — Control Creation

_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).


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 a user-defined message (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, Notify & 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: 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.

UpdateStatusBarEnsureDriverReadyIoctlGetStatus → updates title bar and toggle button text. Repaint suppressed when state has not changed.

RefreshListsIoctlEnumPaths + IoctlEnumTrustedLVM_DELETEALLITEMS_LvInsertItem for each entry.


drop.asm — Drag & Drop

_OnDropFiles(rcx=HDROP, rdx=hMainWnd):

  1. DragQueryFileW(0) → first dropped path into g_pathBuf
  2. Detects drop target: if cursor Y-position is over the Trusted panel → route to trusted add path
  3. Last 4 chars are .lnk (via wcscmp_ci) → call ResolveLnkPathGetLongPathNameW (canonical case)
  4. Trusted panel drop: extracts filename component from path → lowercase → IoctlAddTrusted + ConfigSaveTrusted
  5. Paths panel drop: auto-commits any prior g_pendingPath to registry via ConfigSavePath(flags=0) before overwriting; 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 (name=VaultGuard, DEMAND_START, binary="<exe>" /svcstart) → StartServiceW
_CliServiceUninstall Opens SCM → OpenServiceWControlService(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) is registered via RegisterServiceCtrlHandlerExW (the extended variant) to receive clean shutdown notification before system-level services stop.


Driver Layer — SCM, Device, IOCTL

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)

config.asm — Registry Persistence

Procedure Description
ConfigLoad Enumerates PathsIoctlAddPath each; enumerates TrustedIoctlAddTrusted each; calls IoctlSetActive(1)
ConfigSavePath(rcx=path, rdx=flags) RegCreateKeyExWRegSetValueExW(path, flags)
ConfigRemovePath(rcx=path) RegOpenKeyExWRegDeleteValueW
ConfigSaveTrusted(rcx=name_lowercase) RegCreateKeyExWRegSetValueExW(name, 1)
ConfigRemoveTrusted(rcx=name) RegOpenKeyExWRegDeleteValueW
ConfigDeleteAll Deletes Paths, Trusted, then HKCU\Software\VG via RegDeleteKeyW

cli.asm — Command-Line Interface

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)


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 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.


res.asm — Driver Extraction (FDI)

The driver vg.sys is embedded inside vg.exe as a resource: an 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 handle is 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

Building from Source

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.


Regression Tests

tests/cli_test.ps184 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.


Known Limitations

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

Project Layout

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

License

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

About

Folder & file protection via kernel FSFilter minifilter - pure x64 MASM assembly, zero CRT, ~ 60 KB. Hide, lock, set read-only or block execution at kernel level. Windows 11 GUI with Dark Mode + Mica, fully scriptable CLI, drag & drop with .lnk resolution, per-process trusted bypass. No installer, no runtime dependencies.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors