Local Kernel Debugger (LKD) is a python wrapper around dbgengine.dll
to
perform local kernel debugging of a Windows kernel booted in DEBUG mode.
Local Kernel Debugging is the ability to perform kernel-mode debugging on a
single computer. In other words, the debugger runs on the same computer
that is being debugged.
Windows offers this functionality through WinDbg and KD binaries which allow to
read/write the kernel memory, perform in/out and access MSRs.
WinDbg and KD use dbgengine.dll
to offer this functionality, but dbgengine
only
allows binary with name windbg.exe
or kd.exe
and also requires that the said
binary embeds a driver used to perform the local kernel debugging actions.
To bypass these restrictions we used IAT hook against dbgengine.dll
to emulate the
resource (that is read from a file) and also change the result of
GetModuleFileNameW
to C:\\windbg.exe
.
With that we are able to retrieve some COM interfaces offered by dbgengine
and
used to perform Local Kernel Debugging.
Last problem is that some functionalities we find useful are missing in the
LKD driver (memory allocation, custom kernel call, ...) so we
use the LKD driver to rewrite some part of it in memory to upgrade its features.
The LKD try to use the usual environment variable _NT_SYMBOL_PATH
to retrieve the symbol path.
If this variable does not exist it will use the .\symbols
directory in the current path
of dbginterface.py
Some part of LKD need (for now) to have access to the ntoskrnl
symbols so the
debugged computer must be connected to Internet at least once.
LKD cannot be done in a SysWow64 process, if you try to debug a 64bits kernel you will need a 64bits python.
You can find every MSI you need at https://www.python.org/downloads/windows/
See the example directory.
-
dbginterface.py
The main file of the project, LKD objects that setup the IAT hooks for the WinDbg imposture, attach to the local kernel, retrieve the COM interfaces and wrap them. -
resource_emulation.py
IAT hooks that allow to emulate a resource from a file in the File System. -
simple_com.py
Simple wrapper to COM interface (used in example\output_demo.py). -
driver_upgrade.py
Code that rewrite part of the LKD driver in memory to upgrade its features -
bin\windbg_driver_x86.sys
LKD (signed) driver for 32bits kernel extracted from WinDbg's resources -
bin\windbg_driver_x64.sys
LKD (signed) driver for 64bits kernel extracted from WinDbg's resources -
bin\DBGDLL\
dbgengine + symbol engine for windows 32bits -
bin\DBGDLL64\
dbgengine + symbol engine for windows 64bits -
test\
Our test, need to be launched on 32bits and 64bits kernels to be complete -
example\
Our demos code of the use and interest of LKD -
doc\
Sphinx documentation (Available online) -
windows\
Side project with some windows helpers, some part of this are not directly used by LKD.
The features used are:- IAT Hooks
- Windows API proxy
- Native execution
- simple_x86 / simple_x64 assembler
There is a COM API to perform I/O: WriteIo
and ReadIo
but it seems that
these API check (nt!KdpSysReadIoSpace
& nt!KdpSysWriteIoSpace
) for some alignment in the port and the size.
A ReadIo(size=4) must be on a port aligned on 4.
For performing any I/O, LKD offers two function: do_in
, do_out
.
do_in(self, port, size)
<require upgraded driver>
Perform IN instruction in kernel mode
do_out(self, port, value, size)
<require upgraded driver>
Perform OUT instruction in kernel mode
Used to convert a symbol address to an ULONG64 requested by the API and vice versa. Problem is that in a 32bits kernel the kernel addresses are bit expended.
ntoskrnl
base in 32bits kernel would not be 0x8XXXXXXX
but 0xffffffff8XXXXXXX
.