Turn a vendor security patch into a working N-day: diff the patched and unpatched binaries, read the newly added safety checks back to a bug class, then write a PoC that crashes the unpatched build. Load for a CVE with a patch but no public PoC, Patch Tuesday triage (ntoskrnl / win32k / afd.sys / clfs.sys), Linux LTS backport analysis, bindiff / ghidriff / Diaphora workflows, patch diff, binary diffing to find what a patch fixed.
Pro scans all 2 files and shows the line behind each finding
Scanned 10/4/2026
npx -y skills add NoorQureshi/SploitAgent --skill reverse-eng-patch-diff --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Reverse Eng Patch Diff?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/noorqureshi-reverse-eng-patch-diff)More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.
---
name: reverse-eng-patch-diff
description: >
Turn a vendor security patch into a working N-day: diff the patched and unpatched binaries,
read the newly added safety checks back to a bug class, then write a PoC that crashes the
unpatched build. Load for a CVE with a patch but no public PoC, Patch Tuesday triage
(ntoskrnl / win32k / afd.sys / clfs.sys), Linux LTS backport analysis, bindiff / ghidriff /
Diaphora workflows, patch diff, binary diffing to find what a patch fixed.
domain: reverse-engineering
type: technique
stability: learning
modes: [pentest, bugbounty]
severity: high
cwe: [CWE-787, CWE-362, CWE-416, CWE-190, CWE-200]
tools: [bindiff, ghidriff, diaphora, radiff2, ghidra, ida, symchk]
schema_version: 1
---
# N-day patch diffing to exploit
## When it applies
A vendor shipped a fix but no details: a CVE advisory says "out-of-bounds write in component
X" with no PoC, a Patch Tuesday drop needs triage, or a Linux distro backport may be
incomplete on some branch. The goal is to recover *what the patch fixed* and prove the bug
against the unpatched build. Weaponizing an N-day is only legitimate against in-scope,
authorized targets (a program that accepts it, an engagement, your own lab) — confirm with
`tradecraft-scope-roe` and record it in `scope.txt`.
## Why it works
A security patch is a confession. The added bounds check, lock, zeroing, or refcount tells you
exactly which invariant the old code violated — and the unpatched binary still violates it.
Diff the two builds, filter to functions that changed *moderately* (identical = untouched,
completely different = new feature), and the fix pattern maps almost one-to-one onto a bug
class you can then trigger.
| The patch adds... | Likely bug class |
|---|---|
| `if (a + b < a)` / `__builtin_add_overflow` | integer overflow |
| `KeAcquireSpinLock` / `mutex_lock` | race condition (TOCTOU / double-free) |
| `if (idx >= MAX)` / `if (len > buf_size)` | OOB read / write |
| `RtlZeroMemory` / `memset(struct, 0, ...)` | uninitialized-memory info leak |
| `InterlockedDecrement` + refcount check | UAF / refcount error |
| `ProbeForRead` / `ProbeForWrite` / `access_ok` | unvalidated user-mode pointer |
| `SeAccessCheck` / capability check | missing authorization |
| removed / tightened IOCTL codes | attack-surface reduction — study the old interface |
## Method
1. **Get the before/after binaries.** Windows: download the MSU for build N (patched) and N-1
from the Microsoft Update Catalog, unpack with `expand.exe`. Linux: `apt download` the two
kernel/package versions and `dpkg-deb -x` / `rpm2cpio` them. Third-party software: grab the
N-1 and N installers. Exact commands per platform in [`cheatsheet.md`](cheatsheet.md).
2. **Align symbols.** Windows: pull PDBs from the Microsoft symbol server with `symchk`.
Linux: matching dbgsym / debuginfo, and `extract-vmlinux` to turn `vmlinuz` back into an
ELF. No symbols for one side → migrate them from the nearest version before diffing.
3. **Diff.** Feed both binaries to BinDiff, ghidriff, or Diaphora:
```bash
ghidriff ntoskrnl_old.exe ntoskrnl_new.exe -o diff_out/
bindiff --primary=old.BinExport --secondary=new.BinExport --output_dir=./bindiff_out/
```
4. **Locate the change.** Filter to functions with similarity ≈ 0.5–0.95. Read what was
*added*: new `if` guards, new loop bounds, new locks — and what was *deleted* (removed code
is a clue too). Map the pattern through the table above to a bug class; before/after
pseudocode pairs are ideal LLM input for a root-cause hypothesis (patterns in
`cheatsheet.md`).
5. **Write and verify the PoC** against the *unpatched* build:
- integer overflow → boundary values (`0xFFFFFFF0 + 0x100`) so the wrap yields a small
allocation but a large copy;
- race → threads hammering two syscalls on one object (close + IOCTL concurrently);
- UAF → spray → free → reclaim → use;
- OOB → drive length/index just past the boundary the new check now guards.
Success criteria are symmetric: the PoC crashes the unpatched build reliably and runs clean
on the patched one.
## Gotchas
- **Mitigation ≠ fix.** Added CFG/XFG instrumentation (`_guard_xfg_dispatch_icall_fptr`) is
hardening, not the bug fix — keep looking.
- **Compiler noise fakes changes.** Inlining decisions, switch-table reordering, and PGO make
unchanged source look different — diff N against N-1 (same toolchain), never across major
versions, and read control/data flow rather than token-level diffs.
- **Alignment failure.** If overall matched ratio < ~90%, stop: wrong pairing, different
compiler, or rebase mismatch — fix the inputs before reading results.
- **A patch may shrink the blast radius, not fix the bug** — the same root cause may still be
reachable via another path (one bug, multiple harvests).
- **A crash on the unpatched build alone proves nothing** — environment faults look identical;
you need the patched build clean *and* a stated root cause before claiming the CVE
(`reporting-triage-validation`).
- **Redact in write-ups** — target hostnames, internal IPs, usernames become placeholders.
## Verify success
You can name the function, the added check, and the violated invariant; your PoC crashes the
unpatched build (BSOD/panic/KASAN naming the expected class) within a predictable window and
exits cleanly on the patched build. That pairing — plus the root cause — is the reproducible
N-day.
## References
Microsoft Update Catalog & MSRC CVRF API; BinDiff / ghidriff / Diaphora docs;
`reverse-eng-binary-triage`, `exploit-poc-development` in this library.
---
_Portions adapted from [reverse-skill](https://github.com/zhaoxuya520/reverse-skill) by zhaoxuya520, MIT License._
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!