模块是程序加载时被动态装载的,模块在装载后其存在于内存中同样存在一个内存基址,当我们需要操作这个模块时,通常第一步就是要得到该模块的内存基址,模块分为用户模块和内核模块,这里的用户模块指的是应用层进程运行后加载的模块,内核模块指的是内核中特定模块地址,本篇文章将实现一个获取驱动ntoskrnl.exe
的基地址以及长度,此功能是驱动开发中尤其是安全软件开发中必不可少的一个功能。
关于该程序的解释,官方的解析是这样的ntoskrnl.exe
是Windows
操作系统的一个重要内核程序,里面存储了大量的二进制内核代码,用于调度系统时使用,也是操作系统启动后第一个被加载的程序,通常该进程在任务管理器中显示为System
。
使用ARK工具也可看出其代表的是第一个驱动模块。
那么如何使用代码得到如上图中所展示的基地址
以及大小
呢,实现此功能我们需要调用ZwQuerySystemInformation
这个API函数,这与上一篇文章《判断自身是否加载成功》
所使用的NtQuerySystemInformation
只是开头部分不同,但其本质上是不同的,如下是一些参考资料;
调用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;
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; }
|
我们编译并运行上方代码,效果如下: