Securing The Kernel via Static Binary Rewriting and Program Shepherding

Securing The Kernel via Static Binary Rewriting and Program Shepherding
Notice: This research summary and analysis were automatically generated using AI technology. For absolute accuracy, please refer to the [Original Paper Viewer] below or the Original ArXiv Source.

Recent Microsoft security bulletins show that kernel vulnerabilities are becoming more and more important security threats. Despite the pretty extensive security mitigations many of the kernel vulnerabilities are still exploitable. Successful kernel exploitation typically grants the attacker maximum privilege level and results in total machine compromise. To protect against kernel exploitation, we have developed a tool which statically rewrites the Microsoft Windows kernel as well as other kernel level modules. Such rewritten binary files allow us to monitor control flow transfers during operating system execution. At this point we are able to detect whether selected control transfer flow is valid or should be considered as an attack attempt. Our solution is especially directed towards preventing remote kernel exploitation attempts. Additionally, many of the local privilege escalation attacks are also blocked (also due to additional mitigation techniques we have implemented). Our tool was tested with Microsoft Windows XP, Windows Vista and Windows 7 (under both virtual and physical machines) on IA-32 compatible processors. Our apparatus is also completely standalone and does not require any third party software.


💡 Research Summary

The paper presents a practical system for protecting the Microsoft Windows kernel and its critical modules against both remote and local exploitation by means of static binary rewriting combined with program shepherding. Unlike dynamic binary instrumentation, which is difficult to apply safely at kernel level and incurs significant performance penalties, the authors opt for a fully static approach that rewrites the Portable Executable (PE) files of the kernel and selected drivers before they are loaded. The system consists of four main components: Integration, Monitoring, Configuration, and Installer.

The Integration component is split into an Analyzer and a Rewriter. The Analyzer performs recursive disassembly of the PE image, classifying code into “Solid code” (clearly identified instructions) and “Prospect code” (regions inferred from relocation information or heuristic testing). This classification helps avoid code‑versus‑data confusion, a common pitfall in static rewriting. The Rewriter then proceeds through three phases:

  1. Instrumentation – Every CALL, indirect JMP, and RET instruction is preceded by a small filtering routine. The routine checks a memory‑map that records the attributes (executable, writable, kernel‑space) of each loaded page. The authors deliberately exclude indirect calls to imported APIs because the Import Address Table is read‑only and such instrumentation would hurt performance.

  2. Calculation – New virtual addresses are allocated for the rewritten basic blocks, and relocation entries are updated accordingly. If an original instruction contains an immediate operand that references a relocation entry, the rewritten version receives a matching entry.

  3. Repairing – All relative offsets used by control‑transfer instructions are recomputed to reflect the new layout, ensuring that the rewritten code remains stable. Short conditional jumps are expanded to at least five bytes to simplify later offset fixing.

The rewritten code can be delivered in two ways. The non‑invasive mode leaves the original binaries untouched and loads the instrumented code into memory via a driver; the invasive mode (implemented in the prototype) actually modifies the PE files on disk, so the system boots directly into a pre‑patched kernel. The authors favor the invasive approach for simplicity but note that the non‑invasive variant would be more robust in production environments.

The Monitoring component is a kernel‑mode driver that builds a memory map of all loaded modules at system start. Its filtering logic treats any control‑flow transfer to a page that is not marked executable or that belongs to user space as an attack. To block many local privilege‑escalation exploits, the system also hooks NtQuerySystemInformation and denies any request for the SystemModuleInformation class, thereby preventing attackers from discovering kernel module base addresses.

When a suspicious transfer is detected, the system can either log the event and continue execution (useful for honeypot‑style analysis) or immediately shut down the machine. Because the monitoring runs in kernel mode, a graceful termination of a single task is not possible; the only safe reaction is a full shutdown.

The authors evaluated their prototype on Windows XP, Vista, and 7 (both virtual and physical machines). They tested against publicly known exploits, notably CVE‑2009‑3103 (SMB2 remote code execution) and CVE‑2010‑2743 (keyboard layout vulnerability used by Stuxnet). In both cases the system successfully detected the malicious control‑flow transfer before the payload could be executed. Most local privilege‑escalation exploits were stopped at the very first stage thanks to the NtQuerySystemInformation hook.

Performance measurements show that the integration phase (disassembly, basic‑block creation, and rewriting) completes in roughly 2–4 seconds for typical kernel modules on a Core 2 Duo laptop. Runtime overhead on a protected Windows 7 system ranges from 2 % to 5 % depending on workload, which is markedly lower than the overhead reported for dynamic instrumentation solutions. The authors acknowledge that not every function can be safely patched (e.g., functions whose first basic block is smaller than five bytes or those consisting solely of ASCII/Unicode data), but the proportion of patched functions was sufficient to achieve effective protection.

In summary, the paper demonstrates that static binary rewriting, when combined with a lightweight program‑shepherding filter, can provide robust kernel‑level protection with modest performance impact. The work addresses several technical challenges—code‑vs‑data discrimination, relocation handling, safe patch placement, and minimal‑impact monitoring—while delivering concrete protection against real‑world exploits. Remaining challenges include automating the deployment process (especially handling Windows File Protection and file‑ownership issues), extending compatibility to a broader set of drivers, and improving handling of self‑modifying kernel code. Nonetheless, the presented approach offers a compelling alternative to purely dynamic defenses for securing the Windows kernel.


Comments & Academic Discussion

Loading comments...

Leave a Comment