驅動開發:內核監控Register注冊表回調

在筆者前一篇文章《驅動開發:內核枚舉Registry注冊表回調》中實現了對注冊表的枚舉 , 本章將實現對注冊表的監控,不同于32位系統在64位系統中,微軟為我們提供了兩個針對注冊表的專用內核監控函數 , 通過這兩個函數可以在不劫持內核API的前提下實現對注冊表增加,刪除,創建等事件的有效監控 , 注冊表監視通常會通過CmRegisterCallback創建監控事件并傳入自己的回調函數,與該創建對應的是CmUnRegisterCallback當注冊表監控結束后可用于注銷回調 。

  • CmRegisterCallback 設置注冊表回調
  • CmUnRegisterCallback 注銷注冊表回調
默認情況下CmRegisterCallback需傳入三個參數,參數一回調函數地址,參數二空余,參數三回調句柄,微軟定義如下 。
// 署名權// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.com// 參數1:回調函數地址// 參數2:無作用// 參數3:回調句柄NTSTATUS CmRegisterCallback([in]PEX_CALLBACK_FUNCTION Function,[in, optional] PVOIDContext,[out]PLARGE_INTEGERCookie);自定義注冊表回調函數MyLySharkCallback需要保留三個參數,CallbackContext回調上下文,Argument1是操作類型,Argument2定義詳細結構體指針 。
NTSTATUS MyLySharkCallback(_In_ PVOID CallbackContext, _In_opt_ PVOID Argument1, _In_opt_ PVOID Argument2)在自定義回調函數內Argument1則可獲取到操作類型,類型是一個REG_NOTIFY_CLASS枚舉結構,微軟對其的具體定義如下所示 。
【驅動開發:內核監控Register注冊表回調】// 署名權// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.comtypedef enum _REG_NOTIFY_CLASS {RegNtDeleteKey,RegNtPreDeleteKey = RegNtDeleteKey,RegNtSetValueKey,RegNtPreSetValueKey = RegNtSetValueKey,RegNtDeleteValueKey,RegNtPreDeleteValueKey = RegNtDeleteValueKey,RegNtSetInformationKey,RegNtPreSetInformationKey = RegNtSetInformationKey,RegNtRenameKey,RegNtPreRenameKey = RegNtRenameKey,RegNtEnumerateKey,RegNtPreEnumerateKey = RegNtEnumerateKey,RegNtEnumerateValueKey,RegNtPreEnumerateValueKey = RegNtEnumerateValueKey,RegNtQueryKey,RegNtPreQueryKey = RegNtQueryKey,RegNtQueryValueKey,RegNtPreQueryValueKey = RegNtQueryValueKey,RegNtQueryMultipleValueKey,RegNtPreQueryMultipleValueKey = RegNtQueryMultipleValueKey,RegNtPreCreateKey,RegNtPostCreateKey,RegNtPreOpenKey,RegNtPostOpenKey,RegNtKeyHandleClose,RegNtPreKeyHandleClose = RegNtKeyHandleClose,//// .Net only//RegNtPostDeleteKey,RegNtPostSetValueKey,RegNtPostDeleteValueKey,RegNtPostSetInformationKey,RegNtPostRenameKey,RegNtPostEnumerateKey,RegNtPostEnumerateValueKey,RegNtPostQueryKey,RegNtPostQueryValueKey,RegNtPostQueryMultipleValueKey,RegNtPostKeyHandleClose,RegNtPreCreateKeyEx,RegNtPostCreateKeyEx,RegNtPreOpenKeyEx,RegNtPostOpenKeyEx,//// new to Windows Vista//RegNtPreFlushKey,RegNtPostFlushKey,RegNtPreLoadKey,RegNtPostLoadKey,RegNtPreUnLoadKey,RegNtPostUnLoadKey,RegNtPreQueryKeySecurity,RegNtPostQueryKeySecurity,RegNtPreSetKeySecurity,RegNtPostSetKeySecurity,//// per-object context cleanup//RegNtCallbackObjectContextCleanup,//// new in Vista SP2//RegNtPreRestoreKey,RegNtPostRestoreKey,RegNtPreSaveKey,RegNtPostSaveKey,RegNtPreReplaceKey,RegNtPostReplaceKey,MaxRegNtNotifyClass //should always be the last enum} REG_NOTIFY_CLASS;其中對于注冊表最常用的監控項為以下幾種類型,當然為了實現監控則我們必須要使用之前,如果使用之后則只能起到監視而無法做到監控的目的 。
  • RegNtPreCreateKey 創建注冊表之前
  • RegNtPreOpenKey打開注冊表之前
  • RegNtPreDeleteKey 刪除注冊表之前
  • RegNtPreDeleteValueKey 刪除鍵值之前
  • RegNtPreSetValueKey 修改注冊表之前
如果需要實現監視則,首先CmRegisterCallback注冊一個自定義回調,當有消息時則觸發MyLySharkCallback其內部獲取到lOperateType操作類型,并通過switch選擇不同的處理例程,每個處理例程都通過GetFullPath得到注冊表完整路徑,并打印出來,這段代碼實現如下 。
// 署名權// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.com#include <ntifs.h>#include <windef.h>// 未導出函數聲明 pEProcess -> PIDPUCHAR PsGetProcessImageFileName(PEPROCESS pEProcess);NTSTATUS ObQueryNameString( _In_ PVOID Object, _Out_writes_bytes_opt_(Length) POBJECT_NAME_INFORMATION ObjectNameInfo, _In_ ULONG Length, _Out_ PULONG ReturnLength );// 注冊表回調CookieLARGE_INTEGER g_liRegCookie;// 獲取注冊表完整路徑BOOLEAN GetFullPath(PUNICODE_STRING pRegistryPath, PVOID pRegistryObject){ // 判斷數據地址是否有效 if ((FALSE == MmIsAddressValid(pRegistryObject)) ||(NULL == pRegistryObject)) {return FALSE; } // 申請內存 ULONG ulSize = 512; PVOID lpObjectNameInfo = ExAllocatePool(NonPagedPool, ulSize); if (NULL == lpObjectNameInfo) {return FALSE; } // 獲取注冊表路徑 ULONG ulRetLen = 0; NTSTATUS status = ObQueryNameString(pRegistryObject, (POBJECT_NAME_INFORMATION)lpObjectNameInfo, ulSize, &ulRetLen); if (!NT_SUCCESS(status)) {ExFreePool(lpObjectNameInfo);return FALSE; } // 復制 RtlCopyUnicodeString(pRegistryPath, (PUNICODE_STRING)lpObjectNameInfo); // 釋放內存 ExFreePool(lpObjectNameInfo); return TRUE;}// 注冊表回調函數NTSTATUS MyLySharkCallback(_In_ PVOID CallbackContext, _In_opt_ PVOID Argument1, _In_opt_ PVOID Argument2){ NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING ustrRegPath; // 獲取操作類型 LONG lOperateType = (REG_NOTIFY_CLASS)Argument1; // 申請內存 ustrRegPath.Length = 0; ustrRegPath.MaximumLength = 1024 * sizeof(WCHAR); ustrRegPath.Buffer = ExAllocatePool(NonPagedPool, ustrRegPath.MaximumLength); if (NULL == ustrRegPath.Buffer) {return status; } RtlZeroMemory(ustrRegPath.Buffer, ustrRegPath.MaximumLength); // 判斷操作 switch (lOperateType) {// 創建注冊表之前 case RegNtPreCreateKey: {// 獲取注冊表路徑GetFullPath(&ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->RootObject);DbgPrint("[創建注冊表][%wZ][%wZ]\n", &ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName);break; } // 打開注冊表之前 case RegNtPreOpenKey: {// 獲取注冊表路徑GetFullPath(&ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->RootObject);DbgPrint("[打開注冊表][%wZ][%wZ]\n", &ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName);break; } // 刪除鍵之前 case RegNtPreDeleteKey: {// 獲取注冊表路徑GetFullPath(&ustrRegPath, ((PREG_DELETE_KEY_INFORMATION)Argument2)->Object);DbgPrint("[刪除鍵][%wZ] \n", &ustrRegPath);break; } // 刪除鍵值之前 case RegNtPreDeleteValueKey: {// 獲取注冊表路徑GetFullPath(&ustrRegPath, ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->Object);DbgPrint("[刪除鍵值][%wZ][%wZ] \n", &ustrRegPath, ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->ValueName);// 獲取當前進程, 即操作注冊表的進程PEPROCESS pEProcess = PsGetCurrentProcess();if (NULL != pEProcess){UCHAR *lpszProcessName = PsGetProcessImageFileName(pEProcess);if (NULL != lpszProcessName){DbgPrint("進程 [%s] 刪除了鍵值對 \n", lpszProcessName);}}break; } // 修改鍵值之前 case RegNtPreSetValueKey: {// 獲取注冊表路徑GetFullPath(&ustrRegPath, ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->Object);DbgPrint("[修改鍵值][%wZ][%wZ] \n", &ustrRegPath, ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->ValueName);break; } default:break; } // 釋放內存 if (NULL != ustrRegPath.Buffer) {ExFreePool(ustrRegPath.Buffer);ustrRegPath.Buffer = NULL; } return status;}VOID UnDriver(PDRIVER_OBJECT driver){ DbgPrint(("Uninstall Driver Is OK \n")); // 注銷當前注冊表回調 if (0 < g_liRegCookie.QuadPart) {CmUnRegisterCallback(g_liRegCookie); }}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath){ DbgPrint(("hello lyshark.com \n")); // 設置注冊表回調 NTSTATUS status = CmRegisterCallback(MyLySharkCallback, NULL, &g_liRegCookie); if (!NT_SUCCESS(status)) {g_liRegCookie.QuadPart = 0;return status; } Driver->DriverUnload = UnDriver; return STATUS_SUCCESS;}

推薦閱讀