这段时间学了PE文件,将我学习的一点心得和理解发给大家。
typedef IMAGE_DOS_HEADER STRUCT
{
+0000h WORD e_magic //EXE标志,“MZ”
+0002h WORD e_cblp // 最后(部分)页中的字节数
+0004h WORD e_cp //文件中全部和部分页数
+0006h WORD e_crlc //重定位表中的指针数
+0008h WORD e_cparhdr //头部尺寸,以段落为单位
+000ah WORD e_minalloc //所需最小附加段
+000ch WORD e_maxalloc //所需最大附加段
+000eh WORD e_ss //DOS代码的初始化堆栈SS
+0010h WORD e_sp //DOS代码的初始化堆栈指针SP
+0012h WORD e_csum // 补码校验值
+0014h WORD e_ip //DOS代码的初始化指令入口[指针IP]
+0016h WORD e_cs //DOS代码的初始堆栈入口 [寄存器CS值]
+0018h WORD e_lfarlc // 重定位表的字节偏移量
+001ah WORD e_ovno //覆盖号
+001ch WORD e_res[4] //保留字
+0024h WORD e_oemid //OEM标识符
+0026h WORD e_oeminfo //OEM信息
+0029h WORD e_res2[10] //保留字
+003ch DWORD e_lfanew //PE头相对于文件的偏移地址 指向PE头
} IMAGE_DOS_HEADER ENDS,IMAGE_DOS-HEADER, *PIMAGE_DOS_HEADER;
typedefstruct_IMAGE_NT_HEADERS
{
+00h DWORD Signature //PE头文件标识,“PE\0\0”
+04h IMAGE_FILE_HEADER FileHeader //PE标准头
+18h IMAGE_OPTIONAL_HEADER32 OptionalHeader //PE扩展头
} IMAGE_NT_HEADERS,*PIMAGE_NT_HEADERS32;
typedef struct _IMAGE_OPTIONAL_HEADER
{
//
// Standard fields.
//
+18h WORD Magic; // ROM 映像(0107h),普通可执行文件(010Bh)
// x32: 10B, x64: 20B
+1Ah BYTE MajorLinkerVersion; // 链接程序的主版本号
+1Bh BYTE MinorLinkerVersion; // 链接程序的次版本号
+1Ch DWORD SizeOfCode; // 所有含代码的节的总大小
+20h DWORD SizeOfInitializedData; // 所有含已初始化数据的节的总大小
+24h DWORD SizeOfUninitializedData; // 所有含未初始化数据的节的大小
+28h DWORD AddressOfEntryPoint; // 程序执行入口RVA
+2Ch DWORD BaseOfCode; // 代码的区块的起始RVA
+30h DWORD BaseOfData; // 数据的区块的起始RVA
//-------------------------------------------------------------
// NT additional fields. 以下是属于NT结构增加的领域。
//-------------------------------------------------------------
+34h DWORD ImageBase; // 程序的首选装载地址(程序建议装载地址)
+38h DWORD SectionAlignment; // 内存中的区块的对齐大小
+3Ch DWORD FileAlignment; // 文件中的区块的对齐大小
+40h WORD MajorOperatingSystemVersion; // 要求操作系统最低版本号的主版本号
+42h WORD MinorOperatingSystemVersion; // 要求操作系统最低版本号的副版本号
+44h WORD MajorImageVersion; // 可运行于操作系统的主版本号
+46h WORD MinorImageVersion; // 可运行于操作系统的次版本号
+48h WORD MajorSubsystemVersion; // 要求最低子系统版本的主版本号
+4Ah WORD MinorSubsystemVersion; // 要求最低子系统版本的次版本号
+4Ch DWORD Win32VersionValue; // 莫须有字段,不被病毒利用的话一般为0
+50h DWORD SizeOfImage; // 映像装入内存后的总尺寸(内存中的整个PE映像尺寸)
+54h DWORD SizeOfHeaders; // 所有头 + 区块表的尺寸大小
+58h DWORD CheckSum; // 映像的校检和
+5Ch WORD Subsystem; // 可执行文件期望的子系统
+5Eh WORD DllCharacteristics; // DllMain()函数何时被调用,默认为 0(DLL文件特性)
+60h DWORD SizeOfStackReserve; // 初始化时的栈大小
+64h DWORD SizeOfStackCommit; // 初始化时实际提交的栈大小
+68h DWORD SizeOfHeapReserve; // 初始化时保留的堆大小
+6Ch DWORD SizeOfHeapCommit; // 初始化时实际提交的堆大小
+70h DWORD LoaderFlags; // 与调试有关,默认为 0
+74h DWORD NumberOfRvaAndSizes; // 下边数据目录的项数,这个字段自Windows NT 发布以来 // 一直是16
+78h IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];// 数据目录表
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
typedef struct _IMAGE_DATA_DIRECTORY
{
DWORD VirtualAddress; //0000h - 数据得起始RVA
DWORD isize; //0004h - 数据块的长度
}IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY;
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 导出表地址和大小
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 导入表地址和大小
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 资源目录资源表地址和大小
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 异常目录异常表地址和大小
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 安全目录属性证书数据地址和大小
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 基地址重定位表地址和大小
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 调试信息地址和大小
#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 预留为0
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 指向全局指针寄存器的值
#define IMAGE_DIRECTORY_ENTRY_TLS 9 线程句柄存储地址和大小
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 加载配置表地址和大小
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 绑定导入表地址和大小
#define IMAGE_DIRECTORY_ENTRY_IAT 12 导入函数地址表地址和大小
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 延迟导入表地址和大小
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 COM信息
#define Reserved 15系统保留
typedefstruct_IMAGE_SECTION_HEADER {
+0h BYTE Name[IMAGE_SIZEOF_SHORT_NAME];//节表名称,如“.text” 8个字节节名 //IMAGE_SIZEOF_SHORT_NAME=8
union
+8h
{
DWORD PhysicalAddress;//物理地址 节区的尺寸
DWORD VirtualSize;//真实长度,这两个值是一个联合结构,可以使用其中的任何一个,一般是取后一个
} Misc;
+ch DWORD VirtualAddress;//节区的 RVA 地址
+10h DWORD SizeOfRawData;//在文件中对齐后的尺寸
+14h DWORD PointerToRawData;//在文件中的偏移量
+18h DWORD PointerToRelocations;//在OBJ文件中使用,重定位的偏移
+1ch DWORD PointerToLinenumbers;//行号表的偏移(供调试使用)
+1eh WORD NumberOfRelocations;//在OBJ文件中使用,重定位项数目
+20h WORD NumberOfLinenumbers;//行号表中行号的数目
+24h DWORD Characteristics;//节属性如可读,可写,可执行等
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
0000204c 是INT表的RVA FOA 64c
00002084 是库名 FOA 684
00002000 是IAT表的RVA FOA 600
首先我们来看导入表结构体的第一个成员包含的是什么信息
来看第一个偏移
654

我们发现INT表保存的是一个 RVA下面我们将 RVA 转换成 FOA 65c

可以看见INT表指向的 数据包含了两个结构
(注:这里INT表指向的RVA地址实际上是一个数组)
前两个字节包含的时函数编号
后面的字符串就是函数名
导入表结构的NAME成员 偏移是 66A

这里我们可以见看 NAME所指向的DLL名
接着我们来查看 IAT表
相信表哥们可以发现一个细节

IAT表指向的地址 和 INT 表所指向的值是一样的。
到这里我们打开OD

我们发现在PE文件装载进内存之后 值就发生了改变
让我们看看这个地址又是什么

在这里我们可以发现 IAT表中的信息已经被改成了 DLL链接库中函数的地址

从上图中可以发现
750b0380是函数的入口地址

可以发现JMP的位置 就是函数的入口地址
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课