diff --git a/archive/kernullist/kn-live-dbg.txt b/archive/kernullist/kn-live-dbg.txt
new file mode 100644
index 00000000..0b004d13
--- /dev/null
+++ b/archive/kernullist/kn-live-dbg.txt
@@ -0,0 +1,101436 @@
+Project Path: arc_kernullist_kn-live-dbg_x9rf8pkd
+
+Source Tree:
+
+```txt
+arc_kernullist_kn-live-dbg_x9rf8pkd
+├── LICENSE
+├── README.md
+├── demo
+├── docs
+│ ├── AI_ASSISTED_WORKFLOWS.md
+│ ├── ARCHITECTURE.md
+│ └── WINDBG_COMMAND_COVERAGE.md
+├── driver
+│ ├── Driver.cpp
+│ ├── KnLiveDbg.rc
+│ ├── KnLiveDbgDriver.vcxproj
+│ └── KnLiveDbgDriver.vcxproj.filters
+├── kn-live-dbg.sln
+├── probe_driver
+│ ├── KnLiveDbgProbe.rc
+│ ├── KnLiveDbgProbeDriver.vcxproj
+│ ├── KnLiveDbgProbeDriver.vcxproj.filters
+│ └── ProbeDriver.cpp
+├── shared
+│ ├── KnLiveDbgIoctl.h
+│ ├── KnLiveDbgProbeIoctl.h
+│ └── KnLiveDbgVersion.h
+├── third_party
+│ └── zydis
+│ ├── README.md
+│ └── amalgamated
+│ ├── LICENSE-Zycore.txt
+│ ├── LICENSE-Zydis.txt
+│ ├── Zydis.c
+│ └── Zydis.h
+├── tools
+│ ├── build.ps1
+│ ├── release.ps1
+│ └── sync-debugging-tools-runtime.ps1
+├── user
+│ ├── AiProvider.cpp
+│ ├── AiProvider.h
+│ ├── CallbackScanner.cpp
+│ ├── CallbackScanner.h
+│ ├── CommandRegistry.cpp
+│ ├── CommandRegistry.h
+│ ├── DbgEngBackend.cpp
+│ ├── DbgEngBackend.h
+│ ├── DeviceClient.cpp
+│ ├── DeviceClient.h
+│ ├── DriverService.cpp
+│ ├── DriverService.h
+│ ├── KnLiveDbg.rc
+│ ├── KnLiveDbg.vcxproj
+│ ├── KnLiveDbg.vcxproj.filters
+│ ├── NativeDisassembler.cpp
+│ ├── NativeDisassembler.h
+│ ├── SymbolEngine.cpp
+│ ├── SymbolEngine.h
+│ └── main.cpp
+└── vendor
+ ├── README.md
+ └── debugging-tools
+ └── x64
+ ├── MANIFEST.md
+ └── symsrv.yes
+
+```
+
+`LICENSE`:
+
+```
+MIT License
+
+Copyright (c) 2026 kernullist
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+```
+
+`README.md`:
+
+```md
+# Kn-Live-Dbg
+
+Kn-Live-Dbg is a Windows kernel live-debugging experiment shaped after the useful part of LiveKD: the kernel driver exposes narrow memory primitives, while the user-mode console owns service lifecycle, symbol loading, type interpretation, and operator UX.
+
+## Demo
+
+https://github.com/user-attachments/assets/f3542a85-c960-46f2-a151-fdd23a8294a6
+
+If the embedded video does not render, open the [README-sized demo](demo/kn-live-dbg-demo-readme.mp4) or the [full-resolution demo](demo/kn-live-dbg-demo.mp4).
+
+### AI Command Demo
+
+
+
+If the AI command demo does not render inline, open [demo/kn-live-dbg-demo-ai.mp4](demo/kn-live-dbg-demo-ai.mp4).
+
+## Scope and Signing Notice
+
+This tool is built for defensive Windows security research, anti-cheat research, driver diagnostics, and controlled lab analysis. It is not designed to bypass Windows Code Integrity and does not include Code Integrity bypass functionality. To load the driver, use Windows test-signing mode with the test-signed build, or sign the driver with an appropriate production certificate such as an EV code-signing certificate for real deployment environments.
+
+## Shape
+
+```text
+kn-live-dbg/
+ shared/KnLiveDbgIoctl.h stable user/kernel ABI
+ shared/KnLiveDbgProbeIoctl.h positive-control probe ABI
+ driver/Driver.cpp WDM driver with virtual/physical memory IOCTLs
+ probe_driver/ProbeDriver.cpp WDM positive-control test buffer driver
+ user/*.cpp elevated TUI, SCM lifecycle, DbgHelp symbols
+ tools/build.ps1 Release/Debug x64 build helper
+ tools/release.ps1 build and zip release package helper
+```
+
+## Current Capabilities
+
+1. Installs or updates, starts, stops, waits on, and deletes `KnLiveDbg.sys` through SCM.
+2. Creates `\\.\KnLiveDbg` with an Administrators/SYSTEM-only device DACL and validates ABI version.
+3. Reads kernel virtual memory through `MmCopyMemory`.
+4. Translates virtual addresses to physical addresses through an x64 page-table walk.
+5. Reads physical memory through `MmCopyMemory`.
+6. Writes physical memory with per-handle write mode enabled by default.
+7. Writes kernel virtual memory with per-handle write mode enabled by default.
+8. Enumerates loaded kernel modules with `NtQuerySystemInformation`.
+9. Loads kernel symbols with `DbgHelp`; the EXE directory and its non-cache subdirectories are searched first, followed by `SRV*\symbols*https://msdl.microsoft.com/download/symbols`, and the `nt` kernel PDB is downloaded/loaded into that EXE-local cache during startup by default.
+10. Provides a WinDbg-compatible command registry for the official base command list.
+11. Implements native live-memory support for memory, symbol, module, type, compare, fill, move, search, and explicit disassembly commands.
+12. Routes stop-state, parser-heavy, extension, and meta commands through the DbgEng backend.
+13. Provides an optional DbgEng backend for raw WinDbg command execution against the local kernel target.
+14. Parses kernel PDB types to enumerate object-manager filters, registry callbacks, process/thread/image-load callbacks, and minifilter callbacks with function/module/context annotations.
+15. Provides an initial AI assistant provider layer for advisory command planning and result interpretation through Codex CLI, ChatGPT/Codex OAuth, DeepSeek, and OpenRouter.
+16. Enforces a single-controller device owner and exposes owner/write-mode state through `drvstatus`.
+17. Resolves process DTBs from `_EPROCESS.Pcb.DirectoryTableBase` and optional `UserDirectoryTableBase` for process-aware `vtop`, `d*`, and `e*` commands; native `e*` writes default to the System process context (`pid 4`).
+18. Detects active LA57 paging and reports whether translation used PML4 or PML5, including the physical address of each page-table entry that was walked.
+19. Supports DbgEng local-kernel and remote-kernel attach modes through `kdinit /local` and `kdinit /remote`.
+20. Falls back to DIA SDK type parsing when `DbgHelp` cannot provide enough UDT/field metadata.
+21. Builds and manages `KnLiveDbgProbe.sys`, a positive-control test driver with known virtual and physical buffer addresses.
+
+## Design Notes
+
+- `docs/ARCHITECTURE.md` describes the driver/user split and backend routing.
+- `docs/WINDBG_COMMAND_COVERAGE.md` tracks native and DbgEng-routed WinDbg command coverage.
+- `docs/AI_ASSISTED_WORKFLOWS.md` captures planned AI-assisted command planning, callback analysis, `dt` interpretation, disassembly annotation, write safety, playbooks, and session reporting.
+
+## Build
+
+Requirements:
+
+1. Visual Studio 2022.
+2. Windows Driver Kit 10.0.26100.0.
+3. x64 developer shell or normal PowerShell with MSBuild at the default VS Professional path.
+4. Vendored Zydis v4.1.1 amalgamated sources under `third_party\zydis\amalgamated` for native x64 disassembly.
+
+Build:
+
+```powershell
+.\tools\build.ps1 -Configuration Release
+```
+
+Refresh the pinned Debugging Tools runtime from the newest complete local x64 set:
+
+```powershell
+.\tools\sync-debugging-tools-runtime.ps1
+```
+
+The driver projects use WDK `TestSign` for Debug and Release x64 builds. The build helper verifies that both `KnLiveDbg.sys` and `KnLiveDbgProbe.sys` have Authenticode signers and prints signature status/thumbprints after MSBuild completes.
+Normal scripted builds reuse the current PE version from `.build\version-state.json` or the existing output file and do not increment it. Use `.\tools\build.ps1 -Configuration Release -BumpVersion` only when a new build version should be minted; with no previous state, the baseline is `0.0.0`, so the first bumped build stamps `0.0.1` into `KnLiveDbg.exe`, `KnLiveDbg.sys`, and `KnLiveDbgProbe.sys`. The generated resource header is written under `.build\generated`, while `shared\KnLiveDbgVersion.h` remains a `0.0.0` fallback for direct Visual Studio builds that do not run the helper script.
+The build helper also stages the pinned `vendor\debugging-tools\x64` runtime beside the EXE (`dbghelp.dll`, `dbgeng.dll`, `dbgcore.dll`, `DbgModel.dll`, `msdia140.dll`, `symsrv.dll`, `srcsrv.dll`, and `symsrv.yes`) so DbgHelp and DbgEng can use the Microsoft symbol server instead of falling back to the limited System32 runtime. If the vendor pair is missing, the script falls back to the locally installed Windows Kits Debugging Tools copy. If `symsrv.dll` is staged but `symsrv.yes` is missing, the sync script, build script, and EXE startup path create `symsrv.yes` before symbol loading. Startup creates `\symbols` and uses it as the downstream symbol store, so downloaded PDBs stay with the runnable EXE bundle rather than going to `C:\Symbols`. When `msdia140.dll` is staged, startup registers it automatically with `DllRegisterServer` before symbol initialization. The symbol engine also has a no-reg fallback that loads the staged `msdia*.dll` directly and creates `IDiaDataSource` through `DllGetClassObject`, so type fallback can still work when COM registration is unavailable.
+
+Create a release zip:
+
+```powershell
+.\tools\release.ps1 -Configuration Release
+```
+
+The release helper runs a version-bumped build unless `-SkipBuild` or `-NoVersionBump` is supplied, then creates `release\KnLiveDbg--Release-x64.zip` containing the built EXE/SYS files, staged Debugging Tools runtime, PDB/CER/CAT files when present, `README.md`, the vendored runtime manifest when present, and `kn-live-dbg-version.json`.
+
+Expected outputs:
+
+```text
+x64\Release\KnLiveDbg.exe
+x64\Release\KnLiveDbg.sys
+x64\Release\KnLiveDbgProbe.sys
+x64\Release\dbghelp.dll
+x64\Release\DbgModel.dll
+x64\Release\symsrv.dll
+```
+
+## Run
+
+Run from an elevated console:
+
+```powershell
+cd .\x64\Release
+.\KnLiveDbg.exe
+```
+
+The EXE expects `KnLiveDbg.sys` beside it. Keep the staged Debugging Tools DLLs beside the EXE as well when copying the tool to another directory; otherwise Windows may load `C:\Windows\System32\dbghelp.dll` without `symsrv.dll`, and startup can report `symType=0 (SymNone)` while trying to download the kernel PDB. If `symsrv.dll` is present but `symsrv.yes` is missing, startup creates `symsrv.yes` before calling DbgHelp so first-run symbol-server consent does not block noninteractive PDB downloads. Startup creates `\symbols`, excludes that cache tree from plain local-directory scanning, and then uses `SRV*\symbols*https://msdl.microsoft.com/download/symbols` as the default Microsoft symbol path. If `msdia140.dll` is present, startup registers DIA COM automatically before DbgHelp/DIA initialization; if registration is not available, DIA type fallback can still instantiate the staged DLL directly without registry state. It resolves the absolute driver path, updates an existing service config when present, creates the service when missing, starts it, and waits for `SERVICE_RUNNING`. Startup, single-instance acquisition, install/update, driver load, device open, ABI verification, automatic EXE-directory symbol path discovery, EXE-local symbol cache setup, symbol initialization, default `nt` kernel PDB download/load, probe load, and unload paths are printed as colored staged `[ .. ]`, `[ OK ]`, `[WARN]`, and `[FAIL]` rows. Only one `KnLiveDbg.exe` instance can run at a time; a second elevated process exits before touching SCM or the driver. On successful startup the console prints a colored welcome banner plus a dashboard with driver, write gate, backend, symbols, AI, probe, and quick-action hints before the `knkd>` prompt. `home` or `dashboard` redraws that screen. `q`, `quit`, `exit`, EOF, Ctrl+C, and `unload` close the device handle, stop the main driver, delete the service, and wait for deletion before exit. If the session loaded `KnLiveDbgProbe.sys` with `probe load`, that probe service is also stopped and deleted during process cleanup. Use `drvstatus` to inspect SCM state plus the active single-controller owner/write-mode state. Use `probe load` when you want the optional positive-control driver loaded from the same output directory.
+
+Interactive command dispatch has a delayed progress watchdog. Silent commands that run longer than about one second print a colored `still running` status line with elapsed time, then a neutral `finished` line when control returns. Once a command starts producing stdout/stderr, the watchdog suppresses further progress rows so status text does not interleave with command output. Console color changes and direct progress writes are serialized so a progress row cannot leave the prompt/output color stuck.
+
+The `knkd>` prompt supports Tab completion for registered commands and context-aware subcommands, plus Up/Down history recall for recent commands. Examples include `callbacks ` for callback scopes, `callbacks object /module` for the module option, `ai ` for primary AI actions, `ai explain callbacks ` for callback scopes, `ai config ` for provider setup, `backend `, `probe `, `procctx `, `write `, and option completion such as `dt -`, `vtop /`, and `db /`. Callback completion and parsing use only canonical scope names (`object`, `registry`, `process`, `thread`, `imageload`, `minifilter`) plus `all`, `/module`, and `help`; short aliases are intentionally not accepted. Help is available as both `help ` and ` help`; nested AI topics also support `ai help` or `ai help `. When a prefix is ambiguous, the prompt prints matching candidates and redraws the current input line without dispatching anything.
+
+Native `` parameters accept simple arithmetic before dispatching to memory, type, disassembly, translation, and AI-preview helpers. Examples include `dt nt!_PS_PROTECTION 0xffffb40c8c1540c0+5fa`, `dq nt!PsLoadedModuleList+10`, and `u nt!KiSystemCall64-20`.
+
+Interactive output highlights high-signal categories and identifiers with console colors. Callback record tags such as `[ob]`, object type names, modules, symbols, translated physical addresses, module/symbol names, type names, field names, and dump line addresses are colored for scanning, while captured stdout/transcript text remains plain.
+
+## TUI Commands
+
+```text
+help
+help all
+help
+ help
+ai help
+help callbacks
+callbacks help
+ai help
+home
+dashboard
+backend [auto|native|dbgeng]
+kdinit [/local [connect-options]|/remote ]
+kd
+kddetach
+version
+drvstatus
+probe [status|load [sys-path]|info|reset|unload]
+.sympath [path]
+.sympath+
+.reload
+lm [filter]
+x
+ln
+addr
+query [length]
+vtop [length]
+vtop /cr3 [length]
+vtop /process [length]
+d, da, db, dc, dd, dD, df, dp, dq, du, dw, dW, dyb, dyd
+d* /process [count]
+dda, ddp, ddu, dpa, dpp, dpu, dqa, dqp, dqu
+dds, dps, dqs
+phys, pdb, pdw, pdd, pdq
+!db, !dw, !dd, !dq
+procctx [status|clear|]
+u [instruction-count]
+uf [max-instructions]
+dt [-rN] [-v] [-b] [address|symbol] [field-filter...]
+dtx [-rN] [-v] [-b] [address|symbol] [field-filter...]
+callbacks [all|object|registry|process|thread|imageload|minifilter] [module]
+callbacks [scope] /module
+!dml_proc [pid]
+ai
+ai status
+ai config [status|providers|provider|policy|model|base-url|effort|auth|test]
+ai plan
+ai explain
+ai show
+ai run
+ai write [confirm]
+ai report
+c
+s [-b|-w|-d|-q]
+f
+m
+write on|off
+e, ea, eb, ed, eD, ef, ep, eq, eu, ew, eza, ezu
+e* [value...] # defaults to System(pid 4) context
+e* /process [value...]
+peb, pew, ped, peq
+!eb, !ew, !ed, !eq
+pe* [value...]
+setfield
+unload
+q, qq, qd, quit, exit
+```
+
+Help syntax notes:
+
+- `help` shows native/TUI commands; `help all` also lists DbgEng-routed WinDbg commands.
+- `help ` and ` help` print detailed command-family syntax.
+- Count arguments are element counts for typed dumps, bytes for range lengths, and instruction counts for `u`/`uf`.
+- `nt!` is treated as the loaded kernel image for symbols and PDB type names.
+- `d*`, `e*`, and `vtop` support `/process ` for one command; `procctx ` pins a default context.
+- Virtual `e*` writes default to System(pid 4) context for kernel addresses and temporarily restore read-only leaf PTE write bits after patching.
+- API-key AI providers load `.env` only from the EXE directory; `ai run` remains read-only and `ai write confirm` is required for write-like plans.
+
+Example:
+
+```text
+knkd> reload
+knkd> lm nt
+knkd> x nt!*Process*
+knkd> ln nt!PsLoadedModuleList
+knkd> u nt!KiSystemCall64 8
+knkd> uf nt!KiSystemCall64
+knkd> dq nt!PsLoadedModuleList 4
+knkd> vtop nt!PsLoadedModuleList
+knkd> procctx 1234
+knkd> db 0000012345678000 80
+knkd> db /process 1234 0000012345678000 80
+knkd> pdb 80
+knkd> !db 80
+knkd> !eb
+knkd> !eq
+knkd> dt nt!_EPROCESS
+knkd> dt -r1 nt!_EPROCESS UniqueProcessId ActiveProcessLinks
+knkd> callbacks all
+knkd> callbacks object
+knkd> callbacks imageload
+knkd> callbacks minifilter
+knkd> callbacks all WdFilter.sys
+knkd> callbacks /module WdFilter.sys
+knkd> !dml_proc
+knkd> !dml_proc 4
+knkd> ai config provider openai-codex-cli
+knkd> ai config test
+knkd> ai a.exe eprocess
+knkd> ai pid 1234 dtb
+knkd> ai explain callbacks all WdFilter.sys
+knkd> ai plan inspect unknown object callbacks
+knkd> ai show
+knkd> ai run 1
+knkd> ai report .\kn-ai-report.md
+knkd> probe load
+knkd> probe info
+```
+
+The registry also knows the standard execution, breakpoint, stack, register, source, exception, I/O port, and script commands. Native live-memory commands stay on the custom driver backend, while stop-state or parser-heavy commands are routed to DbgEng in `auto` or `dbgeng` mode.
+
+Memory display commands use sparse reads. If part of a requested virtual or physical range cannot be read, the printable dump stays aligned and unknown bytes are shown as `??`, with wider unit displays using `0x????...` for unreadable units.
+
+`u` and `uf` are explicit disassembly commands. `u` resolves an address or symbol, reads code bytes through the loaded driver, disassembles them with the vendored Zydis x64 decoder, and remembers the next offset for a following bare `u`; if the driver device is not open, it falls back to the DbgEng disassembly path. `uf` uses the same driver-backed Zydis path and linearly disassembles from the start address until a function terminal instruction such as `ret`, `iret`, `sysret`, `sysexit`, `hlt`, `ud2`, or `int3`, with an optional instruction cap.
+
+## AI Assistant Providers
+
+The `ai` command is an operator intent layer plus an advisory provider bridge. Free-form questions first go through an AI tool planner that can choose from a small local read-only capability catalog, then the C++ executor runs the chosen tools. If no local tool fits, the planner falls back to a normal advisory answer. It does not execute generated debugger commands automatically, and it does not hide write operations. Use it to answer small debugger questions, ask for command plans, explain command output, or draft investigation next steps.
+
+```text
+ai
+ai status
+ai config [status]
+ai config providers
+ai config provider
+ai config policy
+ai config model
+ai config base-url
+ai config effort
+ai config auth
+ai config test [prompt]
+ai plan
+ai explain
+ai show
+ai run
+ai write [confirm]
+ai report
+```
+
+Examples:
+
+```text
+ai a.exe pid
+ai a.exe eprocess
+ai a.exe process eprocess info
+ai pid 1234 dtb
+ai WdFilter.sys object callbacks
+ai explain callbacks all WdFilter.sys
+ai explain dt nt!_EPROCESS UniqueProcessId ActiveProcessLinks
+ai explain uf nt!PspCreateProcessNotifyRoutine 128
+```
+
+Provider configuration is loaded from `.env` beside `KnLiveDbg.exe` and can be overridden by real process environment variables. Copy `.env.example` to the EXE directory as `.env` and fill in only the provider you want to use:
+
+```text
+KNLIVEDBG_AI_PROVIDER=openrouter
+KNLIVEDBG_AI_REMOTE_POLICY=allow-remote
+KNLIVEDBG_AI_MODEL=openai/gpt-oss-120b
+KNLIVEDBG_OPENROUTER_API_KEY=sk-or-...
+```
+
+Or:
+
+```text
+KNLIVEDBG_AI_PROVIDER=deepseek
+KNLIVEDBG_AI_MODEL=deepseek-chat
+KNLIVEDBG_DEEPSEEK_API_KEY=sk-...
+```
+
+Supported keys:
+
+1. `KNLIVEDBG_AI_PROVIDER` selects `openai-codex-cli`, `openai-codex-subscription`, `deepseek`, or `openrouter`.
+2. `KNLIVEDBG_AI_REMOTE_POLICY` selects `allow-remote` or `local-only`; `local-only` blocks HTTP-backed providers such as ChatGPT/Codex OAuth, DeepSeek, and OpenRouter.
+3. `KNLIVEDBG_AI_MODEL` overrides the provider default model.
+4. `KNLIVEDBG_AI_BASE_URL` overrides the provider base URL.
+5. `KNLIVEDBG_DEEPSEEK_API_KEY` or `DEEPSEEK_API_KEY` supplies the DeepSeek API key.
+6. `KNLIVEDBG_OPENROUTER_API_KEY` or `OPENROUTER_API_KEY` supplies the OpenRouter API key.
+7. `KNLIVEDBG_CODEX_ACCESS_TOKEN`, `KERNFORGE_CODEX_ACCESS_TOKEN`, `KNLIVEDBG_CODEX_AUTH_FILE`, or `KERNFORGE_CODEX_AUTH_FILE` supplies ChatGPT/Codex OAuth credentials.
+8. If no Codex auth file is configured, Kn Live Dbg checks `%USERPROFILE%\.kernforge\codex_auth.json` and `%USERPROFILE%\.codex\auth.json`.
+9. `KNLIVEDBG_CODEX_CLI_PATH` overrides the `codex` executable used by `openai-codex-cli`.
+
+Run `codex login` outside Kn Live Dbg when ChatGPT/Codex OAuth credentials are missing or expired. `ai status` shows the loaded `.env` path, remote policy, and credential source. `ai config test` sends a tiny marker request to the selected provider/model and prints transport status, HTTP status when available, elapsed time, and whether the expected marker came back. `ai config policy local-only` can be used during a sensitive session to block HTTP-backed providers without editing `.env`. Legacy direct forms such as `ai policy local-only`, `ai ask`, `ai preview`, `ai analyze callbacks`, `ai annotate`, `ai diagnose`, `ai playbook`, `ai transcript`, and `ai audit` are still accepted for compatibility, but the main help surface groups provider setup under `ai config` and evidence analysis under `ai explain`.
+
+For `ai `, the provider sees the operator prompt plus a capability catalog, not live memory contents. The first catalog includes `process.find`, `process.describe`, `type.describe`, and `callbacks.list`, so prompts such as `ai a.exe process eprocess info` can become a structured tool plan that finds the process through `_EPROCESS.ActiveProcessLinks` and prints PID, EPROCESS, DTB, PEB, or a `dt nt!_EPROCESS` view locally. Callback prompts such as `ai WdFilter.sys object callbacks` can become a validated `callbacks object WdFilter.sys` run through the native callback scanner. The compatibility local process resolver remains as a fallback when the provider is disabled or the tool planner cannot produce a usable local plan.
+
+`ai plan ` asks the selected model to return a strict `kn-live-dbg.ai-plan.v2` command proposal JSON object, validates proposed commands before storing them, and prints numbered commands with purpose, risk, backend, and expected-output notes. Empty commands, missing purpose metadata, unsupported backend expectations, command chaining, multiline commands, nested `ai`, shutdown/unload commands, backend/session mutation, probe service control, bare `kd`, raw `kd` wrapping of blocked commands, overlong commands, and unknown non-DbgEng commands are rejected; write-like proposals are forced to require confirmation. `ai explain ` runs a read-only evidence command, preserves stdout/stderr, adds a deterministic output summary, then asks the selected model for analysis. It has tuned prompts for `callbacks`, `dt`/`dtx`, and `u`/`uf`, so the older `ai analyze callbacks` and `ai annotate` flows are now covered by the shorter explain form.
+
+`ai run ` executes only non-write, non-shutdown planned commands. Write-like commands such as `e*`, `pe*`, `setfield`, `f`, `m`, and raw `kd` wrappers around write-like commands are blocked from `ai run`; `ai write ` prints a write preview with target class, byte count, backup/read-current command, restore-current command for small ranges, verification command, and safe read-only preflight output. `ai write confirm` re-runs the backup read, dispatches the write-like command, re-runs the verification command, and prints a deterministic before/after stdout/stderr diff for the verification command. `ai transcript ` enables JSONL capture of AI events and command stdout/stderr, including backend mode, command class, write-like classification, stdout/stderr character counts, deterministic output summary, raw stdout/stderr, and keep-running state. `ai transcript max ` rotates long transcript files, `ai transcript redact on` redacts long hex addresses and `sk-...` style tokens from captured stdout/stderr, and `ai audit ` writes a separate JSONL record for every write-like command that executes through the normal dispatcher. `ai report ` exports a Markdown summary of the current AI session, transcript settings, write-audit path, and plan.
+
+## DbgEng Backend
+
+The default backend mode is `auto`:
+
+1. Native memory/symbol/type commands run through the custom driver and `DbgHelp`.
+2. Stop-state WinDbg commands, parser-heavy commands, `!extension` commands, and unknown `.meta` commands are routed to DbgEng after `kdinit` or lazy DbgEng initialization.
+3. `backend dbgeng` sends commands directly to `IDebugControl::ExecuteWide`.
+4. `backend native` disables automatic DbgEng routing.
+
+Backend mode behavior:
+
+| Mode | Command routing | Best fit | Notes |
+| --- | --- | --- | --- |
+| `auto` | Native commands use the driver/`DbgHelp` path; DbgEng-only, extension, and unknown meta commands are lazily routed to DbgEng. | Default interactive use. | Keeps live-memory features native while preserving access to WinDbg parser and stop-state commands. |
+| `native` | Uses the native command handlers and blocks generic DbgEng fallback. | Driver-backed memory, symbol, type, callback, disassembly, and physical-memory work. | `!extension`, stack/register/breakpoint/execution/source/exception commands are reported as DbgEng-only instead of being executed. Explicit `u` and `uf` stay driver-backed when the device is open. |
+| `dbgeng` | Sends most non-session commands directly to DbgEng raw execution. | WinDbg-compatible parser behavior. | Session commands, `callbacks`, `!dml_proc`, native physical bang commands, and explicit `u`/`uf` are still handled by the TUI before the raw DbgEng catch-all. |
+
+`kd ` is an explicit raw DbgEng escape hatch and does not depend on the current backend mode.
+
+Examples:
+
+```text
+knkd> kdinit
+knkd> kdinit /remote net:port=50000,key=1.2.3.4
+knkd> !dml_proc
+knkd> backend dbgeng
+knkd> k
+knkd> backend auto
+knkd> kddetach
+```
+
+DbgEng uses `IDebugClient5::AttachKernelWide(DEBUG_ATTACH_LOCAL_KERNEL, ...)` for local mode and `DEBUG_ATTACH_KERNEL_CONNECTION` for `kdinit /remote `. Local mode still follows the limits of Windows local kernel debugging; not every KD command has the same behavior as a remote break-in session. Use native `!dml_proc` when you need a process list without relying on DbgEng current process/thread state or extension exports.
+
+## Native `dt`
+
+Native `dt` now supports both type layout and value display:
+
+```text
+dt nt!_EPROCESS
+dt nt!_EPROCESS
+dt -r1 nt!_EPROCESS
+dt -v nt!_EPROCESS UniqueProcessId
+dt nt!*EPROCESS*
+dt nt!*
+```
+
+Supported options:
+
+- `-r` or `-rN`: recursively expand nested UDT fields up to the requested depth.
+- `-v`: print internal symbol tag/type id/length diagnostics.
+- `-b`: bare output, omitting type names.
+
+Field filters are case-insensitive substring filters applied to field names and type names.
+
+Wildcard type patterns such as `dt nt!*EPROCESS*` and `dt nt!*` enumerate PDB types through `SymEnumTypesW` and apply the `*`/`?` filter in user mode. If DbgHelp cannot enumerate a loaded module's type stream, the symbol engine falls back to DIA UDT enumeration from either the loaded PDB path or the module image plus symbol path. Wildcard `dt` is list-only; use an exact type name when dumping fields or reading a value at an address.
+
+The native symbol engine treats `nt` as the kernel-image alias and loads `ntoskrnl.exe`/`ntkrnl*` into DbgHelp under the `nt` module name, so commands such as `x nt!*`, `ln nt!PsLoadedModuleList`, and `dt nt!*` do not depend on the on-disk kernel image name.
+
+Exact type layouts first try `SymGetTypeFromNameW`. If that lookup fails for an alias-qualified type such as `nt!_TP_POOL`, the symbol engine reuses `SymEnumTypesW` to find the exact type and dumps the layout by module base plus type id before trying DIA. The enumeration fallback matches module-qualified names, leaf names, and leading-underscore variants, so `nt!_OBJECT_TYPE`, `_OBJECT_TYPE`, `OBJECT_TYPE`, and enumerated `nt!_OBJECT_TYPE` spellings resolve to the same type. DIA fallback uses the same full-enumeration matcher and reports module/PDB/image details when a type is not found.
+
+When `DbgHelp` cannot return a usable UDT layout, the symbol engine tries a DIA SDK fallback against the loaded PDB path or resolves the PDB from the module image plus symbol path. DIA creation first uses registered COM and then falls back to direct `DllGetClassObject` activation from the staged `msdia*.dll`, which makes registration failures distinct from real type lookup failures. The fallback is especially useful for field offsets, bit positions, and private type metadata used by callback scanning and `dt`; recursive expansion still prefers the normal `DbgHelp` type-id path when it is available.
+
+## Native Process Listing
+
+`!dml_proc` is implemented natively so it works even when the optional DbgEng backend does not have a current process/thread or cannot load WinDbg extension exports:
+
+```text
+!dml_proc [pid]
+```
+
+The command resolves PID 4 through the driver, uses PDB metadata for `_EPROCESS.ActiveProcessLinks`, then walks the active process list from live kernel memory. If a decimal PID argument is supplied, for example `!dml_proc 4`, only records whose process ID matches that value are printed. Output includes EPROCESS, PID, parent PID when available, active thread count, directory-table base, image name, and a ready-to-run `dt nt!_EPROCESS ` follow-up.
+
+## Kernel Callback Scanner
+
+`callbacks` walks live kernel callback lists using kernel PDB type layouts and the native memory reader:
+
+```text
+callbacks all
+callbacks object
+callbacks registry
+callbacks process
+callbacks thread
+callbacks imageload
+callbacks minifilter
+callbacks object WdFilter.sys
+callbacks minifilter UnionFS
+```
+
+The scanner currently covers:
+
+1. Object-manager filters from `_OBJECT_TYPE.CallbackList`. The scanner first discovers `_OBJECT_TYPE` objects through `ObTypeIndexTable` and `_OBJECT_TYPE.Name`, then falls back to `PsProcessType`, `PsThreadType`, and `ExDesktopObjectType` when the table is unavailable. Some public nt PDBs expose `_OBJECT_TYPE.CallbackList` but omit the private callback item type; in that expected public-symbol case the scanner uses a guarded x64 item-layout fallback, validates live list pointers, callback-entry pointers, operation masks, and callback routine pointers, and only reports a warning if the PDB exposes a partial/drifted item layout or the live validation fails.
+2. Registry callbacks by enumerating and validating registry callback list-head candidates such as `CmpCallbackListHead`, then walking callback context entries with guarded x64 fallback layouts when public nt PDBs omit `_CM_CALLBACK_CONTEXT_BLOCK`.
+3. Process creation callbacks by enumerating and validating create-process notify routine table candidates such as `PspCreateProcessNotifyRoutine`, then decoding callback routine blocks with a stable x64 fallback when public nt PDBs omit `_EX_CALLBACK_ROUTINE_BLOCK`. Process notify metadata is decoded into `notifyType`, for example `0x2` becomes `PsSetCreateProcessNotifyRoutineEx`.
+4. Thread creation callbacks by enumerating and validating create-thread notify routine table candidates such as `PspCreateThreadNotifyRoutine`, then decoding callback routine blocks with the same stable x64 fallback.
+5. Image load callbacks by enumerating and validating load-image notify routine table candidates such as `PspLoadImageNotifyRoutine`, then decoding callback routine blocks with the same stable x64 fallback.
+6. Minifilter callbacks by discovering `fltmgr!FltGlobals`, validating the frame-list root, walking `FrameList`, each `_FLTP_FRAME.RegisteredFilters`, and each `_FLT_FILTER.Operations` registration array.
+
+Each record prints the discovered root address and source, callback function address, nearest symbol, owning module, object type name/index/address and discovery source for object callbacks, minifilter name/altitude/frame/driver object when present, callback/list block addresses, altitude when present, and registration/callback context pointer. Object callback rows use `object=` in both the header and detail line so Process, Thread, Desktop, and other object-manager surfaces are visible without interpreting the raw `_OBJECT_TYPE` address. Image-load output uses `function` for the `PLOAD_IMAGE_NOTIFY_ROUTINE` owner and reports the decoded notify block plus raw encoded slot value. Minifilter output includes operation callbacks, filter unload, instance setup/teardown, name provider, KTM, section, and volume-mount routines when the target build exposes those fields. Add a module name after the callback scope, for example `callbacks object WdFilter.sys` or `callbacks minifilter UnionFS`, to print only records whose pre/function or post callback is owned by that module; the match is case-insensitive and treats `WdFilter` and `WdFilter.sys` as the same module stem. Registry callback routines are validated against loaded kernel image ranges before being emitted. Registration and callback context pointers are annotated with a module and nearest symbol only when they point into a loaded kernel image; process creation notify block context values are printed as `notifyType= metadata=` because they are internal notify metadata, not callback module pointers. The implementation is PDB-driven where public or private type metadata exists; the expected public-PDB object-callback item fallback is validated against live pointers before records are emitted, while warnings are reserved for partial PDB layouts, structure drift, or failed validation.
+
+## Virtual-To-Physical And Physical Memory
+
+Native physical memory support is intentionally explicit:
+
+```text
+vtop nt!PsLoadedModuleList
+vtop /cr3 100
+vtop /process 100
+procctx
+db 80
+db /process 80
+pdb 80
+pdq 10
+!db 80
+!dq 10
+write off
+!eb
+peq
+!eq
+```
+
+`vtop` uses the current CR3 when no directory-table base is supplied. `vtop /process` asks the driver to resolve the target EPROCESS, reads the DTB offsets from PDB type metadata, and walks that process address space. It prints whether the walk used PML4 or PML5, the physical address of each page-table entry that was read, and the leaf entry kind, physical address, and writable state. `procctx ` stores the same process context for later user VA reads/writes. `d*` commands accept `/process ` for one-shot process-aware access, while native `e*` virtual writes default to the System process context (`pid 4`) and accept `/process ` when a process-specific user VA should be edited. If `e*` is invoked with only an address, the TUI enters a WinDbg-style one-line edit prompt that shows the current value, accepts replacement values, and cancels on an empty line, `.`, `q`, or `quit`. These paths translate each VA range before access. Reads use physical pages, translated user VA writes use physical writes through the selected process context, and kernel VA writes use the original virtual address after any required page-table write-enable step. When a translated leaf PTE/PDE/PDPTE is present but not writable, `e*` temporarily sets the write bit on that leaf entry, flushes the virtual address, performs the edit, restores the write bit to its original state, and flushes again. The driver walks x64 paging structures through physical reads, reports PML5E/PML4E/PDPTE/PDE/PTE entries when applicable, handles 4 KB, 2 MB, and 1 GB pages, and returns the number of contiguous bytes remaining in the translated page. Physical reads are available through both native names (`phys`, `pdb`, `pdw`, `pdd`, `pdq`) and WinDbg-style extension names (`!db`, `!dw`, `!dd`, `!dq`). Physical writes are available through both native names (`peb`, `pew`, `ped`, `peq`) and WinDbg-style extension names (`!eb`, `!ew`, `!ed`, `!eq`), and are routed through page-sized `MmMapIoSpaceEx` mappings with `MmGetVirtualForPhysical` fallback when Windows already has a direct mapping for the PFN. Address-only physical enter commands prompt with the current physical value before writing, matching the native `eb` edit flow. Write mode is enabled by default for each device handle; use `write off` when you want a read-only console session.
+
+## Positive-Control Probe
+
+`KnLiveDbgProbe.sys` is an optional test driver that exposes a deterministic 4 KB contiguous nonpaged buffer. It is intended for smoke tests of virtual reads, VA-to-PA translation, physical reads, physical writes, and restore flows without guessing at arbitrary kernel memory.
+
+```text
+probe load
+probe status
+probe info
+db 40
+pdb 40
+probe reset
+probe unload
+```
+
+The buffer pattern is `(index * 13 + 0x5a) & 0xff`. `probe info` prints both the virtual and physical buffer addresses and example `db`/`pdb` commands.
+
+## Operational Caveats
+
+1. This is a live kernel memory tool. Bad writes can crash or corrupt the machine.
+2. `MmCopyMemory` makes reads fault-tolerant, but it does not make all addresses meaningful.
+3. Writes are enabled by default, can be disabled with `write off`, and still require an IOCTL acknowledgment magic.
+4. Native VA-to-PA translation supports x64 4-level paging and active LA57 five-level paging, and reports which paging depth was used for each walk.
+5. Physical memory writes can corrupt page tables, code, pool, device memory, or firmware-owned ranges.
+6. Type dumping depends on matching PDBs and the local `DbgHelp`/DIA behavior.
+7. Callback enumeration depends on internal kernel symbols and type names. Field drift is reported as a warning; Filter Manager callback decoding depends on matching `fltmgr` private type layouts.
+8. Do not hard-code kernel structure offsets for production use; resolve them from symbols at runtime.
+9. Live loading requires test-signing or another valid code integrity path.
+10. Native mode intentionally differs from full WinDbg for commands that stop or control the target. Use `backend dbgeng` for DbgEng-backed command execution, and still expect local-kernel debugging limitations.
+
+```
+
+`docs/AI_ASSISTED_WORKFLOWS.md`:
+
+```md
+# AI-Assisted Workflows
+
+This document captures implementation ideas for adding AI assistance to Kn Live Dbg. The goal is not to turn the tool into an autonomous kernel editor. The useful direction is an operator assistant that explains live kernel state, proposes commands, highlights risk, and produces repeatable investigation records.
+
+## Design Principles
+
+1. AI assistance should be advisory by default. It can suggest commands and explain results, but command execution should stay visible to the operator.
+2. Every generated command should have a preview that shows the exact Kn Live Dbg command line, selected backend expectation, target address or symbol, and expected read/write behavior.
+3. Write operations need stronger gates than read operations. Before `e*`, `pe*`, `setfield`, or other write commands, the assistant should show the current value, proposed value, byte width, target VA/PA context when available, backup command, and post-write verification command.
+4. Reports should keep raw evidence. AI summaries must include the original commands, relevant output snippets, addresses, symbols, modules, and failure messages used to reach the conclusion.
+5. Backend behavior must be explicit. The assistant should know when a request maps to the native backend, DbgEng fallback, or `kd ` escape hatch.
+6. Sensitive memory and symbol output should be handled through a configurable model provider policy. Offline/local models should be possible for private kernel state.
+7. The driver should remain a narrow memory primitive provider. AI parsing, planning, risk scoring, and report generation belong in user mode.
+
+## Current Implementation
+
+The first integration layer is implemented as a user-mode `ai` command and `AiProviderRuntime` module. It is intentionally advisory, but the visible surface is now smaller:
+
+1. `ai ` is the default operator entrypoint. It asks the selected provider to choose from a small read-only capability catalog, then validates and executes the selected local tools in C++. The initial catalog exposes `process.find`, `process.describe`, `type.describe`, `callbacks.list`, and `assistant.answer`.
+2. `ai status` reports the selected provider, model, base URL, remote policy, credential source, loaded `.env` path, Codex CLI path, reasoning effort, and timeout.
+3. `ai config ...` groups provider setup and smoke checks under one visible subcommand. It supports `status`, `providers`, `provider`, `policy`, `model`, `base-url`, `effort`, `auth`, and `test`.
+4. `ai plan ` asks the model for a strict command proposal JSON object and stores the parsed command plan in memory.
+5. `ai explain ` runs a read-only evidence command, preserves raw output, then asks the model for interpretation and follow-up commands. It has tuned prompts for `callbacks`, `dt`/`dtx`, and `u`/`uf`.
+6. `ai show` prints the most recent parsed or playbook command plan.
+7. `ai run ` executes only planned commands that are not write-like, shutdown, unload, or nested AI commands.
+8. `ai write [confirm]` provides an explicit confirmation path for planned write-like commands. Without `confirm`, it prints target classification, size, backup/read-current, restore-current for small ranges, translation, verification, purpose, risk, and confirmation syntax.
+9. `ai report ` exports a Markdown report with session context, provider status, transcript settings, write-audit path, the parsed plan, and the raw AI plan response.
+
+The older detailed forms remain accepted for compatibility: `ai providers`, `ai provider`, `ai policy`, `ai model`, `ai base-url`, `ai effort`, `ai auth`, `ai preview`, `ai ask`, `ai analyze callbacks`, `ai annotate`, `ai diagnose`, `ai playbook`, `ai transcript`, and `ai audit`.
+
+Initial provider support:
+
+1. `openai-codex-cli` shells out to `codex exec`, mirroring KernForge's Codex CLI bridge pattern.
+2. `openai-codex-subscription` uses ChatGPT/Codex OAuth-style bearer tokens and the Codex Responses endpoint. It can read `KNLIVEDBG_CODEX_ACCESS_TOKEN`, `KERNFORGE_CODEX_ACCESS_TOKEN`, configured auth files, `%USERPROFILE%\.kernforge\codex_auth.json`, and `%USERPROFILE%\.codex\auth.json`.
+3. `deepseek` uses an OpenAI-compatible chat-completions request with DeepSeek defaults and optional reasoning effort.
+4. `openrouter` uses an OpenAI-compatible chat-completions request with OpenRouter defaults and metadata headers.
+
+The runtime automatically loads `.env` only from the executable directory. Real process environment variables override `.env` values. `.env.example` documents the common OpenRouter and DeepSeek keys plus `KNLIVEDBG_AI_REMOTE_POLICY`. `.env` and `.env.local` are ignored by Git.
+
+`ai config test [prompt]` is the provider round-trip smoke check. Without a custom prompt, it asks the selected provider/model to return `kn-live-dbg-ai-ok`, then prints the configured provider, model, remote policy, credential status, transport result, HTTP status when available, elapsed time, and marker match result.
+
+The current layer can execute approved read-only model-proposed commands through `ai run`. Write-like commands remain blocked from `ai run` and require `ai write confirm`. Write confirmation now runs deterministic preflight reads before mutation, emits exact byte restore commands for small recognized ranges, runs verification reads afterward when the command can be classified, and prints a deterministic before/after stdout/stderr diff for the verification command. Transcript mode captures full command output after it is enabled, including backend mode, origin, command class, write-like classification, stdout, stderr, keep-running state, output character counts, and deterministic output summaries. Transcript rotation and stdout/stderr redaction are configurable for long live sessions, and the optional write audit log records every write-like command that passes through the normal dispatcher. AI callback analysis now fits under `ai explain callbacks`, while compatibility aliases still consume normal `callbacks [module]` evidence. The command proposal JSON is now versioned as `kn-live-dbg.ai-plan.v2`, with stricter command metadata validation for purpose, risk, backend expectation, expected output, command chaining, session mutation, and raw `kd` write/session wrapping.
+
+The main `ai help` output stays focused on the primary workflow. Detailed compatibility topics still have operator help through `ai help` and `ai help `.
+
+## Candidate Features
+
+### Natural-Language Operator Entry
+
+Implemented entrypoint: `ai `.
+
+The command now behaves like a small tool-using agent. The model receives the operator request and the capability catalog, returns a strict `kn-live-dbg.ai-capability-plan.v1` JSON object, and the local executor runs only supported read-only tools. The first catalog handles process and `_EPROCESS` questions by walking `_EPROCESS.ActiveProcessLinks` through the same native data path as `!dml_proc`, and callback listing by dispatching to the native `callbacks` scanner:
+
+- "a.exe pid" -> matching PID records from the live process list
+- "a.exe eprocess" -> matching `_EPROCESS` addresses
+- "pid 1234 dtb" -> kernel and user directory-table base values
+- "pid 1234 peb" -> `_EPROCESS.Peb` when the field is available
+- "a.exe process eprocess info" -> `process.find` followed by `process.describe` or `type.describe`
+- "WdFilter.sys object callbacks" -> `callbacks.list` with `scope=object` and `module=WdFilter.sys`
+
+The model-backed planner remains available through `ai plan ` for multi-command investigations:
+
+- "Show object callbacks" -> `callbacks object`
+- "Dump this address as an EPROCESS" -> `dt nt!_EPROCESS `
+- "Translate this virtual address and read the physical bytes" -> `vtop ` followed by `pdb ` or `!db `
+- "Disassemble this callback routine" -> `u ` or `uf `
+
+Implementation notes:
+
+1. Keep the AI-facing catalog small and explicit; adding more tools should be easier than adding more natural-language keyword rules.
+2. Send only the request and catalog during tool selection. Live process records, kernel addresses, and structure dumps are produced by the local executor after validation.
+3. Resolve symbols before proposing commands when the request contains a symbol-like token.
+4. Prefer `backend auto` unless the requested command clearly needs raw DbgEng parser semantics.
+5. Display a command preview and require confirmation before execution for `ai plan` command proposals.
+6. For ambiguous requests, use `assistant.answer` or propose two or three command plans with tradeoffs rather than guessing silently.
+
+### Callback Analysis Report
+
+Implemented entrypoint: `ai explain callbacks [all|object|registry|process|thread|imageload|minifilter] [module]`.
+
+Compatibility entrypoint: `ai analyze callbacks [all|object|registry|process|thread|imageload|minifilter] [module]`.
+
+The command post-processes `callbacks` output into an investigation report:
+
+1. Count object-manager, registry, process creation, thread creation, image-load, and minifilter callbacks.
+2. Group records by owning module and callback surface.
+3. Flag callback addresses that do not resolve to a loaded module or nearest symbol.
+4. Flag addresses outside expected executable image ranges.
+5. Highlight modules that own callbacks across multiple surfaces.
+6. Highlight unusual minifilter altitudes, missing names, missing unload routines, or duplicate operation handlers.
+7. Separate known platform/security drivers from unknown or unsigned modules when signature metadata is available.
+
+The report should include module name, callback address, nearest symbol, callback context, root/list address, object type or filter address, altitude, and confidence notes.
+
+### `dt` and Structure Interpretation
+
+Implemented entrypoint: `ai explain
`.
+
+The command layers semantic explanations on top of native `dt` and `dtx` output:
+
+1. Explain important fields for common kernel types such as `_EPROCESS`, `_ETHREAD`, `_DRIVER_OBJECT`, `_DEVICE_OBJECT`, `_OBJECT_TYPE`, `_CALLBACK_ENTRY`, `_CM_CALLBACK_CONTEXT_BLOCK`, `_EX_CALLBACK_ROUTINE_BLOCK`, `_FLT_FILTER`, and `_FLT_OPERATION_REGISTRATION`.
+2. Suggest follow-up commands for pointer fields, `LIST_ENTRY` fields, callback routine fields, and object name fields.
+3. Detect suspicious or inconsistent values such as null callback routines, self-referential list entries outside the expected head, invalid-looking pool pointers, or module pointers outside loaded image ranges.
+4. Preserve raw field offsets and values so the AI explanation can be audited.
+
+### Write Safety Assistant
+
+Implemented entrypoint: `ai write [confirm]`.
+
+Because write mode is enabled by default per device handle, the AI path adds an operator safety layer around planned mutations:
+
+1. Before a write, read and display the current value.
+2. Show the exact target range, write width, interpreted type field when available, and whether the write crosses a page boundary.
+3. For native `e*` virtual writes, treat the default address-space context as System (`pid 4`) unless the command includes `/process `.
+4. For virtual writes, offer `vtop` context when useful, including the leaf entry physical address, writable state, and whether a temporary write-bit flip plus VA flush is expected. Kernel VA edits should be described as temporary write-enable plus original-VA write; translated user VA edits should be described as physical writes through the selected process context.
+5. For physical writes, warn when the target could be page tables, device memory, or firmware-owned memory.
+6. Generate backup and restore commands before applying the write.
+7. Re-read the target after the write and show a compact before/after diff.
+8. Mark high-risk targets such as list links, reference counts, callback routine pointers, dispatch tables, page table entries, and executable code.
+
+This feature should never hide the exact command being executed.
+
+### Disassembly Annotator
+
+Implemented entrypoint: `ai explain [instruction-count]`.
+
+Compatibility entrypoint: `ai annotate [instruction-count]`.
+
+The command adds AI analysis on top of `u` and `uf` output:
+
+1. Summarize the likely purpose of a function.
+2. Identify direct and indirect call targets when symbols are available.
+3. Classify routines as object callback, registry callback, process callback, thread callback, image-load callback, minifilter operation callback, dispatch routine, or unknown.
+4. Highlight suspicious patterns such as indirect calls through writable memory, global list mutation, callback registration, page table access, MSR access, or code patching.
+5. Suggest next commands such as `ln`, `x`, `dt`, `dq`, or `uf` on a discovered call target.
+
+The annotator should treat disassembly as evidence, not proof. The output should include uncertainty and the exact instruction ranges used.
+
+### Symbol and Layout Diagnostics
+
+Implemented entrypoint: `ai diagnose `.
+
+The command uses AI to explain failures in symbol, type, and backend setup:
+
+1. PDB mismatch, missing private type information, or DIA/DbgHelp failure.
+2. Field drift between Windows builds.
+3. Missing non-exported symbols used by callback discovery.
+4. DbgEng local-kernel attach limitations.
+5. Fallback root selection for callback scanners.
+6. DbgEng command failures caused by local KD behavior rather than command syntax.
+
+The diagnostic output should recommend concrete remediation steps such as `.sympath`, `.reload`, `backend auto`, `kdinit`, re-running as administrator, or checking the Windows build and symbol cache.
+
+### Investigation Playbooks
+
+Implemented entrypoint: `ai playbook [argument] [run|dry-run]`.
+
+The command packages common multi-command investigations into repeatable workflows:
+
+1. Callback surface audit: `callbacks all`, module grouping, disassembly of unknown callbacks, report generation.
+2. Suspect driver surface map: list modules, enumerate symbols, find callbacks, inspect dispatch table, inspect minifilter registrations.
+3. Address provenance: `ln`, module range lookup, `vtop`, `dq/db`, optional `dt`, and disassembly if executable.
+4. Minifilter chain review: enumerate filters, sort by altitude, inspect operation callbacks, summarize unload/name-provider/instance routines.
+5. Object callback integrity review: discover object types, walk callback lists, inspect pre/post operation routines and contexts.
+
+Each playbook has a dry-run mode that prints the planned command sequence before execution. `run` dispatches the plan through the same guarded executor used by `ai run`.
+
+### Session Report Generation
+
+Generate Markdown or JSONL reports from an operator session:
+
+1. Commands executed, backend used, and timestamps.
+2. Driver load/unload and write mode transitions.
+3. Symbol path, module baseline, and Windows build information.
+4. Callback records and analysis annotations.
+5. Write operations with before/after evidence.
+6. Errors, partial reads, failed symbol lookups, and fallback paths.
+
+The report format should be stable enough to compare sessions across Windows builds and machines.
+
+## Suggested Implementation Order
+
+No immediate AI workflow backlog items remain in this document. Larger platform items are tracked in `ARCHITECTURE.md`.
+
+Completed implementation-order items:
+
+1. Transcript/event rotation and redaction controls are implemented with `ai transcript max` and `ai transcript redact`.
+2. Write-command JSONL audit logging is implemented with `ai audit `.
+3. Callback module filtering is implemented with `callbacks [scope] [module]`, and `ai explain callbacks` consumes the same filtered command output as evidence.
+4. Deterministic before/after diff rendering is implemented for `ai write confirm` verification output.
+5. Model-proposed command plans are validated at ingestion with the v2 schema contract: empty commands, missing purpose metadata, unsupported backend expectations, command chaining, multiline commands, nested `ai`, shutdown/unload commands, backend/session mutation, probe service control, bare `kd`, raw `kd` wrapping of blocked commands, overlong commands, and unknown non-DbgEng commands are rejected before they can be shown as a runnable plan. Write-like proposals, including raw `kd` write-like wrappers, are forced to require confirmation.
+6. Command transcript and AI evidence prompts include deterministic output summaries with stdout/stderr character counts, line counts, interesting-line counts, and first/last non-empty lines before raw output.
+7. Local/offline provider policy is implemented with `ai config policy local-only` and `KNLIVEDBG_AI_REMOTE_POLICY=local-only`, which block HTTP-backed providers.
+
+## Non-Goals
+
+1. Do not move AI parsing or model execution into the kernel driver.
+2. Do not allow hidden autonomous writes.
+3. Do not treat AI explanations as authoritative without raw command evidence.
+4. Do not require a remote model provider for normal live-kernel use.
+
+```
+
+`docs/ARCHITECTURE.md`:
+
+```md
+# Architecture
+
+## Recommended Split
+
+Kn Live Dbg follows a LiveKD-style split:
+
+1. Kernel driver
+ - Owns only privileged memory operations.
+ - Creates the device with an Administrators/SYSTEM-only security descriptor.
+ - Validates IOCTL buffers and sizes.
+ - Uses `MmCopyMemory` for virtual reads.
+ - Walks x64 page tables for VA-to-PA translation.
+ - Detects active LA57 and includes PML5E in translation responses when five-level paging is enabled.
+ - Uses `MmCopyMemory` for physical reads.
+ - Uses page-sized `MmMapIoSpaceEx` mappings for physical writes, with `MmGetVirtualForPhysical` fallback when the kernel already has a direct mapping for the target PFN.
+ - Keeps write access controlled per open handle, with writes enabled by default.
+ - Enforces one active controller PID at a time.
+
+2. User-mode TUI
+ - Owns driver install/load/unload through SCM.
+ - Acquires a process-wide named mutex before touching SCM so only one `KnLiveDbg.exe` instance can run at a time.
+ - Displays colored staged lifecycle output for elevation checks, single-instance acquisition, SCM query/install/start, device open, ABI verification, symbol initialization, probe load, and service unload paths.
+ - Automatically closes the device handle, stops the main driver, and deletes the service on normal process exit, EOF, Ctrl+C, `q`, `quit`, `exit`, and `unload`.
+ - Tracks `probe load` within the session and automatically stops/deletes the probe service during process cleanup when the session loaded it.
+ - Prints a colored startup welcome banner and dashboard with driver, write gate, backend, symbol, AI, probe, and quick-action status before the interactive prompt.
+ - Owns kernel module enumeration.
+ - Owns symbol path, PDB loading, type lookup, and field offset resolution.
+ - Uses DIA SDK as a fallback when `DbgHelp` cannot return complete UDT field metadata.
+ - Owns PDB-driven callback list decoding for object, registry, process, thread, image-load, and minifilter callbacks.
+ - Presents Windbg-like commands.
+ - Redraws the dashboard with `home` or `dashboard`.
+ - Optionally attaches a DbgEng local-kernel backend for commands that need debugger-engine semantics.
+
+This keeps the driver small and reduces the amount of complex parser/symbol code running in kernel mode.
+
+## IOCTL Contract
+
+The ABI lives in `shared/KnLiveDbgIoctl.h`.
+
+Current calls:
+
+1. `IOCTL_KNDBG_GET_VERSION`
+2. `IOCTL_KNDBG_READ_VIRTUAL`
+3. `IOCTL_KNDBG_WRITE_VIRTUAL`
+4. `IOCTL_KNDBG_SET_WRITE_MODE`
+5. `IOCTL_KNDBG_QUERY_ADDRESS`
+6. `IOCTL_KNDBG_TRANSLATE_VIRTUAL`
+7. `IOCTL_KNDBG_READ_PHYSICAL`
+8. `IOCTL_KNDBG_WRITE_PHYSICAL`
+9. `IOCTL_KNDBG_GET_SESSION_STATUS`
+10. `IOCTL_KNDBG_RESOLVE_PROCESS`
+11. `IOCTL_KNDBG_FLUSH_VIRTUAL`
+
+All requests include an explicit `Size` field. Variable read/write payloads use `FIELD_OFFSET(..., Data)` as the header size. ABI version 5 adds `IOCTL_KNDBG_FLUSH_VIRTUAL` plus explicit translation metadata for paging level and page-table entry physical addresses.
+
+## Physical Memory Flow
+
+1. `vtop` sends a virtual address, optional directory-table base, and requested length to the driver.
+2. If the directory-table base is zero, the driver uses the current x64 CR3.
+3. `vtop /process` and `procctx ` resolve process DTBs by asking the driver to read PDB-resolved EPROCESS offsets.
+4. The driver checks CR4.LA57, records whether the walk is PML4 or PML5, reads PML5E when LA57 is active, then reads PML4E, PDPTE, PDE, and PTE entries with `MmCopyMemory(..., MM_COPY_MEMORY_PHYSICAL)`.
+5. The walk supports 4 KB pages plus 2 MB and 1 GB large pages.
+6. The response reports CR3, VA, PA, paging level, page size, page offset, contiguous translated bytes, the page-table entries that were used, the physical address of each entry, and the leaf PTE/PDE/PDPTE physical address plus writable state.
+7. `d*` can use `/process ` or a stored `procctx` to translate user VAs page-by-page for physical reads. Native `e*` virtual writes default to the System process context (`pid 4`) and can use `/process ` for process-specific user VAs. Address-only `e*` first reads the current value through the selected context and opens a one-line replacement prompt before writing. If the translated leaf PTE/PDE/PDPTE is not writable, `e*` writes that leaf entry through physical memory with the write bit set, flushes the VA through `IOCTL_KNDBG_FLUSH_VIRTUAL`, performs kernel-VA edits through the original virtual address, uses physical writes for translated user VAs, restores the original write state, and flushes again before continuing.
+8. Virtual and physical display commands perform sparse reads for dump output. Readable bytes are printed normally, while unreadable bytes keep the requested layout and are shown as `??` or `0x????...` for wider units.
+9. `phys`, `pdb`, `pdw`, `pdd`, `pdq`, `!db`, `!dw`, `!dd`, and `!dq` read physical memory directly through `IOCTL_KNDBG_READ_PHYSICAL`.
+10. `peb`, `pew`, `ped`, `peq`, `!eb`, `!ew`, `!ed`, and `!eq` write physical memory through `IOCTL_KNDBG_WRITE_PHYSICAL`; address-only form prompts with the current physical value before writing, and write mode starts enabled and can be disabled with `write off`.
+
+## Native Process Listing Flow
+
+1. `!dml_proc` stays on the native TUI path, accepts an optional decimal PID filter, and is not routed to DbgEng extension loading.
+2. The all-process form resolves PID 4 through `IOCTL_KNDBG_RESOLVE_PROCESS` using PDB-resolved `_KPROCESS.DirectoryTableBase` offsets.
+3. User mode resolves `_EPROCESS.ActiveProcessLinks`, `_EPROCESS.UniqueProcessId`, and optional display fields from the loaded kernel PDB.
+4. The walker prefers `nt!PsActiveProcessHead` as the list anchor. If that symbol is unavailable, it falls back to PID 4's `ActiveProcessLinks`, prints the System process first, then follows `Flink` with a guard for the hidden list head.
+5. Output includes EPROCESS, PID, parent PID when available, active thread count, directory-table base, image name, and a `dt nt!_EPROCESS ` follow-up.
+
+## Symbol Flow
+
+1. TUI calls `NtQuerySystemInformation(SystemModuleInformation)`.
+2. TUI translates `\SystemRoot\...` paths into local Windows paths.
+3. The build helper stages the pinned `vendor\debugging-tools\x64` Debugging Tools runtime DLLs beside the EXE so DbgHelp can load `symsrv.dll`, DbgEng can load `DbgModel.dll`, and both can use the Microsoft symbol server instead of the limited System32 runtime. If the vendor runtime is incomplete, it falls back to the locally installed Windows Kits Debugging Tools copy for missing debugger-runtime files.
+4. The sync script, build script, and EXE startup path create `symsrv.yes` when `symsrv.dll` is present but the consent marker is missing.
+5. Startup registers staged `msdia140.dll` with `DllRegisterServer` before symbol initialization so DIA fallback does not require a separate `regsvr32` step in normal elevated runs.
+6. Startup creates `\symbols`, prepends the EXE directory and its non-cache subdirectories to the DbgHelp/DbgEng symbol path, then appends `SRV*\symbols*https://msdl.microsoft.com/download/symbols` so downloaded PDBs are managed under the runnable EXE bundle.
+7. TUI calls `SymLoadModuleExW` for each loaded kernel image.
+8. Kernel images named `ntoskrnl.exe` or `ntkrnl*` are loaded into DbgHelp under the `nt` module alias.
+9. After driver load and ABI verification, startup forces the `nt` kernel image symbols to materialize, which downloads the PDB into the configured symbol cache when it is not already present.
+10. If forced symbol loading still leaves the module at `SymNone`, the warning includes the loaded `dbghelp.dll` path and `symsrv.dll` load state for runtime diagnosis.
+11. `addr` uses `SymFromNameW`.
+12. Native address parsing accepts numbers, loaded symbols, WinDbg backtick-separated addresses, and simple `+`/`-` arithmetic before command dispatch. Arithmetic expressions are evaluated in user mode so `dt`, `d*`, `e*`, `vtop`, `u`, `uf`, `setfield`, and AI preview helpers do not hand expression text to DbgHelp as a raw symbol.
+13. Exact `dt` layouts use `SymGetTypeFromNameW` and `SymGetTypeInfo`.
+14. If exact `SymGetTypeFromNameW` lookup fails, the symbol engine reuses `SymEnumTypesW` exact matching to recover module base plus type id before falling back to DIA; the match accepts module-qualified names, leaf type names, and leading-underscore variants.
+15. Wildcard `dt` patterns such as `dt nt!*` use `SymEnumTypesW` against matching loaded kernel modules, apply the `*`/`?` filter in user mode, fall back to DIA UDT enumeration when DbgHelp cannot enumerate the type stream, and print list-only type matches.
+16. If `DbgHelp` fails to return a usable UDT layout, the user-mode symbol engine opens the loaded PDB with DIA, or asks DIA to resolve the PDB from the module image plus symbol path, and recovers field names, offsets, lengths, bit positions, and type names. DIA activation first uses registered COM, then falls back to creating `IDiaDataSource` directly from the staged `msdia*.dll` through `DllGetClassObject` so COM registration failures are reported separately from PDB/type lookup failures.
+17. `callbacks` resolves private callback structure fields from kernel PDBs, discovers object type objects from `ObTypeIndexTable`, discovers registry/process/thread/image-load callback roots by enumerating and validating candidate symbols, discovers minifilters from `fltmgr!FltGlobals.FrameList`, walks live list/table roots through the memory reader, and annotates callback routine addresses plus image-backed context addresses with loaded module ownership.
+18. `u` resolves an address or symbol with the native symbol engine, reads bounded code bytes through `IOCTL_KNDBG_READ_VIRTUAL`, and formats x64 instructions with the vendored Zydis decoder. This keeps local live-kernel disassembly independent from DbgEng memory callbacks, which can fail in local-kernel sessions. If the driver device is not open, `u` still falls back to the DbgEng disassembly path.
+19. `uf` resolves an address or symbol, reads a bounded code window through the driver, and uses the same Zydis decoder as `u` while stopping at common function terminal instructions. If the driver device is not open, it falls back to the DbgEng command path.
+20. `setfield` resolves a field offset in user mode, then sends a byte write to the driver.
+
+## Callback Scanner Flow
+
+1. Object callbacks: resolve `_OBJECT_TYPE.CallbackList`, discover object type objects from `ObTypeIndexTable`, read `_OBJECT_TYPE.Name` and table index, then walk each callback list. Human output carries the object type name/index alongside the `_OBJECT_TYPE` address. If the target nt PDB exposes `_OBJECT_TYPE` but omits the private callback item type, the scanner falls back to the guarded x64 object-callback item field layout, validates live list pointers, callback-entry pointers, operation masks, and callback routine pointers, and treats that as the normal public-symbol path unless validation fails.
+2. Registry callbacks: enumerate `CmpCallbackListHead` and nearby candidate symbols, validate the list-head shape, then walk callback context records. Public nt PDBs can omit `_CM_CALLBACK_CONTEXT_BLOCK`, so the scanner uses guarded x64 fallback layouts and accepts only records whose callback routine fields resolve inside loaded kernel images. Callback context values are module-annotated only when they also point into a loaded kernel image.
+3. Process callbacks: enumerate `PspCreateProcessNotifyRoutine` and nearby candidate symbols, validate table slots, decode fast references, then read callback routine blocks. Public nt PDBs can omit `_EX_CALLBACK_ROUTINE_BLOCK`, so the scanner uses the stable x64 function/context fallback layout for this block. The block context value is decoded as `notifyType` metadata and is not module-annotated.
+4. Thread callbacks: enumerate `PspCreateThreadNotifyRoutine` and nearby candidate symbols, validate table slots, decode fast references, then read callback routine blocks with the same stable x64 layout.
+5. Image-load callbacks: enumerate `PspLoadImageNotifyRoutine` and nearby candidate symbols, validate table slots, decode fast references, then read callback routine blocks with the same stable x64 layout.
+6. Minifilter callbacks: resolve `fltmgr!FltGlobals`, validate `FrameList`, walk `_FLTP_FRAME.RegisteredFilters`, decode `_FLT_FILTER` metadata, and enumerate `_FLT_OPERATION_REGISTRATION` entries plus filter-level routines.
+
+## DbgEng Flow
+
+1. `kdinit` or `backend dbgeng` initializes `DbgEngBackend`.
+2. The backend calls `DebugCreate(__uuidof(IDebugClient5))`.
+3. It installs an `IDebugOutputCallbacksWide` capture sink.
+4. It queries `IDebugControl4` and `IDebugSymbols3`.
+5. It mirrors the current symbol path into DbgEng.
+6. It attaches with `AttachKernelWide(DEBUG_ATTACH_LOCAL_KERNEL, nullptr)` for local mode or `AttachKernelWide(DEBUG_ATTACH_KERNEL_CONNECTION, connectionOptions)` for remote mode.
+7. Raw commands are executed with `IDebugControl4::ExecuteWide`.
+
+The DbgEng backend is intentionally isolated from the native memory backend. Native read/write operations still go through `KnLiveDbg.sys`, while DbgEng commands execute through the debugger engine.
+
+`u` and `uf` are deliberately wired as explicit commands instead of falling through the generic command router. Both stay on the driver-backed memory path and use Zydis for instruction decoding when the device is open; DbgEng is only a fallback when the native device is unavailable.
+
+Backend routing is mode-dependent:
+
+1. `auto` is the default. Implemented native commands stay on the driver/`DbgHelp` path, while registered DbgEng commands, `!extension` commands, and unknown dot meta-commands initialize or reuse DbgEng.
+2. `native` disables generic DbgEng fallback. Commands that require WinDbg parser, stop-state, extension, breakpoint, register, stack, source, trace, or exception semantics are reported as DbgEng-only instead of being executed.
+3. `dbgeng` routes most non-session commands through `IDebugControl4::ExecuteWide`. The TUI still intercepts shutdown/service commands, backend management, native callback scanning, and explicit `u`/`uf`.
+4. `kd ` is a raw DbgEng escape hatch independent of the selected backend mode.
+
+Interactive command execution is wrapped by a delayed progress watchdog. If a dispatched command runs longer than about one second without producing stdout/stderr, the watchdog writes elapsed-time status rows directly to the console with `WriteConsoleW`, outside stdout/stderr transcript capture, and stops once the command returns. After command output starts, progress rows are suppressed for that command so watchdog text does not split normal output lines. Console color scopes, captured stdout/stderr forwarding, and watchdog writes share a console-output lock so attribute restore cannot race with progress rendering.
+
+The interactive prompt uses a console key reader instead of plain line input. Tab completion is built from `CommandRegistry` plus context-specific subcommand tables for `callbacks`, `ai`, `backend`, `kdinit`, `probe`, `procctx`, `write`, `dt`, `vtop`, `s`, and native memory commands. Up/Down recalls recent non-empty commands with a bounded in-memory history; editing a recalled line turns it into the current draft, while blank Enter still repeats the last command for WinDbg-like ergonomics. The same command-family tables feed detailed help routing: `help `, ` help`, `callbacks help`, `ai help`, and `ai help ` all stop before command execution and print operator-focused usage for that command family. The top-level help explains state tags (`native`, `alias`, `dbgeng`), symbol/address conventions, `/process` versus `procctx`, count units, sparse-read `??` output, and write-context defaults so operators do not have to infer those rules from examples. The completer replaces the current token with a unique match or the longest common prefix; if the prefix is ambiguous, it prints candidates and redraws the current `knkd>` line.
+
+Human-readable native command output uses scoped console attributes for high-signal tokens such as callback kind tags, object types, modules, symbols, translated physical addresses, and type/field names. The color layer is applied only while writing to the console stream, so transcript capture and JSONL records remain plain text.
+
+## Build and Release Flow
+
+1. `tools\build.ps1` is the authoritative scripted build path for PE-stamped artifacts.
+2. It reads `.build\version-state.json`, falls back to the existing output PE version, and otherwise starts from `0.0.0`.
+3. Normal builds do not increment the version; `-BumpVersion` increments the patch component by one and saves the new version state after a successful build.
+4. The first bumped build is therefore `0.0.1`.
+5. The script writes `.build\generated\KnLiveDbgVersion.h`, then MSBuild compiles VERSIONINFO resources into `KnLiveDbg.exe`, `KnLiveDbg.sys`, and `KnLiveDbgProbe.sys` before the WDK TestSign step.
+6. After a successful build, the script verifies the stamped PE versions, verifies driver signatures, and stages Debugging Tools runtime DLLs beside the EXE.
+7. `tools\release.ps1` runs a version-bumped build by default and creates `release\KnLiveDbg---x64.zip` from the output directory. `-NoVersionBump` keeps the current version for ad hoc packages.
+8. The release zip includes the runnable EXE/SYS files, staged runtime dependencies, PDB/CER/CAT files when present, README/runtime manifest metadata, and `kn-live-dbg-version.json`.
+
+## AI Provider Flow
+
+1. `AiProviderRuntime` lives entirely in user mode.
+2. Provider selection defaults to `.env` plus environment variables and can be changed for the current TUI session with `ai config provider`, `ai config policy`, `ai config model`, `ai config base-url`, and `ai config effort`.
+3. `openai-codex-cli` executes `codex exec` as an external process and captures stdout/stderr.
+4. `openai-codex-subscription` reads Codex OAuth credentials from environment variables or auth files and calls the Codex Responses endpoint.
+5. `deepseek` and `openrouter` use OpenAI-compatible chat-completions requests over WinHTTP.
+6. `.env` is loaded only from the executable directory; real environment variables override `.env` values.
+7. AI requests receive only curated session context and the operator prompt. The model cannot call memory IOCTLs or execute generated debugger commands directly.
+8. `ai ` uses a strict `kn-live-dbg.ai-capability-plan.v1` tool-router JSON schema. The provider chooses from local read-only tools such as `process.find`, `process.describe`, `type.describe`, `callbacks.list`, or `assistant.answer`; the C++ executor validates and runs the selected tools locally.
+9. `ai config test` performs a real provider/model round-trip with a small marker prompt and reports transport status, HTTP status when available, elapsed time, and marker match result.
+10. `KNLIVEDBG_AI_REMOTE_POLICY=local-only` or `ai config policy local-only` blocks HTTP-backed providers (`openai-codex-subscription`, `deepseek`, and `openrouter`) for private sessions.
+11. `ai plan` requires a strict JSON command proposal object, parses it into in-memory plan state, and shows numbered commands with purpose and risk notes.
+12. `ai plan` validates each proposed command before storing it: empty commands, nested `ai`, shutdown/unload commands, bare `kd`, overlong commands, and unknown non-DbgEng commands are rejected. Write-like proposals are forced to require explicit confirmation.
+13. `ai run` routes approved read-only plan commands back through the normal TUI command dispatcher, so backend mode, DbgEng routing, and native command handling stay consistent.
+14. Write-like commands, shutdown commands, unload commands, and nested `ai` commands are blocked from `ai run`.
+15. `ai analyze callbacks`, `ai explain dt`, and `ai annotate u|uf` execute read-only evidence commands through the same dispatcher, then send bounded stdout/stderr evidence to the selected model. Callback analysis uses `callbacks [module]` output.
+16. `ai diagnose` uses current session context plus an operator note to explain symbol, type-layout, DbgEng, or setup failures.
+17. `ai playbook` creates deterministic read-only command plans for callback, minifilter, object-callback, address, and suspect-driver investigations. `run` still goes through the guarded `ai run` path.
+18. `ai write confirm` is the explicit operator confirmation path for planned write-like commands. The preflight path builds backup/read-current, small-range restore, translation, and verification commands for recognized write forms, then prints a deterministic before/after diff of verification stdout/stderr after the write.
+19. `ai transcript` captures AI events and command stdout/stderr as JSONL after it is enabled.
+20. `ai transcript max ` rotates long transcript files, and `ai transcript redact ` controls stdout/stderr redaction for long hex addresses and `sk-...` style tokens.
+21. `ai audit ` writes a separate JSONL record for every write-like command that executes through the normal dispatcher.
+20. Transcript command records include origin, backend mode, command class, write-like status, stdout/stderr character counts, deterministic output summaries, raw stdout/stderr, and keep-running state.
+21. `ai report` writes a Markdown report with session context, provider status, transcript settings, write-audit path, parsed plan, and raw AI plan response.
+22. `backend dbgeng` does not swallow `ai`; the TUI handles it before raw DbgEng command routing.
+
+## Hardening Backlog
+
+No large hardening backlog item is currently left open in this document. New work should be added here once it is scoped.
+
+Completed hardening items:
+
+1. JSONL write-command audit logging is implemented with `ai audit `.
+2. Transcript rotation and redaction controls are implemented with `ai transcript max` and `ai transcript redact`.
+3. Transcript command records now include command-class labels such as `memory-read`, `physical-write`, `type`, `callbacks`, `disassembly`, `symbols`, `dbgeng`, and `session`.
+4. Callback module filtering is implemented with `callbacks [scope] [module]`.
+5. Write verification before/after diff rendering is implemented for confirmed AI write commands.
+6. AI plan command schema validation is implemented before plan storage.
+7. Local-only AI provider policy is implemented with `ai config policy local-only` and `KNLIVEDBG_AI_REMOTE_POLICY=local-only`.
+8. Single-controller ownership is implemented in the driver and reported by `drvstatus`.
+9. Process DTB resolution is implemented with PDB-resolved EPROCESS offsets and `procctx`/`vtop /process`; native `e*` writes use System (`pid 4`) as the default context.
+10. LA57 detection and PML5E reporting are implemented in the page-table walker.
+11. DIA fallback is implemented for UDT field metadata when `DbgHelp` fails.
+12. `KnLiveDbgProbe.sys` provides a positive-control contiguous virtual and physical test buffer.
+13. Remote KD attach is available through `kdinit /remote `.
+14. AI command proposal validation is versioned as `kn-live-dbg.ai-plan.v2` and rejects missing purpose metadata, command chaining, session mutation, raw `kd` blocked-command wrappers, and unsupported backend expectations.
+15. AI command evidence and transcripts include deterministic output summaries before raw stdout/stderr.
+
+```
+
+`docs/WINDBG_COMMAND_COVERAGE.md`:
+
+```md
+# WinDbg Command Coverage
+
+This project follows the Microsoft WinDbg command reference as the command registry baseline.
+
+Reference pages:
+
+- `Commands`: https://learn.microsoft.com/en-us/windows-hardware/drivers/debuggercmds/commands
+- `Local Kernel-Mode Debugging`: https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/performing-local-kernel-debugging
+
+## Backend Rule
+
+KnLiveDbg currently has a native live-memory backend, not a KD transport backend. This means commands fall into three groups:
+
+1. Native
+ - Can be implemented using the current driver, `DbgHelp`, and local system APIs.
+ - Examples: virtual/physical memory display/write/search/compare/fill/move, VA-to-PA translation, module list, symbol lookup, type display, explicit disassembly commands.
+
+2. DbgEng-routed
+ - The command exists in the WinDbg command surface, but it needs debugger-engine parser or stop-state semantics.
+ - In `auto` or `dbgeng` backend mode, these commands are routed to DbgEng.
+ - Examples: execution control, breakpoints, stack walking, registers, source commands, exception commands, I/O port commands.
+
+3. Extension commands
+ - Any `!` command is recognized as an extension command.
+ - In `auto` or `dbgeng` backend mode, extension commands are routed to DbgEng.
+
+4. Meta-commands
+ - Any unknown `.` is recognized as a WinDbg meta-command.
+ - The native backend implements `.sympath`, `.sympath+`, and `.reload`; other meta-commands are routed to DbgEng in `auto` or `dbgeng` mode.
+
+## Backend Commands
+
+```text
+backend [auto|native|dbgeng]
+kdinit [/local [connect-options]|/remote ]
+kd
+kddetach
+home|dashboard
+probe [status|load [sys-path]|info|reset|unload]
+ai [status|providers|provider|policy|model|base-url|effort|auth|preview|ask|plan|analyze|explain|annotate|diagnose|playbook|show|run|write|transcript|audit|report]
+```
+
+`auto` is the default. Native commands stay on the custom driver backend, while DbgEng-routed commands and extension/meta commands are sent to DbgEng when possible.
+
+Backend mode differences:
+
+| Mode | Native handlers | DbgEng fallback | Typical use |
+| --- | --- | --- | --- |
+| `auto` | Enabled for implemented live-memory commands. | Enabled for DbgEng-routed commands, `!extension` commands, and unknown `.meta` commands. | Normal mixed operation. |
+| `native` | Enabled. | Disabled except for explicit raw escapes such as `kd`. | Verifying driver-backed behavior without accidental raw WinDbg execution. |
+| `dbgeng` | Only session/TUI exceptions run before the raw DbgEng catch-all. | Enabled for most commands through `IDebugControl4::ExecuteWide`. | WinDbg parser, stop-state, extension, breakpoint, register, stack, source, trace, and exception commands. |
+
+The `dbgeng` catch-all intentionally excludes `q`, `qq`, `qd`, `quit`, `exit`, `unload`, `drvstatus`, `home`, `dashboard`, `probe`, `procctx`, `callbacks`, native `!dml_proc`/physical bang commands, and `ai` so shutdown, service control, status/dashboard, probe control, native process context, callback/process scanning, native physical memory, and AI provider control stay under the TUI. The explicit `u`/`uf` handler also runs before that catch-all. `kd ` always executes a raw DbgEng command regardless of the selected backend mode.
+
+## Native Commands
+
+```text
+?, ||, ||s, |, .sympath, .sympath+, .reload
+lm, ld, ln, x
+d, da, db, dc, dd, dD, df, dp, dq, du, dw, dW, dyb, dyd
+dda, ddp, ddu, dpa, dpp, dpu, dqa, dqp, dqu
+dds, dps, dqs
+vtop, procctx
+phys, pdb, pdw, pdd, pdq, !db, !dw, !dd, !dq
+peb, pew, ped, peq, !eb, !ew, !ed, !eq
+dt, dtx
+callbacks
+!dml_proc [pid]
+help , help
+callbacks help
+ai help, ai help
+help syntax notes for native/dbgeng state tags, count units, /process/procctx, nt! kernel aliases, sparse ?? reads, and write context
+u, uf
+ai
+e, ea, eb, ed, eD, ef, ep, eq, eu, ew, eza, ezu
+c, f, fp, m, s
+n, sq
+version, vertarget, vercommand, drvstatus, home, dashboard, probe
+q, qq, qd
+```
+
+## DbgEng-Routed Commands
+
+```text
+$<, $><, $$<, $$><, $$>a<
+??, #, |s, ~, ~e, ~f, ~u, ~n, ~m, ~s
+a, ad, ah, al, as
+ba, bc, bd, be, bl, bp, bu, bm, br, bs, bsc
+dg, dl, dv, dx
+g, gc, gh, gn, gN, gu
+ib, iw, id, ob, ow, od
+j
+k, kb, kc, kp, kP, kv
+l+, l-, ls, lsa, lsc, lse, lsf, lsf-, lsp
+p, pa, pc, pct, ph, pt
+r, rdmsr, rm, wrmsr
+so, ss, sx, sxd, sxe, sxi, sxn, sxr, sx-
+t, ta, tb, tc, tct, th, tt, wt
+up, ur, ux
+z
+```
+
+## Implementation Notes
+
+1. `x nt!*Mask*` is handled by splitting `module!symbol-mask` and enumerating the matching loaded kernel module. The `nt` module alias maps to `ntoskrnl.exe`/`ntkrnl*` and is loaded into DbgHelp under the `nt` name.
+2. `ln` resolves the input as either a number or symbol, then asks `DbgHelp` for the nearest symbol.
+3. `dps`, `dqs`, and `dds` annotate pointer-like values with nearest symbols when available.
+4. `dpa`, `dpu`, `dqa`, and `dqu` read a pointer value first, then display the referenced string.
+5. Write commands start enabled per handle; `write off` disables the driver gate and `write on` re-enables it.
+6. Single transfer size is capped by `KNDBG_MAX_TRANSFER_SIZE`.
+7. The build helper copies the pinned `vendor\debugging-tools\x64` Debugging Tools runtime DLLs beside the EXE, with Windows Kits as a fallback; this includes `dbghelp.dll`, `dbgeng.dll`, `dbgcore.dll`, `DbgModel.dll`, `msdia140.dll`, `symsrv.dll`, `srcsrv.dll`, and `symsrv.yes`, and is required for reliable symbol-server downloads and DbgEng runtime loading when the tool is moved to another directory.
+8. The sync script, build script, and EXE startup path create `symsrv.yes` when `symsrv.dll` is present but the consent marker is missing.
+9. Startup registers staged `msdia140.dll` with `DllRegisterServer` before symbol initialization so DIA fallback works without a separate `regsvr32` step in normal elevated runs. If COM registration is unavailable, DIA fallback can still create `IDiaDataSource` directly from the staged `msdia*.dll` through `DllGetClassObject`.
+10. Startup creates `\symbols` and uses `SRV*\symbols*https://msdl.microsoft.com/download/symbols` as the default Microsoft symbol server path. It then forces the `nt` kernel image symbols to materialize after the driver is loaded and the ABI is verified, so the kernel PDB is downloaded/loaded into the EXE-local symbol cache before the first type-heavy command. If DbgHelp still reports `SymNone`, the warning includes loaded `dbghelp.dll` and `symsrv.dll` diagnostics.
+11. Native `dt` supports layout-only output, value output when an address is supplied, recursive UDT expansion with `-rN`, verbose diagnostics with `-v`, bare output with `-b`, field filters, and wildcard type enumeration such as `dt nt!*EPROCESS*` or `dt nt!*`. Address arguments are resolved by the native address parser and accept simple `+`/`-` arithmetic before type dumping, for example `dt nt!_PS_PROTECTION 0xffffb40c8c1540c0+5fa`. Exact type layouts use `SymGetTypeFromNameW`, then fall back to `SymEnumTypesW` exact matching to recover module base plus type id before DIA is tried; the fallback accepts module-qualified names, leaf type names, and leading-underscore variants. Wildcard patterns use `SymEnumTypesW` with user-mode `*`/`?` filtering and are list-only, with DIA UDT enumeration as fallback when DbgHelp cannot enumerate the type stream. When `DbgHelp` cannot provide a usable UDT layout, DIA fallback recovers field offsets, lengths, bit positions, and type names from the loaded PDB or from a DIA-resolved PDB using the module image plus symbol path. DIA fallback reports whether activation failed, module matching failed, or the specific type was absent from the PDB.
+12. `vtop` translates a virtual address to a physical address using the current CR3, a supplied directory-table base, or `/process `. The output reports whether the walk used PML4 or PML5, prints the physical address of each page-table entry that was read, and includes the leaf page-table entry physical address plus writable state.
+13. `procctx ` stores a process DTB context. `d*` can use `/process ` or the stored context for process-aware user VA physical reads. Native `e*` virtual writes default to the System process context (`pid 4`) and can use `/process ` for one-shot process-aware user VA writes. Address-only `e*` enters a WinDbg-style one-line edit prompt and cancels on an empty line, `.`, `q`, or `quit`. If the leaf PTE/PDE/PDPTE is read-only, `e*` temporarily sets its write bit for the edit, flushes the VA, writes kernel VAs through the original VA, keeps translated user VAs on the physical-write path, restores the write bit afterward, and flushes again. Display reads are sparse, so unreadable bytes stay visible as `??` instead of aborting the whole dump.
+14. `phys`, `pdb`, `pdw`, `pdd`, `pdq`, `!db`, `!dw`, `!dd`, and `!dq` read physical memory and use the same sparse `??` display for unreadable ranges; `peb`, `pew`, `ped`, `peq`, `!eb`, `!ew`, `!ed`, and `!eq` write physical memory through the same write gate as virtual writes. Address-only physical enter commands prompt with the current physical value, like the virtual `e*` edit flow. The `!d*` and `!e*` physical-memory commands are intercepted by the TUI before generic DbgEng extension routing.
+15. `probe` manages `KnLiveDbgProbe.sys` and prints a deterministic positive-control virtual/physical test buffer.
+16. `!dml_proc` is a native process listing command that does not require a DbgEng current process/thread. It resolves PID 4, reads `_EPROCESS.ActiveProcessLinks`, and walks the active process list from live kernel memory. `!dml_proc ` accepts a decimal PID and prints only matching process records.
+17. `callbacks [all|object|registry|process|thread|imageload|minifilter] [module]` and `callbacks [scope] /module ` parse kernel PDB type layouts and live memory to list object-manager filters, registry callbacks, process/thread/image-load callbacks, and minifilter callbacks with module, symbol, altitude, context, root, and object/filter address annotations. The optional module filter is case-insensitive and is applied after scanning; it keeps records whose pre/function or post callback belongs to the requested module name or module stem. Callback parsing intentionally accepts only canonical scope names, not short aliases.
+18. Object callback scanning discovers `_OBJECT_TYPE` objects from `ObTypeIndexTable` before walking each `_OBJECT_TYPE.CallbackList`, with documented type globals used only as fallback. Human output includes the object type name and index when available, so Process, Thread, Desktop, and other object-manager surfaces are explicit instead of requiring the operator to interpret the `_OBJECT_TYPE` address. Public nt PDBs can omit `_CALLBACK_ENTRY_ITEM`; when that happens, the scanner uses a guarded x64 object-callback item layout fallback, validates live list links, callback-entry pointers, operation masks, and callback routine pointers, and treats validated fallback records as normal output. Warnings are reserved for partial PDB layouts, structure drift, or failed live validation.
+19. Registry, process, thread, and image-load callback scanning enumerate candidate root symbols and validate the expected list/table shape before emitting records. Public nt PDBs often omit `_CM_CALLBACK_CONTEXT_BLOCK` and `_EX_CALLBACK_ROUTINE_BLOCK`; those scanners use guarded x64 fallback layouts, validate registry callback routines against loaded kernel image ranges, annotate callback routine addresses with module and symbol ownership, print process notify block context values as decoded `notifyType` metadata instead of treating them as module pointers, and preserve image-load notify block/raw slot values for follow-up inspection.
+20. Minifilter scanning discovers `fltmgr!FltGlobals`, validates the frame-list root, walks frames and registered filters, and reports operation, unload, instance, name provider, KTM, section, and volume-mount callbacks. Non-exact globals candidates must produce concrete callback records before they are accepted.
+21. `u [instruction-count]` resolves the address natively, including `+`/`-` address arithmetic, reads up to `instruction-count * 16` code bytes through the driver, disassembles them with vendored Zydis, caps explicit counts at 256 instructions, and remembers the next offset for a following bare `u`. If the driver device is not open, the command falls back to the DbgEng disassembly path.
+22. `uf [max-instructions]` resolves the address natively, reads a bounded code window through the driver, disassembles with vendored Zydis, and stops at common function terminal instructions such as `ret`, `iret`, `sysret`, `sysexit`, `hlt`, `ud2`, or `int3`. If the driver device is not open, the command falls back to DbgEng.
+23. `kdinit /remote ` attaches DbgEng with `DEBUG_ATTACH_KERNEL_CONNECTION`; plain `kdinit` and `kdinit /local` use local-kernel attach.
+24. `ai plan ` stores model-proposed commands in a parsed in-memory `kn-live-dbg.ai-plan.v2` plan after schema validation rejects empty commands, missing purpose metadata, unsupported backend expectations, command chaining, multiline commands, nested-AI, shutdown/unload, backend/session mutation, probe service control, bare `kd`, raw `kd` blocked-command wrappers, overlong commands, and unknown non-DbgEng commands. `ai run ` executes only non-write, non-shutdown planned commands through the normal TUI dispatcher.
+25. `ai analyze callbacks`, `ai explain dt`, `ai annotate u|uf`, and `ai diagnose` provide evidence-backed AI assistance on top of implemented native command output. Callback analysis uses normal `callbacks [module]` evidence. Evidence prompts include deterministic stdout/stderr summaries before raw output.
+26. `ai playbook ` creates repeatable read-only command plans with dry-run output and optional guarded execution.
+27. `ai write confirm` is required to dispatch planned write-like commands and performs backup/read-current plus verification preflight when the write form is recognized. It also renders a deterministic before/after diff for verification stdout/stderr. `ai transcript ` captures AI events and command output as JSONL, including output summaries and raw stdout/stderr. `ai transcript max` rotates long transcript files, `ai transcript redact` redacts captured stdout/stderr, `ai audit ` captures write-command JSONL audit records, and `ai report ` exports the current AI session summary.
+28. Interactive command dispatch has a delayed progress watchdog. Silent commands that run longer than about one second emit colored elapsed-time status rows directly to the console with `WriteConsoleW`, outside transcript stdout/stderr capture, and print a final elapsed row only if the watchdog became visible. Once stdout/stderr output starts, progress rows are suppressed for that command to avoid interleaving with long listings such as callback scans.
+29. Human-readable console output colorizes high-signal tokens for scanning: callback kind tags, object type names, modules, symbols, translated physical addresses, type/field names, and dump line addresses. The same output remains plain text in stdout capture, AI transcript JSONL, and write-audit JSONL records.
+30. The `knkd>` prompt supports Tab completion for registered commands and context-aware subcommands/options, including callback scopes, AI subcommands, backend modes, probe/procctx/write actions, `dt` options, `vtop` context options, and `/process` memory command options. Ambiguous prefixes print matching candidates and redraw the current line without dispatching a command. Up/Down recalls recent non-empty commands from the current session history.
+31. `tools\build.ps1` stamps `KnLiveDbg.exe`, `KnLiveDbg.sys`, and `KnLiveDbgProbe.sys` with the current PE version without incrementing it by default. `-BumpVersion` increments the patch component, with `0.0.0` as the local baseline and `0.0.1` as the first bumped build. `tools\release.ps1` packages the built PE files, runtime DLLs, symbols/certs when present, and `kn-live-dbg-version.json` into `release\KnLiveDbg---x64.zip`, using a version-bumped build unless `-NoVersionBump` is supplied.
+
+```
+
+`driver/Driver.cpp`:
+
+```cpp
+#include
+#include
+#include
+#include "../shared/KnLiveDbgIoctl.h"
+
+extern "C"
+NTKERNELAPI
+NTSTATUS
+PsLookupProcessByProcessId(
+ HANDLE ProcessId,
+ PEPROCESS* Process);
+
+#if defined(_M_X64)
+#pragma intrinsic(__readcr3)
+#pragma intrinsic(__readcr4)
+#pragma intrinsic(__invlpg)
+#endif
+
+typedef struct _KNDBG_FILE_CONTEXT
+{
+ ULONG OwnerPid;
+ BOOLEAN WriteEnabled;
+ BOOLEAN OwnsController;
+} KNDBG_FILE_CONTEXT, *PKNDBG_FILE_CONTEXT;
+
+static DRIVER_UNLOAD KnDbgUnload;
+static DRIVER_DISPATCH KnDbgCreateClose;
+static DRIVER_DISPATCH KnDbgDeviceControl;
+static DRIVER_DISPATCH KnDbgNotSupportedDispatch;
+
+static const ULONGLONG KNDBG_PAGE_OFFSET_MASK = 0xfffull;
+static const ULONGLONG KNDBG_PTE_PRESENT = 0x1ull;
+static const ULONGLONG KNDBG_PTE_LARGE_PAGE = 0x80ull;
+static const ULONGLONG KNDBG_PTE_4K_BASE_MASK = 0x000ffffffffff000ull;
+static const ULONGLONG KNDBG_PTE_2MB_BASE_MASK = 0x000fffffffe00000ull;
+static const ULONGLONG KNDBG_PTE_1GB_BASE_MASK = 0x000fffffc0000000ull;
+static const ULONGLONG KNDBG_2MB_PAGE_SIZE = 0x200000ull;
+static const ULONGLONG KNDBG_1GB_PAGE_SIZE = 0x40000000ull;
+static const ULONGLONG KNDBG_CR4_LA57 = 0x1000ull;
+static const GUID KNDBG_DEVICE_CLASS_GUID =
+{
+ 0x4c2d7102,
+ 0xd0b5,
+ 0x4e3f,
+ {0x95, 0x92, 0x6f, 0x9f, 0xb0, 0x2d, 0x46, 0x78}
+};
+
+static FAST_MUTEX g_KnDbgOwnerLock;
+static ULONG g_KnDbgOwnerPid = 0;
+static ULONG g_KnDbgOwnerOpenCount = 0;
+
+typedef struct _KNDBG_TLB_FLUSH_CONTEXT
+{
+ ULONGLONG StartAddress;
+ SIZE_T PageCount;
+} KNDBG_TLB_FLUSH_CONTEXT, *PKNDBG_TLB_FLUSH_CONTEXT;
+
+static NTSTATUS KnDbgCompleteIrp(PIRP Irp, NTSTATUS Status, ULONG_PTR Information)
+{
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = Information;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ return Status;
+}
+
+static bool KnDbgCheckInputHeader(PVOID Buffer, ULONG InputLength, ULONG MinimumLength)
+{
+ bool ok = false;
+
+ do
+ {
+ if (Buffer == nullptr)
+ {
+ break;
+ }
+
+ if (InputLength < sizeof(KNDBG_UINT32))
+ {
+ break;
+ }
+
+ KNDBG_UINT32 size = *(reinterpret_cast(Buffer));
+ if (size < MinimumLength)
+ {
+ break;
+ }
+
+ if (InputLength < size)
+ {
+ break;
+ }
+
+ ok = true;
+ } while (false);
+
+ return ok;
+}
+
+static bool KnDbgRangeOverflows(ULONGLONG Address, SIZE_T Length)
+{
+ bool overflows = true;
+
+ do
+ {
+ if (Length == 0)
+ {
+ break;
+ }
+
+ ULONGLONG lastOffset = static_cast(Length - 1);
+ overflows = Address > (~0ull - lastOffset);
+ } while (false);
+
+ return overflows;
+}
+
+static NTSTATUS KnDbgReadVirtualAddress(ULONGLONG Address, PVOID Output, SIZE_T Length, PSIZE_T BytesCopied)
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+ do
+ {
+ if (Output == nullptr || BytesCopied == nullptr)
+ {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (Length == 0 || Length > KNDBG_MAX_TRANSFER_SIZE)
+ {
+ status = STATUS_INVALID_BUFFER_SIZE;
+ break;
+ }
+
+ if (KnDbgRangeOverflows(Address, Length))
+ {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ MM_COPY_ADDRESS source = {};
+ source.VirtualAddress = reinterpret_cast(static_cast(Address));
+
+ *BytesCopied = 0;
+ status = MmCopyMemory(Output, source, Length, MM_COPY_MEMORY_VIRTUAL, BytesCopied);
+ } while (false);
+
+ return status;
+}
+
+static NTSTATUS KnDbgWriteVirtualAddress(ULONGLONG Address, const VOID* Input, SIZE_T Length, PSIZE_T BytesCopied)
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+ do
+ {
+ if (Input == nullptr || BytesCopied == nullptr)
+ {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (Length == 0 || Length > KNDBG_MAX_TRANSFER_SIZE)
+ {
+ status = STATUS_INVALID_BUFFER_SIZE;
+ break;
+ }
+
+ if (KnDbgRangeOverflows(Address, Length))
+ {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ *BytesCopied = 0;
+
+ __try
+ {
+ RtlCopyMemory(reinterpret_cast(static_cast(Address)), Input, Length);
+ *BytesCopied = Length;
+ status = STATUS_SUCCESS;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ status = GetExceptionCode();
+ }
+ } while (false);
+
+ return status;
+}
+
+static SIZE_T KnDbgMinSize(SIZE_T Left, SIZE_T Right)
+{
+ return Left < Right ? Left : Right;
+}
+
+static bool KnDbgIsLa57Active()
+{
+ bool active = false;
+
+ do
+ {
+#if defined(_M_X64)
+ active = (__readcr4() & KNDBG_CR4_LA57) != 0;
+#endif
+ } while (false);
+
+ return active;
+}
+
+static bool KnDbgIsCanonicalAddress(ULONGLONG VirtualAddress, bool La57Active)
+{
+ bool canonical = false;
+
+ do
+ {
+ ULONG signBit = La57Active ? 56u : 47u;
+ ULONG highShift = La57Active ? 57u : 48u;
+ ULONGLONG high = VirtualAddress >> highShift;
+ ULONGLONG sign = (VirtualAddress >> signBit) & 0x1ull;
+ ULONGLONG expectedHigh = La57Active ? 0x7full : 0xffffull;
+
+ if (sign == 0)
+ {
+ canonical = high == 0;
+ }
+ else
+ {
+ canonical = high == expectedHigh;
+ }
+ } while (false);
+
+ return canonical;
+}
+
+static ULONG_PTR KnDbgFlushVirtualRangeWorker(ULONG_PTR Context)
+{
+ PKNDBG_TLB_FLUSH_CONTEXT flushContext = reinterpret_cast(Context);
+
+ do
+ {
+ if (flushContext == nullptr)
+ {
+ break;
+ }
+
+#if defined(_M_X64)
+ for (SIZE_T index = 0; index < flushContext->PageCount; ++index)
+ {
+ ULONGLONG address = flushContext->StartAddress + index * PAGE_SIZE;
+ __invlpg(reinterpret_cast(static_cast(address)));
+ }
+#endif
+ } while (false);
+
+ return 0;
+}
+
+static NTSTATUS KnDbgFlushVirtualRange(ULONGLONG VirtualAddress, SIZE_T Length)
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+ do
+ {
+ if (Length == 0 || Length > KNDBG_MAX_TRANSFER_SIZE)
+ {
+ status = STATUS_INVALID_BUFFER_SIZE;
+ break;
+ }
+
+ if (KnDbgRangeOverflows(VirtualAddress, Length))
+ {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ bool la57Active = KnDbgIsLa57Active();
+ if (!KnDbgIsCanonicalAddress(VirtualAddress, la57Active))
+ {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ ULONGLONG endAddress = VirtualAddress + Length - 1;
+ if (!KnDbgIsCanonicalAddress(endAddress, la57Active))
+ {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ ULONGLONG startPage = VirtualAddress & ~KNDBG_PAGE_OFFSET_MASK;
+ ULONGLONG endPage = endAddress & ~KNDBG_PAGE_OFFSET_MASK;
+ SIZE_T pageCount = static_cast(((endPage - startPage) / PAGE_SIZE) + 1);
+ KNDBG_TLB_FLUSH_CONTEXT context = {};
+ context.StartAddress = startPage;
+ context.PageCount = pageCount;
+
+#if defined(_M_X64)
+ KeIpiGenericCall(KnDbgFlushVirtualRangeWorker, reinterpret_cast(&context));
+#endif
+ status = STATUS_SUCCESS;
+ } while (false);
+
+ return status;
+}
+
+static NTSTATUS KnDbgAcquireController(PKNDBG_FILE_CONTEXT FileContext)
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+ do
+ {
+ if (FileContext == nullptr)
+ {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ ULONG currentPid = HandleToULong(PsGetCurrentProcessId());
+ ExAcquireFastMutex(&g_KnDbgOwnerLock);
+
+ if (g_KnDbgOwnerPid != 0 && g_KnDbgOwnerPid != currentPid)
+ {
+ status = STATUS_DEVICE_BUSY;
+ }
+ else
+ {
+ g_KnDbgOwnerPid = currentPid;
+ ++g_KnDbgOwnerOpenCount;
+ FileContext->OwnerPid = currentPid;
+ FileContext->OwnsController = TRUE;
+ status = STATUS_SUCCESS;
+ }
+
+ ExReleaseFastMutex(&g_KnDbgOwnerLock);
+ } while (false);
+
+ return status;
+}
+
+static void KnDbgReleaseController(PKNDBG_FILE_CONTEXT FileContext)
+{
+ do
+ {
+ if (FileContext == nullptr || FileContext->OwnsController == FALSE)
+ {
+ break;
+ }
+
+ ExAcquireFastMutex(&g_KnDbgOwnerLock);
+
+ if (g_KnDbgOwnerPid == FileContext->OwnerPid)
+ {
+ if (g_KnDbgOwnerOpenCount > 0)
+ {
+ --g_KnDbgOwnerOpenCount;
+ }
+
+ if (g_KnDbgOwnerOpenCount == 0)
+ {
+ g_KnDbgOwnerPid = 0;
+ }
+ }
+
+ ExReleaseFastMutex(&g_KnDbgOwnerLock);
+ FileContext->OwnsController = FALSE;
+ } while (false);
+}
+
+static NTSTATUS KnDbgReadPhysicalAddress(ULONGLONG PhysicalAddress, PVOID Output, SIZE_T Length, PSIZE_T BytesCopied)
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+ do
+ {
+ if (Output == nullptr || BytesCopied == nullptr)
+ {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (Length == 0 || Length > KNDBG_MAX_TRANSFER_SIZE)
+ {
+ status = STATUS_INVALID_BUFFER_SIZE;
+ break;
+ }
+
+ if (KnDbgRangeOverflows(PhysicalAddress, Length))
+ {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ MM_COPY_ADDRESS source = {};
+ source.PhysicalAddress.QuadPart = static_cast(PhysicalAddress);
+
+ *BytesCopied = 0;
+ status = MmCopyMemory(Output, source, Length, MM_COPY_MEMORY_PHYSICAL, BytesCopied);
+ } while (false);
+
+ return status;
+}
+
+static NTSTATUS KnDbgReadPhysicalU64(ULONGLONG PhysicalAddress, PULONGLONG Value)
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+ do
+ {
+ if (Value == nullptr)
+ {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ ULONGLONG localValue = 0;
+ SIZE_T bytesCopied = 0;
+ status = KnDbgReadPhysicalAddress(PhysicalAddress, &localValue, sizeof(localValue), &bytesCopied);
+ if (!NT_SUCCESS(status))
+ {
+ break;
+ }
+
+ if (bytesCopied != sizeof(localValue))
+ {
+ status = STATUS_PARTIAL_COPY;
+ break;
+ }
+
+ *Value = localValue;
+ status = STATUS_SUCCESS;
+ } while (false);
+
+ return status;
+}
+
+static NTSTATUS KnDbgWritePhysicalAddress(ULONGLONG PhysicalAddress, const VOID* Input, SIZE_T Length, PSIZE_T BytesCopied)
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+ do
+ {
+ if (Input == nullptr || BytesCopied == nullptr)
+ {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (Length == 0 || Length > KNDBG_MAX_TRANSFER_SIZE)
+ {
+ status = STATUS_INVALID_BUFFER_SIZE;
+ break;
+ }
+
+ if (KnDbgRangeOverflows(PhysicalAddress, Length))
+ {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ *BytesCopied = 0;
+ SIZE_T offset = 0;
+
+ while (offset < Length)
+ {
+ ULONGLONG currentPhysical = PhysicalAddress + offset;
+ ULONGLONG alignedPhysical = currentPhysical & ~KNDBG_PAGE_OFFSET_MASK;
+ SIZE_T pageOffset = static_cast(currentPhysical & KNDBG_PAGE_OFFSET_MASK);
+ SIZE_T chunk = KnDbgMinSize(Length - offset, static_cast(PAGE_SIZE) - pageOffset);
+ SIZE_T mapLength = pageOffset + chunk;
+
+ PHYSICAL_ADDRESS mapAddress = {};
+ mapAddress.QuadPart = static_cast(alignedPhysical);
+
+ PVOID mapped = MmMapIoSpaceEx(mapAddress, mapLength, PAGE_READWRITE);
+ bool mappedWithIoSpace = true;
+ if (mapped == nullptr)
+ {
+ PHYSICAL_ADDRESS exactAddress = {};
+ exactAddress.QuadPart = static_cast(currentPhysical);
+ mapped = MmGetVirtualForPhysical(exactAddress);
+ mappedWithIoSpace = false;
+ }
+
+ if (mapped == nullptr)
+ {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ __try
+ {
+ PUCHAR destination = mappedWithIoSpace ?
+ reinterpret_cast(mapped) + pageOffset :
+ reinterpret_cast(mapped);
+ RtlCopyMemory(destination, reinterpret_cast(Input) + offset, chunk);
+ KeMemoryBarrier();
+ status = STATUS_SUCCESS;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ status = GetExceptionCode();
+ }
+
+ if (mappedWithIoSpace)
+ {
+ MmUnmapIoSpace(mapped, mapLength);
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ break;
+ }
+
+ offset += chunk;
+ *BytesCopied = offset;
+ }
+ } while (false);
+
+ return status;
+}
+
+static NTSTATUS KnDbgTranslateVirtualAddress(
+ ULONGLONG DirectoryTableBase,
+ ULONGLONG VirtualAddress,
+ ULONG Length,
+ KNDBG_TRANSLATE_VIRTUAL_RESPONSE* Response)
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+ do
+ {
+ if (Response == nullptr)
+ {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (Length == 0 || Length > KNDBG_MAX_TRANSFER_SIZE)
+ {
+ status = STATUS_INVALID_BUFFER_SIZE;
+ break;
+ }
+
+ bool la57Active = KnDbgIsLa57Active();
+ if (!KnDbgIsCanonicalAddress(VirtualAddress, la57Active))
+ {
+ status = STATUS_ACCESS_VIOLATION;
+ break;
+ }
+
+ ULONG flags = 0;
+ ULONGLONG directoryTableBase = DirectoryTableBase;
+ if (directoryTableBase == 0)
+ {
+#if defined(_M_X64)
+ directoryTableBase = __readcr3();
+ flags |= KNDBG_TRANSLATE_FLAG_CURRENT_CR3;
+#else
+ status = STATUS_NOT_SUPPORTED;
+ break;
+#endif
+ }
+
+ directoryTableBase &= KNDBG_PTE_4K_BASE_MASK;
+ if (directoryTableBase == 0)
+ {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ RtlZeroMemory(Response, sizeof(KNDBG_TRANSLATE_VIRTUAL_RESPONSE));
+ Response->Size = sizeof(KNDBG_TRANSLATE_VIRTUAL_RESPONSE);
+ Response->Flags = flags;
+ if (la57Active)
+ {
+ Response->Flags |= KNDBG_TRANSLATE_FLAG_LA57_ACTIVE;
+ }
+ Response->DirectoryTableBase = directoryTableBase;
+ Response->VirtualAddress = VirtualAddress;
+ Response->RequestedLength = Length;
+ Response->PagingLevels = la57Active ? 5u : 4u;
+
+ ULONGLONG pml4Base = directoryTableBase;
+ if (la57Active)
+ {
+ ULONGLONG pml5Index = (VirtualAddress >> 48) & 0x1ffull;
+ ULONGLONG pml5eAddress = directoryTableBase + pml5Index * sizeof(ULONGLONG);
+ Response->Pml5eAddress = pml5eAddress;
+ status = KnDbgReadPhysicalU64(pml5eAddress, &Response->Pml5e);
+ if (!NT_SUCCESS(status))
+ {
+ break;
+ }
+
+ if ((Response->Pml5e & KNDBG_PTE_PRESENT) == 0)
+ {
+ status = STATUS_ACCESS_VIOLATION;
+ break;
+ }
+
+ pml4Base = Response->Pml5e & KNDBG_PTE_4K_BASE_MASK;
+ }
+
+ ULONGLONG pml4Index = (VirtualAddress >> 39) & 0x1ffull;
+ ULONGLONG pdptIndex = (VirtualAddress >> 30) & 0x1ffull;
+ ULONGLONG pdIndex = (VirtualAddress >> 21) & 0x1ffull;
+ ULONGLONG ptIndex = (VirtualAddress >> 12) & 0x1ffull;
+
+ ULONGLONG pml4eAddress = pml4Base + pml4Index * sizeof(ULONGLONG);
+ Response->Pml4eAddress = pml4eAddress;
+ status = KnDbgReadPhysicalU64(pml4eAddress, &Response->Pml4e);
+ if (!NT_SUCCESS(status))
+ {
+ break;
+ }
+
+ if ((Response->Pml4e & KNDBG_PTE_PRESENT) == 0)
+ {
+ status = STATUS_ACCESS_VIOLATION;
+ break;
+ }
+
+ ULONGLONG pdpteAddress = (Response->Pml4e & KNDBG_PTE_4K_BASE_MASK) + pdptIndex * sizeof(ULONGLONG);
+ Response->PdpteAddress = pdpteAddress;
+ status = KnDbgReadPhysicalU64(pdpteAddress, &Response->Pdpte);
+ if (!NT_SUCCESS(status))
+ {
+ break;
+ }
+
+ if ((Response->Pdpte & KNDBG_PTE_PRESENT) == 0)
+ {
+ status = STATUS_ACCESS_VIOLATION;
+ break;
+ }
+
+ if ((Response->Pdpte & KNDBG_PTE_LARGE_PAGE) != 0)
+ {
+ ULONGLONG pageOffset = VirtualAddress & (KNDBG_1GB_PAGE_SIZE - 1);
+ ULONGLONG pageBytes = KNDBG_1GB_PAGE_SIZE - pageOffset;
+
+ Response->Flags |= KNDBG_TRANSLATE_FLAG_LARGE_PAGE;
+ Response->PageSize = KNDBG_1GB_PAGE_SIZE;
+ Response->PageOffset = pageOffset;
+ Response->PageBytes = pageBytes;
+ Response->PhysicalAddress = (Response->Pdpte & KNDBG_PTE_1GB_BASE_MASK) + pageOffset;
+ Response->TranslatedLength = static_cast(
+ KnDbgMinSize(static_cast(Length), static_cast(pageBytes)));
+ status = STATUS_SUCCESS;
+ break;
+ }
+
+ ULONGLONG pdeAddress = (Response->Pdpte & KNDBG_PTE_4K_BASE_MASK) + pdIndex * sizeof(ULONGLONG);
+ Response->PdeAddress = pdeAddress;
+ status = KnDbgReadPhysicalU64(pdeAddress, &Response->Pde);
+ if (!NT_SUCCESS(status))
+ {
+ break;
+ }
+
+ if ((Response->Pde & KNDBG_PTE_PRESENT) == 0)
+ {
+ status = STATUS_ACCESS_VIOLATION;
+ break;
+ }
+
+ if ((Response->Pde & KNDBG_PTE_LARGE_PAGE) != 0)
+ {
+ ULONGLONG pageOffset = VirtualAddress & (KNDBG_2MB_PAGE_SIZE - 1);
+ ULONGLONG pageBytes = KNDBG_2MB_PAGE_SIZE - pageOffset;
+
+ Response->Flags |= KNDBG_TRANSLATE_FLAG_LARGE_PAGE;
+ Response->PageSize = KNDBG_2MB_PAGE_SIZE;
+ Response->PageOffset = pageOffset;
+ Response->PageBytes = pageBytes;
+ Response->PhysicalAddress = (Response->Pde & KNDBG_PTE_2MB_BASE_MASK) + pageOffset;
+ Response->TranslatedLength = static_cast(
+ KnDbgMinSize(static_cast(Length), static_cast(pageBytes)));
+ status = STATUS_SUCCESS;
+ break;
+ }
+
+ ULONGLONG pteAddress = (Response->Pde & KNDBG_PTE_4K_BASE_MASK) + ptIndex * sizeof(ULONGLONG);
+ Response->PteAddress = pteAddress;
+ status = KnDbgReadPhysicalU64(pteAddress, &Response->Pte);
+ if (!NT_SUCCESS(status))
+ {
+ break;
+ }
+
+ if ((Response->Pte & KNDBG_PTE_PRESENT) == 0)
+ {
+ status = STATUS_ACCESS_VIOLATION;
+ break;
+ }
+
+ ULONGLONG pageOffset = VirtualAddress & KNDBG_PAGE_OFFSET_MASK;
+ ULONGLONG pageBytes = PAGE_SIZE - pageOffset;
+
+ Response->PageSize = PAGE_SIZE;
+ Response->PageOffset = pageOffset;
+ Response->PageBytes = pageBytes;
+ Response->PhysicalAddress = (Response->Pte & KNDBG_PTE_4K_BASE_MASK) + pageOffset;
+ Response->TranslatedLength = static_cast(
+ KnDbgMinSize(static_cast(Length), static_cast