PE解析
一、什么是可执行文件
1、可执行文件(executable file)指的是可以由操作系统进行加载执行的文件。
2、可执行文件的格式:
-
Windows平台:
- PE(Portable Executable)文件结构
-
Linux平台:
- ELF(Executable and Linking Format)文件结构
二、如何识别PE文件
1、PE文件的特征(PE指纹)
分别打开.exe .dll .sys等文件,观察特征前2个字节,都是4D5A,变成字母是MZ,是DOS系统开发人员的一个名字,再看3C字节是什么,3C存的是一个地址,这个地址对应的5045,变为字母就是PE。
2、不要仅仅通过文件的后缀名来认定PE文件
三、对齐
对齐是为了读写速度,找起来快,用空间换时间
1、硬盘对齐
早期计算机硬盘水平比较低,只有很小的几个G,节与节之间的空隙比较小,硬盘对齐为200H,后来随着硬件水平提高了,硬盘对齐与内存对齐一样了为1000H
2、内存对齐
四、为什么要分节
1、节省硬盘空间.(这个不是决定的,由编译器决定)
2、一个应用程序多开
比如一个qq程序有两个部分,数据1(可读),数据2(可读可写) ,都是100M,我运行一个账号就需要200M,我再开一个,数据1(可读)已经有了,就没必要在复制一份丢在内存中,只需要复制一份数据2就可以了
3、理解FileBuffer和ImageBuffer
五、DOS头(64个字节)
typedef struct IMAGE_DOS_HEADER{
WORD e_magic; //DOS头的标识,为4Dh和5Ah。分别为字母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];
DWORD e_lfanew; //指向IMAGE_NT_HEADERS的所在(PE头相对于文件的偏移,用子定位E文件
)
}IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
WORD 为一个16bit 的无符号数,重点是e_magic
和e_magic
六、PE文件头(NT头)
typedef struct IMAGE_NT_HEADERS{
DWORD Signature; //PE标识
IMAGE_FILE_HEADER FileHeader; //标准PE头(20字节)
IMAGE_OPTIONAL_HEADER32 OptionalHeader; //扩展PE头(大小不确定)
}IMAGE_NT_HEADERS,*PIMAGE_NT_HEADERS;
七、节表
#define IMAGE_SIZEOF_SHORT_NAME 8
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; //ASCII字符串 可自定义 只截取8个 可以8个字节都是名字
union { //Misc 双字 是该节在没有对齐前的真实尺寸,该值可以不准确
DWORD PhysicalAddress; //内存中的真实长度,这两个值是一个联合结构,可以使用其中的任何一个
DWORD VirtualSize; //一般是取后一个
} Misc;
DWORD VirtualAddress; //在内存中的偏移地址,加上ImageBase才是在内存中的真正地址
DWORD SizeOfRawData; //节在文件中对齐后的尺寸
DWORD PointerToRawData; //节区在文件中的偏移
DWORD PointerToRelocations; //调试相关
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics; //节的属性
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
八、导出表
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics; // 未使用
DWORD TimeDateStamp; // 时间戳
WORD MajorVersion; // 未使用
WORD MinorVersion; // 未使用
DWORD Name; // 指向该导出表文件名字符串
DWORD Base; // 导出函数起始序号
DWORD NumberOfFunctions; // 所有导出函数的个数(不准确,有可能是错的)
DWORD NumberOfNames; // 以函数名字导出的函数个数
DWORD AddressOfFunctions; // 导出函数地址表RVA
DWORD AddressOfNames; // 导出函数名称表RVA
DWORD AddressOfNameOrdinals; // 导出函数序号表RVA
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
九、重定位表
typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress;
DWORD SizeOfBlock;
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION ,* PIMAGE_BASE_RELOCATION;
十、导入表
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk; //RVA 指向IMAGE_THUNK_DATA结构数组
};
DWORD TimeDateStamp; //时间戳
DWORD ForwarderChain;
DWORD Name; //RVA,指向dll名字,该名字已0结尾
DWORD FirstThunk; //RVA,指向IMAGE_THUNK_DATA结构数组
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;