View Full Version : [WinInternals] Reverse Engineering of kdbgctrl - How are builded Kernel Triage Dumps

June 11th, 2010, 12:13

In this little blog post I'm going to reverse kdbgctrl.exe an handy tool delivered with Windows Debugging Tools kit (windbg package). This tool has the handy functionality to allow Kernel Triage Dumps building, also from a non /DEBUG bootted machine.

As written before, from nynaeve, Triage Dump writing make able the kernel to create small dump files which contains the basical and usual informations of a Dump, like processes with associated threads and relative stacks.

There is not many documentation about the usage of kdbgctrl, we have only a basical:
kdbgctrl -td pid file

Let's now see how internally works kdbgctrl.exe; why this?..curiosity and the potentiality to know which functions are used and became able to rewrite it, and also (as asked by some people) to show a subset of considerations that make you able to fastly and effectively reverse the functionality of an application.

What we have is an application that takes two arguments, one of them is a filename, so after disassembling it we can immediately search for File Management Operations, like CreateFile and WriteFile, at this point we can trace back to the origins, locating the requestor and consecutively identify the functions that provide data dumped into file.

.text:0100340F loc_100340F: ; CODE XREF: sub_1003310+BF j
.text:0100340F push 0 ; hTemplateFile
.text:01003411 push 80h ; dwFlagsAndAttributes
.text:01003416 push 2 ; dwCreationDisposition
.text:01003418 push 0 ; lpSecurityAttributes
.text:0100341A push 0 ; dwShareMode
.text:0100341C push 40000000h ; dwDesiredAccess
.text:01003421 mov ecx, [ebp+lpFileName]
.text:01003424 push ecx ; lpFileName
.text:01003425 call ds:CreateFileA

the first line denotes a Cross Reference, so let's follow it:

.text:010033AE loc_10033AE: ; CODE XREF: sub_1003310+84 j
.text:010033AE lea eax, [ebp+nNumberOfBytesToWrite]
.text:010033B1 push eax
.text:010033B2 mov ecx, [ebp+dwSize]
.text:010033B5 push ecx
.text:010033B6 mov edx, [ebp+lpAddress]
.text:010033B9 push edx
.text:010033BA push 24h
.text:010033BC lea eax, [ebp+var_44]
.text:010033BF push eax
.text:010033C0 push 1Dh
.text:010033C2 call ds:NtSystemDebugControl

This is the heart of kdbgctrl algorithm, the Triage Dump is obtained by calling NtSystemDebugControl, we have now to uncover its parameters to be able to reproduce the code.

IN ULONG InputBufferLength,
IN ULONG OutputBufferLength,

In our case SYSDBG_COMMAND has the value 1D, with a little research we can discover that this value belongs to SysDbgGetTriageDump, and immediately after the involved struct

typedef struct _SYSDBG_TRIAGE_DUMP
ULONG Flags;
ULONG BugCheckCode;
ULONG_PTR BugCheckParam1;
ULONG_PTR BugCheckParam2;
ULONG_PTR BugCheckParam3;
ULONG_PTR BugCheckParam4;
ULONG ProcessHandles;
ULONG ThreadHandles;
PHANDLE Handles;

Immediately before NtSystemDebugControl we meet:

.text:0100336C mov [ebp+var_40], 69696969h
.text:01003373 mov [ebp+dwSize], 400000h
.text:0100337A push 4 ; flProtect
.text:0100337C push 1000h ; flAllocationType
.text:01003381 mov edx, [ebp+dwSize]
.text:01003384 push edx ; dwSize
.text:01003385 push 0 ; lpAddress
.text:01003387 call ds:VirtualAlloc

69696969 belongs to the tipical BugCheck of Triage Dumps. A bit upper we have

.text:0100335D push ecx
.text:0100335E call sub_1002EB0
.text:01003363 test eax, eax
.text:01003365 jz short loc_100336C ;Prosecute with Dumping Process
.text:01003367 jmp loc_100357C ;Jump Out

Let's see what happens inside call sub_1002EB0

.text:01002EE3 push offset aDbgeng_dll ; "dbgeng.dll"
.text:01002EE8 call ds:LoadLibraryA
.text:01002F45 push offset aDebugcreate ; "DebugCreate"
.text:01002F4A mov edx, [ebp+arg_8]
.text:01002F4D mov eax, [edx]
.text:01002F4F push eax ; hModule
.text:01002F50 call ds:GetProcAddress

The DebugCreate function creates a new client object and returns an interface pointer to it, the parameters passed to DebugCreate are the same as those passed to IUnknown::QueryInterface, and they are treated the same way.

.text:0100307A call sub_1004D90
.text:0100307F push eax
.text:01003080 push offset aAttachprocessF ; "AttachProcess failed, %s\n"
.text:01003085 call dsrintf

kdbgctrl attempts to a Debug Attach by using the pid inserted by user.

.text:010030BA call sub_1004D90
.text:010030BF push eax
.text:010030C0 push offset aWaitforeventFa ; "WaitForEvent failed, %s\n"
.text:010030C5 call dsrintf

and waits for events.

.text:010030FA call sub_1004D90
.text:010030FF push eax
.text:01003100 push offset aGetnumberthrea ; "GetNumberThreads failed, %s\n"
.text:01003105 call dsrintf

Retrieve the number of Threads, this value is necessary to define the limits of a cycle that will perform a per thread scan.

.text:010031AB call sub_1004D90
.text:010031B0 push eax
.text:010031B1 push offset aGetthreadidsby ; "GetThreadIdsByIndex failed, %s\n"
.text:010031B6 call dsrintf
.text:010031BC add esp, 8
.text:010031BF jmp loc_1003252

GetThreadIdsByIndex belongs (Like GetNumberThreads) to the interface IDebugSystemObjects, the GetThreadIdsByIndex method returns the engine and system thread IDs for the specified threads in the current process.

.text:010031E8 call sub_1004D90
.text:010031ED push eax
.text:010031EE push offset aSetcurrentthre ; "SetCurrentThreadId failed, %s\n"
.text:010031F3 call dsrintf
.text:010031F9 add esp, 8
.text:010031FC jmp short loc_1003252

The currently indexed thread, became the Current Thread, at this point we can know the thread handle, by calling GetCurrentThreadHandle.

This enumeration routine provides all informations that will be stored into the Triage Dump.

Should be now clear how is builded a Triage Dump, just an hint if you want to uncover other kdbgctrl functionalities, as you have seen the core function is NtSystemDebugControl, so easly list all xRefs of this function.

See you to the next post..
Giuseppe 'Evilcry' Bonfa