关于PE可执行文件的修改_第1页
关于PE可执行文件的修改_第2页
关于PE可执行文件的修改_第3页
关于PE可执行文件的修改_第4页
关于PE可执行文件的修改_第5页
已阅读5页,还剩7页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、关于PE可执行文件的修改 在windows 9x、NT、2000下,所有的可执行文件都是基于Microsoft设计的一种新的文件格式Portable Executable File Format(可移植的执行体),即PE格式。有一些时候,我们需要对这些可执行文件进行修改,下面文字试图详细的描述PE文件的格式及对PE格式文件的修改。1、 PE文件框架构成DOS MZ headerDOS stub PE headerSection tableSection 1Section 2S

2、ection .Section n 上 表是PE文件结构的总体层次分布。所有 PE文件(甚至32位的 DLLs) 必须以一个简单的 DOS MZ header 开始,在偏移0处有DOS下可执行文件的“MZ标志”,有了它,一旦程序在DOS下执行,DOS就能识别出这是有效的执行体,然后运行紧随 MZ header 之后的 DOS stub。DOS stub实际上是个有效的EXE,在不支持 PE文件格式的操作系统中,它将简

3、单显示一个错误提示,类似于字符串 " This program cannot run in DOS mode " 或者程序员可根据自己的意图实现完整的 DOS代码。通常DOS stub由汇编器/编译器自动生成,对我们的用处不是很大,它简单调用中断21h服务9来显示字符串"This program cannot run in DOS mode"。紧接着 DOS 

4、;stub 的是 PE header。 PE header 是PE相关结构 IMAGE_NT_HEADERS 的简称,其中包含了许多PE装载器用到的重要域。可执行文件在支持PE文件结构的操作系统中执行时,PE装载器将从 DOS MZ header的偏移3CH处找到 PE header 的起始偏移量。因而跳过了 DOS stub 直接定位到真正的文件头 PE header。PE文件的真正内容划分成块,称之为s

5、ections(节)。每节是一块拥有共同属性的数据,比如“.text”节等,那么,每一节的内容都是什么呢?实际上PE格式的文件把具有相同属性的内容放入同一个节中,而不必关心类似“.text”、“.data”的命名,其命名只是为了便于识别,所有,我们如果对PE格式的文件进行修改,理论上讲可以写入任何一个节内,并调整此节的属性就可以了。PE header 接下来的数组结构 section table(节表)。 每个结构包含对应节的属性、文件偏移量、虚拟偏移量等。如果PE文件里有5个节,那么此结构数组内就有5个成员。以上就是PE文件格式的物理分布,下

6、面将总结一下装载一PE文件的主要步骤:1、 PE文件被执行,PE装载器检查 DOS MZ header 里的 PE header 偏移量。如果找到,则跳转到 PE header。 2、PE装载器检查 PE header 的有效性。如果有效,就跳转到PE header的尾部。3、紧跟 PE header 的是节表。PE装载器读取其中的节信息,并采用文件映射方法将这些节映射到内存,同时付上节表里指定的节属性。4、PE文件

7、映射入内存后,PE装载器将处理PE文件中类似 import table(引入表)逻辑部分。上述步骤是一些前辈分析的结果简述。2、 PE文件头概述  我们可以在winnt.h这个文件中找到关于PE文件头的定义:  typedef struct _IMAGE_NT_HEADERS   DWORD Signature;  /PE文件头标志 :“PE00”。在开始DOS header的偏移3CH处所指向的地址开始  IMAGE_FILE_HEADER FileHe

8、ader; /PE文件物理分布的信息  IMAGE_OPTIONAL_HEADER32 OptionalHeader; /PE文件逻辑分布的信息   IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;  typedef struct _IMAGE_FILE_HEADER   WORD Machine; /该文件运行所需要的CPU,对于Intel平台是14Ch  WORD NumberOfSections;

9、 /文件的节数目  DWORD TimeDateStamp; /文件创建日期和时间  DWORD PointerToSymbolTable; /用于调试  DWORD NumberOfSymbols; /符号表中符号个数  WORD SizeOfOptionalHeader; /OptionalHeader 结构大小  WORD Characteristics; /文件信息标记,区分文件是exe还是dll  

10、0;IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;  typedef struct _IMAGE_OPTIONAL_HEADER   WORD Magic; /标志字(总是010bh)  BYTE MajorLinkerVersion; /连接器版本号  BYTE MinorLinkerVersion; /  DWORD SizeOfCode; /代码段大小  DWORD S

11、izeOfInitializedData; /已初始化数据块大小  DWORD SizeOfUninitializedData; /未初始化数据块大小  DWORD AddressOfEntryPoint; /PE装载器准备运行的PE文件的第一个指令的RVA,若要改变整个执行的流程,可以将该值指定到新的RVA,这样新RVA处的指令首先被执行。(许多文章都有介绍RVA,请去了解)  DWORD BaseOfCode; /代码段起始RVA  DWORD BaseOfData;&#

12、160;/数据段起始RVA  DWORD ImageBase; /PE文件的装载地址  DWORD SectionAlignment; /块对齐  DWORD FileAlignment; /文件块对齐  WORD MajorOperatingSystemVersion;/所需操作系统版本号  WORD MinorOperatingSystemVersion;/  WORD MajorImageVersion; /用户自定义版本号

13、60; WORD MinorImageVersion; /  WORD MajorSubsystemVersion; /win32子系统版本。若PE文件是专门为Win32设计的  WORD MinorSubsystemVersion; /该子系统版本必定是4.0否则对话框不会有3维立体感  DWORD Win32VersionValue; /保留  DWORD SizeOfImage; /内存中整个PE映像体的尺寸  DWORD Siz

14、eOfHeaders; /所有头+节表的大小  DWORD CheckSum; /校验和  WORD Subsystem; /NT用来识别PE文件属于哪个子系统  WORD DllCharacteristics; /  DWORD SizeOfStackReserve; /  DWORD SizeOfStackCommit; /  DWORD SizeOfHeapReserve; /  DWORD

15、 SizeOfHeapCommit; /  DWORD LoaderFlags; /  DWORD NumberOfRvaAndSizes; /  IMAGE_DATA_DIRECTORY DataDirectoryIMAGE_NUMBEROF_DIRECTORY_ENTRIES;  /IMAGE_DATA_DIRECTORY 结构数组。每个结构给出一个重要数据结构的RVA,比如引入地址表等   IMAGE_OPTIONAL_HEADER32, 

16、*PIMAGE_OPTIONAL_HEADER32;  typedef struct _IMAGE_DATA_DIRECTORY   DWORD VirtualAddress; /表的RVA地址  DWORD Size; /大小   IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;  PE文件头后是节表,在winnt.h下如下定义  typedef struct _IMAGE_SECT

17、ION_HEADER   BYTE NameIMAGE_SIZEOF_SHORT_NAME;/节表名称,如“.text”  union   DWORD PhysicalAddress; /物理地址  DWORD VirtualSize; /真实长度   Misc;  DWORD VirtualAddress; /RVA  DWORD SizeOfRawData; /物理长度  DWORD 

18、;PointerToRawData; /节基于文件的偏移量  DWORD PointerToRelocations; /重定位的偏移  DWORD PointerToLinenumbers; /行号表的偏移  WORD NumberOfRelocations; /重定位项数目  WORD NumberOfLinenumbers; /行号表的数目  DWORD Characteristics; /节属性   IMAGE

19、_SECTION_HEADER, *PIMAGE_SECTION_HEADER;  以上结构就是在winnt.h中关于PE文件头的定义,如何我们用C/C+来进行PE可执行文件操作,就要用到上面的所有结构,它详细的描述了PE文件头的结构。  3、修改PE可执行文件  现在让我们把一段代码写入任何一个PE格式的可执行文件,代码如下:  - test.asm -  .386p  .model flat, stdcall  option casemap:none 

20、 include masm32includewindows.inc  include masm32includeuser32.inc  includelib masm32libuser32.lib  .code  start:  INVOKE MessageBoxA,0,0,0,MB_ICONINFORMATION or MB_OK  ret  end start  以上代码只显示一个MessageBox框,编译后得到二进制代码如下: 

21、 unsigned char writeline18=  0x6a,0x40,0x6a,0x0,0x6a,0x0,0x6a,0x0,0xe8,0x01,0x0,0x0,0x0,0xe9,0x0,0x0,0x0,0x0  ;  好,现在让我们看看该把这些代码写到那。现在用Tdump.exe显示一个PE格式得可执行文件信息,可以发现如下描述:  Object table:  # Name VirtSize RVA PhysSize Phys off 

22、Flags  - - - - - - -  01 .text 0000CCC0 00001000 0000CE00 00000600 60000020 CER  02 .data 00004628 0000E000 00002C00 0000D400 C0000040 IRW  03 .rsrc 000003C8 0001300

23、0 00000400 00010000 40000040 IR  Key to section flags:  C - contains code  E - executable  I - contains initialized data  R - readable  W - writeable  上面描述此文件中存在3个段及

24、每个段得信息,实际上我们的代码可以写入任何一个段,这里我选择“.text”段。 用如下代码得到一个PE格式可执行文件的头信息:  /writePE.cpp  #include <windows.h>  #include <stdio.h>  #include <io.h>  #include <fcntl.h>  #include <time.h>  #include <SYSSTAT.H&

25、gt;  unsigned char writeline18=  0x6a,0x40,0x6a,0x0,0x6a,0x0,0x6a,0x0,0xe8,0x01,0x0,0x0,0x0,0xe9,0x0,0x0,0x0,0x0  ;  DWORD space;  DWORD entryaddress;  DWORD entrywrite;  DWORD progRAV;  DWORD oldentryaddress;  DWORD

26、0;newentryaddress;  DWORD codeoffset;  DWORD peaddress;  DWORD flagaddress;  DWORD flags;  DWORD virtsize;  DWORD physaddress;  DWORD physsize;  DWORD MessageBoxAadaddress;  int main(int argc,char *&#

27、160;* argv)    HANDLE hFile, hMapping;  void *basepointer;  FILETIME * Createtime;  FILETIME * Accesstime;  FILETIME * Writetime;  Createtime = new FILETIME;  Accesstime = new FILE

28、TIME;  Writetime = new FILETIME;  if (hFile = CreateFile(argv1, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0) = INVALID_HANDLE_VALUE)/打开要修改的文件   

29、puts("(could not open)");  return EXIT_FAILURE;    if(!GetFileTime(hFile,Createtime,Accesstime,Writetime)    printf("nerror getfiletime: %dn",GetLastError();    /得到要修改文件的创建、修改等时间  if (!(hMapping = Cre

30、ateFileMapping(hFile, 0, PAGE_READONLY | SEC_COMMIT, 0, 0, 0)    puts("(mapping failed)");  CloseHandle(hFile);  return EXIT_FAILURE;    if (!(basepointer = MapViewOfFile(hMapping, FILE_MAP_READ,

31、 0, 0, 0)    puts("(view failed)");  CloseHandle(hMapping);  CloseHandle(hFile);  return EXIT_FAILURE;    /把文件头映象存入baseointer  CloseHandle(hMapping);  CloseHandle(hFile);  map_exe(basepointer);/得到相关地址  UnmapVie

32、wOfFile(basepointer);  printaddress();  printf("nn");  if(space<50)    printf("n空隙太小,数据不能写入.n");    else    writefile();/写文件    if (hFile = CreateFile(argv1, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_

33、READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0) = INVALID_HANDLE_VALUE)    puts("(could not open)");  return EXIT_FAILURE;    if(!SetFileTime(hFile,Createtime,Accesstime,Writetime)    pri

34、ntf("error settime : %dn",GetLastError();    /恢复修改后文件的建立时间等  delete Createtime;  delete Accesstime;  delete Writetime;  CloseHandle(hFile);  return 0;    void map_exe(const void *base)  

35、0; IMAGE_DOS_HEADER * dos_head;  dos_head =(IMAGE_DOS_HEADER *)base;  #include <pshpack1.h>  typedef struct PE_HEADER_MAP    DWORD signature;  IMAGE_FILE_HEADER _head;  IMAGE_OPTIONAL_HEADER opt_head;  IM

36、AGE_SECTION_HEADER section_header;   peHeader;  #include <poppack.h>  if (dos_head->e_magic != IMAGE_DOS_SIGNATURE)    puts("unknown type of file");  return;    peHeader * header; 

37、header = (peHeader *)(char *)dos_head + dos_head->e_lfanew);/得到PE文件头  if (IsBadReadPtr(header, sizeof(*header)    puts("(no PE header, probably DOS executable)");  return;    DWORD mods;&

38、#160; char tmpstr4=0;  DWORD tmpaddress;  DWORD tmpaddress1;  if(strstr(const char *)header->section_header0.Name,".text")!=NULL)    virtsize=header->section_header0.Misc.VirtualSize;  /此段的真实长度  physaddress=header->sectio

39、n_header0.PointerToRawData;  /此段的物理偏移  physsize=header->section_header0.SizeOfRawData;  /此段的物理长度  peaddress=dos_head->e_lfanew;  /得到PE文件头的开始偏移  peHeader peH;  tmpaddress=(unsigned long )&peH;  /得到结构的偏移  tmpaddress1=(unsigned 

40、long )&(peH.section_header0.Characteristics);  /得到变量的偏移  flagaddress=tmpaddress1-tmpaddress+2;  /得到属性的相对偏移  flags=0x8000; /一般情况下,“.text”段是不可读写的,如果我们要把数据写入这个段需要改变其属性,实际上这个程序并没有把数据写入“.text”段,所以并不需要更改,但如果你实现复杂的功能,肯定需要数据,肯定需要更改这个值,  space=physsize-virtsize;  /

41、得到代码段的可用空间,用以判断可不可以写入我们的代码  /用此段的物理长度减去此段的真实长度就可以得到  progRAV=header->opt_head.ImageBase;  /得到程序的装载地址,一般为400000  codeoffset=header->opt_head.BaseOfCode-physaddress;  /得到代码偏移,用代码段起始RVA减去此段的物理偏移  /应为程序的入口计算公式是一个相对的偏移地址,计算公式为:  /代码的写入地址codeoffset  entrywrite

42、=header->section_header0.PointerToRawData+header->section_header0.Misc.VirtualSize;  /代码写入的物理偏移  mods=entrywrite%16;  /对齐边界  if(mods!=0)    entrywrite+=(16-mods);    oldentryaddress=header->opt_head.AddressOfEntryPoint;  /保存旧的程序入口地址  newentr

43、yaddress=entrywrite+codeoffset;  /计算新的程序入口地址  return;    void printaddress()    HINSTANCE gLibMsg=NULL;  DWORD funaddress;  gLibMsg=LoadLibrary("user32.dll");  funaddress=(DWORD)GetProcAddress(gLibMsg,"MessageBoxA");

44、0; MessageBoxAadaddress=funaddress;  gLibAMsg=LoadLibrary("kernel32.dll");  /得到MessageBox在内存中的地址,以便我们使用    void writefile()    int ret;  long retf;  DWORD address;  int tmp;  unsigned char waddress4=0;

45、0; ret=_open(filename,_O_RDWR | _O_CREAT | _O_BINARY,_S_IREAD | _S_IWRITE);  if(!ret)    printf("error openn");  return;    retf=_lseek(ret,(long)peaddress+40,SEEK_SET);  /程序的入口地址在PE文件头开始的40处  if(retf=-1)  

46、0; printf("error seekn");  return;    address=newentryaddress;  tmp=address>>24;  waddress3=tmp;  tmp=address<<8;  tmp=tmp>>24;  waddress2=tmp;  tmp=address<<16;  tmp=tmp>>24;  waddress1=tmp;  tm

47、p=address<<24;  tmp=tmp>>24;  waddress0=tmp;  retf=_write(ret,waddress,4);  /把新的入口地址写入文件  if(retf=-1)    printf("error write: %dn",GetLastError();  return;    retf=_lseek(ret,(long)entrywrite,SEEK_SET);  if(retf=-

48、1)    printf("error seekn");  return;    retf=_write(ret,writeline,18);  if(retf=-1)    printf("error write: %dn",GetLastError();  return;    /把writeline写入我们计算出的空间  retf=_lseek(ret,(long)entrywrite+9,SEEK_SET);  /更改MessageBox函数地址,它的二进制代码在writeline10处  if(retf=-1)&

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论