首先CR3是什么,CR3是一个寄存器,该寄存器内保存有页目录表物理地址(PDBR地址),其实CR3内部存放的就是页目录表的内存基地址,运用CR3切换可实现对特定进程内存地址的强制读写操作,此类读写属于有痕读写,多数驱动保护都会将这个地址改为无效,此时CR3读写就失效了,当然如果能找到CR3的正确地址,此方式也是靠谱的一种读写机制。

在读写进程之前需要先找到进程的PEPROCESS结构,查找结构的方法也很简单,依次遍历进程并对比进程名称即可得到。

#include <ntifs.h>
#include <windef.h>
#include <intrin.h> NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);
NTKERNELAPI CHAR* PsGetProcessImageFileName(PEPROCESS Process); // 定义全局EProcess结构
PEPROCESS Global_Peprocess = NULL; // 根据进程名获得EPROCESS结构
NTSTATUS GetProcessObjectByName(char *name)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
SIZE_T i; __try
{
for (i = 100; i<20000; i += 4)
{
NTSTATUS st;
PEPROCESS ep;
st = PsLookupProcessByProcessId((HANDLE)i, &ep);
if (NT_SUCCESS(st))
{
char *pn = PsGetProcessImageFileName(ep);
if (_stricmp(pn, name) == 0)
{
Global_Peprocess = ep;
}
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return Status;
}
return Status;
} VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint(("Uninstall Driver Is OK \n"));
} NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("hello lyshark \n"); NTSTATUS nt = GetProcessObjectByName("Tutorial-i386.exe"); if (NT_SUCCESS(nt))
{
DbgPrint("[+] eprocess = %x \n", Global_Peprocess);
} Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

以打开Tutorial-i386.exe为例,打开后即可返回他的Proces,当然也可以直接传入进程PID同样可以得到进程Process结构地址。

// 根据PID打开进程
PEPROCESS Peprocess = NULL;
DWORD PID = 6672;
NTSTATUS nt = PsLookupProcessByProcessId((HANDLE)PID, &Peprocess);

通过CR3读取内存实现代码如下,我们读取Tutorial-i386.exe里面的0x0009EDC8这段内存,读出长度是4字节,代码如下。

#include <ntifs.h>
#include <windef.h>
#include <intrin.h> #define DIRECTORY_TABLE_BASE 0x028 #pragma intrinsic(_disable)
#pragma intrinsic(_enable) NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);
NTKERNELAPI CHAR* PsGetProcessImageFileName(PEPROCESS Process); // 关闭写保护
KIRQL Open()
{
KIRQL irql = KeRaiseIrqlToDpcLevel();
UINT64 cr0 = __readcr0();
cr0 &= 0xfffffffffffeffff;
__writecr0(cr0);
_disable();
return irql;
} // 开启写保护
void Close(KIRQL irql)
{
UINT64 cr0 = __readcr0();
cr0 |= 0x10000;
_enable();
__writecr0(cr0);
KeLowerIrql(irql);
} // 检查内存
ULONG64 CheckAddressVal(PVOID p)
{
if (MmIsAddressValid(p) == FALSE)
return 0;
return *(PULONG64)p;
} // CR3 寄存器读内存
BOOLEAN CR3_ReadProcessMemory(IN PEPROCESS Process, IN PVOID Address, IN UINT32 Length, OUT PVOID Buffer)
{
ULONG64 pDTB = 0, OldCr3 = 0, vAddr = 0;
pDTB = CheckAddressVal((UCHAR*)Process + DIRECTORY_TABLE_BASE);
if (pDTB == 0)
{
return FALSE;
} _disable();
OldCr3 = __readcr3();
__writecr3(pDTB);
_enable(); if (MmIsAddressValid(Address))
{
RtlCopyMemory(Buffer, Address, Length);
DbgPrint("读入数据: %ld", *(PDWORD)Buffer);
return TRUE;
} _disable();
__writecr3(OldCr3);
_enable();
return FALSE;
} VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint(("Uninstall Driver Is OK \n"));
} NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("hello lyshark \n"); // 根据PID打开进程
PEPROCESS Peprocess = NULL;
DWORD PID = 6672;
NTSTATUS nt = PsLookupProcessByProcessId((HANDLE)PID, &Peprocess); DWORD buffer = 0; BOOLEAN bl = CR3_ReadProcessMemory(Peprocess, (PVOID)0x0009EDC8, 4, &buffer); DbgPrint("readbuf = %x \n", buffer);
DbgPrint("readbuf = %d \n", buffer); Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

读出后输出效果如下:

写出内存与读取基本一致,代码如下。

#include <ntifs.h>
#include <windef.h>
#include <intrin.h> #define DIRECTORY_TABLE_BASE 0x028 #pragma intrinsic(_disable)
#pragma intrinsic(_enable) NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);
NTKERNELAPI CHAR* PsGetProcessImageFileName(PEPROCESS Process); // 关闭写保护
KIRQL Open()
{
KIRQL irql = KeRaiseIrqlToDpcLevel();
UINT64 cr0 = __readcr0();
cr0 &= 0xfffffffffffeffff;
__writecr0(cr0);
_disable();
return irql;
} // 开启写保护
void Close(KIRQL irql)
{
UINT64 cr0 = __readcr0();
cr0 |= 0x10000;
_enable();
__writecr0(cr0);
KeLowerIrql(irql);
} // 检查内存
ULONG64 CheckAddressVal(PVOID p)
{
if (MmIsAddressValid(p) == FALSE)
return 0;
return *(PULONG64)p;
} // CR3 寄存器写内存
BOOLEAN CR3_WriteProcessMemory(IN PEPROCESS Process, IN PVOID Address, IN UINT32 Length, IN PVOID Buffer)
{
ULONG64 pDTB = 0, OldCr3 = 0, vAddr = 0; // 检查内存
pDTB = CheckAddressVal((UCHAR*)Process + DIRECTORY_TABLE_BASE);
if (pDTB == 0)
{
return FALSE;
} _disable(); // 读取CR3
OldCr3 = __readcr3(); // 写CR3
__writecr3(pDTB);
_enable(); // 验证并拷贝内存
if (MmIsAddressValid(Address))
{
RtlCopyMemory(Address, Buffer, Length);
return TRUE;
}
_disable(); // 恢复CR3
__writecr3(OldCr3);
_enable();
return FALSE;
} VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint(("Uninstall Driver Is OK \n"));
} NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("hello lyshark \n"); // 根据PID打开进程
PEPROCESS Peprocess = NULL;
DWORD PID = 6672;
NTSTATUS nt = PsLookupProcessByProcessId((HANDLE)PID, &Peprocess); DWORD buffer = 999; BOOLEAN bl = CR3_WriteProcessMemory(Peprocess, (PVOID)0x0009EDC8, 4, &buffer);
DbgPrint("写出状态: %d \n", bl); Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

写出后效果如下:

至于进程将CR3改掉了读取不到该寄存器该如何处理,这里我找到了一段参考代码,可以实现寻找CR3地址这个功能。

#include <ntddk.h>
#include <ntstrsafe.h>
#include <windef.h>
#include <intrin.h> #pragma pack(push, 1) typedef struct _IDTR // IDT基址
{
USHORT limit; // 范围 占8位
ULONG64 base; // 基地址 占32位 _IDT_ENTRY类型指针
}IDTR, *PIDTR; typedef union _IDT_ENTRY
{
struct kidt
{
USHORT OffsetLow;
USHORT Selector;
USHORT IstIndex : 3;
USHORT Reserved0 : 5;
USHORT Type : 5;
USHORT Dpl : 2;
USHORT Present : 1;
USHORT OffsetMiddle;
ULONG OffsetHigh;
ULONG Reserved1;
}idt;
UINT64 Alignment;
} IDT_ENTRY, *PIDT_ENTRY; #pragma pack(pop) // 输出调试内容
void DebugPrint(const char* fmt, ...)
{
UNREFERENCED_PARAMETER(fmt);
va_list ap;
va_start(ap, fmt);
vDbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, fmt, ap);
va_end(ap);
return;
} // 获取IDT表地址
ULONG64 GetIdtAddr(ULONG64 pIdtBaseAddr, UCHAR pIndex)
{
PIDT_ENTRY Pidt_info = (PIDT_ENTRY)(pIdtBaseAddr);
Pidt_info += pIndex;
ULONG64 vCurrentAddr = 0;
ULONG64 vCurrentHighAddr = 0;
vCurrentAddr = Pidt_info->idt.OffsetMiddle;
vCurrentAddr = vCurrentAddr << 16;
vCurrentAddr += Pidt_info->idt.OffsetLow; vCurrentHighAddr = Pidt_info->idt.OffsetHigh;
vCurrentHighAddr = vCurrentHighAddr << 32;
vCurrentAddr += vCurrentHighAddr;
return vCurrentAddr;
} VOID UnLoadDriver()
{ } NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT pPDriverObj, _In_ PUNICODE_STRING pRegistryPath)
{
UNREFERENCED_PARAMETER(pRegistryPath);
pPDriverObj->DriverUnload = (PDRIVER_UNLOAD)UnLoadDriver; /**
TP版KiPageFault
fffff880`09f54000 50 push rax
// 这里实际上是真实处理函数的地址 需要 & 0xFFFFFFFFFFF00000
fffff880`09f54001 48b87830ce0980f8ffff mov rax,0FFFFF88009CE3078h
fffff880`09f5400b 4883ec08 sub rsp,8
fffff880`09f5400f 48890424 mov qword ptr [rsp],rax
fffff880`09f54013 48311424 xor qword ptr [rsp],rdx
fffff880`09f54017 e810000000 call fffff880`09f5402c
fffff880`09f5401c 896eff mov dword ptr [rsi-1],ebp
fffff880`09f5401f 230500000089 and eax,dword ptr [fffff87f`92f54025]
**/
//得到TP KiPageFault地址
// _IDTR vContent;
// __sidt(&vContent);
ULONG64 vTpKiPageFault = GetIdtAddr(vContent.base, 0xE); //得到TP 动态内存起始值
ULONG64 vTpMemory = *(PULONG64)(vTpKiPageFault + 0x3) & 0xFFFFFFFFFFF00000; //得到TP KiPageFault真实处理函数
ULONG64 vTpKiPageFaultFuncAddr = vTpMemory + 0x4CE7C; if (MmIsAddressValid((PVOID)vTpKiPageFaultFuncAddr))
{//真实处理函数有效 //得到TP数据对象基地址
ULONG64 vTpDataObjectBase = *(PULONG)(vTpMemory + 0x1738B) + vTpMemory + 0x1738F; if (MmIsAddressValid((PVOID)vTpDataObjectBase))
{//基地址有效 //得到TP 用来保存真实CR3 保存当前所属进程ID 的对象
ULONG64 vTpDataObject = *(PULONG64)vTpDataObjectBase; DebugPrint("数据对象:0x%016llx, 真实CR3:0x%016llx, 所属进程ID:%d\n", vTpDataObject, *(PULONG64)(vTpDataObject + 0x70), *(PULONG)(vTpDataObject + 0x18));
}
else
DebugPrint("vTpDataObjectBase无法读取:0x%016llx\n", vTpDataObjectBase);
}
else
DebugPrint("vTpKiPageFaultFuncAddr无法读取:0x%016llx\n", vTpKiPageFaultFuncAddr); return STATUS_SUCCESS;
}

驱动开发:内核CR3切换读写内存的更多相关文章

  1. Windows驱动开发-内核常用内存函数

    搞内存常用函数 C语言 内核 malloc ExAllocatePool memset RtlFillMemory memcpy RtlMoveMemory free ExFreePool

  2. 驱动开发:内核运用LoadImage屏蔽驱动

    在笔者上一篇文章<驱动开发:内核监视LoadImage映像回调>中LyShark简单介绍了如何通过PsSetLoadImageNotifyRoutine函数注册回调来监视驱动模块的加载,注 ...

  3. 《Windows内核安全与驱动开发》 3.2 内存与链表

    <Windows内核安全与驱动开发>阅读笔记 -- 索引目录 <Windows内核安全与驱动开发> 3.2 内存与链表 1. 尝试生成一个链表头并将其初始化. 2. 尝试向内存 ...

  4. Linux驱动开发常用调试工具---之内存读写工具devmem和devkmem【转】

    转自:https://blog.csdn.net/gatieme/article/details/50964903 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原 ...

  5. 驱动开发:内核R3与R0内存映射拷贝

    在上一篇博文<驱动开发:内核通过PEB得到进程参数>中我们通过使用KeStackAttachProcess附加进程的方式得到了该进程的PEB结构信息,本篇文章同样需要使用进程附加功能,但这 ...

  6. Windows内核安全与驱动开发

    这篇是计算机中Windows Mobile/Symbian类的优质预售推荐<Windows内核安全与驱动开发>. 编辑推荐 本书适合计算机安全软件从业人员.计算机相关专业院校学生以及有一定 ...

  7. Linux驱动开发必看详解神秘内核(完全转载)

    Linux驱动开发必看详解神秘内核 完全转载-链接:http://blog.chinaunix.net/uid-21356596-id-1827434.html   IT168 技术文档]在开始步入L ...

  8. 《Windows内核安全与驱动开发》 7.1&7.2&7.3 串口的过滤

    <Windows内核安全与驱动开发>阅读笔记 -- 索引目录 <Windows内核安全与驱动开发> 7.1&7.2&7.3 串口的过滤 一.设备绑定的内核API ...

  9. Windows驱动开发-设备读写方式

    设备读写方式共三种: 方式 Flag 特点 缓冲区方式读写 DO_BUFFERED_IO I/O管理器先创建一个与用户模式数据缓冲区大小相等的系统缓冲区.而你的驱动程序将使用这个系统缓冲区工作.I/O ...

随机推荐

  1. 04 MySQL_数据相关的SQL

    字符编码问题: 如果使用Windows电脑SQL语句中写中文报错的话,通过以下指令解决 set names gbk; 判空NULL问题: 判值用=,判空用is xxx = 非空值; xxx is nu ...

  2. Note -「因数的欧拉函数求和」

    归档. 试证明:\(\sum \limits _{d | x} \varphi (d) = x\) Lemma 1. 试证明:\(\sum \limits _{d | p^k} \varphi (d) ...

  3. Hbuilderx Eslint配置

    [参照链接]https://blog.csdn.net/m0_67394002/article/details/123346267 安装插件 eslint-js eslint-plugin-vue 复 ...

  4. 【一本通提高树链剖分】「ZJOI2008」树的统计

    [ZJOI2008]树的统计 题目描述 一棵树上有 n n n 个节点,编号分别为 1 1 1 到 n n n,每个节点都有一个权值 w w w. 我们将以下面的形式来要求你对这棵树完成一些操作: I ...

  5. Linux学习系列--用户(组)新增、查看和删除

    在实际的工作中,在接触Linux的用户组管理的时候,一般来说都是在系统开建设的时候设置好,root权限由特定的负责人保管用户密码,避免误操作带来不必要的麻烦. 在具体使用的时候,会利用相关的命令设置一 ...

  6. rsync 文件备份

    # rsync # 实现文件的备份. # 备份位置可以是当前主机,也可以是远程主机. # rsync实现了完全备份和增量备份 # 可以做到:1.将本地主机的文件复制到另一个位置(本地.远程). # 2 ...

  7. Auto.js 特殊定位控件方法 不能在ui线程执行阻塞操作,请使用setTimeout代替

    本文所有教程及源码.软件仅为技术研究.不涉及计算机信息系统功能的删除.修改.增加.干扰,更不会影响计算机信息系统的正常运行.不得将代码用于非法用途,如侵立删! Auto.js 特殊定位控件方法 操作环 ...

  8. Three---面向对象与面向过程/属性和变量/关于self/一些魔法方法的使用/继承/super方法/多态

    python的面向对象 面向对象与面向过程 面向过程 面向过程思想:需要实现一个功能的时候,看重的是开发的步骤和过程,每一个步骤都需要自己亲力亲为,需要自己编写代码(自己来做) 面向对象 面向对象的三 ...

  9. Linux 07 用户组文件

    参考源 https://www.bilibili.com/video/BV187411y7hF?spm_id_from=333.999.0.0 版本 本文章基于 CentOS 7.6 概述 用户组的所 ...

  10. Redis 10 位图

    参考源 https://www.bilibili.com/video/BV1S54y1R7SB?spm_id_from=333.999.0.0 版本 本文章基于 Redis 6.2.6 概述 Redi ...