模塊是程式加載時被動態裝載的,模塊在裝載后其存在于記憶體中同樣存在一個記憶體基址,當我們需要操作這個模塊時,通常第一步就是要得到該模塊的記憶體基址,模塊分為用戶模塊和內核模塊,這里的用戶模塊指的是應用層行程運行后加載的模塊,內核模塊指的是內核中特定模塊地址,本篇文章將實作一個獲取驅動ntoskrnl.exe的基地址以及長度,此功能是驅動開發中尤其是安全軟體開發中必不可少的一個功能,
關于該程式的解釋,官方的決議是這樣的ntoskrnl.exe是Windows作業系統的一個重要內核程式,里面存盤了大量的二進制內核代碼,用于調度系統時使用,也是作業系統啟動后第一個被加載的程式,通常該行程在任務管理器中顯示為System,
使用ARK工具也可看出其代表的是第一個驅動模塊,

那么如何使用代碼得到如上圖中所展示的基地址以及大小呢,實作此功能我們需要呼叫ZwQuerySystemInformation這個API函式,這與上一篇文章《驅動開發:判斷自身是否加載成功》所使用的NtQuerySystemInformation只是開頭部分不同,但其本質上是不同的,如下是一些參考資料;
-
從內核模式呼叫
Nt和Zw系列API,其最終都會連接到nooskrnl.lib匯出庫:- Nt系列API將直接呼叫對應的函式代碼,而Zw系列API則通過呼叫
KiSystemService最終跳轉到對應的函式代碼, - 重要的是兩種不同的呼叫對內核中
previous mode的改變,如果是從用戶模式呼叫Native API則previous mode是用戶態,如果從內核模式呼叫Native API則previous mode是內核態, - 如果
previous為用戶態時Native API將對傳遞的引數進行嚴格的檢查,而為內核態時則不會檢查,
- Nt系列API將直接呼叫對應的函式代碼,而Zw系列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;
}
我們編譯并運行上方代碼,效果如下:

參考文獻:
https://blog.csdn.net/u012410612/article/details/17096597
文章作者:lyshark (王瑞)文章出處:https://www.cnblogs.com/LyShark/p/16770955.html
著作權宣告:本博客文章與代碼均為學習時整理的筆記,文章 [均為原創] 作品,轉載請 [添加出處] ,您添加出處是我創作的動力!
轉載文章請遵守《中華人民共和國著作權法》相關規定或遵守《署名CC BY-ND 4.0國際》禁止演繹規范,合理合規攜帶原創出處轉載,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/511880.html
標籤:C++
下一篇:C++ 如何讀取亂碼檔案內容?
