Windows 对象管理

😄

1 对象目录

1.1 对象目录管理

对象分为内核对象、用户对象。用户对象主要用于 GUI 下(没有对象头)。

  • 内核对象:包含对象头、对象体。
    • 无名对象,其他进程不能通过对象名打开对象,对象不可以共享。不命名的对象一般意义不大。
    • 命名对象,可以通过对象名与其他进程共享内核对象。
  • 用户对象:不具有对象头,参见《GUI Subsystem 用户对象》。

在系统中,通过对象目录对命名对象进行组织管理,对象目录是树状结构而非网状结构。

  1. 根节点: 目录对象(_OBJECT_DIRECTORY)。根节点对象名为\。
  2. 中间节点:目录对象(_OBJECT_DIRECTORY)、符号链接对象(_OBJECT_SYMBOLIC_LINK)。
  3. 叶节点:普通内核对象、目录对象(_OBJECT_DIRECTORY)。

说明:根节点是一个数据目录类型,节点名为 “\”,其为 37 个元素的哈希数组,每个元素串起一个单链表队列,同一个链表上的所有节点对象名的 Hash 值相同。

2.png

目录对象:可以是根节点、中间节点、叶节点。

1
2
3
4
5
6
7
8
9
10
11
12
//0x158 bytes (sizeof)
struct _OBJECT_DIRECTORY
{
struct _OBJECT_DIRECTORY_ENTRY* HashBuckets[37]; //0x0
struct _EX_PUSH_LOCK Lock; //0x128
struct _DEVICE_MAP* DeviceMap; //0x130
struct _OBJECT_DIRECTORY* ShadowDirectory; //0x138
VOID* NamespaceEntry; //0x140
VOID* SessionObject; //0x148
ULONG Flags; //0x150
ULONG SessionId; //0x154
};

主要关注第一个成员,可以看到它是一个含有 37 = NUMBER_HASH_BUCKETS 个成员的指针数组,每个成员都指向 _OBJECT_DIRECTORY_ENTRY 结构。

1
2
3
4
5
6
7
//0x18 bytes (sizeof)
struct _OBJECT_DIRECTORY_ENTRY
{
struct _OBJECT_DIRECTORY_ENTRY* ChainLink; //0x0
VOID* Object; //0x8
ULONG HashValue; //0x10
};
  • ChainLink:指向下一个 _OBJECT_DIRECTORY_ENTRY,是个单向链表。同一条链表上的所有节点对象名的 Hash 值相同。
  • Object:指向该 Entry 所管理的那个对象,比如 EPROCESS,ETHREAD 等等。
  • HashValue:该 Entry 的 Hash 值

注意:目录项 _OBJECT_DIRECTORY_ENTRY 并非内核对象,但是每一个内核对象插入到对象目录树时,必须有目录项来进行承接才行。

符号链接 _OBJECT_SYMBOLIC_LINK:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//0x28 bytes (sizeof)
struct _OBJECT_SYMBOLIC_LINK
{
union _LARGE_INTEGER CreationTime; //0x0
union
{
struct _UNICODE_STRING LinkTarget; //0x8
struct
{
LONG (*Callback)(struct _OBJECT_SYMBOLIC_LINK* arg1, VOID* arg2, struct _UNICODE_STRING* arg3, VOID** arg4); //0x8
VOID* CallbackContext; //0x10
};
};
ULONG DosDeviceDriveIndex; //0x18
ULONG Flags; //0x1c
ULONG AccessMask; //0x20
};

其中 LinkTarget 就是其关联的对象名,关于该对象的解析可看 DKOM – Now with Symbolic Links!。

节点遍历:

如果从根节点或某个中间节点开始遍历某个对象,在路径上的每个节点前加 “\” 进行分隔,就形成一个路径名。

  • 绝对路径:根节点\中间节点\叶节点。
  • 相对路径:中间节点\叶节点。

1.png

1.2 创建目录对象

创建目录对象使用 ZwCreateDirectoryObject 函数。

1
2
3
4
5
6
NTSYSAPI NTSTATUS ZwCreateDirectoryObject(
[out] PHANDLE DirectoryHandle, // 输出值,新创建的目录对象句柄
[in] ACCESS_MASK DesiredAccess, // 指定对该目录对象的访问权限
[in] POBJECT_ATTRIBUTES ObjectAttributes // 该对象的属性(如对象名称)
);

  • DesiredAccess:该参数除了可以使用 ACCESS_MASK 值进行定义外,还可以指定以下一个或多个权限:

    ACCESS_MASK标志 访问类型
    DIRECTORY_QUERY 查询
    DIRECTORY_TRAVERSE 名称查找
    DIRECTORY_CREATE_OBJECT 名称创建
    DIRECTORY_CREATE_SUBDIRECTORY 创建子目录
    DIRECTORY_ALL_ACCESS 上述所有类型
  • 使用 ObjectAttributes 结构指针前,必须先调用 InitializeObjectAttributes 对其进行初始化。

该函数核心是通过 ObCreateObject 创建目录对象,然后再用 ObInsertObject 将对象插入到句柄表中。

对象层次目录中插入,查询和删除目录或目录项常用的函数如下:

  • ObpLookupDirectoryEntry:在一个指定的目录中查找一个指定名称的对象。
  • ObpInsertDirectoryEntry:把一个对象插入到一个目录中。
  • ObpDeleteDirectoryEntry:删除指定目录中的指定对象。

1.2 对象遍历

查看/遍历内核对象主要有三种方法:

  1. 通过 WinObj.exe 工具(以管理员权限运行)。
  2. 通过 Windbg(使用全局指针 ObpRootDirectoryObject)。
  3. 通过代码遍历。

一、WinObj.exe 工具查看内核对象(以管理员权限运行)

可以从微软官方 SysinternalsSuite 下载该工具。

3.png

可以看到根节点是 \,然后下面的中间节点是目录对象或符号链接对象。如子目录 Device 二级目录下还有 4 个三级目录。

二、通过 Windbg 查看

  1. 通过全局指针 ObpRootDirectoryObject 找到根目录节点。

    1
    2
    3
    kd> dq ObpRootDirectoryObject
    fffff800`588440a8 ffff9e02`12423ce0 ffffaf8f`846b1380
    fffff800`588440b8 00000000`00000000 ffffaf8f`846b1380

    可以看到根节点的地址为 0xffff9e02’12423ce0。

  2. 查看根节点目录。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    kd> dt _OBJECT_DIRECTORY ffff9e02`12423ce0
    ntdll!_OBJECT_DIRECTORY
    +0x000 HashBuckets : [37] (null)
    +0x128 Lock : _EX_PUSH_LOCK
    +0x130 DeviceMap : (null)
    +0x138 ShadowDirectory : (null)
    +0x140 NamespaceEntry : (null)
    +0x148 SessionObject : (null)
    +0x150 Flags : 0
    +0x154 SessionId : 0xffffffff
  3. 查看 HashBuckets 数组。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    kd> dx -id 0,0,ffffaf8f846a1040 -r1 (*((nt!_OBJECT_DIRECTORY_ENTRY * (*)[37])0xffff9e0212423ce0))
    (*((nt!_OBJECT_DIRECTORY_ENTRY * (*)[37])0xffff9e0212423ce0)) [Type: _OBJECT_DIRECTORY_ENTRY * [37]]
    [0] : 0x0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [1] : 0xffff9e0212bbbd00 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [2] : 0xffff9e02183524e0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [3] : 0xffff9e0212547550 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [4] : 0x0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [5] : 0xffff9e02124b45d0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [6] : 0xffff9e0215b52f60 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [7] : 0x0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [8] : 0xffff9e02124b43c0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [9] : 0xffff9e0218351d90 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [10] : 0xffff9e021883a0e0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [11] : 0xffff9e0212bbbdc0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [12] : 0x0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [13] : 0xffff9e0212bbeaf0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [14] : 0xffff9e02125450f0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [15] : 0xffff9e0212cfe6e0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [16] : 0xffff9e02124af630 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [17] : 0xffff9e0212bbc330 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [18] : 0xffff9e02124b49f0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [19] : 0xffff9e0212cfe860 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [20] : 0xffff9e0212b66be0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [21] : 0xffff9e0217efd440 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [22] : 0xffff9e0212bbc090 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [23] : 0xffff9e0212537580 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [24] : 0xffff9e02125472e0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [25] : 0x0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [26] : 0xffff9e0215b52360 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [27] : 0x0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [28] : 0xffff9e02183523f0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [29] : 0x0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [30] : 0xffff9e0212547130 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [31] : 0x0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [32] : 0xffff9e0212565a40 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [33] : 0x0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [34] : 0xffff9e0215b52db0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [35] : 0xffff9e0218352870 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [36] : 0xffff9e0217ef8a00 [Type: _OBJECT_DIRECTORY_ENTRY *]

    可以看到,如下标 0、4、7、12 的目录项下面没有数据。

  4. 以下标 1 为例查看其链表队列。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    kd> dx -id 0,0,ffffaf8f846a1040 -r1 ((ntkrnlmp!_OBJECT_DIRECTORY_ENTRY *)0xffff9e0212bbbd00)
    ((ntkrnlmp!_OBJECT_DIRECTORY_ENTRY *)0xffff9e0212bbbd00) : 0xffff9e0212bbbd00 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [+0x000] ChainLink : 0xffff9e02124af3c0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [+0x008] Object : 0xffffaf8f87c66e50 [Type: void *]
    [+0x010] HashValue : 0xb8888f8d [Type: unsigned long]

    kd> dx -id 0,0,ffffaf8f846a1040 -r1 ((ntkrnlmp!_OBJECT_DIRECTORY_ENTRY *)0xffff9e02124af3c0)
    ((ntkrnlmp!_OBJECT_DIRECTORY_ENTRY *)0xffff9e02124af3c0) : 0xffff9e02124af3c0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [+0x000] ChainLink : 0x0 [Type: _OBJECT_DIRECTORY_ENTRY *]
    [+0x008] Object : 0xffff9e0212426230 [Type: void *]
    [+0x010] HashValue : 0x6c1c47fb [Type: unsigned long]

    可以看到根节点 OBJECT_DIRECTORY.HashBuckets[1] 链表上挂着两个节点,下面分别查看一下节点类型。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    kd> !object 0xffffaf8f87c66e50
    Object: ffffaf8f87c66e50 Type: (ffffaf8f8468e4e0) Mutant
    ObjectHeader: ffffaf8f87c66e20 (new version)
    HandleCount: 0 PointerCount: 1
    Directory Object: ffff9e0212423ce0 Name: PendingRenameMutex

    kd> !object 0xffff9e0212426230
    Object: ffff9e0212426230 Type: (ffffaf8f846b14e0) Directory
    ObjectHeader: ffff9e0212426200 (new version)
    HandleCount: 0 PointerCount: 69
    Directory Object: ffff9e0212423ce0 Name: ObjectTypes

    Hash Address Type Name
    ---- ------- ---- ----
    00 ffffaf8f846b9380 Type TmTm
    01 ffffaf8f8468ed20 Type Desktop
    ffffaf8f8468f140 Type Process
    02 ffffaf8f846b9d20 Type EnergyTracker
    ffffaf8f846b9220 Type RegistryTransaction
    03 ffffaf8f8468ff00 Type DebugObject
    ...

    第一个节点是 Mutant、第二个节点为 Directory。

1.3 对象名哈希值计算

待办:需要逆向分析 ObpLookupDirectoryEntry 函数,解析出函数名的 Hash 过程。https://bbs.kanxue.com/thread-262316.htm。

ObpLookupDirectoryEntry:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
PAGE:000000014078122C // PVOID ObpLookupDirectoryEntry(
PAGE:000000014078122C // IN POBJECT_DIRECTORY Directory,
PAGE:000000014078122C // IN PUNICODE_STRING Name,
PAGE:000000014078122C // IN ULONG Attributes,
PAGE:000000014078122C // IN UCHAR SearchShadow,
PAGE:000000014078122C // IN POBP_LOOKUP_CONTEXT Context
PAGE:000000014078122C // )
PAGE:000000014078122C
PAGE:000000014078122C ObpLookupDirectoryEntry proc near // CODE XREF: ObInitSystem+576↓p
PAGE:000000014078122C // DATA XREF: .pdata:000000014010BFC0↑o
PAGE:000000014078122C
PAGE:000000014078122C var_18 = byte ptr -18h
PAGE:000000014078122C var_10 = dword ptr -10h
PAGE:000000014078122C
PAGE:000000014078122C sub rsp, 38h
PAGE:0000000140781230 mov qword ptr [rsp+38h+var_10], r9 // int
PAGE:0000000140781235 xor r9d, r9d
PAGE:0000000140781238 mov [rsp+38h+var_18], 0 // char
PAGE:000000014078123D call ObpLookupDirectoryEntryEx
PAGE:0000000140781242 add rsp, 38h
PAGE:0000000140781246 retn
PAGE:0000000140781246 // ---------------------------------------------------------------------------
PAGE:0000000140781247 db 0CCh
PAGE:0000000140781247 ObpLookupDirectoryEntry endp

ObpLookupDirectoryEntryEx:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
PAGE:00000001406E4C98 // __int64 __usercall ObpLookupDirectoryEntryEx@<rax>(PVOID Object@<rcx>, char, int, __int64)
PAGE:00000001406E4C98 ObpLookupDirectoryEntryEx proc near // CODE XREF: ObCloseHandleTableEntry+744↑p
PAGE:00000001406E4C98 // ObpDeleteNameCheck+19B↑p ...
PAGE:00000001406E4C98
PAGE:00000001406E4C98 arg_0 = qword ptr 8
PAGE:00000001406E4C98 arg_8 = qword ptr 10h
PAGE:00000001406E4C98 arg_10 = qword ptr 18h
PAGE:00000001406E4C98 arg_18 = qword ptr 20h
PAGE:00000001406E4C98 arg_20 = byte ptr 28h
PAGE:00000001406E4C98 arg_28 = dword ptr 30h
PAGE:00000001406E4C98
PAGE:00000001406E4C98 // FUNCTION CHUNK AT PAGE:0000000140827254 SIZE 0000009E BYTES
PAGE:00000001406E4C98
PAGE:00000001406E4C98 mov rax, rsp
PAGE:00000001406E4C9B mov [rax+8], rbx
PAGE:00000001406E4C9F mov [rax+10h], rbp
PAGE:00000001406E4CA3 mov [rax+18h], rsi
PAGE:00000001406E4CA7 mov [rax+20h], rdi
PAGE:00000001406E4CAB push r12
PAGE:00000001406E4CAD push r14
PAGE:00000001406E4CAF push r15
PAGE:00000001406E4CB1 sub rsp, 20h
PAGE:00000001406E4CB5 movzx r11d, [rdx+UNICODE_STRING.Length] // Name
PAGE:00000001406E4CB9 mov r14d, r8d
PAGE:00000001406E4CBC mov rdi, [rdx+UNICODE_STRING.Buffer]
PAGE:00000001406E4CC0 mov r12b, r9b // 0
PAGE:00000001406E4CC3 shr r14d, 6
PAGE:00000001406E4CC7 mov r15, rdx
PAGE:00000001406E4CCA and r14b, 1
PAGE:00000001406E4CCE shr r11d, 1 // r11d = Name 中字符的个数
PAGE:00000001406E4CCE //
PAGE:00000001406E4CCE // Length 是缓冲区字节数,是字符的两倍
PAGE:00000001406E4CD1 mov rbp, rcx
PAGE:00000001406E4CD4 cmp r11d, 4
PAGE:00000001406E4CD8 jb loc_1406E4DDE // Name 中字符的个数小于 4
PAGE:00000001406E4CDE xor ebx, ebx
PAGE:00000001406E4CE0
PAGE:00000001406E4CE0 loc_1406E4CE0: // CODE XREF: ObpLookupDirectoryEntryEx+87↓j
PAGE:00000001406E4CE0 mov r9, [rdi]
PAGE:00000001406E4CE3 mov rax, 0FF80FF80FF80FF80h
PAGE:00000001406E4CED test rax, r9
PAGE:00000001406E4CF0 jnz loc_140827254
PAGE:00000001406E4CF6 mov rax, 0FFDFFFDFFFDFFFDFh
PAGE:00000001406E4D00 and r9, rax
PAGE:00000001406E4D03
PAGE:00000001406E4D03 loc_1406E4D03: // CODE XREF: ObpLookupDirectoryEntryEx+1425EB↓j
PAGE:00000001406E4D03 mov rax, rbx
PAGE:00000001406E4D06 add rdi, 8
PAGE:00000001406E4D0A shr rax, 1
PAGE:00000001406E4D0D lea rbx, [rbx+rbx*2]
PAGE:00000001406E4D11 add rax, r9
PAGE:00000001406E4D14 add r11d, 0FFFFFFFCh
PAGE:00000001406E4D18 add rbx, rax
PAGE:00000001406E4D1B cmp r11d, 4
PAGE:00000001406E4D1F jnb short loc_1406E4CE0
PAGE:00000001406E4D21 mov r10, rbx
PAGE:00000001406E4D24 shr r10, 20h
PAGE:00000001406E4D28 add r10d, ebx
PAGE:00000001406E4D2B jmp short loc_1406E4D3C // r11d = Name 中字符的个数
PAGE:00000001406E4D2D // ---------------------------------------------------------------------------
PAGE:00000001406E4D2D
PAGE:00000001406E4D2D loc_1406E4D2D: // CODE XREF: ObpLookupDirectoryEntryEx+C7↓j
PAGE:00000001406E4D2D movzx ecx, dx
PAGE:00000001406E4D30 call NLS_UPCASE
PAGE:00000001406E4D35 movzx r9d, ax
PAGE:00000001406E4D39 add r10d, r9d
PAGE:00000001406E4D3C
PAGE:00000001406E4D3C loc_1406E4D3C: // CODE XREF: ObpLookupDirectoryEntryEx+93↑j
PAGE:00000001406E4D3C // ObpLookupDirectoryEntryEx+D0↓j ...
PAGE:00000001406E4D3C test r11d, r11d // r11d = Name 中字符的个数
PAGE:00000001406E4D3F jz short loc_1406E4D6A // 是无名称对象
PAGE:00000001406E4D41 movzx edx, word ptr [rdi] // 取低 1 字符
PAGE:00000001406E4D44 mov ecx, r10d
PAGE:00000001406E4D47 shr ecx, 1
PAGE:00000001406E4D49 lea r10d, [r10+r10*2]
PAGE:00000001406E4D4D add r10d, ecx
PAGE:00000001406E4D50 lea rdi, [rdi+2] // rdi 指向低 2 字符
PAGE:00000001406E4D54 dec r11d
PAGE:00000001406E4D57 cmp edx, 'a'
PAGE:00000001406E4D5A jb short loc_1406E4D65
PAGE:00000001406E4D5C cmp edx, 'z'
PAGE:00000001406E4D5F ja short loc_1406E4D2D
PAGE:00000001406E4D61 add r10d, 0FFFFFFE0h
PAGE:00000001406E4D65
PAGE:00000001406E4D65 loc_1406E4D65: // CODE XREF: ObpLookupDirectoryEntryEx+C2↑j
PAGE:00000001406E4D65 add r10d, edx
PAGE:00000001406E4D68 jmp short loc_1406E4D3C // r11d = Name 中字符的个数
PAGE:00000001406E4D6A // ---------------------------------------------------------------------------
PAGE:00000001406E4D6A
PAGE:00000001406E4D6A loc_1406E4D6A: // CODE XREF: ObpLookupDirectoryEntryEx+A7↑j
PAGE:00000001406E4D6A mov rdi, qword ptr [rsp+38h+arg_28] // 是无名称对象
PAGE:00000001406E4D6F mov eax, 0BACF914Dh
PAGE:00000001406E4D74 mul r10d
PAGE:00000001406E4D77 mov eax, r10d
PAGE:00000001406E4D7A mov r9b, r14b
PAGE:00000001406E4D7D sub eax, edx
PAGE:00000001406E4D7F mov [rdi+18h], r10d
PAGE:00000001406E4D83 shr eax, 1
PAGE:00000001406E4D85 mov r8, rdi
PAGE:00000001406E4D88 add eax, edx
PAGE:00000001406E4D8A mov rdx, r15
PAGE:00000001406E4D8D shr eax, 5
PAGE:00000001406E4D90 movzx eax, ax
PAGE:00000001406E4D93 imul ecx, eax, 25h
PAGE:00000001406E4D96 movzx eax, r10w
PAGE:00000001406E4D9A sub ax, cx
PAGE:00000001406E4D9D mov rcx, rbp
PAGE:00000001406E4DA0 mov [rdi+1Ch], ax
PAGE:00000001406E4DA4 call ObpLookupDirectoryUsingHash
PAGE:00000001406E4DA9 mov rsi, rax
PAGE:00000001406E4DAC test rax, rax
PAGE:00000001406E4DAF jz short loc_1406E4DD4
PAGE:00000001406E4DB1
PAGE:00000001406E4DB1 loc_1406E4DB1: // CODE XREF: ObpLookupDirectoryEntryEx+13F↓j
PAGE:00000001406E4DB1 // ObpLookupDirectoryEntryEx+14264A↓j ...
PAGE:00000001406E4DB1 mov rbx, [rsp+38h+arg_0]
PAGE:00000001406E4DB6 mov rax, rsi
PAGE:00000001406E4DB9 mov rsi, [rsp+38h+arg_10]
PAGE:00000001406E4DBE mov rbp, [rsp+38h+arg_8]
PAGE:00000001406E4DC3 mov rdi, [rsp+38h+arg_18]
PAGE:00000001406E4DC8 add rsp, 20h
PAGE:00000001406E4DCC pop r15
PAGE:00000001406E4DCE pop r14
PAGE:00000001406E4DD0 pop r12
PAGE:00000001406E4DD2 retn
PAGE:00000001406E4DD2 // ---------------------------------------------------------------------------
PAGE:00000001406E4DD3 align 4
PAGE:00000001406E4DD4
PAGE:00000001406E4DD4 loc_1406E4DD4: // CODE XREF: ObpLookupDirectoryEntryEx+117↑j
PAGE:00000001406E4DD4 test r12b, r12b
PAGE:00000001406E4DD7 jz short loc_1406E4DB1
PAGE:00000001406E4DD9 jmp loc_140827288
PAGE:00000001406E4DDE // ---------------------------------------------------------------------------
PAGE:00000001406E4DDE
PAGE:00000001406E4DDE loc_1406E4DDE: // CODE XREF: ObpLookupDirectoryEntryEx+40↑j
PAGE:00000001406E4DDE xor r10d, r10d // Name 中字符的个数小于 4
PAGE:00000001406E4DE1 jmp loc_1406E4D3C // r11d = Name 中字符的个数
PAGE:00000001406E4DE1 ObpLookupDirectoryEntryEx endp

代码还原如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
ULONG64 ObpLookupDirectoryEntryEx(
IN POBJECT_DIRECTORY Directory,
IN PUNICODE_STRING Name,
IN ULONG Attributes,
IN UCHAR SearchShadow,
IN POBP_LOOKUP_CONTEXT Context
OUT PVOID pOutPutValue
)
{
ULONG64 uNameLen = 0; // 对象字符长度
PWCHAR pwName = NULL;
PVOID pOutTemp = NULL;
ULONG64 uHashValue = 0;
ULONG uHashValueLow = 0;
ULONG uHashValueHigh = 0;
PVOID pTempDirectory = NULL;

Attributes /= 64;
Attributes &= 1;

uNameLen = Name.Length;
pwName = Name.Buffer;
uNameLen /= 2; // 对象名称字符个数 = 字节数/ sizeof(WCHAR)

if(uNameLen < 4)
{
ULONG TempR10d = 0;
ULONG uTemp = 0;
ULONG uTemp2 = 0;

TempR10d = 0;

loc_1406E4D3C:
if(uNameLen == 0) // 是无名称对象
{
ULONG uPading = 0;
ULONG64 uRet = 0;

pOutTemp = pOutPutValue;
uPading = 0xBACF914D;

uHashValue = TempR10d * uPading;
uHashValueLow = *(PULONG)uHashValue;
uHashValueHigh = *(PULONG)((ULONG64)uHashValue+4);

uHashValueLow = TempR10d;
uHashValueLow -= uHashValueHigh;
uHashValueLow /= 2;
uHashValueLow += uHashValueHigh;
uHashValueLow /= 32;
uHashValueLow = *(PWORD)uHashValueLow; // 取低 2 字节
uTemp = uHashValueLow * 37;
uTemp2 = *(PWORD)TempR10d;
uTemp2 = uTemp2 - *(PWORD)uTemp;

pTempDictory = Directory;
pOutTemp->_0x18 = TempR10d;
pOutTemp->_0x1C = *(PWORD)uTemp2;

uRet = ObpLookupDirectoryUsingHash(pTempDictory, Name, pOutTemp, *(PBYTE)Attributes);

if(uRet == 0)
{
if(SearchShadow == 0) return uRet;
else { goto loc_140827288 };
}
return uRet;
}
else // 有名称对象,对象名称小于 4 字符
{
PWCHAR CurrentwChar = *(PWORD)pwName; // 取最低 1 字符
PWCHAR NextwChar = NULL;

TempR10d = TempR10d * 3 + (TempR10d >> 1);

NextwChar = *(PWORD)((ULONG64)pwName + 2); // 取下一个字符
uNameLen -= 1;

if(CurrentwChar < 'a')
{
TempR10d += CurrentwChar;
goto loc_1406E4D3C;
}
if(CurrentwChar > 'z')
{
NLS_UPCASE(CurrentwChar);
/**/
/**/
goto loc_1406E4D3C;
}

TempR10d += 0xFFFFFFE0;
TempR10d += CurrentwChar;
goto loc_1406E4D3C;
}
} // if(uNameLen < 4)
else // if(uNameLen >= 4)
{
ULONG TempRbx = 0;
ULONG TempRax = 0;
ULONG TempR10 = 0;

TempRbx = 0;

do
{
PVOID pCurrent = NULL;
pCurrent = *(PULONG64)pwName;

if(pCurrent & 0xFF80FF80FF80FF80)
{
goto loc_140827254 ;
}

pCurrent &= 0xFFDFFFDFFFDFFFDF;

pwName += 4; // 取下面 4 个字节
uNameLen -= 4; // 字符个数 -4

TempRax = TempRbx; // 0
TempRax /= 2;
TempRax += pCurrent;
TempRbx = TempRbx * 3 + TempRax;

if(uNameLen < 4)
{
TempR10 = TempRbx;
TempR10 /= 32;
TempR10d = *(PWORD)TempR10 + TempRbx;
goto loc_1406E4D3C;
}
}while(uNameLen >= 4)

} // if(uNameLen >= 4)

}

1.4 符号链接

DKOM – Now with Symbolic Links!

2 对象结构

一个内核对象由以下五部分构成:

  1. 对象内存头
  2. 可选信息头
  3. 对象头
  4. 对象体
  5. 对象脚

4.png

1.1 POOL_HEADER

在每块系统内存池被分配的时候,都会默认分配该结构(在申请返回的内存前)。系统内存池即内核 System 进程的堆。

1
2
3
4
5
6
7
8
9
10
kd> dt nt!_POOL_HEADER
+0x000 PreviousSize : Pos 0, 8 Bits
+0x000 PoolIndex : Pos 8, 8 Bits
+0x002 BlockSize : Pos 0, 8 Bits
+0x002 PoolType : Pos 8, 8 Bits
+0x000 Ulong1 : Uint4B
+0x004 PoolTag : Uint4B
+0x008 ProcessBilled : Ptr64 _EPROCESS
+0x008 AllocatorBackTraceIndex : Uint2B
+0x00a PoolTagHash : Uint2B

POOL_HEADER 结构用来描述每一块已经分配的内核内存块属性。该结构从 Windows 10 1909开始发生了使用上的变化。具体需要研究分页/非分页内存池的分配相关内容。以下文章较详细描述了 POOL_HEADER 的意义和用途:

4.png

1.2 可选信息头

目前,可选信息头共有 8 个,理论上已经全部使用,因为 InfoMask 是 UCHAR 类型,共 8 Bit(每个 Bit 对应一种可选信息头)。

5.png

特别说明:扩展信息可选头如果存在,就意味着有对象脚存在。_OBJECT_HEADER_EXTENDED_INFO 中有成员指向对象脚结构。

每个可选信息头的大小、位置顺序都是固定的。如何确定一个可选信息头是否存在:

  • OBJECT_HEADER_CREATOR_INFO:由 OBJECT_HEADER.Flags 决定是否存在。
  • 其他 7 个可选信息头:由 OBJECT_HEADER.InfoMask 决定是否存在。

每个可选信息头对应 OBJECT_HEADER.InfoMask 1 Byte 大小数据中的一位掩码(如下表8-17),然后使用 ObpInfoMaskToOffset[InfoMask & (DesiredHeaderBit | (DesiredHeaderBit-1))] 找到期望的可选信息头的偏移,最后用 OBJECT_HEADER 起始地址减去偏移即可得到可选信息头的起始地址。

该公式参考 CodeMachine - Article - Windows Object Headers。该文章指出,这个数组应该足够大,才能容纳 InfoMask(UCHAR) 8 Bits 共 $2^8$ 种可能性偏移。

6.png

可选信息头是否存在,是在系统初始化或者对象创建时决定的,存在的条件如表 8-18。

7.png

简单总结可选信息头存在的条件:

  1. 名称信息头 _OBJECT_HEADER_NAME_INFO:当一个对象是有名称对象时,该信息头存在。
  2. 配额信息头 _OBJECT_HEADER_QUOTA_INFO:如果对象是被系统初始化进程或空闲系统进程创建的,该信息头存在。
  3. 进程信息头 _OBJECT_HEADER_PROCESS_INFO:如果 _OBJECT_HEADER.ExclusiveObject == 1,则信息头存在。
  4. 句柄信息头 _OBJECT_HEADER_HANDLE_INFO:如果句柄类型的 _OBJECT_TYPE_INITIALIZER.MaintainHandleCount == 1,则信息头存在。
  5. 创建信息头 _OBJECT_HEADER_CREATOR_INFO:如果句柄类型的 _OBJECT_TYPE_INITIALIZER.MaintainTypeList == 1,则信息头存在。
  6. Audit 信息头 _OBJECT_HEADER_AUDIT_INFO:只有文件对象才具有该信息头。
  7. 需要信息脚 OBJECT_FOOTER 的对象,才具有 _OBJECT_HEADER_EXTENDED_INFO 扩展信息头。
  8. 填充信息头 _OBJECT_HEADER_PADDING_INFO:如果句柄类型的 _OBJECT_TYPE_INITIALIZER.CacheAligned == 1,则信息头存在。

参考:

计算可选信息头偏移示例:

查看根目录下的所有对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
kd> !object \
Object: ffff9e0212423ce0 Type: (ffffaf8f846b14e0) Directory
ObjectHeader: ffff9e0212423cb0 (new version)
HandleCount: 0 PointerCount: 57
Directory Object: 00000000 Name: \

Hash Address Type Name
---- ------- ---- ----
01 ffffaf8f87c66e50 Mutant PendingRenameMutex
ffff9e0212426230 Directory ObjectTypes
02 ffffaf8f88f03fa0 FilterConnectionPort storqosfltport
03 ffffaf8f84ea0ad0 FilterConnectionPort MicrosoftMalwareProtectionRemoteIoPortWD
05 ffff9e02124257c0 SymbolicLink SystemRoot
06 ffffaf8f87bfa300 Event DSYSDBG.Debug.Trace.Memory.2a0
ffff9e0212b72b00 Directory Sessions
...

下面以对象名 PendingRenameMutex、ObjectTypes 进行举例。

  1. PendingRenameMutex。

    • 查看对象头

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      kd> dt _OBJECT_HEADER 0xffffaf8f87c66e50-0x30
      nt!_OBJECT_HEADER
      +0x000 PointerCount : 0n1
      +0x008 HandleCount : 0n0
      +0x008 NextToFree : (null)
      +0x010 Lock : _EX_PUSH_LOCK
      +0x018 TypeIndex : 0xab ''
      +0x019 TraceFlags : 0 ''
      +0x019 DbgRefTrace : 0y0
      +0x019 DbgTracePermanent : 0y0
      +0x01a InfoMask : 0xa ''
      +0x01b Flags : 0x10 ''
      +0x01b NewObject : 0y0
      +0x01b KernelObject : 0y0
      +0x01b KernelOnlyAccess : 0y0
      +0x01b ExclusiveObject : 0y0
      +0x01b PermanentObject : 0y1
      +0x01b DefaultSecurityQuota : 0y0
      +0x01b SingleHandleEntry : 0y0
      +0x01b DeletedInline : 0y0
      +0x01c Reserved : 0x20
      +0x020 ObjectCreateInfo : 0xfffff800`58872840 _OBJECT_CREATE_INFORMATION
      +0x020 QuotaBlockCharged : 0xfffff800`58872840 Void
      +0x028 SecurityDescriptor : 0xffff9e02`12b5fbae Void
      +0x030 Body : _QUAD

      可以看到 InfoMask = 0xa,说明该对象有 _OBJECT_HEADER_NAME_INFO 和 _OBJECT_HEADER_QUOTA_INFO 可选信息头存在。

    • 计算 _OBJECT_HEADER_NAME_INFO 头的偏移。

      这里期望的可选头 DesiredHeaderBit == 0x2,根据公示 ObpInfoMaskToOffset[0xa & (0x2 | 0x1)] == ObpInfoMaskToOffset[2]。

      1
      2
      3
      4
      5
      6
      kd> ? 0xa & (0x2 | (0x2 - 1))
      Evaluate expression: 2 = 00000000`00000002

      kd> db ObpInfoMaskToOffset+0x2
      fffff800`58844dc2 20 40 10 30 30 50 20 40-40 60 30 50 50 70 10 30 @.00P @@`0PPp.0
      fffff800`58844dd2 30 50 20 40 40 60 30 50-50 70 40 60 60 80 10 30 0P @@`0PPp@``..0

      可以看到偏移为 0x20。

      1
      2
      3
      4
      5
      6
      kd> dt _OBJECT_HEADER_NAME_INFO 0xffffaf8f87c66e50-0x30-0x20
      nt!_OBJECT_HEADER_NAME_INFO
      +0x000 Directory : 0xffff9e02`12423ce0 _OBJECT_DIRECTORY // 根对象
      +0x008 Name : _UNICODE_STRING "PendingRenameMutex"
      +0x018 ReferenceCount : 0n0
      +0x01c Reserved : 0x1f01ff
    • 计算 _OBJECT_HEADER_QUOTA_INFO 头的偏移。

      期望的可选头 DesiredHeaderBit == 0x8,计算如下:

      1
      2
      3
      4
      5
      6
      kd> ? 0xa & (0x8 | (0x8 - 1))
      Evaluate expression: 10 = 00000000`0000000a

      kd> db ObpInfoMaskToOffset+0xa
      fffff800`58844dca 40 60 30 50 50 70 10 30-30 50 20 40 40 60 30 50 @`0PPp.00P @@`0P
      fffff800`58844dda 50 70 40 60 60 80 10 30-30 50 20 40 40 60 30 50 Pp@``..00P @@`0P

      可以看到偏移为 0x40。

      1
      2
      3
      4
      5
      6
      7
      8
      kd> dt _OBJECT_HEADER_QUOTA_INFO 0xffffaf8f87c66e50-0x30-0x40
      nt!_OBJECT_HEADER_QUOTA_INFO
      +0x000 PagedPoolCharge : 0 // 分页内存开支为 0,说明该对象是在非分页内存中申请的
      +0x004 NonPagedPoolCharge : 0x90 // 非分页内存开支为 0x90
      +0x008 SecurityDescriptorCharge : 0x3c
      +0x00c Reserved1 : 0x14
      +0x010 SecurityDescriptorQuotaBlock : 0xfffff800`58872840 Void
      +0x018 Reserved2 : 0x00140011`00000001
    • 查看该对象所在内存页分配情况。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      kd> !pool 0xffffaf8f87c66e50
      Pool page ffffaf8f87c66e50 region is Nonpaged pool
      ffffaf8f87c66000 size: 50 previous size: 0 (Free) ....
      ffffaf8f87c66050 size: c0 previous size: 0 (Allocated) FMsl
      ffffaf8f87c66110 size: c0 previous size: 0 (Allocated) FMsl
      ffffaf8f87c661d0 size: c0 previous size: 0 (Allocated) FMsl
      ffffaf8f87c66290 size: c0 previous size: 0 (Allocated) FMsl
      ffffaf8f87c66350 size: c0 previous size: 0 (Allocated) FMsl
      ffffaf8f87c66410 size: c0 previous size: 0 (Allocated) FMsl
      ffffaf8f87c664d0 size: c0 previous size: 0 (Allocated) FMsl
      ffffaf8f87c66590 size: c0 previous size: 0 (Allocated) NDvb
      ffffaf8f87c66650 size: c0 previous size: 0 (Allocated) FMsl
      ffffaf8f87c66710 size: c0 previous size: 0 (Allocated) FMsl
      ffffaf8f87c667d0 size: c0 previous size: 0 (Allocated) FMsl
      ffffaf8f87c66890 size: c0 previous size: 0 (Allocated) HDAu
      ffffaf8f87c66950 size: c0 previous size: 0 (Allocated) FMsl
      ffffaf8f87c66a10 size: c0 previous size: 0 (Allocated) XHCW
      ffffaf8f87c66ad0 size: c0 previous size: 0 (Allocated) XHCW
      ffffaf8f87c66b90 size: c0 previous size: 0 (Allocated) cdro
      ffffaf8f87c66c50 size: c0 previous size: 0 (Allocated) XHCW
      ffffaf8f87c66d10 size: c0 previous size: 0 (Allocated) HDAu
      *ffffaf8f87c66dd0 size: c0 previous size: 0 (Allocated) *Muta
      Pooltag Muta : Mutant objects
      ffffaf8f87c66e90 size: c0 previous size: 0 (Allocated) NDvb
      *******************************************************************
      *ffffaf8f87c66dd0 size: c0 previous size: 0 (Allocated) *Muta <=== POOL_HEADER
      Pooltag Muta : Mutant objects
      ffffaf8f87c66de0 00000000 00000090 0000003c 00000014 <=== OBJECT_HEADER_QUOTA_INFO
      ffffaf8f87c66df0 58872840 fffff800 00000001 00140011
      ffffaf8f87c66e00 12423ce0 ffff9e02 00240024 00000000 <=== OBJECT_HEADER_NAME_INFO
      ffffaf8f87c66e10 12bda250 ffff9e02 00000000 001f01ff
      ffffaf8f87c66e20 00000001 00000000 00000000 00000000 <=== OBJECT_HEADER
      ffffaf8f87c66e30 00000000 00000000 100a00ab 00000020
      ffffaf8f87c66e40 58872840 fffff800 12b5fbae ffff9e02
      ffffaf8f87c66e50 00000002 00000001 87c66e58 ffffaf8f <=== PendingRenameMutex OBJECT_BODY
      ffffaf8f87c66e60 87c66e58 ffffaf8f 00000000 00000000
      ffffaf8f87c66e70 00000000 00000000 00000000 00000000
      ffffaf8f87c66e80 00000000 00000000 880fc085 00000097

      可以看到该内核对象 POOL_HEADER == 0xffffaf8f87c66dd0,且 Pooltag == Muta:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      kd> dt _POOL_HEADER ffffaf8f87c66dd0
      nt!_POOL_HEADER
      +0x000 PreviousSize : 0y00000000 (0)
      +0x000 PoolIndex : 0y10000011 (0x83)
      +0x002 BlockSize : 0y00001100 (0xc)
      +0x002 PoolType : 0y00000010 (0x2)
      +0x000 Ulong1 : 0x20c8300
      +0x004 PoolTag : 0x6174754d
      +0x008 ProcessBilled : 0x0000010a`870f38f8 _EPROCESS
      +0x008 AllocatorBackTraceIndex : 0x38f8
      +0x00a PoolTagHash : 0x870f
      kd> db ffffaf8f87c66dd0+0x4
      ffffaf8f`87c66dd4 4d 75 74 61 f8 38 0f 87-0a 01 00 00 00 00 00 00 Muta.8..........
      ffffaf8f`87c66de4 90 00 00 00 3c 00 00 00-14 00 00 00 40 28 87 58 ....<.......@(.X
  2. ObjectTypes。

    • 查看对象头。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      kd> dt nt!_OBJECT_HEADER 0xffff9e0212426230-0x30
      nt!_OBJECT_HEADER
      +0x000 PointerCount : 0n69
      +0x008 HandleCount : 0n0
      +0x008 NextToFree : (null)
      +0x010 Lock : _EX_PUSH_LOCK
      +0x018 TypeIndex : 0xb5 ''
      +0x019 TraceFlags : 0 ''
      +0x019 DbgRefTrace : 0y0
      +0x019 DbgTracePermanent : 0y0
      +0x01a InfoMask : 0x2 ''
      +0x01b Flags : 0x12 ''
      +0x01b NewObject : 0y0
      +0x01b KernelObject : 0y1
      +0x01b KernelOnlyAccess : 0y0
      +0x01b ExclusiveObject : 0y0
      +0x01b PermanentObject : 0y1
      +0x01b DefaultSecurityQuota : 0y0
      +0x01b SingleHandleEntry : 0y0
      +0x01b DeletedInline : 0y0
      +0x01c Reserved : 0x1f62f
      +0x020 ObjectCreateInfo : 0x00000000`00000001 _OBJECT_CREATE_INFORMATION
      +0x020 QuotaBlockCharged : 0x00000000`00000001 Void
      +0x028 SecurityDescriptor : 0xffff9e02`1242662e Void
      +0x030 Body : _QUAD

      可以看到 InfoMask == 0x2,说明该对象有 _OBJECT_HEADER_NAME_INFO 可选信息头存在。

    • 计算 _OBJECT_HEADER_NAME_INFO 头的偏移。

      期望的可选头 DesiredHeaderBit == 0x2,计算如下:

      1
      2
      3
      4
      5
      6
      kd> ? 0x2 & (0x2 | (0x2 - 1))
      Evaluate expression: 2 = 00000000`00000002

      kd> db ObpInfoMaskToOffset+0x2
      fffff800`58844dc2 20 40 10 30 30 50 20 40-40 60 30 50 50 70 10 30 @.00P @@`0PPp.0
      fffff800`58844dd2 30 50 20 40 40 60 30 50-50 70 40 60 60 80 10 30 0P @@`0PPp@``..0

      可以看到偏移为 0x20。

      1
      2
      3
      4
      5
      6
      kd> dt nt!_OBJECT_HEADER_NAME_INFO 0xffff9e0212426230-0x30-0x20
      nt!_OBJECT_HEADER_NAME_INFO
      +0x000 Directory : 0xffff9e02`12423ce0 _OBJECT_DIRECTORY
      +0x008 Name : _UNICODE_STRING "ObjectTypes"
      +0x018 ReferenceCount : 0n0
      +0x01c Reserved : 0x9f1fb

1.3 OBJECT_HEADER

对象头结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
kd> dt nt!_OBJECT_HEADER
nt!_OBJECT_HEADER
+0x000 PointerCount : Int8B
+0x008 HandleCount : Int8B
+0x008 NextToFree : Ptr64 Void
+0x010 Lock : _EX_PUSH_LOCK
+0x018 TypeIndex : UChar
+0x019 TraceFlags : UChar
+0x019 DbgRefTrace : Pos 0, 1 Bit
+0x019 DbgTracePermanent : Pos 1, 1 Bit
+0x01a InfoMask : UChar
+0x01b Flags : UChar
+0x01b NewObject : Pos 0, 1 Bit
+0x01b KernelObject : Pos 1, 1 Bit
+0x01b KernelOnlyAccess : Pos 2, 1 Bit
+0x01b ExclusiveObject : Pos 3, 1 Bit
+0x01b PermanentObject : Pos 4, 1 Bit
+0x01b DefaultSecurityQuota : Pos 5, 1 Bit
+0x01b SingleHandleEntry : Pos 6, 1 Bit
+0x01b DeletedInline : Pos 7, 1 Bit
+0x01c Reserved : Uint4B
+0x020 ObjectCreateInfo : Ptr64 _OBJECT_CREATE_INFORMATION
+0x020 QuotaBlockCharged : Ptr64 Void
+0x028 SecurityDescriptor : Ptr64 Void
+0x030 Body : _QUAD

8.png

成员 偏移 解释
PointerCount +0x00 通过对象指针访问对象,表示对对象的引用总数,并且每次引用对象时都会增加,例如在查询有关对象的更多信息时。PreviousMode == 0 访问对象时使用。
HandleCount
NextToFree
+0x08 以对象句柄访问对象,对象句柄的引用计数。PreviousMode == 1 访问对象时使用。
Lock +0x10 对 OBJECT_HEADER 或可选信息头进行更改前,需要上锁。Windows 7 以前只能对 OBJECT_TYPE.TypeLock 进行上锁,但是这样的弊端就是在整个系统中,在对象类型上锁期间,无法操作该类型的其他对象。为了更细粒度上锁,就有了 OBJECT_HEADER.Lock。
TypeIndex +0x18 指定 OBJECT_BODY 所属的类型。
TraceFlags +0x19 与跟踪和调试工具特别相关的附加标志。
InfoMask +0x1a 用来指定哪些可选信息头存在。
Flags
PermanentObject
+0x1b 标志位,描述当前对象的安全属性、访问权限等。创建对象时,Flags 的信息来自于 _OBJECT_ATTRIBUTES。
PermanentObject:如果 PermanentObject == 1,当该对象的引用计数减为 0 时不应该删除该对象。如果 PermanentObject == 0,在非分页内存申请的内核对象,如果 IRQL >= DISPATCH_LEVEL 且引用计数为 0 时不会将内核对象删除,降低 IRQL 后才可以删除该对象。
ObjectCreateInfo +0x20 在对象完全插入命名空间之前,存储有关对象创建的临时信息。 该字段在创建后转换为指向配额块的指针。
SecurityDescriptor +0x28 安全描述符,确定谁可以使用该对象以及他们可以用它做什么操作。 请注意,根据定义,未命名对象不能具有安全性。该字段是指向 _SECURITY_DESCRIPTOR 结构的指针,该结构可能包含安全标识符(SID)、DACL 和系统访问控制列表(SACL)。对象创建时,安全描述符来自于 OBJECT_ATTRIBUTES.SecurityDescriptor。

参考内容:

1.4 OBJECT_HEADER.Flags

OBJECT_HEADER.Flags 是一些标志位(UCHAR 类型仅 8 位),描述当前对象的属性、访问权限等。创建对象时,Flags 的信息来自于 _OBJECT_ATTRIBUTES。

当通过 Windows 子系统中的 APl(例如 CreateEvent 或 CreateFile)创建对象时,调用者不指定任何对象属性——子系统 DLL 在幕后执行工作。 因此,通过 Win32 创建的所有命名对象都位于 BaseNamedObjects 目录中,无论是全局对象还是每个会话中的对象,因为这个目录是 Kernelbase.dll 指定为对象指定的。 《Windows Internals 7 Part2 Chapter 8》

在创建对象时,先使用 InitializeObjectAttributes 将对象属性进行初始化赋值。其中指定的以 OBJ_ 开头的属性就是传递给 OBJECT_HEADER.Flags 的属性。

1
2
3
4
5
6
7
VOID InitializeObjectAttributes(
[out] POBJECT_ATTRIBUTES p,
[in] PUNICODE_STRING n,
[in] ULONG a, // 指定以 OBJ_ 开头的属性
[in] HANDLE r,
[in, optional] PSECURITY_DESCRIPTOR s
);

OBJ_ 开头属性和 OBJECT_HEADER.Flags 对照如下:

Bitmask Flag Attributes Flags Header Flags Bit Description
NewObject N/A OB_FLAG_NEW_OBJECT (0x01) Define that the object was created but not stored into the namespace of the object.指定对象已创建但尚未插入到对象名称空间中。
KernelObject OBJ_KERNEL_HANDLE OB_FLAG_KERNEL_OBJECT (0x02) 指定此对象是内核对象,只能在 PreviousMode == 0 时才能访问。
KernelOnlyAccess OBJ_KERNEL_EXCLUSIVE OB_FLAG_KERNEL_ONLY_ACCESS (0x04) Define that only the kernel can open an handle to the object.只能在 PreviousMode == 0 时才能访问。
ExclusiveObject OBJ_EXCLUSIVE OB_FLAG_EXCLUSIVE_OBJECT (0x08) Define that only the process that created the object can access and use the object.定义只有创建该对象的进程才能访问和使用该对象。
PermanentObject OBJ_PERMANENT OB_FLAG_PERMANENT_OBJECT(0x10) Define that the object will not be disposed when the handle count drop to 0, if named object.如果是命名对象,当句柄计数下降到 0 时不会释放该对象。如果 PermanentObject == 1,当该对象的引用计数减为 0 时不应该删除该对象。如果 PermanentObject == 0,在非分页内存申请的内核对象,如果 IRQL >= DISPATCH_LEVEL 且引用计数为 0 时不会将内核对象删除,降低 IRQL 后才可以删除该对象。
DefaultSecurityQuota N/A OB_FLAG_DEFAULT_SECURITY_QUOTA (0x20) Define that the security descriptor use the default 2-KB quota.定义安全描述符使用默认的 2 KB 配额。
SingleHandleEntry N/A OB_FLAG_SINGLE_HANDLE_ENTRY(0x40) Define that the optional header OBJECT_HEADER_HANDLE_INFO structure only contains one entry and not a list of handle.定义可选头 OBJECT_HEADER_HANDLE_INFO 结构仅包含一个条目而不是句柄列表。
DeletedInline N/A OB_FLAG_DELETED_INLINE (0x80) Specifies that the object is not being de leted through the deferred deletion worker thread but rather inline through a call to ObDereferenceObject(Ex).指定不通过延迟删除工作线程删除对象,而是通过调用 ObDereferenceObject(Ex) 内联删除对象。

参考内容:

3 对象类型

对象是进行分类的,每个对象都有所属的类型。为了做到这一点,每个对象都需要对应到一个类型对象(OBJECT_TYPE)上去,类型对象用来描述该对象所属的类型及其一些属性。对象类型中的描述的属性是该类型下所有对象共有的属性。

系统为每一种对象类型创建了一个内核对象,用该对象来对某一类的对象进行描述,这个对象结构为 OBJECT_TYPE。系统中约有 64 种类型的对象,那就会有 64 个 OBJECT_TYPE 结构存在,每一个 OBJECT_TYPE 结构的 Name 成员指出当前类型对象所描述的对象类型。需要理清楚几个概念:

  1. 系统使用类型对象 OBJECT_TYPE 来将各种对象类型进行分类,对相同类型的对象属性进行定义。
  2. 同一类内核对象对应的 OBJECT_TYPE 都是同一个,如 EPROCESS 1、EPROCESS 2 对应的类型对象 OBJECT_TYPE 是同一个,且 OBJECT_TYPE.Name == Process。
  3. 并不是每个内核对象创立时都会新建一个类型对象。类型对象是在系统初始化时创建好的,后面每当新建内核对象时,都要指定对象对所属的全局类型对象指针(如 PsProcessType)。
  4. 同一类型的内核对象和类型对象 OBJECT_TYPE 的关系是:多对一,如下图所有进程对象对应的类型对象都是同一个。

11.png

系统预定义的对象类型约有 64 种,但是系统可以开放出来让开发者自己定义想要的对象类型。

系统中有全局指针指向这个 64 个类型对象(指针类型为 POBJECT_TYPE),在这个类型对象下面的双链表指针串着相同类型的所有对象。如下是 WRK 中列出的 31 个指向类型对象的指针(每个指针类型为 POBJECT_TYPE)。

10.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
kd> dq PsProcessType
fffff800`5891b410 ffffaf8f`8468f140 00002330`0002625a
fffff800`5891b420 ffffaf8f`846a1040 00000000`00000000

kd> dt nt!_OBJECT_TYPE ffffaf8f`8468f140
+0x000 TypeList : _LIST_ENTRY [ 0xffffaf8f`8468f140 - 0xffffaf8f`8468f140 ]
+0x010 Name : _UNICODE_STRING "Process"
+0x020 DefaultObject : (null)
+0x028 Index : 0x7 ''
+0x02c TotalNumberOfObjects : 0x4c
+0x030 TotalNumberOfHandles : 0x384
+0x034 HighWaterNumberOfObjects : 0x61
+0x038 HighWaterNumberOfHandles : 0x416
+0x040 TypeInfo : _OBJECT_TYPE_INITIALIZER
+0x0b8 TypeLock : _EX_PUSH_LOCK
+0x0c0 Key : 0x636f7250
+0x0c8 CallbackList : _LIST_ENTRY [ 0xffff9e02`1288b400 - 0xffff9e02`1288b400 ]

全局变量 ObpObjectTypes 数组记录了所有已创建的类型,但是 OBJECT_TYPE.TypeIndex 经过运算之后为 ObTypeIndexTable 数组的索引。两个数组的区别如下(Windows 7及之后就没有使用 ObpObjectTypes):

1
2
3
4
5
6
7
8
9
10
kd> dq ObTypeIndexTable l50
fffff800`5891be80 00000000`00000000 ffffd681`72d08000
fffff800`5891be90 ffffaf8f`846b1380 ffffaf8f`846b14e0
fffff800`5891bea0 ffffaf8f`846b1e80 ffffaf8f`846b1220
fffff800`5891beb0 ffffaf8f`846b1a60 ffffaf8f`8468f140
...
kd> dq ObpObjectTypes l50
fffff800`588440c0 ffffaf8f`846b1380 ffffaf8f`846b14e0
fffff800`588440d0 ffffaf8f`846b1e80 ffffaf8f`846b1220
fffff800`588440e0 ffffaf8f`846b1a60 ffffaf8f`8468f140

就是数组前几个成员不相同,索引位置也不同。Windows 7 开始使用的是 ObTypeIndexTable 数组。

3.1 OBJECT_TYPE 类型对象

注意,通过 OBJECT_TYPE.TypeList 并不能遍历当前类型下的所有对象。Type 对象一般都具有 _OBJECT_HEADER_CREATOR_INFO 可选信息头,OBJECT_HEADER_CREATOR_INFO.TypeList 一般都串着同一类型下的所有对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
kd> dt nt!_OBJECT_TYPE
+0x000 TypeList : _LIST_ENTRY // 串着同一类型的所有对象,但是意义不大,类型对象需参考创建可选头的TypeList
+0x010 Name : _UNICODE_STRING // 类型名称,如 Process,Thread,Type 等
+0x020 DefaultObject : Ptr64 Void // 默认等待对象
+0x028 Index : UChar // 当前类型对应的索引
+0x02c TotalNumberOfObjects : Uint4B // 该类型下所有活动对象数量
+0x030 TotalNumberOfHandles : Uint4B // 该类型下所有活动对象数量被打开的句柄个数
+0x034 HighWaterNumberOfObjects : Uint4B // 用于记录对象的引用计数,只增加不减少
+0x038 HighWaterNumberOfHandles : Uint4B // 用于记录句柄的引用计数,只增加不减少
+0x040 TypeInfo : _OBJECT_TYPE_INITIALIZER // 对象类型初始化信息
+0x0b8 TypeLock : _EX_PUSH_LOCK // 对象类型锁,对同类型的所有对象上锁
+0x0c0 Key : Uint4B // 标签,如:'Tjbo'
+0x0c8 CallbackList : _LIST_ENTRY // 重要,该类型下面所有对象共享的回调函数

系统预定义的对象类型约有 64 种,但是系统可以开放出来让开发者自己定义想要的对象类型。通过 ObCreateObjectType 函数创建新的对象类型。以下通过 Windbg 枚举对象类型实际上不一定全,查看对应系统版本有多少种对象类型的方法(全部对象)可参考Reversing Windows Internals (Part 1) - Digging Into Handles, Callbacks & ObjectTypes。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
kd> dx Debugger.Utility.Collections.FromListEntry( (*(nt!_OBJECT_TYPE **)&nt!ObpTypeObjectType)->TypeList, "nt!_OBJECT_TYPE", "TypeList").Select(o => (nt!_OBJECT_HEADER*)((unsigned char *)&o + 0x20)).Select( o => o->ObjectName)
Debugger.Utility.Collections.FromListEntry( (*(nt!_OBJECT_TYPE **)&nt!ObpTypeObjectType)->TypeList, "nt!_OBJECT_TYPE", "TypeList").Select(o => (nt!_OBJECT_HEADER*)((unsigned char *)&o + 0x20)).Select( o => o->ObjectName)
[0x0] : "Type"
[0x1] : "Directory"
[0x2] : "SymbolicLink"
[0x3] : "Token"
[0x4] : "Job"
[0x5] : "Process"
[0x6] : "Thread"
[0x7] : "Partition"
[0x8] : "UserApcReserve"
[0x9] : "IoCompletionReserve"
[0xa] : "ActivityReference"
[0xb] : "PsSiloContextPaged"
[0xc] : "PsSiloContextNonPaged"
[0xd] : "DebugObject"
[0xe] : "Event"
[0xf] : "Mutant"
[0x10] : "Callback"
[0x11] : "Semaphore"
[0x12] : "Timer"
[0x13] : "IRTimer"
[0x14] : "Profile"
[0x15] : "KeyedEvent"
[0x16] : "WindowStation"
[0x17] : "Desktop"
[0x18] : "Composition"
[0x19] : "RawInputManager"
[0x1a] : "CoreMessaging"
[0x1b] : "ActivationObject"
[0x1c] : "TpWorkerFactory"
[0x1d] : "Adapter"
[0x1e] : "Controller"
[0x1f] : "Device"
[0x20] : "Driver"
[0x21] : "IoCompletion"
[0x22] : "WaitCompletionPacket"
[0x23] : "File"
[0x24] : "TmTm"
[0x25] : "TmTx"
[0x26] : "TmRm"
[0x27] : "TmEn"
[0x28] : "Section"
[0x29] : "Session"
[0x2a] : "Key"
[0x2b] : "RegistryTransaction"
[0x2c] : "ALPC Port"
[0x2d] : "EnergyTracker"
[0x2e] : "PowerRequest"
[0x2f] : "WmiGuid"
[0x30] : "EtwRegistration"
[0x31] : "EtwSessionDemuxEntry"
[0x32] : "EtwConsumer"
[0x33] : "CoverageSampler"
[0x34] : "DmaAdapter"
[0x35] : "PcwObject"
[0x36] : "FilterConnectionPort"
[0x37] : "FilterCommunicationPort"
[0x38] : "NdisCmState"
[0x39] : "DxgkSharedResource"
[0x3a] : "DxgkSharedKeyedMutexObject"
[0x3b] : "DxgkSharedSyncObject"
[0x3c] : "DxgkSharedSwapChainObject"
[0x3d] : "DxgkDisplayManagerObject"
[0x3e] : "DxgkCurrentDxgThreadObject"
[0x3f] : "DxgkSharedProtectedSessionObject"
[0x40] : "DxgkSharedBundleObject"
[0x41] : "DxgkCompositionObject"
[0x42] : "VRegConfigurationContext"

创建新类型使用 ObCreateObjectType 函数:

1
2
3
4
5
6
NTSTATUS ObCreateObjectType(
__in PUNICODE_STRING TypeName, // 新的类型名称
__in POBJECT_TYPE_INITIALIZER ObjectTypeInitializer, // 新类型相关的属性
__in_opt PSECURITY_DESCRIPTOR SecurityDescriptor, // 新类型下面对象的安全属性
__out POBJECT_TYPE *ObjectType // 新类型对应的类型对象
);

其中 ObjectTypeInitializer 指定新类型相关属性,以及该类型下相关的操作函数,这些属性随后会复制到 OBJECT_TYPE.TypeInfo 中。

Typelnfo 字段指向 _OBJECT_TYPE_INITIALIZER 数据结构,该数据结构保存对象类型的所有对象共有的属性、标志和设置,以及指向对象类型的自定义方法的指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
kd> dt nt!_OBJECT_TYPE_INITIALIZER
+0x000 Length : Uint2B // sizeof(OBJECT_TYPE_INITIALIZER)
+0x002 ObjectTypeFlags : Uint2B // 该类型下面对象相关的属性
+0x002 CaseInsensitive : Pos 0, 1 Bit // 对象名称是否大小写敏感
+0x002 UnnamedObjectsOnly : Pos 1, 1 Bit // 是否只能是未命名对象
+0x002 UseDefaultObject : Pos 2, 1 Bit // 是否使用 ObpDefaultObject 作为默认等待对象
+0x002 SecurityRequired : Pos 3, 1 Bit // 是否需要安全描述符
+0x002 MaintainHandleCount : Pos 4, 1 Bit // 是否需要OBJECT_HEADER_HANDLE_INFO可选信息头
+0x002 MaintainTypeList : Pos 5, 1 Bit // 是否需要类型列表
+0x002 SupportsObjectCallbacks : Pos 6, 1 Bit// 是否支持对象回调,目前仅PsProcessType、PsThreadType、ExDesktopobjectType(Windows 10)支持
+0x002 CacheAligned : Pos 7, 1 Bit
+0x003 UseExtendedParameters : Pos 0, 1 Bit // 是否使用下面 Ex 扩展的解析方法
+0x003 Reserved : Pos 1, 7 Bits
+0x004 ObjectTypeCode : Uint4B // 用来描述对象类型对应的数字,如文件对象为1,同步对象为2,线程为4
+0x008 InvalidAttributes : Uint4B // 指定哪些标识对该类型下面对象无效
+0x00c GenericMapping : _GENERIC_MAPPING // 指出四种通用访问权限(读/写/执行/所有)对应的掩码
+0x01c ValidAccessMask : Uint4B // 打开此类对象应该具有的权限(你以什么权限访问我)
+0x020 RetainAccess : Uint4B // 永远不能被任何第三方对象管理器回调(回调列表的一部分)删除的访问权限
+0x024 PoolType : _POOL_TYPE // 该类型的对象应该从哪种类型内存池分配创建
+0x028 DefaultPagedPoolCharge : Uint4B // 进程默认可以使用的分页池配额
+0x02c DefaultNonPagedPoolCharge : Uint4B // 进程默认可以使用的非分页池配额
+0x030 DumpProcedure : Ptr64 void // 下面是一些同类型对象使用到的相关函数,下面分别说明
+0x038 OpenProcedure : Ptr64 long
+0x040 CloseProcedure : Ptr64 void
+0x048 DeleteProcedure : Ptr64 void
+0x050 ParseProcedure : Ptr64 long
+0x050 ParseProcedureEx : Ptr64 long
+0x058 SecurityProcedure : Ptr64 long
+0x060 QueryNameProcedure : Ptr64 long
+0x068 OkayToCloseProcedure : Ptr64 unsigned char
+0x070 WaitObjectFlagMask : Uint4B
+0x074 WaitObjectFlagOffset : Uint2B
+0x076 WaitObjectPointerOffset : Uint2B

下面介绍同类型下面内核对象的操作函数,如在创建、删除或修改对象时,应该调用什么函数等。

例程方法 什么时候被调用 举例(进程为例)
DumpProcedure 转储例程,没有使用。 /
OpenProcedure 当一个对象句柄被创建、打开、复制或继承时。 nt!PspProcessOpen
CloseProcedure 当一个对象句柄被关闭时。 nt!PspProcessClose
DeleteProcedure 在对象管理器删除一个对象前进行调用。 nt!PspProcessDelete
ParseProcedure 对象管理器搜索对象名时调用,如 ObpLookupObjectName。 /
ParseProcedureEx 如果 OBJECT_TYPE_INITIALIZER.Flags 中的标志位 UseExtendedParameters == 1,则调用该方法而不调用 ParseProcedure 方法。 /
SecurityProcedure 当一个进程读取或改变对象的保护属性时调用。 nt!SeDefaultObjectMethod
QueryNameProcedure 当一个线程查询对象名称时,调用该方法。主要用在 ObQueryNameString 函数。 /
OkayToCloseProcedure 当一个对象句柄被关闭时。该函数会优先于 CloseProcedure 之前被调用。
对象类型为 WindowStation 时对应的函数 ExpWindowStationOkToClose。
对象类型为 Desktop 时对应的函数 ExpDesktopOkToClose。
/

如下示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
kd> dt nt!_OBJECT_TYPE ffffaf8f`8468f140
+0x000 TypeList : _LIST_ENTRY [ 0xffffaf8f`8468f140 - 0xffffaf8f`8468f140 ]
+0x010 Name : _UNICODE_STRING "Process"
+0x020 DefaultObject : (null)
+0x028 Index : 0x7 ''
+0x02c TotalNumberOfObjects : 0x4c
+0x030 TotalNumberOfHandles : 0x384
+0x034 HighWaterNumberOfObjects : 0x61
+0x038 HighWaterNumberOfHandles : 0x416
+0x040 TypeInfo : _OBJECT_TYPE_INITIALIZER
+0x0b8 TypeLock : _EX_PUSH_LOCK
+0x0c0 Key : 0x636f7250
+0x0c8 CallbackList : _LIST_ENTRY [ 0xffff9e02`1288b400 - 0xffff9e02`1288b400 ]


kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffaf8f8468f180
+0x000 Length : 0x78
+0x002 ObjectTypeFlags : 0xca
+0x002 CaseInsensitive : 0y0
+0x002 UnnamedObjectsOnly : 0y1
+0x002 UseDefaultObject : 0y0
+0x002 SecurityRequired : 0y1
+0x002 MaintainHandleCount : 0y0
+0x002 MaintainTypeList : 0y0
+0x002 SupportsObjectCallbacks : 0y1
+0x002 CacheAligned : 0y1
+0x003 UseExtendedParameters : 0y0
+0x003 Reserved : 0y0000000 (0)
+0x004 ObjectTypeCode : 0x20
+0x008 InvalidAttributes : 0xb0
+0x00c GenericMapping : _GENERIC_MAPPING
+0x01c ValidAccessMask : 0x1fffff
+0x020 RetainAccess : 0x101000
+0x024 PoolType : 200 ( NonPagedPoolNx )
+0x028 DefaultPagedPoolCharge : 0x1000
+0x02c DefaultNonPagedPoolCharge : 0xa98
+0x030 DumpProcedure : (null)
+0x038 OpenProcedure : 0xfffff800`582558d0 long nt!PspProcessOpen+0
+0x040 CloseProcedure : 0xfffff800`58310150 void nt!PspProcessClose+0
+0x048 DeleteProcedure : 0xfffff800`582c62f0 void nt!PspProcessDelete+0
+0x050 ParseProcedure : (null)
+0x050 ParseProcedureEx : (null)
+0x058 SecurityProcedure : 0xfffff800`58206670 long nt!SeDefaultObjectMethod+0
+0x060 QueryNameProcedure : (null)
+0x068 OkayToCloseProcedure : (null)
+0x070 WaitObjectFlagMask : 0
+0x074 WaitObjectFlagOffset : 0
+0x076 WaitObjectPointerOffset : 0

该类型下对象共有的权限说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffaf8f8468f180
+0x000 Length : 0x78
+0x002 ObjectTypeFlags : 0xca
+0x002 CaseInsensitive : 0y0
+0x002 UnnamedObjectsOnly : 0y1
+0x002 UseDefaultObject : 0y0
+0x002 SecurityRequired : 0y1
+0x002 MaintainHandleCount : 0y0
+0x002 MaintainTypeList : 0y0
+0x002 SupportsObjectCallbacks : 0y1
+0x002 CacheAligned : 0y1
+0x003 UseExtendedParameters : 0y0
+0x003 Reserved : 0y0000000 (0)
+0x004 ObjectTypeCode : 0x20
+0x008 InvalidAttributes : 0xb0
+0x00c GenericMapping : _GENERIC_MAPPING
+0x01c ValidAccessMask : 0x1fffff
+0x020 RetainAccess : 0x101000
+0x024 PoolType : 200 ( NonPagedPoolNx )
+0x028 DefaultPagedPoolCharge : 0x1000
+0x02c DefaultNonPagedPoolCharge : 0xa98
+0x030 DumpProcedure : (null)
+0x038 OpenProcedure : 0xfffff800`582558d0 long nt!PspProcessOpen+0
+0x040 CloseProcedure : 0xfffff800`58310150 void nt!PspProcessClose+0
+0x048 DeleteProcedure : 0xfffff800`582c62f0 void nt!PspProcessDelete+0
+0x050 ParseProcedure : (null)
+0x050 ParseProcedureEx : (null)
+0x058 SecurityProcedure : 0xfffff800`58206670 long nt!SeDefaultObjectMethod+0
+0x060 QueryNameProcedure : (null)
+0x068 OkayToCloseProcedure : (null)
+0x070 WaitObjectFlagMask : 0
+0x074 WaitObjectFlagOffset : 0
+0x076 WaitObjectPointerOffset : 0

该类型下多有对象通用权限枚举:

1
2
3
4
5
6
kd> dt _GENERIC_MAPPING 0xffffaf8f8468f180+0xC
ntdll!_GENERIC_MAPPING
+0x000 GenericRead : 0x20410
+0x004 GenericWrite : 0x20bea
+0x008 GenericExecute : 0x121001
+0x00c GenericAll : 0x1fffff

可对当前类型对象进行的访问权限:

1
+0x01c ValidAccessMask  : 0x1fffff	// GenericAll,全部

不允许其他进程修改的权限 Object retention:

1
+0x020 RetainAccess     : 0x101000	// 暂未查到

3.2 OBJECT_HEADER.TypeIndex

一、Windows XP

Windows XP 中使用 Type 直接指向 OBJECT_TYPE 结构。

1
2
3
4
5
kd> dt nt!_OBJECT_HEADER
+0x000 PointerCount : Int8B
...
+0x018 Type : Ptr64 OBJECT_TYPE
...

二、Windows 7/8/8.1

Windows 7/8/8.1 下,使用 TypeIndex 索引来取代 Type 成员。

此时的 TypeIndex 是全局数组 ObTypeIndexTable 的索引,该数组中的每个成员都是 POBJECT_TYPE 指针类型,指向其对应的类型对象。

三、Windows 10

Windows 10 下 TypeIndex 不再直接作为全局数组 ObTypeIndexTable 的索引,需要经过运算。运算过程通过逆向 ObGetObjectType 函数得:

9.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
POBJECT_HEADER ObGetObjectType(PVOID Object)
{
POBJECT_HEADER pObHeader = NULL;
UCHAR TypeIndex = 0;
UCHAR Cookie = 0;
USHAORT uIndex= 0;

pObHeader = (POBJECT_HEADER)(Object - 0x30);
TypeIndex = (UCHAR)(Object - 0x18);
uIndex = (USHAORT)pObHeader;
Cookie = ObHeaderCookie;

uIndex = uIndex >> 8;
uIndex &= 0xFF;

uIndex ^= TypeIndex;
uIndex ^= Cookie;

return ObTypeIndexTable[uIndex];
}

计算过程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
kd> !object ffffaf8f`87c66e50
Object: ffffaf8f87c66e50 Type: (ffffaf8f8468e4e0) Mutant
ObjectHeader: ffffaf8f87c66e20 (new version)
HandleCount: 0 PointerCount: 1
Directory Object: ffff9e0212423ce0 Name: PendingRenameMutex
*************************************************************************************
kd> ? (ffffaf8f`87c66e20>>8)&0xFF // 取第二个最小有效字节
Evaluate expression: 110 = 00000000`0000006e

kd> ? (0x6e ^ 0xab) // kd> db(ffffaf8f`87c66e50-0x18) == 0xab
Evaluate expression: 197 = 00000000`000000c5

kd> ? (0xc5 ^ 0xd4) // db ObHeaderCookie == 0xd4
Evaluate expression: 17 = 00000000`00000011

kd> dq poi(ObTypeIndexTable+0x11*8)
fffff800`5891bf08 ffffaf8f`8468e4e0 ffffaf8f`8468fc40
fffff800`5891bf18 ffffaf8f`8468e220 ffffaf8f`8468e380

kd> dt nt!_OBJECT_TYPE ffffaf8f`8468e4e0
+0x000 TypeList : _LIST_ENTRY [ 0xffffaf8f`8468e4e0 - 0xffffaf8f`8468e4e0 ]
+0x010 Name : _UNICODE_STRING "Mutant"
+0x020 DefaultObject : (null)
+0x028 Index : 0x11 ''
+0x02c TotalNumberOfObjects : 0x1d5
+0x030 TotalNumberOfHandles : 0x232
+0x034 HighWaterNumberOfObjects : 0x220
+0x038 HighWaterNumberOfHandles : 0x357
+0x040 TypeInfo : _OBJECT_TYPE_INITIALIZER
+0x0b8 TypeLock : _EX_PUSH_LOCK
+0x0c0 Key : 0x6174754d
+0x0c8 CallbackList : _LIST_ENTRY [ 0xffffaf8f`8468e5a8 - 0xffffaf8f`8468e5a8 ]

可以看到 OBJECT_TYPE.Index == 0x11。

1
2
3
4
5
6
7
8
9
10
11
12
PAGE:00000001407002E0                 public ObGetObjectType
PAGE:00000001407002E0 ObGetObjectType proc near
PAGE:00000001407002E0 lea rax, [rcx-30h]
PAGE:00000001407002E4 movzx ecx, byte ptr [rcx-18h] // TypeIndex
PAGE:00000001407002E8 shr rax, 8 // rax = &OBJECT_HEADER
PAGE:00000001407002EC movzx eax, al
PAGE:00000001407002EF xor rax, rcx
PAGE:00000001407002F2 movzx ecx, byte ptr cs:ObHeaderCookie
PAGE:00000001407002F9 xor rax, rcx
PAGE:00000001407002FC lea rcx, ObTypeIndexTable
PAGE:0000000140700303 mov rax, [rcx+rax*8]
PAGE:0000000140700307 retn

参考内容: