Windows ObjectType Hook 之 SecurityProcedure
1、背景
Object Type Hook 是基于 Object Type的一种深入的 Hook,比起常用的 SSDT Hook 更为深入。
有关 Object Type 的分析见文章 《Windows驱动开发学习记录-ObjectType Hook之ObjectType结构相关分析》。
这里进行的 Hook 为 其中之一的 SecurityProcedure。文章实现进程打开的过滤。
2、SecurityProcedure函数声明
见文章 《Windows驱动开发学习记录-ObjectType Hook之ObjectType结构相关分析》。
这里取 x64 环境下结构:
typedef NTSTATUS (*OB_SECURITY_METHOD)(
IN PVOID Object,
IN SECURITY_OPERATION_CODE OperationCode,
IN PSECURITY_INFORMATION SecurityInformation,
IN OUT PSECURITY_DESCRIPTOR SecurityDescriptor,
IN OUT PULONG CapturedLength,
IN OUT PSECURITY_DESCRIPTOR *ObjectsSecurityDescriptor,
IN POOL_TYPE PoolType,
IN PGENERIC_MAPPING GenericMapping
);
3、SecurityProcedure 使用逻辑分析
用 IDA 分析 Win11 22621 版本的 ntoskrnl.exe,查找 SecurityProcedure 的使用逻辑,如下:
__int64 __fastcall ObpCreateHandle(int a1, _QWORD* a2, signed int a3, __int64 a4, int a5, int a6, char a7, __int64 a8, int a9, PVOID* a10, _QWORD* a11)
{
......
v79 = (_OBJECT_TYPE*)ObTypeIndexTable[(unsigned __int8)ObHeaderCookie ^ v78];
v139 = v79;
if ((_UNKNOWN*)v79->TypeInfo.SecurityProcedure == &SeDefaultObjectMethod)
{
......
}
v84 = ExAllocatePool2(256i64, (unsigned int)ObpDefaultSecurityDescriptorLength, 1901290063i64);
if (v84)
{
v105 = v79->TypeInfo.SecurityProcedure;
GenericMapping = &v79->TypeInfo.GenericMapping;
v122 = v139->TypeInfo.PoolType;
v138 = (void**)(v77 + 40);
v29 = v105(
Object,
QuerySecurityDescriptor,
(unsigned int*)&v141,
(void*)v84,
&v137,
(void**)v77 + 5,
v122,
GenericMapping,
AccessMode);
if (v29 == 0xC0000023)
{
......
}
if (v29 >= 0)
{
......
}
ExFreePoolWithTag((PVOID)v84, 0);
}
return (unsigned int)v29;
}
可以看到在调用 ObpCreateHandle 中有判断 SecurityProcedure 是否为默认的 SeDefaultObjectMethod,不为则调用指定的 SecurityProcedure ,若 SecurityProcedure 返回失败,则整个 ObpCreateHandle 返回失败。
于是我们的实验逻辑即过滤进程对象的 SecurityProcedure ,再用任务管理器结束过滤的进程。
4、进程对象过滤
4.1 代码
.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
NTSTATUS
(NTAPI *PSECURITY_PROCEDURE)(
IN PVOID Object,
IN SECURITY_OPERATION_CODE OperationCode,
IN PSECURITY_INFORMATION SecurityInformation,
IN OUT PSECURITY_DESCRIPTOR SecurityDescriptor,
IN OUT PULONG CapturedLength,
IN OUT PSECURITY_DESCRIPTOR* ObjectsSecurityDescriptor,
IN POOL_TYPE PoolType,
IN PGENERIC_MAPPING GenericMapping,
IN CHAR Flag
);
typedef struct _OBJECT_TYPE_HOOK_INFORMATION
{
POBJECT_TYPE_EX pHookedObject;
PSECURITY_PROCEDURE pOringinalSecurityProcedureAddress;
}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_usCurrentProcessName = RTL_CONSTANT_STRING(L"*TASKMGR.EXE*");
UNICODE_STRING g_usTargetProcessName = RTL_CONSTANT_STRING(L"*DBGVIEW64.EXE*");
NTSTATUS
NTAPI CustomQuerySecurityProcedure(
IN PVOID Object,
IN SECURITY_OPERATION_CODE OperationCode,
IN PSECURITY_INFORMATION SecurityInformation,
IN OUT PSECURITY_DESCRIPTOR SecurityDescriptor,
IN OUT PULONG CapturedLength,
IN OUT PSECURITY_DESCRIPTOR* ObjectsSecurityDescriptor,
IN POOL_TYPE PoolType,
IN PGENERIC_MAPPING GenericMapping,
IN CHAR Flag)
{
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
PFILE_OBJECT pTargetFileObject = NULL;
PFILE_OBJECT pCurrentFileObject = NULL;
POBJECT_NAME_INFORMATION pTargetProcessNameInformation = NULL;
POBJECT_NAME_INFORMATION pCurrentProcessNameInformation = NULL;
BOOLEAN bDenied = false;
do
{
if (!Object)
{
KDPRINT("【ObjectTypeHook】", "Object is Null\r\n");
break;
}
POBJECT_TYPE pObjectType = ObGetObjectType(Object);
if (pObjectType != *PsProcessType)
{
break;
}
if (OperationCode != SECURITY_OPERATION_CODE::QuerySecurityDescriptor)
{
break;
}
ntStatus = PsReferenceProcessFilePointer((PEPROCESS)Object, (PVOID*)&pTargetFileObject);
if (!NT_SUCCESS(ntStatus))
{
//KDPRINT("【ObjectTypeHook】", "PsReferenceProcessFilePointer failed, cod:0x%08x\r\n", ntStatus);
break;
}
ntStatus = IoQueryFileDosDeviceName(pTargetFileObject, &pTargetProcessNameInformation);
if (!NT_SUCCESS(ntStatus))
{
KDPRINT("【ObjectTypeHook】", "IoQueryFileDosDeviceName failed 1\r\n");
break;
}
if (!FsRtlIsNameInExpression(&g_usTargetProcessName, &pTargetProcessNameInformation->Name, true, NULL))
{
break;
}
PEPROCESS pCurrentProcess = PsGetCurrentProcess();
ntStatus = PsReferenceProcessFilePointer(pCurrentProcess, (PVOID*)&pCurrentFileObject);
if (!NT_SUCCESS(ntStatus))
{
//KDPRINT("【ObjectTypeHook】", "PsReferenceProcessFilePointer failed, cod:0x%08x\r\n", ntStatus);
break;
}
ntStatus = IoQueryFileDosDeviceName(pCurrentFileObject, &pCurrentProcessNameInformation);
if (!NT_SUCCESS(ntStatus))
{
KDPRINT("【ObjectTypeHook】", "IoQueryFileDosDeviceName failed 2\r\n");
break;
}
if (!FsRtlIsNameInExpression(&g_usCurrentProcessName, &pCurrentProcessNameInformation->Name, true, NULL))
{
break;
}
KDPRINT("【ObjectTypeHook】", "Action denied\r\n");
KDPRINT("【ObjectTypeHook】", "Target Process: %wZ\r\n", &pTargetProcessNameInformation->Name);
KDPRINT("【ObjectTypeHook】", "Current Operation Process: %wZ\r\n", &pCurrentProcessNameInformation->Name);
bDenied = true;
} while (false);
if (pCurrentFileObject)
{
ObDereferenceObject(pCurrentFileObject);
pCurrentFileObject = NULL;
}
if (pTargetFileObject)
{
ObDereferenceObject(pTargetFileObject);
pTargetFileObject = NULL;
}
if (pCurrentProcessNameInformation)
{
ExFreePoolWithTag(pCurrentProcessNameInformation, '0');
pCurrentProcessNameInformation = NULL;
}
if (pTargetProcessNameInformation)
{
ExFreePoolWithTag(pTargetProcessNameInformation, '0');
pTargetProcessNameInformation = NULL;
}
if (bDenied)
{
ntStatus = STATUS_ACCESS_DENIED;
}
else
{
if (g_HookInfomation.pOringinalSecurityProcedureAddress)
{
ntStatus = g_HookInfomation.pOringinalSecurityProcedureAddress(
Object, OperationCode, SecurityInformation, SecurityDescriptor, CapturedLength,
ObjectsSecurityDescriptor, PoolType, GenericMapping, Flag);
}
}
return ntStatus;
}
void UnHookObjectType()
{
KDPRINT("【ObjectTypeHook】", "UnHook...\r\n");
if (g_HookInfomation.pHookedObject)
{
InterlockedExchangePointer(
(PVOID*)(&g_HookInfomation.pHookedObject->TypeInfo.SecurityProcedure),
g_HookInfomation.pOringinalSecurityProcedureAddress);
}
}
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)(*PsProcessType);
g_HookInfomation.pOringinalSecurityProcedureAddress =
(PSECURITY_PROCEDURE)(((POBJECT_TYPE_EX)(*PsProcessType))->TypeInfo.SecurityProcedure);
InterlockedExchangePointer(
(PVOID*)(&g_HookInfomation.pHookedObject->TypeInfo.SecurityProcedure),
CustomQuerySecurityProcedure);
KDPRINT("【ObjectTypeHook】", "Hook QueryNameProcedure!\r\n");
return ntStatus;
}
代码实现了任务管理器进程 Taskmgr.exe 打开 DbgView64.exe 时 SecurityProcedure 返回拒绝访问,也即 ObpCreateHandle 返回失败。
4.2 实验效果
加载驱动后打开 DbgView64.exe, 然后在进程管理器中结束该进程,效果如下: