读取物理内存
作者:admin 日期:2009-10-25
大家知道在windows NT中,如果已知虚拟地址可以通过进程页表或者内核提供的MmGetPhysicalAddress来取得对应的物理地址。现在我们反过来看一下,如果已知一个物理内存地 址 (假设地址有效),如何取得物理地址中的内容呢?经过一番思考后,我发现一个方法,但我这里先卖个关子,为什么呢?因为我觉得这个方法有些繁琐。经过和csdn的各位高手讨论之后,加上本人的一共总结出3种方法,现在想和大家分享一下,下面不再废话,立即进入正题 J[方法1]:使用内核提供的MmMapIoSpace函数原来内核早就提供了很简单的接口,就是MmMapIoSpace函数,不过在DDK文档中看到该函数的说明如下:PVOID MmMapIoSpace( IN PHYSICAL_ADDRESS PhysicalAddress, IN ULONG NumberOfBytes,IN MEMORY_CACHING_TYPE CacheType );它只有3个形参,这和实际Masm32v9.0声明的4个形参有所不同,为了确定,我通过 IDA 反汇编证实该函数确实有4个形参。经过测试,我猜测中间的ULONG NumberOfBytes参数实际由64位字节的(两个双字)高低两部分组成,即在Masm32v9.0中有:PVOID MmMapIoSpace( IN PHYSICAL_ADDRESS PhysicalAddress,IN ULONG NumberOfBytesHighPart,IN ULONG NumberOfBytesLowPart,IN MEMORY_CACHING_TYPE CacheType );这样的话调用就很简单了:invoke MmMapIoSpace,_phyaddr,0,4,MmNonCached若成功该函数返回影射后的虚拟地址,否则返回NULL。这样就可以间接达到读取物理地址中内容的第一个方法。但可能我对参数功能的猜测也有错误,如果有误请指出。不胜感谢。[方法2]:通过操作物理内存对象来完成到虚拟地址的影射简单的说这个方法的基本思想如下:a : 取得本进程句柄;b : 取得物理内存对象的句柄;c : 将物理内存地址影射到进程的虚拟地址空间中。下面是我从C改写后的汇编关键代码:;***************************************************************************GetValByPhyAddr proc uses esi edi ebx _phyaddr local hpmem:dword local hp:dword local dwsize:dword local dwbase:dword local dwret:dword local cid:CLIENT_ID local stLR:LARGE_INTEGER local ObjAttrsMem:OBJECT_ATTRIBUTES local ObjAttrsP:OBJECT_ATTRIBUTES mov dwbase,0 mov dwsize,4 push _phyaddr pop stLR.LowPart mov stLR.HighPart,0 invoke PsGetCurrentProcessId mov cid.UniqueProcess,eax mov cid.UniqueThread,0 invoke InitObjAttrs,addr ObjAttrsP,0 ;取得本进程句柄 Invoke ZwOpenProcess,addr hp,PROCESS_DUP_HANDLE,\ addr ObjAttrsP,addr cid invoke InitObjAttrs,addr ObjAttrsMem,addr devphymem ;取得物理对象的句柄 Invoke ZwOpenSection,addr hpmem,\ SECTION_MAP_READ or SECTION_MAP_WRITE,\ addr ObjAttrsMem ;将物理内存地址影射到进程的虚拟地址空间中 invoke ZwMapViewOfSection,hpmem,hp,addr dwbase,\ 0,4,addr stLR,addr dwsize,1,\ MEM_TOP_DOWN,PAGE_READWRITEmov edx,dwbase mov eax,[edx] ;取得影射后对应虚拟地址中的内容 mov dwret,eax ;释放资源 invoke ZwUnmapViewOfSection,hp,addr dwbase invoke ZwClose,hpmem invoke ZwClose,hp mov eax,dwret retGetValByPhyAddr endp;***************************************************************************[方法3]:关闭CPU分页机制达到直接读取物理地址的目的第3种方法就是我前面卖关子的方法 J ,这种方法不依赖于OS,了解保护模式的朋友都知道:进入保护模式后如果关闭分页机制则CPU就会将线形地址直接解释成物理地址。关闭分页很简单,只需3行汇编代码:mov eax,cr0and eax,7fffffffhmov cr0,eax打开分页同样很简单:mov eax,cr0or eax,80000000h mov cr0,eax既然这么简单,为什么我在前面说这个方法有些繁琐呢?原因之一是如果仅仅关闭分页就会使原来依赖于分页的windows变得一团糟,马上会发生著名的蓝屏现象,不爽哦!L那要怎么做呢?经过我的尝试,发现有3点非常重要:1 : 要保证windows处在较高的IRQL级别,防止关闭分页后 被打断;2 : 要将处理分页、读取物理地址的指令块放到线形地址等于 物理地址的页中;3 : 要做好数据的恢复工作。第1点和第3点都容易理解,可能有人会疑问第2点,为什么要放到线形地址等于物理地址的空间中呢?原因其实也很简单,就是一旦关闭分页,所有地址都将以物理地址来解释,如果先前的虚拟地址和物理地址不相同那么很可能就会发生执行错误指令流的问题。(当然也可以不相同,只要你能保证关分页后能执行正确指令即可。)那么如何将物理地址和虚拟地址设置为相同呢?我的方法是将虚拟地址对应地页表PTE中的物理页基址改为包容即可。我随机选择了一个虚拟地址 0x1c00000 ,为了简单它正好是页面的一个基址。下面我构造了它的PTE使得其中的物理也帧指向它:mov edx,0ffdf0000h mov eax,[edx] mov tmp0,eax ; 保存原始值 ;mov dword ptr [edx],1c00027h mov dword ptr [edx],1c00007h ;初始化PTE mov edx,0c03ff7c0h mov eax,[edx] and eax,0fffff000h or eax,7h mov edx,0c030001ch mov ebx,[edx] mov tmp1,ebx ; 保存原始值 mov dword ptr [edx],eax ;刷新TLB mov eax,cr3 mov cr3,eax 最后两句指令在我前一篇: <<Windows内核编程研究一:改变进程PTE>> 中有过说明,这里就不解释了。结束了PTE的设置后,下面就是将指令块拷贝到指定位置,代码如下:mov ecx,slenmov esi,subasmmov edi,1c00000hcldrep movsb而subasm指令块完成的就是实际设置分页和读取物理地址内容的工作,如下:subasm: mov eax,cr0 and eax,7fffffffh ;关闭分页机制 mov cr0,eax jmp SHORT PageCPageC: mov edx,[ecx]mov eax,cr0 or eax,80000000h ;打开分页机制 mov cr0,eax jmp SHORT PageOPageO: jmp edislen = $ - subasm借用一句话作为结尾“…一切都那么清楚明白,然而却无法透彻理解。理解就是改变,就是超越自己已有的认识。”
评论: 0 | 查看次数: 8445