遍历Windows内核ObjectType

一、背景

        Windows内核中有很多类型,例如PROCESS、THREAD、FILE、MUTANT,这些类型都由对象管理器集中管理,见下图:

 

 

  其中有些对象类型是导出的,例如有IoDriverObjectType, PsProcessType等,有些是文档化的,有些是导出的,还有的是未导出的。在使用未导出的对象指针时就得想办法获得,在此遍历对象类型及其相关信息,使用未导出的对象类型时可以直接使用,对象类型结构之类不作详细分析,直接上相关实现的分析。

 

 二、几个主要的内核变量

   ObpTypeObjectType、ObpObjectTypes、ObTypeIndexTable。

  • ObpTypeObjectType

  这个表保存的是原始对象名称为"Type"的对象类型。Windbg调试Win7格式化如下:

5: kd>  dq nt!ObpTypeObjectType
fffff800`0666e5a8  fffffa80`30e4c530 00000000`00000000
fffff800`0666e5b8  00000000`00000000 fffffa80`30e4c530
fffff800`0666e5c8  fffffa80`30e4c3e0 fffffa80`30e4c290
fffff800`0666e5d8  fffffa80`30e525a0 fffffa80`30e52380
fffff800`0666e5e8  fffffa80`30e52230 fffffa80`30ee0080
fffff800`0666e5f8  fffffa80`30ee0f30 fffffa80`30ee0de0
fffff800`0666e608  fffffa80`30ee1080 fffffa80`30f5a940
fffff800`0666e618  fffffa80`30f4ca10 fffffa80`30f4c8c0

  然后取第一个数据查看如下:

5: kd> dt _object_type fffffa80`30e4c530
ntdll!_OBJECT_TYPE
   +0x000 TypeList         : _LIST_ENTRY [ 0xfffffa80`30e4c4e0 - 0xfffffa80`3205db20 ]
   +0x010 Name             : _UNICODE_STRING "Type"
   +0x020 DefaultObject    : 0xfffff800`0666e7a0 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`30e4c5f0 - 0xfffffa80`30e4c5f0 ]

  这个就是第一个名为Type的ObjectType。再取后边第五个非fffffa80`30e4c530的看:

5: kd> dt _object_type fffffa80`30e4c3e0
ntdll!_OBJECT_TYPE
   +0x000 TypeList         : _LIST_ENTRY [ 0xfffffa80`30e4c3e0 - 0xfffffa80`30e4c3e0 ]
   +0x010 Name             : _UNICODE_STRING "Directory"
   +0x020 DefaultObject    : 0xfffff800`0666e7a0 Void
   +0x028 Index            : 0x3 ''
   +0x02c TotalNumberOfObjects : 0x25
   +0x030 TotalNumberOfHandles : 0x70
   +0x034 HighWaterNumberOfObjects : 0x29
   +0x038 HighWaterNumberOfHandles : 0x83
   +0x040 TypeInfo         : _OBJECT_TYPE_INITIALIZER
   +0x0b0 TypeLock         : _EX_PUSH_LOCK
   +0x0b8 Key              : 0x65726944
   +0x0c0 CallbackList     : _LIST_ENTRY [ 0xfffffa80`30e4c4a0 - 0xfffffa80`30e4c4a0 ]

  

  其为目录对象。

  那似乎通过ObpTypeObjectType这个就可以遍历所有的对象类型,之前查看其它的文章也有这个指出的。

  那如何获取到ObpTypeObjectType,根据Windbg调试发现ObCreateObjectTypeEx中有对其的引用 :

nt!ObCreateObjectTypeEx+0x2e3:
fffff800`068a1373 0fb74602        movzx   eax,word ptr [rsi+2]
fffff800`068a1377 488d4c2460      lea     rcx,[rsp+60h]
fffff800`068a137c 488bd6          mov     rdx,rsi
fffff800`068a137f 6689442462      mov     word ptr [rsp+62h],ax
fffff800`068a1384 e827fec0ff      call    nt!RtlCopyUnicodeString (fffff800`064b11b0)
fffff800`068a1389 4c8b2518d2dcff  mov     r12,qword ptr [nt!ObpTypeObjectType (fffff800`0666e5a8)]
fffff800`068a1390 4d3be6          cmp     r12,r14
fffff800`068a1393 7555            jne     nt!ObCreateObjectTypeEx+0x35a (fffff800`068a13ea)  Branch

  而ObCreateObjectTypeEx又被ObCreateObjectType调用:

5: kd> uf nt!ObCreateObjectType
nt!ObCreateObjectType:
fffff800`068a1a90 4883ec38        sub     rsp,38h
fffff800`068a1a94 4c894c2420      mov     qword ptr [rsp+20h],r9
fffff800`068a1a99 4533c9          xor     r9d,r9d
fffff800`068a1a9c e8eff5ffff      call    nt!ObCreateObjectTypeEx (fffff800`068a1090)
fffff800`068a1aa1 4883c438        add     rsp,38h
fffff800`068a1aa5 c3              ret

  

  而ObCreateObjectType是导出的。

  似乎得出结论可以用此来定位。

  但在Win10系统上格式化结构却得出来的数据不正确,见下:

0: kd> dq nt!ObpTypeObjectType
fffff806`11c25b30  ffffb386`f147fea0 ffffe001`81a2fce0
fffff806`11c25b40  00000000`00000000 00000000`00000000
fffff806`11c25b50  00000000`00000000 00000000`00000000
fffff806`11c25b60  00000000`00000000 00000000`00000000
fffff806`11c25b70  00000000`00000000 00000000`00000000
fffff806`11c25b80  00000000`00000000 00000000`00000000
fffff806`11c25b90  00000000`00000000 00000000`00000000
fffff806`11c25ba0  00000000`00000000 00000000`00000000

  发现除了第一个头外后边都是空的。

  其原因就是在Win7上后边连续填充的数据对应的是另一个ObpObjectTypes的数据。

  

5: kd>  dq nt!ObpTypeObjectType
fffff800`0666e5a8  fffffa80`30e4c530 00000000`00000000
fffff800`0666e5b8  00000000`00000000 fffffa80`30e4c530
fffff800`0666e5c8  fffffa80`30e4c3e0 fffffa80`30e4c290
fffff800`0666e5d8  fffffa80`30e525a0 fffffa80`30e52380
fffff800`0666e5e8  fffffa80`30e52230 fffffa80`30ee0080
fffff800`0666e5f8  fffffa80`30ee0f30 fffffa80`30ee0de0
fffff800`0666e608  fffffa80`30ee1080 fffffa80`30f5a940
fffff800`0666e618  fffffa80`30f4ca10 fffffa80`30f4c8c0
5: kd> dq nt!ObpObjectTypes
fffff800`0666e5c0  fffffa80`30e4c530 fffffa80`30e4c3e0
fffff800`0666e5d0  fffffa80`30e4c290 fffffa80`30e525a0
fffff800`0666e5e0  fffffa80`30e52380 fffffa80`30e52230
fffff800`0666e5f0  fffffa80`30ee0080 fffffa80`30ee0f30
fffff800`0666e600  fffffa80`30ee0de0 fffffa80`30ee1080
fffff800`0666e610  fffffa80`30f5a940 fffffa80`30f4ca10
fffff800`0666e620  fffffa80`30f4c8c0 fffffa80`30f30c30
fffff800`0666e630  fffffa80`30f30ae0 fffffa80`30f538c0

  可以看到 ObpTypeObjectType之后第三个数据的地址及为ObpObjectTypes的开始地址。

  而在Win10上是不连续的,没办法用ObpTypeObjectType来遍历了。

0: kd> dq nt!ObpTypeObjectType
fffff806`11c25b30  ffffb386`f147fea0 ffffe001`81a2fce0
fffff806`11c25b40  00000000`00000000 00000000`00000000
fffff806`11c25b50  00000000`00000000 00000000`00000000
fffff806`11c25b60  00000000`00000000 00000000`00000000
fffff806`11c25b70  00000000`00000000 00000000`00000000
fffff806`11c25b80  00000000`00000000 00000000`00000000
fffff806`11c25b90  00000000`00000000 00000000`00000000
fffff806`11c25ba0  00000000`00000000 00000000`00000000
0: kd> dq nt!ObpObjectTypes
fffff806`11c25280  ffffb386`f147fea0 ffffb386`f1460a10
fffff806`11c25290  ffffb386`f145fe70 ffffb386`f14c7e80
fffff806`11c252a0  ffffb386`f14c77a0 ffffb386`f14c7220
fffff806`11c252b0  ffffb386`f14c7640 ffffb386`f14c70c0
fffff806`11c252c0  ffffb386`f14c7900 ffffb386`f14c7a60
fffff806`11c252d0  ffffb386`f14c7380 ffffb386`f14c74e0
fffff806`11c252e0  ffffb386`f14c7d20 ffffb386`f14c7bc0
fffff806`11c252f0  ffffb386`f14f1f00 ffffb386`f14f1c40

 

  • ObpObjectTypes

  这个变量来说确实是保存了所有的对象类型,但有个问题是不太容易从导出的函数来定位这个数据。IDA查看其代码引用处如下:

 

   第2、3、4个函数不是直接使用的ObpObjectTypes,而其它的都不能方便的从导出函数进行定位。

  所以此方法不通。

 

  • ObTypeIndexTable

  此变量是以索顺序来存放对象类型的指针的,结构数据如下:

5: kd> dq ObTypeIndexTable
fffff800`06670100  00000000`00000000 00000000`bad0b0b0
fffff800`06670110  fffffa80`30e4c530 fffffa80`30e4c3e0
fffff800`06670120  fffffa80`30e4c290 fffffa80`30e525a0
fffff800`06670130  fffffa80`30e52380 fffffa80`30e52230
fffff800`06670140  fffffa80`30ee0080 fffffa80`30ee0f30
fffff800`06670150  fffffa80`30ee0de0 fffffa80`30ee1080
fffff800`06670160  fffffa80`30f5a940 fffffa80`30f4ca10
fffff800`06670170  fffffa80`30f4c8c0 fffffa80`30f30c30
5: kd> dt _object_type fffffa80`30e4c530
nt!_OBJECT_TYPE
   +0x000 TypeList         : _LIST_ENTRY [ 0xfffffa80`30e4c4e0 - 0xfffffa80`3205db20 ]
   +0x010 Name             : _UNICODE_STRING "Type"
   +0x020 DefaultObject    : 0xfffff800`0666e7a0 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`30e4c5f0 - 0xfffffa80`30e4c5f0 ]
5: kd> dt _object_type fffffa80`30e4c3e0
nt!_OBJECT_TYPE
   +0x000 TypeList         : _LIST_ENTRY [ 0xfffffa80`30e4c3e0 - 0xfffffa80`30e4c3e0 ]
   +0x010 Name             : _UNICODE_STRING "Directory"
   +0x020 DefaultObject    : 0xfffff800`0666e7a0 Void
   +0x028 Index            : 0x3 ''
   +0x02c TotalNumberOfObjects : 0x29
   +0x030 TotalNumberOfHandles : 0x76
   +0x034 HighWaterNumberOfObjects : 0x29
   +0x038 HighWaterNumberOfHandles : 0x83
   +0x040 TypeInfo         : _OBJECT_TYPE_INITIALIZER
   +0x0b0 TypeLock         : _EX_PUSH_LOCK
   +0x0b8 Key              : 0x65726944
   +0x0c0 CallbackList     : _LIST_ENTRY [ 0xfffffa80`30e4c4a0 - 0xfffffa80`30e4c4a0 ]

   从第2个 fffffa80`30e4c530的地址结构格式化可以看出其Index为2,对应表中的地址索引为2。其这个表就是从第2号索引开始, 第1号索引00000000`bad0b0b0    指向的实际是无效对象类型。

   IDA查看其引用如下,有很多函数中都用到:

 

 

   取其中较实现查找的ObGetObjectType,通过查找特征码就可以获得:

5: kd> u nt!ObGetObjectType
nt!ObGetObjectType:
fffff800`06796e60 0fb641e8        movzx   eax,byte ptr [rcx-18h]
fffff800`06796e64 488d0d9592edff  lea     rcx,[nt!ObTypeIndexTable (fffff800`06670100)]
fffff800`06796e6b 488b04c1        mov     rax,qword ptr [rcx+rax*8]
fffff800`06796e6f c3              ret
fffff800`06796e70 cc              int     3
fffff800`06796e71 cc              int     3
fffff800`06796e72 cc              int     3
fffff800`06796e73 cc              int     3

三、代码实现

  PrintOjbectTypeList.h

#pragma once
#include <ntddk.h>

#if DBG
#define KDPRINT(projectName, format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,\
                                                                                          projectName "::【" __FUNCTION__  "" ##format, \
                                                                                          ##__VA_ARGS__ ) 
#else
#define KDPRINT(format, ...)
#endif
typedef struct _OBJECT_TYPE_FLAGS {
    UCHAR CaseInsensitive : 1;
    UCHAR UnnamedObjectsOnly : 1;
    UCHAR UseDefaultObject : 1;
    UCHAR SecurityRequired : 1;
    UCHAR MaintainHandleCount : 1;
    UCHAR MaintainTypeList : 1;
    UCHAR SupportsObjectCallbacks : 1;
    UCHAR CacheAligned : 1;
}OBJECT_TYPE_FLAGS, * P_OBJECT_TYPE_FLAGS;


typedef struct _OBJECT_TYPE_INITIALIZER {
    USHORT                wLength;
    OBJECT_TYPE_FLAGS    ObjectTypeFlags;
    ULONG                ObjcetTypeCode;
    ULONG                InvalidAttributes;
    GENERIC_MAPPING        GenericMapping;
    ULONG                ValidAccessMask;
    ULONG                RetainAccess;
    ULONG                PoolType;
    ULONG                DefaultPagedPoolCharge;
    ULONG                DefaultNonPagedPoolCharge;
    PVOID                DumpProcedure;
    PVOID                OpenProcedure;
    PVOID                CloseProcedure;
    PVOID                DeleteProcedure;
    PVOID                ParseProcedure;
    PVOID                SecurityProcedure;
    PVOID                QueryNameProcedure;
    PVOID                OkayToCloseProcedure;
}OBJECT_TYPE_INITIALIZER, * POBJECT_TYPE_INITIALIZER;

typedef struct _OBJECT_TYPE_EX {
    LIST_ENTRY                    TypeList;
    UNICODE_STRING                Name;
    ULONGLONG                    DefaultObject;
    ULONG                        Index;
    ULONG                        TotalNumberOfObjects;
    ULONG                        TotalNumberOfHandles;
    ULONG                        HighWaterNumberOfObjects;
    ULONG                        HighWaterNumberOfHandles;
    OBJECT_TYPE_INITIALIZER        TypeInfo;
    ULONGLONG                    TypeLock;
    ULONG                        Key;
    LIST_ENTRY                    CallbackList;
}OBJECT_TYPE_EX, * POBJECT_TYPE_EX;

 

​  PrintObjectTypeList.cpp

#include "PrintObjectTypeList.h"

VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
    UNREFERENCED_PARAMETER(pDriverObject);
    KDPRINT("【PrintObjectTypeList】", "CurrentProcessId : 0x%p CurrentIRQL : 0x%u \r\n",
        PsGetCurrentProcessId(),
        KeGetCurrentIrql());
}

void PrintObTypeIndexList(PVOID pObTypeIndexTable)
{
    if (pObTypeIndexTable)
    {
        PUCHAR pStartAddress = ((PUCHAR)pObTypeIndexTable + 8 * 2); //从第2个开始
        POBJECT_TYPE_EX *pTempObjectType = (POBJECT_TYPE_EX*)(pStartAddress);
        ULONG ulIndex = 0;
        while(*pTempObjectType != NULL)
        {
            KDPRINT("【PrintObjectTypeList】", "Index:%02ld  Address:0x%p Name:%wZ\r\n", 
                ulIndex,
                *pTempObjectType,
                &(*pTempObjectType)->Name);
            pTempObjectType++;
            ulIndex++;
        }

    }
}

PVOID GetObTypeIndexTable()
{
    UNICODE_STRING usObGetObjectType = RTL_CONSTANT_STRING(L"ObGetObjectType");
    PVOID pGetObTypeIndexTable = NULL;
    PVOID pObGetObjectType = (PVOID)MmGetSystemRoutineAddress(&usObGetObjectType);
    do
    {
        if (!pObGetObjectType)
        {
            KDPRINT("【PrintObjectTypeList】", "MmGetSystemRoutineAddress Failed! \r\n");
            break;
        }

        PUCHAR pStartAddress = (PUCHAR)pObGetObjectType;
        PUCHAR pTempAddress = pStartAddress;
        for (; pTempAddress < pStartAddress + PAGE_SIZE; pTempAddress++)
        {
            if ((*(pTempAddress - 3) == 0x48) &&
                (*(pTempAddress - 2) == 0x8d) &&
                (*(pTempAddress - 1) == 0x0d) &&
                (*(pTempAddress + 4) == 0x48) &&
                (*(pTempAddress + 5) == 0x8b) &&
                (*(pTempAddress + 6) == 0x04) &&
                (*(pTempAddress + 7) == 0xc1))
            {
                LONG lOffset = *(PLONG)(pTempAddress);
                pGetObTypeIndexTable = pTempAddress + 4 + lOffset;
                break;
            }
        }

    } while (false);
    if (pGetObTypeIndexTable)
    {
        KDPRINT("【ObRegisterCallback】", "Found ObTypeIndexTable Address:0x%p \r\n", pGetObTypeIndexTable);
    }
    else
    {
        KDPRINT("【PrintObjectTypeList】", "ObTypeIndexTable Not Found!\r\n");
    }
    return pGetObTypeIndexTable;
}

EXTERN_C NTSTATUS  DriverEntry(PDRIVER_OBJECT pDriverObject,
    PUNICODE_STRING pRegistryPath)
{
    UNREFERENCED_PARAMETER(pDriverObject);
    UNREFERENCED_PARAMETER(pRegistryPath);
    KDPRINT("【PrintObjectTypeList】", " Hello Kernel World! CurrentProcessId:0x%p CurrentIRQL:0x%u\r\n",
        PsGetCurrentProcessId(),
        KeGetCurrentIrql());
    pDriverObject->DriverUnload = DriverUnload;

    NTSTATUS ntStatus = STATUS_SUCCESS;
    PVOID pGetObTypeIndexTable = GetObTypeIndexTable();
    if (pGetObTypeIndexTable)
    {
        PrintObTypeIndexList(pGetObTypeIndexTable);
    }

    return ntStatus;
}

 

四、调试结果

  •   Win7 x64

 

  •   Win10 x64

 

 

 

五、结束语

  本次代码只考虑了x64环境下的Win7 和 Win10,32位环境下以及XP未考虑,但思路大同小异。

  通过对对象类型的遍历,可以用在使用未导出的对象类型的时候取消对象指针。

 

posted @ 2022-07-18 21:28  禁锢在时空之中的灵魂  阅读(266)  评论(0编辑  收藏  举报