PE格式
导语
如果你想学习PE病毒的编写,那么你首先得知道PE的格式和他的工作原理。
PE的百度百科
PE(Protable Executable),即可移植的执行体。在Windows操作系统平台下,所有的可执行文件如EXE文件、DLL文件、SYS文件、OCX文件、COM文件等均使用PE结构。
PE文件的结构
一张简简单单的图送给各位
还有一个比较详细的pdf送给各位
链接:https://pan.baidu.com/s/1SGU2AUQvzoHU9Foxx1-UYQ 提取码:ckdi
MS-DOS(DOS头)=MZ文件头+DOS Stub
DOS头的作用是定位文件PE头的开始位置,也可用于PE文件的合法性检查。而在DOS下运行时则会提示用户本文件不能在DOS下运行。在C语言的Windows.h中有预定义的数据结构IMAGE_DOS_HEADER如下
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header |
MZ文件头
MZ文件头是真正意义的DOS头,其中包含了以下文件信息
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header |
DOS Stub
DOS Stub主要是为了兼容DOS操作系统,目的是当这个文件在MS-DOS上运行时提示一段文字,大部分情况下是:This program cannot be run in DOS mode。
PE header
PEheader是PE相关结构NT映像头(IMAGE_NT_HEADER)的简称,其中包含许多PE装载器用到的重要字段。
同样的我们看看C语言中对IMAGE_NT_HEADER的预定义(我们先只研究32位下的咯)
typedef struct _IMAGE_NT_HEADERS { |
提示字串(signature)
这儿就是一个DWORD,内容是50h,45h,00h,00h,也就是ascii的PE空格空格
###映像文件头(FileHeader)
包含了PE文件物理分布信息,比如节数目、后续可选文件头大小、及其类型等等
定义如下
typedef struct _IMAGE_FILE_HEADER { |
###可选文件头(OptionalHeader)
定义了PE文件许多的关键信息,入内存镜像加载地址、程序入口点、节在文件和内存中的对齐粒度、程序在内存中的镜像大小、文件大小等等。定义如下 <span id="jump">
typedef struct _IMAGE_OPTIONAL_HEADER { |
名词解释
ImageBase:PE文件在内存中安装在地址
RVA地址:Relative Virtual Address,相虚拟地址,他是相对于ImageBase的偏移位置。
对齐粒度:文件中节的对齐粒度就是文件一个节的长度,内存中节的对齐粒度就是内存中一个节的长度。
在磁盘中就按照文件的对齐粒度IMAGE_OPTIONAL_HEADER.FileAlignment对齐,在装载到内存中去运行的时候就按照内存中的节的对齐粒度IMAGE_OPTIONAL_HEADER.SectionAlignment对齐。
常见情况下内存的对齐粒度是0x1000,而文件的对齐粒度是0x200,这样做是为了与磁盘的扇区大小和内存单元的大小对齐。
可选文件头中比较重要的关键字
如图
输入表
输入表是记录PE输入函数相关信息的一张表,他记录了PE文件在运行过程中调用动态链接库的一些函数的名称和地址。输入表分为输入名称表(INT)和输入地址表(IAT)。
在之前的IMAGE_OPTIONAL_HEADER那里可以看到有一个IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]的内容,他的数据结构如下,就是简单地偏移量和大小的数据分块,一共分了16块,每一块都有对应的内容。
typedef struct _IMAGE_DATA_DIRECTORY { |
IMAGE_NUMBEROF_DIRECTORY_ENTRIES为2的data directory内装的是一个叫做IMAGE_IMPORT_DESCRIPTOR的数据结构(IID),
typedef struct _IMAGE_IMPORT_DESCRIPTOR { |
Name:输入的DLL的名字指针,它是一个以00结尾的ASCII字符的RVA地址,该字符串包含输入的DLL名。例如:KERNEL32.DLL,或者USER32.DLL。
OriginalFirstThunk指向第一个unbound的INT(import name table,导入名称表),INT指向IMAGE_THUNK_DATA结构
FirstThunk:包含指向输入地址表(IAT)的RVA。IAT也是指向IMAGE_THUNK_DATA结构,他的格式如下。
typedef struct _IMAGE_THUNK_DATA32 { |
ForwarderString 指向一个转向者字符串的RVA;
Function 被输入的函数的内存地址
Ordinal 被输入的API的序数值
AddressOfData又指向一个IMAGE_IMPORT_BY_NAME的数据结构
typedef struct _IMAGE_IMPORT_BY_NAME { |
Hint字段:指示本函数在其所驻留的输出表的中序号该域被PE装载器用来在DLL的输出表里快速查询。该值不是必须的,一些链接器将此值设为0;
NAME字段:这个字段比较重要。它含有输入函数的函数名,函数名是一个ASCII码字符串,并以NULL结尾。注意,这里虽然将NAME的大小定义为字节,其实他是可变的。
在之前的大图也可以看出来,通过IMAGE_DIRECTORY_ENTRY_IMPORT->多个 _IMAGE_IMPRT_DESCRIPTOR->每个指向一个IMAGE_THUNK_DATA->每个指向一个union域
问题来了,OriginalFirstThunk和FirstThunk都指向IMAGE_THUNK_DATA,这是为什么呢?
我们查看IMAGE_THUNK_DATA的数据结构可以发现,union可以取
- DWORD ForwarderString; // PBYTE
- DWORD Function; // PDWORD
- DWORD Ordinal;
- DWORD AddressOfData; // PIMAGE_IMPORT_BY_NAME
在OriginalFirstThunk所指向的IMAGE_THUNK_DATA使用了AddressOfData域,用来指向IMAGE_IMPORT_BY_NAME。他的IMAGE_THUNK_DATA的数组被称作INT(import name table,导入名称表)
而FirstThunk所指向的IMAGE_THUNK_DATA使用了Function域,用来指向函数的实际地址。他的IMAGE_THUNK_DATA的数组被称作IAT表(import address table,导入地址表)
找到这张表有两种方式,一种就是通过导入表这里找到,第二种就是通过数据目录表,倒数第三个项就是指向的 IAT 表,IMAGE_DIRECTORY_ENTRY_IAT,也是以0结束。在文件加载前,这两个表存的内容完全一致,都是存储的IMAGE_THUNK_DATA
在加载前,不论是INT还是IAT的IMAGE_THUNK_DATA结构都只存一个 RVA,这个 RVA 指向上图的IMAGE_IMPORT_BY_NAME 结构
在加载后,IAT 表变为存储函数的地址了,在调用导入函数的时候,就可以call这个地址了。
节表
顾名思义,是存放节表项的,相当于节的目录。
节表项包含 节名、节在文件和内存中的开始地址、长度、节属性等。
typedef struct _IMAGE_SECTION_HEADER { |
可能大家会有疑问,virtualaddress和pointertorawdata的区别是什么,其实很简单,VirtualAddress是指内存中的rva(相对虚地址),PointerToRawData是指文件中的偏移
节属性
0x00000020? 这个块包含代码。置位
0x00000040? 这个块包含已初始化的数据
0x00000080? 这个块包含未初始化的数据(如.bss 块)
0x00000200? 这个块包含注释或其它的信息。
0x00000800? 这个块的内容不应放进最终的EXE文件中。
0x02000000? 这个块可以被丢弃,因为一旦它被载入,其进程就不需要它。最通常的可丢弃块是基本重定位块(.reloc )。
0x10000000? 这个块是可共享的。
0x20000000? 这个块是可执行的。
0x40000000? 这个块是可读的。
0x80000000? 这个块是可写的。
节
节就是程序主体了,一般PE文件有很多节,比较常见的有
代码节
数据节
引入函数节
资源节等(如图标)
引出函数节(DLL文件中常见)
重定位节(DLL文件中常见)
PE程序执行流程
PE程序主要依赖PE程序装载器,在检查了头部信息(包括引入函数表的地址信息装入等等)之后会读取节表,根据节表来动态的将各节的内容装入内存中。这个过程中会自动的修改代码段中的jmp的地址值使它能够成功。
这点儿知识对下个PE病毒编写有好处,尽请期待。
参考:
https://blog.csdn.net/qq_39805477/article/details/121236663
https://blog.csdn.net/qq_40422314/article/details/89741040
https://www.cnblogs.com/2f28/p/9800992.html