Windows Kernel Object的结构
从_OBJECT_HEADER看起
kd> dt nt!_OBJECT_HEADER
+0x000 PointerCount : Int8B
+0x008 HandleCount : Int8B
+0x008 NextToFree : Ptr64 Void
+0x010 Lock : _EX_PUSH_LOCK
+0x018 TypeIndex : UChar
+0x019 TraceFlags : UChar
+0x01a InfoMask : UChar
+0x01b Flags : UChar
+0x020 ObjectCreateInfo : Ptr64 _OBJECT_CREATE_INFORMATION
+0x020 QuotaBlockCharged : Ptr64 Void
+0x028 SecurityDescriptor : Ptr64 Void
+0x030 Body : _QUAD
当我们查看一个windows object时,我们查看的是object的body字段。
kd> !process 0 0 explorer.exe
PROCESS fffffa801a8e1b30
SessionId: 1 Cid: 087c Peb: 7fffffd7000 ParentCid: 0840
DirBase: 0aa8e000 ObjectTable: fffff8a001e39d30 HandleCount: 642.
Image: explorer.exe
如上,可以看到EPROCESS的位置在fffffa801a8e1b30,根据_OBJECT_HEADER的结构我们可以计算出其_OBJECT_HEADER的位置在fffffa801a8e1b30-30,即fffffa801a8e1b00上。可以使用!object来确认计算的结果是否正确
kd> dt _OBJECT_HEADER fffffa801a8e1b00
nt!_OBJECT_HEADER
+0x000 PointerCount : 0n366
+0x008 HandleCount : 0n7
+0x008 NextToFree : 0x00000000`00000007 Void
+0x010 Lock : _EX_PUSH_LOCK
+0x018 TypeIndex : 0x7 ''
+0x019 TraceFlags : 0 ''
+0x01a InfoMask : 0x8 ''
+0x01b Flags : 0 ''
+0x020 ObjectCreateInfo : 0xfffffa80`1a400100 _OBJECT_CREATE_INFORMATION
+0x020 QuotaBlockCharged : 0xfffffa80`1a400100 Void
+0x028 SecurityDescriptor : 0xfffff8a0`01dfd8db Void
+0x030 Body : _QUAD
kd> !object fffffa801a8e1b30
Object: fffffa801a8e1b30 Type: (fffffa8018d42a80) Process
ObjectHeader: fffffa801a8e1b00 (new version)
HandleCount: 7 PointerCount: 366
然而_OBJECT_HEADER与Body并不是整个object的全部,实际上在object header前面还有optional headers与pool header,一个完全的windows object应该是这样的:
- _POOL_HEADER
- _OBJECT_QUOTA_CHARGES (optional)
- _OBJECT_HANDLE_DB (optional)
- _OBJECT_NAME (optional)
- _OBJECT_CREATOR_INFO (optional)
- _OBJECT_HEADER
- body
_OBJECT_HEADER在_OBJECT_HEADER->InfoMask中使用掩码的方式来表示哪些可选头存在
Bit | Type |
---|---|
0x01 | nt!_OBJECT_HEADER_CREATOR_INFO |
0x02 | nt!_OBJECT_HEADER_NAME_INFO |
0x04 | nt!_OBJECT_HEADER_HANDLE_INFO |
0x08 | nt!_OBJECT_HEADER_QUOTA_INFO |
0x10 | nt!_OBJECT_HEADER_PROCESS_INFO |
内核中存在一个数组ObpInfoMaskToOffset,我们可以根据InfoMask我们可以计算出一个数值作为数组的索引,从而获取我们想要的optional header距离object header的偏移
Offset = ObpInfoMaskToOffset[OBJECT_HEADER->InfoMask & (DesiredHeaderBit | (DesiredHeaderBit-1))] |
在explorer.exe的例子中,其InfoMask值为8,因此他只有一个_OBJECT_HEADER_QUOTA_INFO的可选头,要计算出他的偏移则计算0x8 & (0x8 | 0x8-1) = 0x8,根据计算出的索引值找到偏移 |
kd> ?nt!ObpInfoMaskToOffset
Evaluate expression: -8796025365056 = fffff800`04085dc0
kd> db fffff800`04085dc0+0x8 L1
fffff800`04085dc8 20
得到偏移为0x20,用object header的地址减去偏移即为我们想找的可选头的地址
kd> dt nt!_OBJECT_HEADER_QUOTA_INFO fffffa8018d42a80-20
+0x000 PagedPoolCharge : 0
+0x004 NonPagedPoolCharge : 0
+0x008 SecurityDescriptorCharge : 0x13030002
+0x010 SecurityDescriptorQuotaBlock : (null)
+0x018 Reserved : 0
_OBJECT_TYPE
windows内核中有许多不同类型的对象,每个对象在object header包含了一个字段标注了其类型。在win7之前的windows版本中存在一个Type字段其包含了一个指针指向一个_OBJECT_TYPE结构体,在新版本中,这个字段变为了TypeIndex,其包含了一个全局数组nt!ObTypeIndexTable的索引,而这个数组中存着不同类型的结构体的指针。
在上述例子中,EPROCESS对象的TypeIndex为7,因此我们可以通过nt!ObTypeIndexTable[0x7]来获取指向其_OBJECT_TYPE的指针
kd> dt nt!_OBJECT_TYPE poi(nt!ObTypeIndexTable + ( 7 * @$ptrsize ))
+0x000 TypeList : _LIST_ENTRY [ 0xfffffa80`18d42a80 - 0xfffffa80`18d42a80 ]
+0x010 Name : _UNICODE_STRING "Process"
+0x020 DefaultObject : (null)
+0x028 Index : 0x7 ''
+0x02c TotalNumberOfObjects : 0x27
+0x030 TotalNumberOfHandles : 0xf0
+0x034 HighWaterNumberOfObjects : 0x27
+0x038 HighWaterNumberOfHandles : 0xf2
+0x040 TypeInfo : _OBJECT_TYPE_INITIALIZER
+0x0b0 TypeLock : _EX_PUSH_LOCK
+0x0b8 Key : 0x636f7250
+0x0c0 CallbackList : _LIST_ENTRY [ 0xfffffa80`18d42b40 - 0xfffffa80`18d42b40 ]
可以看到,该对象是一个Process类型对象。
在windbg中可以使用”!object \ObjectTypes”来获取所有对象类型。
在windows10中,处于安全考虑,TypeIndex字段被使用异或加密
http://www.powerofcommunity.net/poc2018/nikita.pdf
一切皆对象——_OBJECT_TYPE对象
如果我们使用!object命令来查看一个_OBJECT_TYPE结构体,我们会发现每一个类型竟然也是作为对象存在的
kd> !object poi(nt!ObTypeIndexTable + ( 7 * @$ptrsize ))
Object: fffffa8018d42a80 Type: (fffffa8018d41c00) Type
ObjectHeader: fffffa8018d42a50 (new version)
HandleCount: 0 PointerCount: 2
Directory Object: fffff8a0000068f0 Name: Process
可以看到,process类型对象的类型为Type。继续查看其object header
kd> dt _OBJECT_HEADER fffffa8018d42a50
nt!_OBJECT_HEADER
+0x000 PointerCount : 0n2
+0x008 HandleCount : 0n0
+0x008 NextToFree : (null)
+0x010 Lock : _EX_PUSH_LOCK
+0x018 TypeIndex : 0x2 ''
+0x019 TraceFlags : 0 ''
+0x01a InfoMask : 0x3 ''
+0x01b Flags : 0x13 ''
+0x020 ObjectCreateInfo : (null)
+0x020 QuotaBlockCharged : (null)
+0x028 SecurityDescriptor : (null)
+0x030 Body : _QUAD
可以看到其TypeIndex为2,说明Type类型同样也存在nt!ObTypeIndexTable中
kd> dt nt!_OBJECT_TYPE poi(nt!ObTypeIndexTable + ( 2 * @$ptrsize ))
+0x000 TypeList : _LIST_ENTRY [ 0xfffffa80`18d41bb0 - 0xfffffa80`1b524d60 ]
+0x010 Name : _UNICODE_STRING "Type"
+0x020 DefaultObject : 0xfffff800`040839e0 Void
+0x028 Index : 0x2 ''
+0x02c TotalNumberOfObjects : 0x2a
+0x030 TotalNumberOfHandles : 0
+0x034 HighWaterNumberOfObjects : 0x2a
+0x038 HighWaterNumberOfHandles : 0
+0x040 TypeInfo : _OBJECT_TYPE_INITIALIZER
+0x0b0 TypeLock : _EX_PUSH_LOCK
+0x0b8 Key : 0x546a624f
+0x0c0 CallbackList : _LIST_ENTRY [ 0xfffffa80`18d41cc0 - 0xfffffa80`18d41cc0 ]
回到ProcessType的object header上,其InfoMask值为3,说明它具有_OBJECT_HEADER_CREATOR_INFO与_OBJECT_HEADER_NAME_INFO两个可选头,其中_OBJECT_HEADER_CREATOR_INFO具有一个双向链表,通过这个链表我们可以遍历所有的Type
Windows Kernel Object存在哪
所有的对象都由windows对象管理器(Object Manager)统一管理并以namespace进行分类,每个named kernel object有一个类似路径一样的名字,例如表示C盘驱动器的对象名为\DosDevices\C:,其中\DosDevice就是该对象的namespace。
打印所有内核对象
使用Nt函数NtOpenDirectoryObject/NtQueryDirectoryObject来遍历所有的Directory,进而遍历所有对象
//https://github.com/adobe/chromium/blob/master/sandbox/tools/finder/finder_kernel.cc
#include <iostream>
#include <Windows.h>
#include <winternl.h>
#include <ntstatus.h>
#define DIRECTORY_QUERY 1
#define BUFFER_SIZE 0x800
typedef struct _OBJDIR_INFORMATION
{
UNICODE_STRING ObjectName;
UNICODE_STRING ObjectTypeName;
BYTE Data[1];
} OBJDIR_INFORMATION, * POBJDIR_INFORMATION;
typedef NTSTATUS(*PFN_NtOpenDirectoryObject)(
_Out_ PHANDLE DirectoryHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes
);
typedef NTSTATUS(*PFN_NtQueryDirectoryObject)(
_In_ HANDLE DirectoryHandle,
_Out_opt_ PVOID Buffer,
_In_ ULONG Length,
_In_ BOOLEAN ReturnSingleEntry,
_In_ BOOLEAN RestartScan,
_Inout_ PULONG Context,
_Out_opt_ PULONG ReturnLength
);
typedef ULONG(*PFN_RtlNtStatusToDosError)(
NTSTATUS Status
);
void PrintNtStatus(NTSTATUS code)
{
LPSTR errmsg = NULL;
if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_ALLOCATE_BUFFER,
GetModuleHandle(L"ntdll.dll"), code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&errmsg, 0, NULL))
{
printf("%s\n", errmsg);
}
}
int wmain(int argc, wchar_t** argv)
{
std::wstring path = L"\\";
if (argc == 2)
{
path = argv[1];
}
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
PFN_NtQueryDirectoryObject pfnNtQueryDirectoryObject = (PFN_NtQueryDirectoryObject)GetProcAddress(hNtdll, "NtQueryDirectoryObject");
PFN_NtOpenDirectoryObject pfnNtOpenDirectoryObject = (PFN_NtOpenDirectoryObject)GetProcAddress(hNtdll, "NtOpenDirectoryObject");
PFN_RtlNtStatusToDosError pfnRtlNtStatusToDosError = (PFN_RtlNtStatusToDosError)GetProcAddress(hNtdll, "RtlNtStatusToDosError");
UNICODE_STRING unicode_str;
unicode_str.Length = (USHORT)path.length() * 2;
unicode_str.MaximumLength = (USHORT)path.length() * 2 + 2;
unicode_str.Buffer = (PWSTR)path.c_str();
OBJECT_ATTRIBUTES path_attributes;
InitializeObjectAttributes(&path_attributes,
&unicode_str,
0, // No Attributes
NULL, // No Root Directory
NULL); // No Security Descriptor
HANDLE file_handle;
NTSTATUS ret = 0;
ret = pfnNtOpenDirectoryObject(&file_handle,
DIRECTORY_QUERY,
&path_attributes);
if (ret != STATUS_SUCCESS)
{
PrintNtStatus(ret);
return 0;
}
ULONG index = 0;
ULONG returnLength;
POBJDIR_INFORMATION buffer = (POBJDIR_INFORMATION)malloc(BUFFER_SIZE);
while (!(ret = pfnNtQueryDirectoryObject(file_handle, buffer, BUFFER_SIZE, TRUE, FALSE, &index, &returnLength)))
{
wprintf(L"%d %s %s\n", index, buffer->ObjectName.Buffer, buffer->ObjectTypeName.Buffer);
}
if (ret != STATUS_NO_MORE_ENTRIES)
{
PrintNtStatus(ret);
return 0;
}
return 0;
}
一些参考资料:
https://codemachine.com/articles/object_headers.html
https://stackoverflow.com/questions/2643084/sysinternals-winobj-device-listing-mechanism
https://github.com/adobe/chromium/blob/master/sandbox/tools/finder/finder_kernel.cc
https://rayanfam.com/topics/reversing-windows-internals-part1/
内核路漫漫。。。