最新消息:

PE文件格式分析(转)

Crack 大步 1868浏览 0评论

Ps:不得不说pe格式真的是很重要,无论是在反汇编和病毒方面都是不可少的,但是说实话要想短时间就弄彻底弄明白显得有点不可能,只能一边对照结构图和lordpe来分析了,建议看下http://bbs.pediy.com/showthread.php?threadid=21932,这篇很详细,还是看雪的给力。

转自:http://www.cnblogs.com/lzjsky/archive/2011/09/22/2184942.html

我们知道,很多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           0x
0001  // 重定位信息被移除
#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              : %04Xn",myfileheader.Machine);
printf("NumberOfSections     : %04Xn",myfileheader.NumberOfSections);
printf("TimeDateStamp        : %08Xn",myfileheader.TimeDateStamp);
printf("PointerToSymbolTable : %08Xn",myfileheader.PointerToSymbolTable);
printf("NumberOfSymbols      : %08Xn",myfileheader.NumberOfSymbols);
printf("SizeOfOptionalHeader : %04Xn",myfileheader.SizeOfOptionalHeader);
printf("Characteristics      : %04Xn",myfileheader.Characteristics);
getch();
return 0;
}

注释比较详细了,大家根据这个就可以读取一个PE文件的基本特征信息了.以上代码VC6编译通过

紧接着上一节,我们来研究下IMAGE_OPTIONAL_HEADER32,这个属于PE中附加结构信息,同样是很重要的。

我们先来看看它的结构:

typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//00h WORD    Magic;                   //幻数,32位pe文件总为010bh
02h BYTE    MajorLinkerVersion;      //连接器主版本号
03h BYTE    MinorLinkerVersion;      //连接器副版本号
04h DWORD   SizeOfCode;              //代码段总大小
08h DWORD   SizeOfInitializedData;   //已初始化数据段总大小
0ch DWORD   SizeOfUninitializedData; //未初始化数据段总大小
10h DWORD   AddressOfEntryPoint;     //程序执行入口地址(RVA)
14h DWORD   BaseOfCode;              //代码段起始地址(RVA)
18h DWORD   BaseOfData;              //数据段起始地址(RVA)

//
// NT additional fields.
//

1ch DWORD   ImageBase;               //程序默认的装入起始地址
20h DWORD   SectionAlignment;        //内存中区块的对齐单位
24h DWORD   FileAlignment;           //文件中区块的对齐单位
28h WORD    MajorOperatingSystemVersion; //所需操作系统主版本号
2ah WORD    MinorOperatingSystemVersion; //所需操作系统副版本号
2ch WORD    MajorImageVersion;       //自定义主版本号
2eh WORD    MinorImageVersion;       //自定义副版本号
30h WORD    MajorSubsystemVersion;   //所需子系统主版本

转载请注明:大步's Blog » PE文件格式分析(转)

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
SiteMap