前言
Windows操作系统的内核代码使用C和汇编进行编写,内核中最重要的就是一些结构体和算法,了解了这些结构体后可以帮助我们对内核的代码进行分析。本文将介绍四个非常重要的结构体。
TRAP_FRAME
结构体是由操作系统维护的一块数据,在内核层用来保存用户层现场的结构体(例如线程在用户层进行系统服务函数调用、中断、异常和在内核层执行APC调用时线程都要暂时将用户层的现场保存在TRAP_FRAME中),该结构体地址由每一个线程的KTHREAD.TrapFrame成员指定,Windbg中使用
dt nt!_KTRAP_FRAME
查看其结构。
EPROCESS
结构体是Windows系统中的每一个进程在内核层描述进程信息的结构体, 这个结构体包含了进程所有的重要的信息。在Winbbg中 使用
dt _EPROCESS
查看EPROCESS结构体的信息。
ETHREAD
结构体,每一个线程在内核层都有一个对应的结构体,该结构体保存了与线程相关的所有信息。在Windbg中 使用dt _ETHREAD查看ETHREAD结构体的信息。
KPCR
结构体,描述了当前CPU的各种状态,包括当前CPU正在运行的线程的部分经常使用的信息,每一个CPU都有一个对应的结构体,Windbg中通过
dd KeNumberProcessors
查看当前操作系统的CPU数量,通过d
d KiProcessorBlock Lxxx
查看xxx个KPCR结构体的地址。
注意
: dd KiProcessorBlock 显示的数据对应的是KPCR结尾的地址,应当减去KPCR结构体的大小。
他们的结构关系如下图所示:
注意
:teb.NtTib指向的地址不等于KPCR.NtTib
Trap_Frame
使用Windbg查看TRAP_FRAME的结构:
使用图形来进行表述:
值得注意的是:
在保护模式下,并不使用最下方的紫色4个成员。
在中断模式下如果发生提权,中断门会将用户层的5个寄存器压入对应的5个成员中(Eip,SegCs,EFlags、HardwareEsp和HardwareSegSs);如果未发生提权只会将用户层的3个成员压入对应的成员中(Eip,SegCs和EFlags)。
EPROCESS(进程内核对象)
EPROCESS结构体包含了KPROCESS结构体,使用Windbg查看EPROCESS的结构体如下
对EPROCESS结构体中的重要成员进行说明(使用偏移进行说明到底在KPROCESS中还是在EPROCESS中)
01) +0x000
Header
: _DISPATCHER_HEADER //“可等待”对象,比如Mutex互斥体、Event事件等(WaitForSingleObject)
02) +0x018
DirectoryTableBase
: [2] Uint4B //页目录表的基址
03) +0x020
LdtDescriptor
: _KGDTENTRY
+0x028
Int21Descriptor
: _KIDTENTRY //历史遗留,16位Windows 段选择子不够 每个进程都有一个LDT表,Int21Descriptor 是 DOS下要用的
04) +0x038
KernelTime
: Uint4B
+0x03c
UserTime
: Uint4B //统计信息 记录了一个进程在内核模式/用户模式下所花的时间
05) +0x05c
Affinity
: Uint4B //规定进程里面的所有线程能在哪些个CPU上跑,第i位为1表示能在第i个核上运行。Xp是4个字节共32位,所以最多32核 Windows64位就64核。当设置运行的CPU不存在时进程无法运行。64位的操作系统将不适用。
06) +0x062
BasePriority
: Char //基础优先级或最低优先级 该进程中的所有线程最起码的优先
07) +0x070
CreateTime
: _LARGE_INTEGER
+0x078
ExitTime
: _LARGE_INTEGER//进程的创建/退出时间
08) +0x084
UniqueProcessId
: Ptr32 Void //进程的编号(就是在任务管理器中的PID)
09) +0x088
ActiveProcessLinks
: _LIST_ENTRY//双向链表,将所有的活动进程都连接在一起,构成了一个链表,进程结构体们彼此拴着各自的腰。而PsActiveProcessHead符号指向全局链表头。如下图所示表述了活动的进程
11) +0x090
QuotaUsage
: [3] Uint4B //物理页相关的统计信息
12) +0x09c
QuotaPeak
: [3] Uint4B //物理页相关的统计峰值信息
13) +0x0a8
CommitCharge
: Uint4B
+0x0ac
PeakVirtualSize
: Uint4B
+0x0b0
VirtualSize
: Uint4B //虚拟内存相关的统计信息
14) +0x11c
VadRoot
: Ptr32 Void //一颗平衡二叉树,标识0-2G哪些地址没占用了,记录了模块映射信息。
15) +0x0bc
DebugPort
: Ptr32 Void
+0x0c0
ExceptionPort
: Ptr32 Void //调试相关,如果被调试了DebugPort保存调试对象的地址
16) +0x0c4
ObjectTable
: Ptr32 _HANDLE_TABLE//句柄表,记录了被当前进程打开的所有的句柄
17) +0x174
ImageFileName
: [16] Uchar//进程镜像文件名 最多16个字节
18) +0x1a0
ActiveThreads
: Uint4B//活动线程的数量
19) +0x1b0
Peb
: Ptr32 _PEB//PEB((Process Environment Block 进程环境块):进程在3环的一个结构体,里面包含了进程的模块列表、是否处于调试状态等信息。
PEB(进程环境块)
PEB是处在用户层的一个数据结构,记录了进程的相关信息,该结构留在用户层主要是减少频繁的进入内核层查询数据,以提高系统的运行速度。PEB结构体成员如下图所示,部分重要成员已在图中进行说明。
ETHREAD(线程内核对象)
ETHREAD结构体包含了KTHREAD结构体,使用Windbg查看ETHREAD结构体如下:
对ETHREAD结构体中的重要成员进行说明(使用偏移进行说明到底在KTHREAD中还是在ETHREAD中)
01) +0x000
Header
: _DISPATCHER_HEADER//“可等待”对象,比如Mutex互斥体、Event事件等(WaitForSingleObject)
02) +0x018
InitialStack
: Ptr32 Void
+0x01c
StackLimit
: Ptr32 Void
+0x028
KernelStack
: Ptr32 Void//与线程切换相关
03) +0x020
Teb
: Ptr32 Void //TEB,Thread Environment Block,线程环境块。大小4KB,位于用户地址空间。线程在用户层FS:[0] = TEB,线程在内核层FS[0]=KPCR(线程在3环时FS[0]指向它的teb,在0环时FS[0]指向KPCR结构体)
04) +0x02c
DebugActive
: UChar//如果值为-1,将不能使用调试寄存器:Dr0 – Dr7
05) +0x034
ApcState
: _KAPC_STATE
+0x0e8
ApcQueueLock
: Uint4B
+0x138
ApcStatePointer
: [2] Ptr32 _KAPC_STATE
+0x14c
SavedApcState
: _KAPC_STATE//与APC相关
06) +0x02d
State
: UChar //线程状态:就绪、等待还是运行
07) +0x06c
BasePriority
: Char//其初始值是所属进程的BasePriority值(KPROCESS->BasePriority),以后可以通过KeSetBasePriorityThread()函数重新设定
08) +0x070
WaitBlock
: [4] _KWAIT_BLOCK//记录了当前线程在进行同步时等待了哪些个对象(WaitForSingleObject)
09) +0x0e0
ServiceTable
: Ptr32 Void //指向系统服务表基址(Windows vista后已弃用)
10) +0x134
TrapFrame
//进0环时保存环境
11) +0x140
PreviousMode
: Char//某些内核函数会判断程序是0环调用还是3环调用的
12) +0x1b0
ThreadListEntry
: _LIST_ENTRY//双向链表 一个进程所有的线程 都挂在一个链表中挂的就是这个位置,一共有两个这样的链表
13) +0x1ec
Cid
: _CLIENT_ID //线程ID
14) +0x220
ThreadsProcess
: Ptr32 _EPROCESS//指向自己所属进程的EPROCESS地址
15) +0x22c
ThreadListEntry
: _LIST_ENTRY//是一个双向链表,一个进程所有的线程都挂在这个链表中,挂的就是这个位置,一共有两个这样的链表。(如下图所示,进程的所有线程通过链表拴住各自的腰)
KPCR(CPU控制区)
简介:
1) 当线程从3环进入0环时,FS:[0]指向KPCR(3环时FS:[0] 指向TEB结构体)
2) 每个CPU都有一个KPCR结构体(一个核一个)
3) KPCR中存储了CPU本身要用的一些重要数据:GDT、IDT以及线程相关的一些信息。(只是为了快而使用KPCR),在Windbg中使用命令dt _kpcr查看KPCR结构体的成员,如下图所示
KPCR内的重要成员已在下图中进行说明
转载于:https://www.cnblogs.com/wf751620780/p/10588949.html