I am Adding Another Answer because editing the earlier answer with new details would make it confusing
the Context can be switched due to several reasons
1) the thread has ceded and is blocked waiting for some input (lets say scanf())
2) An Interrupt occurred and the running thread was interrupted (exception , high priority thread became runnable etc etc)
3) a user mode to kernel mode transition
lets suppose nt!KiSwapContext is the function that is responsible for switching the contexts.
to verify our supposition or hypothesis we can set a process specific conditional breakpoint on that function and log
debugger win 7 sp1 32 bit physical machine
debuggee win 7 sp1 32 bit vm
transport serial pipe
breakpoint list bl output we have one processes specific conditional bp
condition print the backward disassembly at the return address on stack
print callstack and continue execution
0 e Disable Clear 8288bf00 0001 (0001) nt!KiSwapContext "ub @$ra;kb;gc"
Match process data 842fe7d0
kd> g
the output is several thousand lines in a short period we will use wc.exe , sed , grep , awk , sort , uniq , gnuwin32 tools to analyze the text output
:\>wc -l swappy.txt
2109 swappy.txt
:\>grep debuggee swappy.txt
Debugger (not debuggee) time: Thu Mar 15 13:03:14.047 2018
Debugger (not debuggee) time: Thu Mar 15 13:06:20.077 2018
:\>grep call.*nt!KiSwapContext swappy.txt | wc -l
153
the output is 2109 lines in 3 minutes of trial time and nt!KiSwapContext has
been called 153 times during this time period for this specific process
each break on those call would output some thing like this
:\>head -n 23 swappy.txt | tail -n 16
kd> g
nt!KiQuantumEnd+0x2ca:
828b976a 8bd6 mov edx,esi
828b976c 8bcb mov ecx,ebx
828b976e c683870100001e mov byte ptr [ebx+187h],1Eh
828b9775 e87ed3faff call nt!KiQueueReadyThread (82866af8)
828b977a 8b542414 mov edx,dword ptr [esp+14h]
828b977e 8bcb mov ecx,ebx
828b9780 c6436a01 mov byte ptr [ebx+6Ah],1
828b9784 e87727fdff call nt!KiSwapContext (8288bf00)
# ChildEBP RetAddr Args to Child
00 80df94d0 828b9789 dcf83678 9601a27a 82959c00 nt!KiSwapContext
WARNING: Process directory table base 16DAC000 doesn't match CR3 00185000
WARNING: Process directory table base 16DAC000 doesn't match CR3 00185000
01 00000000 00000000 00000000 00000000 00000000 nt!KiQuantumEnd+0x2e9
nt!KiSwapThread+0x256:
we can sort and get the unique occurances of each call like this
:\>grep -B10 call.*nt!KiSwapContext swappy.txt | grep +.*: | sort | uniq
nt!KiExitDispatcher+0x123:
nt!KiQuantumEnd+0x2ca:
nt!KiSwapThread+0x256:
:\>grep -B10 call.*nt!KiSwapContext swappy.txt | grep +.*: | sort | grep Exit | wc -l
12
:\>grep -B10 call.*nt!KiSwapContext swappy.txt | grep +.*: | sort | grep Quant | wc -l
101
:\>grep -B10 call.*nt!KiSwapContext swappy.txt | grep +.*: | sort | grep SwapThread | wc -l
40
we can infer from this sample data that the context was likely swapped largely due to time slice completion
followed by thread ceding followed by interrupts
lets study the largest occurrance first whose calling sequence looks like this
so a ready thread is queued and the context is swapped
we can see ebx is being used and ebx appears to be a structure (we can see the members @ offset 0x187 and 0x6a being accessed in the calling sequence)
:\>grep -m 3 -B10 call.*nt!KiSwapContext swappy.txt | grep -m 1 -A 10 +.*:
nt!KiQuantumEnd+0x2ca:
828b976a 8bd6 mov edx,esi
828b976c 8bcb mov ecx,ebx
828b976e c683870100001e mov byte ptr [ebx+187h],1Eh
828b9775 e87ed3faff call nt!KiQueueReadyThread (82866af8)
828b977a 8b542414 mov edx,dword ptr [esp+14h]
828b977e 8bcb mov ecx,ebx
828b9780 c6436a01 mov byte ptr [ebx+6Ah],1
828b9784 e87727fdff call nt!KiSwapContext (8288bf00)
lets modify our breakpoint and stop and continue manually with f5 or g until we reach a QuantumEnd call sequence
kd> bp /p 842fe7d0 nt!KiSwapContext ".printf \"%y\n\" , @$ra"
breakpoint 0 redefined
kd> g
nt!KiExitDispatcher+0x140 (8288be87) nt!KiSwapContext:
8288bf00 83ec10 sub esp,10h
kd> g
nt!KiQuantumEnd+0x2e9 (828b9789) nt!KiSwapContext:
8288bf00 83ec10 sub esp,10h
kd> r
eax=00000000 ebx=84e50b40 ecx=84e50b40 edx=84304030 esi=82959d20 edi=84e50b40
eip=8288bf00 esp=8c691b4c ebp=8c691b88
from the register we can see the disassembly of the call sequence matches
ecx , ebx & edi are same (pointer to new thread a Ready Thread )
edx matches adjusting for pushes (call uses one dword a return address so instead of [esp+14] we check [esp+18] ) pointer to current thread
esi = prcb
kd> ? dwo(@esp+18)
Evaluate expression: -2077212624 = 84304030
kd> ? @$thread
Evaluate expression: -2077212624 = 84304030
kd> ? edx
Evaluate expression: -2077212624 = 84304030
kd> ?? @$prcb == (int *)(@esi)
bool true
kd> ? @$prcb ; ? @esi
Evaluate expression: -2104124128 = 82959d20
Evaluate expression: -2104124128 = 82959d20
kd> ? @ecx;? @ebx;? @edi;!thread @ebx 0
Evaluate expression: -2065364160 = 84e50b40
Evaluate expression: -2065364160 = 84e50b40
Evaluate expression: -2065364160 = 84e50b40
THREAD 84e50b40 Cid 0174.01ec Teb: 7ffd9000 Win32Thread: ff9461a0 READY on processor 0
since we confirmed ebx = the thread that is going to be the new thread
we can confirm what the 187h and 6ah offsets point to
kd> .enable_long_status 1
kd> ?? #FIELD_OFFSET(nt!_KTHREAD , WaitReason)
long 0x187
kd> ?? #FIELD_OFFSET(nt!_KTHREAD , WaitIrql)
long 0x6a
we can also confirm the Wait Reason and WaitIrql from Header File
:\>grep WaitReason "c:\Program Files\Windows Kits\10\Include\10.0.16299.0\km\wdm.h"
MaximumWaitReason
_In_ _Strict_type_match_ KWAIT_REASON WaitReason,
_In_ _Strict_type_match_ KWAIT_REASON WaitReason,
:\>grep -n KWAIT_REASON "c:\Program Files\Windows Kits\10\Include\10.0.16299.0\km\wdm.h"
20139:typedef enum _KWAIT_REASON {
20181:} KWAIT_REASON;
20925: _In_ _Strict_type_match_ KWAIT_REASON WaitReason,
20941: _In_ _Strict_type_match_ KWAIT_REASON WaitReason,
:\>awk "NR==20139+0x1f" "c:\Program Files\Windows Kits\10\Include\10.0.16299.0\km\wdm.h"
WrQuantumEnd,
:\>grep -n define.*APC_LEVEL "c:\Program Files\Windows Kits\10\Include\10.0.16299.0\km\wdm.h"
175:#define APC_LEVEL 1 // APC interrupt level
since we have decipherd almost everything we can now look into the function
kd> uf .
nt!KiSwapContext:
8288bf00 83ec10 sub esp,10h
8288bf03 895c240c mov dword ptr [esp+0Ch],ebx
8288bf07 89742408 mov dword ptr [esp+8],esi
8288bf0b 897c2404 mov dword ptr [esp+4],edi
8288bf0f 892c24 mov dword ptr [esp],ebp
8288bf12 648b1d1c000000 mov ebx,dword ptr fs:[1Ch]
8288bf19 8bf9 mov edi,ecx
8288bf1b 8bf2 mov esi,edx
8288bf1d 0fb64f6a movzx ecx,byte ptr [edi+6Ah]
8288bf21 e87a010000 call nt!SwapContext (8288c0a0)
8288bf26 8b2c24 mov ebp,dword ptr [esp]
8288bf29 8b7c2404 mov edi,dword ptr [esp+4]
8288bf2d 8b742408 mov esi,dword ptr [esp+8]
8288bf31 8b5c240c mov ebx,dword ptr [esp+0Ch]
8288bf35 83c410 add esp,10h
8288bf38 c3 ret
so the function takes fs:[1c] which is self.pcr the WaitIrql of the new thread and enters the nt!SwapContext () which does the actual swap
step until the nt!SwapContext and you will see
kd>
nt!KiSwapContext+0x21:
8288bf21 e87a010000 call nt!SwapContext (8288c0a0)
kd> r
eax=00000000 ebx=82959c00 ecx=00000001 edx=84304030 esi=84304030 edi=84e50b40
eip=8288bf21 esp=8c691b3c ebp=8c691b88 iopl=0 nv up ei ng nz na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000286
nt!KiSwapContext+0x21:
8288bf21 e87a010000 call nt!SwapContext (8288c0a0)
here is a start
kd> r
eax=00000000 ebx=82959c00 ecx=00000001 edx=84304030 esi=84304030 edi=84e50b40
eip=8288c0a0 esp=8c691b38 ebp=8c691b88 iopl=0 nv up ei ng nz na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000286
nt!SwapContext:
8288c0a0 807e3900 cmp byte ptr [esi+39h],0 ds:0023:84304069=00
kd> ?? #FIELD_OFFSET( nt!_KTHREAD , Running)
long 0x39
kd> $$ checks if the current thread is running if it is running it stops it
with a pause if it is not running it sets the running member clears
interrupts updates the counters
kd> it is a big function check it out to see what registers are pushed , copied , moved to where
the nt!SwapContext calls these functions the begin accumalation call saves floating point registers under a condition
other registers are saved as necessary
nt!SwapContext (8288c0a0)
call to hal!HalRequestSoftwareInterrupt (82820258)
call to nt!KiBeginCounterAccumulation (8290d6a7)
call to nt!PsCheckThreadCpuQuota (829263f0)
call to nt!EtwTraceContextSwap (82847de8)
call to nt!KeBugCheckEx (8290940a)
ask or start a new thread with specific question linking this thread