驅動開發:核心取ntoskrnl模組基地址

2022-10-09 12:04:15

模組是程式載入時被動態裝載的,模組在裝載後其存在於記憶體中同樣存在一個記憶體基址,當我們需要操作這個模組時,通常第一步就是要得到該模組的記憶體基址,模組分為使用者模組和核心模組,這裡的使用者模組指的是應用層程序執行後載入的模組,核心模組指的是核心中特定模組地址,本篇文章將實現一個獲取驅動ntoskrnl.exe的基地址以及長度,此功能是驅動開發中尤其是安全軟體開發中必不可少的一個功能。

關於該程式的解釋,官方的解析是這樣的ntoskrnl.exeWindows作業系統的一個重要核心程式,裡面儲存了大量的二進位制核心程式碼,用於排程系統時使用,也是作業系統啟動後第一個被載入的程式,通常該程序在工作管理員中顯示為System

使用ARK工具也可看出其代表的是第一個驅動模組。

那麼如何使用程式碼得到如上圖中所展示的基地址以及大小呢,實現此功能我們需要呼叫ZwQuerySystemInformation這個API函數,這與上一篇文章《驅動開發:判斷自身是否載入成功》所使用的NtQuerySystemInformation只是開頭部分不同,但其本質上是不同的,如下是一些參考資料;

  • 從核心模式呼叫NtZw系列API,其最終都會連線到nooskrnl.lib匯出庫:

    • Nt系列API將直接呼叫對應的函數程式碼,而Zw系列API則通過呼叫KiSystemService最終跳轉到對應的函數程式碼。
    • 重要的是兩種不同的呼叫對核心中previous mode的改變,如果是從使用者模式呼叫Native APIprevious mode是使用者態,如果從核心模式呼叫Native APIprevious 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;
}

我們編譯並執行上方程式碼,效果如下:


參考文獻:

https://blog.csdn.net/u012410612/article/details/17096597