fweWindows 很多API函数都会创建和使用句柄(传入参数),句柄代表一个内核对象的内存地址,每个进程都有一个句柄表,它保存着进程拥有的句柄,内核也有一个句柄表 PspCidTable,它保存着整个系统的句柄。
ExpLookupHandleTableEntry windows内核句柄表结构解析
PAGE:00000001405F93D0 ExpLookupHandleTableEntry proc near ; CODE XREF: ObpReferenceObjectByHandleWithTag+EA↑p
PAGE:00000001405F93D0 ; NtClose+B5↓p ...
PAGE:00000001405F93D0 mov eax, [rcx]
PAGE:00000001405F93D2 and rdx, 0FFFFFFFFFFFFFFFCh
PAGE:00000001405F93D6 cmp rdx, rax ; 如果pid大于句柄表地址则认为是无效pid
PAGE:00000001405F93D9 jnb short loc_1405F9435
PAGE:00000001405F93DB mov r8, [rcx+8]
PAGE:00000001405F93DF mov eax, r8d
PAGE:00000001405F93E2 and eax, 3
PAGE:00000001405F93E5 cmp eax, 1 ; 低两位表示句柄表层数level
PAGE:00000001405F93E8 jnz short loc_1405F9402 ; 判断是否是2级句柄表
PAGE:00000001405F93EA mov rax, rdx ; rax=进程ID
PAGE:00000001405F93ED shr rax, 0Ah ; 进程ID右移0xA
PAGE:00000001405F93F1 and edx, 3FFh ; 进程ID & 0x3ff
PAGE:00000001405F93F7 mov rax, [r8+rax*8-1]
PAGE:00000001405F93FC lea rax, [rax+rdx*4]
PAGE:00000001405F9400 retn
PAGE:00000001405F9400 ; ---------------------------------------------------------------------------
PAGE:00000001405F9401 align 2
PAGE:00000001405F9402
PAGE:00000001405F9402 loc_1405F9402: ; CODE XREF: ExpLookupHandleTableEntry+18↑j
PAGE:00000001405F9402 test eax, eax ; 1级句柄表处理
PAGE:00000001405F9404 jnz short loc_1405F940C
PAGE:00000001405F9406 lea rax, [r8+rdx*4] ; 进程ID * 4
PAGE:00000001405F940A retn
PAGE:00000001405F940A ; ----------------------------------------------------------------
句柄表结构
nt!_HANDLE_TABLE +0x000 NextHandleNeedingPool : Uint4B +0x004 ExtraInfoPages : Int4B +0x008 TableCode : Uint8B 句柄表地址低两位表示层级 +0x010 QuotaProcess : Ptr64 _EPROCESS +0x018 HandleTableList : _LIST_ENTRY +0x028 UniqueProcessId : Uint4B +0x02c Flags : Uint4B +0x02c StrictFIFO : Pos 0, 1 Bit +0x02c EnableHandleExceptions : Pos 1, 1 Bit +0x02c Rundown : Pos 2, 1 Bit +0x02c Duplicated : Pos 3, 1 Bit +0x02c RaiseUMExceptionOnInvalidHandleClose : Pos 4, 1 Bit +0x030 HandleContentionEvent : _EX_PUSH_LOCK +0x038 HandleTableLock : _EX_PUSH_LOCK +0x040 FreeLists : [1] _HANDLE_TABLE_FREE_LIST +0x040 ActualEntry : [32] UChar +0x060 DebugInfo : Ptr64 _HANDLE_TRACE_DEBUG_INFO
进程句柄表位置
nt!_EPROCESS
+0x570 ObjectTable : Ptr64 _HANDLE_TABLE
notepad.exe进程的句柄表地址
1: kd> dt _eprocess objecttable 0xffff9f8f1219b080 nt!_EPROCESS +0x570 ObjectTable : 0xffff8d85`56370c40 _HANDLE_TABLE
0xffff8d85`570ff000 notepad.exe句柄表地址,低两位为0 表示是1级表
1: kd> dq 0xffff8d85`570ff000
ffff8d85`570ff000 00000000`00000000 00000000`00000000
ffff8d85`570ff010 9f8f124d`1d30ffff 00000000`001f0003
ffff8d85`570ff020 9f8f124d`14b0fff3 00000000`001f0003
ffff8d85`570ff030 9f8f1212`67b0fff5 00000000`00000001
ffff8d85`570ff040 9f8f1227`9290ffd7 00000000`001f0003
ffff8d85`570ff050 9f8f123d`39d0ff89 00000000`000f00ff
ffff8d85`570ff060 9f8f121f`2370ffff 00000000`00100002
ffff8d85`570ff070 9f8f1212`7990ffff 00000000`00000001
解析这个句柄对象 9f8f124d`14b0fff3
(句柄项>>0x10)&0xfffffffffffffff0 = 对象地址(低位)
(0x9f8f124d14b0fff3>>0x10)&0xfffffffffffffff0 = 0x9f8f124d14b0 加上高位 0xffff 等于 0xffff9f8f124d14b0
dt _object_header 0xffff9f8f124d14b0
+0x000 PointerCount : 0n32764 +0x008 HandleCount : 0n1 +0x008 NextToFree : 0x00000000`00000001 Void +0x010 Lock : _EX_PUSH_LOCK +0x018 TypeIndex : 0x2d '-' 句柄类型 +0x019 TraceFlags : 0 '' +0x019 DbgRefTrace : 0y0 +0x019 DbgTracePermanent : 0y0
计算对象类型索引
2d^ObHeaderCookie^(对象地址倒数第二个字节14)
1: kd> ?? 0x2d^0x14^0x29 int 0n16
逆向内核函数 ObGetObjectType得知计算方式
PAGE:00000001406FD860 ObGetObjectType proc near ; DATA XREF: .pdata:000000014010986C↑o
PAGE:00000001406FD860 lea rax, [rcx-30h]
PAGE:00000001406FD864 movzx ecx, byte ptr [rcx-18h]
PAGE:00000001406FD868 shr rax, 8
PAGE:00000001406FD86C movzx eax, al
PAGE:00000001406FD86F xor rax, rcx
PAGE:00000001406FD872 movzx ecx, byte ptr cs:ObHeaderCookie
PAGE:00000001406FD879 xor rax, rcx
PAGE:00000001406FD87C lea rcx, ObTypeIndexTable
PAGE:00000001406FD883 mov rax, [rcx+rax*8]
PAGE:00000001406FD887 retn
PAGE:00000001406FD887 ObGetObjectType endp
ObTypeIndexTable+(Index*8)
1: kd> dq fffff801`1af08e10+0x16*8
fffff801`1af08ec0 ffff9f8f`0ceca560 ffff9f8f`0cecac40
fffff801`1af08ed0 ffff9f8f`0ceca6c0 ffff9f8f`0cec97a0
fffff801`1af08ee0 ffff9f8f`0cecada0 ffff9f8f`0cec9a60
fffff801`1af08ef0 ffff9f8f`0cec9d20 ffff9f8f`0cee1e80
fffff801`1af08f00 ffff9f8f`0cee14e0 ffff9f8f`0cee1d20
fffff801`1af08f10 ffff9f8f`0cee1220 ffff9f8f`0cee2ae0
fffff801`1af08f20 ffff9f8f`0cee22a0 ffff9f8f`0cee2140
fffff801`1af08f30 ffff9f8f`0cee2f00 ffff9f8f`0cee17a0
1: kd> dt _object_type ffff9f8f`0ceca560
nt!_OBJECT_TYPE
+0x000 TypeList : _LIST_ENTRY [ 0xffff9f8f`0ceca560 - 0xffff9f8f`0ceca560 ]
+0x010 Name : _UNICODE_STRING "Profile"
+0x020 DefaultObject : (null)
+0x028 Index : 0x16 ''
+0x02c TotalNumberOfObjects : 0
+0x030 TotalNumberOfHandles : 0
+0x034 HighWaterNumberOfObjects : 0
+0x038 HighWaterNumberOfHandles : 0
+0x040 TypeInfo : _OBJECT_TYPE_INITIALIZER
+0x0b8 TypeLock : _EX_PUSH_LOCK
+0x0c0 Key : 0x666f7250
+0x0c8 CallbackList : _LIST_ENTRY [ 0xffff9f8f`0ceca628 - 0xffff9f8f`0ceca628 ]
可以看到这个句柄类型是 Profile类型
内核句柄表
PspCidTable 内核变量存储内核全局句柄表的内存地址
1: kd> dq PspCidTable fffff801`1af085c0 ffff8d85`50279dc0 ffff9f8f`0cef6d80 fffff801`1af085d0 ffff9f8f`0cee2140 00000000`00000002 fffff801`1af085e0 00000000`00000000 00001000`00010000 fffff801`1af085f0 00000000`00000000 00000000`00005000 fffff801`1af08600 00000000`00000000 000001db`00000000 fffff801`1af08610 00000000`00000000 00000000`00000000 fffff801`1af08620 00000000`00000000 00000000`00000000 fffff801`1af08630 ffff9f8f`0cfef900 fffff801`19d06000 0xffff8d85`53efc001 低两位为1 表示是一个二级表 1: kd> dt _handle_table ffff8d85`50279dc0 nt!_HANDLE_TABLE +0x000 NextHandleNeedingPool : 0x1c00 +0x004 ExtraInfoPages : 0n0 +0x008 TableCode : 0xffff8d85`53efc001 +0x010 QuotaProcess : (null) +0x018 HandleTableList : _LIST_ENTRY [ 0xffff8d85`50279dd8 - 0xffff8d85`50279dd8 ] +0x028 UniqueProcessId : 0 +0x02c Flags : 1 +0x02c StrictFIFO : 0y1 +0x02c EnableHandleExceptions : 0y0 +0x02c Rundown : 0y0
查找notepad.exe的句柄 pid 为 6264 (0x1878)
由上内核函数逆向可知
句柄表地址+(进程ID>>0xa*8)-1
1: kd> dq 0xffff8d85`53efc001+(6*8)-1 ffff8d85`53efc030 ffff8d85`565fb000 00000000`00000000
得到进程所在1级表地址 ffff8d85`565fb000
得到内核对象句柄项
1: kd> dq ffff8d85`565fb000+(0x78*4)
ffff8d85`565fb480 9f8f121a`3080fffd 00000000`00000000
从内核句柄项中解析出内核对象地址
(0x9f8f121a`3080fffd>>0x10)&0xfffffffffffffff0 = 0xffff9f8f1219b080
1: kd> dt _eprocess imagefilename 0xffff9f8f1219b080 nt!_EPROCESS +0x5a8 ImageFileName : [15] "notepad.exe"