$npx -y skills add ergrelet/windiff --skill windiff-version-diff-analysisGenerate and analyze a diff between two Windows versions (or two patch levels of one version) for security research, using the WinDiff CLI in this repo. Use this whenever the user wants to compare Windows builds to find what Microsoft changed between versions — new or removed sys
| 1 | # WinDiff Version Diff Analysis |
| 2 | |
| 3 | Compare two Windows builds and turn the raw symbol/type/syscall delta into a |
| 4 | security-research report: what was added, what it probably *does*, and why it |
| 5 | matters for attack surface, exploitation, or defense. |
| 6 | |
| 7 | This skill runs inside the **WinDiff** repo. It uses `windiff_cli` to generate |
| 8 | the per-binary JSON databases, then diffs and interprets them. The interpretation |
| 9 | is the point — anyone can list new symbols; the value is explaining intent from |
| 10 | Windows internals conventions. |
| 11 | |
| 12 | ## Workflow |
| 13 | |
| 14 | ### 1. Pin down scope |
| 15 | |
| 16 | Establish, asking the user only if genuinely ambiguous: |
| 17 | |
| 18 | - **Two OS versions** as WinDiff triples `version / update / architecture` |
| 19 | (e.g. `21H2 / BASE / amd64` and `11-24H2 / KB5074105 / amd64`). `update` is |
| 20 | `BASE` for an RTM image or a `KB...` number for a patch. The path suffix used |
| 21 | in filenames is `version_update_architecture`, e.g. `11-24H2_KB5074105_amd64`. |
| 22 | - **Binaries** to compare. Default to the security-relevant core when the user is |
| 23 | vague: `ntoskrnl.exe`, `ntdll.dll`, `win32k.sys`, `win32kbase.sys`, |
| 24 | `win32kfull.sys`, `ci.dll`, `cng.sys`. See `references/windows-components.md` |
| 25 | for what each one governs. |
| 26 | - **Focus**: syscalls, mitigation flags, new attack surface, a specific |
| 27 | component/feature, etc. This steers interpretation, not data generation. |
| 28 | |
| 29 | `ci/db_configuration.json` is the canonical list of tracked versions and binaries |
| 30 | — consult it for valid `version`/`update` spellings. |
| 31 | |
| 32 | ### 2. Generate the databases with windiff_cli |
| 33 | |
| 34 | Write a **minimal** config containing only the two OS versions and the chosen |
| 35 | binaries, then run the CLI into a scratch output dir (keep it under the repo's |
| 36 | git-ignored `local/`). Use `scripts/make_config.py` to build the config: |
| 37 | |
| 38 | ```bash |
| 39 | python3 .claude/skills/windiff-version-diff-analysis/scripts/make_config.py \ |
| 40 | --os "21H2:BASE:amd64" --os "11-24H2:KB5074105:amd64" \ |
| 41 | --binary ntoskrnl.exe --binary ntdll.dll --binary win32k.sys --binary ci.dll \ |
| 42 | > local/windiff_diff_config.json |
| 43 | |
| 44 | cd windiff_cli |
| 45 | cargo run --release -- --low-storage-mode \ |
| 46 | ../local/windiff_diff_config.json ../local/windiff_diff_out/ |
| 47 | ``` |
| 48 | |
| 49 | This downloads PEs from Winbindex and PDBs from MSDL, so it **needs network |
| 50 | access** and takes minutes per binary. `--low-storage-mode` keeps memory bounded. |
| 51 | If the CLI fails for one OS (a build may be missing from Winbindex), report which |
| 52 | version/update is unavailable and suggest the nearest tracked one from |
| 53 | `ci/db_configuration.json`. |
| 54 | |
| 55 | If the user says the databases already exist (e.g. in `windiff_frontend/public/`), |
| 56 | skip generation and point the diff script at that directory instead. |
| 57 | |
| 58 | ### 3. Diff each binary |
| 59 | |
| 60 | `scripts/windiff_diff.py` does the deterministic set/text diff so you never hand- |
| 61 | compute it. Run it per binary; it prints a summary to stderr and structured JSON |
| 62 | to stdout. |
| 63 | |
| 64 | ```bash |
| 65 | python3 .claude/skills/windiff-version-diff-analysis/scripts/windiff_diff.py \ |
| 66 | local/windiff_diff_out ntoskrnl.exe 21H2_BASE_amd64 11-24H2_KB5074105_amd64 \ |
| 67 | > local/diff_ntoskrnl.json |
| 68 | ``` |
| 69 | |
| 70 | Use `--list` to see available suffixes, `--kinds` to restrict (e.g. |
| 71 | `--kinds syscalls types`). Anonymous `_unnamed_0xNNNN` types are hidden from the |
| 72 | top-level added/removed/modified lists by default (their synthetic ids churn |
| 73 | between builds — noise); pass `--include-anon` only if you specifically need them. |
| 74 | |
| 75 | **`resolved_member_changes` — where new mi |