通过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;
}
相关推荐
阿昭L4 天前
调试CreateProcess
windows·进程·逆向工程·windows内核
阿昭L1 个月前
调试Windows11启动过程
windows·uefi·windows内核
LittleFishC2 个月前
08_长调用与短调用
c语言·汇编·逆向·windows内核
阿昭L2 个月前
NT驱动程序和WDM驱动程序
驱动开发·windows内核
阿昭L2 个月前
Windows内核驱动重要知识
windows·windows内核
阿昭L2 个月前
Windows驱动重要数据结构
windows内核
脸红ฅฅ*的思春期3 个月前
Windows内核攻防—利用RTCore64驱动绕过Windows签名校验
windows·windows内核·dse绕过·rtcore64
永不复还6 个月前
Windows APC注入解析
windows·windows内核·远程注入
脸红ฅฅ*的思春期6 个月前
免杀对抗—WinDbg查看Windows内存
windows·windbg·windows内核·windows内存查看