Windows内存放血篇,突破物理内存的CopyOnWrite
1. PAE:Physical Address Extension,Inter为了支持更大的物理内存寻址而设计的x86寻址方式,虚拟地址没有变化都是32位,只是描述物理内存的位数由原先的32为增加到36位,能够最多寻址 2^4 * 4GB = 64GB内存,也就意味着你机器上如果存在超过4GB的内存条,那么一般都可以被充分利用到,这只是体现在多进程多任务的性能上,并没有增加一个进程的寻址空间,仍然为4GB。微软喜欢把页面表基地址放在0xC0000000上,当发生进程切换操作时这块页表内容会随CR3引导的页面表的内容而发生改变(一般内核的高2GB不会变化太大,主要体现在低2GB内存),那么这就有规律可言,在内核情景分析中可能大家都已经见过未开启PAE的几个公式:1) 未开启PAE状态下 (10/10/12)
PTE = (VA >> 12) << 2 + PTE_BASEPDE = (VA >> 22) << 2 + PTE_BASE因为 PDE_BASE 是描述PTE_BASE的PTE显然PDE_BASE = (PTE_BASE >> 12) << 2 + PTE_BASE = (0xC0000000 >> 12) << 2 +
0xC0000000 = 0xC0300000
那么自己推导下PAE下的计算方式2) 开启PAE状态下 (2/9/9/12)PTE = (VA >> 12) << 3 + PTE_BASE
PDE = (VA >> 21) << 3 + PTE_BASE
PDPE = (VA >> 30) << 3 + PDE_BASE
因为 PDE_BASE 是描述PTE_BASE的PTE
显然 PDE_BASE = (PTE_BASE >> 12) << 3 + PTE_BASE = (0xC0000000 >> 12) << 3 + 0xC0000000 = 0xC0600000
2. x64 公式推导
WRK或者WDK开发包头文件中定义了64位下 PTE_BASE 的内容
1234#define PTE_BASE 0xFFFFF68000000000UI64#define PPE_BASE 0xFFFFF6FB7DA00000UI64#define PDE_BASE 0xFFFFF6FB40000000UI64#define PXE_BASE 0xFFFFF6FB7DBED000UI64自然,这几个值看起来都是固定了,其实是因为PTE_BASE固定的,才有个下面这几个固定的值,计算方式如下:
PDE_BASE = ((PTE_BASE & 0x0000FFFFFFFFF000) >> 12) * 8 + PTE_BASE
= 0xF68000000 * 8 + PTE_BASE
= 0x7B40000000 + PTE_BASE = 0xFFFFF6FB40000000
PPE_BASE = ((PDE_BASE & 0x0000FFFFFFFFF000) >> 12) * 8 + PTE_BASE= 0xF6FB40000 * 8 + PTE_BASE = 0x7B7DA00000 + PTE_BASE
= 0xFFFFF6FB7DA00000
PXE_BASE = ((PPE_BASE & 0x0000FFFFFFFFF000) >> 12) * 8 + PTE_BASE= 0xF6FB7DA00 * 8 + PTE_BASE
= 0x7B7DBED000 + PTE_BASE = 0xFFFFF6FB7DBED000
在PAE开启状态下
(下文默认)
或者x64系统下,描述PTE结构的定义为:
12345678910111213141516171819202122232425262728293031323334typedef struct _MMPTE_HARDWARE {ULONGLONG Valid :1;ULONGLONG Write :1;//UP versionULONGLONG Owner :1;ULONGLONG WriteThrough :1;ULONGLONG CacheDisable :1;ULONGLONG Accessed :1;ULONGLONG Dirty :1;ULONGLONG LargePage :1;ULONGLONG Global :1;ULONGLONG CopyOnWrite :1;//software fieldULONGLONG Prototype :1;//software fieldULONGLONG reserved0 :1;//software fieldULONGLONG PageFrameNumber :28;ULONG64 reserved1 :24-(_HARDWARE_PTE_WORKING_SET_BITS+1);ULONGLONG SoftwareWsIndex : _HARDWARE_PTE_WORKING_SET_BITS;ULONG64 NoExecute :1;} MMPTE_HARDWARE,*PMMPTE_HARDWARE;typedef struct _MMPTE {union {//ULONG_PTRLong;MMPTE_HARDWARE Hard;//MMPTE_HARDWARE_LARGEPAGE HardLarge;//HARDWARE_PTE Flush;//MMPTE_PROTOTYPE Proto;//MMPTE_SOFTWARE Soft;//MMPTE_TRANSITION Trans;//MMPTE_SUBSECTION Subsect;//MMPTE_LISTList;} u;} MMPTE;typedef MMPTE*PMMPTE;
0x02 Physical Memory Patch
实际上这个ULONGLONG CopyOnWrite : 1; // software field我并没有看出什么玄机,重点是这个ULONGLONG Write : 1; // UP version找到虚拟地址对应的PTE项,将Write位置为1,自然这块内存就不再为写拷贝了,看Inter手册上对这个字段的描述也不是特别的清楚,下图为2MB的大页面对应的结构,跟4KB的小页面也差不了多少,对R/W字段的描述也不是很明显,只是WRK/Win2000上的这个software field的3个字段全部为Ignored...这个位起着的作用看上去不是只有一个可写属性,当我写一个Dll让一个目标进程去Load然后用这种方式把他的PE头给Patch了之后,达到了与MDL修改物理内存一样的效果(MDL其实也是一个突破CopyOnWrite的一个方法),以后这个进程再也加载不起来这个Dll了,因为原始的物理页已经被修改了。
12345678910111213141516171819202122232425262728293031323334typedef struct tag_CTRLV2{PVOID lpAddress;PVOID lpPatchContext;ULONG ulSize;} CtrlV2,*PCtrlV2;BOOLEAN ModifyPhysicalAddressX86(PCtrlV2 pV2){if(g_bPAEON){PMMPTE_PAE ProtectPTE=MiGetPteAddressForPAE(pV2->lpAddress);__try{if(ProtectPTE->Valid){//Disable CopyOnWriteProtectPTE->Write=1;//Now Patch Physical Memorymemcpy(pV2->lpAddress, pV2->lpPatchContext, pV2->ulSize);DbgPrint("[Wxoit] ModifyPhysicalAddressX86 pV2->lpAddress:%x, Context:%x\r\n",pV2->lpAddress,*(ULONG*)pV2->lpAddress);}}__except(EXCEPTION_EXECUTE_HANDLER){DbgPrint("[Wxoit] ModifyPhysicalAddressX86 Raise Exception %x", GetExceptionCode());}}returnTRUE;}第一次加载NopDll.dll 并Patch NopDll.dll 的PE DOS_SIGNATURE。第二次加载NopDll.dll时,发现这个Dll已经是一个bad exe format当然这个方法,我也给大家支持了64位,但是警告大家不要去随意搞系统的内存,出问题本人概不负责...
代码写的比较急,没有支持跨进程操作物理内存,大家如果想做只要KeStackAttachProcess下就OK了,代码在最后的附件中
0x02 Things of MDL
最后就当作福利吧,前段时间在看MDL的一些API,把我所学分享给大家。
IoAllocateMdl
MmProbeAndLockPages/MmBuildMdlForNonPagedPool
MmMapLockedPagesSpecifyCache
MDL不止只有下面描述的结构,在这个结构的后面还存在着这个MDL描述的所有的物理页的页面帧号
12345678910typedef struct _MDL {struct _MDL*Next;CSHORT Size;CSHORT MdlFlags;struct _EPROCESS*Process;PVOID MappedSystemVa;PVOID StartVa;ULONG ByteCount;ULONG ByteOffset;} MDL,*PMDL;
1. IoAllocateMdl
PMDLIoAllocateMdl(
IN PVOID VirtualAddress,
IN ULONG Length,
IN BOOLEAN SecondaryBuffer,
IN BOOLEAN ChargeQuota,
IN OUT PIRP Irp OPTIONAL
)
这个API没啥好说的,就是小心点大小检测,当传入的Length越过了0x17个页面时,对MDL的大小有要求(不能超过0xFFFF),第三参数只有在第五参数存在时才有意义:标志这个是不是一个链式内存(一般只有在IRP结构中需要处理),第四参数没看到在哪用。一般地,三四五参数都传NULL。2.MmProbeAndLockPages
VOID
MmProbeAndLockPages (
IN OUT PMDL MemoryDescriptorList,
IN KPROCESSOR_MODE AccessMode,
IN LOCK_OPERATION Operation
)好了,这个API开始就要注意了,这块特别容易抛异常
1. 进入这个函数之前,不要随便给MDL置标记(不管是你手动的还是API帮你置的位),特别是
MDL_PAGES_LOCKEDMDL_MAPPED_TO_SYSTEM_VA
MDL_SOURCE_IS_NONPAGED_POOL
MDL_PARTIAL
MDL_IO_SPACE
2. 存在当前模式,如果传入UserMode,那么在第一步初始化MDL如果描述的虚拟地址是一个内核地址,那么这直接抛0xC0000005异常
3. 这个API紧接这会去锁住MDL描述的物理内存页面,当你传入MDL的虚拟地址是一个Ring3地址, 也会校验你传入的Operation,
其中
一个页面不具有写属性你却传入了 IoWriteAccess/IoModifyAccess 那么不好意思,同样RaiseException
4. 检查当前进程(对是当前进程!,调用这个函数如果你要修改别人家的物理内存那么请先KeStackAttachProcess )
的虚拟内存对应的物理
页面映射关系,如果你尝试传入一个缺页的内存,这个函数会尝试处理这个缺页情况,再做类似第三步的动作
5. 即使找到了虚拟页面映射的物理页面,如果传入
IoWriteAccess/IoModifyAccess 也会校验对应的VAD是否具有MM_READWRITE属性
使用这个函数时,如果你要修改内存那么不必急着传入
IoWriteAccess/IoModifyAccess 这样会造成这个函数代码内部的检测逻辑,因为最
后在调用MmMapLockedPagesSpecifyCache 函数时,不管是Ring3还是Ring0应该都是具有读写属性的。在我的理解上来看.......
3.MmBuildMdlForNonPagedPool
VOIDMmBuildMdlForNonPagedPool (
IN OUT PMDL MemoryDescriptorList
)这个函数很简单,就负责置MDL的标志位以及填充页面帧号,当然也要求当前进程的页面表能够访问到的内存MemoryDescriptorList->MdlFlags |= MDL_SOURCE_IS_NONPAGED_POOL;
4.MmMapLockedPagesSpecifyCache
PVOIDMmMapLockedPagesSpecifyCache (IN PMDL MemoryDescriptorList,IN KPROCESSOR_MODE AccessMode,IN MEMORY_CACHING_TYPE CacheType,IN PVOID RequestedAddress,IN ULONG BugCheckOnFailure,IN MM_PAGE_PRIORITY Priority)当MDL的页面帧号都填充完毕时,通过MmMapLockedPagesSpecifyCache最后一步映射物理内存到当前进程页面表中,
不知道微软是怎么想到设计这个接口的,这个函数实在过于强大。强大不光体现在他能越过内存的CopyOnWrite机制,而且通过MmMapLockedPagesSpecifyCache得到的虚拟内存地址具有读写属性......
1. KernelMode 内核模式下会得到一个内核地址,我们都知道内核中申请或者Map的内存都是可读可写可执行的2. UserMode 用户模式下Map的地址同样具有读写属性,具体实现见MiMapLockedPagesInUserSpace,在LoadImage回调下这个函数有进程的AddressCreationLock限制,所以在模块回调时不要用UserMode!至少到目前为止的Windows版本都是可读写的。说到这里,我想到某厂的驱动开发人员写了这样一段代码,看的我哭笑不得
这个人即想把MDL映射到内核地址(MDL_MAPPED_TO_SYSTEM_VA
),又使用UserMode的映射....... 局外人啊。不过
这段代码不会出什么问题,因为MmMapLockedPagesSpecifyCache 还是先校验
AccessMode的,如果是UserMode就不会
看MDL_MAPPED_TO_SYSTEM_VA标记了,而且这个厂商用这个方法 Patch 动态库让动态库无法加载,实在让人深恶痛
绝,因为改了物理内存,所有进程都加载不了这个动态库了。而且从时间上的观察来看,这个厂商甚至不知道这些函数干了些啥,只知道这样可以获取内存的写权限......
- jpg改rar

Windows内存放血篇,突破物理内存的CopyOnWrite的更多相关文章
- 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件
本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...
- 全面介绍Windows内存管理机制及C++内存分配实例
转自:http://blog.csdn.net/yeming81/article/details/2046193 本文基本上是windows via c/c++上的内容,笔记做得不错.. 本文背景: ...
- Windows内存管理和linux内存管理
windows内存管理 windows 内存管理方式主要分为:页式管理,段式管理,段页式管理. 页式管理的基本原理是将各进程的虚拟空间划分为若干个长度相等的页:页式管理把内存空间按照页的大小划分成片或 ...
- windows内存体系结构 内存查询,读,写(附录源码)
“进程内存管理器”这个程序实现的最基本功能也就是对内存的读写,之前的两篇文章也就是做的一个铺垫,介绍了内核模式切换和IoDeviceControl函数进行的应用程序与驱动程序通信的问题.接下来就进入正 ...
- 【C/C++学院】0724-堆栈简单介绍/静态区/内存完毕篇/多线程
[送给在路上的程序猿] 对于一个开发人员而言,可以胜任系统中随意一个模块的开发是其核心价值的体现. 对于一个架构师而言,掌握各种语言的优势并能够运用到系统中.由此简化系统的开发.是其架构生涯的第一步. ...
- 第13章 Windows内存体系结构
13.1 Windows的虚拟地址空间安排 13.1.1虚拟地址空间的分区(即虚拟地址空间布局) 进程的地址空间划分 分区 x86 32位 Windows 3GB用户模式下的x86 32位Window ...
- windows内存映射学习及帮助类实现
本文通过创建文件内存映射类,学习windows内存映射相关知识:创建内存映射文件后,可以按照内存操作方式操作文件:支持32位程序处理超过4G大小的文件. 感谢http://blog.csdn.net/ ...
- Windows内存管理[转]
本文主要内容:1.基本概念:物理内存.虚拟内存:物理地址.虚拟地址.逻辑地址:页目录,页表2.Windows内存管理3.CPU段式内存管理4.CPU页式内存管理 一.基本概念1. 两个内存概念物理内存 ...
- Windows内存原理与内存管理
WIndows为每个进程分配了4GB的虚拟地址空间,让每个进程都认为自己拥有4GB的内存空间,4GB怎么来的? 32位 CPU可以取地址的空间为2的32次方,就是4GB(正如16位CPU有20根寻址线 ...
随机推荐
- Java如何使用catch来处理异常?
在Java编程中,如何使用catch块来处理异常? 此示例显示如何使用catch来处理异常. package com.yiibai; public class UseOfCatch { public ...
- Java如何显示小时和分钟?
在Java中,如何显示小时和分钟(当前时间)? 此示例演示如何使用Calender类的Calender.getInstance()来显示某个时刻的小时和分钟. package com.yiibai; ...
- Spring JDBC PreparedStatementSetter接口示例
org.springframework.jdbc.core.PreparedStatementSetter接口充当JdbcTemplate类使用的一般回调接口.该接口在JdbcTemplate类提供的 ...
- dendrogram 和 barplot 的组合
示例代码: data <- mtcars[1:10, ] hc <- hclust(dist(data)) hcd <- as.dendrogram(hc) par(mfrow = ...
- jquery选择器玩得不6啊,只能慢慢写判断了,唉..........................
jquery选择器玩得不6啊,只能慢慢写判断了,唉..........................
- HTML5 Canvas火焰效果 像火球发射一样
Canvas是HTML5中非常重要而且有用的东西,我们可以在Canvas上绘制任意的元素,就像你制作Flash一样.今天我们就在Canvas上来制作一款火焰发射的效果.就像古代的火球炮一样,而且可以在 ...
- linux中安装typecho的pathinfo配置
最近,我安装typecho,安装完之后发现,只有首页能够访问,其他的页面报404错误 后来发现时nginx默认情况下不支持pathinfo模式,于是我查找一下资料.终于得到解决. 我的nginx.co ...
- yii2 页面渲染方法解析
render渲染.renderPartial渲染部分.renderContent.renderAjax.renderFile ① render显示view和layout ② renderPartial ...
- 关于TensorFlow多种安装方式
Tensorflow的官网其实给出了很详细的安装教程,细分包括: Pip install: Install TensorFlow on your machine, possibly upgrading ...
- .net网站建设页面提交后css失效的问题
问题描述:.net网站建设在提交后出现css部分失效,如div位置,字体大小. 问题解决:原因是,过去的提示语句我们一律使用了Response.write("<script>al ...



