驱动开发:内核CR3切换读写内存
首先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切换读写内存的更多相关文章
- Windows驱动开发-内核常用内存函数
搞内存常用函数 C语言 内核 malloc ExAllocatePool memset RtlFillMemory memcpy RtlMoveMemory free ExFreePool
- 驱动开发:内核运用LoadImage屏蔽驱动
在笔者上一篇文章<驱动开发:内核监视LoadImage映像回调>中LyShark简单介绍了如何通过PsSetLoadImageNotifyRoutine函数注册回调来监视驱动模块的加载,注 ...
- 《Windows内核安全与驱动开发》 3.2 内存与链表
<Windows内核安全与驱动开发>阅读笔记 -- 索引目录 <Windows内核安全与驱动开发> 3.2 内存与链表 1. 尝试生成一个链表头并将其初始化. 2. 尝试向内存 ...
- Linux驱动开发常用调试工具---之内存读写工具devmem和devkmem【转】
转自:https://blog.csdn.net/gatieme/article/details/50964903 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原 ...
- 驱动开发:内核R3与R0内存映射拷贝
在上一篇博文<驱动开发:内核通过PEB得到进程参数>中我们通过使用KeStackAttachProcess附加进程的方式得到了该进程的PEB结构信息,本篇文章同样需要使用进程附加功能,但这 ...
- Windows内核安全与驱动开发
这篇是计算机中Windows Mobile/Symbian类的优质预售推荐<Windows内核安全与驱动开发>. 编辑推荐 本书适合计算机安全软件从业人员.计算机相关专业院校学生以及有一定 ...
- Linux驱动开发必看详解神秘内核(完全转载)
Linux驱动开发必看详解神秘内核 完全转载-链接:http://blog.chinaunix.net/uid-21356596-id-1827434.html IT168 技术文档]在开始步入L ...
- 《Windows内核安全与驱动开发》 7.1&7.2&7.3 串口的过滤
<Windows内核安全与驱动开发>阅读笔记 -- 索引目录 <Windows内核安全与驱动开发> 7.1&7.2&7.3 串口的过滤 一.设备绑定的内核API ...
- Windows驱动开发-设备读写方式
设备读写方式共三种: 方式 Flag 特点 缓冲区方式读写 DO_BUFFERED_IO I/O管理器先创建一个与用户模式数据缓冲区大小相等的系统缓冲区.而你的驱动程序将使用这个系统缓冲区工作.I/O ...
随机推荐
- NOI / 2.3基本算法之递归变递推-6262:流感传染
OpenJudge - 6262:流感传染http://noi.openjudge.cn/ch0203/6262/ 6262:流感传染 总时间限制: 1000ms 内存限制: 65536k ...
- 背包问题学习笔记 / Dynamic Programming(updating)
01背包问题 朴素版:(二维数组) 状态表示: dp[i][j]:从前i个物品中选择(每个物品只能选0或1个)且总体积不超过j的集合的最大价值,则dp[n][m]就是最终答案(n:物品数量,m ...
- 破坏正方形UVA1603
题目大意 有一个由火柴棍组成的边长为n的正方形网格,每条边有n根火柴,共2n(n+1)根火柴.从上至下,从左到右给每个火柴编号,现在拿走一些火柴,问在剩下的后拆当中ongoing,至少还要拿走多少根火 ...
- PHP生成器yield使用示例
<?php function getLines($file) { $f = fopen($file, 'r'); try { while ($line = fgets($f)) { yield ...
- vue-resource && axios
1 # axios 2 # 1.安装:npm i axios 3 # 2.使用: 4 import axios from 'axios' 5 axios.get(URL).then(response= ...
- 《吐血整理》进阶系列教程-拿捏Fiddler抓包教程(12)-Fiddler设置IOS手机抓包,你知多少???
1.简介 Fiddler不但能截获各种浏览器发出的 HTTP 请求,也可以截获各种智能手机发出的HTTP/ HTTPS 请求. Fiddler 能捕获Android 和 Windows Phone 等 ...
- 用VS Code搞Qt6:至简窗口部件——QWidget
在正题开始之前,老周照例扯点别的.嗯,咱们扯一下在 VS 2022 下结合 CMake 开发 Qt6 时的环境变量设置问题.在VS Code 中,通够通过 CMake Tools 扩展的配置来设置环境 ...
- 跟我学Python图像处理丨基于灰度三维图的图像顶帽运算和黑帽运算
摘要:本篇文章结合灰度三维图像讲解图像顶帽运算和图像黑猫运算,通过Python调用OpenCV函数实现. 本文分享自华为云社区<[Python图像处理] 十三.基于灰度三维图的图像顶帽运算和黑帽 ...
- Dynamic CRM使用FetchXML在js中查询与调用传递编码问题
在页面交互脚本js中实现窗体交互逻辑是很常见的crm场景,一般情况下使用拓展工具RESTBuilder编辑器,可以很方便的进行操作,增删改查均能实现,但在某些较为特殊的场景下,需要根据条件去拼接查询过 ...
- 超实用在线工具!能将文字加密为Emoji表情
试想一下,如果你需要将一段比较敏感的内容发送给你的好友. 但如果这段内容不小心外泄,被别人看到了,可能会带来很多麻烦. 那么,有什么方法能够让传输的文本内容不那么容易被"看破"呢? ...