接下来我们将做一个实验,使用OD调试器动态分析已经加载到内存中的PE文件。
实验五:动态分析32位PE文件
使用DTDebug调试器随便打开一个进程,我们以一个32位记事本程序为例,看一下它的低2GB(0~2G)内存空间的分布。
提示
DTDebug.exe调试器是昆山滴水信息技术有限公司开发的一个具有R3访问权限的调试器,可以动态调试分析具有R3权限的普通应用程序。可以在编程达人官网www.bcdaren.com免费下载。
使用DTDebug调试器将一个普通32位PE文件(R3访问权限)加载到内存后,我们只可以观察到0~2GB的虚拟内存空间。
将notepad32.exe拖入调试器,点击工具栏“M”按钮,打开内存映射窗口,如图2-15所示:
图2-15 动态分析32位记事本
先从整体观察,内存映射窗口显示的虚拟地址范围刚好是低2GB的用户空间。用户空间的前后64KB内存为空,不可访问。因为在32位操作系统的用户空间(低2GB)中,通常会保留一些特殊的区域,其中包括前后各64KB的空间被称为“guard pages”(保护页),它们是不可访问的。这些保护页的存在是为了提高程序的安全性和稳定性。
保护页的作用如下:
●安全性: 保护页的存在可以防止用户进程越界访问。如果用户进程意外或恶意地访问了这些保护页,操作系统会检测到该错误并触发访问异常,使进程崩溃或被终止,从而保护系统的安全性。
●调试和异常处理: 保护页还可以用于调试和异常处理。当用户进程发生异常(如访问无效的内存地址)时,操作系统可以使用这些保护页来收集有关异常的信息,并进行相应的调试和处理操作。
●内存保留: 保护页也可以用于保留一些内存空间,以备将来使用。这样,操作系统可以根据需要动态地分配和管理这些空间,而不需要将其分配给用户进程。
【注意】保护页的大小通常是根据操作系统和硬件架构而定的,并不局限于64KB。这个大小可能会因不同的系统而有所不同。
低2GB虚拟内存空间的分配如表2-3所示:
虚拟内存地址范围 | 功能 |
0x0~0xFFFF | 拒绝访问区域,用于帮助程序员避免引用错误的指针;试图访问这个区域地址将会导致访问越权。 |
0x10000~0x7FFEFFFF | 进程专用地址空间。 |
0x7EFDE000~0x7EFDEFFF | 用于第一个线程的线程环境块(TEB)。系统会在这一页的前面创建附加的TEB(从地址0x7FFDD000开始向上)。每创建一个线程都会在此区间分配一个线程环境块。 |
0x7FFDF000~0x7FFDFFFF | 进程环境块(PEB)。 |
0x7FFE0000~0x7FFE0FFF | 共享的用户数据页,这个只读方式的页面被映射到系统空间中包含系统时间、时钟计数和版本号信息的一个页面。这个页面的存在使数据中用户态下可以直接读取而不必请求内核态的转换。 |
0x7FFE1000~0x7FFEFFFF | 拒绝访问区域(共享用户数据页面以后剩余的64KB)。 |
0x7FFF0000~0x7FFFFFFF | 拒绝访问区域,用于防止线程跨越用户/系统空间边界传送缓存区。在变量MmUserProbeAddress中包含此页的起始地址。 |
表2-3 用户态低2GB空间分配
■进程专用地址空间
如果我们加载一个PE文件到内存,则该PE文件应该在进程专用地址空间0x10000~0x7FFEFFFF内。对照一下调试器的内存映射窗口,notepad32.exe程序位于该进程专用地址空间内。在进程专用地址空间内,分别用于系统动态链接库、堆区和用户创建的动态链接库和exe区域。用户创建的动态链接库和exe区域即我们要加载的PE文件。
●系统动态链接库
系统dll动态链接库加载到进程专用地址空间的高地址处。第一个映射加载的dll一定是ntdll.dll动态链接库,最后一个是winspool.drv。winspool.drv 是一个Windows操作系统中的系统文件,它是与打印机和打印相关的驱动程序文件。这个文件提供了一组用于与打印机设备进行通信和管理的函数和接口。
为什么要第一个加载ntdll.dll呢?这是因为其他系统dll对ntdll.dll具有依赖性。ntdll.dll 是一个重要的系统文件,它是Windows操作系统的核心组件之一。该文件提供了操作系统内部函数和接口,用于支持和管理各种系统级操作和功能。
以下是一些 ntdll.dll 的主要功能和作用:
1.系统调用(System Calls): ntdll.dll 包含了操作系统的系统调用接口。系统调用是应用程序与操作系统内核之间进行交互的一种机制,允许应用程序请求操作系统提供的服务和资源。这些服务可以涉及文件操作、进程管理、内存管理、网络通信等。
2.进程和线程管理: ntdll.dll 提供了与进程和线程管理相关的函数和接口。它包括创建和终止进程、创建和管理线程、访问进程和线程的上下文信息等功能。这些功能对于操作系统的正常运行和应用程序的执行至关重要。
3.内存管理: ntdll.dll 提供了与内存管理相关的函数和接口,用于分配、释放和管理系统内存资源。它包括虚拟内存管理、页面文件管理、内存映射文件等功能,帮助操作系统有效地管理内存资源。
4.异常处理和错误处理: ntdll.dll 包含了异常处理和错误处理的函数和接口。当应用程序出现异常或错误时,操作系统会使用这些函数来处理并提供相应的错误信息、记录日志等。
5.安全和访问控制: ntdll.dll 实现了许多与系统安全和访问控制相关的功能。它包括用户身份验证、权限管理、文件和对象的安全性等。这些功能确保了系统的安全性和数据的保护。
注意
1.ntdll.dll 是一个系统级的库文件,主要供操作系统内部使用。它并不是为应用程序开发人员直接调用的库文件,而是作为操作系统的核心组件提供支持和功能。
2.这些系统dll中同一个Windows操作系统版本中,加载的地址通常都是固定分配的。如果程序需要考虑不同操作系统版本间的兼容,则必须动态的获取系统dll的起始地址。
●PE映像文件区
PE映象文件区即我们加载的exe和dll区域。如图1-3所示,本例32位记事本程序只有一个exe模块。加载到内存中的notepad.exe分为4个区块,如表2-4所示。
起始地址 | 大小 | 属于 | 节区 | 包含 | 类型 | 访问权限 | 初始 权限 |
0x01000000 | 1000h | notepad32 | PE头 | Image | R | RWE | |
0x01001000 | 8000h | notepad32 | .text | 代码 | Image | R | RWE |
0x01009000 | 2000h | notepad32 | .data | 数据 | Image | R | RWE |
0x0100B000 | 8000h | notepad32 | .rsrc | 资源 | Image | R | RWE |
表2-4 notepad.exe内存分区
如果要查看节区的内容,双击就可以了 如图2-16所示:
2-16 查看.data节区
除了默认的PE头之外,每个节区都是有命名的,例如.text节区为代码段,.data节区为数据段,.rsrc节区保存资源。双击打开节区内容窗口后,我们还可以选择不同的查看方式,以数据、文本或反汇编代码的形式查看内容。如图2-17所示:
图2-17 以反汇编形式查看.text节区
这些节区的内容就是磁盘上PE文件对应的节区加载到低2GB虚拟空间中的内容,包括代码段、数据段和资源。
●堆区
在进程专用地址空间中,除了系统DLL区域和PE文件映射区域,剩下的空间就是可以被利用的堆区。如果要利用这些剩余的空间,我们可以调用API函数HeapAlloc申请使用。它用来在指定的堆上分配内存,并且分配后的内存不可移动。申请成功之后,这个空间就变成了一个私有的内存空间。利用完之后,调用API函数HeapFree释放这个私有堆空间。我们观察一下内存映射窗口,在.rsrc节区下方,从0x03020000地址开始,已经分配了1000h(4KB)大小的私有堆空间,页属性为RW(可读可写),内容为空。如图2-18所示:
图2-18 私有堆空间
进程专业地址空间中其余空白的内存空间为尚未分配的堆空间。堆空间的大小对于每个进程来说都是不一样的,最大不会超过2GB,因为其仅为进程专用地址空间中除PE映像文件和系统DLL之外的剩余空间。
提示
在 32 位 Windows 中,可以在启动时指定大于 2 GB的用户空间。 但是,这意味着可用于系统空间的虚拟地址更少。 可以将用户空间的大小增加到 3 GB,系统空间仅保留 1 GB。 若要增大用户空间的大小,请使用 BCDEdit /set increaseuserva。
BCDEdit/set 命令在 Windows 启动配置数据存储 (BCD) 中设置启动项目选项值。 使用 BCDEdit /set 命令可配置特定的启动项目元素,如内核调试程序设置、内存选项或启用测试签名的内核模式代码或负载备用硬件抽象层 (HAL) 的选项和内核文件。 若要删除启动项目选项,请使用 BCDEdit/deletevalue 命令。
【注意】需要管理权限才能使用 BCDEdit 来修改 BCD。 使用 BCDEdit /set 命令更改某些启动项目选项可能导致计算机无法运行。 或者,使用启动设置或系统配置实用工具 (MSConfig.exe)更改启动设置。
【备注】 设置 BCDEdit 选项之前,可能需要禁用或暂停计算机上的 BitLocker 和安全启动。
●已加载的模块列表和和内存列表的区别:
1. 内存列表是相同属性放在一起,模块列表是很多模块块放在一起了,也就是说模块里面没有的那些内存都是调用VirtualAlloc分配的。如果说是有模块的那段内存都是在磁盘上有对应的EXE或DLL。
2. 有模块就是有对应的磁盘PE文件。
3. 一个EXE 或DLL在磁盘中叫做文件,在内存中叫模块。
4. 每个模块都有一个格式就是一个结构体,Windows把它叫做PE。
实验六:动态分析64位PE文件
使用x64dbg调试器随便打开一个进程,我们以一个64位记事本程序为例,看一下它的用户内存空间的分布。
将notepad64.exe拖入调试器,点击工具栏“内存布局”按钮,打开内存映射窗口,如图2-19所示:
图2-19 动态分析64位记事本
X64 CPU 仅支持 64 位虚拟地址中的 48 位,这 48 位虚拟地址被运行在该 CPU 上的软件使用。 对于用户模式地址,64 位虚拟地址中的高 16 位总是被设置为 0x0;对于内核模式地址,总是设置为 0xF。
这有效地将 X64 地址空间分开成2部分——用户模式地址的范围:0x00000000`00000000~0x0000FFFF`FFFFFFFF;内核模式地址的范围:0xFFFF0000`00000000~0xFFFFFFFF`FFFFFFFF。
此内核虚拟地址范围总计为 256 TB,用于 Windows 上可访问的全部内核虚拟地址空间。然后,Windows 静态划分此空间成多个固定大小的虚拟地址范围(VA),每个范围被赋予特定用途。每个范围的起始和结束地址如下表所示:
表2-5 64位Windows系统内核空间分布
在64位Windows操作系统中,用户空间的内存分布地址区间通常如下所示:
用户代码段(User Code Segment):0x0000000000000000 - 0x00007FFFFFFFFFFF
用户数据段(User Data Segment):0x0000000000000000 - 0x00007FFFFFFFFFFF
用户堆(User Heap):通常位于用户数据段的高地址部分,具体地址取决于应用程序的运行时动态分配。
用户栈(User Stack):通常位于用户空间的高地址部分,具体地址取决于线程的栈大小和分配方式。
用户共享库(User Shared Libraries):位于用户空间的不同地址范围,具体地址取决于库的加载和地址分配。
用户未分配空间(User Unallocated Space):0x0000800000000000 - 0x7FFFFF8000000000
需要注意的是,这只是一种常见的内存分布情况,实际的地址区间可能因操作系统版本、应用程序配置和其他因素而有所不同。此外,用户空间的具体内存分布还受到地址空间隔离、虚拟内存管理和安全保护机制等因素的影响。
总结起来,Windows 64位程序的用户空间内存分布地址区间通常从0x0000000000000000到0x00007FFFFFFFFFFF,其中包括用户代码段、用户数据段、用户堆、用户栈、用户共享库和用户未分配空间。
如图1-19所示,64位程序notepad64.exe加载到虚拟内存00007FF6E2390000~00007FF6E23D0FFF之间,包含6个节区,分别为.text、.rdata、.data、.pdata、.rsrc、.reloc。其中。.text节区为可执行代码,大小为19*4KB个字节,页属性为“ER”(可读可执行)。.data节区大小为3*4KB,页属性为RW(可读可写)。
用户共享系统动态链接库位于映像文件模块下方高地址处。映像文件模块上方低地址处分配了线程栈、线程环境块、PEB进程环境块。用户空间剩余部分为保留的未分配空间。
实验七:分析多种不同格式的文件
请读者用winhex分别打开不同的文件格式。比如: .exe .txt .doc .jpg .ink .htm .dll .pdf 等等 打开10个 看看前四个字节是什么,将其记录下来。
实验方法:
1. 将文件拖入WinHex,在WinHex里面将鼠标拖动到最后,观察文件的大小,然
如图2-20所示:
图2-20 使用WinHex观察文件大小
2.鼠标选中磁盘文件,点击鼠标右键“属性”,查看磁盘文件大小,如图2-21所示:
图2-21 查看磁盘文件大小
我们会发现,在WinHex中观察到的磁盘文件大小和文件属性中的大小是一样大的。
大家发现没有,相同的文件前面几个字节是一样的。
.doc .mp3 .jpg等等都是有文件格式的,我们只需要关心.exe和.dll文件格式,因为.exe和.dll文件是可执行文件格式,其他的文件是不可执行的,不能在CPU上运行的。每一种操作系统它最重要的格式就是它的可执行文件格式,因为操作系统就是为了支持这些文件而生成的,内核里面有很多机制,也是配合这种文件格式设计的。换句话说,这种文件格式也是配合操作系统设计的。
比如: PE 它是windows下的文件格式,是MZ打头的(4D5A)只有两个字节,后面很大一片就是对这个结构体的管理,比如代码在什么位置,图像在什么位置,文字在什么位置,在前面PE头中都是有记录的。也就是说,前面的PE头部分不仅仅是一个标志,而是一个结构体。不同的文件有不同的结构体,本书的内容就是带着大家一起学习Windows下PE文件格式。
目前为止,我们已经对32位和64位PE文件有了一个大致的了解。剩下的事情就是详细了解PE文件各个部分的细节。诸如PE头中的结构体具体包含哪些内容呢?我们将在下一章中详细讲解。