Sample driver + user component to demonstrate writing into arbitrary process memory from Kernel via CR3 manipulation (opposed to the usual KeStackAttachProcess API).
Note: Only for fun and demonstration
There's a few fun techniques in this that have been individually useful outside of this demo project. This includes:
- Halting all other cores/threads on the machine except my own executing code
- Providing windows and alternative x86-generic methods for certain things (e.g. IRQLs v CR8)
- Resolving offsets of unexported structures and union fields at runtime via runtime PDB parsing (instead of hardcoding offsets, etc)
- Modifying arbitrary user process memory from Kernel without KeStackAttachProcess (optionally checking VaSpaceDeleted via PDB-provided offsets)
- Surviving page-faults in >= DISPATCH without try/catch or letting the Kernel log the fault, achieved via IDT hijacking
This thing is also written in Rust.
For PDB parsing, I pulled in and modified pdblister https://github.com/microsoft/pdblister to support building as a lib.
- Navigate to read_write_driver
- run
cargo make
(sometimes it requires the first run to be done as administrator, you can safely ignore any "missing INF" related errors/warnings that pop up, build process also documented here: https://github.com/microsoft/windows-drivers-rs) - Copy the driver (e.g for debug builds it'll be
read_write_driver\target\debug\read_write_driver.sys
to your target machine/VM - Start the driver (e.g. in an administrator cmd prompt run
sc create readwrite binPath= C:\\code\\read_write_driver.sys type= kernel
followed bysc start readwrite
. Replace the paths with your own) - Navigate to
read_write_user
and build (e.g.cargo build
orcargo build --release
) - Copy the binary (either
read_write_user\target\debug\read_write_user.exe
orread_write_user\target\release\read_write_user.exe
) to your target machine/VM - Find a PID and address in that PID you want to overwrite (e.g. launch notepad.exe, note its pid is 0x1234, attach a debugger and find some address in the target)
- If the address if valid + paged-in, it'll be overwritten with hardcoded sample bytes, if the address is invalid the driver will return an error to our userland process. No BSOD should occur regardless.
- Run the userland process, to run the example that'll leverage runtime PDB parsing add the
--use-symbols
flag, e.g.read_write_user.exe --pid 0x1234 --address 0x100000 --use-symbols
. The address can be specified in hex (prefixed by0x
) or in decimal without the prefix. - If no error was displayed in the userland process, observe the modified bytes at your chosen address.