通过cr3读写进程内存

通过cr3读写进程内存

本文总结于lyshark的《Windows内核安全编程技术实践》。

技术原理

cr3保存着当前进程的最高级页目录地址(物理地址),修改cr3为目标进程的最高级页目录地址,即可读写目标进程的内存空间。

目标进程的最高级页目录地址存放于KPEOCESS结构体偏移0x28处,同时也是EPROCESS结构体0x28处。

我们通过PsLookupProcessByProcessId和PsGetProcessImageFileName函数获取获取目标进程的EPROCESS地址。之后加上0x28便得到最高级页目录地址。

获取目标进程EPROCESS

我们希望通过目标进程的可执行文件名称获取EPROCESS结构体:

cpp 复制代码
static PEPROCESS sg_pEprocess = nullptr;

EXTERN_C NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId,
	PEPROCESS* Process);

EXTERN_C NTKERNELAPI CHAR* PsGetProcessImageFileName(PEPROCESS Process);


// 通过进程名获取EPROCESS结构
NTSTATUS GetProcessObjectByName(char* name)
{
	NTSTATUS status = STATUS_UNSUCCESSFUL;
	SIZE_T i;

	__try
	{
		for (i = 0; i < 20000; i++)
		{
			PEPROCESS ep;
			NTSTATUS st = PsLookupProcessByProcessId((HANDLE)i, &ep);
			if (NT_SUCCESS(st))
			{
				char* pn = PsGetProcessImageFileName(ep);

				// 带下划线版本的是微软的标准扩展实现,应该优先使用
				if (_stricmp(pn, name) == 0)
				{
					DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
						"Found %s\n", name);
					sg_pEprocess = ep;
					__leave;
				}
			}
		}

		DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
			"Not Found %s\n", name);
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		return status;
	}

	return STATUS_SUCCESS;
}

读写目标进程的内存

现在有了EPROCESS,我们开着手修改cr3寄存器的内容,也就是开始切换页表:

cpp 复制代码
	// 切换页表
	_disable();
	UINT64 cr3 = __readcr3();
	__writecr3(pDTB);
	_enable();

这里使用_disable()来关闭中断,CR3寄存器控制当前进程的页表基址,切换CR3会立即改变地址空间映射。如果在切换过程中被中断打断,中断处理程序可能在不正确的地址空间中执行,导致数据访问错误或系统崩溃。

示例代码

cpp 复制代码
#include <ntddk.h>
#include <windef.h>
#include <intrin.h>

// 页表偏移
#define DIRECTORY_TABLE_BASE 0x028

// _disable直接生成cli指令,需要在内核模式中使用
#pragma intrinsic(_disable) // 将_disable声明为内联函数
#pragma intrinsic(_enable)

EXTERN_C NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId,
	PEPROCESS* Process);

EXTERN_C NTKERNELAPI CHAR* PsGetProcessImageFileName(PEPROCESS Process);

static PEPROCESS sg_pEprocess = nullptr;

// 通过进程名获取EPROCESS结构
NTSTATUS GetProcessObjectByName(char* name)
{
	NTSTATUS status = STATUS_UNSUCCESSFUL;
	SIZE_T i;

	__try
	{
		for (i = 0; i < 20000; i++)
		{
			PEPROCESS ep;
			NTSTATUS st = PsLookupProcessByProcessId((HANDLE)i, &ep);
			if (NT_SUCCESS(st))
			{
				char* pn = PsGetProcessImageFileName(ep);

				// 带下划线版本的是微软的标准扩展实现,应该优先使用
				if (_stricmp(pn, name) == 0)
				{
					DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
						"Found %s\n", name);
					sg_pEprocess = ep;
					__leave;
				}
			}
		}

		DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
			"Not Found %s\n", name);
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		return status;
	}

	return STATUS_SUCCESS;
}

VOID DriverUnload(PDRIVER_OBJECT pDriverObj)
{
	UNREFERENCED_PARAMETER(pDriverObj);
	DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Uninstall Driver Successfully\n");
}

ULONG64 CheckAddressVal(PVOID p)
{
	if (MmIsAddressValid(p) == FALSE)
	{
		return 0;
	}

	// 返回p指向的地址所存的内存
	return *(PULONG64)p;
}

BOOLEAN CR3_ReadProcessMemory(PEPROCESS ep, PVOID addr, UINT32 length, PVOID buf)
{
	ULONG64 pDTB = CheckAddressVal((UCHAR*)ep + DIRECTORY_TABLE_BASE);
	if (pDTB == 0)
	{
		return false;
	}

	// 切换页表
	_disable();
	UINT64 cr3 = __readcr3();
	__writecr3(pDTB);
	_enable();

	if (MmIsAddressValid(addr))
	{
		RtlCopyMemory(buf, addr, length);
		DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
			"read data: %ld\n", *(PDWORD)buf);
		
		return true;
	}

	_disable();
	__writecr3(cr3);
	_enable();
	return FALSE;
}

EXTERN_C NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegPath)
{
	UNREFERENCED_PARAMETER(pRegPath);
	DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "AccessMem Hello\n");

	pDriverObj->DriverUnload = DriverUnload;

	NTSTATUS status = GetProcessObjectByName("x64dbg.exe");
	if (NT_SUCCESS(status))
	{
		DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
			"[+] eprocess = %p\n", sg_pEprocess);
	}

	DWORD buf = 0;
	PVOID addr = (PVOID)0x0000000009edc800;
	BOOLEAN bl = CR3_ReadProcessMemory(sg_pEprocess, addr, 4, &buf);
	UNREFERENCED_PARAMETER(bl);

	DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
		"read process x64 address %p: %x, %d\n", addr, buf, buf);

	return STATUS_SUCCESS;
}
相关推荐
阿昭L5 天前
记一次符号类型不一致导致的链接错误
windows内核
阿昭L13 天前
Windows对象句柄回调 (Object Handle Callbacks)
windows内核
阿昭L2 个月前
Windows内核事件通知机制
windows·windows内核
阿昭L2 个月前
通过KiSystemServiceUser获取SSDT基址
逆向工程·windows内核·ssdt
阿昭L2 个月前
Windows键盘过滤
windows·驱动开发·windows内核·过滤驱动
阿昭L2 个月前
调试CreateProcess
windows·进程·逆向工程·windows内核
阿昭L3 个月前
调试Windows11启动过程
windows·uefi·windows内核
LittleFishC4 个月前
08_长调用与短调用
c语言·汇编·逆向·windows内核
阿昭L4 个月前
NT驱动程序和WDM驱动程序
驱动开发·windows内核