首页
社区
课程
招聘
[原创]通过ObRegisterCallbacks学习对象监控与反对象监控
2021-10-27 16:18 24747

[原创]通过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

指向触发回调例程的对象类型的指针。它指定以下值之

  • PsProcessType:用于进程句柄操作

  • PsThreadType:用于线程句柄操作

  • ExDesktopObjectType:用于桌面句柄操作。此值在WIN10之前的系统中不支持

OB_OPERATION

指定以下一个或多个标志:

  • OB_OPERATION_HANDLE_CREATE:已打开或将要打开一个新的进程,线程或桌面句柄

  • OB_OPERATION_HANDLE_DUPLICATE:已复制或将要复制进程,线程或桌面句柄

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世界

最后于 2022-1-19 10:40 被1900编辑 ,原因:
收藏
点赞3
打赏
分享
最新回复 (1)
雪    币: 504
活跃值: (2968)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wem 2021-12-3 21:54
2
0
谢谢分享
游客
登录 | 注册 方可回帖
返回