可执行文件

可执行文件,是由操作系统进行加载执行的文件,不可执行文件,如jpg,png需要由其它软件加载

在windows平台下:PE文件结构
在linux平台下:ELF文件结构

PE文件的指纹特征

受PE结构影响以十六进制打开PE文件时:

文件开头是4d5a(MZ)
在地址3C处存有指向PE文件头标识的地址数据。PE 00

不能只看文件后缀exe

PE文件结构

PE文件有四个部分组成:DOS部分,PE文件头,节表,节数据。
在PE文件中,其中使用了很多结构体,我们需要知道结构体的作用。
在这里插入图片描述

DOS部分

DOS部分又分为两个部分,DOS MZ文件头和DOS块。

DOS MZ文件头

DOS MZ文件头本质是一个IMAGE_DOS_HEADER结构体,在内存中大小为64字节。
以记事本程序为例,路径为c:windows\system32\notepad.exe,大家可以自己尝试。
此时选中的部分就是64字节,都是DOS MZ部分的数据
在这里插入图片描述
在结构体中,含有以下成员,其中现在使用的成员只有第一个(标记文件是否为可执行)和最后一个(指向PE文件头的标识PE00),其它的成员可以任意修改,每一个成员前面为对应地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct _IMAGE_DOS_HEADER{
0X00 WORD e_magic; MZ用于标记是否是可执行文件
0X02 WORD e_cblp; //Bytes on last page of file
0X04 WORD e_cp; //Pages in file
0X06 WORD e_crlc; //Relocations
0X08 WORD e_cparhdr; //Size of header in paragraphs
0X0A WORD e_minalloc; //Minimun extra paragraphs needs
0X0C WORD e_maxalloc; //Maximun extra paragraphs needs
0X0E WORD e_ss; //intial(relative)SS value
0X10 WORD e_sp; //intial SP value
0X12 WORD e_csum; //Checksum
0X14 WORD e_ip; //intial IP value
0X16 WORD e_cs; //intial(relative)CS value
0X18 WORD e_lfarlc; //File Address of relocation table
0X1A WORD e_ovno; //Overlay number
0x1C WORD e_res[4]; //Reserved words
0x24 WORD e_oemid; //OEM identifier(for e_oeminfo)
0x26 WORD e_oeminfo; //OEM information;e_oemid specific
0x28 WORD e_res2[10]; //Reserved words
0x3C DWORD e_lfanew; 指向PE文件头的标识
};

其中第一个成员和最后一个成员依次对应如下:
在这里插入图片描述
在这里插入图片描述
问题一:修改数据无法打开。

DOS块

dos块大小不确定,给连接器使用。
范围是从DOS MZ文件头后面,到PE文件头之间

从64字节到3c所指向的地址处,内容可以修改

PE文件头

PE文件头是结构体IMAGE_NT_HEADERS,其中含有三个成员:
Signature 4个字节,标准PE头 20个字节,扩展PE头 分32和64位

1
2
3
4
5
6
struct _IMAGE_NT_HEADERS{
DWORD Signature; 这里是PE文件标识,对应数据PE 00,占四个字节
_IMAGE_FILE_HEADER FileHeader; 这里是标准PE头,占20个字节
_IMAGE_OPTIONAL_HEADER32 OptionalHeader; 这里是扩展PE头,大小由标志PE头的成员决定。
//或者_IMAGE_OPTIONAL_HEADER64 OptionalHeader;
};

PE标识

Signature:PE标识,对应PE 00,占四个字节

不能更改

标准PE头

_IMAGE_FILE_HEADER FileHeader

20个字节

选中部分即标准PE头
在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;//运行平台 014C为intel386以及后续上
//8664为x64,其它CPU为0
WORD NumberOfSections;//文件的节的数目
DWORD TimeDateStamp;//文件编译时的时间戳
DWORD PointerToSymbolTable;//调试相关属性
DWORD NumberOfSymbols;//调试相关属性
WORD SizeOfOptionalHeader;//IMAGE_OPTIONAL_HEADER32结构大小
//32位0xE0,64位0xF0
WORD Characteristics;//文件属性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

上面可以看到:

  1. Machine是8664(小端序),为x64机器上
  2. NumberOfSections这里有七个节
  3. TimeDateStamp时间戳,不是创建时间,修改时间。可以自己修改
  4. PointerToSymbolTable,用于调试
  5. NumberOfSymbols,用于调试
  6. SizeOfOptionalHeader函数决定扩展PE头的大小。32位为E0,64位为F0
  7. Characteristics,最后选中的为十六进制的0022,转化为二进制0000 0000 0010 0010,然后根据属性图查相关属性
    在这里插入图片描述

扩展PE头

_IMAGE_OPTIONAL_HEADER32
或者
_IMAGE_OPTIONAL_HEADER64

32位:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
typedef struct _IMAGE_OPTIONAL_HEADER {
**WORD Magic; //PE的位数,10B是32位,20B是64位
BYTE MajorLinkerVersion; //链接器版本号
BYTE MinorLinkerVersion; //链接器版本号
DWORD SizeOfCode; //所有代码节的总和,文件对齐后的大小,由编译器填写,没有用
DWORD SizeOfInitializedData; //包含所有已经初始化数据的节的总大小,文件对齐后的大小,由编译器填写,没有用
DWORD SizeOfUninitializedData; //包含未初始化数据的节的总大小,文件对齐后的大小,由编译器填写,没有用
**DWORD AddressOfEntryPoint; //程序入口,配合内存镜像基址使用
DWORD BaseOfCode; //代码开始的基址,由编译器填写
DWORD BaseOfData; //数据开始的基址,由编译器填写
**DWORD ImageBase; //内存镜像基址,决定内存展开的位置
**DWORD SectionAlignment; //内存对齐
**DWORD FileAlignment; //文件对齐
WORD MajorOperatingSystemVersion;//标识操作系统版本号,主版本号
WORD MinorOperatingSystemVersion;//标识操作系统版本号,次版本号
WORD MajorImageVersion; //PE文件自身的版本号
WORD MinorImageVersion; //PE文件自身的版本号
WORD MajorSubsystemVersion; //运行所需子系统版本号
WORD MinorSubsystemVersion; //运行所需子系统版本号
DWORD Win32VersionValue; //子系统版本的值,必须为0
**DWORD SizeOfImage; //内存中整个PE文件的映射尺寸,可以比实际的值大,必须是SectionAlignMent(内存对齐)的整数倍
**DWORD SizeOfHeaders; //所有头+节表按照文件对齐后的大小,否则加载会出错
**DWORD CheckSum; //校验和,一些系统文件由要求用来判断文件是否被修改,不是所有程序都有。重要dll,判断有没有被改过。整个PE文件以2个字节相加,加完以后如果溢出不管,得到的结果+文件长度=校验和
WORD Subsystem; //子系统,驱动程序:1、图形界面:2、控制台或者DLL:3
WORD DllCharacteristics; //文件特征,不是针对DLL文件的
DWORD SizeOfStackReserve; //初始化时保留的栈大小
DWORD SizeOfStackCommit; //初始化时实际提交的栈的大小(初始化时真正使用的栈的大小)
DWORD SizeOfHeapReserve; //初始化时保留的堆大小
DWORD SizeOfHeapCommit; //初始化时实践提交的堆的大小(初始化时真正使用的堆的大小)
DWORD LoaderFlags; //调试相关
DWORD NumberOfRvaAndSizes; //目录项目数量,当前程序会用到各种表,如导入表导出表等,描述有多少个这种表
IMAGE_DATA_DIRECTORY DataDirector //结构体数组,NumberOfRvaAndSizes的长度是多少它的长度就是多少 DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
  1. ImageBase+AddressOfEntryPoint = 运行在内存中的地址
  2. FileAlignment,文件对齐是硬盘中
  3. SectionAlignment,内存对齐是在内存中
  4. 关于文件特征DllCharacteristics,也是转二进制查属性

在这里插入图片描述

节表

节表有多个结构体,每一个结构体占用40字节,对应节数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
typedef struct _IMAGE_SECTION_HEADER
{
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; //节的名字,只取8个字节
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc; //双字 是该节在没有对齐前的真实尺寸,该值可以不准确。
DWORD VirtualAddress; // 节区在内存中的偏移地址
DWORD SizeOfRawData; // 在文件中对齐后的尺寸
DWORD PointerToRawData; // 在文件中的偏移
DWORD PointerToRelocations; // 在OBJ文件中使用
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics; // 节区属性字段
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;