Windows ObjectType Hook 之 OpenProcedure

发布时间 2023-11-01 11:24:52作者: 禁锢在时空之中的灵魂

1、背景

  Object Type Hook 是基于 Object Type的一种深入的 Hook,比起常用的 SSDT Hook 更为深入。

  有关 Object Type 的分析见文章 《Windows驱动开发学习记录-ObjectType Hook之ObjectType结构相关分析》

  这里进行的 Hook 为 其中之一的 OpenProcedure。文章分两部分,分别实现 Event 对象和 Process 对象的过滤。

 

2、OpenProcedure 函数声明

  见文章 《Windows驱动开发学习记录-ObjectType Hook之ObjectType结构相关分析》

  这里取 x64 环境下结构:

typedef NTSTATUS (*OB_OPEN_METHOD)(
    IN OB_OPEN_REASON OpenReason,
    IN CHAR Flag,
    IN PEPROCESS Process OPTIONAL,
    IN PVOID Object,
    IN OUT PACCESS_MASK GrantedAccess,
    IN ULONG HandleCount
    );

 

3、Event 对象过滤

3.1 实验目标

  这里实验的目标魔兽争霸3,正常情况下只能单开,如下图所示:

  

  这里多开即会提示已经运行的消息,使用的是大多数程序单例运行的处理逻辑,启动时创建一个命名事件,然后进行相关判断。

 

3.2 魔兽的IDA分析

  用 IDA 分析魔兽的 Frozen Throne.exe,如下:

int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
        ......

        v4 = 0;
        ExitCode = 0;
        v5 = GetTickCount() + 25000;
        v19 = v5;
        hObject = CreateEventA(0, 0, 0, LauncherName);
        if (GetLastError() == 183)
        {
                CloseHandle(hObject);
                result = 0;
        }
        else
        {
                v17 = CreateEventA(0, 0, 0, aWarcraftIiiGam);
                if (GetLastError() == 183)
                {
                        LoadStringA(hInstance, 0xCu, Buffer, 260);
                        LoadStringA(hInstance, 0xDu, Caption, 260);
                        MessageBoxA(0, Buffer, Caption, 0);
                        CloseHandle(v17);
                        result = 0;
                }
                else
                {
                    ......

  其中 LanucherName 和 aWarcraftIiiGam 定义如下:

.data:00408048 ; CHAR aWarcraftIiiGam[]
.data:00408048 aWarcraftIiiGam db 'Warcraft III Game Application',0
.data:00408048                                         ; DATA XREF: WinMain(x,x,x,x):loc_40105F↑o
.data:00408066                 align 4
.data:00408068 ; CHAR LauncherName[]
.data:00408068 LauncherName    db 'WARCRAFT_III_LAUNCHER',0

  可以看出逻辑是创建这两个命名事件,如果返回 183,即 当文件已存在时,无法创建该文件的情况,表示已经在运行了,然后弹出上边的对话框。

  我们这里要实现的功能就是让它在创建命名事件时不返回 183,而是直接返回拒绝访问,按以上的逻辑就可以实现魔兽的多开。

 

3.3 实现代码

#pragma once
#include <ntifs.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;


typedef enum _OB_OPEN_REASON {
        ObCreateHandle,
        ObOpenHandle,
        ObDuplicateHandle,
        ObInheritHandle,
        ObMaxOpenReason
} OB_OPEN_REASON;

typedef
NTSTATUS
(NTAPI* POPEN_PROCEDURE)(
        IN OB_OPEN_REASON Reason,
        IN CHAR cUnnamed,
        IN PEPROCESS Process OPTIONAL,
        IN PVOID ObjectBody,
        IN PACCESS_MASK GrantedAccess,
        IN ULONG HandleCount);

typedef struct _OBJECT_TYPE_HOOK_INFORMATION
{
        POBJECT_TYPE_EX pHookedObject;
        POPEN_PROCEDURE pOringinalOpenProcedureAddress;
}OBJECT_TYPE_HOOK_INFORMATION, * POBJECT_TYPE_HOOK_INFORMATION;


OBJECT_TYPE_HOOK_INFORMATION g_HookInfomation = { 0 };

UNICODE_STRING g_usEventWarIIIOK = RTL_CONSTANT_STRING(L"*WARCRAFT_III_OK*");
UNICODE_STRING g_usEventWarIIIGameApplication = RTL_CONSTANT_STRING(L"*WARCRAFT III GAME APPLICATION*");
UNICODE_STRING g_usEventWarIIILauncher = RTL_CONSTANT_STRING(L"*WARCRAFT_III_LAUNCHER*");
PVOID GetObTypeIndexTable()
{
        UNICODE_STRING usObGetObjectType = RTL_CONSTANT_STRING(L"ObGetObjectType");
        PVOID pGetObTypeIndexTable = NULL;
        PVOID pObGetObjectType = (PVOID)MmGetSystemRoutineAddress(&usObGetObjectType);
        do
        {
                if (!pObGetObjectType)
                {
                        KDPRINT("【ObjectTypeHook】", "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("【ObjectTypeHook】", "ObTypeIndexTable Not Found!\r\n");
        }
        return pGetObTypeIndexTable;
}

void HookObjectType(PVOID pObTypeIndexTable, PUNICODE_STRING pUsObjectTypeName, PVOID pHookFunction)
{
        if (pObTypeIndexTable)
        {
                PUCHAR pStartAddress = ((PUCHAR)pObTypeIndexTable + 8 * 2); //从第2个开始
                POBJECT_TYPE_EX* pTempObjectType = (POBJECT_TYPE_EX*)(pStartAddress);
                ULONG ulIndex = 0;
                PVOID pOpenProcedureAddress = NULL;
                while (*pTempObjectType != NULL)
                {
                        KDPRINT("【ObjectTypeHook】", "Index:%02ld  Address:0x%p Name:%wZ\r\n",
                                ulIndex,
                                *pTempObjectType,
                                &(*pTempObjectType)->Name);
                        if (RtlCompareUnicodeString(&(*pTempObjectType)->Name, pUsObjectTypeName, true) == 0)
                        {
                                KDPRINT("【ObjectTypeHook】", "Found Target, Hooking...\r\n");
                                g_HookInfomation.pHookedObject = *pTempObjectType;
                                g_HookInfomation.pOringinalOpenProcedureAddress =
                                        (POPEN_PROCEDURE)(*pTempObjectType)->TypeInfo.OpenProcedure;
                                pOpenProcedureAddress  = &((*pTempObjectType)->TypeInfo.OpenProcedure);
                                InterlockedExchangePointer((PVOID*)pOpenProcedureAddress, pHookFunction);
                        }
                        pTempObjectType++;
                        ulIndex++;
                }

        }
}

NTSTATUS
NTAPI
CustomEventOpen(
        IN OB_OPEN_REASON Reason,
        IN CHAR Flag,
        IN PEPROCESS Process OPTIONAL,
        IN PVOID ObjectBody,
        IN PACCESS_MASK GrantedAccess,
        IN ULONG HandleCount)
{
        NTSTATUS ntStatus = STATUS_SUCCESS;
        ULONG ulRet = 0;
        BOOLEAN bFilterEvent = false;
        if (Reason == OB_OPEN_REASON::ObCreateHandle)
        {
                if (ObjectBody)
                {
                        POBJECT_NAME_INFORMATION pName = (POBJECT_NAME_INFORMATION)ExAllocatePoolWithTag(
                                NonPagedPool, 1024, 'Mut');
                        if (pName)
                        {
                                ntStatus = ObQueryNameString(ObjectBody, pName, 1024, &ulRet);
                                if (NT_SUCCESS(ntStatus))
                                {
                                        if (//FsRtlIsNameInExpression(&g_usWarIIIOK, &pName->Name, true, NULL) ||
                                                FsRtlIsNameInExpression(&g_usEventWarIIIGameApplication, &pName->Name, true, NULL) ||
                                                FsRtlIsNameInExpression(&g_usEventWarIIILauncher, &pName->Name, true, NULL))
                                        {
                                                KDPRINT("【ObjectTypeHook】", "Need Filter Event Name Is %wZ\r\n", &pName->Name);
                                                bFilterEvent = true;
                                        }
                                }
                                ExFreePoolWithTag(pName, 'name');
                        }
                }
        }

        if (bFilterEvent)
        {
                return STATUS_ACCESS_DENIED;
        }
        else
        {
                ntStatus = STATUS_SUCCESS;
                if (g_HookInfomation.pOringinalOpenProcedureAddress)
                {
                        ntStatus = g_HookInfomation.pOringinalOpenProcedureAddress(
                                Reason, Flag, Process, ObjectBody, GrantedAccess, HandleCount);
                }

                return ntStatus;
        }
    
}

void UnHookObjectType()
{
        KDPRINT("【ObjectTypeHook】", "UnHook...\r\n");
        if (g_HookInfomation.pHookedObject)
        {
                InterlockedExchangePointer(
                        (PVOID*)(&g_HookInfomation.pHookedObject->TypeInfo.OpenProcedure), g_HookInfomation.pOringinalOpenProcedureAddress);
        }
}

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



EXTERN_C NTSTATUS  DriverEntry(PDRIVER_OBJECT pDriverObject,
        PUNICODE_STRING pRegistryPath)
{
        UNREFERENCED_PARAMETER(pDriverObject);
        UNREFERENCED_PARAMETER(pRegistryPath);
        NTSTATUS ntStatus = STATUS_SUCCESS;
        KDPRINT("【ObjectTypeHook】", " Hello Kernel World! CurrentProcessId:0x%p CurrentIRQL:0x%u\r\n",
                PsGetCurrentProcessId(),
                KeGetCurrentIrql());
        pDriverObject->DriverUnload = DriverUnload;
        UNICODE_STRING usEventName = RTL_CONSTANT_STRING(L"Event");
        PVOID pGetObTypeIndexTable = GetObTypeIndexTable();
        if (pGetObTypeIndexTable)
        {
                HookObjectType(pGetObTypeIndexTable, &usEventName, CustomEventOpen);
        }


        return ntStatus;
}

  其中 GetObTypeIndexTable 是利用特征码搜索 Object Type, 详细参见《遍历Windows内核ObjectType》。

 

3.4 实现效果

  安装驱动效果如下:

  

  再打开魔兽争霸,效果如下:

  

  

 

4、Process 对象过滤

  实现原理同 Event 对象过滤,只不过对象换成了 Process。

  在任务管理器结束进程时要先打开目标进程,然后再结束,我这里在打开进程时直接返回失败来实现相关过滤。

 

4.1 实现代码


#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;


typedef enum _OB_OPEN_REASON {
        ObCreateHandle,
        ObOpenHandle,
        ObDuplicateHandle,
        ObInheritHandle,
        ObMaxOpenReason
} OB_OPEN_REASON;

typedef
NTSTATUS
(NTAPI* POPEN_PROCEDURE)(
        IN OB_OPEN_REASON Reason,
        IN CHAR cUnnamed,
        IN PEPROCESS Process OPTIONAL,
        IN PVOID ObjectBody,
        IN PACCESS_MASK GrantedAccess,
        IN ULONG HandleCount);

typedef struct _OBJECT_TYPE_HOOK_INFORMATION
{
        POBJECT_TYPE_EX pHookedObject;
        POPEN_PROCEDURE pOringinalOpenProcedureAddress;
}OBJECT_TYPE_HOOK_INFORMATION, * POBJECT_TYPE_HOOK_INFORMATION;

OBJECT_TYPE_HOOK_INFORMATION g_HookInfomation = { 0 };

PVOID GetObTypeIndexTable()
{
        UNICODE_STRING usObGetObjectType = RTL_CONSTANT_STRING(L"ObGetObjectType");
        PVOID pGetObTypeIndexTable = NULL;
        PVOID pObGetObjectType = (PVOID)MmGetSystemRoutineAddress(&usObGetObjectType);
        do
        {
                if (!pObGetObjectType)
                {
                        KDPRINT("【ObjectTypeHook】", "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("【ObjectTypeHook】", "ObTypeIndexTable Not Found!\r\n");
        }
        return pGetObTypeIndexTable;
}

void HookObjectType(PVOID pObTypeIndexTable, PUNICODE_STRING pUsObjectTypeName, PVOID pHookFunction)
{
        if (pObTypeIndexTable)
        {
                PUCHAR pStartAddress = ((PUCHAR)pObTypeIndexTable + 8 * 2); //从第2个开始
                POBJECT_TYPE_EX* pTempObjectType = (POBJECT_TYPE_EX*)(pStartAddress);
                ULONG ulIndex = 0;
                PVOID pOpenProcedureAddress = NULL;
                while (*pTempObjectType != NULL)
                {
                        KDPRINT("【ObjectTypeHook】", "Index:%02ld  Address:0x%p Name:%wZ\r\n",
                                ulIndex,
                                *pTempObjectType,
                                &(*pTempObjectType)->Name);
                        if (RtlCompareUnicodeString(&(*pTempObjectType)->Name, pUsObjectTypeName, true) == 0)
                        {
                                KDPRINT("【ObjectTypeHook】", "Found Target, Hooking...\r\n");
                                g_HookInfomation.pHookedObject = *pTempObjectType;
                                g_HookInfomation.pOringinalOpenProcedureAddress =
                                        (POPEN_PROCEDURE)(*pTempObjectType)->TypeInfo.OpenProcedure;
                                pOpenProcedureAddress = &((*pTempObjectType)->TypeInfo.OpenProcedure);
                                InterlockedExchangePointer((PVOID*)pOpenProcedureAddress, pHookFunction);
                        }
                        pTempObjectType++;
                        ulIndex++;
                }

        }
}

NTSTATUS
NTAPI
CustomProcessOpen(
        IN OB_OPEN_REASON Reason,
        IN CHAR Flag,
        IN PEPROCESS Process OPTIONAL,
        IN PVOID ObjectBody,
        IN PACCESS_MASK GrantedAccess,
        IN ULONG HandleCount)
{
        NTSTATUS ntStatus = STATUS_SUCCESS;

        BOOLEAN bFilterProcess = false;
        if (Reason == OB_OPEN_REASON::ObOpenHandle)
        {
                if (ObjectBody)
                {
                        POBJECT_NAME_INFORMATION pName = (POBJECT_NAME_INFORMATION)ExAllocatePoolWithTag(
                                NonPagedPool, 1024, 'Proc');
                        if (pName)
                        {
                                HANDLE hProcessId = PsGetProcessId((PEPROCESS)ObjectBody);
                                if (hProcessId == (HANDLE)5284) // exporer.exe
                                {
                                        KDPRINT("【ObjectTypeHook】", "Need Filter Mutex Name Is %wZ\r\n", &pName->Name);
                                        bFilterProcess = true;
                                }
                                ExFreePoolWithTag(pName, 'Proc');
                        }
                }
        }

        if (bFilterProcess)
        {
                return STATUS_ACCESS_DENIED;
        }
        else
        {
                ntStatus = STATUS_SUCCESS;
                if (g_HookInfomation.pOringinalOpenProcedureAddress)
                {
                        ntStatus = g_HookInfomation.pOringinalOpenProcedureAddress(
                                Reason, Flag, Process, ObjectBody, GrantedAccess, HandleCount);
                }

                return ntStatus;
        }

}

void UnHookObjectType()
{
        KDPRINT("【ObjectTypeHook】", "UnHook...\r\n");
        if (g_HookInfomation.pHookedObject)
        {
                InterlockedExchangePointer(
                        (PVOID*)(&g_HookInfomation.pHookedObject->TypeInfo.OpenProcedure), g_HookInfomation.pOringinalOpenProcedureAddress);
        }
}

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



EXTERN_C NTSTATUS  DriverEntry(PDRIVER_OBJECT pDriverObject,
        PUNICODE_STRING pRegistryPath)
{
        UNREFERENCED_PARAMETER(pDriverObject);
        UNREFERENCED_PARAMETER(pRegistryPath);
        NTSTATUS ntStatus = STATUS_SUCCESS;
        KDPRINT("【ObjectTypeHook】", " Hello Kernel World! CurrentProcessId:0x%p CurrentIRQL:0x%u\r\n",
                PsGetCurrentProcessId(),
                KeGetCurrentIrql());
        pDriverObject->DriverUnload = DriverUnload;
        UNICODE_STRING usEventName = RTL_CONSTANT_STRING(L"Process");
        PVOID pGetObTypeIndexTable = GetObTypeIndexTable();
        if (pGetObTypeIndexTable)
        {
                HookObjectType(pGetObTypeIndexTable, &usEventName, CustomProcessOpen);
        }


        return ntStatus;
}

 

4.2 实现效果

  在安装驱动后用任务管理器结束 explorer.exe,在测试机上 PID 为 5284,效果如下: