首页
论坛
专栏
课程

[原创]彩蛋解密之物理内存读写到****的转变

2019-8-19 04:18 2915

[原创]彩蛋解密之物理内存读写到****的转变

2019-8-19 04:18
2915

莫名其妙的说明:

这次的彩蛋解密是针对上次发的文章所写的。
传送门:驱动层代码隐藏执行(有码)https://bbs.pediy.com/thread-253677.htm
在上次的文章结尾处我挖了个坑,这次给它填上。
PhysicalMemoryOperation.h这个小东西是半年前写的了,当时写这玩意的目的是为了实现驱动对物理内存的高速读写加深我对分页机制的理解,写完这个小东西之后我发现经过升级还能有更多的玩法,这里我将只对基础版本进行详细分析,并在结尾处继续挖个坑。

令人挠头的操作:

首先放上一段在DriverEntry中让人看了挠头的操作
ContextVirtualToPhysical(&g_PhysicalOpCR3);
ULONG64 Read1 = *(PULONG64)0x0;
MyPrint(_TitleAndFunc "ReadTest1:%16IX\n", Read1);
ContextPhysicalToVirtual(&g_PhysicalOpCR3);
啊,读空指针啊,这不明摆着蓝屏吗?

唉,咋没蓝屏,还读到东西了呢?
下面我们来windbg看一下

唉,有点东西是吧

咋实现的啊,我先去看看代码吧:


咋看不懂呢

使用windbg简单分析:


这有个cr3,看看是啥东西?
这一堆整整齐齐的都是啥玩意啊?
不管了直接!vtop看一看
我相信看到这里,对分页机制比较熟悉的大手子们就应该大概懂实现原理了

超级困难简单的基础知识:

首先需要熟悉分页机制,不懂的查阅intel手册和网上的一些文章进行学习。
下面贴出intel手册中对于分页使用规则的详细说明。

真的简单的程序流程介绍:

调用CreatePhysicalOpCR3BySystemCR3进行初始化
在其中分别依次调用pAllocPhysicalOpPageTableMemory、pMapSystemPML4T、pFillGeneratedPML4TandPDPT进行初始化,并填充结构

需要读取物理内存时调用ContextVirtualToPhysical、ContextPhysicalToVirtual进行环境切换

卸载时调用FreePhysicalOpCR3
在其中依次调用pUnmapSystemPML4T、pFreePhysicalOpPageTableMemory并清理结构

详细分析:

首先使用:CreatePhysicalOpCR3BySystemCR3
进行初始化工作,下面是这个函数的代码
NTSTATUS CreatePhysicalOpCR3BySystemCR3(ULONG64 SystemCR3, PPHYSICAL_OP_CR3 pPhysicalOpCR3)
{
	//check the init state
	if (g_IsPhysicalOpInit)
		return STATUS_UNSUCCESSFUL;


	//allocate page table memory and fill the structure
	if (!NT_SUCCESS(pAllocPhysicalOpPageTableMemory(pPhysicalOpCR3)))
		return STATUS_UNSUCCESSFUL;

	//map pSystemPML4T to virtual address and fill the structure
	if (!NT_SUCCESS(pMapSystemPML4T(SystemCR3, pPhysicalOpCR3)))
		return STATUS_UNSUCCESSFUL;

	//fill PML4T and PDPT page table
	if (!NT_SUCCESS(pFillGeneratedPML4TandPDPT(pPhysicalOpCR3)))
		return STATUS_UNSUCCESSFUL;

	//generate new cr3 for reading the physical memory and add cr3 flag
	ULONG64	SystemCR3Flag = GetCR3Flag(SystemCR3);
	pPhysicalOpCR3->CR3Generated = (ULONG64)pPhysicalOpCR3->pAllocPA_PML4T | SystemCR3Flag;

	//fill the structure part:CR3System
	pPhysicalOpCR3->CR3System = SystemCR3;

	//print structure
	pPrintPhysicalOpStructure(pPhysicalOpCR3);

	g_IsPhysicalOpInit = TRUE;
	return STATUS_SUCCESS;
}

在这个函数中完成了所有初始化操作
第一步调用pAllocPhysicalOpPageTableMemory
NTSTATUS pAllocPhysicalOpPageTableMemory(PPHYSICAL_OP_CR3 pPhysicalOpCR3)
{
	//PML4T
	pPhysicalOpCR3->pAllocVA_PML4T = MmAllocateNonCachedMemory(PAGE_TABLE_SIZE);
	//check allocate state
	if (pPhysicalOpCR3->pAllocVA_PML4T == NULL)
		goto Lable_Error;
	pPhysicalOpCR3->pAllocPA_PML4T = (PVOID)MmGetPhysicalAddress(pPhysicalOpCR3->pAllocVA_PML4T).QuadPart;


	//PDPT
	pPhysicalOpCR3->pAllocVA_PDPT = MmAllocateNonCachedMemory(PAGE_TABLE_SIZE);
	//check allocate state
	if (pPhysicalOpCR3->pAllocVA_PDPT == NULL)
		goto Lable_Error;
	pPhysicalOpCR3->pAllocPA_PDPT = (PVOID)MmGetPhysicalAddress(pPhysicalOpCR3->pAllocVA_PDPT).QuadPart;


	return STATUS_SUCCESS;
Lable_Error:
	//free allocated memory
	pFreePhysicalOpPageTableMemory(pPhysicalOpCR3);
	return STATUS_UNSUCCESSFUL;
}
这个函数的作用是申请两个页面,分别用于存储自己创建的PML4T和用于存放大页的PDPT

第二步调用pMapSystemPML4T
NTSTATUS pMapSystemPML4T(ULONG64 SystemCR3, PPHYSICAL_OP_CR3 pPhysicalOpCR3)
{
	ULONG64 SystemCR3NonFlag = ClearCR3Flag(SystemCR3);
	PVOID	pSystemPML4T = (PVOID)SystemCR3NonFlag;

	if (g_SectionHandle == NULL)
		g_SectionHandle = OpenPhysicalMemory();

	pPhysicalOpCR3->pSystemPML4TMap = MapPhysicalMemory(pSystemPML4T, PAGE_TABLE_SIZE);

	return pPhysicalOpCR3->pSystemPML4TMap == NULL ? STATUS_UNSUCCESSFUL : STATUS_SUCCESS;
}
这个函数的作用是将系统的PML4T映射一份并保存到结构中

第三步调用pFillGeneratedPML4TandPDPT
NTSTATUS pFillGeneratedPML4TandPDPT(PPHYSICAL_OP_CR3 pPhysicalOpCR3)
{
	//copy the system space map
	PVOID		pSystemStart = (PVOID)VA_SYSTEM_START;
	ULONG64		SystemPML4TStart = ((PMMVA)&pSystemStart)->PML4T;

	MyPrint(_TitleAndFunc"SystemPML4TStart:%16X\n", SystemPML4TStart);
	RtlCopyMemory((PVOID)((ULONG64)pPhysicalOpCR3->pAllocVA_PML4T + SystemPML4TStart*ENTRY_SIZE),
		(PVOID)((ULONG64)pPhysicalOpCR3->pSystemPML4TMap + SystemPML4TStart*ENTRY_SIZE),
		(MAX_ENTRY_COUNT - SystemPML4TStart)*ENTRY_SIZE
	);

	//make the first address point to my PDPT table
	*(PULONG64)pPhysicalOpCR3->pAllocVA_PML4T = (ULONG64)pPhysicalOpCR3->pAllocPA_PDPT | PAGE_TABLE_PML4T_FLAG;

	//fill the PDPT page table
	//add flag
	ULONG64 CurrentPDPTEntry = PAGE_TABLE_PDPT_FLAG;
	for (int i = 0; i < MAX_ENTRY_COUNT; i++)
	{
		//change pfn
		((PMMPDPTE)&CurrentPDPTEntry)->PageFrameNumber = i;
		//
		*(PULONG64)((ULONG64)pPhysicalOpCR3->pAllocVA_PDPT + i*ENTRY_SIZE) = CurrentPDPTEntry;
	}
	return STATUS_SUCCESS;
}
这个函数的作用是填充第一步申请的两块内存

到此为止初始化工作就基本结束了
难以理解的函数主要是 pFillGeneratedPML4TandPDPT,这里要详细讲解一下

通过查阅资料我们发现驱动层的代码不会出现在PML4T表的前0x80项中的,这样等于我们就有了0x80*(0x1000/8)*1G=65536G的空间是可以用来映射物理内存的

这里我们决定使用1G的大页面映射方式进行映射,我们的代码中只对前512G进行了映射,基本上现在内存没有超过这个大小的了。

第一步把系统的PML4T的内容拷贝到自己的PML4T中,这样操作一下子相当于拷贝了一份系统的映射关系,我们的代码就能正常的跑在我们新创建的这个PML4T中了
第二步我们让我们的PML4T的第一项指向我们的PDPT,并给上相应的标志位,具体标志位对应的权限请查阅上面的图表或intel手册
第三步我们填充PDPT,让其指向物理页面,使前512G的映射关系为虚拟地址和物理地址一一对应,同样的给上相应的标志

到此初始化部分的分析算是结束了

读写物理内存的环境切换代码很简单
就是个切cr3,切irql,关/开中断,就不做具体分析了

逐渐奇怪的升级:

直接给出方程式

大概懂了吧
经过测试,主产物的读取速度基本是千万次每秒级别的

项目地址:

https://github.com/zouxianyu/PhysicalMemoryRW

(别想了,这只是原料,反应需要自己实现的)


后记:

希望大家不要沉迷编程,多拿出一些时间陪陪家人恋人朋友,不要给自己留下遗憾。


[公告]安全服务和外包项目请将项目需求发到看雪企服平台:https://qifu.kanxue.com

最新回复 (18)
kingswb 2019-8-19 06:47
2
0
来个沙发,顺便问句,虚拟机支持刷tlbs吗?
万剑归宗 1 2019-8-19 08:35
3
0
你这不是沉迷编程,而是沉迷如何当个大手子
mydvdf 2019-8-19 08:58
4
0
带手子来支持一下
刘铠文 2019-8-19 10:38
5
0
给力嗷
StriveMario 2019-8-19 11:39
6
0
最近帖子都那么活泼吗? 又get到一波表情包
Buu 1 2019-8-19 11:40
7
0
hzqst 3 2019-8-19 11:57
8
0
搞了个半天自己实现了一个不稳定版的MmMapIoSpace,意义何在
鬼才zxy 1 2019-8-19 11:58
9
0
kingswb 来个沙发,顺便问句,虚拟机支持刷tlbs吗?
是要做啥?没看懂
鬼才zxy 1 2019-8-19 11:59
10
0
hzqst 搞了个半天自己实现了一个不稳定版的MmMapIoSpace,意义何在
重点在结尾,没细说的那部分
刘铠文 2019-8-19 12:45
11
0
刚才没细看,细看了一下,我评价一下,首先你这个帖子的确很少见,过程讲的也不错,但是没有太大的意义,如果只是用来研究的话,的确可以,因为系统本身的物理内存映射方法也不只有这一种,而且这种读写方式有弊端,如果分页内存换出,你的这个读写就毫无用处了,分页内存换回的机制是触发异常以后才会换回来,这个坑以前我就踩过,目前还真的没有解决方法
刘铠文 2019-8-19 12:48
12
0
也不是说不能拿来用,只能说。。。。不得劲
鬼才zxy 1 2019-8-19 13:38
13
0
对,据我了解换出的话需要用缺页isr中的某些函数处理一下
鬼才zxy 1 2019-8-19 13:44
14
0
如果忽略缺页问题的话,那么这种方案移植到应用层会是很好玩的
刘铠文 2019-8-19 14:02
15
0
鬼才zxy 对,据我了解换出的话需要用缺页isr中的某些函数处理一下
访问一下虚拟内存就回来了,但是这种方式读写的话做不到
芃杉 2019-8-19 14:46
16
0
mark
kingswb 2019-8-19 18:17
17
0
鬼才zxy 是要做啥?没看懂
没什么,就是隐藏执行的话使用两张pte表,就行。一个读写,一个执行。就是需要刷tbls,虚拟机好像不行,我不确定,特此求问
鬼才zxy 1 2019-8-19 19:44
18
0
kingswb 没什么,就是隐藏执行的话使用两张pte表,就行。一个读写,一个执行。就是需要刷tbls,虚拟机好像不行,我不确定,特此求问[em_12]
stlb了解一下,现在的cpu微架构不支持这种分离了
上古邪神 2019-8-21 10:52
19
0
学习
游客
登录 | 注册 方可回帖
返回