Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 

Repository files navigation

mtxC9CB.sys — Reverse Engineering Report

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.

Binary Information

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.

Device Info

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

Driver Structure

Entry Point (DriverEntry)

The driver creates a device object and symbolic link, registering three dispatch routines:

  • IRP_MJ_CREATE (0) -> DispatchPassThrough
  • IRP_MJ_CLOSE (2) -> DispatchPassThrough
  • IRP_MJ_DEVICE_CONTROL (14) -> IrpDeviceControl
  • DriverUnload -> DriverUnload

DispatchPassThrough returns STATUS_SUCCESS unconditionally. Any process can open the device. DriverUnload deletes the symbolic link and device object.

Dispatch Chain

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.

IOCTL Reference

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

IOCTL encoding

The code 0x9C4064XX decodes to:

  • Device type: 0x22 (FILE_DEVICE_UNKNOWN)
  • Function code: 0x1900 + XX (i.e., 0x19000x1988)
  • Method: METHOD_BUFFERED (0)
  • Access: FILE_ANY_ACCESS (0)

Function Analysis

MapPhysicalMemory (0x11DB0) — IOCTL 0x9C40644C

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:

  1. Opens \Device\PhysicalMemory with SECTION_ALL_ACCESS
  2. Resolves the section object via ObReferenceObjectByHandle
  3. Calls ZwMapViewOfSection into the calling process with ViewShare
  4. 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.

PciConfigRead (0x11F30) — IOCTL 0x9C40640C

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 0xCF8 value after completion

PciConfigWrite (0x12060) — IOCTL 0x9C406410

Same pattern as read, but writes via 0xCF8/0xCFC ports. Tries HalSetBusDataByOffset first.

PciConfigWriteDword (0x121A0)

Helper that writes a single DWORD to PCI config. Used by PciGetBarInfo.

PciFindCapability (0x122C0) — IOCTL 0x9C406460

Scans the PCI capabilities linked list for a specific capability ID:

  1. Reads status register at offset 0x04, checks bit 4 (Capabilities List bit)
  2. Reads Capabilities Pointer at offset 0x34
  3. 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

PciGetBarInfo (0x125F0) — IOCTL 0x9C40645C

Determines a PCI BAR's base address and size using the standard probe technique:

  1. Read original BAR value
  2. Write 0xFFFFFFFF to the BAR
  3. Read back the masked value -> size = ~(masked & 0xFFFFFFF0) + 1 (computed by caller)
  4. Restore original BAR value

Returns original base (lower 4 bytes masked) and the all-ones probe result.

MapMmioSpace (0x12860) — IOCTL 0x9C406484

Maps a physical address range as MMIO using MmMapIoSpace. Tries three cache types in order:

  1. MmNonCached
  2. MmWriteCombined
  3. MmCached

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.

IoPortRead (0x12B40) — IOCTL 0x9C406404

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.

IoPortWrite (0x12BE0) — IOCTL 0x9C406408

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.

MemCopyAligned (0x111E0) — IOCTL 0x9C406414

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.

MemCopyAlignedReverse (0x11260) — IOCTL 0x9C406418

Same as MemCopyAligned but the layout is in reverse — the destination pointer comes from within the source structure.

ValidateCommandType0 / ValidateCommandType1 (0x112F0, 0x11560)

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

AllocateContiguousMemory (0x117E0) — IOCTL 0x9C406464

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.

FreeContiguousMemory (0x11890) — IOCTL 0x9C406468

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.

Pool allocation (inline in IoctlDispatch)

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.

Import Table

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

Security Analysis

No access control on device creation

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.

Physical memory mapping (IOCTL 0x9C40644C)

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.

I/O port access (IOCTL 0x9C406404, 0x9C406408)

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.

PCI configuration access (IOCTL 0x9C40640C, 0x9C406410)

Reads and writes PCI configuration space on any bus/device/function. Can reprogram BARs, enable bus mastering, or disable devices.

MMIO mapping (IOCTL 0x9C406484)

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.

Kernel pool allocation (IOCTL 0x9C40647C)

Allows allocating kernel pool with a user-specified pool type and size. Combined with the contiguous memory functions, this provides flexible kernel heap manipulation.

POC

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

Remarks

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.

CWE Classification

The driver exhibits weaknesses spanning multiple CWE categories, primarily in access control, privilege management, and resource handling.

Access Control & Authorization

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.

Privilege Management

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.

Memory & Resource Management

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.

Input Validation

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

About

Matrox old driver still works for something, eh?

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages