This issue: I pointed Claude Code and Ghidra at the kernel drivers on my gaming laptop, and the prompt I used to get started.
How I Let an LLM Reverse Engineer My Laptop's Kernel Drivers
Windows ships with hundreds of third-party kernel drivers. Most were written years ago, signed with valid certificates, and forgotten. They run at the highest privilege level on your machine. Many of them are terrible.
This all started with me looking into BYOVD attacks and wanting to come up with some of my own. I thought the drivers in c:\windows\system32\drivers on my gaming laptop would be a good place to start looking.
I discovered GhidraMCP, and combining it with Claude Code, was able to find 8 findings in one Sunday and 1 while writing this. To be clear, Claude did most of this and I just supervised. I'm not a career reverse engineer. I've taken one class and done some crackmes on a website years ago. But I've found 9 issues and submitted them to 3 different vendors. 3 have been rejected as "by design" so far. 2 have been accepted. I’m still waiting to hear back about the other 4. This is static analysis with Claude Code and GhidraMCP. Adding MCP server for WinDBG is a next step for dynamic analysis but for now we are just going to use C to test things.
I developed a process and a janky tool, cthaeh, to help narrow down which drivers to target on my laptop. It runs Ghidra in headless mode, analyzes each driver's functionality, and scores it. It’s not great yet. It checks PE imports for dangerous primitives, maps IOCTL handlers, flags missing security descriptors, and scores based on driver class and vendor CNA status.
Let's dive into the process and walk through how to do this with an actual driver.
The setup.
You'll need the following setup to play along:
WSL2 running Claude Code setup with MCP Ghidra
We want to run on Windows so we can run POCs
MinGW Setup for POC Development
A remote VM (I didn’t use one but POCs will crash the OS)
Claude Opus 4.6 Thinking
The first thing I do is run cthaeh to figure out which driver I want to target. You can also just look at c:\windows\system32\drivers and pick one. Make sure it’s loaded, not in loldrivers.io, and that you have the latest version.

cthaeh scoring still needs adjustment.
Dead End: AMD
We are going to start with amd_dpfc.sys v1.4.0.0, AMD’s Dynamic and Frequency Control Service driver. My laptop runs an AMD CPU with an Nvidia GPU, and Nvidia's driver package bundles this AMD driver alongside the GPU drivers. So I already have the latest version.
I fire up Claude Code and prompt it a bit with my old markdown from previous runs. I always send it this BYOVD repo and sometimes other kernel exploit dev blogs to try to prime it with prompts before starting. I’ve included a prompt at the end of the article. This may not be needed with today's models though. They are a lot better than they used to be and may not require as much guidance. Another thing I like to do is tell Claude to create a markdown report as it goes so that if we crash then I can copy paste from that markdown report and resume where I left off.
I've already set up MCP Ghidra. You'll want that with the target driver loaded up so Claude can look at it. You'll want to press 2 a few times in Claude Code so it won't ask you for permission for every Ghidra function it calls.

Choose option 2, or prepare to be interrupted a lot.
This takes some time but it generates a POC. It says the POC requires an admin user since the SDDL requires local administrator.
I run the POC as admin and copy paste the results back into Claude.

Uh, but we are admin so who cares?
This all looks impressive but we have admin access when we ran it. There isn't anything interesting here. No dangerous IOCTLs at least. Nothing to weaponize.
The Find: ASUS.
Next up, we're going to load AsusPTPFilter.sys.
Claude quickly found some issues and created a POC for me to test.

Claude moves through a driver quickly with Ghidra.
Running the POC, it appears that a regular user can access places they shouldn't be able to.
PS C:\Users\jeffb\Desktop> .\asus_ptpfilter_poc.exe
=== AsusPTPFilter.sys v16.0.0.41 PoC ===
ASUS Precision Touchpad Filter Driver
7 named devices, 60+ IOCTLs, no SDDL
=== Device Access Test ===
[+] OPENED \\.\AsusHITBIService (GENERIC_READ|GENERIC_WRITE)
...........................(cut for brevity)
=== Summary ===
Devices opened: 7 / 9
IOCTLs succeeded: 0
Total tests: 98
[!] VULN: 7 device(s) accessible from current user contextThe POC confirmed that a regular user could open the devices, but none of the IOCTLs actually worked. So I fed the output back to Claude and asked it to dig deeper. It generates another POC and now we have a confirmed medium severity vulnerability.
I run the next POC.
=== AsusPTPFilter.sys v16.0.0.41 PoC v3 ===
ASUS Precision Touchpad Filter Driver
7 named devices, 60 IOCTLs, no SDDL
Magic: 0xFF0000FF (corrected)
..................(cut for brevity)
=== Summary ===
Devices opened: 7 / 7
IOCTLs succeeded: 5
Total tests: 5
[!] VULN CONFIRMED: 7 device(s) accessible - no SDDL
[!] VULN CONFIRMED: 5 IOCTL(s) succeeded from user contextI ask Claude to explain the security issues so I can attempt to spot BS and spare the ASUS PSIRT team from my AI shenanigans. The first issue I spot is we have 4 vulns, instead of 1. They all have the same root cause and fix so I have Claude combine them.
The driver calls IoCreateDevice() instead of WdmlibIoCreateDeviceSecure().
The difference is that the secure version takes an SDDL security descriptor string that defines who can open the device. Without an SDDL string, the I/O Manager applies a permissive default ACL that grants Interactive Users access. This creates a potential privilege escalation vector at worst, and is a bit sloppy.
Next, I ask Claude to score the vuln, with CVSS and explain its thinking. I review this and agree with the medium result. This is a qualitative measurement here. We don’t have arbitrary read or write so that means we aren’t in high or better territory. Our final CVSS vector being AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N scoring 4.4 - medium.
The Disclosure
I have Claude draft an email to ASUS PSIRT. I’m not optimistic for this bug as I sent a similar one to them like this and they told me it was by design.
By design, vendor speak for “we know and we don't care.”
So I’m not sure if this will be treated differently. I don’t see a clear path to weaponizing this. There aren’t (as far as I know) any interesting functions to abuse in the driver. But I haven’t spent much time with it. Just this afternoon.
Overall, of the 9 findings across 3 vendors: 2 have been accepted and are pending patches, 3 were rejected as by design, and 4 are still pending response.
I'll cover the accepted findings in a future issue once patches ship and disclosure timelines allow.
The barrier to entry for this kind of research is lower than you'd think. MCP Ghidra lets Claude see inside a compiled driver the same way a human would in a disassembler. It can trace how user input flows through the code, spot where checks are missing, build PoCs, and draft vendor reports. I mostly just ran the PoCs and asked follow-up questions.
The prompt in the appendix below is everything you need to get started. If you try this, let me know what you find. I'm genuinely curious how many drivers out there are sitting on the same class of bugs.
That’s it for issue #2.
If you found this useful, forward it to someone who'd actually read it.
Got questions, corrections, or want to argue about something? Just hit reply. I read everything.
— Jeff
Appendix: Claude Code Prompt
# Claude Code Prompt
Authorized security research on Windows kernel drivers. Find MEDIUM+ severity vulns (LPE, kernel R/W, info leak) reportable for CVEs.
## Setup
- **Ghidra MCP** for RE.
Cross-compile: `x86_64-w64-mingw32-gcc -o poc.exe poc.c -Wall`
- Deploy: `cp poc.exe /mnt/c/Users/<user>/Desktop/`
- Each driver gets `<driver>-research/` dir with `RESEARCH_NOTES.md` and `poc/` subdirectory
## 1. Triage
**Check imports** — dangerous primitives determine attack surface:
`MmMapIoSpace`, `MmGetPhysicalAddress`, `HalSet/GetBusDataByOffset`, `__read/__writemsr`, `ZwMapViewOfSection`
**Find device creation** — trace DriverEntry → IoCreateDevice. Extract device name and SDDL string.
- `IoCreateDevice` without SDDL = weak default ACL = accessible to standard users
- `IoCreateDeviceSecure` with `D:P(A;;GA;;;BA)` = admin-only
## 2. Reverse Engineering
**Map dispatch chain:**
DriverEntry → MajorFunction[14] (IRP_MJ_DEVICE_CONTROL) → IOCTL dispatch handler
**IOCTL dispatch patterns:**
- **Switch/if** — Direct switch on IoControlCode
- **Table-based** — Array of `[DWORD code, QWORD handler]` entries (16 bytes each) in .rdata. Count often returned by a hardcoded function.
- **Nested** — 2-3 levels deep: per-device dispatch → per-IOCTL sub-dispatch → leaf handler
**For each IOCTL handler, get:** IOCTL code, required buffer sizes (handlers check EXACT sizes), magic header values, what it does, which user-controlled values become offsets/addresses/sizes.
**Decompiler gotcha — signed values:**
```
piVar1[2] == -0xffff01 // Ghidra shows this (SIGNED)
-0xffff01 = 0xFF0000FF // Two's complement — THIS is what you send
```
## 3. PoC Development
**Rules:**
- Match exact buffer sizes from handler checks
- Set magic values in correct positions
- Test read-only operations first, writes second
- Try device open with: `GENERIC_READ|WRITE`, `READ` only, `WRITE` only, then zero access
- **Always test from standard (non-admin) user first** — determines severity
**Error codes:** 2=device not found, 5=access denied (has SDDL), 31=handler rejected (wrong magic/size), 32=already open
## 4. Report Template (RESEARCH_NOTES.md)
```
# <driver>.sys v<version> - Research Notes
Researcher/Driver/Version/SHA256/Vendor PSIRT/Signing/Device path
## Architecture — [DriverEntry flow, device creation, dispatch]
## Imports — [security-relevant imports table]
## IOCTL Surface — [code | handler | purpose | sizes | danger]
## Vulnerability — [CWE, CVSS vector + score, root cause, impacts, fix]
## PoC Results — [paste actual output]
## Key Addresses — [function/data address table]