PE学习

PE文件结构

1.可执行文件

windows平台:PE(Portable Executable)文件结构

  • 病毒与反病毒
  • 外挂与反外挂
  • 加壳与脱壳
  • 无源码修改功能、软件汉化等

Linux平台:ELF(Executable and Linking Format)文件结构

2.如何识别PE结构

PE文件的特征(PE指纹)

.exe文件 .dll文件 .sys文件image-20240404142236842

​ 上图是.exe文件例子,先看开头的4D 5A(MZ)特征,随后找到3C位置的数字为F0,再找到F0位置的50 45(PE)特征,即可判断此文件为PE结构文件。

不能仅仅通过文件后缀名来认定PE文件。

3.PE文件的整体结构

PE文件结构笔记 - 知乎

节表就像一个说明书,定义了一个个节,节数据部分存储节的内容。

PE文件的两种状态

1.主要结构体

PE文件结构笔记 - 知乎

IMAGE_DOS_HEADER结构体(DOS MZ 文件头)(64)

image-20240405101939336

例子

image-20240405101458399

在目前的32位和64位平台中,只有标红的两个成员有作用,其余的成员即便修改也不影响程序运行。

DOS_STUB(DOS块)

image-20240405102209744

到PE头之前,可以修改,不影响运行

PE头结构体

image-20240405102646097

Signature 占用四个字节,PE标识,不能改动

IMAGE_FILE_HEADER(标准PE头)(20)

image-20240405102657515

characteristics文件特征

image-20240408220015364

IMAGE_OPTIONAL_HEADER(拓展PE头)(224,240)

image-20240405102719386

-拓展PE头大小可以修改

SizeOfHeaders大小一定是FileAlignment(文件对齐)整数倍

IMAGE_SECTION_HEADERA(节表)(40)

image-20240405104129583

VirtualSize可能比SizeOfRawData大,也可能比它小。

举例:在节中存储的没有初始值的全局变量,在文件中不分配内存,在内存中要分配内存。如果这种无初值全局变量较多,则真实大小会比在文件中对齐的大小要大。在内存中展开时按两者相对较大一方的大小展开。

image-20240414104855544

2.PE文件的两种状态

image-20240405110335435

在程序运行时,块的大小遵循内存对齐(SectionAligmment)

内存对齐的目的是为了提高CPU读写内存里数据的速度。现代的CPU读取内存并不是一个一个字节挨着读取,这样做的效率非常低。现代的CPU一般以4个字节(32bit数据总线)或者8个字节(64bit数据总线)为一组,一组一组地读写内存里的数据。

内存对齐的目的是以空间换取速度,内存对齐时操作一块只需要一次读取,如果一个块分在两个内存空间,就需要进行两次读取来操作这个块,会大大降低效率。

RVA与FOA的转换

RVA(相对虚拟地址)= 内存地址 - ImageBase

FOA(文件偏移地址)

(1)判断RVA是否在头部,在的话直接返回(或文件对齐和内存对齐相同)

​ FOA = = RVA

(2)判断RVA位于哪个节

​ RVA >= 节.VirtualAddress

​ RVA <= 节.VirtualAddress+当前节内存对齐后的大小

​ 差值 = RVA - 节.VirtualAdress

(3)FOA = 节.PointerToRawData + 差值

空白区添加代码

内存中 E8(call) 要跳转的地址 - E8指令当前地址 - 5

​ E9(jmp)

(1)构造要写入的代码

(2)在PE的空白区写入构造的代码

(3)修改入口地址为新增代码

(4)新增代码执行后,跳回原入口地址

扩大节

扩大节是为了能在空白区能加入更多的代码。

扩大节一般是扩大最后一个节。

(1)分配一块新的空间,大小为S

(2)将最后一个节的SizeOfRawData和VirtualSize改成N

​ N=(SizeOfRawData或VirtualSize内存对齐后的值)+ S

(3)修改SizeOfImage大小

如果节没有可执行属性,要在characteristics中修改节属性改为可执行。

SizeOfImage通常是依据VirtualSize来计算的。

新增节

(1)判断是否有足够的空间,可以增加一个节表

(2)在节表中新增一个成员

(3)修改PE头中节的数量

(4)修改SizeOfImage的大小

(5)在原有数据的最后,新增一个节的数据(内存对齐的整数倍)

(6)修正新增节表的属性

合并节

(1)按照内存对齐展开

(2)将第一个节的内存大小、文件大小改成一样

​ Max = SizeOfRawData > VirtualSize?SizeOfRawData:VirtualSize

​ SizeOfRawData = VirtualSize =

​ 最后一个节的VirtualAddress + Max - SizeOfHeaders内存对齐后的大小

(3)将第一个节的属性改为包含所有节的属性

(4)修改节的数量为1

导出表

  • 一个可执行程序是由一组PE文件组成的

  • .dll是动态链接库

  • .dll还可能用到其他的.dll

  • 导出表存储的是当前的PE文件提供了哪些函数给其他文件用

  • 通常情况下exe不提供导出表,但是可以提供导出表

  • dll既有导出表也有导入表

(1)如何定位导出表

导出表是PE头拓展头中的IMAGE_DATA_DIRECTORY的第一个结构体数组

image-20240503194112029

导出表结构

image-20240503194129252

序号表中的成员与名称表中的成员相同

序号表成员是word ,地址表和名称表成员都是dword

搜索函数过程

按名字找:函数名称表映射到序号表,再映射到地址表

没名字的函数:由导出函数起始序号向下查询,直接查询地址表

image-20240516091807813

导入表

1 如何通过导入表确定依赖模块

导入表是PE头拓展头中的IMAGE_DATA_DIRECTORY的第二个结构体数组

image-20240516091218889

导入表结构

image-20240516092336334

导入表结束的标志是20个0(00 00 00 00 00 00 00 00 00 00)

Name成员用来描述所依赖的模块名字

Name成员指向的地址 存储模块名字的ASCII码字符串,到0截至

2 确定依赖函数

不仅要确定依赖的模块,还要确定依赖模块中的哪些函数

OriginalFirstThunk和FirstThunk指向的表不一样,但是表中的内容是一样的(pe文件加载前)

以OriginalFirstThunk为例(其中每个结构体占4字节)指向 INT表 0为结束符

image-20240516095601890

image-20240516095712510

Hint可能为空,编译器决定,如果不为空 则是函数在导出表中的地址索引

Name[] 函数名称,以0结尾 (函数名字长度不确定,所以只存储第一个字节)

3 确定函数地址

使用其他dll中的函数时,都使用间接call,call指向IAT表(导入地址表)

在pe文件加载后,IAT表中内容发生变化,直接指向函数地址

4 额外知识

一、结构体struct

各成员各自拥有自己的内存,各自使用互不干涉,同时存在的,遵循内存对齐原则。一个struct变量的总长度等于所有成员的长度之和。

二、联合体union

各成员共用一块内存空间,并且同时只有一个成员可以得到这块内存的使用权(对该内存的读写),各变量共用一个内存首地址。因而,联合体比结构体更节约内存。一个union变量的总长度至少能容纳最大的成员变量,而且要满足是所有成员变量类型大小的整数倍。不允许对联合体变量名U2直接赋值或其他操作。

重定位表

  • 重定位表是为了防止 模块在非自身imagebase基础上展开时,模块内部地址无法正确定位
  • 重定位表中会记录模块中所有需要修正的地址
  • 系统每次加载都是不固定的,有了重定位表,模块就可以在内存不同位置展开,不影响使用

导入表是PE头拓展头中的IMAGE_DATA_DIRECTORY的第六个结构体数组

image-20240516101647447

重定位表结构

image-20240516103244679

SizeOfBlock以字节为单位,代表了当前块的大小

重定位表区以 00 00 00 00结束

每个物理页大小为4kb(2^12),所以搜索地址只需要12个二进制位

注入shellcode

shellcode是不依赖环境,放到任何地方都可以执行的机器码

编写规则:

  • 不能有全局变量
  • 不能使用常量字符串
  • 不能使用系统调用
  • 不能嵌套调用其他函数

字符串示例

char szBuffer[] = {'c','h','i','n','a','\0'};
//这样字符串数据会直接生成在堆栈里,而不是在使用时把一整个字符串从另一处mov过来

本文作者:yee-l

本文链接:https://www.cnblogs.com/yee-l/p/18232909

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   yee-l  阅读(20)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起