Analysis of Windows 10 X64 kernel object handle table

Many API functions of fweWindows will create and use handles (incoming parameters). The handle represents the memory address of a kernel object. Each process has a handle table, which stores the handles owned by the process. The kernel also has a handle table PspCidTable, which stores A handle to the entire system.

ExpLookupHandleTableEntry windows kernel handle table structure analysis

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 ; If the pid is greater than the address of the handle table, it is considered an invalid 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 ; The lower two digits indicate the level of the handle surface
PAGE:00000001405F93E8 jnz short loc_1405F9402 ; Determine whether it is a level 2 handle table
PAGE:00000001405F93EA mov rax, rdx ; rax=process ID
PAGE:00000001405F93ED shr rax, 0Ah ; Process ID shifted right by 0xA
PAGE:00000001405F93F1 and edx, 3FFh ; Process ID & amp; 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 ; Level 1 handle table processing
PAGE:00000001405F9404 jnz short loc_1405F940C
PAGE:00000001405F9406 lea rax, [r8 + rdx*4] ; Process ID * 4
PAGE: 00000001405F940A retn
PAGE:00000001405F940A ; ---------------------------------------------- ------------------

handle table structure

nt!_HANDLE_TABLE
    + 0x000 NextHandleNeedingPool : Uint4B
    + 0x004 ExtraInfoPages : Int4B
    + 0x008 TableCode : Uint8B The lower two bits of the address of the handle table indicate the level
    + 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

process handle table location

nt!_EPROCESS
 + 0x570 ObjectTable : Ptr64 _HANDLE_TABLE

Address of the handle table of the notepad.exe process

1: kd> dt _eprocess objecttable 0xffff9f8f1219b080
nt!_EPROCESS
    + 0x570 ObjectTable : 0xffff8d85`56370c40 _HANDLE_TABLE
0xffff8d85`570ff000 notepad.exe handle table address, the lower two bits are 0, which means it is a level 1 table
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

Parse this handle object 9f8f124d`14b0fff3

(handle item>>0x10) & 0xfffffffffffffff0 = object address (low order)

(0x9f8f124d14b0fff3>>0x10) & 0xfffffffffffffff0 = 0x9f8f124d14b0 plus high bit 0xffff equals 0xffff9f8f124d14b0

dt_object_header 0xffff9f8f124d14b0
 + 0x000 PointerCount : 0n32764
    + 0x008 HandleCount: 0n1
    + 0x008 NextToFree : 0x00000000`00000001 Void
    + 0x010 Lock : _EX_PUSH_LOCK
    + 0x018 TypeIndex : 0x2d '-' handle type
    + 0x019 TraceFlags : 0 ''
    + 0x019 DbgRefTrace : 0y0
    + 0x019 DbgTracePermanent : 0y0

Calculate object type index

2d^ObHeaderCookie^ (the penultimate byte of the object address is 14)

1: kd>  0x2d^0x14^0x29
int 0n16

Reverse the kernel function ObGetObjectType to know the calculation method

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 ]

You can see that the handle type is Profile type



kernel handle table

The PspCidTable kernel variable stores the memory address of the kernel global handle table

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 The lower two bits are 1, which means it is a secondary table
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

Find the handle pid of notepad.exe is 6264 (0x1878)

It can be known from the reverse of the upper kernel function

Handle table address + (process ID>>0xa*8)-1

1: kd> dq 0xffff8d85`53efc001 + (6*8)-1 ffff8d85`53efc030 ffff8d85`565fb000 00000000`00000000

Get the address of the level 1 table where the process is located ffff8d85`565fb000

get kernel object handle entry

1: kd> dq ffff8d85`565fb000 + (0x78*4)

ffff8d85`565fb480 9f8f121a`3080fffd 00000000`00000000

Parse kernel object address from kernel handle entry

(0x9f8f121a`3080fffd>>0x10) & 0xfffffffffffffff0 = 0xffff9f8f1219b080

1: kd> dt _eprocess imagefilename 0xffff9f8f1219b080
nt!_EPROCESS
    + 0x5a8 ImageFileName : [15] "notepad.exe"