作者: sdlj8051 发布日期: 2006-8-26 查看数: 19 出自: http://emuch.net |
PE 文件的知识是基本的知识.网上有很多这方面的资料.然而系统讲解的却不多.我不是这方面的专家,却希望能抛砖引玉,得到这方面的一些指点.
计算机这门科学是实践性很强的一门学问,如果想扎扎实实的学会点东西,还是要亲自动手试一试. 在继续向下看之前,我假定你会基本的C语言和简单的使用VC6.0,以下的例子都用用到这些.除此之外,不再做任何假设. 关于pe的一些结构可以在winnt.h这个头文件里找到.
声明一下:这里所有的结构及常量定义都是基于intel的x86 CPU的,在其他的系统上可能有所不同,你应该去查看相应的资料.关于这点以后不再声明.
首先在pe文件的开始是这样一个结构(为了方便阅读,我加上了字节偏移):
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header 00h WORD e_magic; // Magic number **DOS头标记 02h WORD e_cblp; // Bytes on last page of file 04h WORD e_cp; // Pages in file 06h WORD e_crlc; // Relocations 08h WORD e_cparhdr; // Size of header in paragraphs 0ah WORD e_minalloc; // Minimum extra paragraphs needed 0ch WORD e_maxalloc; // Maximum extra paragraphs needed 0eh WORD e_ss; // Initial (relative) SS value 10h WORD e_sp; // Initial SP value 12h WORD e_csum; // Checksum 14h WORD e_ip; // Initial IP value 16h WORD e_cs; // Initial (relative) CS value 18h WORD e_lfarlc; // File address of relocation table 1ah WORD e_ovno; // Overlay number 1ch WORD e_res[4]; // Reserved words 24h WORD e_oemid; // OEM identifier (for e_oeminfo) 26h WORD e_oeminfo; // OEM information; e_oemid specific 28h WORD e_res2[10]; // Reserved words 3ch LONG e_lfanew; // File address of new exe header **指向PE头部 } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
这个结构就是DOS MZ头,是为了向下兼容的.当在DOS下运行windows程序不至于出错. 我们只关心两个域:e_magic 和e_lfanew. e_magic 的值应该等于0x5A4D,像下面定义这样的: #define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
e_lfanew是一个指针,指向PE文件头在PE文件中的偏移.
PE文件头是这样一个结构,它包含了许多PE装载器要用到的域. typedef struct _IMAGE_NT_HEADERS { DWORD Signature; **PE文件标识 IMAGE_FILE_HEADER FileHeader; **映像文件头 IMAGE_OPTIONAL_HEADER32 OptionalHeader; **映像可选头 } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
Signature的定义如下: #define IMAGE_NT_SIGNATURE 0x00004550 // PE00
好了,让我们实际做点事.写个小程序查看一下PE文件的这些信息,并判断一个PE文件是否有效. 代码如下,你可以在VC中建一个控制台程序,然后把这段代码拷进去(我建议你手工输入.什么?你不愿意?哈哈,我可是一个一个字母敲进去的,你比我还懒 )
#include "stdafx.h" #include "windows.h" #include "stdio.h"
int main(int argc, char* argv[]) { FILE *p; IMAGE_DOS_HEADER mydosheader; unsigned long sig;
p = fopen("test1.exe","r+b"); if(p == NULL)return -1;
fread(&mydosheader,sizeof(mydosheader),1,p); fseek(p,mydosheader.e_lfanew,SEEK_SET); fread(&sig,4,1,p); fclose(p);
printf("IMAGE_DOS_HEADER dump:/n"); printf("e_magic : %04x/n",mydosheader.e_magic); printf("e_cblp : %04x/n",mydosheader.e_cblp); printf("e_cp : %04x/n",mydosheader.e_cp); printf("e_crlc : %04x/n",mydosheader.e_crlc); printf("e_cparhdr : %04x/n",mydosheader.e_cparhdr); printf("e_minalloc: %04x/n",mydosheader.e_minalloc); printf("e_maxalloc: %04x/n",mydosheader.e_maxalloc); printf("e_ss : %04x/n",mydosheader.e_ss); printf("e_sp : %04x/n",mydosheader.e_sp); printf("e_csum : %04x/n",mydosheader.e_csum); printf("e_ip : %04x/n",mydosheader.e_ip); printf("e_cs : %04x/n",mydosheader.e_cs); printf("e_lfarlc : %04x/n",mydosheader.e_lfarlc); printf("e_ovno : %04x/n",mydosheader.e_ovno); printf("e_res[0] : %04x/n",mydosheader.e_res[0]); printf("e_oemid : %04x/n",mydosheader.e_oemid); printf("e_oeminfo : %04x/n",mydosheader.e_oeminfo); printf("res2[0] : %04x/n",mydosheader.e_res2[0]); printf("lfanew : %08x/n",mydosheader.e_lfanew);
if((mydosheader.e_magic ==IMAGE_DOS_SIGNATURE) && (sig == IMAGE_NT_SIGNATURE)) printf("有效的PE文件/n"); else printf("无效的PE文件/n"); return 0; }
PE知识学习(二)
我们了解了pe头部的dos部首部分,我们知道在这个结构里e_magic和e_lfanew这两个域对我们来说很重要.同时我们也提到了e_lfanew域指向IMAGE_NT_HEADERS32结构在pe文件的偏移. 补充声明一下:这里的知识是适用于32位字的机器上的.
下面我们接着看IMAGE_NT_HEADERS32结构,这个部分在pe文件的学习里至关重要.
IMAGE_NT_HEADERS32的结构定义如下:
typedef struct _IMAGE_NT_HEADERS { DWORD Signature; **PE文件标识 "PE",0,0 IMAGE_FILE_HEADER FileHeader; **映像文件头 IMAGE_OPTIONAL_HEADER32 OptionalHeader; **映像可选头 } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
这其中又包含了两个结构.我们一点一点的往下看. 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;
我们看一下这几个域:
1) 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
2) NumberOfSections pe文件中区块的数量,关于区块下面还要讲到,这里先有个印象就可以了.
3)TimeDateStamp 文件日期时间戳,指这个pe文件生成的时间,它的值是从1969年12月31日16:00:00以来的秒数.
4)PointerToSymbolTable Coff调试符号表的偏移地址.
5)NumberOfSymbols Coff符号表中符号的个数. 这个域和前个域在release版本的程序里是0.
6)SizeOfOptionalHeader IMAGE_OPTIONAL_HEADER32结构的大小(即多少字节).我们接着就要提到这个结构了.事实上,pe文件的大部分重要的域都在IMAGE_OPTIONAL_HEADER结构里.
7)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文件的特征值就是这些属性值加在一起的.
希望这些没有让你头晕,其实内容不多,只是一个IMAGE_FILE_HEADER结构,而这个结构包含7个域而已. 让我们先熟悉这个结构,我们编个程序来显示这些信息.
#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"); if(p == NULL)return -1;
fseek(p,0x3c,SEEK_SET); 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; }
此程序在win98 + vc6.0 环境下编译通过. |
|
|