Windows ObjectType Hook 之 OkayToCloseProcedure

发布时间 2023-11-05 11:13:11作者: 禁锢在时空之中的灵魂

1、背景

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

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

  这里进行的 Hook 为 其中之一的 OkayToCloseProcedure。文章实现文件对象的过滤。

 

2、OkayToCloseProcedure函数声明

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

  这里取 x64 环境下结构:

typedef BOOLEAN (*OB_OKAYTOCLOSE_METHOD)(
    IN PEPROCESS Process OPTIONAL,
    IN PVOID Object,
    IN HANDLE Handle,
    IN KPROCESSOR_MODE PreviousMode
    );

  

3、OkayToCloseProcedure 使用逻辑分析

  用 IDA 分析 Win11 22621 版本的 ntoskrnl.exe,查找 OkayToCloseProcedure 的使用逻辑,如下:

NTSTATUS __stdcall NtClose(HANDLE Handle)
{
        char v2; // di
        ULONG_PTR v4; // rcx

        v2 = KeGetCurrentThread()->PreviousMode;
        if ((MmVerifierData & 0x100) != 0 && !v2 && !(unsigned __int8)ObpIsKernelHandle(Handle, 0i64))
                VfCheckUserHandle(v4);
        return ObpCloseHandle((ULONG_PTR)Handle);
}
__int64 __fastcall ObpCloseHandle(ULONG_PTR BugCheckParameter1, unsigned __int8 a2)
{
        ......
                v9 = ExGetHandlePointer(v7);
        v51 = BYTE1(v9);
        v10 = (_OBJECT_TYPE*)ObTypeIndexTable[(unsigned __int8)ObHeaderCookie ^ *(unsigned __int8*)(v9 + 24) ^ (unsigned __int64)BYTE1(v9)];
        if (v10->TypeInfo.OkayToCloseProcedure)
        {
                if (KeGetCurrentThread()->ApcState.Process != (struct _KPROCESS*)BugCheckParameter1a)
                {
                        KiStackAttachProcess(BugCheckParameter1a);
                        v42 = 1;
                }
                v20 = (struct _EX_RUNDOWN_REF*)BugCheckParameter1a;
                if (!v10->TypeInfo.OkayToCloseProcedure((_EPROCESS*)BugCheckParameter1a, (void*)(v9 + 48), (void*)v4, a2))
                {
                        _InterlockedExchangeAdd64(v7, 1ui64);
                        _InterlockedOr(v41, 0);
                        if (*(_QWORD*)(v6 + 48))
                                ExfUnblockPushLock(v6 + 48, 0i64);
                        KeLeaveCriticalRegion();
                        if (v42)
                                KiUnstackDetachProcess(v52, 0i64);
                        v12 = 0xC0000235;
                        goto LABEL_36;
                }
        }
        ......
}

  可以看到在调用 NtClose,然后 NtClose 调用 ObpCloseHandle,而在 ObpCloseHandle 中有判断 OkayToCloseProcedure 是否为空,不为空则调用 OkayToCloseProcedure,若 OkayToCloseProcedure 返回失败,则整个 ObpCloseHandle 返回失败,也即 NtClose 返回失败。

  于是我们的实验逻辑即在驱动中打开一个文件句柄,然后过滤文件对象的 OkayToCloseProcedure,再使用 IObit Unlocker 进行解除占用删除。

 

4、文件对象过滤

4.1 使用驱动打开一个文件占用

  主要代码如下:

EXTERN_C NTSTATUS  DriverEntry(PDRIVER_OBJECT DriverObject,
        PUNICODE_STRING RegistryPath)
{

        KDPRINT("【Hello】", "Enter...\r\n");
        KDPRINT("【Hello】", "Hello Kernel World! CurrentProcessId:0x%p CurrentIRQL:0x%u\r\n", PsGetCurrentProcessId(), KeGetCurrentIrql());
        if (RegistryPath != NULL)
        {
                KDPRINT("【Hello】", "RegistryPath:%wZ\r\n", RegistryPath);
        }

        DriverObject->DriverUnload = DriverUnload;

        OBJECT_ATTRIBUTES oba = { 0 };
        IO_STATUS_BLOCK iosb = { 0 };
        UNICODE_STRING usFilePath = RTL_CONSTANT_STRING(L"\\??\\C:\\Users\\Administrator\\Desktop\\HelloDriver.exe");
        InitializeObjectAttributes(
                &oba,
                &usFilePath,
                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE | OBJ_OPENIF,
                NULL,
                NULL);
        NTSTATUS ntStatus = ZwCreateFile(
                &hSysFile,
                GENERIC_READ,
                &oba,
                &iosb,
                NULL,
                FILE_ATTRIBUTE_NORMAL,
                FILE_SHARE_READ,
                FILE_OPEN_IF,
                FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS | FILE_SYNCHRONOUS_IO_NONALERT,
                NULL,
                0);
        if (!NT_SUCCESS(ntStatus))
        {
                KDPRINT("【Hello】", "ZwCreateFile Failed, Code:0x%08x\r\n", ntStatus);
        }
        else
        {
                KDPRINT("【Hello】", "ZwCreateFile File OK\r\n");
        }

        return STATUS_SUCCESS;
}

  驱动打开了桌面的一个文件 HelloDriver.exe,并占用, 此时直接删除文件是会提示失败。

  安装此驱动后,DebugView信息如下:

  

  直接删除如下:

  

  之后使用 IObit Unlocker 解除占用并删除,如下:

  

  文件直接被删除。

 

4.2 Hook 文件对象 OkayToCloseProcedure 

  .h文件

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

#ifdef _AMD64_
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;
#else // _AMD64_
typedef struct _OBJECT_TYPE_INITIALIZER {
        USHORT Length;
        BOOLEAN UseDefaultObject;
        BOOLEAN CaseInsensitive;
        ULONG InvalidAttributes;
        _GENERIC_MAPPING GenericMapping;
        ULONG ValidAccessMask;
        UCHAR SecurityRequired;
        UCHAR MaintainHandleCount;
        UCHAR MaintainTypeList;
        _POOL_TYPE 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;
#endif



#ifdef _AMD64_
typedef struct _OBJECT_TYPE_EX {
        LIST_ENTRY					TypeList;
        UNICODE_STRING				Name;
        PVOID					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;
#else
typedef struct _OBJECT_TYPE_EX {
        UCHAR                                           Unamed[0x38];
        LIST_ENTRY					TypeList;
        UNICODE_STRING				Name;
        PVOID					DefaultObject;
        ULONG						Index;
        ULONG						TotalNumberOfObjects;
        ULONG						TotalNumberOfHandles;
        ULONG						HighWaterNumberOfObjects;
        ULONG						HighWaterNumberOfHandles;
        OBJECT_TYPE_INITIALIZER		TypeInfo;
        ULONG						Key;
        LIST_ENTRY					CallbackList;
}OBJECT_TYPE_EX, * POBJECT_TYPE_EX;
#endif

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

typedef
BOOLEAN
(NTAPI* POKAYTOCLOSE_PROCEDURE)(
        IN PEPROCESS Process OPTIONAL,
        IN PVOID Object,
        IN HANDLE Handle,
        IN KPROCESSOR_MODE PreviousMode);



typedef struct _OBJECT_TYPE_HOOK_INFORMATION
{
        POBJECT_TYPE_EX pHookedObject;
        POKAYTOCLOSE_PROCEDURE pOringinalOkToCloseProcedureAddress;
}OBJECT_TYPE_HOOK_INFORMATION, * POBJECT_TYPE_HOOK_INFORMATION;



EXTERN_C
NTKERNELAPI
POBJECT_TYPE
NTAPI
ObGetObjectType(
        PVOID Object
);


#ifdef _AMD64_
EXTERN_C
NTKERNELAPI
NTSTATUS
PsReferenceProcessFilePointer(
        IN PEPROCESS Process,
        OUT PVOID* pFilePointer
);
#endif

EXTERN_C
NTKERNELAPI
PCHAR 
PsGetProcessImageFileName(PEPROCESS pEProcess);

void UnHookObjectType();

  .cpp 文件

#include "ObjectTypeHook.h"


OBJECT_TYPE_HOOK_INFORMATION g_HookInfomation = { 0 };
UNICODE_STRING g_usProtectedFileName = RTL_CONSTANT_STRING(L"*HELLODRIVER.EXE*");
UNICODE_STRING g_usSeperator = RTL_CONSTANT_STRING(L"\\");


BOOLEAN
NTAPI
CustomOkayToCloseProcedure(
        IN PEPROCESS Process OPTIONAL,
        IN PVOID Object,
        IN HANDLE Handle,
        IN KPROCESSOR_MODE PreviousMode)
{
        BOOLEAN bReturn = true;

        if (Object)
        {
                POBJECT_TYPE pObjectType = ObGetObjectType(Object);
                if (pObjectType == *IoFileObjectType)
                {
                       

                        if (FsRtlIsNameInExpression(&g_usProtectedFileName, &((PFILE_OBJECT)Object)->FileName, true, NULL))
                        {
                                if (PsGetProcessId(Process) == (HANDLE)4)
                                {
                                        KDPRINT("【ObjectTypeHook】", "Need Filter File Path Is %wZ\r\n", ((PFILE_OBJECT)Object)->FileName);
                                        KDPRINT("【ObjectTypeHook】", "Denied Process Id is 0x%08d\r\n", PsGetCurrentProcessId());
                                        bReturn = false;
                                } 
                        }
                }
        }

        if (!bReturn)
        {
                return false;
        }
        else
        {
                if (g_HookInfomation.pOringinalOkToCloseProcedureAddress)
                {
                        bReturn = g_HookInfomation.pOringinalOkToCloseProcedureAddress(
                                Process, Object, Handle, PreviousMode);
                }

                return bReturn;
        }

}

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

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;
        g_HookInfomation.pHookedObject = (POBJECT_TYPE_EX)(*IoFileObjectType);
        g_HookInfomation.pOringinalOkToCloseProcedureAddress =
                (POKAYTOCLOSE_PROCEDURE)(((POBJECT_TYPE_EX)(*IoFileObjectType))->TypeInfo.OkayToCloseProcedure);
        InterlockedExchangePointer(
                (PVOID*)(&g_HookInfomation.pHookedObject->TypeInfo.OkayToCloseProcedure),
                CustomOkayToCloseProcedure);
        KDPRINT("【ObjectTypeHook】", "Hook OkayToCloseProcedure!\r\n");
        return ntStatus;
}

  

4.3 实验效果

  加载第一个驱动实现文件占用,之后再加载第二个驱动进行钩子安装,如下:

  

  之后使用 使用 IObit Unlocker 进行解除占用删除。效果如下:

  

  

  虽然提示解锁删除成功,但文件还在,说明实际 IObit Unlocker 进行解除占用关闭句柄时失败了,目的实现。