BOOT CHAIN LAB
Lab 2 · detection engineering

Detecting call-table hooks.

A rootkit hides by redirecting a table the OS trusts — an import table, an interrupt vector, the syscall dispatcher. This lab doesn't build that. It teaches the invariant a defender checks: every entry in those tables should point into known, signed code — and a hook is a pointer that no longer does.

01 — the invariant

What the tables are, and what a hook changes

Each of these is a table of pointers the OS follows to reach code. Legitimately, every pointer lands in a specific, known place. A hook swaps one pointer for its own — so the shape of the table is what gives it away.

IATImport Address Table — per-process, resolves a DLL's exported functions. Entries should point into the mapped module they import from.
IDTInterrupt Descriptor Table — vectors to interrupt/exception handlers. Each gate should point into kernel text, with the expected segment and privilege.
SSDTSystem Service Dispatch Table — the syscall handlers. Entries should resolve inside ntoskrnl's code range.
GDTGlobal Descriptor Table — segment descriptors. An unexpected call gate or extra ring-0 code segment is the anomaly.

The defender's move is the same for all four: know where each pointer should land, then check that it still does.

02 — the method

Baseline → resolve → classify

  • Baseline each table from a known-good system (same OS build), storing the resolved location of every entry — not the raw address, which moves with KASLR.
  • Resolve each current handler to the module that owns that address (which driver / which section).
  • Classify: in the expected kernel image = normal; in another module = investigate; outside every known module = critical; wrong code segment or privilege = critical.
  • Record the finding with its evidence so a human can confirm — not auto-convict.

What a clean vs hooked entry looks like

# resolved, not raw — KASLR moves addresses
NtCreateFile   → ntoskrnl.exe + .text   ok
IDT[0x2e]      → ntoskrnl.exe + .text   ok
NtEnumerateKey → 0x????  (no owning module)  critical
IDT[0x1f]      → unknown.sys (not core)   investigate
03 — run an integrity scan

Classify the table

An illustrative table of handlers (sample data — on a real host this comes from idtscan). Run the scan: each entry is resolved to its owning module and classified. Watch which ones a defender would chase.

in kernel expected known security product benign hook other module investigate outside all modules critical
04 — with your tools

Where this runs for real

idtscan

Your read-only IDT/GDT scanner already does the IDT/GDT half: it reads the real table, resolves each gate, and flags a handler outside kernel text, inside a module, a wrong code segment, or a ring-3 gate that shouldn't exist.

sudo python3 idtscan/linux/idtscan_linux.py --out scan.jsonl
python3 idtscan/probe_1f.py scan.jsonl --baseline known-good.jsonl
# exit 3 = high/critical · 1 = gap · 0 = clean

honest limits

  • Addresses move with KASLR — compare resolved locations, not raw pointers.
  • A rootkit that re-points IDTR at a shadow table isn't caught by reading the real one — a known gap, recorded as such.
  • lockdown=confidentiality blocks /proc/kcore — that's a scan gap, never a "clean" pass.
  • On x64 Windows, PatchGuard/KPP makes kernel SSDT hooking rare — a hit there is high-signal but needs context.

A confirmed finding becomes a Vertical case: preserve the scan line, resolve the module, record the verdict with its basis. Same discipline as Lab 1 — detect, record, don't auto-convict.

05 — false positives are the hard part

Not every hook is hostile

EDR and anti-virus legitimately hook — that's the noise you tune against. The skill isn't spotting a hook; it's telling a security product's hook from an unknown module's, and an unknown module's from a handler that belongs to no module at all. The last one is the one that matters, and it's the one a defender can state with confidence.

Next: Lab 3 — cross-view detection (catching what a rootkit hides by comparing a high-level list against a low-level one), built on the detection lab.