Skip to content

Make kernel reader minimal and read-only#1

Open
Shadysmetools wants to merge 1 commit into
mainfrom
codex/create-guide-for-read-only-kernel-driver
Open

Make kernel reader minimal and read-only#1
Shadysmetools wants to merge 1 commit into
mainfrom
codex/create-guide-for-read-only-kernel-driver

Conversation

@Shadysmetools

@Shadysmetools Shadysmetools commented Jun 1, 2026

Copy link
Copy Markdown
Owner

Motivation

  • Provide a small, educational, read-only example of a Windows kernel reader that mirrors the clean Valthrun-style architecture while reducing surface area and risk.
  • Keep the kernel/user contract explicit and byte-for-byte stable by centralizing the IOCTL and request layout in a shared header using CTL_CODE and a simple request struct.
  • Favor safety and stability by using METHOD_BUFFERED, MmCopyVirtualMemory, IRQL checks, input/output length validation, and strict lifetime discipline for PEPROCESS references.

Description

  • Replace the multi-command trainer client with a minimal read-only console client client/client.cpp that accepts client.exe <pid> <hex-address> <size>, opens \\.\KernelReader, issues DeviceIoControl with IOCTL_READ_PROCESS_MEMORY, and hex-dumps the returned bytes.
  • Simplify the kernel driver driver/driver.c to a focused WDM implementation that creates the device and DOS symbolic link, handles IRP_MJ_CREATE/IRP_MJ_CLOSE, and services a single buffered IOCTL read path which validates inputs, requires PASSIVE_LEVEL, looks up the target process with PsLookupProcessByProcessId, calls MmCopyVirtualMemory, returns the copied byte count, and always dereferences the process object.
  • Consolidate the shared contract in shared/ioctl_shared.h to define IOCTL_READ_PROCESS_MEMORY via CTL_CODE(FILE_DEVICE_KERNEL_READER, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS) and the exact READ_MEMORY_REQUEST layout containing ProcessId, TargetAddress, and Size.
  • Rewrote README.md as an educational guide describing the architecture, the shared wire format, build/load instructions, stability checklist, and how this maps to a Valthrun-style Rust port.

Testing

  • Ran git diff --check (no whitespace or diff errors) and committed the changes; this check succeeded.
  • Performed environment/tool checks for MSVC/WDK (cl/msbuild) in the container and found the Visual Studio/WDK toolchain unavailable, so no driver or client builds were executed in CI here.
  • No runtime device-driver tests were run inside this environment (driver .sys build and kernel testing require a Windows VM with WDK and test-signing enabled).

Codex Task

Summary by CodeRabbit

  • Refactor

    • Streamlined the tool to a single-purpose utility: read process memory and display hex/ASCII dumps.
    • Simplified CLI to accept only process ID, memory address, and size parameters.
    • Removed multi-command functionality for process/module listing and memory manipulation.
  • Documentation

    • Updated README to reflect the focused read-only memory operation workflow.

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This pull request transforms a multi-feature kernel-memory-reader tool into a minimal read-only utility. The shared IOCTL protocol is simplified to a single request struct, the kernel driver is pared down to handle only buffered memory reads via MmCopyVirtualMemory, and the user-mode client becomes a simple CLI that validates arguments and dumps the returned bytes.

Changes

Kernel-Reader simplification to single read-only IOCTL

Layer / File(s) Summary
Shared IOCTL contract
shared/ioctl_shared.h
Replaced multi-IOCTL protocol with a single IOCTL_READ_PROCESS_MEMORY using METHOD_BUFFERED, a FILE_DEVICE_KERNEL_READER vendor type, and a compact READ_MEMORY_REQUEST struct (ProcessId, TargetAddress, Size). Removed REQUEST_TYPE enum and all associated request/response structs for process/module listing, writing, and region queries.
Kernel driver read path
driver/driver.c
Implemented ReadProcessMemoryBuffered handler that validates buffered-I/O constraints (size caps, PASSIVE_LEVEL), looks up the source process by PID, calls MmCopyVirtualMemory to copy from target into the system buffer, and returns the byte count. DispatchDeviceControl now routes only IOCTL_READ_PROCESS_MEMORY, rejecting all others.
Driver entry and unload
driver/driver.c
Updated DriverEntry to create device and DOS symbolic link, enable DO_BUFFERED_IO, and wire the IRP major function table; DriverUnload now cleans up the link and device object. Made g_DeviceObject static and renamed the IOCTL dispatch function to DispatchDeviceControl.
User-mode client read and dump
client/client.cpp
Simplified CLI to a single <pid> <hex-address> <size> command. Validates arguments, enforces KR_MAX_READ_BYTES limit, opens the driver for GENERIC_READ, sends READ_MEMORY_REQUEST via DeviceIoControl, and prints a formatted hex-dump of the returned bytes.
Documentation and build instructions
README.md
Rewrote README to focus on the read-only IOCTL contract, kernel driver flow with explicit IRQL/PASSIVE_LEVEL notes, and user-mode client walkthrough. Removed broad command overview, trainer/session features, and pointer-chain resolution; condensed safety notes and build/load steps.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A rabbit whispered to the kernel so deep,
"Let's trim the fat and keep memory reads cheap!
One IOCTL stark, one buffer so pure,
No modules, no writes—just copies, secure!"
The driver said yes, and the client stood tall,
One read-only path—the simplest of all. 🎯

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Make kernel reader minimal and read-only' directly and clearly summarizes the main change—transforming a multi-command kernel driver/client into a single-purpose, read-only tool with minimal functionality.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/create-guide-for-read-only-kernel-driver

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 525cc46725

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread shared/ioctl_shared.h
Comment on lines +15 to +16
#define IOCTL_READ_PROCESS_MEMORY \
CTL_CODE(FILE_DEVICE_KERNEL_READER, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore compatibility with the Rust controller

When the existing Rust controller is used, it still imports IOCTL_DISPATCH from rust/shared/src/lib.rs and sends the old discriminated request structs from rust/controller/src/driver.rs, but this change makes the C driver accept only the new IOCTL_READ_PROCESS_MEMORY value and the smaller READ_MEMORY_REQUEST layout. That means the documented Rust controller now gets rejected before dispatch, or would have its payload misinterpreted if pointed at the new IOCTL; please update/remove the Rust shared/controller code or keep a compatibility path.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
driver/driver.c (1)

148-154: 💤 Low value

Unsupported IRPs should fail rather than succeed silently.

The loop sets all major functions to DispatchCreateClose, which returns STATUS_SUCCESS. This means unsupported operations like ReadFile/WriteFile succeed with 0 bytes instead of failing. Consider returning STATUS_INVALID_DEVICE_REQUEST for unsupported IRPs.

Suggested approach
+static NTSTATUS DispatchUnsupported(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
+    UNREFERENCED_PARAMETER(DeviceObject);
+    return CompleteIrp(Irp, STATUS_INVALID_DEVICE_REQUEST, 0);
+}
+
 // In DriverEntry:
     for (ULONG i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; ++i) {
-        DriverObject->MajorFunction[i] = DispatchCreateClose;
+        DriverObject->MajorFunction[i] = DispatchUnsupported;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@driver/driver.c` around lines 148 - 154, The current loop assigns
DriverObject->MajorFunction[*] = DispatchCreateClose which causes unsupported
IRPs to return STATUS_SUCCESS; change the default assignment to a new handler
(e.g., DispatchUnsupported) that returns STATUS_INVALID_DEVICE_REQUEST, then
explicitly set DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreateClose,
[IRP_MJ_CLOSE] = DispatchCreateClose and [IRP_MJ_DEVICE_CONTROL] =
DispatchDeviceControl as you already do; add a simple DispatchUnsupported(IRP,
...) implementation that completes the IRP with IoCompleteRequest and returns
STATUS_INVALID_DEVICE_REQUEST so unsupported Read/Write/etc. fail properly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@driver/driver.c`:
- Around line 148-154: The current loop assigns DriverObject->MajorFunction[*] =
DispatchCreateClose which causes unsupported IRPs to return STATUS_SUCCESS;
change the default assignment to a new handler (e.g., DispatchUnsupported) that
returns STATUS_INVALID_DEVICE_REQUEST, then explicitly set
DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreateClose, [IRP_MJ_CLOSE]
= DispatchCreateClose and [IRP_MJ_DEVICE_CONTROL] = DispatchDeviceControl as you
already do; add a simple DispatchUnsupported(IRP, ...) implementation that
completes the IRP with IoCompleteRequest and returns
STATUS_INVALID_DEVICE_REQUEST so unsupported Read/Write/etc. fail properly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 95731d6c-7600-4586-a6b9-240ac6751948

📥 Commits

Reviewing files that changed from the base of the PR and between d9618d2 and 525cc46.

📒 Files selected for processing (4)
  • README.md
  • client/client.cpp
  • driver/driver.c
  • shared/ioctl_shared.h

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant