PE文件时windows中的32位可执行文件。在linux中是elf。如exe,dll,scr等都是PE文件

基本结构

PE文件中的RVA(相对虚拟地址)一般不是直接转换到真实的虚拟地址的,还要加上一个基准地址(ImageBase).也就是说VA = RVA + ImageBase

这样做主要是因为DLL,DLL动态链接时如果发现这个虚拟地址已经有其他的dll了,那么就要进行重定位到其他位置,如果没有ImageBase那么重定位是很麻烦的。

PE头

DOS头

typedef struct _IMAGE_DOS_HEADER
{
WORD e_magic;
WORD e_cblp;
WORD e_cp;
WORD e_crlc;
WORD e_cpaarhdr;
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];
WORD e_lfanew;
}IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

上面就是dos头的结构。

  • e_magic: DOS签名(魔数)也就是4D5A(ascii的MZ)
  • e_lfanew: 指示NT头的偏移(NT头位置)

NT头

结构体:

typedef struct _IMAGE_NT_HEADERS
{
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
}IMAGE_NT_HEADER32, *PIMAGE_NT_HEADERS32;

  • Signature: 签名(50450000h)
typedef struct _MIAGE_FILE_HEADER
{
WORD Mechine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
}IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
  • Machine: 机器型号码,例如Intel x86是14c。
  • NumerOfSections: 节的数量
  • SizeOfOptionalHeader
  • Characteristics: 用来标识文件的属性,文件是否处于可运行状态,是否是dll文件。
    在winnt.h中有Characterristics的信息
    IMAGE_FILE_RELOCS_STPIPPED 0X001
    IMAGE_FILE_EXECUATABLE_IMAGE 0X002
    IMAGE_FILE_LINE_NUMS_STRIPPED 0X004
    IMAGE_FILE_LOCAL_SYMS_STRIPPED 0X008
    IMAGE_FILE_AGGRSIVE_WS_TRIM 0X0010
    IMAGE_FILE_LARGE_ADDRESS_AWARE 0X0020
    IMAGE_FILE_BYTES_REVERSED_L0 0X0080
    IMAGE_FILE_32BIT_MACHINE 0X0100
    IMAGE_FILE_DEBUG_STRIPPED 0X200
    ...
    可选头,结构体为IMAGE_OPTIONAL_HEADER32,重要成员有9个:
  • Magic:IMAGE_OPTIONAL_HEADER32为10B,IMAGE_OPTIONAL_HEADER64为20B
  • AddressOfEntryPoint:持有EP的RVA值,指出程序最先执行的代码起始地址
  • ImageBase:指出文件的优先装入地址(32位进程虚拟内存范围为:0~7FFFFFFF)
  • SectionAlignment,FileAlignment:前者制定了节区在内存中的最小单位,后者制定了节区在磁盘文件中的最小单位
  • SizeOfImage:指定了PE Image在虚拟内存中所占空间的大小
  • SizeOfHeaders:指出整个PE头的大小
  • Subsystem:区分系统驱动文件和普通可执行文件
  • NumberOfRvaAndSize:指定DataDirectory数组的个数
  • DataDirectory:由IMAGE_DATA_DIRECTORY结构体组成的数组

节区头

typedef struct _MIAGE_SECTION_HEADER
{
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union
{
DWORD PhysicalAddress;
DWORD VirtualSize;
}
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelacations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
}IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
  • VirtualSize: 节区所占大小
  • VirtualAddress: 节区起始位置
  • SizeOfRawData: 磁盘中的大小
  • PointerToRwaData: 磁盘中的位置
  • Characteristics: 节区.属性

虚拟地址与内存地址之间的转换关系为RAW - PointerToRawData = RVA - VirtualAddress

其中RAW是文件偏移,PointerToRawData是该节在文件中的首地址。RVA是在内存中的地址,VirtualAddress是在节在内存中的首地址

IAT(导入地址表)

IAT是记录程序正在使用库中那些函数的表。

DLL

为了理解IAT,首先要了解DLL(动态链接库,在linux中是.so).它是在程序运行时动态加载的,而不是在链接过程中加载的

IMAGE_IMPORT_DESCRIPTOR

这个表记录了要导入那些头文件.它的结构体数组的首地址是IMAGE_OPTIONAL_HEADER32.DataDirectory[1].VirtualAddress

typedef struct _IMAGE_IMPORT_DESCRIPTOR
{
union
{
DWORD Characteristics;
DWORD OriginalFirstThubk;
};
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk;
};IMAGE_IMPORT_DESCRIPTOR;

typedef struct _IMAGE_IMPORT_BY_NAME
{
WORD Hint;
BYTE Name[1];
}IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

每一个导入的库的信息使用用一个IMAGE_IMPORT_DESCRIPTOR进行描述

  • OriginalFirstThunk: INT地址(RVA)
  • Nam: 库名称字符串的地址(RVA)
  • FirstThunk: IAT地址(RVA)
  • OriginalFirstThunk(INT): INT是包含导入函数信息的结构体指针数组,只有获得了这些信息才有可能在加载到进程的库中获得函数首地址。

EAT

EAT是dll库用来提供函数地址的,IAT记录了导入的dll库的函数地址。

与IAT不同EAT只有一个,位置在IMAGE_OPTIONAL_HEADER32.DataDirectory[0].VirtualAddress中

typedef struct _IMAGE_EXPORT_DIRECTORY
{
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFuctions;

DWORD AddressOfNames;
DWORD AddressOfNameOrdinals;
}IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
  • NumberOfFunctions: 实际Export函数个数
  • NumberOfNames: 实际有名字函数个数
  • AddressOfFunctions: Export函数地址数组
  • AddressOfNames: 函数名称地址数组
  • AddressOfNameOrdinals: Ordinal地址数组