PE知识学习,连载----------------------1,2

 
PE知识学习,连载----------------------1,2
2007-10-23 13:04
作者: 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 环境下编译通过.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值