在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