A Windows x64 kernel driver (mtxC9CB.sys) providing unrestricted hardware-level access to user-mode processes. Originally compiled as MtxVxd.sys (builded on 2013-05-17), this driver exposes physical memory mapping, PCI configuration space operations, I/O port access, MMIO mapping and kernel pool allocation. Actually related to Matrox Graphics Inc.
| Field | Value |
|---|---|
| PDB string | d:\usr\fboucher\mtxvxd-daily\build-20130517\source\mtxvxdsysfull\objfre_wnet_AMD64\amd64\MtxVxd.pdb |
| Build date | 2013-05-17 |
| Original name | MtxVxd.sys |
| Renamed to | mtxC9CB.sys |
| Architecture | x86-64 (AMD64) |
| Sections | .text (~8KB), .idata, .rdata, .data, .pdata, INIT |
| SHA256 | 0414c0d5bb6ddbcc84b3d59ce411acf1ed8b17d17054c6192e0a7594b5146d60 |
The PDB path suggests this was built as part of a daily build pipeline (mtxvxd-daily). The objfre_wnet_AMD64 path indicates the Windows .NET (WNET) WDK platform, targeting Windows Server 2003 / XP x64 era. Fortunately it still loads on Windows 10.
| Field | Value |
|---|---|
| Device name | \Device\MtxVxd |
| Symbolic link | \DosDevices\DosMtxVxd -> accessible as \\.\DosMtxVxd from user mode |
| Device type | 0x22 (FILE_DEVICE_UNKNOWN) |
| Buffering method | METHOD_BUFFERED |
| Security descriptor | Not set. Basically no ACL on the device object |
The driver creates a device object and symbolic link, registering three dispatch routines:
IRP_MJ_CREATE(0) ->DispatchPassThroughIRP_MJ_CLOSE(2) ->DispatchPassThroughIRP_MJ_DEVICE_CONTROL(14) ->IrpDeviceControlDriverUnload->DriverUnload
DispatchPassThrough returns STATUS_SUCCESS unconditionally. Any process can open the device. DriverUnload deletes the symbolic link and device object.
Application -> \\.\DosMtxVxd -> IOCTL -> IrpDeviceControl() -> IoctlDispatch() -> handler
IrpDeviceControl extracts the IRP stack parameters and calls IoctlDispatch. Return values are boolean (1 = success, 0 = failure). The IOCTL handler then converts this to STATUS_SUCCESS or STATUS_UNSUCCESSFUL.
All IOCTL codes use METHOD_BUFFERED. The input buffer structure varies per function but most share a common header layout.
| IOCTL Code | Function | Description | Input size | Output size |
|---|---|---|---|---|
0x9C406400 |
inline | Get version (returns 0x55AA0001) |
any | 4 |
0x9C406404 |
IoPortRead |
Read from I/O ports | >=16 | variable |
0x9C406408 |
IoPortWrite |
Write to I/O ports | >=16 | variable |
0x9C40640C |
PciConfigRead |
Read PCI configuration space | >=16 | variable |
0x9C406410 |
PciConfigWrite |
Write PCI configuration space | >=16 | variable |
0x9C406414 |
MemCopyAligned |
Aligned memory copy (forward) | 16 | variable |
0x9C406418 |
MemCopyAlignedReverse |
Aligned memory copy (reverse) | >=16 | variable |
0x9C40641C |
stub | Returns 1 | any | 8 |
0x9C406420 |
stub | Returns 1 | 16 | any |
0x9C406424 |
ValidateCommandType0 |
Validate PCI command (Type 0 params) | >=16 | variable |
0x9C406428 |
ValidateCommandType1 |
Validate PCI command (Type 1 params) | >=16 | variable |
0x9C40642C |
stub | Returns 1 | any | 8 |
0x9C406430–0x9C406440 |
stubs | Returns 0 (unimplemented) | any | any |
0x9C406444 |
stub | Returns 1 | any | 16 |
0x9C406448 |
stub | Returns 0 (unimplemented) | any | any |
0x9C40644C |
MapPhysicalMemory |
Map arbitrary physical memory into caller's address space | 16 | 8 |
0x9C406450 |
inline | Unmap previously mapped physical memory | 8 | — |
0x9C406454 |
stub | Returns 1 | any | any |
0x9C406458 |
stub | Returns 1 | any | any |
0x9C40645C |
PciGetBarInfo |
Get PCI BAR address and size | any | >=16 |
0x9C406460 |
PciFindCapability |
Find PCI capability by ID | any | >=4 |
0x9C406464 |
AllocateContiguousMemory |
Allocate contiguous physical memory | 24 | 24 |
0x9C406468 |
FreeContiguousMemory |
Free contiguous physical memory | 24 | 24 |
0x9C40647C |
inline | Allocate kernel pool (ExAllocatePoolWithTag, tag 'dDk ') |
8 | 8 |
0x9C406480 |
inline | Free kernel pool (ExFreePoolWithTag) |
8 | — |
0x9C406484 |
MapMmioSpace |
Map MMIO physical region via MmMapIoSpace |
24 | 24 |
0x9C406488 |
inline | Unmap MMIO region via MmUnmapIoSpace |
24 | 24 |
The code 0x9C4064XX decodes to:
- Device type:
0x22(FILE_DEVICE_UNKNOWN) - Function code:
0x1900 + XX(i.e.,0x1900–0x1988) - Method:
METHOD_BUFFERED(0) - Access:
FILE_ANY_ACCESS(0)
The most interesting function in the driver. It maps physical memory into user-mode space by opening the \Device\PhysicalMemory section object:
Input: { uint64_t PhysicalAddress; uint32_t Size; uint32_t Flags; }
Output: { uint64_t VirtualAddress; }
Steps:
- Opens
\Device\PhysicalMemorywithSECTION_ALL_ACCESS - Resolves the section object via
ObReferenceObjectByHandle - Calls
ZwMapViewOfSectioninto the calling process withViewShare - Adjusts the returned base address by the offset between requested and actual section offset
The Flags parameter selects page protection:
0->PAGE_READWRITE(0x204)1->PAGE_READWRITE | PAGE_NOCACHE(0x404)- any other ->
PAGE_READWRITE(0x04: this is the same as 0x204 sans the SEC-based flags, effectively the same result)
The returned VA is offset-adjusted so the caller can read/write PhysicalAddress directly.
Unmap (IOCTL 0x9C406450): calls ZwUnmapViewOfSection with -1 as process handle.
Reads from PCI configuration space. First tries HalGetBusDataByOffset. If that fails (returns a different byte count), falls back to direct port I/O:
- Writes address to
0xCF8(CONFIG_ADDRESS) - Reads data from
0xCFC(CONFIG_DATA) - Handles unaligned reads by doing byte/word/dword at appropriate port offsets
- Restores original
0xCF8value after completion
Same pattern as read, but writes via 0xCF8/0xCFC ports. Tries HalSetBusDataByOffset first.
Helper that writes a single DWORD to PCI config. Used by PciGetBarInfo.
Scans the PCI capabilities linked list for a specific capability ID:
- Reads status register at offset 0x04, checks bit 4 (Capabilities List bit)
- Reads Capabilities Pointer at offset 0x34
- Walks the linked list (each entry: 1 byte cap ID + 1 byte next pointer) until it finds the target cap ID or exhausts the list
Determines a PCI BAR's base address and size using the standard probe technique:
- Read original BAR value
- Write
0xFFFFFFFFto the BAR - Read back the masked value -> size =
~(masked & 0xFFFFFFF0) + 1(computed by caller) - Restore original BAR value
Returns original base (lower 4 bytes masked) and the all-ones probe result.
Maps a physical address range as MMIO using MmMapIoSpace. Tries three cache types in order:
MmNonCachedMmWriteCombinedMmCached
Input/Output is 24 bytes: { uint64_t VirtualAddress; uint64_t PhysicalAddress; uint64_t Size; }. If the input VA is non-null, the function skips the mapping (acts as a no-op passthrough).
Unmap (IOCTL 0x9C406488): calls MmUnmapIoSpace if the input VA is non-null.
Reads from I/O ports using IN instructions. Input struct (at least 16 bytes):
{ int32_t PortAddress; int32_t Count; /* data at offset 8? no — Count at offset 8 */ }
Output: Count bytes read from PortAddress, PortAddress+1, ...
Handles unaligned port access by doing byte/word/dword reads as needed. No port range validation.
Writes to I/O ports using OUT instructions. Struct layout:
offset 0: int32_t PortAddress
offset 4: (padding)
offset 8: int32_t Count
offset 16: byte Data[] // data to write
Total struct size must be ≥16, and TotalSize - 16 == Count. No port validation.
Copies data from a source buffer to a destination with alignment optimization. Input struct:
{ void* SrcPtr; uint32_t Size; /* at offset 8 within src blob */ void* DstPtr; uint32_t Count; }
Validates that params.Size == 16 and the embedded size field matches Count, then copies aligning to dword/word/byte boundaries.
Same as MemCopyAligned but the layout is in reverse — the destination pointer comes from within the source structure.
These validate command structures for what appears to be a PCI or device command protocol. There are 24 possible command types (0–23), mapped to class/function pairs:
- Class 0 (functions 0–13): "PCI-like" commands (config read/write, memory operations)
- Class 1 (functions 0, 2, 3, 4): "Bridge-like" commands
- Class 2 (functions 0, 1, 2, 3, 6, 7): "Switch-like" commands
Command type 7 maps to class 0 / function 7 in Type0, and class 0 / function 7 in Type1. Type1 validates total size differently (params.Size - 8 == expected vs params.Count == expected in Type0). These differences suggest Type0 and Type1 correspond to PCI configuration space header types (Type 0 = device, Type 1 = PCI-to-PCI bridge).
Allocates physically contiguous memory. Input 24 bytes: { uint64_t VirtualAddress; uint64_t PhysicalAddress; uint64_t Size; }.
If VA is 0 and Size is even, calls MmAllocateContiguousMemory(align4k(Size), 0xFFFFFFFF), stores the VA and gets its physical address via MmGetPhysicalAddress. If VA is already non-zero, it's a query, so it returns the existing physical address.
Frees memory allocated by the above. Frees only if VA != 0 and the low bit of Size is 0. The low bit check on Size is odd. It might be a flag bit indicating ownership or type.
IOCTL 0x9C40647C: calls ExAllocatePoolWithTag(PoolType, Size, 'dDk '). If the allocation succeeds and there's an output buffer, writes the pointer. If the output buffer pointer (BytesReturned parameter) is null, frees immediately (leak test?).
IOCTL 0x9C406480: calls ExFreePoolWithTag(Pointer, 0). No validation on whether the pointer was previously allocated by this driver.
All imports come from ntoskrnl.exe (and hal.dll for two HAL functions):
| Function | Purpose |
|---|---|
IoCreateDevice |
Create the device object |
IoCreateSymbolicLink |
Create user-mode accessible symlink |
IoDeleteDevice |
Cleanup on unload |
IoDeleteSymbolicLink |
Cleanup on unload |
IoCompleteRequest |
Complete IRPs |
RtlInitUnicodeString |
Initialize string descriptors |
ZwOpenSection |
Open \Device\PhysicalMemory |
ZwMapViewOfSection |
Map physical memory into address space |
ZwUnmapViewOfSection |
Unmap physical memory |
ZwClose |
Close kernel handles |
ObReferenceObjectByHandle |
Get object from handle |
ObfDereferenceObject |
Release object reference |
MmMapIoSpace |
Map physical space for MMIO |
MmUnmapIoSpace |
Unmap MMIO space |
MmAllocateContiguousMemory |
Allocate contiguous physical RAM |
MmFreeContiguousMemory |
Free contiguous physical RAM |
MmGetPhysicalAddress |
Convert VA to physical address |
ExAllocatePoolWithTag |
Allocate paged/non-paged pool |
ExFreePoolWithTag |
Free pool allocation |
HalGetBusDataByOffset |
Read PCI configuration via HAL |
HalSetBusDataByOffset |
Write PCI configuration via HAL |
The driver registers the device without a security descriptor. IoCreateDevice's Exclusive flag is FALSE, and no IoCreateDeviceSecure is used. The default security for non-exclusive FILE_DEVICE_UNKNOWN devices allows all processes to open the device. DispatchPassThrough returns STATUS_SUCCESS unconditionally.
MapPhysicalMemory opens \Device\PhysicalMemory from kernel mode. This section object normally requires SeLockMemoryPrivilege (granted only to administrators and service accounts), but the driver opens it on behalf of any caller without checking. Once mapped, the physical pages are accessible with read/write from user mode, bypassing any kernel memory protection.
IoPortRead/IoPortWrite execute IN/OUT instructions on any port without validation. On x64, IN/OUT are only available from ring 0 (unless the TSS I/O permission bitmap allows it), so user-mode processes normally can't execute them.
Reads and writes PCI configuration space on any bus/device/function. Can reprogram BARs, enable bus mastering, or disable devices.
Maps arbitrary physical addresses as device MMIO. The IOCTL returns the VA in the caller's output buffer. The caller can then read/write device registers directly.
Allows allocating kernel pool with a user-specified pool type and size. Combined with the contiguous memory functions, this provides flexible kernel heap manipulation.
A proof-of-concept is included in poc.cpp demonstrating:
- Opening the device from user mode
- Physical memory read (
ReadPhys) and write (WritePhys) via the mapping IOCTL - Scanning physical memory for the ntoskrnl.exe PE header
The driver must be loaded for the POC to work (either via sc create/sc start or a tool like OSR Loader).
The driver implements a superset of functionality that resembles a PCI/device-level debugging tool. The presence of both HAL-based and direct port I/O methods for PCI access, the fallback patterns in mapping functions, and the stub IOCTLs for unimplemented features all suggest this is a work-in-progress or a test/diagnostic driver. The pool tag 'dDk ' (0x206B6444 = "Dk " reversed) doesn't correspond to any known Microsoft or third-party tag.
The two ValidateCommand functions with their class/function mapping strongly hint at PCI Express capability or AER (Advanced Error Reporting) command validation — Type 0 for devices and Type 1 for bridges. The command types (1,2 = 6 bytes; 3,4 = 2 bytes; 6,7 = 8 bytes; default = 4 bytes) line up with PCIe transaction descriptor sizes.
The driver exhibits weaknesses spanning multiple CWE categories, primarily in access control, privilege management, and resource handling.
| CWE | Name | How it applies |
|---|---|---|
| CWE-862 | Missing Authorization | The device object is created without a security descriptor. DispatchPassThrough returns STATUS_SUCCESS unconditionally — any process (including low-integrity) can open \\.\DosMtxVxd. No identity or integrity-level check is performed. |
| CWE-284 | Improper Access Control | None of the IOCTL handlers check the caller's privileges before executing. IOCTLs that map physical memory, access PCI config space, or read/write I/O ports all operate without any permission gate. |
| CWE-306 | Missing Authentication for Critical Function | There is no authentication mechanism whatsoever. The driver does not verify the identity or integrity level of the calling process before granting access to any operation. |
| CWE | Name | How it applies |
|---|---|---|
| CWE-266 | Incorrect Privilege Assignment | User-mode processes are given capabilities that belong to kernel mode: physical memory mapping (normally requires SeLockMemoryPrivilege), I/O port access (normally restricted by TSS IOPL/I/O bitmap), and PCI configuration manipulation. |
| CWE-250 | Execution with Unnecessary Privileges | The driver runs as a kernel component (ring 0) and exposes all of its capabilities to unprivileged callers without any reduction in privilege level. |
| CWE-269 | Improper Privilege Management | Sensitive operations that should require SeLockMemoryPrivilege, SeTcbPrivilege, or administrative rights are made available without any privilege escalation or impersonation check. |
| CWE | Name | How it applies |
|---|---|---|
| CWE-770 | Uncontrolled Allocation | ExAllocatePoolWithTag (IOCTL 0x9C40647C) accepts arbitrary PoolType and Size values from the caller without validation or limits. MmAllocateContiguousMemory (IOCTL 0x9C406464) similarly accepts unbounded sizes. |
| CWE-400 | Uncontrolled Resource Consumption | Repeated calls to the allocation IOCTLs can exhaust kernel pool or physical memory, leading to denial of service. |
| CWE-787 | Out-of-bounds Write | MapPhysicalMemory (IOCTL 0x9C40644C) allows writing to arbitrary physical addresses with no bounds checking. Combined with MapMmioSpace and the PCI config write operations, any physical memory location can be modified. |
| CWE-125 | Out-of-bounds Read | Same mechanism allows reading from any physical address without restriction. |
| CWE-823 | Use of Out-of-range Pointer Offset | The MemCopyAligned and MemCopyAlignedReverse functions perform pointer arithmetic within user-controlled structures; incorrect struct sizes could lead to out-of-bounds pointer calculations. |
| CWE | Name | How it applies |
|---|---|---|
| CWE-129 | Improper Validation of Array Index | IoPortRead and IoPortWrite accept any port address (0–0xFFFF) without validating the port range or checking whether the caller should have access to specific ports. |
| CWE-20 | Improper Input Validation | Several IOCTLs check struct sizes but do not validate the semantic content of the parameters (e.g., PciConfigRead doesn't verify the PCI bus/device/function exists; MapPhysicalMemory doesn't verify the physical address range is safe to expose). |