-
-
[原创]通过ObRegisterCallbacks学习对象监控与反对象监控
-
2021-10-27 16:18 24747
-
一.前言
实现环境是WIN7 X86。
要实现的目标是对进程对象和线程对象实现监控来保护目标进程不被任务管理器关掉。
二.对象监控
在Windows中可以通过ObRegisterCallbacks来设置对象监控,该函数的定义如下
NTSTATUS ObRegisterCallbacks( IN POB_CALLBACK_REGISTRATION CallBackRegistration, OUT PVOID *RegistrationHandle);
参数 | 含义 |
CallBackRegistration | 指向指定回调列表和其他注册信息的OB_CALLBACK_REGISTRATION结构的指针 |
RegistrationHandle | 指向变量的指针,该变量接收一个标识已注册的回调例程集合的值。调用者将此值传递给ObUnRegisterCallbacks例程以注销回调集 |
OB_CALLBACK_REGISTRATION结构体的定义如下
typedef struct _OB_CALLBACK_REGISTRATION { __in USHORT Version; __in USHORT OperationRegistrationCount; __in UNICODE_STRING Altitude; __in PVOID RegistrationContext; __in OB_OPERATION_REGISTRATION *OperationRegistration; } OB_CALLBACK_REGISTRATION, *POB_CALLBACK_REGISTRATION;
参数 | 含义 |
Version | 请求对象回调注册的版本。驱动程序指定OB_FLT_REGISTRATION_VERSION |
OperationRegistrationCount | 指定OperationRegistration数组中的条目数 |
Altitude | 指定驱动程序Altitude的Unicode字符串。它必须指定,不能置为NULL,但是可以任意指定 |
RegistrationContext | 当运行回调例程的时候,系统将RegistrationContext值传递回调例程。该值含义由驱动程序自定义 |
OperationRegistration | 指向由OB_OPERATION_REGISTRATION结构数组的指针。每个结构指定ObjectPreCallback和ObjectPostCallback回调例程以及调用例程的操作类型 |
OB_OPERATION_REGISTRATION结构体定义如下
typedef struct _OB_OPERATION_REGISTRATION { __in POBJECT_TYPE *ObjectType; __in OB_OPERATION Operations; __in POB_PRE_OPERATION_CALLBACK PreOperation; __in POB_POST_OPERATION_CALLBACK PostOperation; } OB_OPERATION_REGISTRATION, *POB_OPERATION_REGISTRATION;
成员 | 含义 |
ObjectType | 指向触发回调例程的对象类型的指针。它指定以下值之一
|
OB_OPERATION | 指定以下一个或多个标志:
|
PreOperation | 指向ObjectPreCallback例程的指针。在请求操作发生之前,系统调用此例程 |
PostOperation | 指向ObjectPostCallback例程的指针。在请求发生之后,系统调用此例程 |
ObjectPreCallback和ObjectPostCallback的定义如下,它们的参数是一样的,只是返回值不同
OB_PREOP_CALLBACK_STATUS ObjectPreCallback( __in PVOID RegistrationContext, __in POB_PRE_OPERATION_INFORMATION OperationInformation); VOID ObjectPostCallback( __in PVOID RegistrationContext, __in POB_POST_OPERATION_INFORMATION OperationInformation );
参数 | 含义 |
RegistrationContext | 驱动程序指定为ObRegisterCallbacks例程的CallBackRegistration->RegistrationContext参数的上下文。此值的含义由驱动程序定义 |
OperationInformation | 指向OB_PRE_OPERATION_INFORMATION结构的指针,用于指定句柄操作的参数 |
最后在介绍一个函数,它可以根据线程对象获得进程对象。该函数的定义如下
PEPROCESS IoThreadToProcess( IN PETHREAD Thread);
根据以上内容就可以实现对进程的保护,具体代码如下
#include <ntifs.h> #define PROCESS_NAME "demo.exe" // 要保护的进程名 #define PROCESS_TERMINATE 0x0001 typedef struct _KLDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializationOrderModuleList; PVOID DllBase; PVOID EntryPoint; UINT32 SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; UINT32 Flags; USHORT LoadCount; USHORT TlsIndex; LIST_ENTRY HashLinks; PVOID SectionPointer; UINT32 CheckSum; UINT32 TimeDateStamp; PVOID LoadedImports; PVOID EntryPointActivationContext; PVOID PatchInformation; } KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY; // 未导出函数声明 PUCHAR PsGetProcessImageFileName(PEPROCESS pEProcess); VOID DriverUnload(IN PDRIVER_OBJECT driverObject); BOOLEAN IsProtectProcess(PEPROCESS pEProcess); // 判断是否是要保护的进程 NTSTATUS SetProcessCallback(); // 设置进程回调函数 NTSTATUS SetThreadCallback(); // 设置线程回调函数 OB_PREOP_CALLBACK_STATUS ProcessPreCall(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION pObPreOperationInfo); // 进程回调函数 OB_PREOP_CALLBACK_STATUS ThreadPreCall(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION pObPreOperationInfo); // 线程回调函数 // 进程回调对象句柄 HANDLE g_obProcessHandle; // 线程回调对象句柄 HANDLE g_obThreadHandle; NTSTATUS DriverEntry(IN PDRIVER_OBJECT driverObject, IN PUNICODE_STRING registryPath) { NTSTATUS status = STATUS_SUCCESS; PKLDR_DATA_TABLE_ENTRY pLdrData = NULL; DbgPrint("驱动加载完成\r\n"); pLdrData = (PKLDR_DATA_TABLE_ENTRY)driverObject->DriverSection; pLdrData->Flags = pLdrData->Flags | 0x20; if (NT_SUCCESS(SetProcessCallback())) { DbgPrint("进程回调函数设置完成\r\n"); } if (NT_SUCCESS(SetThreadCallback())) { DbgPrint("线程回调函数设置完成\r\n"); } exit: driverObject->DriverUnload = DriverUnload; return STATUS_SUCCESS; } NTSTATUS SetProcessCallback() { NTSTATUS status = STATUS_SUCCESS; OB_CALLBACK_REGISTRATION obCallbackReg = { 0 }; OB_OPERATION_REGISTRATION obOperationReg = { 0 }; RtlZeroMemory(&obCallbackReg, sizeof(obCallbackReg)); RtlZeroMemory(&obOperationReg, sizeof(obOperationReg)); //设置OB_OPERATION_REGISTRATION obOperationReg.ObjectType = PsProcessType; obOperationReg.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE; obOperationReg.PreOperation = (POB_PRE_OPERATION_CALLBACK)(&ProcessPreCall); //设置OB_CALLBACK_REGISTRATION obCallbackReg.Version = ObGetFilterVersion(); obCallbackReg.OperationRegistrationCount = 1; obCallbackReg.RegistrationContext = NULL; RtlInitUnicodeString(&obCallbackReg.Altitude, L"1900"); obCallbackReg.OperationRegistration = &obOperationReg; status = ObRegisterCallbacks(&obCallbackReg, &g_obProcessHandle); if (!NT_SUCCESS(status)) { DbgPrint("ObRegisterCallbacks Error 0x%X\r\n", status); } return status; } OB_PREOP_CALLBACK_STATUS ProcessPreCall(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION pObPreOperationInfo) { PEPROCESS pEProcess = NULL; // 判断对象类型 if (*PsProcessType != pObPreOperationInfo->ObjectType) { goto exit; } // 获取进程结构体对象 pEProcess = (PEPROCESS)pObPreOperationInfo->Object; if (IsProtectProcess(pEProcess)) // 是否是要保护的进程 { // 判断操作类型是创建句柄还是复制句柄 if (pObPreOperationInfo->Operation == OB_OPERATION_HANDLE_CREATE) { //是否具有关闭进程的权限,有的话删掉它 if (pObPreOperationInfo->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_TERMINATE) { pObPreOperationInfo->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_TERMINATE; } } else if (pObPreOperationInfo->Operation == OB_OPERATION_HANDLE_DUPLICATE) { //是否具有关闭进程的权限,有的话删掉它 if (pObPreOperationInfo->Parameters->DuplicateHandleInformation.DesiredAccess & PROCESS_TERMINATE) { pObPreOperationInfo->Parameters->DuplicateHandleInformation.DesiredAccess &= ~PROCESS_TERMINATE; } } } exit: return OB_PREOP_SUCCESS; } NTSTATUS SetThreadCallback() { NTSTATUS status = STATUS_SUCCESS; OB_CALLBACK_REGISTRATION obCallbackReg = { 0 }; OB_OPERATION_REGISTRATION obOperationReg = { 0 }; RtlZeroMemory(&obCallbackReg, sizeof(OB_CALLBACK_REGISTRATION)); RtlZeroMemory(&obOperationReg, sizeof(OB_OPERATION_REGISTRATION)); // 设置 OB_OPERATION_REGISTRATION obOperationReg.ObjectType = PsThreadType; obOperationReg.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE; obOperationReg.PreOperation = (POB_PRE_OPERATION_CALLBACK)(&ThreadPreCall); // 设置 OB_CALLBACK_REGISTRATION obCallbackReg.Version = ObGetFilterVersion(); obCallbackReg.OperationRegistrationCount = 1; obCallbackReg.RegistrationContext = NULL; RtlInitUnicodeString(&obCallbackReg.Altitude, L"1900"); obCallbackReg.OperationRegistration = &obOperationReg; // 注册回调函数 status = ObRegisterCallbacks(&obCallbackReg, &g_obThreadHandle); if (!NT_SUCCESS(status)) { DbgPrint("ObRegisterCallbacks Error[0x%X]\n", status); return status; } return status; } // 线程回调函数 OB_PREOP_CALLBACK_STATUS ThreadPreCall(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION pObPreOperationInfo) { PEPROCESS pEProcess = NULL; // 判断对象类型 if (*PsThreadType != pObPreOperationInfo->ObjectType) { return OB_PREOP_SUCCESS; } // 获取线程对应的进程 PEPROCESS pEProcess = IoThreadToProcess((PETHREAD)pObPreOperationInfo->Object); // 判断是否是要保护的进程, 若是, 则拒绝结束线程 if (IsProtectProcess(pEProcess)) { // 判断操作类型是创建句柄还是复制句柄 if (pObPreOperationInfo->Operation == OB_OPERATION_HANDLE_CREATE) { //是否具有关闭线程的权限,有的话删掉它 if (pObPreOperationInfo->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_TERMINATE) { pObPreOperationInfo->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_TERMINATE; } } else if (pObPreOperationInfo->Operation == OB_OPERATION_HANDLE_DUPLICATE) { //是否具有关闭线程的权限,有的话删掉它 if (pObPreOperationInfo->Parameters->DuplicateHandleInformation.DesiredAccess & PROCESS_TERMINATE) { pObPreOperationInfo->Parameters->DuplicateHandleInformation.DesiredAccess &= ~PROCESS_TERMINATE; } } } return OB_PREOP_SUCCESS; } BOOLEAN IsProtectProcess(PEPROCESS pEProcess) { BOOLEAN bRet = FALSE; PUCHAR pProcName = PsGetProcessImageFileName(pEProcess); // 获取要保护的进程名 if (pProcName) { if (strcmp(pProcName, PROCESS_NAME) == 0) { bRet = TRUE; } } return bRet; } VOID DriverUnload(IN PDRIVER_OBJECT driverObject) { // 删除进程回调 if (g_obProcessHandle) { ObUnRegisterCallbacks(g_obProcessHandle); g_obProcessHandle = NULL; } // 卸载线程回调 if (NULL != g_obThreadHandle) { ObUnRegisterCallbacks(g_obThreadHandle); g_obThreadHandle = NULL; } DbgPrint("驱动卸载完成\r\n"); }
三.反对象监控
系统设置的对象回调函数会存储在一个名头CallbackList表头的双向链表里,它存储着系统上所有ObRegisterCallbacks对象回调地址,包括操作前和操作后的回调函数地址以及对象回调句柄信息。CallbackList双向链表的结构定义如下
#pragma pack(1) typedef struct _OB_CALLBACK { LIST_ENTRY ListEntry; ULONGLONG Unknown; HANDLE ObHandle; PVOID ObTypeAddr; PVOID PreCall; PVOID PostCall; }OB_CALLBACK, *POB_CALLBACK; #pragma pack()
成员 | 含义 |
ListEntry | 存储着上一个或者下一个OB_CALLBACK结构体指针信息 |
PreCall | 操作前的回调函数 |
PostCall | 操作后的回调函数 |
ObHandle | 对象回调句柄 |
如果想要获取进程回调的双向链表信息,可以从*PsProcessType中获取。要获得线程回调的双向链表信息,可以从*PsThreadType中获取。*PsProcessType和*PsThreadType的数据结构类型为POBJECT_TYPE,定义如下
typedef struct _OBJECT_TYPE { LIST_ENTRY TypeList; // _LIST_ENTRY UNICODE_STRING Name; // _UNICODE_STRING PVOID DefaultObject; // Ptr64 Void UCHAR Index; // UChar ULONG TotalNumberOfObjects; // Uint4B ULONG TotalNumberOfHandles; // Uint4B ULONG HighWaterNumberOfObjects; // Uint4B ULONG HighWaterNumberOfHandles; // Uint4B OBJECT_TYPE_INITIALIZER TypeInfo; // _OBJECT_TYPE_INITIALIZER EX_PUSH_LOCK TypeLock; // _EX_PUSH_LOCK ULONG Key; // Uint4B LIST_ENTRY CallbackList; // _LIST_ENTRY }OBJECT_TYPE, *POBJECT_TYPE;
最后一个成员CallbackList就是双向链表表头。而倒数第四个成员的类型为OBJECT_TYPE_INITIALIZER,它的定义如下
typedef struct _OBJECT_TYPE_INITIALIZER { USHORT Length; // Uint2B UCHAR ObjectTypeFlags; // UChar ULONG ObjectTypeCode; // Uint4B ULONG InvalidAttributes; // Uint4B GENERIC_MAPPING GenericMapping; // _GENERIC_MAPPING ULONG ValidAccessMask; // Uint4B ULONG RetainAccess; // Uint4B POOL_TYPE PoolType; // _POOL_TYPE ULONG DefaultPagedPoolCharge; // Uint4B ULONG DefaultNonPagedPoolCharge; // Uint4B PVOID DumpProcedure; // Ptr64 void PVOID OpenProcedure; // Ptr64 long PVOID CloseProcedure; // Ptr64 void PVOID DeleteProcedure; // Ptr64 void PVOID ParseProcedure; // Ptr64 long PVOID SecurityProcedure; // Ptr64 long PVOID QueryNameProcedure; // Ptr64 long PVOID OkayToCloseProcedure; // Ptr64 unsigned char #if (NTDDI_VERSION >= NTDDI_WINBLUE) // Win8.1 ULONG WaitObjectFlagMask; // Uint4B USHORT WaitObjectFlagOffset; // Uint2B USHORT WaitObjectPointerOffset; // Uint2B #endif }OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;
所以可以从*PsProcessType和*PsThreadType中获得链表头,然后遍历链表找到对应的句柄对象在调用ObUnRegisterCallbacks将回调删除,具体代码如下
#include <ntifs.h> #include <wdm.h> #pragma pack(1) typedef struct _OBJECT_TYPE_INITIALIZER { USHORT Length; // Uint2B UCHAR ObjectTypeFlags; // UChar ULONG ObjectTypeCode; // Uint4B ULONG InvalidAttributes; // Uint4B GENERIC_MAPPING GenericMapping; // _GENERIC_MAPPING ULONG ValidAccessMask; // Uint4B ULONG RetainAccess; // Uint4B POOL_TYPE PoolType; // _POOL_TYPE ULONG DefaultPagedPoolCharge; // Uint4B ULONG DefaultNonPagedPoolCharge; // Uint4B PVOID DumpProcedure; // Ptr64 void PVOID OpenProcedure; // Ptr64 long PVOID CloseProcedure; // Ptr64 void PVOID DeleteProcedure; // Ptr64 void PVOID ParseProcedure; // Ptr64 long PVOID SecurityProcedure; // Ptr64 long PVOID QueryNameProcedure; // Ptr64 long PVOID OkayToCloseProcedure; // Ptr64 unsigned char #if (NTDDI_VERSION >= NTDDI_WINBLUE) // Win8.1 ULONG WaitObjectFlagMask; // Uint4B USHORT WaitObjectFlagOffset; // Uint2B USHORT WaitObjectPointerOffset; // Uint2B #endif }OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER; typedef struct _OBJECT_TYPE { LIST_ENTRY TypeList; // _LIST_ENTRY UNICODE_STRING Name; // _UNICODE_STRING PVOID DefaultObject; // Ptr64 Void UCHAR Index; // UChar ULONG TotalNumberOfObjects; // Uint4B ULONG TotalNumberOfHandles; // Uint4B ULONG HighWaterNumberOfObjects; // Uint4B ULONG HighWaterNumberOfHandles; // Uint4B OBJECT_TYPE_INITIALIZER TypeInfo; // _OBJECT_TYPE_INITIALIZER EX_PUSH_LOCK TypeLock; // _EX_PUSH_LOCK ULONG Key; // Uint4B LIST_ENTRY CallbackList; // _LIST_ENTRY }OBJECT_TYPE, *POBJECT_TYPE; typedef struct _OB_CALLBACK { LIST_ENTRY ListEntry; ULONGLONG Unknown; HANDLE ObHandle; PVOID ObTypeAddr; PVOID PreCall; PVOID PostCall; }OB_CALLBACK, *POB_CALLBACK; #pragma pack() VOID DriverUnload(IN PDRIVER_OBJECT driverObject); BOOLEAN RemoveProcessObCallback(); BOOLEAN RemoveThreadObCallback(); NTSTATUS DriverEntry(IN PDRIVER_OBJECT driverObject, IN PUNICODE_STRING registryPath) { NTSTATUS status = STATUS_SUCCESS; if (RemoveProcessObCallback()) { DbgPrint("删除进程回调成功\r\n"); } else { DbgPrint("删除进程回调失败\r\n"); } if (RemoveThreadObCallback()) { DbgPrint("删除线程回调成功\r\n"); } else { DbgPrint("删除线程回调失败\r\n"); } exit: driverObject->DriverUnload = DriverUnload; return STATUS_SUCCESS; } BOOLEAN RemoveThreadObCallback() { BOOLEAN bRet = TRUE; LIST_ENTRY CallbackList = ((POBJECT_TYPE)(*PsProcessType))->CallbackList; // 获取线程表头 POB_CALLBACK pObCallback = NULL; pObCallback = (POB_CALLBACK)CallbackList.Flink; do{ if (!MmIsAddressValid(pObCallback)) { bRet = FALSE; break; } if (pObCallback->ObHandle) { ObUnRegisterCallbacks(pObCallback->ObHandle); DbgPrint("删除回调函数成功,函数地址:0x%X\r\n", pObCallback->PreCall); } } while (CallbackList.Flink != (PLIST_ENTRY)pObCallback); return bRet; } BOOLEAN RemoveProcessObCallback() { BOOLEAN bRet = TRUE; POB_CALLBACK pObCallback = NULL; LIST_ENTRY CallbackList = ((POBJECT_TYPE)(*PsProcessType))->CallbackList; // 获取设置进程对象回调函数的双向链表表头 pObCallback = (POB_CALLBACK)CallbackList.Flink; do{ if (!MmIsAddressValid(pObCallback)) { bRet = FALSE; break; } if (pObCallback->ObHandle) { ObUnRegisterCallbacks(pObCallback->ObHandle); DbgPrint("删除回调成功,函数地址为:0x%X\r\n", pObCallback->PreCall); } pObCallback = (POB_CALLBACK)pObCallback->ListEntry.Flink; }while (CallbackList.Flink != (PLIST_ENTRY)pObCallback); return bRet; } VOID DriverUnload(IN PDRIVER_OBJECT driverObject) { DbgPrint("驱动卸载完成\r\n"); }
四.运行结果
设置回调以后想要关闭进程就会失败
将回调删除以后就可以正常关闭进程
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界