Windows ObjectType Hook 之 OkayToCloseProcedure
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 进行解除占用关闭句柄时失败了,目的实现。