文章目录
- 声明
- 初识PE
- PE文件中的对齐
- 16系统下的PE结构
- 32位系统下的PE结构
- PE文件头部解析
- 数据结构字段详解
- IMAGE_FILE_HEADER的字段详解
- IMAGE_OPTIONAL_HEADER32的字段详解
-
- IMAGE_OPTIONAL_HEADER32.Magic
- IMAGE_OPTIONAL_HEADER32.MajorLinkerVersion
- IMAGE_OPTIONAL_HEADER32.MinorLinkerVersion
- IMAGE_OPTIONAL_HEADER32.SizeOfCode
- IMAGE_OPTIONAL_HEADER32.SizeOfInitializedData
- IMAGE_OPTIONAL_HEADER32.SizeOfUninitializedData
- IMAGE_OPTIONAL_HEADER32.AddressOfEntryPoint
- IMAGE_OPTIONAL_HEADER32.BaseOfCode
- IMAGE_OPTIONAL_HEADER32.BaseOfData
- IMAGE_OPTIONAL_HEADER32.ImageBase
- IMAGE_OPTIONAL_HEADER32.SectionAlignment
- IMAGE_OPTIONAL_HEADER32.FileAlignment
- IMAGE_OPTIONAL_HEADER32.MajorOperatingSystemVersion
- IMAGE_OPTIONAL_HEADER32.MinorOperatingSystemVersion
- IMAGE_OPTIONAL_HEADER32.MajorImageVersion
- IMAGE_OPTIONAL_HEADER32.MinorImageVersion
- IMAGE_OPTIONAL_HEADER32.MajorSubsystemVersion
- IMAGE_OPTIONAL_HEADER32.MinorSubsystemVersion
- IMAGE_OPTIONAL_HEADER32.Win32VersionValue
- IMAGE_OPTIONAL_HEADER32.SizeOfHeaders
- IMAGE_OPTIONAL_HEADER32.CheckSum
- IMAGE_OPTIONAL_HEADER32.Subsystem
- IMAGE_OPTIONAL_HEADER32.DllCharacteristics
- IMAGE_OPTIONAL_HEADER32.SizeOfStackReserve
- IMAGE_OPTIONAL_HEADER32.SizeOfStackCommit
- IMAGE_OPTIONAL_HEADER32.SizeOfHeapReserve
- IMAGE_OPTIONAL_HEADER32.SizeOfHeapCommit
- IMAGE_OPTIONAL_HEADER32.LoaderFlags
- IMAGE_OPTIONAL_HEADER32.NumberOfRvaAndSize
- IMAGE_OPTIONAL_HEADER32.DataDirectory
- IMAGE_DATA_DIRECTORY
- 节表项IMAGE_SECTION_HEADER的字段
-
- IMAGE_SECTION_HEADER.Name1
- IMAGE_SECTION_HEADER.Misc
- IMAGE_SECTION_HEADER.VirtualAddress
- IMAGE_SECTION_HEADER.SizeOfRawData
- IMAGE_SECTION_HEADER.PointerToRawData
- IMAGE_SECTION_HEADER.PointerToRelocations
- IMAGE_SECTION_HEADER.PointerToLinenumbers
- IMAGE_SECTION_HEADER.NumberOfRelocations
- IMAGE_SECTION_HEADER.NumberOfLinenumbers
- IMAGE_SECTION_HEADER.Characteristics
声明
以下皆为手打,并非复制粘贴,一方面是为了自己以后复习,另一方面希望能方便大家。
初识PE
PE(Portable Executeable File Format,可移植的执行体文件格式),使用该格式的目标是使连接生成的EXE能在不同的CPU工作指令下工作。
可执行文件的格式是操作系统工作方式的真实写照。Windows操作系统中可执行程序有好多种,比如COM,PIF,SCR,EXE等,这些文件的格式大部分都继承自PE。其中,EXE是最常见的PE 文件,动态链接库(大部分以dll为扩展名的文件)也是PE文件
内存映射文件与PE内存映像
内存映射文件:是指将硬盘上的文件不做修改地装载到内存中。这样,文件中字节与字节之间就是顺序排列的了。
解释:
在硬盘上,文件被分割成若干簇,这些簇不一定会按照文件内容顺序排列在一起,当我们访问磁盘上的文件时,需要计算机首先将不同位置的内容读取到内存。有了内存映射文件,访问就会变得更轻松和快捷,由于读取磁盘的操作集中到一起
执行,读写效率会提高很多。被一次性读取到内存的文件字节按线性排列,访问相对简单,速度也提升了不少。
PE内存映像:是指将PE文件按照一定的规则装载到内存中,装入后的整个文件头内容不会发送变化,但PE文件的某一部分如节的内容会按照字段中的
对齐方式在内存中对齐,从而使得内存中的PE映像与装载前的PE文件不同。
为什么PE内存映像不能和一般的内存映射文件一样呢?
因为PE文件是由操作性装载进内存的,其目的是为了运行。为了配合操作系统的运行,方便调度,提高运行效率,PE映像必须按照一定的格式对齐,所以内存中的PE映像和原来硬盘上的文件时不同的,当然与内存映射文件也就不同。
PE文件中的对齐
数据在内存中的对齐
由于Windows操作系统对内存属性结构的设置以页为单位,所以通常情况下,节在内存中的对齐单位必须至少是一个页的大小。对32位的Windows XP操作系统来说,这个值是4KB(1000h
);而对于64位操作系统来说,这个值就是8KB(2000h
)。
数据在文件中的对齐
为了提高磁盘利用率,通常情况下,定义的节在文件中的对齐单位要远小于内存对齐单位;通常会以一个物理扇区的大小作为对齐粒度的值,即512字节,十六进制是200h
处于节约资源考虑,操作系统允许节在内存和文件中的对齐尺度不一致。这就直接造成PE在文件中和在内存中的大小也会不一致。通常情况下,PE在内存中的尺寸要比在文件中的尺寸要大。用户可以自定义这些对齐的值。
注意:
如果内存对齐被定义为小于操作系统页的大小,则文件对齐和内存对齐的值必须一致
资源文件中资源数据的对齐
资源文件中,资源字节码部分一般会要求以双字(4个字节)方式对齐,在资源表中详解。
在PE格式中,每一个大的部分的对齐方式就是按照200h
大小对齐的
在汇编语言中,Unicode字符串被定义为一个结构体,定义如下:
typedef struct _NUICODE_STRING{
USHORT Length; //字符串的长度(字节数)
USHORT MaximumLength;//字符串缓冲区的长度(字节数)
PWSTR Buffer; //字符串缓冲区
} UNICODE_STRING , *PUNICODE_STRING;
16系统下的PE结构
DOS头部分的存在见证了PE强大兼容性。为了保持与16位系统的兼容,在PE里依旧保留了16位系统下的标准可执行程序
执行时所必需的文件头部(DOS MZ头)和指令代码(DOS Stub)。
在16位操作系统下,PE结构可以分为两部分:DOS头和冗余数据,如下
在16位系统下,PE 的四部分内容被重新组合成两部分——可以在16位系统下运行的DOS头和冗余数据。把Windows下的PE文件存储到DOS系统并运行,它就是DOS系统下的一个EXE文件。
DOS头分为两部分,DOS MZ头和DOS Stub(即指令字节码)。大部分情况下,这些指令实现的功能都非常简单,根本不会涉及重定位信息。再往后的PE头和PE数据区可以看成是16操作系统下的可执行文件的冗余数据。
DOS MZ 头(长度0x40
)
13B7:0100 4D 5A 90 00 03 00 00 00-04 00 00 00 FF FF 00 00 MZ..............
13B7:0110 B8 00 00 00 00 00 00 00-40 00 00 00 00 00 00 00 ........@.......
13B7:0120 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
13B7:0130 00 00 00 00 00 00 00 00-00 00 00 00 B0 00 00 00 ................
在Windows的PE格式中,DOS MZ
头定义如下:
IMAGE_DOS_EADER STRUCT
e_magic WORD ? ;0000h - EXE标志,“MZ”
e_cblp WORD ? ;0002h - 最后(部分页中的字节数)
c_cp WORD ? ;0004h - 文件中的全部和部分页数
e_crlc WORD ? ;0006h - 重定位表中的指针
e_cparhdr WORD ? ;0008h - 头部尺寸,以段落为单位
e_minalloc WORD ? ;000ah - 所需的最小附加段
e_maxalloc WORD ? ;000ch - 所需的最大附加段
e_ss WORD ? ;000eh - 初始的SS值(相对偏移)
e_sp WORD ? ;0010h - 初始的SP值
e_csum WORD ? ;0012h - 补码校验值
e_ip WORD ? ;0014h - 初始的IP值
e_cs WORD ? ;0016h - 初始的CS值
e_lfarlc WORD ? ;0018h - 重定位表的字节偏移量
e_ovmo WORD ? ;001ah - 覆盖号
e_res WORD 4 dup(?);001ch - 保留字
e_oemid WORD ? ;0024h - OEM标识符(相对e_oeminfo)
e_oeminfo WORD ? ;0026h - OEM信息
e_res2 WORD 10dup(?);0028h - 保留字
e_lfanew WORD ? ;003ch - PE头相对于文件的偏移地址
如上所示,偏移1c
以后(包括1c
)在16系统下是没有定义的。由于其开始的标志字为“MZ”(Mark Zbikowski,它是DOS操作系统的开发者之一),所以称它为“DOS MZ头”。
注意:DOS MZ头部分的字节码(包括DOS Stub程序字节码)的添加是由链接程序link.exe自动实现的。所以在源程序.asm中是找不到相应的定义语句的。
DOS Stub(长度不固定)
由于DOS Stub的大小 不固定,因此DOS头 的大小也是不固定的。DOS Stub部分是该程序在DOS 系统下运行的指令字节码。
举个例子:
一个程序的DOS Stub(指令字节码)如下:
13B7:0140 0E 1F BA 0E 00 B4 09 CD-21 B8 01 4C CD 21 54 68 ........!..L.!Th
13B7:0150 69 73 20 70 72 6F 67 72-61 6D 20 63 61 6E 6E 6F is program canno
13B7:0160 74 20 62 65 20 72 75 6E-20 69 6E 20 44 4F 53 20 t be run in DOS
13B7:0170 6D 6F 64 65 2E 0D 0D 0A-24 00 00 00 00 00 00 00 mode....$.......
13B7:0180 5D 5C 6D C1 19 3D 03 92-19 3D 03 92 19 3D 03 92 ]\m..=...=...=..
13B7:0190 97 22 10 92 1E 3D 03 92-E5 1D 11 92 18 3D 03 92 ."...=.......=..
13B7:01A0 52 69 63 68 19 3D 03 92-00 00 00 00 00 00 00 00 Rich.=..........
那么这些指令都做了哪些工作:
首先将这个程序名更改为“123”,然后复制到C:\Documents and Settings \administrator中。在命令提示符下输入下面指令:
dubug 123 -U 0140 014d
然后显示
13B7:0140 0E PUSH CS ;将CS段地址给DS
13B7:0141 1F POP DS
13B7:0142 BA0E00 MOV DX,000E ;DS:DX指向要显示的字符串014e处
13B7:0145 B409 MOV AH,09 ;调用9号中断,屏幕显示字符串
13B7:0147 CD21 INT 21
13B7:0149 B8014C MOV AX,4C01 ;调用4C号中断,正常退出程序
13B7:014C CD21 INT 21
13B7:014C 5468…………………… 数据区“This program” ;要显示的字符串
它就调用了int 21中断的9号功能,实现在屏幕输出一段字符串。
32位系统下的PE结构
16位系统中,PE头和PE数据部分被当成是冗余数据;在32位系统中,刚好相反,即DOS头称为冗余数据。所谓冗余,是针对DOS头不参与32位系统运行过程而言的。尽管该部分不参与运行,但也不能把这些数据从PE结构除去。在DOS MZ头中有一个IMAGE_DOS_HEADER.e_lfanew
,没有它操作系统就定位不到标准的PE头部,这个可执行程序就会被操作系统认为是非法的PE映像
定位标准PE头
DOS头放在PE 的起始位置,然后在DOS MZ头中有一个字段e_lfanew
专门去找标准PE头的偏移。该地段的值是一个相对偏移量。绝对定位的时候得加上DOS MZ 的基地址。
PE文件结构
在32位系统下,最重要的部分就是PE头和PE数据区。如下图
32位操作系统下的PE文件结构被划为为5个部分:
DOS MZ头,DOS Stub,PE头,节表和节内容。
节表和节内容两部分其实就是图中的PE数据区。DOS MZ头的大小是0x40(十进制:64)个字节,PE头的大小是456个字节(由于数据目录表项不一定是16个,准确地说,PE头也是一个不能确定大小的结构,该结构的实际大小由字段IMAGE_FILE_HEADER.SizeOfOptionalHeader
来确定)。节表的大小之所以不固定,因为每个PE中节的数量是不固定的。但是每个节的描述信息是则是个固定值,共40个字节,节表是由不确定数量的节描述信息组成,其大小等于节的数量 X 40,节的数量由字段IMAGE_FILE_HEARDER.NumberOfSections
来定义。DOS Stub和节内容都是大小不确定的。(前面所说的是节的描述信息是固定的,40字节,并非说节的内容是固定的,别混淆。)
虽然节的描述信息确定,但是节的数量却是不确定的,所以呢,节内容自然也就不确定喽。
PE文件头部 = DOS头 +PE头。
总览PE结构
标准的PE文件一般由四大部分组成:
- DOS头
- PE头(
IMAGE_NT_HEADERS
) - 节表(多个
IMAGE_SECTION_HEADERS
结构) - 节内容
详述:
DOS头的话,分为DOS MZ头 和DOS Stub
PE头(IMAGE_NT_HEADERS
)包括了4字节的标识符号(Signature
),20个字节的基本头信息(IMAGE_FILE_HEADER
),216个字节的扩展头信息(IMAGE_OPTIONAL_HEADER32
)
(即PE头=Signature
+IMAGE_FILE_HEADER
+IMAGE_OPTIONAL_HEADER32
)
PE文件头部=DOS头+PE头 +节表(PE头和PE文件头部不是一个意思,别混淆。)
PE文件身体 = 节内容
PE文件头部解析
DOS MZ头IMAGE_DOS_HEADER
IMAGE_DOS_EADER STRUCT
e_magic WORD ? ;0000h - EXE标志,“MZ”
e_cblp WORD ? ;0002h - 最后(部分页中的字节数)
c_cp WORD ? ;0004h - 文件中的全部和部分页数
e_crlc WORD ? ;0006h - 重定位表中的指针
e_cparhdr WORD ? ;0008h - 头部尺寸,以段落为单位
e_minalloc WORD ? ;000ah - 所需的最小附加段
e_maxalloc WORD ? ;000ch - 所需的最大附加段
e_ss WORD ? ;000eh - 初始的SS值(相对偏移)
e_sp WORD ? ;0010h - 初始的SP值
e_csum WORD ? ;0012h - 补码校验值
e_ip WORD ? ;0014h - 初始的IP值
e_cs WORD ? ;0016h - 初始的CS值
e_lfarlc WORD ? ;0018h - 重定位表的字节偏移量
e_ovmo WORD ? ;001ah - 覆盖号
e_res WORD 4 dup(?);001ch - 保留字
e_oemid WORD ? ;0024h - OEM标识符(相对e_oeminfo)
e_oeminfo WORD ? ;0026h - OEM信息
e_res2 WORD 10dup(?);0028h - 保留字
e_lfanew WORD ? ;003ch - PE头相对于文件的偏移地址
注意:
注释后的偏移是基于IMAGE_DOS_HEADER头的
DOS Stub (略过)
DOS MZ 头的下面是DOS Stub。整个DOS Stub是一个字节块,其内容随着链接时使用的链接器不同而不同,PE中并没有与之对应的相关结构,所以不需要纠结。
PE头标识Signature
紧跟在DOS Stub后面的是PE头标识Signature。与大部分文件格式的头部结构一样,PE头部信息中有一个四字节的标识,该标识位于指针IMAGE_DOS_HEADER.e_lfanew
指向的位置。其内容固定,对应于ASCII码的字符串“PE\0\0”
标准PE头IMAGE_FILE_HEADER
标准PE头IMAGE_FILE_HEADER紧跟于PE标识头后,位于IMAGE_DOS_HEADER
的e_lfanew
+4的位置。由此位置开始的20字节为数据结构标准PE头IMAGE_FILE_HEADER
的内容,此结构在微软的官方文档中被称为 标准通用对象文件格式(Common Object File Format,COFF)头。它记录了PE文件的全局属性,定义如下:
IMAGE_FILE_HEADER STRUCT
Machine WORD ?;0004h - 运行平台
NumberOfSections WORD ?;0006h - PE中节的数量
TimeDateStamp DWORD ?;0008h - 文件创建日期和时间
PointerToSymbolTable DWORD ?;000ch - 指向符号表(用于调试)
NumberOfSymbols DWORD ?;0010h - 符号表中的符号数量
SizeOfOptionalHeader WORD ?;0014h - 扩展头结构的长度
Characteristics WORD ?;0016h - 文件属性
注意:
注释后的偏移是基于IMAGE_NT_HEADERS
头的
标准PE头IMAGE_OPTIONAL_HEADER32
详细定义如下:
IMAGE_OPTIONAL_HEADER32 STRUCT
Magic WORD ?;0018h -魔术字 107h=ROM Image 10Bh=exe Image
MajorLinkerVersion BYTE ?;001ah -链接器版本号
MinorLinkerVersion BYTE ?;001bh -
SizeOfCode DWORD ?;001ch -所有含代码的节的总大小
SizeOfInitializedData DWORD ?;0020h -所有含已初始化数据的节的总大小
SizeOfUninitializedData DWORD ?;0024h -所有含未初始化数据的节的大小
AddressOfEntryPoint DWORD ?;0028h -程序执行入口RVA
BaseOfCode DWORD ?;002ch -代码的节的起始RVA
BaseOfData DWORD ?;0030h -数据的节的起始RVA
ImageBase DWORD ?;0034h -程序的建议装载地址
SectionAlignment DWORD ?;0038h -内存中的节的对齐粒度
FileAlignment DWORD ?;003ch -文件中的节的对齐粒度
MajorOperatingSystemVersion WORD ?;0040h -操作系统版本号
MinorOperatingSystemVersion WORD ?;0042h -
MajorImageVersion WORD ?;0044h -该PE的版本号
MinorImageVersion WORD ?;0046h -
MajorSubsystemVersion WORD ?;0048h -所需子系统的版本号
MinorSubsystemVersion WORD ?;004ah -
Win32VersionValue DWORD ?;004ch -未用
SizeOfImage DWORD ?;0050h -内存中的整个PE映像尺寸
SizeOfHeaders DWORD ?;0054h -所有头+节表的大小
CheckSum DWORD ?;0058h -校验和
Subsystem WORD ?;005ch -文件的子系统
DllCharacteristics WORD ?;005eh -DLL文件特性
SizeOfStackReserve DWORD ?;0060h -初始化时的栈大小
SizeOfStackCommit DWORD ?;0064h -初始化时实际提交的栈大小
SizeOfHeapReserve DWORD ?;0068h -初始化时保留的堆大小
SizeOfHeapCommit DWORD ?;006ch -初始化时实际提交的堆大小
LoaderFlags DWORD ?;0070h -与调试有关
NumberOfRvaAndSizes DWORD ?;0074h -下面的数据目标结构的项目数量
DataDirectory IMAGE_DATA_DIRECTORY 16dup(<>) ?;0078h -
IMAGE_OPTIONAL_HEADER32 ENDS
注意:
注释后的偏移是基于IMAGE_NT_HEADERS头的
文件执行时的入口地址,文件被操作系统装入内存后的默认基地址,以及节在磁盘和内存中的对齐单位等信息均可以在上面结构中找到。
PE头IMAGE_NT_HEADERS
这个结构是广义上的PE头,在标准的PE文件中其大小为456字节。它是Signature
,IMAGE_FILE_HEADER
,IMAGE_OPTIONAL_HEADER32
这三个数据结构的组合。
该结构的详细定义如下:
IMAGE_NT_HEADERS STRUCT
Signature DOWRD ?; -PE文件标识,"PE\0\0"
FileHeader IMAGE_FILE_HEADER <>;0004h -PE标准头
OptionalHeader IMAGE_OPTIONAL_HEADER32 <>;0018h -PE扩展头
数据目录项 IMAGE_DATA_DIRECTORY
IMAGE_OPTIONAL_HEADE32
(扩展PE头)结构的最后一个字段为DataDirectory
。该字段定义了PE文件中出现的所有不同类型的数据的目录信息。结构定义如下:
IMAGE_DATA_DIRECTORY STRUCT
VirtualAddress DWORD ?;0000h -数据的起始RVA
isize DWORD ?;0004h -数据块的长度
总的数据目录一共由16个相同的IMAGE_DATA_DIRECTORY
结构连续排列在一起组成,示意图如下:
这16个元组的数据每一项均代表PE中的某一个类型的数据,数据类型如下:
0 Export table address and size 导出表地址和大小
1 Import table address and size 导入表地址和大小
2 Resource table address and size 资源表地址和大小
3 Exception table address and size 异常表地址和大小
4 Certificate table address and size 属性证书数据地址和大小
5 base relocation table address and size 基址重定位表地址和大小
6 Debugging information starting address and size 调试信息地址和大小
7 Architecture-specific data 预留为0
8 Global pointer register relative virtual address 指向全局指针寄存器的值
9 Thread local storage(TLS) table address and size 线程局部存储地址和大小
10 Load configuration table address and size 加载配置表地址和大小
11 Bound import table address and size 绑定导入表地址和大小
12 Import address table and size 导入函数地址表和大小
13 Delay import descriptor address and size 延迟导入表地址和大小
14 CLR Runtime Header address and size CLR运行时头部数据地址和大小
15 Reserved 系统保留
节表项 IMAGE_SECTION_HEADER
PE头IMAGE_NT_HEADERS后紧跟着节表。它由许多个节表项(IMAGE_SECTION_HEADER
)组成,每个节表项记录了PE中某个特定的节有关的信息,如节的属性,节的大小,在文件和内存中的起始位置等。节表中节的数量由字段IMAGE_FILE_HEADER.NumberOfSections
来定义。节表项的数据结构定义如下:
IMAGE_SECTION_HEADER STRUCT
Namel db IMAGE_SIZEOF_SHORT_NAME dup(?);0000h -8个字节节名
union Misc
PhysicalAddress dd?
VirtualSize dd?;0008h - 节区的尺寸
ends
VirtualAddress dd?;000ch - 节区的RVA地址
SizeOfRawData dd?;0010h -在文件中对齐后的尺寸
PointerToRawData dd?;0014h -在文件中的偏移
PointerToRelocations dd?;0018h -在OBJ文件中使用
PointerToLinenumbers dd?;001ch -行号表的位置(供调试用)
NumberOfRelocations dw?;0020h -在OBJ文件中使用
NumberOfLinenumbers dw?;0022h -行号表中行号的数量
Characteristics dd?;0024 -节的属性
IMAGE_SECTION_HEADER ENDS
注意:
注释后的偏移是基于IMAGE_SECTION_HEADER
头的
数据结构字段详解
IMAGE_NT_HEADER.Signature
+0000h
,双字。PE文件标识,被定义为00004550h
。也就是“P”,“E”加上两个0,0x45
就是“E”,0x50
就是“P”,这也是PE这个称呼的由来.如果更改其中的任何一个字节,操作系统就无法把该文件识别为正确的PE文件。通过修改这个字段,会导致PE文件在32位系统中加载失败,但由于文件的其他部分(特别是DOS头)并没有破坏,系统还是可以识别出其为DOS系统下的可执行程序,并通过调用纯DOS环境来运行DOS Stub中的程序代码。
确认操作系统中的某个PE文件携带病毒,并且开机后会被加载进内存运行,最简单的处理办法是通过Windows PE盘启动系统,在系统中找到病毒文件,使用记事本简单地修改其中任何一个字符,保存文件,重新开机启动后即可防止病毒文件被加载
注意:
此PE非彼PE,windows PE是一个操作系统,其全称为:Windows PreInstallation Environment,即Windows的预安装环境。该操作系统区别于Windows XP/2000/Vista等,可以从光盘引导。
IMAGE_NT_HEADER.FileHeader
+0004,结构。该结构指向IMAGE_FILE_HEADER,由于PE扩展自通用COFF规范,所以,该字段在官方文档中被称为标准COFF头
IMAGE_NT_HEADER.OptionalHeader
+0018h,结构。该结构指向IMAGE_OPTIONAL_HEADER32。在符合COFF规范的“.obj”目标文件中该部分并不存在,所以被称为OptionalHeader(“可选头”)
可选头又分为两部分,前10个字段原属于COFF,用来加载和执行一个可执行文件;后21个字段则是通过链接器追加的。作为PE扩展部分,用于描述可执行文件的一些信息,供PE加载器加载使用。
IMAGE_FILE_HEADER的字段详解
IMAGE_FILE_HEADER.Machine
+0004,单字。用来指定PE文件运行的平台。由于Windows最初被设计为可以运行在Intel,Sun,Dec,IBM等多种硬件平台上,或者能模拟这些平台的软件环境中,而不同的硬件平台其指令的机器码不相同,因此为不同平台编译的EXE
是无法通用的。
下面列出常见值:
IMAGE_FILE_HEADER.NumberOfSections
+0006h,单字。文件中存在的节的总数。Windows XP中,可以有0个节,但数值不能小于1,也不能超过96。如果将该值设置为0,则操作系统装载时会提示不是有效的win32程序。如果想在PE中增加或删除节,必须变更此处的值。
另外,这个值既不能比实际内存中存在的节多,也不能比它少,否则装载时会发生错误,提示不是有效的Win32应用程序
IMAGE_FILE_HEADER.TimeDateStamp
+0008h,双字。编译器创建此文件时的时间戳。低32为存放的值是字1970年1月1日00:00时开始到创建时间为止的总秒数。
该数值可以随意修改而不会影响程序运行。所以,有的链接器在这里填入固定的值,有的则随意写入任何值,这对用户创建的文件并没有实际的意义。另外,这个时间值与操作系统文件属性可以看到的三个时间(创建时间,修改时间,访问时间)也没用任何联系
IMAGE_FILE_HEADER.PointerToSymbolTable
+000Ch,双字。COFF符号表的偏移。如果不存在COFF符号表,此值为0。对于映像文件来说,此值为0,因为微软已经不赞成在PE中使用COFF调试信息。
IMAGE_FILE_HEADER.NumberOfSymbols
+0010h,双字。符号表中元素的数目。由于字符串表紧跟在符号表后,所以可以利用这个值来定位字符串表。对于映像文件来说,此值为0,主要用于调试。
IMAGE_FILE_HEADER.SizeOfOptionalHeader
+0014h。单字。指定结构IMAGE_OPTIONAL_HEADER32
的长度,默认情况下这个值等于00e0h
;如果是64位PE文件,该结构的默认大小为00F0h
。
用户可以自己定义这个值的大小,不过需要注意两点:
(1)更改完以后,需要自行将文件中IMAGE_OPTIONAL_HEADER32
的大小扩充为你指定的值(一般以0补足)
(2)扩充完以后,要维持文件中的对齐特性(比如在HelloWorld.exe
中,此处增加了8个字节后,一定要在后面相应删除8个字节,以保证.text节起始位置处于0400h
)
IMAGE_FILE_HEADER.Characteristics
+0016h,单字。文件属性标志字段,它的不同数据位定义了不同的文件属性。
解释:
- 当位13为1时,这表示是一个DLL文件,那么系统将使用调用DLL入口函数的方式执行文件入口函数;当位13为0时,表示这是一个普通的可执行文件,系统直接跳到入口处执行。对于普通的可执行PE文件来说,这个字段的值一般是0fh,而对于DLL文件来说,这个字段的值一般是210eh。
- 当第0位为1时,表明此文件不包含基址重定位信息,因此必须将其加载到文件头中指定的基地址字段位置。如果进程空间此处的基地址被占用,加载器会报错。在程序运行前如果发现文件中存在可重定位信息,链接器会执行移出可执行文件中的重定位信息的操作。
- 当第1位为1时,表明此映像文件是合法的,可以运行。如果未设置此标志,表明出现链接器错误
- 当第7位为1时,表明文件是小尾方式,即内存中,最低有效位LSB位于最高有效位MSB的前面,与第15位的大尾方式(MSB在前,LSB在后)一样,都不赞成使用该标志,最好将其设置为0
- 当第10位为1时,如果此映像文件在可移动存储介质上,那么加载器将完全加载它并把它复制到内存交换文件中
- 当第11位为1时,如果此映像文件在网络上,那么加载器也将完全加载它并把它复制到内存交换文件中
- 当第13位为1时,表明此映像文件是动态链接库(DLL)。这样的文件总被认为是可执行文件,尽管它们并不能直接运行
- 可执行文件的标志设置为010fh,即第0,1,2,3,8位分别设置为1,表明该文件为可执行文件,不含重定位信息,不含符号和行号信息,文件只在32位平台运行。
IMAGE_OPTIONAL_HEADER32的字段详解
IMAGE_OPTIONAL_HEADER32.Magic
+0018h,单字。魔术字,说明文件的类型,如果为010BH,则表示该文件为PE32;如果为0107h,则表示文件为ROM映像;如果为020BH,则表示该文件为PE32+,即64位下的PE文件
IMAGE_OPTIONAL_HEADER32.MajorLinkerVersion
IMAGE_OPTIONAL_HEADER32.MinorLinkerVersion
+001ah,单字。这两个字段都是字节型,指定链接器版本号,对执行没有任何影响
IMAGE_OPTIONAL_HEADER32.SizeOfCode
+001h,双字。所有代码节的总和(以字节计算),该大小是基于文件对齐后的大小,而非内存对齐后的大小。稍后还会介绍一个字段SizeOfmage
,它是基于内存对齐后的大小。需要注意一点:判断某个节是否包含代码的方法不是根据节的属性中是否含有IMAGE_SCN_MEM_EXECUTE
标志,而是根据节的属性是否含有IMAGE_SCN_CNT_CODE
标志
IMAGE_OPTIONAL_HEADER32.SizeOfInitializedData
+0020h,双字。所有包含已经初始化的数据的节的总大小。
IMAGE_OPTIONAL_HEADER32.SizeOfUninitializedData
+0024h,双字。所有包含为初始化的数据的节的总大小。这些数据被定为未初始化,在文件中不占用空间;但在内加载到内存以后,PE加载程序应该为这些数据分配适当大小虚拟地址空间
IMAGE_OPTIONAL_HEADER32.AddressOfEntryPoint
+0028h,双字。在Windows中,可执行程序运行在虚拟地址空间中,由于4GB空间对于程序是唯一的,所以这里的虚拟空间可以简单地理解为真实的地址。该字段的值是一个RVA,它记录了启动代码距离该PE加载后的起始位置到底有多少个字节。
如果在一个可执行文件中附加了一段自己的代码,并且想让这段代码首先被执行,一般都要修改这里的值使之指向自己的代码位置。对于一般程序映像来说,它就是启动地址;对于设备驱动程序来说,它是初始化函数的地址。入口点对于DLL来说是可选的,如果不存在入口点,这个字段必须设置为0。
IMAGE_OPTIONAL_HEADER32.BaseOfCode
+002Ch,双字。代码节的起始RVA,表示映像被加载内存时代码节的开头相对于映像基址的偏移地址。一般情况下,代码节紧跟在PE头部后面,节的名称通常为“.text”
IMAGE_OPTIONAL_HEADER32.BaseOfData
+0030h,双字。数据节的起始RVA,表示映像被加载进内存时数据节的开头相对于映像基地址的偏移地址。一般情况下,数据节位于文件末尾,节的名称通常为“.data”
IMAGE_OPTIONAL_HEADER32.ImageBase
+0034h,双字。该字段指出了PE映像的优先装入地址。也就是在IMAGE_OPTIONAL_HEADER32.AddressOfEntryPoint中说的程序被加载到内存后的起始VA。那么为什么要设置这个地址呢?因为链接器在产生可执行文件的时候,是对应这个地址来生成机器码的。如果操作系统也是按照这个地址加载机器码到内存中,那么指令中的许多重定位信息就不需要修改了,这样运行速度就会更快一些。
前面说过,对于EXE文件来说,每个文件使用的都是独立的虚拟地址空间,所以,优先装入的地址通常不会被其他模块占据。也就是说,EXE文件总是能按照这个地址装入,这就意味着装入后的EXE文件不需要进行重定位了
在链接的时候,可以使用参数“-base”来指定优先装入的地址,如果不确定,那么链接器默认装入EXE的地址就是0x00400000。而相对于DLL文件来说,它默认优先装入地址则是0x10000000。如果一个进程用到了多个DLL文件,其装入地址可能会发生冲突。PE加载器会调整其中的地址,使所有的DLL文件都能被正确装入。所以,不要错误地认为内存中动态链接库的基地址和其文件头字段IMAGE_OPTIONAL_HEADER32.ImageBase指定的完全一样。
你可以自己定义这个值,但取值有限制:第一,取值不能超出边界,即取的值必须在进程地址空间中;第二,该值必须是64KB的整数倍
IMAGE_OPTIONAL_HEADER32.SectionAlignment
+0038h,双字。内存中节的对齐粒度,该字段指定了节被装入内存后的对齐单位。
解释:
为什么16位汇编里取数时要从偶地址开始?(取一个字从偶地址开始,只需要一个CPU周期就可以取到;而从奇地址取一个字,则需要两个CPU周期)其实对齐和它一个道理,内存中的数据存取以页面为单位。
win32的页面大小是4KB,所以Win32 PE中节的内存对齐粒度一般都选择4KB大小。十六进制表示为01000h,
SectionAlignment必须大于或等于FileAlignment,当它小于系统页面大小时,必须保证SectionAlignment和FileAlignment
IMAGE_OPTIONAL_HEADER32.FileAlignment
+003ch,双字。文件中节的对齐粒度。文件中的节对齐并不是提高本身代码的执行效率,同样也是为了提高文件从磁盘加载的效率。Windows XP同来组织硬盘的所有文件系统都是基于簇(分配单元)的,每个簇包含几个物理扇区。扇区是磁盘物理存取的最小单位。簇越大,硬盘存储信息的容量就越大,但存取所花费的时间也越长。通常情况下,Windows会选择使用(200h)512字节的簇大小(1个物理扇区的大小)来格式化分区,最大可以达到4KB。
IMAGE_OPTIONAL_HEADER32.MajorOperatingSystemVersion
IMAGE_OPTIONAL_HEADER32.MinorOperatingSystemVersion
+0040h,23和24标准的两个字段都为单字,共计为双字。标识操作系统的版本号,分为主版本号和次版本号两部分
IMAGE_OPTIONAL_HEADER32.MajorImageVersion
IMAGE_OPTIONAL_HEADER32.MinorImageVersion
+0044h,双字。本PE文件映像的版本号
IMAGE_OPTIONAL_HEADER32.MajorSubsystemVersion
IMAGE_OPTIONAL_HEADER32.MinorSubsystemVersion
+0048h,双字。运行所需要的子系统的版本号。
IMAGE_OPTIONAL_HEADER32.Win32VersionValue
+004ch,双字。子系统版本的值,暂时保留未用,必须设置为0,比如将此处的值更改为696C6971h,程序运行将失败。错误如下:
IMAGE_OPTIONAL_HEADER32.SizeOfHeaders
+0054h,双字。所有头+节表按照文件对齐粒度对齐后的大小(即含补足的0)。在PE文件中,该部分数据是严格按照200h对齐的,如果不对齐,系统在加载时会提示出错
IMAGE_OPTIONAL_HEADER32.CheckSum
+0058h,双字。校验和,在大多数的PE文件中,该值是0,但在一些内核模式的驱动程序和系统DLL中,该值则是必须存在且正确的,比如kernel32.dll中PE的检验和是0011E97Eh。Windows系统目录下有一个动态链接库IMAGEHELP.DLL,它是Win32中专门用来操作PE文件的函数库,这里面的函数CheckSumMappedFile就是用来计算文件头检验和的,对于整个PE文件也有一个检验和函数MapFileAndCheckSum。该动态链接库中还包括其他一些常用的函数。
IMAGE_OPTIONAL_HEADER32.Subsystem
+005Ch,单字。指定使用界面的子系统,取值如下表。这个字段决定了系统如何为程序建立初始的界面,链接时使用的参数-subsystem:xxx选项指定的就是这个字段的值,如果将子系统指定为Windows命令行用户交互模式(Command User Interface,CUI),那么系统会自动为程序建立一个控制台窗口;如果指定为Windows GUI,窗口程序代码必须由用户自己建立。
取值 | 常量符号 | 含义 |
---|---|---|
0 | IMAGE_SUBSYSTEM_UNKNOWN | 未知的子系统 |
1 | IMAGE_SUBSYSTEM_NATIVE | 设备驱动程序和Native Windows进程 |
2 | IMAGE_SUBSYSTEM_WINDOWS_GUI | Windows图形用户界面 |
3 | IMAGE_SUBSYSTEM_WINDOWS_CUI | Windows字符模式(控制台) |
7 | IMAGE_SUBSYSTEM_POSIX_CUI | POSIX字符模式(控制台) |
9 | IMAGE_SUBSYSTEM_WINDOWS_CE_CUI | Windows CE图形界面 |
10 | IMAGE_SUBSYSTEM_EFI_APPLICATION | 可扩展固件接口(EFI)应用程序 |
11 | IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIIVER | 带引导服务的EFI驱动程序 |
12 | IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIIVER | 带运行时服务的EFI驱动程序 |
13 | IMAGE_SUBSYSTEM_EFI_ROM | EFI ROM映像 |
14 | IMAGE_SUBSYSTEM_XBOX | XBOX |
MASM32的link程序的链接开关-subsystem的常见选项如下表
链接开关 | 取值 | 常见文件尾 |
---|---|---|
-subsystem:native | subsystem=1 | .sys |
-subsystem:windows | subsystem=2 | .exe |
-subsystem:console | subsystem=3 | .exe |
IMAGE_OPTIONAL_HEADER32.DllCharacteristics
+005eh,单字。DLL文件属性,它是一个标志集,不是针对DLL文件,而是针对所有的PE文件的。
数据位 | 常量符号 | 为1时的含义 |
---|---|---|
0 | 保留,必须为0 | |
1 | 保留,必须为0 | |
2 | 保留,必须为0 | |
3 | 保留,必须为0 | |
6 | IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE | DLL可以在加载时被重定位 |
7 | IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY | 强制代码实施完整性验证 |
8 | IMAGE_DLLCHARACTERISTICS_NX_COMPAT | 该映像兼容DEP |
9 | IMAGE_DLLCHARACTERISTICS_DO_ISOLATION | 可以隔离,但并不隔离此映像 |
10 | IMAGE_DLLCHARACTERISTICS_NO_SEH | 映像不使用SEH |
11 | IMAGE_DLLCHARACTERISTICS_NO_BIND | 不绑定映像 |
12 | 保留,必须为0 | |
13 | IMAGE_DLLCHARACTERISTICS_WDM_DRIVER | 该映像为一个WDM driver |
14 | 保留,必须为0 | |
15 | IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE | 可用于终端服务器 |
这个字段定义了PE文件装载时的一些特性
IMAGE_OPTIONAL_HEADER32.SizeOfStackReserve
+0060h,双字,初始化时保留的栈大小。该字段表示为初始线程的栈而保留的虚拟内存数量,然而并不是留出的所有虚拟内存都可以用栈(真正的栈大小由下一个字段SizeOfStackCommit
决定)。该字段的默认值为0x100000(1MB),如果调用API函数CreatThread
时,把NULL当做传入的参数,那么创建出来的栈大小也会是1MB
IMAGE_OPTIONAL_HEADER32.SizeOfStackCommit
+0064h,双字,初始化时实际提交的栈大小。保证初始线程的栈实际占用内存空间的大小,它是被系统提交的,这些提交的栈不存在于交换文件里,而是存在于内存里。对于Microsoft的链接器来说,这个域的初始值为0x1000字节(1页),对于TLINK32,则为2页。
IMAGE_OPTIONAL_HEADER32.SizeOfHeapReserve
+0068h,双字,初始化时保留的堆大小。用来保留给初始进程堆使用的虚拟内存,这个堆的句柄可以通过调用GetProcessHeap
函数获得。每一个进程至少会有一个默认的进程堆,该堆在进程启动的时候被创建,而且说进程的生命期中永远不会被删除。默认值为1MB,我们可以通过链接器的“-heap”参数指定起始的保留堆内存大小和实际提交的堆大小。
IMAGE_OPTIONAL_HEADER32.SizeOfHeapCommit
+006Ch,双字。初始化时实际提交的堆大小,在进程初始化时设定的堆所占用的内存空间,默认值为1页。
IMAGE_OPTIONAL_HEADER32.LoaderFlags
+0070h,双字。加载标志
IMAGE_OPTIONAL_HEADER32.NumberOfRvaAndSize
+0074h,双字。定义数据目录结构的数量,一般为00000010h,即16个。该值由字段SizeOfOptionalHeaders决定,实际应用中可以取2~16的值
IMAGE_OPTIONAL_HEADER32.DataDirectory
+0078h,结构。由16个IMAGE_DATA_DIRECTORY
结构线性排列而成,用于定义PE中16种不同类别的数据所在的位置和大小。以下是对这16数据的说明:
- 导出数据所在的节通常被命名为.edata,它包含一些可被其他EXE程序访问的符号的相关信息,比如导出函数和资源等。这些符号通常出现在DLL中,但DLL也可以包含导入符号,而且在某些EXE中也可以有导出符号。
- 导入数据所在的节通常被命名为.idata,它包含了PE映像中所有导入的符号。导入信息在EXE和DLL中几乎都存在
- 异常表数据所在的节通常被命名为.pdata。该节是由用于异常处理的函数表项组成的数组。可选文件头中的
ExceptionTable
(异常表)字段指向它。在将他们放进最终的映像文件之前,这些表项必须按函数地址进行排序,并且这些函数表项的描述必须符合特定的目标平台。该部分的数据主要用于基于表的异常处理,适用于除X86之外的所有CPU - 资源数据所在的节通常被命名为.rsrc。该节是一个多层的二叉排序树,该树的节点指向PE中各种类型的资源,如图标,对话框,菜单等。树的深度可达231层,但是PE中经常使用的只有3层:类型层,名称层,语言代码层。
- 属性证书数据的作用类似PE文件的校验和或者MD5码,通过这种属性证书方式可以验证一个PE文件是否被非法修改过,为PE文件添加属性证书表可以使该PE与属性证书相关联。属性证书表是由一组连续的按八进制(从任意字节边界开始的16个连续字节)边界对齐的属性证书表项组成,每个属性证书表项指向WIN_CERTIFICATE结构。此结构可以在WinTrust.H文件中找到,结构定义如下:
WIN_CERTIFICATE STRUCT
dwLength DWORD ?;0000h
wRevision WORD ?;0004h
wCertificateType WORD ?;0006h
bCertificate byte ?;0008h
WIN_CERTIFICATE ENDS
注意:
该数据并不作为映像的一部分被映射到内存,因此,DataDirectory.Certificate_VirtualAddress
字段是文件偏移,而不是RVA
。
DataDirectory.Certificate_VirtualAddress
字段给出了属性证书表中第一个属性证书表项在文件中的偏移,与后续的属性证书表项,可以通过当前属性证书表项的文件便宜加上WIN_CERTIFICATE.dwLength
字段的值,并将结果向上舍入为8个字节的倍数来访问。后续的属性证书表项可以一直以这种方式访问,直到这些WIN_CERTIFICATE.dwLength
字段(已经向上舍入为8字节的倍数)的和等于可选文件头中的DataDirectory.Certificate_isize
的值。如果上述的值最后不等于isize
字段的值,要么是属性证书表被破坏了,要是isize域被修改了。
- 基址重定位信息所处的节通常被命名为
.reloc
,基址重定位表包含了映像中所有需要重定位的内容。它被划分别许多块,每一块表示一个4KB页面范围内的基址重定位信息,它必须从32位边界开始。一般情况下,Windows加载器是不需要处理由链接器解析的基址重定位信息,除非该映像不能被如约地加载到IMAGE_OPTIONAL_HEADER32.ImageBase
指定的位置。 - 调试数据所处的节通常被命名为.debug,它指向
IMAGE_DEBUG_DIRECTORY
结构数组。其中的每个元素都描述了PE中的一些调试信息。要获得IMAGE_DEBUG_DIRECTORY结构的数目,可以用isize字段除以IMAGE_DEBUG_DIRECTORY
结构的大小
注意:
在默认情况下,调试信息并不会映射到映像到虚拟地址空间中。调试目录可以位于一个可丢弃的.debug节(如果存在)中,或者位于PE文件的其他节中,或者不任何节中。所以,它可能被加载到虚拟内存中,而大部分情况下是被丢弃的。 - 预留,必须为0
- Global Ptr数据描述的是被存在全局指针寄存器中的一个值
- 线程本地存储数据所处的节,通常命名为.tls。线程本地存储(TLS)是Windows支持的一种特殊存储类别,其中的数据对象不是栈变量,是对应于运行相应代码的单个线程。因此,每个线程都可以为使用TLS定义的变量来维护一个不同有其它线程的值
当创建线程时,PE加载器通过线程环境块(TEB)的地址放入FS寄存器来传递线程的TLS数组地址,距TEB开头0x2C的位置处有一个指针指向该TLS数据。线程本地存储技术是特定于intel x86平台。 - 加载配置信息用于包含保留SEH技术,该技术基于x86的32位系统,它提供了一个安全的结构化异常处理程序列表,操作系统在进行异常处理时要用到这些异常处理程序
- 绑定导入数据的存在主要是为了优化导入信息,提高PE的加载效率。当PE文件被加载到内存时,加载器会先检查导入表,然后把需要加载的DLL载入到地址空间中。加载器还有一项比较重要的工作是根据导入信息的描述使用动态链接库里输入函数的实际地址取替换IAT表的内容,这个步骤会花去一部分时间。但是,如果程序员(或者链接器)可以完全知道函数的地址,就可以直接把数据中的元素替换为地址,这能节省想当多的时间,这种方法称为绑定。简单来说,绑定是由程序员或链接器代替Windows PE加载器完成了一部分对导入表的处理工作(在加载时)
- IAT是导入地址表的英文缩写。准确地讲,它是导入表的一部分,这个双字数组里定义了所有导入函数的VA,程序可以直接通过跳转指令跳转到该VA处执行
- 延迟导入数据也和动态链接库调用有关,这种数据的存在是为了给“应用程序直到首次调用某个DLL中的函数或数据时才加载这个DLL(即延迟加载)”这种行为提供一种统一的访问机制
- CLR数据所处的节通常被命名为.cormeta,该信息是.NET框架的一个重要组成部分,所有基于.NET框架开发的程序,其初始化部分都是通过访问这部分定义而实现的。PE加载时将通过该结构加载代码托管机制需要的所有动态链接库文件,并完成与CLR有关的一些其他操作。
- 系统预留,未定义
IMAGE_DATA_DIRECTORY
IMAGE_DATA_DIRECTORY.VirtualAddress:
+0000h,双字。如上所述,这个字段记录了特定类型的起始RVA。当然针对不同的数据结构,该字段包含的数据含义并不一样,有的数据甚至还不是RVA(如属性证书数据中该字段的值表示的值FOA)
IMAGE_DATA_DIRECTORY.isize:
+0004h,双字。该字段记录了特定类型的数据块的长度。
节表项IMAGE_SECTION_HEADER的字段
IMAGE_SECTION_HEADER.Name1
+0000h,8字节,该字段一共就8节,一般情况下是一个以“\0”结尾的ASCII码字符串来标识节的名称,内容可以自行定义
该名称并不遵循Ansi字符串必须以“\0”结尾的规则,如果不以“\0”结尾,系统依然会认为它是一个字符串,但会根据8个字节的长度对其进行截断处理
IMAGE_SECTION_HEADER.Misc
+0008h,双字。该字段是一个union型的数据,这是节的数据在没有对齐前的真实尺寸,步过很多PE文件该值并不准确
IMAGE_SECTION_HEADER.VirtualAddress
+000ch,双字。节区的RVA地址
IMAGE_SECTION_HEADER.SizeOfRawData
+0010h,双字。节咋文件中对齐后的尺寸。一般512(200h)字节。
IMAGE_SECTION_HEADER.PointerToRawData
+0014h,双字。节区起始数据在文件中的偏移。
IMAGE_SECTION_HEADER.PointerToRelocations
+0018h,双字。在“.obj”文件中使用,指向在重定位表的指针
IMAGE_SECTION_HEADER.PointerToLinenumbers
+001ch,双字,行号表的位置(供调试用)
IMAGE_SECTION_HEADER.NumberOfRelocations
+0020h,单字。重定位表的个数(在OBJ文件中使用)
IMAGE_SECTION_HEADER.NumberOfLinenumbers
+0022h,单字。行号表中行号的数量
IMAGE_SECTION_HEADER.Characteristics
+0024h,双字。节的属性。这个字段很重要,这是节的属性标志字段,其中不同的数据为代表不同的属性,如下:
数据位 | 常量符号 | 位为1时的含义 |
---|---|---|
5 | IMAGE_SCN_CNT_CODE或00000020h | 节中包含代码 |
6 | IMAGE_SCN_CNT_INITIALIZED_DATA或00000040h | 节中包含已初始化数据 |
7 | IMAGE_SCN_CNT_UNINITIALIZED_DATA或00000080h | 节中包含未初始化数据 |
8 | IMAGE_SCN_CNT_LINK_OTHER或00000100h | 保留供将来使用 |
25 | IMAGE_SCN_MEM_DISCARDABLE或02000000h | 节中的数据在进程开始以后将丢弃,如.reloc |
26 | IMAGE_SCN_MEM_NOT_CACHED或04000000h | 节中的数据不会经过缓存 |
27 | IMAGE_SCN_MEM_NOT_PAGED或08000000h | 节中的数据不会被交换到磁盘 |
28 | IMAGE_SCN_MEM_SHARED或10000000h | 表示节中的数据将被不同的进程所共享 |
29 | IMAGE_SCN_MEM_EXECUTE或20000000h | 映射到内存后的页面包含可执行属性 |
30 | IMAGE_SCN_MEM_READ或40000000h | 映射到内存后的页面包含可读属性 |
31 | IMAGE_SCN_MEM_WRITE或80000000h | 映射到内存后的页面包含可写属性 |
代码节的属性一般为60000020h,也就是可执行,可读和“节中包含代码”;数据节的属性一般为c0000040h,也就是可读,可读和“包含已初始化数据”;而常量节(对应源代码中的.const段)的属性为40000040h,也就是可读和“包含已初始化数据”;资源节的属性和常量节的属性一般是相同的。
节属性的定义不一定必须是这些值。比如,PE文件被压缩工具压缩以后,包含代码的节往往被同时设置成具有可执行,可读和可写属性,因为解压部分需要将解压后的代码回写到代码段中