驱动开发:内核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; } 

读出后输出效果如下:

驱动开发:内核CR3切换读写内存

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

#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改掉了读取不到该寄存器该如何处理,这里我找到了一段参考代码,可以实现寻找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:%dn", vTpDataObject, *(PULONG64)(vTpDataObject + 0x70), *(PULONG)(vTpDataObject + 0x18)); 		} 		else 			DebugPrint("vTpDataObjectBase无法读取:0x%016llxn", vTpDataObjectBase); 	} 	else 		DebugPrint("vTpKiPageFaultFuncAddr无法读取:0x%016llxn", vTpKiPageFaultFuncAddr);  	return STATUS_SUCCESS; } 

发表评论

相关文章