Windows XP 与 Windows 2000 句柄表(增加个人注解)

“句柄”:是一个32位值,它是在句柄表中的索引,但它的含义丰富些。
“句柄表”:是一个围绕“三层表”概念的表。它有两种,一个是EPROCESS的,一个是PspCidTable的。(EPROCESS的ObjectTable成员是指向HANDLE_TABLE的指针。PspCidTable(未导出)是指向HANDLE_TABLE指针的指针。)

XP与2000对它们的管理有所不同。


一、Windows XP


1、EPROCESS
1)句柄表:

lkd> dt _EPROCESS//rong:显示EPROCESS结构
nt!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER
+0x078 ExitTime : _LARGE_INTEGER
......
+0x0c4 ObjectTable : Ptr32 _HANDLE_TABLE
......

+0x258 Cookie : Uint4B


lkd> dt _HANDLE_TABLE//rong:显示_HANDLE_TABLE结构
nt!_HANDLE_TABLE
+0x000 TableCode : Uint4B
+0x004 QuotaProcess : Ptr32 _EPROCESS
+0x008 UniqueProcessId : Ptr32 Void
......
+0x030 FirstFree : Uint4B
+0x034 LastFree : Uint4B
+0x038 NextHandleNeedingPool : Uint4B
+0x03c HandleCount : Int4B
+0x040 Flags : Uint4B
+0x040 StrictFIFO : Pos 0, 1 Bit


TableCode它的含义比较丰富,它的底两位用来是表示当前的表有几层,它有可能是0、1、2。

(1)如果是0,说明当前只有一层表即第0层,TableCode&(FFFFFFFC)运算的值是指向第0层的指针。第0层是HANDLE_TABLE_ENTRY结构数组,每项占8个字节。
2如果是1,说明当前有两层表即第0层和第1层,TableCode&(FFFFFFFC)运算的值是指向第1层的指针。第1层是PHANDLE_TABLE_ENTRY结构数组,每项指向一个第0层。
3如果是2,说明当前有三层表即第0层、第1层和第2层,TableCode&(FFFFFFFC)运算的值是指向第2层的指针。第2层是PPHANDLE_TABLE_ENTRY结构数组,每项指向一个第1层。


lkd> dt _HANDLE_TABLE_ENTRY//rong:显示_HANDLE_TABLE_ENTRY结构

nt!_HANDLE_TABLE_ENTRY
+0x000 Object : Ptr32 Void
+0x000 ObAttributes : Uint4B
+0x000 InfoTable : Ptr32 _HANDLE_TABLE_ENTRY_INFO
+0x000 Value : Uint4B
+0x004 GrantedAccess : Uint4B
+0x004 GrantedAccessIndex : Uint2B
+0x006 CreatorBackTraceIndex : Uint2B

+0x004 NextFreeTableEntry : Int4B


HANDLE_TABLE_ENTRY是一个包含2个32位(rong:8字节)成员的结构,第1个成员是一个指向“对象头部”的指针(包含一些标志),第2个成员是访问掩码。
其中第1个成员需要说明:windows能保证对象头的分配地址总是8的倍数,所以这32位的对象头的指针低3位肯定都为0,windows将这低3位用作其他用途;因为内核对象分配都在0x80000000以上,所以最高位必须为1,window把这位当作锁位。因此,其实把最低3位置0,最高位置1才是真正指向对象头部。


2)句柄值:

我们在程序设计的时候经常用到句柄,其实它是一个在句柄表中的索引。如图:



3)测试:
lkd> !process 0 0 //rong:获取用户空间的所有的进程的信息
......
PROCESS 83f7e738 SessionId: 0 Cid: 02f8 Peb: 7ffde000 ParentCid: 02b4
DirBase: 0fce8040 ObjectTable: e15b9f38 HandleCount: 378.
Image: csrss.exe
......
PROCESS 83d82020 SessionId: 0 Cid: 0de8 Peb: 7ffdd000 ParentCid: 077c
DirBase: 0fce8360 ObjectTable: e1820510 HandleCount: 84.
Image: WINDBG.EXE


我选择csrss.exe进程。

lkd> !handle 0 1 2f8 //rong:查看PID为2f8的进程的句柄表的所有句柄项的信息
processor number 0, process 000002f8
Searching for Process with Cid == 2f8
PROCESS 83f7e738 SessionId: 0 Cid: 02f8 Peb: 7ffde000 ParentCid: 02b4
DirBase: 0fce8040 ObjectTable: e15b9f38 HandleCount: 389.
Image: csrss.exe
......
Handle table at e1573000 with 389 Entries in use
0004: Object: e1006e70GrantedAccess: 000f0003
......

!handle 0 1 2f8这个命令的意思是查看PID为2f8的进程的句柄表的所有句柄项的信息,更详细的用法查看windbg help,我选择句柄值为4的项进行验证,其中e1006e70是我们要验证的值。


lkd> dt _EPROCESS 83f7e738//rong:查看特定地址83f7e738的eprocess结构内容

nt!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
......
+0x0c4 ObjectTable : 0xe15b9f38 _HANDLE_TABLE
......
+0x255 WorkingSetAcquiredUnsafe : 0 ''

+0x258 Cookie : 0x6102e87b


lkd> dt _handle_table e15b9f38//rong:查看特定地址e15b9f38_HANDLE_TABLE结构内容
nt!_HANDLE_TABLE
+0x000 TableCode : 0xe1573000
......
+0x038 NextHandleNeedingPool : 0x800
+0x03c HandleCount : 381
+0x040 Flags : 0
+0x040 StrictFIFO : 0y0
因为TableCode的低2位是0,所以只有第0层表。句柄值4,所以第0层索引是1(rong:4/4=1)1*8就是在第0层表的偏移。(rong:因为句柄表的第一个值不被使用,做为一个无效值,用来提供给程序员做错误处理使用。)


lkd> dd e1573000+1*8//rong:查看指定 e1573000+1*8地址的内容(第二个d表示DWORD格式,此外还有db(byte)、du(Unicode)、dc(char)等等。)

e1573008 e1006e59 000f0003 e14c64a9 00000003
e1573018 8452530b 00100020 e156d149 000f000f
e1573028 84504569 001f0fff e15b4969 000f0001
e1573038 e156f901 000f000f e1574019 000f000f
e1573048 e14c7cd9 000f000f e152a349 000f001f
......
因此e1006e59&(FFFFFFF8)|(80000000)=e1006e58指向对象头部,指向对象头部的指针加0x18就是指向对象体的指针即e1006e58+18=e1006e70,这个值与上面用!handle显示的值相同。


2.PspCidTable

PspCidTable(未导出)是指向HANDLE_TABLE指针的指针,这是一个特殊的句柄表,保存着所有进程和线程对象的指针:PID(进程ID)和 ThreadID(线程ID)就是在这个句柄表中的索引。它不属于任何进程,被称作内核句柄表。


1)句柄表

和上面讲的几乎一样的,唯一不一样的是:
HANDLE_TABLE_ENTRY的第1个32位成员,它是指向“对象体”的指针,说明同上。


2)句柄

和上面的一样。


3)测试

打开任务管理器:



我们选择winlogon.exe作为验证,它的PID为十进制784,即0x310
lkd> dd PspCidTable L 1//rong:查看指定PspCidTable地址的内容(L 1表示后面的一个字)
80882360 e1001898


lkd> dt _HANDLE_TABLE e1001898 //rong:查看特定地址e1001898_HANDLE_TABLE结构内容

nt!_HANDLE_TABLE
+0x000 TableCode : 0xe30d4001
......
+0x030 FirstFree : 0xf70
+0x034 LastFree : 0xf60
+0x038 NextHandleNeedingPool : 0x1000
+0x03c HandleCount : 395
+0x040 Flags : 1
+0x040 StrictFIFO : 0y1
TableCode低2位的值为1,说明有第0层表和第1层表,e30d4000指像第1层表句柄值0x310即二进制0000 0011 0001 0000,所以第0层表中的索引0xc4rong:0x310(784)/4=0xc4(196)),第1层表的索引为0。

lkd> dd e30d4000+0*4//rong:查看指定e30d4000+0*4地址的内容
e30d4000 e1003000 e30d5000 00000000 00000000
e30d4010 00000000 00000000 00000000 00000000
e30d4020 00000000 00000000 00000000 00000000
e30d4030 00000000 00000000 00000000 00000000
......
lkd> dd e1003000+c4*8//rong:查看指定e1003000+c4*8地址的内容
e1003620 84504581 00000000 83f82911 00000000
e1003630 83f05021 00000000 8450baf9 00000000
e1003640 83f8d021 00000000 83fe1da9 00000000
e1003650 00000000 00000fe8 84642cc1 00000000
......

所以84504581&(FFFFFFF8)|(80000000)= 84504580为指向对象体的指针。


lkd> !object 84504580 //rong:查看地址为84504580 的内核对象
Object: 84504580 Type: (847bbca0) Process
ObjectHeader: 84504568 (old version)
HandleCount: 11 PointerCount: 288


lkd> !process 84504580//rong:!process命令可以打印出活动进程的信息。第一个参数是要打印的EPROCESS的地址,如果指定为0则表示打印所有的进程。第二个参数用于说明打印进程信息的详细级别。指定0则表示打印最简单的信息。

PROCESS 84504580 SessionId: 0 Cid: 0310 Peb: 7ffda000 ParentCid: 02b4
DirBase: 0fce8060 ObjectTable: e1590b00 HandleCount: 507.

Image: winlogon.exe

rong:

输出结果中我可以看到几个重要的字段:
·Cid : 进程id
·Peb : 进程环境块的地址
·ParentCid : 父进程id
·DirBase : 目录表 (用于转换虚拟地址和物理地址PDT)
·ObjectTable : 进程的句柄表


可以看出和我们用任务管理器相符。


二、Windows 2000

从上面的两个测试,我们知道Windows XP的句柄表有时候只有1层,有时候有2层,有时候有3层,其实Windows XP和Windows 2003,只有最底层(第0层)句柄表才在创建进程的时候分配好,而其他的层都是根据需要才创建的。但在Windows 2000三层表总是被分配的:最高层有256项,每项占4个字节,总共1k;中间层有256项,每项占4个字节,总共1k;最低层有256项,每项占8个字节,总共2k。在Windows XP和Windows 2003中,句柄表中包含的表项的数目等于能填满一页的表项数减去一项,此减去的一项被用于处理审计。比如:我的机器一页的大小是0x1000即4k,那么我最高层能有0x1000/4-1=1023个表项,中间层能有0x1000/4-1=1023个表项,最低层能有0x1000/8-1=511个表项。


句柄表:
dt _HANDLE_TABLE
+00 uint32 Flags +04 int32 HandleCount +08 struct _HANDLE_TABLE_ENTRY ***Table
...... +5e byte Size
5
+5f byte Inserted +60 int32 SignalState +64 struct _LIST_ENTRY WaitListHead +64 struct _LIST_ENTRY *Flink +68 struct _LIST_ENTRY *Blink
Table成员的低两位总是为0,它总是指向最高层。


句柄:

它与XP的不同,每个索引都占8位。如图:


其他与XP是一样的!
参考:
1、深入解析Windows操作系统
2、JIURL玩玩Win2k进程线程篇 HANDLE_TABLE_
3、深入理解句柄表

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值