-
十一 21
我们知道,很多PE分析工具都可以查看一个EXE文件的引用DLL文件函数表,其实,这个本身就是存储在EXE头部的一个重要信息,今天,我们就来研究一下:
我们借用一张PE结构图来分析:
一个EXE完整的PE结构分五大部分。见上图.
最开头的是部分是DOS部首,DOS部首由两部分组成:DOS的MZ文件标志和DOS stub(DOS存根程序)。之所以设置DOS部首是微软为了兼容原有的DOS系统下的程序而设立的。紧接着的是真正的PE文件头。值得注意的是PE文件头中的IMAGE_OPTIONAL_HEADER32是一个非常重要的结构,PE文件中的导入表、导出表、资源、重定位表等数据的位置和长度都保存在这个结构里。
我们按照顺序,先说说IMAGE_FILE_HEADER。
IMAGE_FILE_HEADER这个结构的定义如下:
typedef struct _IMAGE_FILE_HEADER { 00h WORD Machine; //运行平台 02h WORD NumberOfSections; //区块数目 06h DWORD TimeDateStamp; //文件日期时间戳 0Ah DWORD PointerToSymbolTable; //指向符号表 0Eh DWORD NumberOfSymbols; //符号表中的符号数量 12h WORD SizeOfOptionalHeader; //映像可选头结构的大小 14h WORD Characteristics; //文件特征值 } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
这个结构体表明一个PE文件的基本特征属性,也是一个PE文件的入口
Machine域说明这个pe文件在什么CPU上运行,具体如下:
#define IMAGE_FILE_MACHINE_UNKNOWN 0
#define IMAGE_FILE_MACHINE_I386 0x014c // Intel 386.
#define IMAGE_FILE_MACHINE_R3000 0x0162 // MIPS little-endian, 0x160 big-endian
#define IMAGE_FILE_MACHINE_R4000 0x0166 // MIPS little-endian
#define IMAGE_FILE_MACHINE_R10000 0x0168 // MIPS little-endian
#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 // MIPS little-endian WCE v2
#define IMAGE_FILE_MACHINE_ALPHA 0x0184 // Alpha_AXP
#define IMAGE_FILE_MACHINE_POWERPC 0x01F0 // IBM PowerPC Little-Endian
#define IMAGE_FILE_MACHINE_SH3 0x01a2 // SH3 little-endian
#define IMAGE_FILE_MACHINE_SH3E 0x01a4 // SH3E little-endian
#define IMAGE_FILE_MACHINE_SH4 0x01a6 // SH4 little-endian
#define IMAGE_FILE_MACHINE_ARM 0x01c0 // ARM Little-Endian
#define IMAGE_FILE_MACHINE_THUMB 0x01c2
#define IMAGE_FILE_MACHINE_IA64 0x0200 // Intel 64
#define IMAGE_FILE_MACHINE_MIPS16 0x0266 // MIPS
#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 // MIPS
#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 // MIPS
#define IMAGE_FILE_MACHINE_ALPHA64 0x0284 // ALPHA64
#define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64
NumberOfSections
pe文件中区块的数量.
TimeDateStamp
文件日期时间戳,指这个pe文件生成的时间,它的值是从1969年12月31日16:00:00以来的秒数.
PointerToSymbolTable
Coff调试符号表的偏移地址.
NumberOfSymbols
Coff符号表中符号的个数. 这个域和前个域在release版本的程序里是0.
SizeOfOptionalHeader
IMAGE_OPTIONAL_HEADER32结构的大小(即多少字节).我们接着就要提到这个结构了.事实上,pe文件的大部分重要的域都在IMAGE_OPTIONAL_HEADER结构里.
Characteristics
这个域描述pe文件的一些属性信息,比如是否可执行,是否是一个动态连接库等.具体定义如下:#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 // 重定位信息被移除
#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // 文件可执行
#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 // 行号被移除
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // 符号被移除
#define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 // Agressively trim working set
#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 // 程序能处理大于2G的地址
#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 // Bytes of machine word are reversed.
#define IMAGE_FILE_32BIT_MACHINE 0x0100 // 32位机器
#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 // .dbg文件的调试信息被移除
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 // 如果在移动介质中,拷到交换文件中运行
#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 // 如果在网络中,拷到交换文件中运行
#define IMAGE_FILE_SYSTEM 0x1000 // 系统文件
#define IMAGE_FILE_DLL 0x2000 // 文件是一个dll
#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 // 文件只能运行在单处理器上
#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 // Bytes of machine word are reversed.
所以,根据这个结构体的信息,我们就可以判断一个文件究竟是不是一个真正的PE文件,该PE文件的类型是可执行的还是可调用的(DLL)
我们可以写个简单的小程序来读取这个信息:
#include "stdafx.h" #include "windows.h" #include "stdio.h" #include "conio.h" int main(int argc, char* argv[]) { FILE *p; LONG e_lfanew; //指向IMAGE_NT_HEADERS32结构在文件中的偏移 IMAGE_FILE_HEADER myfileheader; p = fopen("test1.exe","r+b");//自定义读取的exe文件 if(p == NULL)return -1;//如果打开失败就返回 fseek(p,0x3c,SEEK_SET);//注意这里是指针偏移,也就是绕过开头的DOS区块 fread(&e_lfanew,4,1,p); fseek(p,e_lfanew+4,SEEK_SET); //指向IMAGE_FILE_HEADER结构的偏移 fread(&myfileheader,sizeof(myfileheader),1,p); printf("IMAGE_FILE_HEADER结构:\n"); printf("Machine : %04X\n",myfileheader.Machine); printf("NumberOfSections : %04X\n",myfileheader.NumberOfSections); printf("TimeDateStamp : %08X\n",myfileheader.TimeDateStamp); printf("PointerToSymbolTable : %08X\n",myfileheader.PointerToSymbolTable); printf("NumberOfSymbols : %08X\n",myfileheader.NumberOfSymbols); printf("SizeOfOptionalHeader : %04X\n",myfileheader.SizeOfOptionalHeader); printf("Characteristics : %04X\n",myfileheader.Characteristics); getch(); return 0; }
注释比较详细了,大家根据这个就可以读取一个PE文件的基本特征信息了.以上代码VC6编译通过


- 评论(0)
发表评论 TrackBack
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。