Win64 驅動核心程式設計-13.回撥監控模組載入
回撥監控模組載入
模組載入包括使用者層模組(.DLL)和核心模組(.SYS)的載入。傳統方法要監控這兩者加在必須 HOOK 好幾個函式,比如 NtCreateSection 和 NtLoadDriver
等,而且這些方法還不能監控未知的驅動載入方法。其實為了監控模組載入而HOOK API 是非常傻的,因為微軟已經提供了一對標準的 API 實現此功能。它們
分別是 PsSetLoadImageNotifyRoutine 和 PsRemoveLoadImageNotifyRoutine,可以設定/取消一個“映像載入通告例程”,當有驅動或者 DLL 被載入時,回撥函
數就會被呼叫。有人可能認為這個標準方法的監控非常表層,其實恰恰相反,這個方法非常底層,大部分隱祕的載入驅動的方法都可以繞過 NtLoadDriver
是無法繞過“映像載入通告例程”。所以用此方法監控驅動載入是最合適的了。
之前說過,這個通告例程不僅僅管載入驅動,連程序載入 DLL 也管,那我們怎麼判斷到底是載入驅動還是載入 DLL 呢?如果說根據字尾名判斷則很明顯是一個挫方法。我的方
法是, 根據回撥函式 e LoadImageNotifyRoutine 的第二個引數判斷,如果 D PID 是0 0 ,則表示載入驅動,如果 D PID 位非零,則 表示載入 DLL 。原因很簡單,我之前說過這個函式很底層,到了一定的深度之後就無法判斷到底是誰主動引發的行為了,一切都是系統的行為。當然,你也可以認為這是通過回撥來監控驅動載入的缺點。判斷了是驅動後,就通過
//新增: PsSetLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)LoadImageNotifyRoutine); //刪除: PsRemoveLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)LoadImageNotifyRoutine); 其中 NotifyRoutine 是一個函式指標,此回撥函式的原型是: VOID (*PLOAD_IMAGE_NOTIFY_ROUTINE) ( __in_opt PUNICODE_STRING FullImageName, __in HANDLE ProcessId, __in PIMAGE_INFO ImageInfo ); 下面是實現模組監控,並且拒絕Powertool的驅動載入的例子程式碼: #include <ntddk.h> #include <ntimage.h> #define dprintf DbgPrint BOOLEAN VxkCopyMemory( PVOID pDestination, PVOID pSourceAddress, SIZE_T SizeOfCopy ) { PMDL pMdl = NULL; PVOID pSafeAddress = NULL; pMdl = IoAllocateMdl( pSourceAddress, (ULONG)SizeOfCopy, FALSE, FALSE, NULL ); if( !pMdl ) return FALSE; __try { MmProbeAndLockPages( pMdl, KernelMode, IoReadAccess ); } __except(EXCEPTION_EXECUTE_HANDLER) { IoFreeMdl( pMdl ); return FALSE; } pSafeAddress = MmGetSystemAddressForMdlSafe( pMdl, NormalPagePriority ); if( !pSafeAddress ) return FALSE; RtlCopyMemory( pDestination, pSafeAddress, SizeOfCopy ); MmUnlockPages( pMdl ); IoFreeMdl( pMdl ); return TRUE; } VOID UnicodeToChar(PUNICODE_STRING dst, char *src) { ANSI_STRING string; RtlUnicodeStringToAnsiString(&string,dst, TRUE); strcpy(src,string.Buffer); RtlFreeAnsiString(&string); } void DenyLoadDriver(PVOID DriverEntry) { UCHAR fuck[]="\xB8\x22\x00\x00\xC0\xC3"; VxkCopyMemory(DriverEntry,fuck,sizeof(fuck)); } PVOID GetDriverEntryByImageBase(PVOID ImageBase) { PIMAGE_DOS_HEADER pDOSHeader; PIMAGE_NT_HEADERS64 pNTHeader; PVOID pEntryPoint; pDOSHeader = (PIMAGE_DOS_HEADER)ImageBase; pNTHeader = (PIMAGE_NT_HEADERS64)((ULONG64)ImageBase + pDOSHeader->e_lfanew); pEntryPoint = (PVOID)((ULONG64)ImageBase + pNTHeader->OptionalHeader.AddressOfEntryPoint); return pEntryPoint; } VOID LoadImageNotifyRoutine ( __in_opt PUNICODE_STRING FullImageName, __in HANDLE ProcessId, __in PIMAGE_INFO ImageInfo ) { PVOID pDrvEntry; char szFullImageName[260]={0}; if(FullImageName!=NULL && MmIsAddressValid(FullImageName)) { if(ProcessId==0) { DbgPrint("[MyDriver]%wZ\n", FullImageName); pDrvEntry=GetDriverEntryByImageBase(ImageInfo->ImageBase); DbgPrint("[MyDriver]DriverEntry: %p\n",pDrvEntry); UnicodeToChar(FullImageName,szFullImageName); if(strstr(_strlwr(szFullImageName),"kevp64.sys")) { DbgPrint("[MyDriver]Deny load [WIN64AST.SYS]"); //禁止載入win64ast.sys DenyLoadDriver(pDrvEntry); } } } } 載入: PsSetLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)LoadImageNotifyRoutine); 登出: PsRemoveLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)LoadImageNotifyRoutine);
執行結果,通過Pchunter看監控當前驅動資訊,PowerTool驅動被拒絕載入之後不但自己沒有提示,而且還在桌面上留下了自己的驅動檔案,這相當於是你雙擊了一個exe,結果在exe入口函式的地方記憶體程式設計不可操作了,這種很難檢測出問題來:
禁止載入驅動的方式也可以用來禁止載入dll。