标题:PE结构详解

标题:PE结构详解

PE文件
如图1所示,一个完整的PE(Portable Executable)文件由DOS头,PE文件头,块表,块和调试信息(调试版本有效)组成。

图1 PE文件格式
在这里插入图片描述

在DOS头部,以e_magic开头,它的值是固定的”0x5a4d”,即(MZ)开头。最开头的是部分是DOS部首,DOS部首由两部分组成:DOS的MZ文件标志和DOS stub(DOS存根程序)。之所以设置DOS部首是微软为了兼容原有的DOS系统下的程序而设立的。

DOS Stub即为一句话“This program Cannot be run in DOS mode”。

typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header

 WORD   e_magic;                     // Magic number

 WORD   e_cblp;                      // Bytes on last page of file

 WORD   e_cp;                        // Pages in file

 WORD   e_crlc;                      // Relocations

 WORD   e_cparhdr;                   // Size of header in paragraphs

 WORD   e_minalloc;                  // Minimum extra paragraphs needed

 WORD   e_maxalloc;                  // Maximum extra paragraphs needed

 WORD   e_ss;                        // Initial (relative) SS value

 WORD   e_sp;                        // Initial SP value

 WORD   e_csum;                      // Checksum

 WORD   e_ip;                        // Initial IP value

 WORD   e_cs;                        // Initial (relative) CS value

 WORD   e_lfarlc;                    // File address of relocation table

 WORD   e_ovno;                      // Overlay number

 WORD   e_res[4];                    // Reserved words

 WORD   e_oemid;                     // OEM identifier (for e_oeminfo)

 WORD   e_oeminfo;                   // OEM information; e_oemid specific

 WORD   e_res2[10];                  // Reserved words

 LONG   e_lfanew;                    // File address of new exe header

} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

在DOS头部,有一个结构成员e_lfanew指向了真正的PE头。值得注意的是PE文件头中的IMAGE_OPTIONAL_HEADER32是一个非常重要的结构,PE文件中的导入表、导出表、资源、重定位表等数据的位置和长度都保存在这个结构里。

typedef struct _IMAGE_NT_HEADERS {

DWORD Signature; //PE头签名PE\0\0

IMAGE_FILE_HEADER FileHeader;//PE文件头

IMAGE_OPTIONAL_HEADER32 OptionalHeader;//PE扩展头

} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

typedef struct _IMAGE_FILE_HEADER {

WORD    Machine;  //014C-IMAGE_FILE_MACHINE_I386

WORD    NumberOfSections; //PE节数量-0007个节

DWORD   TimeDateStamp;   //时间戳E72B4FA9

DWORD   PointerToSymbolTable;  //指向符号表0000

DWORD   NumberOfSymbols;  //符号表数量0000

WORD    SizeOfOptionalHeader; //扩展PE头大小00E0

WORD Characteristics; //文件属性

} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

typedef struct _IMAGE_OPTIONAL_HEADER {

//

// Standard fields.

//



WORD    Magic;   //010B-IMAGE_NT_OPTIONAL_HDR32_MAGIC

BYTE    MajorLinkerVersion;   //0A-连接器主版本号

BYTE    MinorLinkerVersion;  //00-连接器小版本号

DWORD   SizeOfCode; //0000008A(138)-代码节大小

DWORD   SizeOfInitializedData;   //0000004C(76)-已初始化数据大小

DWORD   SizeOfUninitializedData;  //00000000(0)-未初始化数据大小

DWORD   AddressOfEntryPoint;  //000110AA程序入口地址

DWORD   BaseOfCode;  //00001000程序段基地址

DWORD   BaseOfData;  //00001000数据段基地址 



//

// NT additional fields.

//



DWORD   ImageBase;  //镜像加载基地址00400000

DWORD   SectionAlignment; //节对其0001000(4096)

DWORD   FileAlignment;   //文件对齐0000200(512)

WORD    MajorOperatingSystemVersion;  //操作系统主版本号0005

WORD    MinorOperatingSystemVersion;  //操作系统小版本号0001

WORD    MajorImageVersion;  //镜像主版本号0000

WORD    MinorImageVersion;  //镜像小版本号0000

WORD    MajorSubsystemVersion;   //子系统主版本号0005

WORD    MinorSubsystemVersion;   //子系统小版本号0001

DWORD   Win32VersionValue;   //0

DWORD   SizeOfImage;  //镜像大小00022000

DWORD   SizeOfHeaders;    //头大小0400

DWORD   CheckSum;  //0

WORD    Subsystem;  //03-IMAGE_SUBSYSTEM_WINDOWS_CUI

WORD    DllCharacteristics;  

DWORD   SizeOfStackReserve; //栈初始化大小010000

DWORD   SizeOfStackCommit; //栈提交大小01000

DWORD   SizeOfHeapReserve;   //堆初始化大小010000

DWORD   SizeOfHeapCommit;  //堆提交大小01000

DWORD   LoaderFlags;   //0

DWORD   NumberOfRvaAndSizes;   //10(16)

IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];//数据目录表

} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

在PE头,最开始的值是一个PE文件特有的签名,即“PE\0\0”,一旦操作系统在执行PE的时候,发现这个位置如果不是这个值,就会报错。然后,PE头分为2个组成部分:File Header和Optional Header。其中在File Header里,包含有如下信息:

l WORD Machine;//该文件运行所需要的CPU,对于Intel平台是14Ch

l WORD NumberOfSections;//文件的节数目

l DWORD TimeDateStamp;//文件创建日期和时间

l DWORD PointerToSymbolTable;//用于调试

l DWORD NumberOfSymbols;//符号表中符号个数

l WORD SizeOfOptionalHeader;//OptionalHeader 结构大小

l WORD Characteristics;//文件信息标记,区分文件是exe还是dll

在Optional Header里,包含了如下信息:

WORD Magic;//标志字(总是010bh)

BYTE MajorLinkerVersion;//连接器高版本号

BYTE MinorLinkerVersion;//连接器低版本号

DWORD SizeOfCode;//代码段大小
DWORD SizeOfInitializedData;//已初始化数据块大小
DWORD SizeOfUninitializedData;//未初始化数据块大小
DWORD AddressOfEntryPoint;//PE装载器准备运行的PE文件的第一个指令的RVA,
//若要改变整个执行的流程,可以将该值指定到新的RVA,这样新RVA处的指令首先

//被执行。
DWORD BaseOfCode;//代码段起始RVA
DWORD BaseOfData;//数据段起始RVA
DWORD ImageBase;//PE文件的装载地址
DWORD SectionAlignment;//块对齐因子
DWORD FileAlignment;//文件块对齐因子
WORD MajorOperatingSystemVersion;//所需操作系统高位版本号
WORD MinorOperatingSystemVersion;// 所需操作系统低位版本号
WORD MajorImageVersion;//用户自定义高位版本号
WORD MinorImageVersion;//用户自定义低位版本号
WORD MajorSubsystemVersion;//win32子系统版本。若PE文件是专门为Win32设计的
WORD MinorSubsystemVersion;//该子系统版本必定是4.0否则对话框不会有3维立体感
DWORD Win32VersionValue;//保留值,系统没用到的,一般被作为是否感染的标志
DWORD SizeOfImage;//内存中整个PE映像体的尺寸
DWORD SizeOfHeaders;//所有头+节表的大小
DWORD CheckSum;//校验和
WORD Subsystem;//NT用来识别PE文件属于哪个子系统
WORD DllCharacteristics;// 用来表示一个DLL映像是否为进程和线程的初始化及终止包含入口点的标记
DWORD SizeOfStackReserve;//
DWORD SizeOfStackCommit;//
DWORD SizeOfHeapReserve;//
DWORD SizeOfHeapCommit;//
//堆栈大小 这些域控制要保留的地址空间数量,并且负责栈和默认堆的申请。在默认//情况下,栈和堆都拥有1个页面的申请值以及16个页面的保留值
DWORD LoaderFlags;// 告知装载器是否在装载时中止和调试,或者默认地正常运行
DWORD NumberOfRvaAndSizes;// 该字段标识了接下来的DataDirectory数组个数。
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
//IMAGE_DATA_DIRECTORY 结构数组。每个结构给出一个重要数据结构的RVA,比如//引入地址表等

图2 PE文件格式全览
在这里插入图片描述

在Optional Header里,有一个重要的数组即DataDirectory数组。在这个数组里面,保存着很多重要的表,比如导入表(IMP),导出表(EMP),IAT表等。

导入表记录了PE使用了多少库函数。导入表的结构如下:

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk;// 指向一个 IMAGE_THUNK_DATA 结构数组的RVA
}
DWORD TimeDateStamp;// 文件生成的时间
DWORD ForwarderChain;// 这个数据一般为0,可以不关心
DWORD Name1; // RVA,指向DLL名字的指针,ASCII字符串
DWORD FirstThunk; //指向一个 IMAGE_THUNK_DATA 结构数组的RVA,这个数据与IAT所指向的地址一致

}IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR

IMAGE_THUNK_DATA 这是一个DWORD类型的集合。通常我们将其解释为指向一个 IMAGE_IMPORT_BY_NAME 结构的指针,其定义如下:

IMAGE_THUNK_DATA{
union {
PBYTE ForwarderString;
PDWORD Function;
DWORD Ordinal;//判定当前结构数据是不是以序号为输出的,如果是的话该值为

    //0x800000000,此时PIMAGE_IMPORT_BY_NAME不可做为名称使用
    PIMAGE_IMPORT_BY_NAME AddressOfData;
}u1;

} IMAGE_THUNK_DATA,*PIMAGE_THUNK_DATA;

typedef struct _IMAGE_IMPORT_BY_NAME{
WORD Hint;// 函数输出序号
BYTE Name1[1];//输出函数名称

} IMAGE_IMPORT_BY_NAME,*PIMAGE_IMPORT_BY_NAME
在这里插入图片描述

节表头结构:

typedef struct _IMAGE_SECTION_HEADER {

BYTE Name[IMAGE_SIZEOF_SHORT_NAME];//节表名称,如“.text”

//IMAGE_SIZEOF_SHORT_NAME=8

union {

DWORD PhysicalAddress;//物理地址

DWORD VirtualSize;//真实长度,这两个值是一个联合结构,可以使用其中的任何一个,

//一般是节的数据大小

} Misc;

DWORD VirtualAddress;//RVA

DWORD SizeOfRawData;//物理长度

DWORD PointerToRawData;//节基于文件的偏移量

DWORD PointerToRelocations;//重定位的偏移

DWORD PointerToLinenumbers;//行号表的偏移

WORD NumberOfRelocations;//重定位项数目

WORD NumberOfLinenumbers;//行号表的数目

DWORD Characteristics;//节属性 如可读,可写,可执行等

} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

1.概述(general)


所有的节在载入内存后都按“SectionAlignment”(节对齐)对齐,在文件中则以“FileAlignment”(文件对齐)对齐。节由节头中的相关项来描述:在文件中你可通过“PointerToRawData”(原始数据指针)来找到,在内存中你可通过“VirtualAddress”(虚拟地址)来找到;长度由“SizeOfRawData”(原始数据长度)决定。

根据节中包含的内容,可分为好几种节。大多数(并非所有)情况下,节中至少由一个数据目录,并在可选头的数据目录数组中有一个指针指向它。

2.代码节(code section)


首先,我将提到代码节。此节,至少,要将“IMAGE_SCN_CNT_CODE”(含有代码节)、“IMAGE_SCN_MEM_EXECUTE”(内存可执行节)和“IMAGE_SCN_MEM_READ”(内存可读节)等标志位设为1,并且“AddressOfEntryPoint”(入口点地址)将指向节中的某个地方,指向开发者希望首先执行的那个函数的开始处。

“BaseOfCode”(代码基址)通常指向这一节的开始处,但是,如果一些非代码字节被放在代码之前的话,它也可能指向节中靠后的某个地方。

通常,除了可执行代码外,本节没有别的东东,并且通常只有一个代码节,但是不要太迷信这一点。

典型的节名有“.text”、“.code”、“AUTO”之类。

3.数据节(data section)


我们要讨论的下一件事情就是已初始化变量;本节包含的是已初始化的静态变量(象“static int i = 5;”)。它将,至少,使“IMAGE_SCN_CNT_INITIALIZED_DATA”(含有已初始化数据节)、“IMAGE_SCN_MEM_READ”(内存可读节)和“IMAGE_SCN_MEM_WRITE”(内存可写节)等标志位被置为1。

一些链接器可能会将常量放在没有可写标志位的它们自己的节中。如果有一部分数据可共享,或者有其它的特定情况,那么可能会有更多的节,且它们的合适的标志位会被设置。

不管是一节,还是多节,它们都将处于从“BaseOfData”(数据基址)到“BaseOfData”+“SizeOfInitializedData”(数据基址+已初始化数据的大小)的范围之内。

典型的名称有“.data”、“.idata”、“DATA”、等等。

4.BSS节(bss section)


其后就是未初始化的数据(一些象“static int k;”之类的静态变量);本节十分象已初始化的数据,但它的“PointerToRawData”(文件偏移量)却为0,表明它的内容不存储在文件中;并且“IMAGE_SCN_CNT_UNINITIALIZED_DATA”(含有未初始化数据节)而不是“IMAGE_SCN_CNT_INITIALIZED_DATA”(含有已初始化数据节)标志位被置为1,表明在载入时它的内容应该被置为0。这就意味着,在文件中只有节头,没有节身;节身将由加载器创建,并全部为0字节。

它的长度由“SizeOfUninitializedData”(未初始化数据大小)确定。

典型的名称有“.bss”、“BSS”之类。

有些节数据“没有”被数据目录指向。它们的内容和结构是由编译器而不是链接器提供。

(栈段和堆段不是二进制文件中的节,它们是由加载器根据可选头中的栈大小和堆大小项来创建的。)

ELF文件格式
ELF(Executable and Linking Format)是一种对象文件的格式,用于定义不同类型的对象文件(Object files)中都放了什么东西、以及都以什么样的格式去放这些东西。

ELF文件格式提供了两种视图:连接视图和运行视图。在两种视图中,ELF头部(ELF Header)都位于文件的开始部分,位置固定,保存了路线图(road map),描述了该文件的组织情况。

图3 ELF文件格式

在这里插入图片描述

在链接视图中,程序头部表(program header table)为可选。从程序的执行来看文件格式,程序头部表告诉系统如何来创建一个进程的内存映象。被用来建立进程映象(执行一个程序)的文件必须要有一个程序头部表,可重定位文件不需要这个头部表。

节区(section)保存着目标文件的信息,从链接视图看,包括指令,数据,符号表和重定位信息等等。从执行视图看,一个段通常包含几个节区,同样保存着指令,数据,符号表和重定位等信息。

节区头部表(section header table)包含了描述节区的信息。每个节区在这个表中有一个入口,该入口给出了节区的名字,大小等等信息。链接过程中的文件必须有一个节区头部表,而在执行视图中这个节区头部表为可选。

目标文件格式支持8位字节/32位体系结构。不过这种格式是可以扩展的,因此,目标文件以某些机器独立的格式来表达某些控制数据,使得能够以一种的公共的方式来识别和解释其内容。目标文件中的其它数据使用目标处理器的编码结构,而不管文件在何种机器上创建。

转载自:https://www.mallocfree.com/interview/windows-1-pe.htm

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值