有两个文件。一个是
LearnHanle.exe
(拼写错误,应该是LearnHandle.exe
),另一个是pop.exe
。
这些文件的源代码在这里:
// LearnHanle.cpp
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
int main() {
STARTUPINFOW startup = { 0 };
startup.cb = sizeof startup;
PROCESS_INFORMATION pi = { 0 };
BOOL createProcResult = FALSE;
if (!CreateProcessW(L"C:\\pop.exe", NULL, NULL, NULL, TRUE, 0, NULL, NULL, &startup, &pi))
MessageBoxA(NULL, "Create Process Failed", "Alert", MB_OK);
char handle[80] = { 0 };
sprintf_s(handle, "Process Handle: %llu ProcessId: %llu", pi.hProcess, pi.dwProcessId);
MessageBoxA(NULL, handle, "Process", MB_OK);
sprintf_s(handle, "Thread Handle: %llu ThreadId: %llu", pi.hThread, pi.dwThreadId);
MessageBoxA(NULL, handle, "Thread", MB_OK);
system("pause");
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
// pop.cpp
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
int main() {
MessageBox(0, 0, 0, 0);
// Just sleep
Sleep(6000000);
}
LearnHanle.exe
只是创建 pop.exe
进程,然后自行挂起。
我用
LearnHanle.exe
查看了Process Explorer
进程的详细信息,找到了与pop.exe
进程关联的HANDLE,然后就得到了HANDLE地址,如下所示:
Basic Information
Name: pop.exe(9092)
Type: Process
Description: Contains threads, an address space, and handles.
Address: 0xFFFFE786E04AF080
我用WinDbg调试了Windows 11内核,我使用
!object
命令查看HANDLE地址。输出在这里:
0: kd> !object 0xffffe786e04af080
Object: ffffe786e04af080 Type: (ffffe786da2cfd20) Process
ObjectHeader: ffffe786e04af050 (new version)
HandleCount: 9 PointerCount: 293899
ObjectHeader
指针指向_EPROCESS
进程的pop.exe
的对象头:
0: kd> dt _OBJECT_HEADER ffffe786e04af050
nt!_OBJECT_HEADER
+0x000 PointerCount : 0n293899
+0x008 HandleCount : 0n9
+0x008 NextToFree : 0x00000000`00000009 Void
+0x010 Lock : _EX_PUSH_LOCK
+0x018 TypeIndex : 0x47 'G'
+0x019 TraceFlags : 0 ''
+0x019 DbgRefTrace : 0y0
+0x019 DbgTracePermanent : 0y0
+0x01a InfoMask : 0x88 ''
+0x01b Flags : 0 ''
+0x01b NewObject : 0y0
+0x01b KernelObject : 0y0
+0x01b KernelOnlyAccess : 0y0
+0x01b ExclusiveObject : 0y0
+0x01b PermanentObject : 0y0
+0x01b DefaultSecurityQuota : 0y0
+0x01b SingleHandleEntry : 0y0
+0x01b DeletedInline : 0y0
+0x01c Reserved : 0
+0x020 ObjectCreateInfo : 0xffffe786`dca5ccc0 _OBJECT_CREATE_INFORMATION
+0x020 QuotaBlockCharged : 0xffffe786`dca5ccc0 Void
+0x028 SecurityDescriptor : 0xffffd705`63f2962f Void
+0x030 Body : _QUAD
但是,
0: kd> dq 0xffffe786e04af080
ffffe786`e04af080 00000000`00000003 ffffe786`e04af088
ffffe786`e04af090 ffffe786`e04af088 ffffe786`e04af098
ffffe786`e04af0a0 ffffe786`e04af098 00000000`9e0b9000
ffffe786`e04af0b0 ffffe786`df8ec378 ffffe786`df8ec378
ffffe786`e04af0c0 00000000`00000000 00000000`00000000
ffffe786`e04af0d0 00000000`00200001 00000000`0000000f
ffffe786`e04af0e0 00000000`00000000 00000000`00000000
ffffe786`e04af0f0 00000000`00000000 00000000`00000000
我不知道该地址存储什么,以及
!object
命令如何获取pop.exe
进程的信息。
然后我使用
!process
命令查看主机进程LearnHanle.exe
。
0: kd> !process 0 0 LearnHanle.exe
PROCESS ffffe786e04020c0
SessionId: 1 Cid: 22a8 Peb: 0036b000 ParentCid: 1344
DirBase: 999c2000 ObjectTable: ffffd70567953400 HandleCount: 121.
Image: LearnHanle.exe
我看了
LearnHanle.exe
_HANDLE_TABLE。
0: kd> dt _HANDLE_TABLE ffffd70567953400
nt!_HANDLE_TABLE
+0x000 NextHandleNeedingPool : 0x400
+0x004 ExtraInfoPages : 0n0
+0x008 TableCode : 0xffffd705`6645a000
+0x010 QuotaProcess : 0xffffe786`e04020c0 _EPROCESS
+0x018 HandleTableList : _LIST_ENTRY [ 0xffffd705`67953bd8 - 0xffffd705`67133918 ]
+0x028 UniqueProcessId : 0x22a8
+0x02c Flags : 0
+0x02c StrictFIFO : 0y0
+0x02c EnableHandleExceptions : 0y0
+0x02c Rundown : 0y0
+0x02c Duplicated : 0y0
+0x02c RaiseUMExceptionOnInvalidHandleClose : 0y0
+0x030 HandleContentionEvent : _EX_PUSH_LOCK
+0x038 HandleTableLock : _EX_PUSH_LOCK
+0x040 FreeLists : [1] _HANDLE_TABLE_FREE_LIST
+0x040 ActualEntry : [32] ""
+0x060 DebugInfo : (null)
0: kd> dt _HANDLE_TABLE_ENTRY
nt!_HANDLE_TABLE_ENTRY
+0x000 VolatileLowValue : Int8B
+0x000 LowValue : Int8B
+0x000 InfoTable : Ptr64 _HANDLE_TABLE_ENTRY_INFO
+0x008 HighValue : Int8B
+0x008 NextFreeHandleEntry : Ptr64 _HANDLE_TABLE_ENTRY
+0x008 LeafHandleValue : _EXHANDLE
+0x000 RefCountField : Int8B
+0x000 Unlocked : Pos 0, 1 Bit
+0x000 RefCnt : Pos 1, 16 Bits
+0x000 Attributes : Pos 17, 3 Bits
+0x000 ObjectPointerBits : Pos 20, 44 Bits
+0x008 GrantedAccessBits : Pos 0, 25 Bits
+0x008 NoRightsUpgrade : Pos 25, 1 Bit
+0x008 Spare1 : Pos 26, 6 Bits
+0x00c Spare2 : Uint4B
我想知道进程句柄表中的句柄表项如何与相应的对象类型关联。
而且我也想知道这两位成员的目的:
struct _HANDLE_TABLE_ENTRY::ObjectPointerBits
、struct _OBJECT_HEADER::TypeIndex
和struct _KPROCESS::Header
(_DISPATCHER_HEADER)。
作为示例,我将从资源管理器进程 (6496) 获取文件句柄 (0x1c8):
1: kd> !handle 0x1c8 3 0n6496
PROCESS ffffe4856647a080
SessionId: 2 Cid: 1960 Peb: 00238000 ParentCid: 1904
DirBase: 1409e3002 ObjectTable: ffffd10029c47740 HandleCount: 2981.
Image: explorer.exe
Handle table at ffffd10029c47740 with 2981 entries in use
01c8: Object: ffffe48565dd7110 GrantedAccess: 00100001 (Inherit) (Audit) Entry: ffffd10029ff9720
Object: ffffe48565dd7110 Type: (ffffe485608f7220) File
ObjectHeader: ffffe48565dd70e0 (new version)
HandleCount: 1 PointerCount: 32783
Directory Object: 00000000 Name: \Windows\en-US\explorer.exe.mui {HarddiskVolume3}
从
_EPROCESS
的手柄表开始:
0: kd> dt nt!_eprocess ffffe4856647a080 -y ObjectTable
nt!_EPROCESS
+0x570 ObjectTable : 0xffffd100`29c47740 _HANDLE_TABLE
0: kd> dt nt!_handle_table 0xffffd100`29c47740
nt!_HANDLE_TABLE
+0x000 NextHandleNeedingPool : 0x3800
+0x004 ExtraInfoPages : 0n0
+0x008 TableCode : 0xffffd100`29ef4001
+0x010 QuotaProcess : 0xffffe485`6647a080 _EPROCESS
+0x018 HandleTableList : _LIST_ENTRY [ 0xffffd100`29c47418 - 0xffffd100`29c47d98 ]
+0x028 UniqueProcessId : 0x1960
+0x02c Flags : 0
+0x02c StrictFIFO : 0y0
+0x02c EnableHandleExceptions : 0y0
+0x02c Rundown : 0y0
+0x02c Duplicated : 0y0
+0x02c RaiseUMExceptionOnInvalidHandleClose : 0y0
+0x030 HandleContentionEvent : _EX_PUSH_LOCK
+0x038 HandleTableLock : _EX_PUSH_LOCK
+0x040 FreeLists : [1] _HANDLE_TABLE_FREE_LIST
+0x040 ActualEntry : [32] ""
+0x060 DebugInfo : (null)
TableCode
字段指向一个表数组,较低位决定应使用哪个表:
0: kd> dq 0xffffd100`29ef4000
ffffd100`29ef4000 ffffd100`29ff9000 ffffd100`29ef5000
ffffd100`29ef4010 ffffd100`255fd000 ffffd100`29d96000
ffffd100`29ef4020 ffffd100`2a1fd000 ffffd100`2a0f7000
ffffd100`29ef4030 ffffd100`258bc000 ffffd100`25b5a000
ffffd100`29ef4040 ffffd100`25bff000 ffffd100`25cfc000
ffffd100`29ef4050 ffffd100`25b57000 ffffd100`25dfe000
ffffd100`29ef4060 ffffd100`258c6000 ffffd100`2802c000
ffffd100`29ef4070 00000000`00000000 00000000`00000000
nt!ExpLookupHandleTableEntry
中的代码有助于确定如何使用表数组(第一个参数是RCX中的_HANDLE_TABLE*
,第二个参数是RDX中的句柄):
PAGE:00000001407427E0 ExpLookupHandleTableEntry proc near ; CODE XREF: ObDuplicateObject+180↑p
PAGE:00000001407427E0 ; ExMapHandleToPointer+11↑p ...
PAGE:00000001407427E0
PAGE:00000001407427E0 ; FUNCTION CHUNK AT PAGE:0000000140895396 SIZE 00000022 BYTES
PAGE:00000001407427E0
PAGE:00000001407427E0 mov eax, [rcx+_HANDLE_TABLE.NextHandleNeedingPool]
PAGE:00000001407427E2 and rdx, 0FFFFFFFFFFFFFFFCh
PAGE:00000001407427E6 cmp rdx, rax
PAGE:00000001407427E9 jnb short loc_140742820
PAGE:00000001407427EB mov r8, [rcx+_HANDLE_TABLE.TableCode]
PAGE:00000001407427EF mov eax, r8d
PAGE:00000001407427F2 and eax, 3
PAGE:00000001407427F5 cmp eax, 1
PAGE:00000001407427F8 jnz short loc_140742812
PAGE:00000001407427FA mov rax, rdx
PAGE:00000001407427FD shr rax, 0Ah
PAGE:0000000140742801 mov rax, [r8+rax*8-1]
PAGE:0000000140742806
PAGE:0000000140742806 loc_140742806: ; CODE XREF: ExpLookupHandleTableEntry+152BD3↓j
PAGE:0000000140742806 and edx, 3FFh
PAGE:000000014074280C lea rax, [rax+rdx*4]
PAGE:0000000140742810 retn
PAGE:0000000140742810 ; ---------------------------------------------------------------------------
PAGE:0000000140742811 align 2
PAGE:0000000140742812
PAGE:0000000140742812 loc_140742812: ; CODE XREF: ExpLookupHandleTableEntry+18↑j
PAGE:0000000140742812 test eax, eax
PAGE:0000000140742814 jnz loc_140895396
PAGE:000000014074281A lea rax, [r8+rdx*4]
PAGE:000000014074281E retn
PAGE:000000014074281E ; ---------------------------------------------------------------------------
PAGE:000000014074281F align 20h
PAGE:0000000140742820
PAGE:0000000140742820 loc_140742820: ; CODE XREF: ExpLookupHandleTableEntry+9↑j
PAGE:0000000140742820 xor eax, eax
PAGE:0000000140742822 retn
PAGE:0000000140742822 ExpLookupHandleTableEntry endp
在我的例子中,它只是数组中的第一个表,因此句柄条目位于
table_base + (handle * 4)
。尽管每个表条目都是 _HANDLE_TABLE_ENTRY
(大小为 0x10),但每个句柄都是 4 的倍数(因此 0x10 / 4 = 4):
1: kd> ? ffffd100`29ff9000 + (0x1c8 * 4)
Evaluate expression: -51676341889248 = ffffd100`29ff9720
表格条目:
1: kd> dt _handle_table_entry ffffd100`29ff9720
nt!_HANDLE_TABLE_ENTRY
+0x000 VolatileLowValue : 0n-1980064459403493377
+0x000 LowValue : 0n-1980064459403493377
+0x000 InfoTable : 0xe48565dd`70e0ffff _HANDLE_TABLE_ENTRY_INFO
+0x008 HighValue : 0n1048577
+0x008 NextFreeHandleEntry : 0x00000000`00100001 _HANDLE_TABLE_ENTRY
+0x008 LeafHandleValue : _EXHANDLE
+0x000 RefCountField : 0n-1980064459403493377
+0x000 Unlocked : 0y1
+0x000 RefCnt : 0y0111111111111111 (0x7fff)
+0x000 Attributes : 0y000
+0x000 ObjectPointerBits : 0y11100100100001010110010111011101011100001110 (0xe48565dd70e)
+0x008 GrantedAccessBits : 0y0000100000000000000000001 (0x100001)
+0x008 NoRightsUpgrade : 0y0
+0x008 Spare1 : 0y000000 (0)
+0x00c Spare2 : 0
这里至少有两个有趣的东西:
GrantedAccessBits
:这是对象的授予访问掩码(这里有 0x100001
,对于文件对象来说它只是 SYNCHRONIZE (0x10000) | FILE_READ_DATA (0x1)
ObjectPointerBits
:经过计算后将是指向对象_OBJECT_HEADER
的指针。
根据
ObjectPointerBits
值,您需要执行以下操作来获取对象头地址:(ObjectPointerBits << 4) | 0xffff000000000000
1: kd> ? (0xe48565dd70e << 4) | 0xffff000000000000
Evaluate expression: -30213385916192 = ffffe485`65dd70e0
1: kd> dt _object_header ffffe485`65dd70e0
nt!_OBJECT_HEADER
+0x000 PointerCount : 0n32783
+0x008 HandleCount : 0n1
+0x008 NextToFree : 0x00000000`00000001 Void
+0x010 Lock : _EX_PUSH_LOCK
+0x018 TypeIndex : 0x14 ''
+0x019 TraceFlags : 0 ''
+0x019 DbgRefTrace : 0y0
+0x019 DbgTracePermanent : 0y0
+0x01a InfoMask : 0x4c 'L'
+0x01b Flags : 0 ''
+0x01b NewObject : 0y0
+0x01b KernelObject : 0y0
+0x01b KernelOnlyAccess : 0y0
+0x01b ExclusiveObject : 0y0
+0x01b PermanentObject : 0y0
+0x01b DefaultSecurityQuota : 0y0
+0x01b SingleHandleEntry : 0y0
+0x01b DeletedInline : 0y0
+0x01c Reserved : 0
+0x020 ObjectCreateInfo : 0xffffe485`62c5ecc0 _OBJECT_CREATE_INFORMATION
+0x020 QuotaBlockCharged : 0xffffe485`62c5ecc0 Void
+0x028 SecurityDescriptor : (null)
+0x030 Body : _QUAD
从对象头中,您可以通过执行以下计算来获取对象类型:
*((BYTE*)nt!ObHeaderCookie) ^ second_byte_of_object_header_address ^ _OBJECT_HEADER.TypeIndex
1: kd> db nt!ObHeaderCookie L1
fffff800`7911ed74 4c
1: kd> ? (ffffe485`65dd70e0 >> 8) & 0xff
Evaluate expression: 112 = 00000000`00000070
1: kd> dt _object_header ffffe485`65dd70e0 -y TypeIndex
nt!_OBJECT_HEADER
+0x018 TypeIndex : 0x14 ''
1: kd> ? 0x4c ^ 0x70 ^ 0x14
Evaluate expression: 40 = 00000000`00000028
上面的结果(在本例中为 0x28)是内核类型数组的索引,该数组是一个名为
nt!ObTypeIndexTable
的全局变量(_OBJECT_TYPE
的数组):
1: kd> dt _OBJECT_TYPE poi(nt!ObTypeIndexTable + (0x28 * 8))
nt!_OBJECT_TYPE
+0x000 TypeList : _LIST_ENTRY [ 0xffffe485`608f7220 - 0xffffe485`608f7220 ]
+0x010 Name : _UNICODE_STRING "File"
+0x020 DefaultObject : 0x00000000`0000009b Void
+0x028 Index : 0x28 '('
+0x02c TotalNumberOfObjects : 0x27cc
+0x030 TotalNumberOfHandles : 0xaae
+0x034 HighWaterNumberOfObjects : 0x2a42
+0x038 HighWaterNumberOfHandles : 0xd36
+0x040 TypeInfo : _OBJECT_TYPE_INITIALIZER
+0x0b8 TypeLock : _EX_PUSH_LOCK
+0x0c0 Key : 0x656c6946
+0x0c8 CallbackList : _LIST_ENTRY [ 0xffffe485`608f72e8 - 0xffffe485`608f72e8 ]
所以它是一个文件对象(由
nt!FILE_OBJECT
表示)。
_DISPATCHER_HEADER
如果一个对象是可等待的(即它的句柄可以传递给
WaitForSingleObject
或WaitForMutipleObjects
),那么它有一个_DISPATCH_HEADER
。