Kdrill is a tool to analyze the kernel land of Windows 64b systems (tested from Windows 7 to Windows 11). Its main objective is to assess if the kernel is compromised by a rootkit.
The code is compatible with python2/3 without dependencies and can perfom checks without Microsoft symbols or Internet connectivity.
For live memory/kernel analysis, the Winpmem
driver is used and Kdrill
interfaces itself with the driver, another possibility is to connect to a remote GDB server. KDrill can also analyze Full crash dumps and Kernel crash dumps (mainly stored in C:\Windows\MEMORY.DMP
), full raw memory dump, and a fucked version of AFF4 dumps (zip, but not zipped).
Kdrill
accesses the physical memory and decodes/re-builds the OS internals structures to explore them, and to verify their intergrity.
The following checks are performed:
- Loaded modules list
- Drivers in memory code (compared to on-disk version)
- Callbacks of kernel objects and internal ntoskrnl lists
- PlugAndPlay tree and filters
- Kernel types callbacks
- FltMgr callbacks
- KTimers DPC functions
- IRP driver's tables
- Driver signing global variables with callbacks
- NDIS filters and callbacks
- NetIO/FwpkCLNT filtering dispatch
- Devices and their attached device objects
- IDT entries
- PatchGuard initialization and state
Kdrill retrieves all kernel structures offsets automatically and builds a specific mapping at each execution. So it doesn't need symbols or Internet connectivity to resolve them (:wink: disconnected networks).
Most checks verify if the callback or pointed function is in a driver and if the driver is inside a "trust list" I made totally random. I strongly recommend you to check if those drivers are signed (by a trusted signer) ;)
However, for integrity drivers checks, you will need to have an Internet access to download Microsoft binaries from MS servers in order to diff them. If you already have them in c:\symbols
it's fine too :)
Some examples of rootkits detections (not all triggers, juste intersting finds).
Winnti replaces functions pointers in the NDIS callback of TCPIP. With the cndis
command we can identify it:
#>> cndis
[*] Checking NDIS Firewall layers
[*] List from fffffa80033d3d70
Driver : pacer.sys
GUID : {B5F4D659-7DAA-4565-8E41-BE220ED60542}
Description : QoS Packet Scheduler
Driver : wfplwf.sys
GUID : {B70D6460-3635-4D42-B866-B8AB1A24454C}
Description : WFP LightWeight Filter
[*] Checking NDIS Protocol layers
[*] List from fffffa8002a71a60
Name : NDIS6FW
Callback fffff88003329e50 -> c:\users\toto\appdata\local\temp\tmp1ec3.tmp (not in white list) SUSPICIOUS
Callback fffff88003329e50 -> c:\users\toto\appdata\local\temp\tmp1ec3.tmp (not in white list) SUSPICIOUS
[...]
Name : NDISWAN
Name : WANARPV6
Name : WANARP
Name : TCPIP6TUNNEL
Name : TCPIPTUNNEL
Name : TCPIP6
Name : TCPIP
Callback fffff8800332a660 -> c:\users\toto\appdata\local\temp\tmp1ec3.tmp (not in white list) SUSPICIOUS
Callback fffff8800332a810 -> c:\users\toto\appdata\local\temp\tmp1ec3.tmp (not in white list) SUSPICIOUS
Callback inside PspCreateProcessNotifyRoutine:
#>> ccb
[*] Checking \Callback\TcpConnectionCallbackTemp : 0xfffffa8002f38360
[*] Checking \Callback\TcpTimerStarvationCallbackTemp : 0xfffffa8004dfd640
[*] Checking \Callback\LicensingData : 0xfffffa80024bc2f0
[*] Checking \Callback\LLTDCallbackRspndr0006000006000000 : 0xfffffa80048713a0
[...]
[*] PspLoadImageNotifyRoutine
[*] PspCreateProcessNotifyRoutine
Callback fffffa8004bc2874 -> SUSPICIOUS ***Unknown*** 48 89 5c 24 08 57 48 81 ec 30 01 00 00 48 8b fa
This rootkit also inserts a network IO filtering in FwpkCLNT:
#>> cnetio
[*] FwpkCLNT/NetIo Callouts (callbacks) : fffffa8004965000 (4790)
Callback fffffa8004bd9580 -> SUSPICIOUS ***Unknown*** 48 8b c4 48 89 58 08 48 89 50 10 55 56 57 41 54
Callback fffffa8004bca6b0 -> SUSPICIOUS ***Unknown*** 33 c0 c3 cc 40 53 48 83 ec 20 48 8b 89 50 01 00
Listing modules (with or without filter):
#>> lm winp
fffff806bd4f0000 10000 \??\C:\Kdrill\winpmem_x64.sys
Display a dump at a specific address:
#>> dq nt 40
FFFFF80668000000 0000000300905A4D 0000FFFF00000004 MZ..........##..
FFFFF80668000010 00000000000000B8 0000000000000040 ........@.......
FFFFF80668000020 0000000000000000 0000000000000000 ................
FFFFF80668000030 0000000000000000 0000011800000000 ................
Kdrill embeds a LDE to have a minimal x86 disassembly. You can have a tiny view of the opcodes with it:
#>> u nt!NtReadFile 10
> fffff806685f44d0 | 4c894c2420 |
fffff806685f44d5 | 4c89442418 |
fffff806685f44da | 4889542410 |
fffff806685f44df | 53 |
fffff806685f44e0 | 56 |
fffff806685f44e1 | 57 |
Display a dump at an pointed address:
#>> dq poi(nt!LpcPortObjectType)
FFFFAA0F7DD524E0 FFFFAA0F7DD524E0 FFFFAA0F7DD524E0 .$.}..##.$.}..##
FFFFAA0F7DD524F0 0000000000140012 FFFFD5000BF7DC90 ..............##
FFFFAA0F7DD52500 00000000000000F9 0000107C0000002E ............|...
FFFFAA0F7DD52510 0000140B00000F31 000000000000137D 1.......}.......
You can display a chunk header of data with a reference in the pool:
#>> fpool poi(nt!LpcPortObjectType)
Pool : ffffaa0f7dd52470
Tag : ObjT
Size : 150
Prev Size : 0
Listing object directory objects:
#>> winobj \Callback
Callback \Callback\IGD_WNICShareObj (ffffaa0f8697ae30)
Callback \Callback\WdProcessNotificationCallback (ffffaa0f7ebf9d30)
Callback \Callback\LLTDCallbackRspndr0006008004000000 (ffffaa0f88872c60)
[...]
Getting informations about a random object in memory:
#>> !addr ffffaa0f7ed3fb10
Pool : ffffaa0f7ed3fac0
Tag : Devi
Size : 210
Prev Size : 0
#>> !addr FFFFF80668CFB280
fffff80668cfb280 in \SystemRoot\system32\ntoskrnl.exe
ntoskrnl+cfb280
Get PTE rights of a page:
#>> list fffff80668cfb280
FFFFF80668CFB000 rw--
List the relation between kernel memory address and a mapped file:
#>> filecache
Vacb : ffffaa0f7dde4000 ; size : 6
0xffffc186e4100000 \Windows\System32\catroot2\edb.log (9684)
0xffffc186fb4c0000 \$MapAttributeValue (320)
[...]
Help infos.
#>> ?
ci : check if some drivers codes are modified (for file dump use "offline 1" command to download them from MS)
fpg : Find if PatchGuard and check if it's running
cirp : check IRP table of all drivers
cio : check IRP table of PnP devices
cci : check g_CiOptions state and CI DSE callbacks
ccb : check Callback directory
cndis : check NDIS callbacks
cnetio : check FwpkCLNT/NetIo callbacks
cktypes : check kernel types callbacks
cfltmgr : check FltMgr callbacks
ctimer : check DPC timers
cidt : check IDT entries
pe : check kernel memory to find hidden drivers
drv_stack : display stacks devices to go to the driver
filecache : Find Vacbs and crawl PFN to identify files mapped
winobj [\Device] : list objects
list start end : display memory
lm : list modules
dump addr length: display memory
!addr addr: get infos on the address
dqs addr [length]: display memory with informations
d[bdq] addr [length]: display memory
!d[bdq] addr [length]: display physical memory
fpool ADDR : Find pool chunck of address
pool ADDR : Get informations on a pool chunck
obj ADDR : Get informations on an object
v[v0] : verbose [very/stop]
offline [0/1] : set 1 if you are analyzing cold dump
cr3 addr : set CR3 register
ncr3 : find next CR3 valid value
o2p 0x123 : file offset to phys address
p2v 0x123 : phys address to virtual address
o2v 0x123 : file offset to virtual address
? : help
- LDE is based on Beatrix work: https://github.com/BeaEngine/lde64
- Winpmem for his driver https://github.com/Velocidex/WinPmem/blob/master/src/binaries/winpmem_x64.sys