Design Philosophy
CS2ExternDrv was designed to be the absolute minimum kernel driver needed to provide stealthy memory access for an external cheat. The key design decisions were driven by one goal: minimize detectable surface area.
- No Device Object — No \Device\Xxx to enumerate
- No Symbolic Link — No \DosDevices\Xxx to query
- No IOCTL Interface — All communication via shared memory
- Minimal Footprint — Single section, polling thread only
Shared Memory Protocol
Traditional drivers create a device object and expose IOCTL codes. This driver creates only a named section that user-mode opens by name:
Kernel creates: \BaseNamedObjects\CS2ExternShared
User-mode opens: Global\CS2ExternShared
Structure:
Magic (4 bytes) - 0x4B3A7F2E
Status (4 bytes) - PENDING/COMPLETED/ERROR
Command (4 bytes) - READ/WRITE/GET_BASE/SET_PID
OperationCounter - User increments to signal new command
ProcessId (4 bytes)
Address (8 bytes)
Size (4 bytes)
Data[4096] - Payload buffer
Why No IOCTL?
IOCTL-based drivers have several detectable components:
- Device object visible in \Device\ namespace
- Symbolic link in GLOBALROOT or user sessions
- IOCTL dispatch routines in driver object
- Predictable IRP patterns
By using shared memory only, the driver only creates a section object. This is significantly less visible and works well with manual mappers that may not properly set up device objects anyway.
Security Model
Process Whitelisting
The driver implements a write-once whitelist:
- First SET_PID command sets the allowed process ID
- Subsequent SET_PID attempts return STATUS_ACCESS_DENIED
- All memory operations are validated against this PID
This prevents an attacker who compromises the user-mode component from using the driver to attack arbitrary processes.
Address Validation
- Kernel addresses (> 0x7FFFFFFF0000) are rejected
- NULL and low memory (< 0x10000) are rejected
- System process (PID 4) is blocked
- Maximum 4KB per operation
Integration Flow
1. User loads driver via kdmapper
2. Driver creates shared section, starts polling thread
3. User-mode cheat opens section via OpenFileMapping
4. Cheat sets target PID (CS2 process)
5. For each memory read:
- Write address/size to shared memory
- Increment OperationCounter
- Driver detects change, performs read
- Driver writes data, sets COMPLETED status
- User-mode reads data
Building and Loading
The driver is built with WDK as a standard kernel driver, but loaded via kdmapper rather than SCM:
- Build: Release | x64 produces CS2ExternDrv.sys
- Load: kdmapper.exe CS2ExternDrv.sys DriverMain
- No test signing required
- No registry entries created
What I Learned
This minimal approach taught me that drivers can be much smaller than typical examples suggest. The shared memory approach, while less efficient than IOCTL for high-throughput scenarios, is perfectly adequate for game cheating use cases where you're reading at display refresh rates.
The main challenge was synchronization: ensuring the polling thread doesn't miss commands while avoiding excessive CPU usage. The solution was exponential backoff with a cap (1ms to 50ms sleep based on activity).