模組是程式載入時被動態裝載的,模組在裝載後其存在於記憶體中同樣存在一個記憶體基址,當我們需要操作這個模組時,通常第一步就是要得到該模組的記憶體基址,模組分為使用者模組和核心模組,這裡的使用者模組指的是應用層程序執行後載入的模組,核心模組指的是核心中特定模組地址,本篇文章將實現一個獲取驅動ntoskrnl.exe
的基地址以及長度,此功能是驅動開發中尤其是安全軟體開發中必不可少的一個功能。
關於該程式的解釋,官方的解析是這樣的ntoskrnl.exe
是Windows
作業系統的一個重要核心程式,裡面儲存了大量的二進位制核心程式碼,用於排程系統時使用,也是作業系統啟動後第一個被載入的程式,通常該程序在工作管理員中顯示為System
。
使用ARK工具也可看出其代表的是第一個驅動模組。
那麼如何使用程式碼得到如上圖中所展示的基地址
以及大小
呢,實現此功能我們需要呼叫ZwQuerySystemInformation
這個API函數,這與上一篇文章《驅動開發:判斷自身是否載入成功》
所使用的NtQuerySystemInformation
只是開頭部分不同,但其本質上是不同的,如下是一些參考資料;
從核心模式呼叫Nt
和Zw
系列API,其最終都會連線到nooskrnl.lib
匯出庫:
KiSystemService
最終跳轉到對應的函數程式碼。previous mode
的改變,如果是從使用者模式呼叫Native API
則previous mode
是使用者態,如果從核心模式呼叫Native API
則previous mode
是核心態。previous
為使用者態時Native API
將對傳遞的引數進行嚴格的檢查,而為核心態時則不會檢查。呼叫Nt API
時不會改變previous mode
的狀態,呼叫Zw API
時會將previous mode
改為核心態,因此在進行Kernel Mode Driver
開發時可以使用Zw
系列API可以避免額外的參數列檢查,提高效率。Zw*
會設定KernelMode
已避免檢查,Nt*
不會自動設定,如果是KernelMode
當然沒問題,如果就UserMode
就掛了。
回到程式碼上來,下方程式碼就是獲取ntoskrnl.exe
基地址以及長度的具體實現,核心程式碼就是呼叫ZwQuerySystemInformation
得到SystemModuleInformation
,裡面的對比部分是在比較當前獲取的地址是否超出了ntoskrnl
的最大和最小範圍。
#include <ntifs.h>
static PVOID g_KernelBase = 0;
static ULONG g_KernelSize = 0;
#pragma pack(4)
typedef struct _PEB32
{
UCHAR InheritedAddressSpace;
UCHAR ReadImageFileExecOptions;
UCHAR BeingDebugged;
UCHAR BitField;
ULONG Mutant;
ULONG ImageBaseAddress;
ULONG Ldr;
ULONG ProcessParameters;
ULONG SubSystemData;
ULONG ProcessHeap;
ULONG FastPebLock;
ULONG AtlThunkSListPtr;
ULONG IFEOKey;
ULONG CrossProcessFlags;
ULONG UserSharedInfoPtr;
ULONG SystemReserved;
ULONG AtlThunkSListPtr32;
ULONG ApiSetMap;
} PEB32, *PPEB32;
typedef struct _PEB_LDR_DATA32
{
ULONG Length;
UCHAR Initialized;
ULONG SsHandle;
LIST_ENTRY32 InLoadOrderModuleList;
LIST_ENTRY32 InMemoryOrderModuleList;
LIST_ENTRY32 InInitializationOrderModuleList;
} PEB_LDR_DATA32, *PPEB_LDR_DATA32;
typedef struct _LDR_DATA_TABLE_ENTRY32
{
LIST_ENTRY32 InLoadOrderLinks;
LIST_ENTRY32 InMemoryOrderLinks;
LIST_ENTRY32 InInitializationOrderLinks;
ULONG DllBase;
ULONG EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING32 FullDllName;
UNICODE_STRING32 BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
LIST_ENTRY32 HashLinks;
ULONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY32, *PLDR_DATA_TABLE_ENTRY32;
#pragma pack()
typedef struct _RTL_PROCESS_MODULE_INFORMATION
{
HANDLE Section;
PVOID MappedBase;
PVOID ImageBase;
ULONG ImageSize;
ULONG Flags;
USHORT LoadOrderIndex;
USHORT InitOrderIndex;
USHORT LoadCount;
USHORT OffsetToFileName;
UCHAR FullPathName[256];
} RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION;
typedef struct _RTL_PROCESS_MODULES
{
ULONG NumberOfModules;
RTL_PROCESS_MODULE_INFORMATION Modules[1];
} RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES;
typedef enum _SYSTEM_INFORMATION_CLASS
{
SystemModuleInformation = 0xb,
} SYSTEM_INFORMATION_CLASS;
// 取出KernelBase基地址
// By: lyshark.com
PVOID UtilKernelBase(OUT PULONG pSize)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG bytes = 0;
PRTL_PROCESS_MODULES pMods = 0;
PVOID checkPtr = 0;
UNICODE_STRING routineName;
if (g_KernelBase != 0)
{
if (pSize)
*pSize = g_KernelSize;
return g_KernelBase;
}
RtlInitUnicodeString(&routineName, L"NtOpenFile");
checkPtr = MmGetSystemRoutineAddress(&routineName);
if (checkPtr == 0)
return 0;
__try
{
status = ZwQuerySystemInformation(SystemModuleInformation, 0, bytes, &bytes);
if (bytes == 0)
{
DbgPrint("Invalid SystemModuleInformation size\n");
return 0;
}
pMods = (PRTL_PROCESS_MODULES)ExAllocatePoolWithTag(NonPagedPoolNx, bytes, "lyshark");
RtlZeroMemory(pMods, bytes);
status = ZwQuerySystemInformation(SystemModuleInformation, pMods, bytes, &bytes);
if (NT_SUCCESS(status))
{
PRTL_PROCESS_MODULE_INFORMATION pMod = pMods->Modules;
for (ULONG i = 0; i < pMods->NumberOfModules; i++)
{
if (checkPtr >= pMod[i].ImageBase &&
checkPtr < (PVOID)((PUCHAR)pMod[i].ImageBase + pMod[i].ImageSize))
{
g_KernelBase = pMod[i].ImageBase;
g_KernelSize = pMod[i].ImageSize;
if (pSize)
*pSize = g_KernelSize;
break;
}
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return 0;
}
if (pMods)
ExFreePoolWithTag(pMods, "lyshark");
return g_KernelBase;
}
VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint(("Uninstall Driver Is OK \n"));
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint(("hello lyshark \n"));
PULONG ulong = 0;
UtilKernelBase(ulong);
DbgPrint("ntoskrnl.exe 模組基址: 0x%p \n", g_KernelBase);
DbgPrint("模組大小: 0x%p \n", g_KernelSize);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
我們編譯並執行上方程式碼,效果如下:
參考文獻: