首先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();// 读取CR3OldCr3 = __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版KiPageFaultfffff880`09f54000 50              push    rax// 这里实际上是真实处理函数的地址 需要 & 0xFFFFFFFFFFF00000fffff880`09f54001 48b87830ce0980f8ffff mov rax,0FFFFF88009CE3078hfffff880`09f5400b 4883ec08        sub     rsp,8fffff880`09f5400f 48890424        mov     qword ptr [rsp],raxfffff880`09f54013 48311424        xor     qword ptr [rsp],rdxfffff880`09f54017 e810000000      call    fffff880`09f5402cfffff880`09f5401c 896eff          mov     dword ptr [rsi-1],ebpfffff880`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));}elseDebugPrint("vTpDataObjectBase无法读取:0x%016llx\n", vTpDataObjectBase);}elseDebugPrint("vTpKiPageFaultFuncAddr无法读取:0x%016llx\n", vTpKiPageFaultFuncAddr);return STATUS_SUCCESS;
}

驱动开发:内核CR3切换读写内存相关推荐

  1. Linux驱动开发—内核I2C驱动详解

    Linux驱动开发--内核I2C驱动 I2C驱动文件结构 I2C数据传输过程 i2c_transfer i2c_msg I2C通讯常用的接口函数(老版本) 快速读写接口函数:(连续读写) 常用的读操作 ...

  2. Windows驱动开发 - 内核模式下的字符串操作

    1 ASCII字符串和宽字符串 char型,记录ansi字符集.每个字符一个字节.以0标志结束.在KdPrint中用%s输出. 宽字符型,wchar_t,描述unicode字符集的字符串,每个字符两个 ...

  3. Linux 驱动开发 三十五:Linux 内核时钟管理

    参考: linux时间管理,时钟中断,系统节拍_u010936265的博客-CSDN博客_系统节拍时钟中断 Linux内核时钟系统和定时器实现_anonymalias的专栏-CSDN博客_linux内 ...

  4. [Linux驱动开发五]实现ioctl函数

    目录 一.前言 1.1 ioctl用来做什么? 1.2 ioctl和unlock_ioctl区别? (1)ioctl()主要用于应用层系统调用 (2)unlock_ioctl主要用于驱动层系统调用 1 ...

  5. [LINUX]LINUX驱动开发最简环境搭建(适用于新手)

    LINUX驱动开发环境搭建 一. 前言 二.系统环境 Ubuntu VMWare{非必选} 三.开发环境搭建 1.用于简单学习(Header) 2.用于复杂驱动开发(源码树) a. 基本包 b. 内核 ...

  6. 1.1、LINUX驱动开发

    LINUX驱动 内存管理 一.LINUX驱动开发 0.驱动开发知识点 1.基础知识储备 2.裸机汇编程序 3.裸机C程序 4.系统移植 1.LINUX系统移植蓝图 2.系统烧写 5.驱动开发 --参考 ...

  7. 驱动开发:内核MDL读写进程内存

    MDL内存读写是最常用的一种读写模式,通常需要附加到指定进程空间内然后调用内存拷贝得到对端内存中的数据,在调用结束后再将其空间释放掉,通过这种方式实现内存读写操作,此种模式的读写操作也是最推荐使用的相 ...

  8. 驱动开发: 封装内核级驱动读写

    内核级别的内存读写可用于绕过各类驱动保护,从而达到强制读写对端内存的目的,本人闲暇之余封装了一个驱动级的内核读写接口,使用此接口可实现对远程字节,字节集,整数,浮点数,多级偏移读写等. 项目地址:Gi ...

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

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

最新文章

  1. matlab 全员极大型Topsis评价代码
  2. 湖南省第6届程序大赛第3题 数字整除
  3. 为什么python不出结果_Python 的 Checksum 为什么结果是一长串数字而不是如下效果...
  4. do msgbox loop_【连载1】DDC模块 DO点与强电电气柜的接口形式
  5. 数据结构(二)---单链表的实现---java版
  6. java 常量折叠_深入理解Java虚拟机之早期编译器优化
  7. Note - Shader - 2
  8. 停航63天!湖北复航了,机票预订火爆程度堪比春运
  9. Golang map 三板斧第三式:实现原理
  10. css带三角形的对话框
  11. 嘉兴市人才网即时招聘栏目Ajax动态翻页爬虫练习
  12. 剑侠世界2不显示服务器,剑侠世界2去哪个服务器好_哪个区人多问题说明_软吧...
  13. OpenGL 高质量纹理过滤的实例
  14. ddns的搭建(动态域名解析DDNS)
  15. 1.居民身份证号码,由17位数字本体码,和1位数字校验码组成 2.判断用户输入的身份证号码是否合法 规则为:号码为18位,不能以0开头,前17位只可以是数字,最后一位可以是数字或‘X‘
  16. 【GitHub上传文件夹:bug】 ! [rejected] master - master (non-fast-forward)
  17. Elastic-Job原理--任务失败转移(五)
  18. 51单片机入门 - 自己动手搭建实验板
  19. 挖掘:如何用迅雷下载4399小游戏站内的所有游戏
  20. 从程序关闭Windows。

热门文章

  1. 中国移动一键免密登录 103101签名错误
  2. 压力变送器在选型时需要掌握哪些选型技巧?
  3. LibreOJ 6282,6284,6285 数列分块入门6,8,9 树状数组,分块,线段树等.
  4. google的glog日志管理
  5. 【cocos creater】7.仿《弓箭传说》- 控制主角朝向敌人
  6. The type XXXX must implement the inherited abstract method XXXX
  7. icon图标素材网站推荐,支持免费下载
  8. python量化交易策略——唐奇安通道和海龟策略(1)
  9. 云客Drupal源码分析之分页器pager
  10. Kernighan-Lin算法