驱动开发:内核取应用层模块基地址

举报
微软技术分享 发表于 2022/11/01 13:26:16 2022/11/01
【摘要】 在上一篇文章`《驱动开发:内核取ntoskrnl模块基地址》`中我们通过调用内核API函数获取到了内核进程`ntoskrnl.exe`的基址,当在某些场景中,我们不仅需要得到内核的基地址,也需要得到特定进程内某个模块的基地址,显然上篇文章中的方法是做不到的,本篇文章将实现内核层读取32位应用层中特定进程模块基址功能。

在上一篇文章《驱动开发:内核取ntoskrnl模块基地址》中我们通过调用内核API函数获取到了内核进程ntoskrnl.exe的基址,当在某些场景中,我们不仅需要得到内核的基地址,也需要得到特定进程内某个模块的基地址,显然上篇文章中的方法是做不到的,本篇文章将实现内核层读取32位应用层中特定进程模块基址功能。

上一篇文章中的PPEB32,PLIST_ENTRY32等结构体定义依然需要保留,此处只保留核心代码,定义部分请看前一篇文章,自定义读取模块基址核心代码如下,调用GetModuleBaseWow64()用户需传入进程的PROCESS结构该结构可通过内核函数PsLookupProcessByProcessId获取到。

对于函数内部执行过程如下:

  • 1.根据传入的EProcess结构调用KeStackAttachProcess附加到该进程内。
  • 2.调用内核函数PsGetProcessWow64Process此函数可得到该进程空间内PEB结构数据。
  • 3.通过for循环遍历整个pPeb->Ldr链表,并在遍历过程中通过RtlEqualUnicodeString判断是否是我们需要的模块。
  • 4.如果判断是我们需要取出的模块名,则将LdrEntry->DllBase取出,此处取出的基地址也即是我们所需要的。
  • 5.比较结束后,通过调用KeUnstackDetachProcess这个内核模块脱离进程空间。
// By: LyShark
ULONGLONG GetModuleBaseWow64(_In_ PEPROCESS pEProcess, _In_ UNICODE_STRING usModuleName)
{
	ULONGLONG BaseAddr = 0;
	KAPC_STATE KAPC = { 0 };
	KeStackAttachProcess(pEProcess, &KAPC);
	PPEB32 pPeb = (PPEB32)PsGetProcessWow64Process(pEProcess);
	if (pPeb == NULL || pPeb->Ldr == 0)
	{
		KeUnstackDetachProcess(&KAPC);
		return 0;
	}

	for (PLIST_ENTRY32 pListEntry = (PLIST_ENTRY32)((PPEB_LDR_DATA32)pPeb->Ldr)->InLoadOrderModuleList.Flink;
		pListEntry != &((PPEB_LDR_DATA32)pPeb->Ldr)->InLoadOrderModuleList; pListEntry = (PLIST_ENTRY32)pListEntry->Flink)
	{
		PLDR_DATA_TABLE_ENTRY32 LdrEntry = CONTAINING_RECORD(pListEntry, LDR_DATA_TABLE_ENTRY32, InLoadOrderLinks);

		if (LdrEntry->BaseDllName.Buffer == NULL)
		{
			continue;
		}

		// 当前模块名链表
		UNICODE_STRING usCurrentName = { 0 };
		RtlInitUnicodeString(&usCurrentName, (PWCHAR)LdrEntry->BaseDllName.Buffer);

		// 比较模块名是否一致
		if (RtlEqualUnicodeString(&usModuleName, &usCurrentName, TRUE))
		{
			BaseAddr = (ULONGLONG)LdrEntry->DllBase;
			KeUnstackDetachProcess(&KAPC);
			return BaseAddr;
		}
	}
	KeUnstackDetachProcess(&KAPC);
	return 0;
}

如上就是如何得到特定模块基址的方法,如下是入口函数的调用方法,首先通过传入6164这个PID号,得到进程EProcess结构,其次使用RtlInitUnicodeString(&unicode, wchar_string)初始化得到kernel32.dll字符串,最终调用GetModuleBaseWow64函数获取到进程6164kernel32.dll的模块基地址信息。

VOID UnDriver(PDRIVER_OBJECT driver)
{
	DbgPrint("驱动卸载成功 \n");
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint(("hello lyshark \n"));

	PEPROCESS pEProcess;
	HANDLE PID = (HANDLE)6164;

	// 初始化字符串
	UNICODE_STRING unicode;
	wchar_t *wchar_string = L"kernel32.dll";
	RtlInitUnicodeString(&unicode, wchar_string);

	// 取模块句柄
	PsLookupProcessByProcessId((HANDLE)PID, &pEProcess);
	ULONGLONG base32 = GetModuleBaseWow64(pEProcess, unicode);

	DbgPrint("ModuleBaseAddress: 0x%X \n", base32);


	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

这段代码输出效果如下所示:

image.png

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。