设为首页 加入收藏

TOP

驱动开发:内核R3与R0内存映射拷贝(一)
2023-07-23 13:31:50 】 浏览:80
Tags:内核

在上一篇博文《驱动开发:内核通过PEB得到进程参数》中我们通过使用KeStackAttachProcess附加进程的方式得到了该进程的PEB结构信息,本篇文章同样需要使用进程附加功能,但这次我们将实现一个更加有趣的功能,在某些情况下应用层与内核层需要共享一片内存区域通过这片区域可打通内核与应用层的隔离,此类功能的实现依附于MDL内存映射机制实现。

应用层(R3)数据映射到内核层(R0)

先来实现将R3内存数据拷贝到R0中,功能实现所调用的API如下:

  • IoAllocateMdl 该函数用于创建MDL(类似初始化)
  • MmProbeAndLockPages 用于锁定创建的地址其中UserMode代表用户层,IoReadAccess以读取的方式锁定
  • MmGetSystemAddressForMdlSafe 用于从MDL中得到映射内存地址
  • RtlCopyMemory 用于内存拷贝,将DstAddr应用层中的数据拷贝到pMappedSrc
  • MmUnlockPages 拷贝结束后解锁pSrcMdl
  • IoFreeMdl 释放MDL

内存拷贝SafeCopyMemory_R3_to_R0函数封装代码如下:

#include <ntifs.h>
#include <windef.h>

// 分配内存
void* RtlAllocateMemory(BOOLEAN InZeroMemory, SIZE_T InSize)
{
	void* Result = ExAllocatePoolWithTag(NonPagedPool, InSize, 'lysh');
	if (InZeroMemory && (Result != NULL))
		RtlZeroMemory(Result, InSize);
	return Result;
}

// 释放内存
void RtlFreeMemory(void* InPointer)
{
	ExFreePool(InPointer);
}

/*
将应用层中的内存复制到内核变量中

SrcAddr  r3地址要复制
DstAddr  R0申请的地址
Size     拷贝长度
*/
NTSTATUS SafeCopyMemory_R3_to_R0(ULONG_PTR SrcAddr, ULONG_PTR DstAddr, ULONG Size)
{
	NTSTATUS status = STATUS_UNSUCCESSFUL;
	ULONG nRemainSize = PAGE_SIZE - (SrcAddr & 0xFFF);
	ULONG nCopyedSize = 0;

	if (!SrcAddr || !DstAddr || !Size)
	{
		return status;
	}

	while (nCopyedSize < Size)
	{
		PMDL pSrcMdl = NULL;
		PVOID pMappedSrc = NULL;

		if (Size - nCopyedSize < nRemainSize)
		{
			nRemainSize = Size - nCopyedSize;
		}

		// 创建MDL
		pSrcMdl = IoAllocateMdl((PVOID)(SrcAddr & 0xFFFFFFFFFFFFF000), PAGE_SIZE, FALSE, FALSE, NULL);
		if (pSrcMdl)
		{
			__try
			{
				// 锁定内存页面(UserMode代表应用层)
				MmProbeAndLockPages(pSrcMdl, UserMode, IoReadAccess);

				// 从MDL中得到映射内存地址
				pMappedSrc = MmGetSystemAddressForMdlSafe(pSrcMdl, NormalPagePriority);
			}
			__except (EXCEPTION_EXECUTE_HANDLER)
			{
			}
		}

		if (pMappedSrc)
		{
			__try
			{
				// 将MDL中的映射拷贝到pMappedSrc内存中
				RtlCopyMemory((PVOID)DstAddr, (PVOID)((ULONG_PTR)pMappedSrc + (SrcAddr & 0xFFF)), nRemainSize);
			}
			__except (1)
			{
				// 拷贝内存异常
			}

			// 释放锁
			MmUnlockPages(pSrcMdl);
		}

		if (pSrcMdl)
		{
			// 释放MDL
			IoFreeMdl(pSrcMdl);
		}

		if (nCopyedSize)
		{
			nRemainSize = PAGE_SIZE;
		}

		nCopyedSize += nRemainSize;
		SrcAddr += nRemainSize;
		DstAddr += nRemainSize;
	}

	status = STATUS_SUCCESS;
	return status;
}

调用该函数实现拷贝,如下代码中首先PsLookupProcessByProcessId得到进程EProcess结构,并KeStackAttachProcess附加进程,声明pTempBuffer指针用于存储RtlAllocateMemory开辟的内存空间,nSize则代表读取应用层进程数据长度,ModuleBase则是读入进程基址,调用SafeCopyMemory_R3_to_R0即可将应用层数据拷贝到内核空间,并最终BYTE* data转为BYTE字节的方式输出。

VOID UnDriver(PDRIVER_OBJECT driver)
{
	DbgPrint(("Uninstall Driver Is OK \n"));
}

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

	NTSTATUS status = STATUS_UNSUCCESSFUL;
	PEPROCESS eproc = NULL;
	KAPC_STATE kpc = { 0 };

	__try
	{
		// HANDLE 进程PID
		status = PsLookupProcessByProcessId((HANDLE)4556, &eproc);

		if (NT_SUCCESS(status))
		{
			// 附加进程
			KeStackAttachProcess(eproc, &kpc);

			// -------------------------------------------------------------------
			// 开始映射
			// ---------------------------------------------------
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇驱动开发:内核中实现Dump进程转储 下一篇C++ 右值引用与一级指针

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目