1. 程式人生 > >Win64 驅動核心程式設計-13.回撥監控模組載入

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 。原因很簡單,我之前說過這個函式很底層,到了一定的深度之後就無法判斷到底是誰主動引發的行為了,一切都是系統的行為。當然,你也可以認為這是通過回撥來監控驅動載入的缺點。判斷了是驅動後,就通過 

ImageInfo->ImageBase 來獲取驅動的映像基址。過 如果不想讓這個驅動載入,就通過 e ImageBase 得 來獲得 y DriverEntry  的地址(ImageBase 就是 DOS 頭,根據 DOS 頭找到 NT 頭,然後在 NT 頭的 OptionalHeader裡就能找到入口點了。入口點的資料就是 DriverEntry 的地址) , 並 寫入 “拒絕訪問 ” 的機器碼 即可。

//新增:
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