- May 7, 2013
- 10,391
Purpose:
The purpose of this article is to provide a short introduction to the purpose of processes, threads and stacks, and the information which they can provide us with when debugging.
Contents:
Processes are integral to the Windows operating system. Every time you open an application such as a web browser, you're creating and interacting with a process. What are processes? Why are they important and how can we explore them within WinDbg? In this section, we will be exploring the answer to those questions.
When speaking of processes, we are actually referring to the .exe files found within Task Manager, each one will begin a process:
To understand what a process and what a thread is, we can use the analogy of a factory. The process is the factory, it is responsible for producing certain outputs, and is nothing more than a building to house our operations and the workers who must work together to produce those outputs. The workers in this instance are our threads, and will be the ones who work within the process, these workers must operate in perfect synchronization in order to avoid any mishaps from happening and to ensure that the day-to-day operations of the factory are running smoothly.
Every process within is represented with a special structure called _EPROCESS, we can dump this structure within WinDbg using the following command:
This will produce the following output:
I've highlighted three of the fields which I feel are important to understand for a small introduction to processes. Please note that there are many more useful fields within this structure, but they require much more knowledge about the Windows operating system.
The UniqueProcessId field is self explanatory and is a unique Id assigned to each process upon creation. This is usually seen in other aspects of Windows as Pid.
The ActiveProcessLinks field is more interesting. It refers to the doubly linked list in which all processes belong to when they are created. This enables Windows to keep track of all the active processes running on the system. This can be manipulated by malicious programs to hide themselves, but fortunately, there is techniques which can be used to detect this kind of manipulation. More details can be found here - Rootkits: Direct Kernel Object Manipulation and Processes
The list head of this doubly linked list is found in a global variable called PsActiveProcessHead. This is the start of the doubly linked list. However, I won't delve into this too much as it is out of the scope of this article. You can dump the address using the following command:
You may noticed another process called _KPROCESS referenced to from the Pcb field, and we can dump it within WinDbg in the same way as before:
Now, there isn't too much of a difference between _EPROCESS and _KPROCESS apart from their fields. The purpose of this was to increase the level of abstraction within processes. The acronym for the Pcb field is Process Control Block. I've highlighted two fields of interest, one of which will lead directly into the next topic of this article which is threads.
The DirectoryTableBase field is an important field used in memory management and address translation. We won't cover the details of address translation here, although, it should be mentioned that it is performed using what is known as page tables. The field contains the address of first table which is used in address translation.
The ThreadListHead field is similar to the process active head for our active processes, and is the head of the doubly linked list for all the threads associated to our running process.
There is a much easier method for dumping process information and that is through the use of the following debugger command extension:
If we do not specify the address of the process, then it will dump the details of the last process which was running at the time of the crash.
Notice some of the similar information we saw in the _EPROCESS and _KPROCESS structures from earlier? The Image field is the name of the process; in our case, it was the system process. We can also see all the current threads associated to this process too, along with, their thread addresses which we which will use in the next section.
Threads:
Threads are where the process execution takes place. Threads are no more than units of execution. Each thread will have two stacks: one of which is for the kernel and one of which is for user mode. We'll explore stacks in the next section. Like with processes, each thread has a _ETHREAD structure and a _KTHREAD structure. Let's firstly dump the _ETHREAD structure:
The Tcb field refers to the thread control block and is a reference to the _KTHREAD structure.
The Cid field is the unique identifier associated with each thread.
The IrpList is a doubly linked list of IRPs which have been generated by this thread. IRPs are I/O request packets which are used by I/O manager to complete various operations such as power management including putting devices in a sleep state.
Now, let's dump the _KTHREAD structure and investigate some of its fields:
The StackLimit and StackBase fields refer to the end and the beginning of the call stack associated to this thread respectively. The KernelStack field contains a pointer to the beginning of the kernel stack. We can use the addresses referenced by these pointers with the dps command to dump the entire stack of thread.
As before, we can dump the details of a thread in a much more user friendly way using the !thread debugger extension.
The highlighted fields correspond directly to the fields discussed within the _ETHREAD and _KTHREAD structures. You can now also see the call stack for this thread. We'll discuss call stacks in the next section.
Stacks:
Each thread has its own call stack. When we are debugging kernel crash dumps, we are actually examining the kernel version of the thread's call stack.
You can think of a call stack as the operations which the thread has been completing. Call stacks are read from the top to the bottom. The last call is placed at the top and the first call is placed at the bottom. Call stacks follow what is known as LIFO (Last In First Out). This means that the last stack frame to be pushed (inserted) onto the stack, is the first stack frame to be popped (removed) from the stack. Each function call results in a new stack frame being pushed onto the stack, once this function has returned, then the stack frame is popped.
We'll discuss stack frames momentarily.
Firstly, let's dump the call stack for the thread. Using the WinDbg k command will dump the call stack for the current thread context.
If examine the stack, we can see four different columns: the stack frame number (#), the stack pointer address (Child-SP), the return address once the call has completed (RetAddr) and the function call being made (Call Site). The k command is the simplest of the WinDbg call stack unwind commands, there are many different variations which dump varying information regarding the call stack, however, the k command will be suffice for this article.
As we can see in the call stack, the last call was to nt!KeBugCheckEx, this the bugcheck exception handler which builds and dispatches our blue screen of death. Each line within the call stack represents an individual stack frame. We can dump a stack frame using the .frame command with the /r switch to dump the registers:
Now each stack frame will have it's own registers saved and parameters pushed to the stack. The first four parameters to a function call are always pushed to rcx, rdx, r8 and r9 registers respectively on x64 machines. Any other additional parameters are pushed onto the stack itself.
Now, using the previously mentioned function call, we can see this in action by dumping the bugcheck parameters using the .dumpdebug command comparing them to the values stored in the parameters within the call stack frame.
As you can see, the first parameter of the bugcheck is pushed into the rcx register, the second parameter is pushed into the rdx register, and the third and fourth into the r8 and r9 registers. However, you may be a little confused now?
The .dumpdebug command is stating that fourth parameter is 00000000`49c8820f, but the r9 register contains the value of third parameter? This is because the KeBugCheckEx function actually takes the STOP code as the first parameter. The fourth bugcheck parameter is actually the fifth parameter to be passed to the function and can be found on the stack.
We can find this by using the stack pointer address stored in the rsp register, and using the dd command with the appropriate offset.
Now, this is just an introduction to processes, threads and the stack; there is much more to learn! Although, I hope this article provides enough information to help you with your debugging efforts. If you wish to learn more about stack, then I throughly recommend you read the CodeMachine article below.
Further Reading:
CodeMachine - Article - X64 Deep Dive
Internals of Windows Thread
Registers (x86)
The purpose of this article is to provide a short introduction to the purpose of processes, threads and stacks, and the information which they can provide us with when debugging.
Contents:
- Processes
- Threads
- Call Stacks
Processes are integral to the Windows operating system. Every time you open an application such as a web browser, you're creating and interacting with a process. What are processes? Why are they important and how can we explore them within WinDbg? In this section, we will be exploring the answer to those questions.
When speaking of processes, we are actually referring to the .exe files found within Task Manager, each one will begin a process:
To understand what a process and what a thread is, we can use the analogy of a factory. The process is the factory, it is responsible for producing certain outputs, and is nothing more than a building to house our operations and the workers who must work together to produce those outputs. The workers in this instance are our threads, and will be the ones who work within the process, these workers must operate in perfect synchronization in order to avoid any mishaps from happening and to ensure that the day-to-day operations of the factory are running smoothly.
Every process within is represented with a special structure called _EPROCESS, we can dump this structure within WinDbg using the following command:
dt _EPROCESS
This will produce the following output:
Code:
6: kd> dt _EPROCESS
nt!_EPROCESS
+0x000 [HI]Pcb[/HI] : _KPROCESS
+0x2d8 ProcessLock : _EX_PUSH_LOCK
+0x2e0 [HI]UniqueProcessId[/HI] : Ptr64 Void
+0x2e8 [HI]ActiveProcessLinks[/HI] : _LIST_ENTRY
+0x2f8 RundownProtect : _EX_RUNDOWN_REF
+0x300 Flags2 : Uint4B
+0x300 JobNotReallyActive : Pos 0, 1 Bit
+0x300 AccountingFolded : Pos 1, 1 Bit
+0x300 NewProcessReported : Pos 2, 1 Bit
+0x300 ExitProcessReported : Pos 3, 1 Bit
+0x300 ReportCommitChanges : Pos 4, 1 Bit
+0x300 LastReportMemory : Pos 5, 1 Bit
+0x300 ForceWakeCharge : Pos 6, 1 Bit
+0x300 CrossSessionCreate : Pos 7, 1 Bit
+0x300 NeedsHandleRundown : Pos 8, 1 Bit
+0x300 RefTraceEnabled : Pos 9, 1 Bit
+0x300 DisableDynamicCode : Pos 10, 1 Bit
+0x300 EmptyJobEvaluated : Pos 11, 1 Bit
+0x300 DefaultPagePriority : Pos 12, 3 Bits
+0x300 PrimaryTokenFrozen : Pos 15, 1 Bit
+0x300 ProcessVerifierTarget : Pos 16, 1 Bit
+0x300 StackRandomizationDisabled : Pos 17, 1 Bit
+0x300 AffinityPermanent : Pos 18, 1 Bit
+0x300 AffinityUpdateEnable : Pos 19, 1 Bit
+0x300 PropagateNode : Pos 20, 1 Bit
+0x300 ExplicitAffinity : Pos 21, 1 Bit
+0x300 ProcessExecutionState : Pos 22, 2 Bits
+0x300 DisallowStrippedImages : Pos 24, 1 Bit
+0x300 HighEntropyASLREnabled : Pos 25, 1 Bit
+0x300 ExtensionPointDisable : Pos 26, 1 Bit
+0x300 ForceRelocateImages : Pos 27, 1 Bit
+0x300 ProcessStateChangeRequest : Pos 28, 2 Bits
+0x300 ProcessStateChangeInProgress : Pos 30, 1 Bit
+0x300 DisallowWin32kSystemCalls : Pos 31, 1 Bit
+0x304 Flags : Uint4B
[...]
I've highlighted three of the fields which I feel are important to understand for a small introduction to processes. Please note that there are many more useful fields within this structure, but they require much more knowledge about the Windows operating system.
The UniqueProcessId field is self explanatory and is a unique Id assigned to each process upon creation. This is usually seen in other aspects of Windows as Pid.
The ActiveProcessLinks field is more interesting. It refers to the doubly linked list in which all processes belong to when they are created. This enables Windows to keep track of all the active processes running on the system. This can be manipulated by malicious programs to hide themselves, but fortunately, there is techniques which can be used to detect this kind of manipulation. More details can be found here - Rootkits: Direct Kernel Object Manipulation and Processes
The list head of this doubly linked list is found in a global variable called PsActiveProcessHead. This is the start of the doubly linked list. However, I won't delve into this too much as it is out of the scope of this article. You can dump the address using the following command:
? poi(PsActiveProcessHead)
You may noticed another process called _KPROCESS referenced to from the Pcb field, and we can dump it within WinDbg in the same way as before:
Code:
6: kd> dt _KPROCESS
nt!_KPROCESS
+0x000 Header : _DISPATCHER_HEADER
+0x018 ProfileListHead : _LIST_ENTRY
+0x028 [HI]DirectoryTableBase[/HI] : Uint8B
+0x030 [HI]ThreadListHead[/HI] : _LIST_ENTRY
+0x040 ProcessLock : Uint4B
+0x044 ProcessTimerDelay : Uint4B
+0x048 DeepFreezeStartTime : Uint8B
+0x050 Affinity : _KAFFINITY_EX
+0x0f8 ReadyListHead : _LIST_ENTRY
+0x108 SwapListEntry : _SINGLE_LIST_ENTRY
+0x110 ActiveProcessors : _KAFFINITY_EX
+0x1b8 AutoAlignment : Pos 0, 1 Bit
+0x1b8 DisableBoost : Pos 1, 1 Bit
+0x1b8 DisableQuantum : Pos 2, 1 Bit
+0x1b8 DeepFreeze : Pos 3, 1 Bit
+0x1b8 TimerVirtualization : Pos 4, 1 Bit
+0x1b8 CheckStackExtents : Pos 5, 1 Bit
+0x1b8 PpmPolicy : Pos 6, 2 Bits
+0x1b8 ActiveGroupsMask : Pos 8, 20 Bits
+0x1b8 ReservedFlags : Pos 28, 4 Bits
+0x1b8 ProcessFlags : Int4B
+0x1bc BasePriority : Char
+0x1bd QuantumReset : Char
+0x1be Visited : UChar
+0x1bf Flags : _KEXECUTE_OPTIONS
+0x1c0 ThreadSeed : [20] Uint4B
+0x210 IdealNode : [20] Uint2B
+0x238 IdealGlobalNode : Uint2B
+0x23a Spare1 : Uint2B
+0x23c StackCount : _KSTACK_COUNT
+0x240 ProcessListEntry : _LIST_ENTRY
+0x250 CycleTime : Uint8B
+0x258 ContextSwitches : Uint8B
+0x260 SchedulingGroup : Ptr64 _KSCHEDULING_GROUP
+0x268 FreezeCount : Uint4B
+0x26c KernelTime : Uint4B
+0x270 UserTime : Uint4B
+0x274 ReadyTime : Uint4B
+0x278 Spare2 : [80] UChar
+0x2c8 InstrumentationCallback : Ptr64 Void
+0x2d0 SecurePid : Uint8B
Now, there isn't too much of a difference between _EPROCESS and _KPROCESS apart from their fields. The purpose of this was to increase the level of abstraction within processes. The acronym for the Pcb field is Process Control Block. I've highlighted two fields of interest, one of which will lead directly into the next topic of this article which is threads.
The DirectoryTableBase field is an important field used in memory management and address translation. We won't cover the details of address translation here, although, it should be mentioned that it is performed using what is known as page tables. The field contains the address of first table which is used in address translation.
The ThreadListHead field is similar to the process active head for our active processes, and is the head of the doubly linked list for all the threads associated to our running process.
Code:
6: kd> dt _EPROCESS -y ThreadListHead
nt!_EPROCESS
+0x488 ThreadListHead : _LIST_ENTRY
There is a much easier method for dumping process information and that is through the use of the following debugger command extension:
!process
If we do not specify the address of the process, then it will dump the details of the last process which was running at the time of the crash.
Code:
0: kd> !process
PROCESS [HI]ffff950a37820040[/HI]
SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 001aa000 ObjectTable: ffffc58291002100 HandleCount: 3021.
Image: [HI]System[/HI]
VadRoot ffff950a39f0b850 Vads 125 Clone 0 Private 34. Modified 387903. Locked 64.
DeviceMap ffffc5829101bd80
Token ffffc58291016a10
ElapsedTime 08:48:37.316
UserTime 00:00:00.000
KernelTime 00:03:39.234
QuotaPoolUsage[PagedPool] 0
QuotaPoolUsage[NonPagedPool] 136
Working Set Sizes (now,min,max) (5527, 50, 450) (22108KB, 200KB, 1800KB)
PeakWorkingSetSize 7613
VirtualSize 25 Mb
PeakVirtualSize 33 Mb
PageFaultCount 15055
MemoryPriority BACKGROUND
BasePriority 8
CommitCharge 46
THREAD [HI]ffff950a3790b780[/HI] Cid 0004.000c Teb: 0000000000000000 Win32Thread: 0000000000000000 WAIT: (Executive) KernelMode Non-Alertable
fffff8013bb4b2e0 SynchronizationEvent
THREAD ffff950a378f3700 Cid 0004.0010 Teb: 0000000000000000 Win32Thread: 0000000000000000 WAIT: (Executive) KernelMode Non-Alertable
fffff8013bb4c1e0 Semaphore Limit 0x7fffffff
[...]
Notice some of the similar information we saw in the _EPROCESS and _KPROCESS structures from earlier? The Image field is the name of the process; in our case, it was the system process. We can also see all the current threads associated to this process too, along with, their thread addresses which we which will use in the next section.
Threads:
Threads are where the process execution takes place. Threads are no more than units of execution. Each thread will have two stacks: one of which is for the kernel and one of which is for user mode. We'll explore stacks in the next section. Like with processes, each thread has a _ETHREAD structure and a _KTHREAD structure. Let's firstly dump the _ETHREAD structure:
Code:
6: kd> dt _ETHREAD
nt!_ETHREAD
+0x000 [HI]Tcb[/HI] : _KTHREAD
+0x5e8 CreateTime : _LARGE_INTEGER
+0x5f0 ExitTime : _LARGE_INTEGER
+0x5f0 KeyedWaitChain : _LIST_ENTRY
+0x600 ChargeOnlySession : Ptr64 Void
+0x608 PostBlockList : _LIST_ENTRY
+0x608 ForwardLinkShadow : Ptr64 Void
+0x610 StartAddress : Ptr64 Void
+0x618 TerminationPort : Ptr64 _TERMINATION_PORT
+0x618 ReaperLink : Ptr64 _ETHREAD
+0x618 KeyedWaitValue : Ptr64 Void
+0x620 ActiveTimerListLock : Uint8B
+0x628 ActiveTimerListHead : _LIST_ENTRY
+0x638 [HI]Cid[/HI] : _CLIENT_ID
+0x648 KeyedWaitSemaphore : _KSEMAPHORE
+0x648 AlpcWaitSemaphore : _KSEMAPHORE
+0x668 ClientSecurity : _PS_CLIENT_SECURITY_CONTEXT
+0x670 [HI]IrpList [/HI] : _LIST_ENTRY
+0x680 TopLevelIrp : Uint8B
+0x688 DeviceToVerify : Ptr64 _DEVICE_OBJECT
+0x690 Win32StartAddress : Ptr64 Void
+0x698 LegacyPowerObject : Ptr64 Void
+0x6a0 ThreadListEntry : _LIST_ENTRY
+0x6b0 RundownProtect : _EX_RUNDOWN_REF
+0x6b8 ThreadLock : _EX_PUSH_LOCK
+0x6c0 ReadClusterSize : Uint4B
+0x6c4 MmLockOrdering : Int4B
+0x6c8 CrossThreadFlags : Uint4B
+0x6c8 Terminated : Pos 0, 1 Bit
+0x6c8 ThreadInserted : Pos 1, 1 Bit
+0x6c8 HideFromDebugger : Pos 2, 1 Bit
+0x6c8 ActiveImpersonationInfo : Pos 3, 1 Bit
+0x6c8 HardErrorsAreDisabled : Pos 4, 1 Bit
+0x6c8 BreakOnTermination : Pos 5, 1 Bit
+0x6c8 SkipCreationMsg : Pos 6, 1 Bit
+0x6c8 SkipTerminationMsg : Pos 7, 1 Bit
+0x6c8 CopyTokenOnOpen : Pos 8, 1 Bit
+0x6c8 ThreadIoPriority : Pos 9, 3 Bits
+0x6c8 ThreadPagePriority : Pos 12, 3 Bits
[...]
The Tcb field refers to the thread control block and is a reference to the _KTHREAD structure.
The Cid field is the unique identifier associated with each thread.
The IrpList is a doubly linked list of IRPs which have been generated by this thread. IRPs are I/O request packets which are used by I/O manager to complete various operations such as power management including putting devices in a sleep state.
Now, let's dump the _KTHREAD structure and investigate some of its fields:
Code:
6: kd> dt _KTHREAD
nt!_KTHREAD
+0x000 Header : _DISPATCHER_HEADER
+0x018 SListFaultAddress : Ptr64 Void
+0x020 QuantumTarget : Uint8B
+0x028 InitialStack : Ptr64 Void
+0x030 [HI]StackLimit [/HI] : Ptr64 Void
+0x038 [HI]StackBase[/HI] : Ptr64 Void
+0x040 ThreadLock : Uint8B
+0x048 CycleTime : Uint8B
+0x050 CurrentRunTime : Uint4B
+0x054 ExpectedRunTime : Uint4B
+0x058 [HI]KernelStack[/HI] : Ptr64 Void
+0x060 StateSaveArea : Ptr64 _XSAVE_FORMAT
+0x068 SchedulingGroup : Ptr64 _KSCHEDULING_GROUP
+0x070 WaitRegister : _KWAIT_STATUS_REGISTER
+0x071 Running : UChar
+0x072 Alerted : [2] UChar
+0x074 AutoBoostActive : Pos 0, 1 Bit
+0x074 ReadyTransition : Pos 1, 1 Bit
+0x074 WaitNext : Pos 2, 1 Bit
+0x074 SystemAffinityActive : Pos 3, 1 Bit
+0x074 Alertable : Pos 4, 1 Bit
+0x074 UserStackWalkActive : Pos 5, 1 Bit
+0x074 ApcInterruptRequest : Pos 6, 1 Bit
+0x074 QuantumEndMigrate : Pos 7, 1 Bit
+0x074 UmsDirectedSwitchEnable : Pos 8, 1 Bit
+0x074 TimerActive : Pos 9, 1 Bit
+0x074 SystemThread : Pos 10, 1 Bit
+0x074 ProcessDetachActive : Pos 11, 1 Bit
+0x074 CalloutActive : Pos 12, 1 Bit
+0x074 ScbReadyQueue : Pos 13, 1 Bit
+0x074 ApcQueueable : Pos 14, 1 Bit
+0x074 ReservedStackInUse : Pos 15, 1 Bit
+0x074 UmsPerformingSyscall : Pos 16, 1 Bit
+0x074 TimerSuspended : Pos 17, 1 Bit
[...]
The StackLimit and StackBase fields refer to the end and the beginning of the call stack associated to this thread respectively. The KernelStack field contains a pointer to the beginning of the kernel stack. We can use the addresses referenced by these pointers with the dps command to dump the entire stack of thread.
As before, we can dump the details of a thread in a much more user friendly way using the !thread debugger extension.
!thread
Code:
6: kd> !thread
THREAD [HI]ffff950a3d56a080[/HI] [HI]Cid[/HI] 2650.0be8 Teb: 00000017e3638000 Win32Thread: ffff950a3c0c6550 RUNNING on processor 6
[HI]IRP List:[/HI]
ffff950a42183240: (0006,0478) Flags: 00060043 Mdl: ffff950a3e724b40
Not impersonating
DeviceMap ffffc582aa67e490
Owning Process ffff950a3f462080 Image: OSBuddy64.exe
Attached Process N/A Image: N/A
Wait Start TickCount 2030855 Ticks: 1 (0:00:00:00.015)
Context Switch Count 2818893 IdealProcessor: 6
UserTime 01:04:06.640
KernelTime 00:23:55.015
Win32 Start Address 0x0000000057901dbc
Stack Init ffffe480e018ec90 Current ffffe480e018df50
[HI]Base[/HI] ffffe480e018f000 [HI]Limit[/HI] ffffe480e0189000 Call 0000000000000000
Priority 6 BasePriority 6 PriorityDecrement 0 IoPriority 2 PagePriority 5
Child-SP RetAddr : Args to Child : Call Site
ffffe480`e018d908 fffff801`3b82eb7c : 00000000`0000001e ffffffff`c0000005 fffff805`f2dda7df 00000000`00000000 : nt!KeBugCheckEx
ffffe480`e018d910 fffff801`3b97b98e : ffffe480`e018e110 00000000`00000000 00000000`00000000 00000000`00000000 : nt!KiDispatchException+0x23c
ffffe480`e018dfc0 fffff801`3b979e57 : 00000000`00000000 00000000`0003081c ffff950a`00000000 00000036`b77d3868 : nt!KiExceptionDispatch+0xce
ffffe480`e018e1a0 fffff805`f2dda7df : ffff950a`00000000 00000000`00000000 00000000`00000000 ffffc582`a28fd128 : nt!KiPageFault+0x217 (TrapFrame @ ffffe480`e018e1a0)
ffffe480`e018e330 fffff805`f2ddb92d : ffffe480`e018e450 ffffe480`e018e638 ffff950a`3dfc6b00 fffff805`f2d21a94 : NTFS!NtfsPreRequestProcessingExtend+0x4f
ffffe480`e018e420 fffff805`f240563d : ffff950a`38d49170 ffff950a`42183240 ffff950a`39f46df0 ffff950a`42183628 : NTFS!NtfsFsdRead+0x1dd
ffffe480`e018e6a0 fffff805`f24034d6 : ffffe480`e018e730 00000000`00000003 00000000`0000000d 00000000`00000000 : FLTMGR!FltpLegacyProcessingAfterPreCallbacksCompleted+0x18d
ffffe480`e018e710 fffff801`3b8404a5 : ffff950a`42183260 ffff950a`42183240 ffff950a`3dfc6bf0 fffff801`3b892171 : FLTMGR!FltpDispatch+0xb6
ffffe480`e018e770 fffff801`3b8a5f42 : ffff950a`3e724a40 00000000`3b893da5 ffff950a`3e724aa0 ffff950a`3e724a60 : nt!IoPageReadEx+0x265
ffffe480`e018e7e0 fffff801`3b8a5970 : 00000000`00000003 ffffe480`e018e8b0 00000000`00000000 fffff801`3b870fe9 : nt!MiIssueHardFaultIo+0xb6
ffffe480`e018e830 fffff801`3b891486 : 00000000`c0033333 ffffe480`e018eb00 00000000`00000000 ffffe480`e018ea10 : nt!MiIssueHardFault+0x190
ffffe480`e018e910 fffff801`3b979d72 : ffff950a`3d56a080 000001c2`00000000 ffff950a`3debd310 ffff950a`3debd310 : nt!MmAccessFault+0xc96
ffffe480`e018eb00 00000000`572be790 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!KiPageFault+0x132 (TrapFrame @ ffffe480`e018eb00)
00000000`fb5fe128 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x572be790
The highlighted fields correspond directly to the fields discussed within the _ETHREAD and _KTHREAD structures. You can now also see the call stack for this thread. We'll discuss call stacks in the next section.
Stacks:
Each thread has its own call stack. When we are debugging kernel crash dumps, we are actually examining the kernel version of the thread's call stack.
You can think of a call stack as the operations which the thread has been completing. Call stacks are read from the top to the bottom. The last call is placed at the top and the first call is placed at the bottom. Call stacks follow what is known as LIFO (Last In First Out). This means that the last stack frame to be pushed (inserted) onto the stack, is the first stack frame to be popped (removed) from the stack. Each function call results in a new stack frame being pushed onto the stack, once this function has returned, then the stack frame is popped.
We'll discuss stack frames momentarily.
Firstly, let's dump the call stack for the thread. Using the WinDbg k command will dump the call stack for the current thread context.
Code:
6: kd> k
# Child-SP RetAddr Call Site
[HI]00[/HI] ffffe480`e018d908 [HI]fffff801`3b82eb7c[/HI] [HI]nt!KeBugCheckEx[/HI]
01 ffffe480`e018d910 fffff801`3b97b98e nt!KiDispatchException+0x23c
02 ffffe480`e018dfc0 fffff801`3b979e57 nt!KiExceptionDispatch+0xce
03 ffffe480`e018e1a0 fffff805`f2dda7df nt!KiPageFault+0x217
04 ffffe480`e018e330 fffff805`f2ddb92d NTFS!NtfsPreRequestProcessingExtend+0x4f
05 ffffe480`e018e420 fffff805`f240563d NTFS!NtfsFsdRead+0x1dd
06 ffffe480`e018e6a0 fffff805`f24034d6 FLTMGR!FltpLegacyProcessingAfterPreCallbacksCompleted+0x18d
07 ffffe480`e018e710 fffff801`3b8404a5 FLTMGR!FltpDispatch+0xb6
08 ffffe480`e018e770 fffff801`3b8a5f42 nt!IoPageReadEx+0x265
09 ffffe480`e018e7e0 fffff801`3b8a5970 nt!MiIssueHardFaultIo+0xb6
0a ffffe480`e018e830 fffff801`3b891486 nt!MiIssueHardFault+0x190
0b ffffe480`e018e910 fffff801`3b979d72 nt!MmAccessFault+0xc96
0c ffffe480`e018eb00 00000000`572be790 nt!KiPageFault+0x132
0d 00000000`fb5fe128 00000000`00000000 0x572be790
If examine the stack, we can see four different columns: the stack frame number (#), the stack pointer address (Child-SP), the return address once the call has completed (RetAddr) and the function call being made (Call Site). The k command is the simplest of the WinDbg call stack unwind commands, there are many different variations which dump varying information regarding the call stack, however, the k command will be suffice for this article.
As we can see in the call stack, the last call was to nt!KeBugCheckEx, this the bugcheck exception handler which builds and dispatches our blue screen of death. Each line within the call stack represents an individual stack frame. We can dump a stack frame using the .frame command with the /r switch to dump the registers:
Code:
6: kd> .frame /r 0
00 ffffe480`e018d908 fffff801`3b82eb7c nt!KeBugCheckEx
rax=0000000049c8820f rbx=ffffe480e018e0f8 rcx=[HI]000000000000001e[/HI]
rdx=[HI]ffffffffc0000005[/HI] rsi=ffffe480e018d940 rdi=ffffe480e018e0f8
rip=fffff8013b9704c0 rsp=ffffe480e018d908 rbp=ffffe480e018de40
r8=[HI]fffff805f2dda7df[/HI] r9=[HI]0000000000000000[/HI] r10=0000000000000000
r11=00007ffffffeffff r12=000000000010001f r13=0000000000000000
r14=ffffe480e018e1a0 r15=ffff950a42183200
iopl=0 nv up ei pl zr na po nc
cs=0010 ss=0000 ds=002b es=002b fs=0053 gs=002b efl=00000246
nt!KeBugCheckEx:
fffff801`3b9704c0 48894c2408 mov qword ptr [rsp+8],rcx ss:ffffe480`e018d910=000000000000001e
Now each stack frame will have it's own registers saved and parameters pushed to the stack. The first four parameters to a function call are always pushed to rcx, rdx, r8 and r9 registers respectively on x64 machines. Any other additional parameters are pushed onto the stack itself.
Now, using the previously mentioned function call, we can see this in action by dumping the bugcheck parameters using the .dumpdebug command comparing them to the values stored in the parameters within the call stack frame.
Code:
6: kd> .dumpdebug
----- 64 bit Kernel Bitmap Dump Analysis - Kernel address space is available,
User address space may not be available.
DUMP_HEADER64:
MajorVersion 0000000f
MinorVersion 00003ad7
KdSecondaryVersion 00000000
DirectoryTableBase 00000001`6c054000
PfnDataBase ffffd880`00000000
PsLoadedModuleList fffff801`3bb505e0
PsActiveProcessHead fffff801`3bb4a040
MachineImageType 00008664
NumberProcessors 00000008
BugCheckCode [HI]0000001e[/HI]
BugCheckParameter1 [HI]ffffffff`c0000005[/HI]
BugCheckParameter2 [HI]fffff805`f2dda7df[/HI]
BugCheckParameter3 [HI]00000000`00000000[/HI]
BugCheckParameter4 00000000`49c8820f
[...]
As you can see, the first parameter of the bugcheck is pushed into the rcx register, the second parameter is pushed into the rdx register, and the third and fourth into the r8 and r9 registers. However, you may be a little confused now?
The .dumpdebug command is stating that fourth parameter is 00000000`49c8820f, but the r9 register contains the value of third parameter? This is because the KeBugCheckEx function actually takes the STOP code as the first parameter. The fourth bugcheck parameter is actually the fifth parameter to be passed to the function and can be found on the stack.
We can find this by using the stack pointer address stored in the rsp register, and using the dd command with the appropriate offset.
Code:
6: kd> dd ffffe480e018d908+24 l2
ffffe480`e018d92c [HI]00000000 49c8820f[/HI]
Now, this is just an introduction to processes, threads and the stack; there is much more to learn! Although, I hope this article provides enough information to help you with your debugging efforts. If you wish to learn more about stack, then I throughly recommend you read the CodeMachine article below.
Further Reading:
CodeMachine - Article - X64 Deep Dive
Internals of Windows Thread
Registers (x86)
Last edited: