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

来源:互联网 发布:大话战国单机修改数据 编辑:程序博客网 时间:2024/06/07 02:05
导读:
  作者: 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 环境下编译通过.

本文转自
http://hi.baidu.com/19chang19/blog/item/501c3c088f336c33e9248827.html
原创粉丝点击