在windbg中查看进程、线程、CPU

此处先讨论用户态调试

.process 可以查看当前 当前进程PEB的地址

0:000> .process
Implicit process is now 01007000
0:000> dt _PEB 01007000
ntdll!_PEB
     +0x000 InheritedAddressSpace : 0 ''
     +0x001 ReadImageFileExecOptions : 0 ''
     +0x002 BeingDebugged    : 0x1 ''   表明当前程序处于调试状态。  ~~~

 

查看当前线程

0:000> .thread
Implicit thread is now 0100a000
0:000> dt _TEB 0100a000
ntdll!_TEB
     +0x000 NtTib            : _NT_TIB    包含ExceptionList  SEH
     +0x01c EnvironmentPointer : (null) 
     +0x020 ClientId         : _CLIENT_ID    包括进程ID和线程ID   +0x028 ActiveRpcHandle : (null) 
     +0x02c ThreadLocalStoragePointer : 0x0100a02c Void
     +0x030 ProcessEnvironmentBlock : 0x01007000 _PEB     进程PEB  ~~~

TEB的起始地址处为_NT_TIB结构,所以使用如下命令观察_NT_TIB

0:000> dt _NT_TIB 0100a000
ntdll!_NT_TIB
     +0x000 ExceptionList    : 0x0137f318 _EXCEPTION_REGISTRATION_RECORD
     +0x004 StackBase        : 0x01380000 Void
     +0x008 StackLimit       : 0x0137d000 Void
     +0x00c SubSystemTib     : (null) 
     +0x010 FiberData        : 0x00001e00 Void
     +0x010 Version          : 0x1e00
     +0x014 ArbitraryUserPointer : (null) 
     +0x018 Self             : 0x0100a000 _NT_TIB   指向了_NT_TIB 起始处,即_TEB起始处。

ExceptionList : 0x0137f318   指向栈上的SEH框架。

 

!exchain可以遍历栈上的SEH链表

 

在x86下使用fs段寄存器寻找TEB信息,如下

0:000> dd fs:[0]
0053:00000000  0137f318 01380000 0137d000 00000000
0053:00000010  00001e00 00000000 0100a000 00000000

偏移0x18处为0100a000,即_TEB的起始地址,也是_NT_TIB的起始地址。

 

在内核态调试器中

使用如下命令观察进程 EPROCESS,每个进程在内核中都有一个_EPROCESS结构体

kd> .process
Implicit process is now 85be6658
kd> dt _EPROCESS 85be6658
nt!_EPROCESS
     +0x000 Pcb              : _KPROCESS   起始地址处为KPROCESS
     +0x098 ProcessLock      : _EX_PUSH_LOCK
     +0x0a0 CreateTime       : _LARGE_INTEGER 0x01d8dab8`f5df4002
     +0x0a8 ExitTime         : _LARGE_INTEGER 0x0
     +0x0b0 RundownProtect   : _EX_RUNDOWN_REF


  +0x0b4 UniqueProcessId : 0x00000004 Void   进程ID,可知当前进程PID为4,为system进程,操作系统虚拟的一个进程,用来执行一些系统服务以及加载卸载驱动

  ~~

  +0x1a8 Peb : (null)  用户态的PEB为NULL,没有用户态进程环境块,没有实体exe,是操作系统虚拟出来的

  ~~

KPROCESS中包含

kd> dt _KPROCESS 85be6658
nt!_KPROCESS
     +0x000 Header           : _DISPATCHER_HEADER
     +0x010 ProfileListHead  : _LIST_ENTRY [ 0x85be6668 - 0x85be6668 ]
     +0x018 DirectoryTableBase : 0x185000   页目录基址表,非常重要的一个属性,线程切换时,会使用此值赋值给cr3寄存器

  ~~

 

使用如下命令观察线程ETHREAD,每个线程在内核都有一个_ETHREAD结构体

kd> .thread
Implicit thread is now 83f3fd00
kd> dt _ETHREAD 83f3fd00
nt!_ETHREAD
     +0x000 Tcb              : _KTHREAD      Tcb变量为_KTHREAD结构体,与_ETHREAD结构体起始地址一样
     +0x200 CreateTime       : _LARGE_INTEGER 0x0  ~~
kd> dt _KTHREAD 83f3fd00
nt!_KTHREAD
     +0x000 Header           : _DISPATCHER_HEADER

    内部保存对象链表,当线程满足某些状态时,就会去设置这些对象,内核中每一个可等待对象中都保存了一个_DISPATCHER_HEADER这样的结构体变量,当自身满足条件时,就去触发链表中的对象。
 

+0x010 CycleTime        : 0x0000207b`2199f615
     +0x018 HighCycleTime    : 0x207b
     +0x020 QuantumTarget    : 0x00000008`9a6400b8
     +0x028 InitialStack     : 0x83f35ed0 Void
     +0x02c StackLimit       : 0x83f33000 Void
     +0x030 KernelStack      : 0x83f35c1c Void

在内核态中还可以使用如下命令观察当前CPU

kd> !pcr
  KPCR for Processor 0 at 80b96000:
      Major 1 Minor 1
	  NtTib.ExceptionList: 83f3509c   内核中的SEH链表
	      NtTib.StackBase: 00000000
	     NtTib.StackLimit: 00000000
	   NtTib.SubSystemTib: 80b93c00
	        NtTib.Version: 013cc21a
	    NtTib.UserPointer: 00000001
	        NtTib.SelfTib: 00000000	              SelfPcr: 80b96000
	                 Prcb: 80b96120   KPCR结构体中的prcb,距离KPCR偏离了0x120
	                 Irql: 0000001f
	                  IRR: 00000000
	                  IDR: ffffffff
	        InterruptMode: 00000000
	                  IDT: 80b93000
	                  GDT: 80b93800
	                  TSS: 80b93c00	        CurrentThread: 83f3fd00  当前线程
	           NextThread: 00000000   下一个即将执行的线程
	           IdleThread: 83f3fd00  空闲线程

  DpcQueue:   处于DPC队列中需要执行的,目前没有需要执行的DPC

每一个CPU都有一个KPCR结构体,地址为上面获取到的地址 80b96000

kd> dt _KPCR 80b96000
nt!_KPCR
     +0x000 NtTib            : _NT_TIB    _NT_TIB结构体,内部保存SEH链表,下面一行也是SEH链表,指的是同一个变量
     +0x000 Used_ExceptionList : 0x83f3509c _EXCEPTION_REGISTRATION_RECORD
     +0x004 Used_StackBase   : (null) 
     +0x008 Spare2           : (null) 
     +0x00c TssCopy          : 0x80b93c00 Void
     +0x010 ContextSwitches  : 0x13cc21a
     +0x014 SetMemberCopy    : 1
     +0x018 Used_Self        : (null) 
     +0x01c SelfPcr          : 0x80b96000 _KPCR
     +0x020 Prcb             : 0x80b96120 _KPRCB  ~~
  +0x120 PrcbData         : _KPRCB  从0x120偏移起始的地方都是_KPRCB的数据
kd> dt _NT_TIB 80b96000
nt!_NT_TIB
     +0x000 ExceptionList    : 0x83f3509c _EXCEPTION_REGISTRATION_RECORD
     +0x004 StackBase        : (null) 
     +0x008 StackLimit       : (null) 
     +0x00c SubSystemTib     : 0x80b93c00 Void
     +0x010 FiberData        : 0x013cc21a Void
     +0x010 Version          : 0x13cc21a
     +0x014 ArbitraryUserPointer : 0x00000001 Void
     +0x018 Self             : (null)  这块不要参考,要参考上面的KPCR结构体

 

在内核中使用fs寄存器来寻找当前线程,从上面KPCR结构体中可以看到偏移0x1c的位置是SelfPcr ,就是KPCR的起始地址,使用如下命令

kd> dd fs:[0]
0030:00000000  83f3509c 00000000 00000000 80b93c00
0030:00000010  013cc21a 00000001 00000000 80b96000  是最后一个数据

偏移0x1c为上面第二行最后一个数据80b96000,这样就可以找到KPCR结构体的地址。

当前线程需要从KPCR的KPRCB结构体中找:

kd> dt _KPCR 80b96000
  ~~~
  +0x120 PrcbData : _KPRCB

而KPRCB中内容如下:

kd> dt _KPRCB 80b96000+0x120
nt!_KPRCB
     +0x000 MinorVersion     : 1
     +0x002 MajorVersion     : 1
     +0x004 CurrentThread    : 0x83f3fd00 _KTHREAD    这就是cpu当前运行的线程ETHREAD结构体
     +0x008 NextThread       : (null) 
     +0x00c IdleThread       : 0x83f3fd00 _KTHREAD

可以看到CurrentThread变量距离_KPRCB偏移0x04,而_KPRCB 距离_KPCR偏移为0x120,而FS指向了KPCR,所以使用FS:[0x124]即可获取到当前线程的_KTHREAD结构体,如下:

kd> u nt!PsGetCurrentThread 
nt!PsGetCurrentThread:
  83e39f02 64a124010000    mov     eax,dword ptr fs:[00000124h]
  83e39f08 c3              ret

 

获取当前进程,如下:

kd> u nt!PsGetCurrentProcess
nt!PsGetCurrentProcess:
  83e87491 64a124010000    mov     eax,dword ptr fs:[00000124h]  获取_ETHREAD
  83e87497 8b4050          mov     eax,dword ptr [eax+50h]  偏移0x50取出_KPROCESS
  83e8749a c3              ret 
kd> dt _KTHREAD 0x83f3fd00 
nt!_KTHREAD
     +0x000 Header           : _DISPATCHER_HEADER
  ~~
     +0x039 Running          : 0x1 ''
     +0x03a Alerted          : [2]  ""
  ~~
     +0x040 ApcState         : _KAPC_STATE
     +0x040 ApcStateFill     : [23]  "@???"  +0x057 Priority         : 0 ''
  ~~

上文nt!PsGetCurrentProcess中通过 fs:[00000124h]  获取_ETHREAD,也就是_KTHREAD的地址,然后偏移0x50,可以看到0x50已经处于ApcState 变量中,是_KAPC_STATE结构体,继续查看

kd> dt _KAPC_STATE 0x83f3fd00+0x40
nt!_KAPC_STATE
     +0x000 ApcListHead      : [2] _LIST_ENTRY [ 0x83f3fd40 - 0x83f3fd40 ]
     +0x010 Process          : 0x85be6658 _KPROCESS   进程KPROCESS地址,也是EPROCESS的地址。
     +0x014 KernelApcInProgress : 0 ''
     +0x015 KernelApcPending : 0 ''
     +0x016 UserApcPending   : 0 ''

所以nt!PsGetCurrentProcess可以获取到EPROCESS。

 

总结一下:

在x86的用户态下fs寄存器指向TEB结构体,包含SEH链表,可以找到PEB

在x86的内核态下fs寄存器指向KPCR结构体,包含SEH链表,可以找到ETHREAD(KTHREAD),进而找到EPROCESS(KPROCESS)。

 

在x64下,使用gs寄存器指向了以上内容。

 

TEB中保存上一个错误

kd> dt _TEB -y last
ntdll!_TEB
     +0x034 LastErrorValue : Uint4B   GetLastError()函数就是通过fs寄存器找到TEB,然后取这个值。
     +0xbf4 LastStatusValue : Uint4B