驅動開發:內核監視LoadImage映像回調
- 2022 年 10 月 25 日
- 筆記
- Windows 內核安全開發, Windows 內核驅動開發
在筆者上一篇文章《驅動開發:內核註冊並監控對象回調》
介紹了如何運用ObRegisterCallbacks
註冊進程與執行緒
回調,並通過該回調實現了攔截
指定進行運行的效果,本章LyShark
將帶大家繼續探索一個新的回調註冊函數,PsSetLoadImageNotifyRoutine
常用於註冊LoadImage
映像監視,當有模組被系統載入時則可以第一時間獲取到載入模組資訊,需要注意的是該回調函數內無法進行攔截,如需要攔截則需寫入返回指令這部分內容將在下一章進行講解,本章將主要實現對模組的監視功能。
監視模組載入與卸載需要費別使用兩個函數,這兩個函數的參數傳遞都是自己的回調地址。
- PsSetLoadImageNotifyRoutine 設置回調
- PsRemoveLoadImageNotifyRoutine 移除回調
此處MyLySharkLoadImageNotifyRoutine
回調地址必須有三個參數傳遞組成,其中FullImageName
代表完整路徑,ModuleStyle
代表模組類型,一般來說ModuleStyle=0
表示載入SYS驅動,如果ModuleStyle=1
則表示載入的是DLL
,最後一個參數ImageInfo
則是映像的詳細參數結構體。
VOID MyLySharkLoadImageNotifyRoutine(PUNICODE_STRING FullImageName, HANDLE ModuleStyle, PIMAGE_INFO ImageInfo)
那麼如何實現監視映像載入呢,來看如下完整程式碼片段,首先PsSetLoadImageNotifyRoutine
註冊回調,當有模組被載入則自動執行MyLySharkLoadImageNotifyRoutine
回調函數,其內部首先判斷ModuleStyle
得出是什麼類型的模組,然後再通過GetDriverEntryByImageBase
拿到當前進程詳細參數並列印輸出。
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntddk.h>
#include <ntimage.h>
// 未導出函數聲明
PUCHAR PsGetProcessImageFileName(PEPROCESS pEProcess);
// 獲取到鏡像裝載基地址
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;
}
// 獲取當前進程名
UCHAR* GetCurrentProcessName()
{
PEPROCESS pEProcess = PsGetCurrentProcess();
if (NULL != pEProcess)
{
UCHAR *lpszProcessName = PsGetProcessImageFileName(pEProcess);
if (NULL != lpszProcessName)
{
return lpszProcessName;
}
}
return NULL;
}
// 設置自己的回調函數
VOID MyLySharkLoadImageNotifyRoutine(PUNICODE_STRING FullImageName, HANDLE ModuleStyle, PIMAGE_INFO ImageInfo)
{
PVOID pDrvEntry;
// MmIsAddress 驗證地址可用性
if (FullImageName != NULL && MmIsAddressValid(FullImageName))
{
// ModuleStyle為零表示載入sys
if (ModuleStyle == 0)
{
// 得到裝載主進程名
UCHAR *load_name = GetCurrentProcessName();
pDrvEntry = GetDriverEntryByImageBase(ImageInfo->ImageBase);
DbgPrint("[LyShark SYS載入] 模組名稱:%wZ --> 裝載基址:%p --> 鏡像長度: %d --> 裝載主進程: %s \n", FullImageName, pDrvEntry, ImageInfo->ImageSize, load_name);
}
// ModuleStyle非零表示載入DLL
else
{
// 得到裝載主進程名
UCHAR *load_name = GetCurrentProcessName();
pDrvEntry = GetDriverEntryByImageBase(ImageInfo->ImageBase);
DbgPrint("[LyShark DLL載入] 模組名稱:%wZ --> 裝載基址:%p --> 鏡像長度: %d --> 裝載主進程: %s \n", FullImageName, pDrvEntry, ImageInfo->ImageSize, load_name);
}
}
}
VOID UnDriver(PDRIVER_OBJECT driver)
{
PsRemoveLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)MyLySharkLoadImageNotifyRoutine);
DbgPrint("[LyShark.com] 驅動卸載完成...");
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("hello lyshark.com \n");
PsSetLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)MyLySharkLoadImageNotifyRoutine);
DbgPrint("[LyShark.com] 驅動載入完成...");
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
運行這個驅動程式,則會輸出被載入的驅動詳細參數。