PE文件结构1

本文详细介绍了WindowsPE文件格式、常用的反汇编和调试工具如C32Asm、WinHex、Ollydbg和IDA的使用方法,涵盖了PE头、节区表、RVA与文件偏移的转换等内容,以及动态调试中的技巧,为IT技术人员提供实用的工具和技术指导。
摘要由CSDN通过智能技术生成

1.工具

1.C32Asm、WinHex、010Editor:

16进制编辑器,可以十分方便的对二进制文件进行各种编辑操作,同时支持反汇编操作。

2.PEiD

PEiD是一款查壳工具,可以查看PE文件的加壳信息,如果文件没有被加壳,则可以识别PE文件的编译器信息。查看PE文件的节表以及对应的节区数据。Section又称为“节”、“节区”。

3.Ollydbg(动态调试)

Ollydbg简称OD,是一款具有图形用户界面的用户模式调试器,可以运行于各种主流Windows操作系统下。Ollydbg具有动态调试和静态分析功能,

常用的快捷键有:
F2 设置一个断点(如果断点已经存在,那么断点将被删除)
F4 运行到光标所在行(运行到光标所在行时自动断下)
F7 单步跟踪(如果遇到一个call,则跟踪进入)
F8 单步跟踪(如果遇到一个call,则执行完整个call)
F9 继续执行(运行程序,直到进程退出或遇到下一个断点)

1.弹出字符串列表窗口

查看是否存在有我们感兴趣的字符串。比如前面曾经提示“密码错误”的提示,那么我们就可以按下Ctrl+F,在弹出的窗口中输入“密码错误”,然后单击确定按钮查找。

2.“查找”——“当前模块中的名称(标签)”,在键盘上敲下MESSAGEBOXA,就会自动定位到MessageBoxA,点击鼠标选中MessageBoxA这一行,单击右键,在弹出的右键菜单中选择“在每个参考上设置断点”,如图所示:

在OD最下方的状态栏上可以看到“已设置 2 个断点”的提示。现在按F9运行程序,随便给程序输入一个密码(如test),单击确定按钮后程序将在OD中断下,断下的位置为对MessageBoxA的调用的位置。

3.对特定的API设置一个断点
Ctrl+G快捷键,在弹出的对话框中输入MessageBoxA,单击“确定”按钮后就可以来到MessageBoxA的函数代码了,我们对其第一条指令设置一个断点,即在77D507EA按下F2即可。

在OD左下角的Command窗口中输入bp命令也可以下断点,比如输入bp MessageBoxA,按下Enter就可以对MessageBoxA设置一个断点了。

4.IDA(静态分析)

IDA是一款交互式反汇编工具,其功能十分强大,支持多操作系统、多处理器下的二进制程序的反汇编分析,并且可以和使用者进行交互来提升处理效率。支持将反汇编代码直接转换为C语言伪代码,极大的提高了反汇编分析人员的工作效率。

IDA常用快捷键:
空格 在图形模式和列表视图模式之间切换反汇编视图
F5 将反汇编指令还原为伪代码
x 查看交叉引用
n 对变量名或者函数名进行重命名操作
d 将二进制数据解释为字节/双字/四字
c 将二进制数据解释为代码
a 将二进制数据解释为字符串

2.PE文件格式

PE(Portable Executable)文件格式是Windows平台下可执行文件的统一格式规范,扩展名为*.exe、*.dll、*.ocx、*.sys等的文件都属于PE文件。PE文件由DOS头部、DOS Stub模块、PE文件头、节区表、节区数据块以及尾部附加的调试信息几个部分组成,PE文件的总体结构图如下图所示:

3.IMAGE_DOS_HEADER结构

1.该结构的具体定义如下:

typedef struct _IMAGE_DOS_HEADER {
WORD e_magic; // MZ标志
WORD e_cblp;WORD e_cp;WORD e_crlc;WORD e_cparhdr;
WORD e_minalloc;WORD e_maxalloc;WORD e_ss;WORD e_sp;
WORD e_csum;WORD e_ip;WORD e_cs;
WORD e_lfarlc;WORD e_ovno;WORD e_res[4];WORD e_oemid;
WORD e_oeminfo;WORD e_res2[10];
LONG e_lfanew; // PE文件头偏移
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

2.字节
3.大端模式和小端模式

对于文件中相邻的四个字节0x12, 0x34, 0x56, 0x78,其中0x12存放于文件的头部,0x78存放于文件的尾部,如果把这四个字节表示为一个DWORD类型的数据,如果是小端模式,则值为0x78563412,而如果是大端模式,则值为0x12345678。对小端模式而言,文件低地址处的数据表示DWORD中低字节的数据,而文件高地址处的数据表示DWORD中高字节的数据。简称“高存高,低存低”。关系如图所示:

4. e_lfanew字段


右侧窗口中自动选中E0 00 00 00,小端模式下表示为0x000000E0,这就是IMAGE_DOS_HEADER结构中e_lfanew成员的值,它表示PE头的文件偏移值为0x000000E0,在右侧十六进制数据窗口的0xE0位置即可找到PE文件头。

4.PE头之IMAGE_FILE_HEADER

1.IMAGE_NT_HEADERS

PE头在WinNt.h头文件中的结构体名称为IMAGE_NT_HEADERS。在IMAGE_DOS_HEADER和IMAGE_NT_HEADERS之间存在一个叫做DosStub的数据块,DosStub实际上是一个DOS程序,当PE文件运行在DOS模式下时这个程序将被执行,它的大小不固定,由此造成PE头的位置也不是固定的,需要动态进行解析。当PE文件运行在DOS模式下时,DosStub通常输出一句话“This program cannot be run in DOS mode.”然后退出。对于PE文件而言,DosStub可有可无,可以直接将其填充为00字节

2.IMAGE_FILE_HEADER结构

IMAGE_NT_HEADERS结构体在WinNT.h头文件中的定义如下所示:
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

(1)Signature字段表示PE文件的一个Magic标志(IMAGE_DOS_HEADER的e_magic也是PE文件的Magic标志)。Signature的值固定为50 45 00 00,在小端模式下表示为0x00004550,字符串表示为“PE\0\0”

已经知道了PE文件的两个Magic标志,现在给定任意一个文件,通过这两个标志的检查就可以判断其是否是PE文件了,判断的过程如下:
1. 读取文件头部的0x40字节的数据,判断这一块数据的前两个字节是否为MZ,如果不是MZ,则表明不是PE文件;
2. 取出第一步读取的0x40字节数据块的最后4字节数据,将其转换为文件偏移值;
3. 根据第二步计算出来的文件偏移值再读取4字节数据,判断这4字节数据是否为PE\0\0,如果不是,则表明不是PE文件;
4. 到这里,基本可以判定该文件是一个PE文件;

(2)该结构的第二个成员为FileHeader,对应的类型为IMAGE_FILE_HEADER,该结构体在WinNt.h头文件中的定义如下:
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;        //占用2个字节,指明PE文件支持的CPU类型;

WORD NumberOfSections;     //占用2个字节,指明PE文件包含的节区数量,对notepad.exe而言,这个值为3,因为其包含.text、.data以及.rsrc三个区块;
DWORD TimeDateStamp;     //占用4个字节,指明文件的创建时间;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;    //指明在IMAGE_NT_HEADERS中紧跟在FileHeader后的OptionalHeader的大小,对于32位的PE文件而言,这个值的大小通常为0x00E0;
WORD Characteristics;     //表明文件的属性,如果Characteristics & 0x2000 = 0x2000,那么表明这是一个DLL文件(在WinNT.h头文件中定义了#define IMAGE_FILE_DLL 0x2000这样一个宏);
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

3.几个重要概念

(1) 文件偏移地址(File Offset)
PE文件存储于磁盘上时,某个数据相对于文件头部的偏移量,第一个字节的偏移量是0,第二个字节的偏移量是1,最后一个字节的偏移量是文件的大小减去1。

(2)基地址(ImageBase)
PE文件被加载到内存空间时的起始地址,在PE文件中会有一个默认的基地址值,表示当被加载到内存时希望加载到这个起始地址,其中EXE文件默认的基地址通常为0x00400000,而DLL默认的基地址通常为0x10000000。PE文件被加载时,实际的基地址可以和PE文件中预设的默认基地址不一样。

(3)程序入口点(EntryPoint)
EXE或者DLL被加载后,CPU将要执行第一条指令的位置。在LordPE中看到notepad.exe的入口点为0x0000739D。是相对虚拟地址(Relative Virtual Address,RVA)
(4)虚拟地址
OllyDbg截图中看到的0x0100739D就是一个虚拟地址(VA内存地址)。

VA - ImageBase = RVA

5.PE头之IMAGE_OPTIONAL_HEADER解析

1.OptionalHeader结构

OptionalHeader的大小并不固定,其大小由FileHeader中的SizeOfOptionalHeader字段来决定。

2.重要字段

1.AddressOfEntryPoint,程序执行的入口点地址,是一个RVA值;
2.ImageBase,重要字段,PE文件在内存中首选的装载基地址;

在IMAGE_NT_HEADERS结构中,Signature占用4字节,FileHeader占用20字节,因此OptionalHeader的文件偏移值就是0xE0 + 4 +20 = 0xF8;根据FileHeader中的SizeOfOPtionalHeader字段,我们可以知道可选头的大小为0xE0(即224)字节

基地址对应ImageBase字段,其在OptionalHeader结构中的偏移值为28,占用4字节数据;入口地址对应AddressOfEntryPoint字段,其在OptionalHeader结构中的偏移值为16,占用4字节数据。

3.子系统查看
子系统对应Subsystem字段,其在OptionalHeader结构中的偏移值为68,占用2字节数据。最常遇到的类型为“Windows图形用户界面(GUI)子系统”以及“Windows字符模式(CUI)子系统”,通俗的理解就是图形界面程序以及控制台程序,前者对应的Subsystem的值为2,后者对应的Subsystem的值为3

4.SizeOfImage验证
在OptionalHeader结构中的偏移值为56,占用4字节大小的数据。注意SizeOfImage表示的是PE文件被装载到内存空间后所占用的大小,并不是指PE文件在磁盘上占用的空间大小。

5.数据目录表结构学习
OptionalHeader的末尾是一个数据目录表数组,即类型为IMAGE_DATA_DIRECTORY的数组DataDirectory,数组的元素个数为16,各个元素所对应的表项如下图所示:

IMAGE_DATA_DIRECTORY结构体在WinNt.h头文件中的定义如下:
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
其中VirtualAddress为数据块的起始RVA地址,Size为数据块的长度。数据目录表中比较重要的表项有输出表、输入表、资源表以及重定位表等。

6.节表头解析

每个节区都会有一个对应的节表头,节表头以及节区的数量都是由IMAGE_NT_HEADERS.FileHeader.NumberOfSections字段的值所决定的。节表头对应的结构体为IMAGE_SECTION_HEADER,该结构体在WinNT.h头文件中的定义如下所示:
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;       //
在未对齐的情况下,区块所有数据的大小
} Misc;
DWORD VirtualAddress;     //
区块被装载到内存时的RVA
DWORD SizeOfRawData;       //区块数据在磁盘文件中按照FileAlignment对齐后的大小
DWORD PointerToRawData;      //区块数据在磁盘文件中的偏移地址
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;     //
区块的属性值,表明区块的可读、可写、可执行等相关属性
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

根据IMAGE_FILE_HEADER的SizeOfOptionalHeader字段,可以知道可选头的大小为0xE0,因为Signature占用4字节,FileHeader占用20字节,所以IMAGE_NT_HEADERS的大小为:0xE0 + 4 + 20 = 248字节;
PE头的偏移为0xE0,大小为248字节,因此可以计算出节表头所在的文件偏移值为0xE0 + 248 = 472,即十六进制的0x1D8;
IMAGE_SECTION_HEADER结构体占用40字节,因此第一个节表头的数据范围是从0x1D8开始的40字节数据,如下图所示:


7.RVA与文件偏移地址的转换

节区的大小是需要进行对齐处理的,而且在文件中的对齐值与在内存中的对齐值是不一样的,在IMAGE_NT_HEADERS的OptionalHeader中指明了这两个值(FileAlignment和SectionAlignment)。

节表头的文件偏移值为0x1D8,而且知道每个节表头的大小为40字节,那么3个节表头的大小就是120字节,因此可以计算出节表头结束的偏移值为:0x1D8 + 40*3 = 0x250。
在PEiD中可以看到区块的文件对齐值为0x200,从文件偏移的0x250开始第一个对齐的文件偏移地址是0x400(如果n % m等于0,则认为n是关于m对齐的)。在6的最后一幅图中,我们已经计算出了第一个区块.text的PointerToRawData值为0x400,与我们这里得到的对齐值是一样的。
那么,在文件数据偏移0x250至0x400之间就存在一个间隙,这个间隙通常使用0x00进行填充。同样的,节区与节区之间也会存在因为对齐而导致的间隙。
当PE文件被映射到内存时,也会存在同样的间隙,只不过间隙的大小是不一样的,因为FileAlignment和SectionAlignment的大小不一样,如下图所示:

正是因为FileAlignment和SectionAlignment的不一致,造成了PE文件数据在磁盘上的分布与在内存中的分布存在差异,最终导致了文件偏移值和相对虚拟地址(RVA)之间的差异,但是两者之间可以进行相互转换。
节区中RVA值转换为Offset(文件偏移)的步骤如下:
1. 通过节表头数组可以遍历所有节区的信息,包括节区的起始RVA地址、起始的文件偏移地址、以及节区的大小;
2. 判断给定的RVA值落在哪个节区上,对应的,可以计算出这个RVA与节区起始RVA的差值diff;
3. 查看包含待转换的RVA的节区的文件偏移起始地址,加上差值diff便是转换得到的文件偏移地址。

(每个区块在磁盘和内存中的偏移地址都知道,只需用RVA减去节区起始RVA算出diff,再用区块在磁盘中的起始偏移地址加上diff就可以算出文件偏移地址)

对于C:\PE\notepad.exe而言,RVA值0x9100转换为文件偏移值是多少呢?
1. 使用PEiD打开C:\PE\notepad.exe,查看各个节区的相关参数,如下图所示:

R.偏移--->磁盘中文件的偏移地址       V.偏移--->内存映像中节区的起始RVA为0x9000

2. 从图中可以看出,0x9100这个RVA位于.data节区,且该节区的起始RVA为0x9000,因此计算出差值为0x9100 - 0x9000 = 0x100;
3. .data节区的文件偏移地址为0x7C00,加上上面计算出来的差值,结果为0x7C00 + 0x100 = 0x7D00,这个就是转换得到的文件偏移地址。
 


 


 


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值