驅動開發:核心註冊並監控物件回撥

2022-10-24 12:02:38

在筆者上一篇文章《驅動開發:核心列舉程序與執行緒ObCall回撥》簡單介紹瞭如何列舉系統中已經存在的程序與執行緒回撥,本章LyShark將通過物件回撥實現對程序執行緒的控制程式碼監控,在核心中提供了ObRegisterCallbacks回撥,使用這個核心回撥函數,可註冊一個物件回撥,不過目前該函數只能監控程序與執行緒控制程式碼操作,通過監控程序或執行緒控制程式碼,可實現保護指定程序執行緒不被終止的目的。

由於目前物件回撥只能監控程序與執行緒,而這個監控是通過ObjectType這麼一個成員控制的,如果成員是PsProcessType則代表監控程序,反之PsThreadType則是監控執行緒,無論監控程序還是執行緒都呼叫ObRegisterCallbacks這個函數來完成註冊。

函數ObRegisterCallbacks其微軟對他的定義是這樣的,使用者傳入OB_OPERATION_REGISTRATION結構,以及OB_CALLBACK_REGISTRATION回撥結構,其中PreOperation則是傳入的回撥函數,也是最重要的,其次是ObjectType指定成程序回撥。

NTSTATUS ObRegisterCallbacks(
  [in]  POB_CALLBACK_REGISTRATION CallbackRegistration,
  [out] PVOID                     *RegistrationHandle
);

首先來實現一個檢測的案例,註冊一個程序回撥物件MyLySharkComObjectCallBack,通過ObRegisterCallbacks註冊的回撥只需要傳入一個填充好的OB_CALLBACK_REGISTRATION回撥結構體,以及一個全域性控制程式碼即可,這個全域性控制程式碼的作用僅僅只是在程式結束時,呼叫ObUnRegisterCallbacks解除安裝監控而已,實現程式碼如下所示。

// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntddk.h>
#include <ntstrsafe.h>

PVOID Globle_Object_Handle;

// 繞過簽名檢測
void BypassCheckSign(PDRIVER_OBJECT pDriverObj)
{
	typedef struct _LDR_DATA
	{
		struct _LIST_ENTRY InLoadOrderLinks;
		struct _LIST_ENTRY InMemoryOrderLinks;
		struct _LIST_ENTRY InInitializationOrderLinks;
		VOID*        DllBase;
		VOID*        EntryPoint;
		ULONG32      SizeOfImage;
		UINT8        _PADDING0_[0x4];
		struct _UNICODE_STRING FullDllName;
		struct _UNICODE_STRING BaseDllName;
		ULONG32      Flags;
	}LDR_DATA, *PLDR_DATA;

	PLDR_DATA ldr;
	ldr = (PLDR_DATA)(pDriverObj->DriverSection);
	ldr->Flags |= 0x20;
}

// 自定義回撥
OB_PREOP_CALLBACK_STATUS MyLySharkComObjectCallBack(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation)
{
	DbgPrint("[lyshark] 執行回撥函數... \n");
	return STATUS_SUCCESS;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
	ObUnRegisterCallbacks(Globle_Object_Handle);
	DbgPrint("回撥解除安裝完成... \n");
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("hello lyshark.com \n");

	BypassCheckSign(Driver);

	OB_OPERATION_REGISTRATION Base;                          // 回撥函數結構體
	OB_CALLBACK_REGISTRATION CallbackReg;                    // 回撥函數

	CallbackReg.RegistrationContext = NULL;                  // 註冊上下文(你回撥函數返回引數)
	CallbackReg.Version = OB_FLT_REGISTRATION_VERSION;       // 註冊回撥版本
	CallbackReg.OperationRegistration = &Base;               // 回撥結構體
	CallbackReg.OperationRegistrationCount = 1;              // 操作計數(下鉤數量)

	RtlUnicodeStringInit(&CallbackReg.Altitude, L"600000");   // 長度
	Base.ObjectType = PsProcessType;                          // 程序操作型別.此處為程序操作
	Base.Operations = OB_OPERATION_HANDLE_CREATE;             // 操作控制程式碼建立
	Base.PreOperation = MyLySharkComObjectCallBack;           // 你自己的回撥函數
	Base.PostOperation = NULL;

	// 註冊回撥
	if (ObRegisterCallbacks(&CallbackReg, &Globle_Object_Handle))
	{
		DbgPrint("[lyshark message] 回撥註冊成功...");
	}
	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

當驅動程式被載入以後,一旦有程序執行則會執行我們自己的MyLySharkComObjectCallBack回撥,而在回撥函數內則可以執行任意功能,執行如下所示。

如上所示只是演示基本的回撥申請流程,回撥函數通常需要包含兩個值,其一RegistrationContext用於標註上下文,其二POB_PRE_OPERATION_INFORMATION則用於標註程序或者執行緒建立的資訊結構體。

OB_PREOP_CALLBACK_STATUS MyLySharkComObjectCallBack(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation)

那麼如何實現攔截程序啟動這個功能呢,我們可以在回撥函數中寫入以下程式碼進行攔截。

  • CreateHandleInformation.DesiredAccess 將開啟控制程式碼的許可權清零
  • CreateHandleInformation.OriginalDesiredAccess 判斷是否終止
if (pOperationInformation->Operation == OB_OPERATION_HANDLE_CREATE)
{
	DbgPrint("lyshark.exe 程序開啟 \n");
	pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess=0;
	if ((pOperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_TERMINATE) == PROCESS_TERMINATE)
	{
		pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_TERMINATE;
	}
}

攔截程序建立核心程式碼如下所示。

// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntddk.h>
#include <ntstrsafe.h>

#define PROCESS_TERMINATE 0x1

// 匯出兩個API
NTKERNELAPI PEPROCESS IoThreadToProcess(PETHREAD Thread);
NTKERNELAPI char* PsGetProcessImageFileName(PEPROCESS Process);

// 全域性控制程式碼
PVOID Globle_Object_Handle = NULL;

// 繞過簽名檢測
void BypassCheckSign(PDRIVER_OBJECT pDriverObj)
{
	typedef struct _LDR_DATA
	{
		struct _LIST_ENTRY InLoadOrderLinks;
		struct _LIST_ENTRY InMemoryOrderLinks;
		struct _LIST_ENTRY InInitializationOrderLinks;
		VOID*        DllBase;
		VOID*        EntryPoint;
		ULONG32      SizeOfImage;
		UINT8        _PADDING0_[0x4];
		struct _UNICODE_STRING FullDllName;
		struct _UNICODE_STRING BaseDllName;
		ULONG32      Flags;
	}LDR_DATA, *PLDR_DATA;

	PLDR_DATA ldr;
	ldr = (PLDR_DATA)(pDriverObj->DriverSection);
	ldr->Flags |= 0x20;
}

// 判斷是否是需要保護的程序
BOOLEAN CheckProcess(PEPROCESS eprocess)
{
	char *Name = PsGetProcessImageFileName(eprocess);
	if (!_stricmp("lyshark.exe", Name))
		return TRUE;
	else
		return FALSE;
}

// 程序回撥
OB_PREOP_CALLBACK_STATUS MyLySharkProcessObjectCallBack(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION pOperationInformation)
{
	HANDLE pid;

	// 只取出程序回撥
	if (pOperationInformation->ObjectType != *PsProcessType)
	{
		return OB_PREOP_SUCCESS;
	}

	// 得到所有程序的ID
	pid = PsGetProcessId((PEPROCESS)pOperationInformation->Object);
	// DbgPrint("程序PID= %ld \n", pid);

	UNREFERENCED_PARAMETER(RegistrationContext);

	// 驗證是否是需要的程序
	if (CheckProcess((PEPROCESS)pOperationInformation->Object))
	{
		// 建立控制程式碼
		if (pOperationInformation->Operation == OB_OPERATION_HANDLE_CREATE)
		{
			DbgPrint("lyshark.exe 程序開啟事件 \n");
			pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess=0;
			if ((pOperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_TERMINATE) == PROCESS_TERMINATE)
			{
				DbgPrint("[LyShark Message] 攔截程序開啟 \n");
				pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_TERMINATE;
			}
		}
		// 複製控制程式碼
		if (pOperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE)
		{
			DbgPrint("lyshark.exe 程序被關閉 \n");
			pOperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess=0;
			if ((pOperationInformation->Parameters->DuplicateHandleInformation.OriginalDesiredAccess & PROCESS_TERMINATE) == PROCESS_TERMINATE)
			{
				pOperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= ~PROCESS_TERMINATE;
			}
		}
	}
	return OB_PREOP_SUCCESS;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
	ObUnRegisterCallbacks(Globle_Object_Handle);
	DbgPrint("回撥解除安裝完成... \n");
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("hello lyshark.com \n");

	BypassCheckSign(Driver);

	OB_OPERATION_REGISTRATION ob_process_callback;
	OB_CALLBACK_REGISTRATION op_process_operation;

	memset(&ob_process_callback, 0, sizeof(ob_process_callback));
	ob_process_callback.ObjectType = PsProcessType;
	ob_process_callback.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
	ob_process_callback.PreOperation = MyLySharkProcessObjectCallBack;
	ob_process_callback.PostOperation = NULL;

	RtlUnicodeStringInit(&op_process_operation.Altitude, L"600000");
	op_process_operation.RegistrationContext = NULL;
	op_process_operation.Version = OB_FLT_REGISTRATION_VERSION;
	op_process_operation.OperationRegistration = &ob_process_callback;
	op_process_operation.OperationRegistrationCount = 1;

	// 註冊程序回撥
	if (ObRegisterCallbacks(&op_process_operation, &Globle_Object_Handle))
	{
		DbgPrint("程序回撥註冊成功...");
	}

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

載入這個驅動,當有程序被建立時,則首先判斷是否是lyshark.exe如果是則直接禁止開啟,也就是終止掉。

同理程序可以被攔截,那麼如果增加更多的過濾條件,則執行緒同樣可以被攔截,攔截執行緒程式碼如下所示。

// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntddk.h>
#include <ntstrsafe.h>

#define THREAD_TERMINATE2 0x1

// 匯出兩個API
NTKERNELAPI PEPROCESS IoThreadToProcess(PETHREAD Thread);
NTKERNELAPI char* PsGetProcessImageFileName(PEPROCESS Process);

// 全域性控制程式碼
PVOID Globle_Object_Handle = NULL;

// 繞過簽名檢測
void BypassCheckSign(PDRIVER_OBJECT pDriverObj)
{
	typedef struct _LDR_DATA
	{
		struct _LIST_ENTRY InLoadOrderLinks;
		struct _LIST_ENTRY InMemoryOrderLinks;
		struct _LIST_ENTRY InInitializationOrderLinks;
		VOID*        DllBase;
		VOID*        EntryPoint;
		ULONG32      SizeOfImage;
		UINT8        _PADDING0_[0x4];
		struct _UNICODE_STRING FullDllName;
		struct _UNICODE_STRING BaseDllName;
		ULONG32      Flags;
	}LDR_DATA, *PLDR_DATA;

	PLDR_DATA ldr;
	ldr = (PLDR_DATA)(pDriverObj->DriverSection);
	ldr->Flags |= 0x20;
}

// 判斷是否是需要保護的程序
BOOLEAN CheckProcess(PEPROCESS eprocess)
{
	char *Name = PsGetProcessImageFileName(eprocess);
	if (!_stricmp("lyshark.exe", Name))
		return TRUE;
	else
		return FALSE;
}

// 執行緒回撥
OB_PREOP_CALLBACK_STATUS MyThreadObjectCallBack(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION pOperationInformation)
{
	PEPROCESS ep;
	PETHREAD et;
	HANDLE pid;

	// 執行緒過濾
	if (pOperationInformation->ObjectType != *PsThreadType)
	{
		return OB_PREOP_SUCCESS;
	}

	et = (PETHREAD)pOperationInformation->Object;
	ep = IoThreadToProcess(et);
	pid = PsGetProcessId(ep);

	// DbgPrint("執行緒PID= %ld | TID= %ld \n", pid, PsGetThreadId(et));
	UNREFERENCED_PARAMETER(RegistrationContext);

	if (CheckProcess(ep))
	{
		if (pOperationInformation->Operation == OB_OPERATION_HANDLE_CREATE)
		{
			pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess=0;
			if ((pOperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & THREAD_TERMINATE2) == THREAD_TERMINATE2)
			{
				DbgPrint("[LyShark] 攔截lyshark.exe程序內 %d 執行緒建立 \n", PsGetThreadId(et));
				pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~THREAD_TERMINATE2;
			}
		}
		if (pOperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE)
		{
			pOperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess=0;
			if ((pOperationInformation->Parameters->DuplicateHandleInformation.OriginalDesiredAccess & THREAD_TERMINATE2) == THREAD_TERMINATE2)
			{
				pOperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= ~THREAD_TERMINATE2;
			}
		}
	}
	return OB_PREOP_SUCCESS;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
	ObUnRegisterCallbacks(Globle_Object_Handle);
	DbgPrint("回撥解除安裝完成... \n");
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("hello lyshark.com \n");

	BypassCheckSign(Driver);

	OB_OPERATION_REGISTRATION ob_thread_callback;
	OB_CALLBACK_REGISTRATION op_thread_operation;

	memset(&ob_thread_callback, 0, sizeof(ob_thread_callback));
	ob_thread_callback.ObjectType = PsThreadType;
	ob_thread_callback.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
	ob_thread_callback.PreOperation = MyThreadObjectCallBack;
	ob_thread_callback.PostOperation = NULL;

	RtlUnicodeStringInit(&op_thread_operation.Altitude, L"600001");
	op_thread_operation.RegistrationContext = NULL;
	op_thread_operation.Version = OB_FLT_REGISTRATION_VERSION;
	op_thread_operation.OperationRegistration = &ob_thread_callback;
	op_thread_operation.OperationRegistrationCount = 1;

	// 註冊程序回撥
	if (ObRegisterCallbacks(&op_thread_operation, &Globle_Object_Handle))
	{
		DbgPrint("程序回撥註冊成功...");
	}

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

這段驅動載入後,如果有新執行緒被建立,則會被攔截並列印輸出,效果圖如下。

參考文獻

https://www.cnblogs.com/ciyze0101/p/5468175.html