《windows核心编程》–Windows内存体结构(二)

13.6页面保护属性

    内存页面保护属性有 PAGE_NOACCESS、PAGE_READONLY、PAGE_READWRITE、PAGE_EXECUTE、PAGE_EXECUTE_READ、PAGE_EXECUTE_READWRITE、PAGE_WRITECOPY、PAGE_EXECUTE_WRITECOPY。这些保护分别表示什么http://127.0.0.1:47873/help/1-3788/ms.help?method=page&id=09839DB7-2118-4A7D-A707-A08C92BD600C&product=VS&productversion=100&locale=zh-CN&topiclocale=EN-US&topicversion=85&SQM=2

一些恶意软件将代码写入到用于数据的内存区域(比如线程栈上),通过这种方式让应用程序执行恶意代码。windows数据执行保护特性提供了对此类恶意攻击的防护。如果启用了DEP,那么只有对那些真正需要执行的代码的内存区域,操作系统才page_execute_*保护属性。其它保护属性(最常 见的就是PAGE_READWRITE)用于只应该存放数据的内存区域。

 

13.6.1 写时复制(PAGE_WRITECOPY PAGE_EXECUTE_WRITECOPY)

Windows支持一种机制,允许两个以上的质监共享同一块存储器。因此,如果有10个记事本程序正在运行,所有进程会共享应用程序的代码页和数据页。让所有的应用程序实例共享相同的存储页极大的提升了系统的性能。但另一方面,这要求应用程序只能读取其中的数据和代码。如果一个程序修改并写入一个存储页,那么这等于修改了其它实例正在使用的存储页,最终将导致混乱。

为了避免此类错误发生,操作系统会给共享存储页指定写时复制属性。当系统把一个exe或dll遇到 一个地址空间时,系统会计算有多少页是可以写的。

当线程试图写入一个共享页面时,系统会介入并执行以下操作

1. 系统在内存中找一个闲置页面。注意,该闲置页面的后备页面来自页交换文件,它是系统最初将模块映射到进程地址空间时分配的。由于 系统第一次进行映射的时候分配了所有可能需要的页交换文件空间,这一步不可能失败。

2.系统把线程想要修改的页面内容复制到第1步中找到的闲置页面。系统会给该闲置页面指定PAGE_READWRITE或PAGE_EXECUTE_READWRITE保护属性,系统不会对原始页面的保护属性和数据做任何修改。

3. 然后系统更新进程页面表,这样一来,原来的虚拟地址现在就对应到内存中的一个新的页面了

13.6.2  一些特殊的访问保护属性标志

使用这些标志时,只需将它们与除PAGE_NOACCESS之外的任何其它保护属性进行按位 或 操作就可以 了

PAGE_NOCACHE:禁止对已调拨页面进行缓存。不建议将该标志用于除驱动程序以外的程序

PAGE_WRITECOMBINE:也是给驱动开发人员用的。它允许把单个设备的多次写操作组合在一起。

PAGE_GUARD:使应用程序能够在页面中任何一个字节被写入时得到通知。

13.7 实例分析

以应用程序VMPMap.exe来测试

以上列出了一个地址空间映射的实例

  • 左边第一列:基地址
    从0x00000000区开始,到可用地址空间最后一个区域为止,最后一个区域的起始地址为0x7FFFe0000。所有区域都是连续的。读者可能还会注意到所有非闲置区域 的基地址都是64KB的整数倍。这是由系统地址空间分配粒度来决定的。如果一个区域的基地址不是64KB的整数倍,这意味着该区域是由操作系统以进程的名义分配的。
  • 第二列 区域类型:
    闲置:区域的虚拟地址没有任何后备存储器。该地址空间尚未预订,应用程序即可以从基地址开始预订区域,也可以从闲置区域的任何地方开始预订区域
    私有:区域虚拟地址以系统的页交换文件为后备存储器
    映象:区域的虚拟地址一开始以映象文件(比如.exe和.dll)为后备存储器,但此后不一定以心映象文件为后备存储器。例如:如果程序写入一个映象文件中的一个全局变量,那么写时复制机制会改用页交换文件来作为后备存储器。
    已映射:区域虚拟地址一开始以内存映射文件为后备存储器,但此后不一定以内存映射文件为后备存储器。例如:内存映射文件可能会使用写时复制保护属性。任何编写操作会使对应的页面改用页交换文件为后备存储器。
  • 第三列 预订字节数
    此列始终是CPU页面大小的整数倍。为了节省磁盘空间,链接器会尽可能的对所生成的PE文件进行压缩。但是当windows将PE文件映射到进程虚拟地址空间时,每 一段必须另起一页,而且起始地址必须是系统页面大小的整数倍。这意味着PE文件所需的虚拟地址空间的大小一般比PE 文件本身的大小
  • 第四列 所预订区域内块的数量
    块是一些连续的页面,这些页面具有相同的保护属性,并且以相同的物理存储器为后备存储器
  • 第五列 区域保护属性
    E = execute R = read W = write
  • 第六列 描述

13.8 数据对齐的重要性

当访问已对齐的数据时,CPU的执行效率才最高。把数据的地址模除数据的大小,如果结果为0,那么数据就是对齐的。例如:一个WORD值的起始地址应该能被2整除,一个DWORD的起始地址应该能被4整除,以此类推。

下面的代码访问了一个错位的数据

VOID SomeFunc(PVOID pvDataBuffer)
{
    //the first byte in the buffer is some byte of information
    char c = * (PBYTE) pvDataBuffer;
   
    //increase past the first byte in the buffer
    pvDataBuffer = (PVOID) ((PBYTE)pvDataBuffer + 1);
   
    //bytes 2 - 5 contain a double-word value
    DWORD dw =  * (DWORD * ) pvDataBuffer;
   
    //the line above raise a data misalignment exception on some CPUS
}

 

CPU处理数据对齐的方式 :

  • x86:
    x86 CPU的EFLAGE寄存器内有一个AC标志。默认为0。如果为0,那么CPU会自动执行必要的操作来访问必要的数据,否则CPU就会触发INT 17H中断 。由于  x86  CPU从来不改变这个标志,因此应用程序在X86 运行时从来不会发生数据错位异常。当应用程序在AMD x86-64处理器上运行时,会有相同的结果。这是因为在默认情况下CPU处理了数据错位的错误。
  • IA-64:
    IA-64 CPU不能自动处理数据错位的错误。当任何代码要访问错位数据时,CPU会通知操作系统。Windows然后决定到底是应该抛出数据错位异常,还是应该没有任何提示的执行额外指令来修正错误并让代码继续执行。在IA-64操作系统里,window会自动将将数据错位错误转化成一个EXCEPTION_DATATYPE_MISALIGNMENT异常。但是我们可以通过SetErrorMode来改变这种行为。注意改变这个标志会影响到进程所有的线程,另外值得注意的一点是这个标志会被子进程继承。IA-64版本的vc++ 编译器支持一个__unaligned关键字。下面的代码是前面的代码经过修改代码的版本。新版使用了__unaligned关键字

    VOID SomeFunc(PVOID pvDataBuffer)
    {
        //the first byte in the buffer is some byte of information
        char c = * (PBYTE) pvDataBuffer;
       
        //increase past the first byte in the buffer
        pvDataBuffer = (PVOID) ((PBYTE)pvDataBuffer + 1);
       
        //bytes 2 - 5 contain a double-word value
        DWORD dw =  * (__unaligned DWORD * ) pvDataBuffer;
       
        //the line above raise a data misalignment exception on some CPUS
    }

时间: 2024-07-30 01:44:14

《windows核心编程》–Windows内存体结构(二)的相关文章

《windows核心编程系列》二谈谈ANSI和Unicode字符集 .

http://blog.csdn.net/ithzhang/article/details/7916732转载请注明出处!! 第二章:字符和字符串处理     使用vc编程时项目-->属性-->常规栏下我们可以设置项目字符集合,它可以是ANSI(多字节)字符集,也可以是unicode字符集.一般情况下说Unicode都是指UTF-16.也就是说每个字符编码为两个字节.65535个字符可以表示世界上大部分的语言.为了软件使国际化大家再编程时应该使用unicode字符集.由于原来学过c语言,不习惯

018_《Delphi下深入Windows核心编程》

<Delphi下深入Windows核心编程> Delphi 教程 系列书籍 (018) <Delphi下深入Windows核心编程> 网友(邦)整理 EMail: shuaihj@163.com 下载地址: Part1 Part2 Part3     书名: Delphi下深入Windows核心编程 作者: 飞思科技产品研发中心 出版社: 电子工业出版社 书号: 7505384023 出版日期:2003年1月 开本: 787*1092 1/16 页码: 525 版次: 2003年1

《windows 核心编程》 -探索虚拟内存

14.1 系统信息 操作系统中有许多值 是由系统所运行的主机所决定的.如果页面大小和分配粒度等.我们决对不应该在代码中将这些值写死. 此函数得到系统信息VOID GetSystemInfo(LPSYSTEM_INFO ps) 如果想得到机器中与处理器有关的详细信息可以调用GetLogicalProcesorInfomation函数 为了让32位应用程序在64位版本的Windows运行,Microsoft提供了一个称为windows 32 bit On Windows 64 的模拟层又称为WOW.

请教windows核心编程 ErrorShow程序例子问题

问题描述 请教windows核心编程 ErrorShow程序例子问题 刚刚学习win32 有个问题TCHAR buffer[100]; case WM_PAINT:{ hdc = BeginPaint (hwnd &ps) ; DWORD systemLocale = MAKELANGID(LANG_NEUTRAL SUBLANG_NEUTRAL); DWORD dwError = 1; HLOCAL hlocal = NULL; BOOL fOk = FormatMessage( FORMAT

[原创/讨论][windows核心编程一外传]关于访问虚拟地址0的方法。

接上一篇 Windows 核心编程研究系列之一(改变进程 PTE) 内容 上一篇观赏地址 :http://community.csdn.net/Expert/topic/5124/5124747.xml?temp=.2832453 当然系统保证不让访问地址0出于一种保护的目的,是防止未初始化的指针读取数据.我说的访问地址0只是出于一种纯实现的目的,也不提倡大家这样做.说白了只是好玩罢了. 大家都知道在 windows 中读取/写入地址0的指令肯定会出错: // 写 0 地址的内容xor edx,

Windows 核心编程研究系列之一(-改变进程PTE属性-)[已补完]

  Windows 核心编程研究系列之一 -改 变 进 程 PTE 属性-              这是我研究windows 核心编程的第一篇正式文章,之所以叫核心编程而不叫内核编程,是我觉得从字面上来看核心(core)比内核(kernel)更靠近windows中心,当然只是偶本人的看法的拉.          我们知道在 win NT 中,系统把每个进程的虚拟4G空间分为两大部份,低2G归用户所有,高2G归系统所有.用户不得访问系统的空间,连读都不行,更别说写了!低2G的用户空间也并不是都能

chHANDLE_DLGMSG(windows核心编程)讲解

看完<Windows程序设计>后开始看<windows核心编程>, 结果看第一个案例的时候就很惊人的发现,Jeffery大牛的代码很深奥.乍一看好像没有包含<windows.h>. 看看包含的头文件发现,CmnHdr.h中已经包含了<windows.h>.而CmnHdr.h中的代码更吓人,如果没有讲解,不知道怎么看才好.后来才知道原来书的最后有专门的搭建环境的介绍,基本上全面的讲解了CmnHdr.h的东西.   CmnHdr.h中包含了大牛的很多自己的东西.

《windows核心编程》–Windows内存体结构(一)

13.1 进程虚拟地址空间 每个进程都有自己的专有地址空间,对32位进程来说,这个地址空间的大小为4GB,这是因为32位指针可以表示从0x00000000-0xffffffff的任意值.对64位进程来说则可以表示0x0000000000000000 到0xffffffffffffffff之间任一值. 因为每一个进程都有自己的专有地址空间,当进程中的各线程运行时,它们只访问属于该进程的内存.线程即看不到其他进程内容空间,也无法访问它们. 进程A可以在位于它地址空间内的0x12345678地址处存储

《windows核心编程》 17章 内存映射文件

内存映射文件主要用于以下三种情况: 系统使用内存映射文件载入并运行exe和dll,这大量节省了页交换文件的空间以及应用程序的启动时间 开发人员可以使用内存映射文件来访问磁盘上的数据文件.这使得我们可以避免直接对文件IO操作和对文件内存进行缓存 进程间通讯 17.1 映射到内存的可执行文件和DLL 当一个线程调用CreateProcess的时候,系统会执行收入步骤: 1.判断exe位置,如果无法找到exe那么不会创建进程,这时会CreateProcess返回FALSE 2.创建一个新的进程内核对象