Поиск, где Windows сохраняет свои регистры в переключении контекста

Я искал, где Windows сохраняет свои регистры во время процесса переключения контекста (как регистры в режиме ядра, так и регистры в режиме пользователя.) Затем я нашел этот вопрос, который описывает, что Windows сохраняет текущий контекст в nt!_KTHREAD в следующем поле:

   +0x1b8 WaitPrcb         : Ptr32 _KPRCB

Я могу найти следующее поле в nt!_KPRCB:

   +0x3658 Context          : Ptr32 _CONTEXT

И, как вы знаете, nt!_CONTEXT это структура, которая содержит почти все регистры, необходимые для переключения контекста.

Чтобы найти это местоположение, я настраиваю отладку ядра VMWare с помощью windbg, затем открываю xdbg64 на гостевой машине и подключаюсь к процессу x64, чтобы увидеть текущее состояние регистров и приостанавливаю гостевую машину, используя windbg хоста, и сначала в Windbg я нахожу цель процесс:

    kd> !process 0 0
    ...
PROCESS ffff9387f70d05c0
    SessionId: 1  Cid: 15e4    Peb: 35cf6bd000  ParentCid: 10b4
    DirBase: 48b46000  ObjectTable: ffffba87f0b628c0  HandleCount: <Data Not Accessible>
    Image: example.exe

    ...

Затем находим темы этого процесса:

    kd> !process ffff9387f70d05c0
PROCESS ffff9387f70d05c0
    SessionId: 1  Cid: 15e4    Peb: 35cf6bd000  ParentCid: 10b4
    DirBase: 48b46000  ObjectTable: ffffba87f0b628c0  HandleCount: <Data Not Accessible>
    Image: example.exe
    VadRoot ffff9387f6238750 Vads 1 Clone 0 Private 168. Modified 0. Locked 0.
    DeviceMap ffffba87ef63f230
    Token                             ffffba87e97ec060
    ElapsedTime                       00:16:35.173
    UserTime                          00:00:00.000
    KernelTime                        00:00:00.000
    QuotaPoolUsage[PagedPool]         0
    QuotaPoolUsage[NonPagedPool]      0
    Working Set Sizes (now,min,max)  (0, 0, 0) (0KB, 0KB, 0KB)
    PeakWorkingSetSize                0
    VirtualSize                       79 Mb
    PeakVirtualSize                   79 Mb
    PageFaultCount                    0
    MemoryPriority                    BACKGROUND
    BasePriority                      8
    CommitCharge                      204
    DebugPort                         ffff9387f6952400
    Job                               ffff9387f82b4830

        THREAD **ffff9387f62f1700**  Cid 15e4.08c4  Teb: 00000035cf6be000 Win32Thread: ffff9387f7b64e50 WAIT: (Executive) KernelMode Non-Alertable
FreezeCount 1
            fffffd8ec29cad80  SynchronizationEvent
        Cannot read nt!_KWAIT_BLOCK at 0000000000000000 - error 1
        Not impersonating
        DeviceMap                 ffffba87ef63f230
        Owning Process            ffff9387f70d05c0       Image:         example.exe
        Attached Process          N/A            Image:         N/A
        Wait Start TickCount      4600127        Ticks: 3 (0:00:00:00.046)
        Context Switch Count      1215             
        UserTime                  00:00:00.000
        KernelTime                00:00:00.015
        Win32 Start Address 0x00007ff7e7a22440
        Stack Init fffffd8ec29cbc90 Current fffffd8ec29ca970
        Base fffffd8ec29cc000 Limit fffffd8ec29c6000 Call 0
        Priority 10 BasePriority 8 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5
        Child-SP          RetAddr           Call Site
        fffffd8e`c29ca9b0 00000000`00000000 nt!KiSwapContext+0x76

На последнем шаге я сопоставил вышеуказанный (нить) адрес с nt!_kthread:

    kd> dt nt!_kthread ffff9387f62f1700
   +0x000 Header           : _DISPATCHER_HEADER
   +0x018 SListFaultAddress : (null) 
   +0x020 QuantumTarget    : 0x878eb54
   +0x028 InitialStack     : 0xfffffd8e`c29cbc90 Void
   +0x030 StackLimit       : 0xfffffd8e`c29c6000 Void
   +0x038 StackBase        : 0xfffffd8e`c29cc000 Void
   ...
   +0x2c8 WaitPrcb         : (null) 
   ...

Но, как вы можете видеть WaitPrcb нулевой!

Итак, мои вопросы:

  1. Что не так с моей темой, что ее контекст указывает на null место нахождения? (Или я не в том месте?)
  2. Как я знаю, для каждого потока должно быть два контекста: один, который тратит свою жизнь в режиме пользователя, и другой контекст, который тратит свою жизнь в режиме ядра, поэтому в Windows должно быть два nt!_CONTEXT состав! Где они?

2 ответа

Решение

Я добавляю другой ответ, потому что редактирование более раннего ответа с новыми деталями может привести к путанице

Контекст может быть переключен по нескольким причинам

1) поток уступил и заблокирован в ожидании некоторого ввода (скажем, scanf ())
2) Произошло прерывание, и работающий поток был прерван (исключение: поток с высоким приоритетом стал работоспособным и т. Д. И т. Д.)
3) переход из режима пользователя в режим ядра

предположим, что nt!KiSwapContext - это функция, которая отвечает за переключение контекстов.

чтобы проверить наше предположение или гипотезу, мы можем установить для этой функции условную точку останова для конкретного процесса и записать в журнал

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

в результате мы получим несколько тысяч строк за короткий промежуток времени. Мы будем использовать инструменты wc.exe, sed, grep, awk, sort, uniq, gnuwin32 для анализа вывода текста.

:\>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

каждый перерыв в этом вызове будет выводить что-то вроде этого

:\>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:

мы можем сортировать и получать уникальные события каждого вызова, как это

:\>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

из этого примера данных можно сделать вывод, что контекст, вероятно, был заменен в значительной степени из-за завершения временного интервала, за которым следовала пересылка потока с последующими прерываниями

давайте сначала изучим наибольшее вхождение, чья вызывающая последовательность выглядит следующим образом, поэтому готовый поток ставится в очередь, а контекст переставляется, мы можем видеть, что ebx используется, а ebx представляется структурой (мы можем видеть доступ к members @ offset 0x187 и 0x6a в вызывающей последовательности)

:\>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)

давайте изменим нашу точку останова и остановимся и продолжим вручную с помощью f5 или g, пока мы не достигнем последовательности вызовов QuantumEnd

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 

из регистра мы можем увидеть разборки совпадений последовательности вызовов

ecx, ebx и edi одинаковы (указатель на новую ветку готовая тема)

edx соответствует настройке для толчков (вызов использует одно слово с обратным адресом, поэтому вместо [esp+14] мы проверяем [esp+18]) указатель на текущий поток

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

так как мы подтвердили ebx = поток, который будет новым потоком, мы можем подтвердить, на что указывают смещения 187h и 6ah

kd> .enable_long_status 1
kd> ?? #FIELD_OFFSET(nt!_KTHREAD , WaitReason)
long 0x187
kd> ?? #FIELD_OFFSET(nt!_KTHREAD , WaitIrql)
long 0x6a

мы также можем подтвердить причину ожидания и WaitIrql из заголовочного файла

:\>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

так как мы расшифровали почти все, что мы теперь можем посмотреть в функцию

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

поэтому функция принимает fs:[1c], которая является self.pcr WaitIrql нового потока и вводит nt!SwapContext (), который выполняет фактический обмен

шаг до nt! SwapContext и вы увидите

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)

вот начало

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

nt! SwapContext вызывает эти функции, вызов начала аккумаляции сохраняет регистры с плавающей запятой при условии, что другие регистры сохраняются при необходимости.

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)

задать или начать новую тему с конкретным вопросом, связывающим эту тему

Вы не знаете, что вы пытаетесь сделать, и ваш вопрос немного запутанный, чтобы ответить, почему вы связываете waitprcb с _context

windbgs kb (backtrace стека) обычно показывает адрес trapframe

! process 0 2 {имя процесса} выдаст все потоки в определенном процессе

это будет вывод Dml, просто нажав на адрес потока в выводе, вы увидите стеки вызовов для каждого потока (команда выполнена! Адрес потока)

или вы можете посмотреть на ловушку с dt nt!_ethread Tcb->TrapFrame->*

это то, что вы пытаетесь сделать?

kd> !process 0 2 calc.exe
PROCESS 841fa930  SessionId: 1  Cid: 0228    Peb: 7ffdf000  ParentCid: 022c
    DirBase: 06812000  ObjectTable: 9512f448  HandleCount:  88.
    Image: calc.exe    
        THREAD 841fa648  Cid 0228.061c  Teb: 7ffde000 Win32Thread: fe7196c0 WAIT: 
        THREAD 841f6a48  Cid 0228.067c  Teb: 7ffdd000 Win32Thread: ff8cc918 WAIT: 
        THREAD 84975b48  Cid 0228.05b8  Teb: 7ffdc000 Win32Thread: fe246298 WAIT: 
        **THREAD 841f7850**  Cid 0228.073c  Teb: 7ffdb000 Win32Thread: 00000000 WAIT: 

kd> .shell -ci "!thread 841fa648" grep TrapFrame
8e2e3d1c 772370b4 001eef48 00000000 00000000 nt!KiFastCallEntry+0x12aTrapFrame @ 8e2e3d34)
kd> .shell -ci "!thread 841f6a48" grep TrapFrame
8e21fd18 772370b4 00000002 0166f810 00000001 nt!KiFastCallEntry+0x12aTrapFrame @ 8e21fd34)
kd> .shell -ci "!thread 84975b48" grep TrapFrame
8c65fd18 772370b4 00000001 026cfde0 00000001 nt!KiFastCallEntry+0x12aTrapFrame @ 8c65fd34)
kd> .shell -ci **"!thread 841f7850"** grep TrapFrame
8e2d3d18 772370b4 00000003 00376558 00000001 nt!KiFastCallEntry+0x12aTrapFrame @ 8e2d3d34)

kd> **!thread 841f7850**
ChildEBP RetAddr  Args to Child              
8e2d3760 8286dd75 841f7850 82937f08 82934d20 nt!KiSwapContext+0x26 (FPO:  [0,0,4])
8e2d3798 8286cbd3 841de4a0 841f7850 841f7964 nt!KiSwapThread+0x266
8e2d37c0 82868c59 841f7850 841f7910 00000000 nt!KiCommitThreadWait+0x1df
8e2d393c 82a11a89 00000003 8e2d3a74 00000001 nt!KeWaitForMultipleObjects+0x535
8e2d3bc8 82a117f6 00000003 8e2d3c00 00000001 nt!ObpWaitForMultipleObjects+0x262
8e2d3d18 8284787a 00000003 00376558 00000001 nt!NtWaitForMultipleObjects+0xcd
8e2d3d18 772370b4 00000003 00376558 01 nt!KiFastCallEntry+0x12a **TrapFrame @ 8e2d3d34**)
WARNING: Frame IP not in any known module. Following frames may be wrong.
0258fd48 00000000 00000000 00000000 00000000 0x772370b4

kd> dt nt!_ETHREAD Tcb->TrapFrame 841f7850
   +0x000 Tcb            : 
      +0x128 TrapFrame      : **0x8e2d3d34 _KTRAP_FRAME**
Другие вопросы по тегам