欢迎光临
我们一直在努力

通过cr3读写进程内存

通过cr3读写进程内存

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

技术原理

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

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

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

获取目标进程EPROCESS

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

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寄存器的内容,也就是开始切换页表:

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

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

示例代码

#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;
}

赞(0)
未经允许不得转载:171主机测评 » 通过cr3读写进程内存
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址