Skip to content

Latest commit

 

History

History
900 lines (788 loc) · 32.3 KB

200912061349.txt.md

File metadata and controls

900 lines (788 loc) · 32.3 KB

标题: Windows句柄表

http://scz.617.cn/windows/200912061349.txt

简单的学习笔记,主要参看了jiurl的文章,原始链接早失效了,用"jiurl 句柄表" 做关键字随便一搜,多的是转载。以前没折腾过这个,下文可能有低级错误存在,请 指正。

Win32 API程序员面对的句柄、PID、TID等等本质上是一种东西,这32-bits中的某些 位用以指明某数组的下标,共有3个不同的数组,这32-bits中也包含了3个不同的下 标。一般将这三个数组称为2级表、1级表、0级表,它们之间的关系是:


/*

  • 2级表的元素是指向1级表的指针 / Array_level_2[] = // 最多一个2级表 { pointer to Array_level_1[], // 可以有很多1级表 pointer to Array_level_1[], ... ... pointer to Array_level_1[] }; /
  • 1级表的元素是指向0级表的指针 / Array_level_1[] = { pointer to Array_level_0[], // 可以有很多0级表 pointer to Array_level_0[], ... ... pointer to Array_level_0[] }; /
  • 0级表的元素"包含"指向对象的指针。
  • 进程相关的0级表这里包含的指针指向对象头,PspCidTable的0级表这里包含的指
  • 针指向对象体。 */ Array_level_0[] = // 至少一个0级表 { pointer to Object, // 有很多对象 pointer to Object, ... pointer to Object };

2000固定使用三级句柄表,即这三个数组都出现了,从XP开始根据需要使用2级表、1 级表,只有0级表是肯定存在的。无论使用几级句柄表,都可以认为32-bits句柄中包 含有3个下标,就某一确定OS版本而言这3个下标所对应的位是确定的。如何从32-bits 句柄中提取这3个下标是随OS版本不同而不同的,但大致思路一致。

无论2000还是2000之后的OS,无论进程相关的0级表、内核0级表还是PspCidTable的0 级表,第0个0级表的第0个元素都不对应某个对象,从第0个0级表的第1个元素(也就 是句柄4所定位的元素)开始才对应某个对象,这意味着最小句柄、PID、TID是4不是0。

我在2000 SP4上做了个实验,第1个(从0计)0级表的第0个元素对应某个对象,是有效 的。显然不是每个0级表的第0个元素都不对应某个对象,外面很多文章在这点上说得 好像不精准,互相抄出来的?句柄值为0x400很正常吧,存疑。

如想手工操作Windows句柄表,一定要区分2000与2000之后的OS。

以2000 SP4为例。

先说说2000,MS源码里是这样定义句柄本身的:


/*

  • from 2000 src ex.h / typedef struct _EXHANDLE { union { struct { / * Application available tag bits / ULONG TagBits : 2; / * The handle table entry index * * 注意这个位域,意味着高30-bits才包含各级索引,低2位的TagBits * 我也不清楚用在何处,至少不是用来标识几级句柄表的,因为2000 * 固定使用三级句柄表。 */ ULONG Index : 30; }; HANDLE GenericHandleOverlay; }; } EXHANDLE, *PEXHANDLE;

MS没有用数据结构细分Index,而是将Index的细分工作留给了相关函数。参看 2000 src handle.c中ExpLookupHandleTableEntry()的实现,2000固定使用三级句柄 表,平时Win32 API程序员面对的HANDLE实际是这样使用的:


type struct _HANDLE { /* * 最低2-bits未使用,恒为0,外在表现就成了HANDLE的值都是4字节对齐的, * 这是以前Win32 API程序员的常见说法。 / unsigned int unused_0 : 2; / * 0级表的下标,从0计,该表的元素类型是HANDLE_TABLE_ENTRY,占8字节 / unsigned int level_0_index : 8; / * 1级表的下标,从0计,该表元素是指向0级表的指针,占4字节 / unsigned int level_1_index : 8; / * 2级表的下标,从0计,该表元素是指向1级表的指针,占4字节 / unsigned int level_2_index : 8; / * 未使用,必须保持为0。 / unsigned int unused_1 : 5; / * 如为1则使用ObpKernelHandleTable,此时的句柄称为内核句柄,否则使用进 * 程相关的句柄表。Win32 API程序员面对的HANDLE该位域为0。 */ unsigned int kernel : 1; } HANDLE, PHANDLE;

在第4版Windows Internals中关于level_n_index写得太含混,基本上会让不了解细 节的读者误以为没有unused_0的存在、level_0_index位于最低8-bits。

三级句柄表中0级表的元素类型是HANDLE_TABLE_ENTRY:

kd> dt nt!_HANDLE_TABLE_ENTRY +0x000 Object : Ptr32 Void +0x000 ObAttributes : Uint4B +0x004 GrantedAccess : Uint4B +0x004 GrantedAccessIndex : Uint2B +0x006 CreatorBackTraceIndex : Uint2B +0x004 NextFreeTableEntry : Int4B

kd的这个显示只能看出低32-bits的Object是一个指针,实际上这里还有一些名堂, 参看2000 src ex.h中对HANDLE_TABLE_ENTRY的定义,在Object成员的注释里这样写 道:

The pointer to the object overloaded with three ob attributes bits in the lower order and the high bit to denote locked or unlocked entries.

第4版Windows Internals第三章介绍Object Manager时对此进行了一些解释,我们不 必太关心别的细节,只要知道为了获取最终的对象头指针(POBJECT_HEADER)得这样干:

Object & ~7 | 0x80000000

对象头指针总是8字节对齐并且总是大于0x80000000的。我们以winlogon.exe进程为 研究对象:

kd> !process 0 0 winlogon.exe PROCESS fd669360 SessionId: 0 Cid: 00e0 Peb: 7ffdf000 ParentCid: 00ac DirBase: 0386a000 ObjectTable: fd686ac8 TableSize: 421. Image: WINLOGON.EXE

kd> dt nt!_EPROCESS ObjectTable 0xfd669360 +0x128 ObjectTable : 0xfd686ac8 _HANDLE_TABLE kd> dt nt!_HANDLE_TABLE 0xfd686ac8 +0x000 Flags : 0 +0x004 HandleCount : 421 +0x008 Table : 0xe22db000 -> 0xe22db400 -> 0xe22db800 _HANDLE_TABLE_ENTRY +0x00c QuotaProcess : 0xfd669360 _EPROCESS +0x010 UniqueProcessId : 0x000000e0 +0x014 FirstFreeTableEntry : 432 +0x018 NextIndexNeedingPool : 768 +0x01c HandleTableLock : _ERESOURCE +0x054 HandleTableList : _LIST_ENTRY [ 0xfd658bbc - 0xfd8ae97c ] +0x05c HandleContentionEvent : _KEVENT kd> dd 0xfd686ac8+0x008 l 1 fd686ad0 e22db000

Table等于0xe22db000,这个成员是一个指向2级表的指针。

kd> !handle 4 3 0xe0 processor number 0 Searching for Process with Cid == e0 PROCESS fd669360 SessionId: 0 Cid: 00e0 Peb: 7ffdf000 ParentCid: 00ac DirBase: 0386a000 ObjectTable: fd686ac8 TableSize: 421. Image: WINLOGON.EXE

Handle Table at e22db000 with 421 Entries in use 0004: Object: e13646b0 GrantedAccess: 000f001f Object: e13646b0 Type: (fd90f580) Section ObjectHeader: e1364698 HandleCount: 1 PointerCount: 1

第一个4是句柄4,第二个3指明输出信息的详细程度,第三个0xe0是PID。现在我们来 手工查看句柄4:


handle = 4; /*

  • 1 / level_0_index = ( handle >> 2 ) & 0xFF; /
  • 0 / level_1_index = ( handle >> 10 ) & 0xFF; /
  • 0 / level_2_index = ( handle >> 18 ) & 0xFF; /
  • 0 / kernel = handle & 0x80000000; /
  • 0xe1364698 / ObjectHeader = poi(poi(poi(0xe22db000+04)+04)+18)&0xFFFFFFF8|0x80000000 /*
  • 0xe13646b0 */ ObjectBody = ObjectHeader + 0x18;

!object的形参是对象体指针(POBJECT_BODY),一般句柄表里获取的是对象头指针, 对象头一般占0x18字节(这个在各版本OS上比较固定)。

kd> ? (poi(poi(poi(0xe22db000+04)+04)+1*8)&0xFFFFFFF8|0x80000000)+0x18 Evaluate expression: -516536656 = e13646b0 kd> !object 0xe13646b0 Object: e13646b0 Type: (fd90f580) Section ObjectHeader: e1364698 HandleCount: 1 PointerCount: 1

对比!object与上面!handle的输出,我们正确地操作了winlogon.exe的句柄表。


/*

  • 这三个全局变量均未导出
  • from wrk handle.c / LIST_ENTRY HandleTableListHead; /
  • from wrk obp.h
  • There is one global kernel handle table accessed via negative handle
  • and only in kernel mode / PHANDLE_TABLE ObpKernelHandleTable; /
  • from wrk psinit.c
  • nonpaged */ PHANDLE_TABLE PspCidTable;

kd> ? nt!HandleTableListHead Evaluate expression: -2142811184 = 80474bd0 kd> dt nt!_LIST_ENTRY #nt!HandleTableListHead [ 0xfd947abc - 0xff9265fc ] +0x000 Flink : 0xfd947abc _LIST_ENTRY [ 0xfd947a3c - 0x80474bd0 ] +0x004 Blink : 0xff9265fc _LIST_ENTRY [ 0x80474bd0 - 0xffa6143c ] kd> ? nt!ObpKernelHandleTable Evaluate expression: -2142755364 = 804825dc kd> ? poi(nt!ObpKernelHandleTable) Evaluate expression: -40601112 = fd9479e8 kd> dt nt!_HANDLE_TABLE poi(nt!ObpKernelHandleTable) +0x000 Flags : 0 +0x004 HandleCount : 107 +0x008 Table : 0xe1003000 -> 0xe1003400 -> 0xe1003800 _HANDLE_TABLE_ENTRY +0x00c QuotaProcess : (null) +0x010 UniqueProcessId : (null) // 内核句柄表,不属于任何进程 +0x014 FirstFreeTableEntry : 131 +0x018 NextIndexNeedingPool : 256 +0x01c HandleTableLock : _ERESOURCE +0x054 HandleTableList : _LIST_ENTRY [ 0xfd68d61c - 0xfd947abc ] +0x05c HandleContentionEvent : _KEVENT

jiurl提到所有的HANDLE_TABLE结构通过HandleTableList成员形成一条双向循环链表, 全局变量HandleTableListHead对应该链表的头。

kd> !list "-t nt!_LIST_ENTRY.Flink -x "? @@(#CONTAINING_RECORD(@$extret, nt!_HANDLE_TABLE, HandleTableList));dt nt!_HANDLE_TABLE QuotaProcess UniqueProcessId @@(#CONTAINING_RECORD(@$extret, nt!_HANDLE_TABLE, HandleTableList))" poi(nt!HandleTableListHead)" Evaluate expression: -40600984 = fd947a68 +0x00c QuotaProcess : (null) +0x010 UniqueProcessId : 0x00000008 // System进程

Evaluate expression: -40601112 = fd9479e8 // ObpKernelHandleTable也在这条链表上 +0x00c QuotaProcess : (null) +0x010 UniqueProcessId : (null)

... ...

Evaluate expression: -43488568 = fd686ac8 +0x00c QuotaProcess : 0xfd669360 _EPROCESS +0x010 UniqueProcessId : 0x000000e0 // winlogon.exe

kd> !process 0 0 System // "!process 0 0 Idle"没有输出 PROCESS fd913020 SessionId: 0 Cid: 0008 Peb: 00000000 ParentCid: 0000 DirBase: 00030000 ObjectTable: fd947a68 TableSize: 206. Image: System

kd> dt nt!_EPROCESS UniqueProcessId ActiveProcessLinks ObjectTable ImageFileName poi(nt!PsIdleProcess) +0x09c UniqueProcessId : (null) +0x0a0 ActiveProcessLinks : _LIST_ENTRY [ 0x0 - 0x0 ] +0x128 ObjectTable : 0xfd947a68 _HANDLE_TABLE +0x1fc ImageFileName : [16] "Idle"

kd> !process 0xfd669360 0 PROCESS fd669360 SessionId: 0 Cid: 00e0 Peb: 7ffdf000 ParentCid: 00ac DirBase: 0386a000 ObjectTable: fd686ac8 TableSize: 421. Image: WINLOGON.EXE

HandleTableListHead的链表上有System进程句柄表以及内核句柄表 (ObpKernelHandleTable)。System进程与Idle进程共用一个句柄表(0xfd947a68),通 过该链表只能找出System进程,找不出Idle进程,因为0xfd947a68的UniqueProcessId 是8。

可以通过HandleTableListHead枚举进程,但要注意ObpKernelHandleTable也在该链 表上,编程时要特殊处理。

kd> ? nt!PspCidTable Evaluate expression: -2142752632 = 80483088 kd> ? poi(nt!PspCidTable) Evaluate expression: -40603096 = fd947228 kd> dt nt!_HANDLE_TABLE poi(nt!PspCidTable) +0x000 Flags : 0 +0x004 HandleCount : 277 +0x008 Table : 0xe1004000 -> 0xe1004400 -> 0xe1004800 _HANDLE_TABLE_ENTRY +0x00c QuotaProcess : (null) +0x010 UniqueProcessId : (null) +0x014 FirstFreeTableEntry : 188 +0x018 NextIndexNeedingPool : 768 +0x01c HandleTableLock : _ERESOURCE +0x054 HandleTableList : _LIST_ENTRY [ 0xfd94727c - 0xfd94727c ] +0x05c HandleContentionEvent : _KEVENT

PspCidTable是一个特殊的句柄表,不在HandleTableListHead的链表上,不属于任何 进程。PID、TID是操作该表的句柄,从PspCidTable的0级表获取的直接就是对象体指 针(POBJECT_BODY)。下面以System进程为例手工操作PspCidTable:


/*

  • 即PID / handle = 8; /
  • 2 / level_0_index = ( handle >> 2 ) & 0xFF; /
  • 0 / level_1_index = ( handle >> 10 ) & 0xFF; /
  • 0 / level_2_index = ( handle >> 18 ) & 0xFF; /
  • 0 / kernel = handle & 0x80000000; /
  • 0xfd913020 / ObjectBody = poi(poi(poi(0xe1004000+04)+04)+28)&0xFFFFFFF8|0x80000000

kd> ? poi(poi(poi(0xe1004000+04)+04)+2*8)&0xFFFFFFF8|0x80000000 Evaluate expression: -40816608 = fd913020 kd> !object 0xfd913020 Object: fd913020 Type: (fd9474e0) Process ObjectHeader: fd913008 HandleCount: 2 PointerCount: 48 kd> !process 0xfd913020 0 PROCESS fd913020 SessionId: 0 Cid: 0008 Peb: 00000000 ParentCid: 0000 DirBase: 00030000 ObjectTable: fd947a68 TableSize: 206. Image: System

jiurl还尝试分析PspCidTable中句柄4对应的对象:

kd> ? poi(poi(poi(0xe1004000+04)+04)+1*8)&0xFFFFFFF8|0x80000000 Evaluate expression: -40813152 = fd913da0 kd> !object 0xfd913da0 Object: fd913da0 Type: (fd9473e0) Thread ObjectHeader: fd913d88 HandleCount: 0 PointerCount: 3 kd> !thread 0xfd913da0 0 THREAD fd913da0 Cid 8.4 Teb: 00000000 Win32Thread: 00000000 WAIT

"Cid 8.4"表示PID为8、TID为4。

可以通过PspCidTable枚举进程、线程。

手工检查内核句柄0x80000008:

kd> dt nt!_HANDLE_TABLE Table poi(nt!ObpKernelHandleTable) +0x008 Table : 0xe1003000 -> 0xe1003400 -> 0xe1003800 _HANDLE_TABLE_ENTRY kd> ? (poi(poi(poi(0xe1003000+04)+04)+2*8)&0xFFFFFFF8|0x80000000)+0x18 Evaluate expression: -42923224 = fd710b28 kd> !object 0xfd710b28 Object: fd710b28 Type: (fd93a580) File // 有个疑问,当此处显示Key时对应的数据结构是什么? ObjectHeader: fd710b10 HandleCount: 1 PointerCount: 3 Directory Object: 00000000 Name: \WINNT\system32\config\SAM.LOG {HarddiskVolume1} kd> dt nt!_FILE_OBJECT FileName 0xfd710b28 +0x030 FileName : _UNICODE_STRING "\WINNT\system32\config\SAM.LOG"

从XP开始,句柄表的操作有了显著变化。

以2008 SP1为例。

wrk里这样定义句柄本身:


/*

  • from wrk ex.h / typedef struct _EXHANDLE { union { struct { / * Application available tag bits / ULONG TagBits : 2; / * The handle table entry index * * 注意这个位域,意味着高30-bits才包含各级索引。 */ ULONG Index : 30; }; HANDLE GenericHandleOverlay;

     /*
      * Amount to increment the Value to get to the next handle
      */

#define HANDLE_VALUE_INC 4

    ULONG_PTR   Value;
};

} EXHANDLE, *PEXHANDLE;

MS没有用数据结构细分Index,而是将Index的细分工作留给了相关函数,参看 wrk handle.c中ExpLookupHandleTableEntry()的实现,比较眼花。下面是可读性更 好的定义:


type struct _HANDLE { /* * 最低2-bits未使用,恒为0,外在表现就成了HANDLE的值都是4字节对齐的, * 这是以前Win32 API程序员的常见说法。 / unsigned int unused_0 : 2; / * 0级表的下标,从0计,该表的元素类型是HANDLE_TABLE_ENTRY,占8字节 / unsigned int level_0_index : 9; / * 1级表的下标,从0计,该表元素是指向0级表的指针,占4字节 / unsigned int level_1_index : 10; / * 2级表的下标,从0计,该表元素是指向1级表的指针,占4字节 / unsigned int level_2_index : 10; / * 如为1则使用ObpKernelHandleTable,此时的句柄称为内核句柄,否则使用进 * 程相关的句柄表。Win32 API程序员面对的HANDLE该位域为0。 */ unsigned int kernel : 1; } HANDLE, PHANDLE;

第5版Windows Internals根本没提level_n_index,把第4版中针对2000的含混说法给 删了。

句柄表中0级表的元素类型是HANDLE_TABLE_ENTRY:

1: kd> dt 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

kd的这个显示只能看出低32-bits的Object是一个指针,实际上这里还有一些名堂, 参看wrk ex.h中对HANDLE_TABLE_ENTRY的定义,在Object成员的注释里这样写 道:

The pointer to the object overloaded with three ob attributes bits in the lower order and the high bit to denote locked or unlocked entries.

第4版Windows Internals第三章介绍Object Manager时对此进行了一些解释,第5版 的图3-19有一些新说法。我们不必太关心别的细节,只要知道为了获取最终的对象头 指针(POBJECT_HEADER)得这样干:

Object & ~7

与2000时的处理相比,不必手工将最高位置1了,因为最高位已经不是锁位。但为了 与2000时的处理兼容,仍然可以:

Object & ~7 | 0x80000000

对象头指针总是8字节对齐并且总是大于0x80000000的。我们以winlogon.exe进程为 研究对象:

1: kd> !process 0 0 winlogon.exe PROCESS 84f2ed90 SessionId: 1 Cid: 0240 Peb: 7ffdd000 ParentCid: 01fc DirBase: 3f495040 ObjectTable: 8af92d38 HandleCount: 132. Image: winlogon.exe

1: kd> dt nt!_EPROCESS ObjectTable 0x84f2ed90 +0x0dc ObjectTable : 0x8af92d38 _HANDLE_TABLE 1: kd> dt nt!_HANDLE_TABLE 0x8af92d38 +0x000 TableCode : 0x8afab000 +0x004 QuotaProcess : 0x84f2ed90 _EPROCESS +0x008 UniqueProcessId : 0x00000240 +0x00c HandleLock : _EX_PUSH_LOCK +0x010 HandleTableList : _LIST_ENTRY [ 0x8afd28c8 - 0x8af90a90 ] +0x018 HandleContentionEvent : _EX_PUSH_LOCK +0x01c DebugInfo : (null) +0x020 ExtraInfoPages : 0 +0x024 Flags : 0 +0x024 StrictFIFO : 0y0 +0x028 FirstFreeHandle : 544 +0x02c LastFreeHandleEntry : 0x8afabff8 _HANDLE_TABLE_ENTRY +0x030 HandleCount : 132 +0x034 NextHandleNeedingPool : 0x800

与2000相比,2000之后OS的HANDLE_TABLE结构有变化,2000的Table成员即XP的 TableCode成员。

TableCode等于0x8afab000,这个成员不再是一个简单的指针,它的低2-bits用以指 明目前使用几级句柄表算法,0表示只有0级表,1表示同时有1级、0级表,2表示同时 有2级、1级、0级表,参看wrk handle.c中LEVEL_CODE_MASK的宏定义。从XP开始不再 固定使用三级句柄表。为了从TableCode中获取指针得这么干:


/*

  • 0 / TableLevel = TableCode & 0x00000003; /
  • 0x8afab000 */ Table = TableCode & ~3;

1: kd> !handle 4 3 0x240 processor number 1, process 00000240 Searching for Process with Cid == 240 PROCESS 84f2ed90 SessionId: 1 Cid: 0240 Peb: 7ffdd000 ParentCid: 01fc DirBase: 3f495040 ObjectTable: 8af92d38 HandleCount: 132. Image: winlogon.exe

Handle table at 8afab000 with 132 Entries in use 0004: Object: 8ae69670 GrantedAccess: 00000003 Entry: 8afab008 Object: 8ae69670 Type: (84534d40) Directory ObjectHeader: 8ae69658 (old version) HandleCount: 38 PointerCount: 67 Directory Object: 82e08030 Name: KnownDlls

第一个4是句柄4,第二个3指明输出信息的详细程度,第三个0xe0是PID。现在我们来 手工查看句柄4:


handle = 4; /*

  • 1 / level_0_index = ( handle >> 2 ) & 0x1FF; /
  • 0 / level_1_index = ( handle >> 11 ) & 0x3FF; /
  • 0 / level_2_index = ( handle >> 21 ) & 0x3FF; /
  • 0 / kernel = handle & 0x80000000; /
  • 0x8ae69658 / ObjectHeader = poi(0x8afab000+18)&0xFFFFFFF8 /*
  • 0x8ae69670 */ ObjectBody = ObjectHeader + 0x18;

!object的形参是对象体指针(POBJECT_BODY),一般句柄表里获取的是对象头指针, 对象头一般占0x18字节(这个在各版本OS上比较固定)。

1: kd> ? (poi(0x8afab000+1*8)&0xFFFFFFF8)+0x18 Evaluate expression: -1964599696 = 8ae69670 1: kd> !object 0x8ae69670 Object: 8ae69670 Type: (84534d40) Directory ObjectHeader: 8ae69658 (old version) HandleCount: 38 PointerCount: 67 Directory Object: 82e08030 Name: KnownDlls

Hash Address  Type          Name
---- -------  ----          ----
 00  82e9f0d8 Section       IMAGEHLP.dll
     881448e0 Section       gdi32.dll
 02  8ae41258 Section       NORMALIZ.dll
 03  8ae697d0 Section       URLMON.dll
     88187188 Section       ole32.dll
 04  82ea0218 Section       USP10.dll
 06  82e021b8 Section       WLDAP32.dll
     82fbe3c8 Section       SHELL32.dll
 09  8ae52a50 Section       user32.dll
 16  82f038b0 SymbolicLink  KnownDllPath
     8ae42258 Section       COMCTL32.dll
 17  8ae3a1a8 Section       PSAPI.DLL
 18  8ae1ae58 Section       OLEAUT32.dll
     82e9f528 Section       advapi32.dll
 19  82e9efd8 Section       IERTUTIL.dll
     88167178 Section       SHLWAPI.dll
 20  8aed9d68 Section       WS2_32.dll
 21  8ae69768 Section       LPK.dll
 23  82e9f2d0 Section       COMDLG32.dll
 25  82e9edb8 Section       Setupapi.dll
 26  8ae6aa68 Section       MSCTF.dll
     8ae6a6e0 Section       WININET.dll
 27  88144c60 Section       IMM32.dll
 28  82e9eef8 Section       MSVCRT.dll
 31  8ae69f08 Section       rpcrt4.dll
     82ea01b0 Section       clbcatq.dll
 32  82ea00d0 Section       kernel32.dll
 35  8aed4be0 Section       NSI.dll

对比!object与上面!handle的输出,我们正确地操作了winlogon.exe的句柄表。


/*

  • 这三个全局变量均未导出
  • from wrk handle.c / LIST_ENTRY HandleTableListHead; /
  • from wrk obp.h
  • There is one global kernel handle table accessed via negative handle
  • and only in kernel mode / PHANDLE_TABLE ObpKernelHandleTable; /
  • from wrk psinit.c
  • nonpaged */ PHANDLE_TABLE PspCidTable;

1: kd> ? nt!HandleTableListHead Evaluate expression: -2123263288 = 817192c8 1: kd> dt nt!_LIST_ENTRY #nt!HandleTableListHead [ 0x82e02fd8 - 0x82e4eae8 ] +0x000 Flink : 0x82e02fd8 _LIST_ENTRY [ 0x88167070 - 0x817192c8 ] +0x004 Blink : 0x82e4eae8 _LIST_ENTRY [ 0x817192c8 - 0x91e9fdc0 ] 1: kd> ? nt!ObpKernelHandleTable Evaluate expression: -2123209872 = 81726370 1: kd> ? poi(nt!ObpKernelHandleTable) Evaluate expression: -2099236920 = 82e02fc8 1: kd> dt nt!_HANDLE_TABLE poi(nt!ObpKernelHandleTable) +0x000 TableCode : 0x91b39001 +0x004 QuotaProcess : (null) +0x008 UniqueProcessId : 0x00000004 // 内核句柄表,同时属于System进程 +0x00c HandleLock : _EX_PUSH_LOCK +0x010 HandleTableList : _LIST_ENTRY [ 0x88167070 - 0x817192c8 ] +0x018 HandleContentionEvent : _EX_PUSH_LOCK +0x01c DebugInfo : (null) +0x020 ExtraInfoPages : 0 +0x024 Flags : 0 +0x024 StrictFIFO : 0y0 +0x028 FirstFreeHandle : 1488 +0x02c LastFreeHandleEntry : 0x91b3aff8 _HANDLE_TABLE_ENTRY +0x030 HandleCount : 494 +0x034 NextHandleNeedingPool : 0x1000 1: kd> ? poi(nt!PsInitialSystemProcess) Evaluate expression: -2074780272 = 84555d90 1: kd> dt nt!_EPROCESS UniqueProcessId ObjectTable poi(nt!PsInitialSystemProcess) +0x09c UniqueProcessId : 0x00000004 +0x0dc ObjectTable : 0x82e02fc8 _HANDLE_TABLE // System进程的句柄表同时也是内核句柄表

2000的ObpKernelHandleTable是独立的,从XP开始ObpKernelHandleTable与System进 程的句柄表是同一个,参上面的0x82e02fc8。当句柄最高位为1时使用 ObpKernelHandleTable,这种句柄称为内核句柄,否则要在相应进程上下文里使用进 程相关的句柄表。

jiurl提到所有的HANDLE_TABLE结构通过HandleTableList成员形成一条双向循环链表, 全局变量HandleTableListHead对应该链表的头。

1: kd> !list "-t nt!_LIST_ENTRY.Flink -x "? @@(#CONTAINING_RECORD(@$extret, nt!_HANDLE_TABLE, HandleTableList));dt nt!_HANDLE_TABLE QuotaProcess UniqueProcessId @@(#CONTAINING_RECORD(@$extret, nt!_HANDLE_TABLE, HandleTableList))" poi(nt!HandleTableListHead)" Evaluate expression: -2099236920 = 82e02fc8 // ObpKernelHandleTable +0x004 QuotaProcess : (null) +0x008 UniqueProcessId : 0x00000004 // System进程

... ...

Evaluate expression: -1963381448 = 8af92d38 +0x004 QuotaProcess : 0x84f2ed90 _EPROCESS +0x008 UniqueProcessId : 0x00000240

1: kd> !process 0 0 System // "!process 0 0 Idle"没有输出 PROCESS 84555d90 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000 DirBase: 00122000 ObjectTable: 82e02fc8 HandleCount: 494. Image: System

1: kd> dt nt!_EPROCESS UniqueProcessId ActiveProcessLinks ObjectTable ImageFileName poi(nt!PsIdleProcess) +0x09c UniqueProcessId : (null) +0x0a0 ActiveProcessLinks : _LIST_ENTRY [ 0x0 - 0x0 ] +0x0dc ObjectTable : 0x82e02fc8 _HANDLE_TABLE +0x14c ImageFileName : [16] "Idle"

HandleTableListHead的链表上有System进程句柄表。System进程与Idle进程共用一 个句柄表(0x82e02fc8),通过该链表只能找出System进程,找不出Idle进程,因为 0x82e02fc8的UniqueProcessId是4。

可以通过HandleTableListHead枚举进程。

1: kd> ? nt!PspCidTable Evaluate expression: -2123216460 = 817249b4 1: kd> ? poi(nt!PspCidTable) Evaluate expression: -2099239624 = 82e02538 1: kd> dt nt!_HANDLE_TABLE poi(nt!PspCidTable) +0x000 TableCode : 0x82e1e001 +0x004 QuotaProcess : (null) +0x008 UniqueProcessId : (null) +0x00c HandleLock : _EX_PUSH_LOCK +0x010 HandleTableList : _LIST_ENTRY [ 0x82e02548 - 0x82e02548 ] +0x018 HandleContentionEvent : _EX_PUSH_LOCK +0x01c DebugInfo : (null) +0x020 ExtraInfoPages : 0 +0x024 Flags : 1 +0x024 StrictFIFO : 0y1 +0x028 FirstFreeHandle : 2912 +0x02c LastFreeHandleEntry : 0x928990d8 _HANDLE_TABLE_ENTRY +0x030 HandleCount : 479 +0x034 NextHandleNeedingPool : 0x1000

PspCidTable是一个特殊的句柄表,不在HandleTableListHead的链表上,不属于任何 进程。PID、TID是操作该表的句柄,从PspCidTable的0级表获取的直接就是对象体指 针(POBJECT_BODY)。下面以System进程为例手工操作PspCidTable:


/*

  • 即PID / handle = 4; /
  • 1 / level_0_index = ( handle >> 2 ) & 0x1FF; /
  • 0 / level_1_index = ( handle >> 11 ) & 0x3FF; /
  • 0 / level_2_index = ( handle >> 21 ) & 0x3FF; /
  • 0 / kernel = handle & 0x80000000; /
  • 1 / TableLevel = TableCode & 0x00000003; /
  • 0x82e1e000 / Table = TableCode & ~3; /
  • 0x84555d90 / ObjectBody = poi(poi(0x82e1e000+04)+1*8)&0xFFFFFFF8

1: kd> ? poi(poi(0x82e1e000+04)+18)&0xFFFFFFF8 Evaluate expression: -2074780272 = 84555d90 1: kd> !object 0x84555d90 Object: 84555d90 Type: (84534680) Process ObjectHeader: 84555d78 (old version) HandleCount: 4 PointerCount: 156 1: kd> !process 0x84555d90 0 PROCESS 84555d90 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000 DirBase: 00122000 ObjectTable: 82e02fc8 HandleCount: 494. Image: System

尝试分析PspCidTable中句柄8对应的对象:

1: kd> ? poi(poi(0x82e1e000+04)+28)&0xFFFFFFF8 Evaluate expression: -2074780952 = 84555ae8 1: kd> !object 0x84555ae8 Object: 84555ae8 Type: (84534500) Thread ObjectHeader: 84555ad0 (old version) HandleCount: 0 PointerCount: 1 1: kd> !thread 0x84555ae8 0 THREAD 84555ae8 Cid 0004.0008 Teb: 00000000 Win32Thread: 00000000 GATEWAIT

"Cid 0004.0008"表示PID为4、TID为8。

可以通过PspCidTable枚举进程、线程。

手工检查内核句柄0x80000004:

1: kd> dt nt!_HANDLE_TABLE TableCode poi(nt!ObpKernelHandleTable) +0x000 TableCode : 0x91b39001 1: kd> ? (poi(poi((0x91b39001&0xFFFFFFFC)+04)+18)&0xFFFFFFF8)+0x18 Evaluate expression: -2074780272 = 84555d90 1: kd> !object 0x84555d90 Object: 84555d90 Type: (84534680) Process ObjectHeader: 84555d78 (old version) HandleCount: 4 PointerCount: 156 1: kd> !process 0x84555d90 0 PROCESS 84555d90 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000 DirBase: 00122000 ObjectTable: 82e02fc8 HandleCount: 494. Image: System

1: kd> dt nt!_EPROCESS ImageFileName 0x84555d90 +0x14c ImageFileName : [16] "System" 1: kd> ? (poi(poi((0x91b39001&0xFFFFFFFC)+04)+18)&0xFFFFFFF8) Evaluate expression: -2074780296 = 84555d78 1: kd> dt nt!_OBJECT_HEADER 0x84555d78 +0x000 PointerCount : 156 +0x004 HandleCount : 4 +0x004 NextToFree : 0x00000004 +0x008 Type : 0x84534680 _OBJECT_TYPE +0x00c NameInfoOffset : 0 '' +0x00d HandleInfoOffset : 0 '' +0x00e QuotaInfoOffset : 0 '' +0x00f Flags : 0x22 '"' +0x010 ObjectCreateInfo : 0x81724840 _OBJECT_CREATE_INFORMATION +0x010 QuotaBlockCharged : 0x81724840 +0x014 SecurityDescriptor : 0x82e0229e +0x018 Body : _QUAD 1: kd> dt nt!_OBJECT_TYPE 0x84534680 +0x000 TypeList : _LIST_ENTRY [ 0x84534680 - 0x84534680 ] +0x008 Name : _UNICODE_STRING "Process" +0x010 DefaultObject : (null) +0x014 Index : 6 +0x018 TotalNumberOfObjects : 0x29 +0x01c TotalNumberOfHandles : 0xaa +0x020 HighWaterNumberOfObjects : 0x2d +0x024 HighWaterNumberOfHandles : 0xb8 +0x028 TypeInfo : _OBJECT_TYPE_INITIALIZER +0x078 Mutex : _ERESOURCE +0x0b0 TypeLock : _EX_PUSH_LOCK +0x0b4 Key : 0x636f7250 +0x0b8 ObjectLocks : [32] _EX_PUSH_LOCK +0x138 CallbackList : _LIST_ENTRY [ 0x845347b8 - 0x845347b8 ]

从XP开始!handle命令得到增强,可以直接查看内核句柄:

1: kd> !handle 0x80000004 0x13 processor number 1, process 84555d90 PROCESS 84555d90 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000 DirBase: 00122000 ObjectTable: 82e02fc8 HandleCount: 494. Image: System

Kernel Handle table at 91b39000 with 494 Entries in use 80000004: Object: 84555d90 GrantedAccess: 001fffff Entry: 82e03008 Object: 84555d90 Type: (84534680) Process ObjectHeader: 84555d78 (old version) HandleCount: 4 PointerCount: 156 Optional Headers:

从XP开始内核句柄表同时也是System进程的句柄表,对比如下输出:

1: kd> !handle 4 3 4 processor number 1, process 00000004 Searching for Process with Cid == 4 PROCESS 84555d90 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000 DirBase: 00122000 ObjectTable: 82e02fc8 HandleCount: 494. Image: System

Handle table at 91b39000 with 494 Entries in use 0004: Object: 84555d90 GrantedAccess: 001fffff Entry: 82e03008 Object: 84555d90 Type: (84534680) Process ObjectHeader: 84555d78 (old version) HandleCount: 4 PointerCount: 156