OpenProcedure of Windows ObjectType Hook

1. Background

Object Type Hook is an in-depth Hook based on Object Type, which is more in-depth than the commonly used SSDT Hook.

For the analysis of Object Type, please see the article “Windows Driver Development Learning Record-ObjectType Hook’s ObjectType Structure Related Analysis”.

The Hook used here is one of the OpenProcedures. The article is divided into two parts, respectively implementing the filtering of Event objects and Process objects.

2. OpenProcedure function declaration

See the article “Windows Driver Development Learning Record-Related Analysis of ObjectType Structure of ObjectType Hook”.

Here is the structure in x64 environment:

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 object filtering

3.1 Experimental objectives

The target of the experiment here is Warcraft 3. Under normal circumstances, it can only be played solo, as shown in the figure below:

When opened here, a message that has been run will be prompted. The processing logic of most singleton runs of the program is used. A named event is created when starting, and then relevant judgments are made.

3.2 IDA analysis of Warcraft

Use IDA to analyze Warcraft’s Frozen Throne.exe, as follows:

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
                {
                    ......

Where LanucherName and aWarcraftIiiGam are defined as follows:

.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

It can be seen that the logic is to create these two named events. If 183 is returned, that is, when the file already exists and the file cannot be created, it means that it is already running, and then the above dialog box will pop up.

The function we want to implement here is to make it not return 183 when creating a named event, but directly return access denied. According to the above logic, multiple openings of World of Warcraft can be achieved.

3.3 Implementation code

#pragma once
#include <ntifs.h>


#ifDBG
#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;
        UCHARUseDefaultObject: 1;
        UCHARSecurityRequired: 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;
        ULONGInvalidAttributes;
        GENERIC_MAPPING GenericMapping;
        ULONG ValidAccessMask;
        ULONG RetainAccess;
        ULONGPoolType;
        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( & amp;usObGetObjectType);
        do
        {
                if (!pObGetObjectType)
                {
                        KDPRINT("[ObjectTypeHook]", "MmGetSystemRoutineAddress Failed! \r\\
");
                        break;
                }

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

        } while (false);
        if (pGetObTypeIndexTable)
        {
                KDPRINT("[ObRegisterCallback]", "Found ObTypeIndexTable Address:0x%p \r\\
", pGetObTypeIndexTable);
        }
        else
        {
                KDPRINT("[ObjectTypeHook]", "ObTypeIndexTable Not Found!\r\\
");
        }
        return pGetObTypeIndexTable;
}

void HookObjectType(PVOID pObTypeIndexTable, PUNICODE_STRING pUsObjectTypeName, PVOID pHookFunction)
{
        if (pObTypeIndexTable)
        {
                PUCHAR pStartAddress = ((PUCHAR)pObTypeIndexTable + 8 * 2); //Start from the 2nd one
                POBJECT_TYPE_EX* pTempObjectType = (POBJECT_TYPE_EX*)(pStartAddress);
                ULONG ulIndex = 0;
                PVOID pOpenProcedureAddress = NULL;
                while (*pTempObjectType != NULL)
                {
                        KDPRINT("[ObjectTypeHook]", "Index: ld Address:0x%p Name:%wZ\r\\
",
                                ulIndex,
                                *pTempObjectType,
                                 & amp;(*pTempObjectType)->Name);
                        if (RtlCompareUnicodeString( & amp;(*pTempObjectType)->Name, pUsObjectTypeName, true) == 0)
                        {
                                KDPRINT("[ObjectTypeHook]", "Found Target, Hooking...\r\\
");
                                g_HookInfomation.pHookedObject = *pTempObjectType;
                                g_HookInfomation.pOringinalOpenProcedureAddress =
                                        (POPEN_PROCEDURE)(*pTempObjectType)->TypeInfo.OpenProcedure;
                                pOpenProcedureAddress = & amp;((*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;
        ULONGulRet = 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, & amp;ulRet);
                                if (NT_SUCCESS(ntStatus))
                                {
                                        if (//FsRtlIsNameInExpression( & amp;g_usWarIIIOK, & amp;pName->Name, true, NULL) ||
                                                FsRtlIsNameInExpression( & amp;g_usEventWarIIIGameApplication, & amp;pName->Name, true, NULL) ||
                                                FsRtlIsNameInExpression( & amp;g_usEventWarIIILauncher, & amp;pName->Name, true, NULL))
                                        {
                                                KDPRINT("[ObjectTypeHook]", "Need Filter Event Name Is %wZ\r\\
", & amp;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\\
");
        if (g_HookInfomation.pHookedObject)
        {
                InterlockedExchangePointer(
                        (PVOID*)( & amp;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\\
",
                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\\
",
                PsGetCurrentProcessId(),
                KeGetCurrentIrql());
        pDriverObject->DriverUnload = DriverUnload;
        UNICODE_STRING usEventName = RTL_CONSTANT_STRING(L"Event");
        PVOID pGetObTypeIndexTable = GetObTypeIndexTable();
        if (pGetObTypeIndexTable)
        {
                HookObjectType(pGetObTypeIndexTable, & amp;usEventName, CustomEventOpen);
        }


        return ntStatus;
}

GetObTypeIndexTable uses signatures to search for Object Type. For details, see “Traversing Windows Kernel ObjectType”.

3.4 Implementing effects

The effect of installing the driver is as follows:

Open Warcraft again, the effect is as follows:

4. Process object filtering

The implementation principle is the same as Event object filtering, except that the object is replaced by Process.

When the task manager ends the process, the target process must be opened first, and then ended. Here, I directly return failure when opening the process to implement related filtering.

4.1 Implementation code

#ifDBG
#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;
        UCHARUseDefaultObject: 1;
        UCHARSecurityRequired: 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;
        ULONGInvalidAttributes;
        GENERIC_MAPPING GenericMapping;
        ULONG ValidAccessMask;
        ULONG RetainAccess;
        ULONGPoolType;
        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( & amp;usObGetObjectType);
        do
        {
                if (!pObGetObjectType)
                {
                        KDPRINT("[ObjectTypeHook]", "MmGetSystemRoutineAddress Failed! \r\\
");
                        break;
                }

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

        } while (false);
        if (pGetObTypeIndexTable)
        {
                KDPRINT("[ObRegisterCallback]", "Found ObTypeIndexTable Address:0x%p \r\\
", pGetObTypeIndexTable);
        }
        else
        {
                KDPRINT("[ObjectTypeHook]", "ObTypeIndexTable Not Found!\r\\
");
        }
        return pGetObTypeIndexTable;
}

void HookObjectType(PVOID pObTypeIndexTable, PUNICODE_STRING pUsObjectTypeName, PVOID pHookFunction)
{
        if (pObTypeIndexTable)
        {
                PUCHAR pStartAddress = ((PUCHAR)pObTypeIndexTable + 8 * 2); //Start from the 2nd one
                POBJECT_TYPE_EX* pTempObjectType = (POBJECT_TYPE_EX*)(pStartAddress);
                ULONG ulIndex = 0;
                PVOID pOpenProcedureAddress = NULL;
                while (*pTempObjectType != NULL)
                {
                        KDPRINT("[ObjectTypeHook]", "Index: ld Address:0x%p Name:%wZ\r\\
",
                                ulIndex,
                                *pTempObjectType,
                                 & amp;(*pTempObjectType)->Name);
                        if (RtlCompareUnicodeString( & amp;(*pTempObjectType)->Name, pUsObjectTypeName, true) == 0)
                        {
                                KDPRINT("[ObjectTypeHook]", "Found Target, Hooking...\r\\
");
                                g_HookInfomation.pHookedObject = *pTempObjectType;
                                g_HookInfomation.pOringinalOpenProcedureAddress =
                                        (POPEN_PROCEDURE)(*pTempObjectType)->TypeInfo.OpenProcedure;
                                pOpenProcedureAddress = & amp;((*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\\
", & amp;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\\
");
        if (g_HookInfomation.pHookedObject)
        {
                InterlockedExchangePointer(
                        (PVOID*)( & amp;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\\
",
                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\\
",
                PsGetCurrentProcessId(),
                KeGetCurrentIrql());
        pDriverObject->DriverUnload = DriverUnload;
        UNICODE_STRING usEventName = RTL_CONSTANT_STRING(L"Process");
        PVOID pGetObTypeIndexTable = GetObTypeIndexTable();
        if (pGetObTypeIndexTable)
        {
                HookObjectType(pGetObTypeIndexTable, & amp;usEventName, CustomProcessOpen);
        }


        return ntStatus;
}

4.2 Implementing effects

After installing the driver, use Task Manager to end explorer.exe. On the test machine, the PID is 5284. The effect is as follows: