免杀对抗--PE文件结构
前言
欢迎来到我的博客
个人主页:北岭敲键盘的荒漠猫-CSDN博客
本文整理windows执行文件PE的结构
帮助大家更好的了解Windows的免杀对抗
理解PE
PE就是Windows的可执行程序,常见的后缀有exe,dll,sys等。
我们所说的PE文件结构可以这么理解。
PE文件就是一大坨二进制数据,我们学习PE文件结构实际上就是去分辨哪个区域的二进制数据是做什么的。
图解PE文件
这个结构要从下往上看哈。
PE文件查看工具
前面说了,学PE文件结构实际上就是学PE文件这一大坨的二进制数据中,哪部分是做什么内容。
010 Editor
如果要看程序的二进制数据可以用010 editor查看。
打开010直接拖进去就行了。
010还有各种插件,用来辅助识别常见的后缀的各个区块
studyPE
有这样一个情况,我们拿一个PE文件就010打开,看里面一坨一坨的二进制,那肯定是相当麻烦,所以就可以用一些辅助工具直接帮我们分析。
studyPE是一个比较推荐的工具。
用起来也比较简单,我们直接把exe,dll之类的拖进去就可以了。
可以很方便的看到PE的文件结构。
winnt.h简介
C语言点进去,在里面能看见PE的全部结构。
PE文件结构
DOS部首
作用:
兼容早期MS-DOS系统,确保PE文件在DOS环境下运行时能显示提示信息(如“此程序不能在DOS模式下运行”)。
总大小不确定
DOS MZ头
全部结构如下:
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE headerWORD e_magic; // Magic numberWORD e_cblp; // Bytes on last page of fileWORD e_cp; // Pages in fileWORD e_crlc; // RelocationsWORD e_cparhdr; // Size of header in paragraphsWORD e_minalloc; // Minimum extra paragraphs neededWORD e_maxalloc; // Maximum extra paragraphs neededWORD e_ss; // Initial (relative) SS valueWORD e_sp; // Initial SP valueWORD e_csum; // ChecksumWORD e_ip; // Initial IP valueWORD e_cs; // Initial (relative) CS valueWORD e_lfarlc; // File address of relocation tableWORD e_ovno; // Overlay numberWORD e_res[4]; // Reserved wordsWORD e_oemid; // OEM identifier (for e_oeminfo)WORD e_oeminfo; // OEM information; e_oemid specificWORD e_res2[10]; // Reserved wordsLONG e_lfanew; // File address of new exe header} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
字节长度:64字节
粉色区域部分,一行16字节,四行,共64字节。
关键结构如下:
e_magic
:
占用2字节
固定值0x5A4D
(ASCII为"MZ"),标识DOS可执行文件
e_lfanew
:
指向NT头的偏移地址(位于DOS头偏移0x3C
处
用于指向PE文件头的位置。
因为DOS stub的大小不确定。
而需要知到下一个PE文件头的位置,就需要使用这个记录下PE文件的位置。
这四个字节要倒着读,即00 00 01 08也就是108
所以DOS stub之后的PE部分的开头就是108位置。
DOS stub
大小:不确定
直观理解:
实际上就是DOS跟win16位系统执行的程序。
当本现代程序出现在了老版系统中的时候就会执行这些老版的程序,报出错误,告诉老系统这个程序是新版程序,不能在他的系统中执行。
避免了出现不可预见的错误。
PE文件头
宏观图标
代码层面
主要三部分。
Signature
大小:固定4字节
作用:标识PE文件头的初始部分。
描述:
可以通过MZ头后的e_lfanew找到这个部分。
FileHeader
大小:固定20字节
结构包括如下部分
Machine
大小:2字节
作用:描述这个程序可以执行在哪个平台上。
0:代表任意都可以
64平台:86 64
NumberOfSections
大小:2字节
作用:
描述程序有几个区块(节表),区块可以理解为程序的分类箱。.text箱子存放代码,.rsrc箱子存放图片等资源,.data存放变量等。他们的权限又不同,比如.text中的代码肯定是要有执行权限的,但是给.rsrc一个执行权限那就有点搬石头打自己脚了。
2字节,图中倒着读代表了7个区块。
TimeDateStamp
大小:4字节
作用:
记录程序的创建时间戳。
PE文件采用小端存储,真实我们要的时间戳是68 3D 50 F8
转化为十进制1748848888
转化为真实时间:2025/06/02 15:21:28
PointerToSymbolTable
大小:4字节
作用:COFF 符号表的 RVA 偏移量,如果 COFF 符号表不存在,则该值为 0
不怎么用,没啥必要。
这里位00 00 00 00就是没有
NumberOfSymbols
大小:4字节
作用:COFF 符号表中的符号个数
跟上面一样用处不大,这里也是0
SizeOfOptionalHeader
大小:2字节
作用:标识_IMAGE_OPTIONAL_HEADER的大小
F0转化为10进制为240
一般来说,32位是E0(224),64位是F0(240)。
Characteristics
大小:2字节
作用:描述文件属性
读取方法:
把16进制转化为2进制,这里是0022转化为0000 0000 0010 0010
对照着这个图比对1的位置
在1,5处为1,所以这个文件属性对应数据位为1,5的属性
1:文件可执行
5:应用程序可处理大于2GB的地址
OptionalHeader
大小:由SizeOfOptionalHeader标识,本程序为240字节。
代码层面
Magic
大小:2字节
作用:描述这个程序是32位还是64位
32位:10BH
64位:20BH
ROM映射文件:107H
这个程序是020B是64位的。
节表
IMAGE_SECTION_HEADERS
情况:
由多个IMAGE_SECTION_HEADER构成。
大小:
一个IMAGE_SECTION_HEADER40字节,有多个。
数量:
由FileHeader中的NumberOfSections决定。
一个IMAGE_SECTION_HEADER的结构:
typedef struct _IMAGE_SECTION_HEADER {BYTE Name[IMAGE_SIZEOF_SHORT_NAME];union {DWORD PhysicalAddress;DWORD VirtualSize;} Misc;DWORD VirtualAddress;DWORD SizeOfRawData;DWORD PointerToRawData;DWORD PointerToRelocations;DWORD PointerToLinenumbers;WORD NumberOfRelocations;WORD NumberOfLinenumbers;DWORD Characteristics; } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
功能简介
1. 基础信息字段
字段名 | 数据类型 | 功能描述 |
---|---|---|
Name | BYTE[8] | 节名称(ASCII字符串),长度≤8字节,不足则用0 填充(如.text 、.data ) 。 |
Misc | Union | 联合体,包含两个等效成员:<br> • VirtualSize :节在内存中的实际大小(未对齐)<br> • PhysicalAddress (极少用) 。 |
VirtualAddress | DWORD | 节加载到内存后的 相对虚拟地址(RVA) ,需按SectionAlignment 对齐 。 |
2. 文件与内存布局字段
字段名 | 数据类型 | 功能描述 |
---|---|---|
SizeOfRawData | DWORD | 节在磁盘文件中的实际大小,需按FileAlignment 对齐 。 |
PointerToRawData | DWORD | 节在磁盘文件中的起始偏移量,用于定位原始数据 。 |
PointerToRelocations | DWORD | 重定位表在文件中的偏移(仅OBJ文件有效,PE文件中通常为0) 。 |
PointerToLinenumbers | DWORD | 行号表在文件中的偏移(仅调试信息有效) 。 |
NumberOfRelocations | WORD | 重定位条目数量(仅OBJ文件有效) 。 |
NumberOfLinenumbers | WORD | 行号条目数量(仅调试信息有效) 。 |
3. 属性标志字段
字段名 | 数据类型 | 功能描述 |
---|---|---|
Characteristics | DWORD | 节属性标志位,通过位运算组合(如可执行/可读/可写) |
Characteristics运算
这是一个区块,最后四字节是节表属性。
十六进制:60 00 00 20
换算二进制为:
110 0000 0000 0000 0000 0000 0010 0000
按照1的位数对照表格判断
6和30,31为1
分别代表途中的信息
已初始化数据,可读可写但是不可执行。(毕竟区块名为text就是文本嘛)
节数据
描述:
可以理解为节表就是书本的目录,节数据就是指定的一章节的具体内容。
定位节数据:
既然这个关系跟书本很像,那么我们要寻找节数据就需要通过目录寻找。
节表中的SizeOfRawData跟PointerToRawData分别代表了节数据的大小跟初始位置。
这个节表中
SizeOfRawData
值为2A600
PointerToRawData
值为400
所以这个节数据的位置就是初始偏移量在400位置,大小是2A600