⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 jiurl pe 格式学习总结(一)-- pe文件概述.htm

📁 关于win2000核心编程的文章
💻 HTM
📖 第 1 页 / 共 2 页
字号:
      <P>这部分内容请参考下面的几篇文章,使用工具 JIURL PEDUMP 
      有助于快速了解。<BR>大家不要因此,而失望不看,本文重点在后三篇,本篇只是为了有个交代,和介绍些相关内容。<BR>注意,在WINNT.H中,有所有PE相关结构的定义。我们用到的结构定义都来自那里。</P>
      <P>Microsoft Portable Executable and Common Object File Format 
      Specification<BR>MSDN&nbsp;</P>
      <P>《Windows95系统程式设计大奥秘》<BR>第8章 PE 与COFF OBJ 档案格式<BR>Matt Pietrek 著 
      侯杰译<BR><BR>Iczelion的PE教程</P>
      <P>PE学习笔记(一) rivershan<BR>PE学习笔记(二) rivershan</P>
      <P>Inside Windows&nbsp;<BR>An In-Depth Look into the Win32 Portable 
      Executable File Format&nbsp;<BR>Matt Pietrek&nbsp;<BR>已经被人翻译了。</P>
      <P>Inside Windows&nbsp;<BR>An In-Depth Look into the Win32 Portable 
      Executable File Format&nbsp;<BR>Matt Pietrek </P>
      <P><B>三 几个要注意的问题</B>
      <P>3.1 文件中大量的空白
      <P>&nbsp;&nbsp;&nbsp; 在 PE Header结构 中的 OptionalHeader 结构中的成员 FileAlignment 
      的值是文件中节的对齐粒度,单位是字节,这个值应该是2的n次方,范围从512到64k。如果这里的值是512,那么PE文件中的节的长度都是512字节的整数倍,内容不够的部分用0填充。比如一个PE文件的 
      FileAlignment 
      为200h(十进制512),它的第一个节在400h处,长度为100h,那么从文件400h到500h中为这一节的内容,而文件对齐粒度是200h,所以为了使这一节长度为FileAlignment的整数倍,500h到600h会被用零填充。而下一个节的开始地址为600h。用16进制编辑器打开PE文件,就可以看到这种情况,PE文件头的内容结束到第一个节开始之间的地方,每一个节中内容结束到下一节开始的地方都会有大量的空白。VC6编译链接时默认的FileAlignment为1000h(4k),可以使用链接选项 
      /ALIGN:<I>number </I>来改变这个值。比如把4k改成512时,可以明显减小生成文件的大小。
      <P>3.2 big-endian和little-endian
      <P>&nbsp;&nbsp;&nbsp; PE Header中的 FileHeader 的成员 Machine 
      中的值,根据WINNT.H中的定义,对于 Intel CPU 应该为 0x014c。但是你用16进制编辑器打开PE文件,看到这个WORD显示的却是 
      4c 01 。你看到的并没有错,你看到的 4c 01 就是 0x014c,只不过由于 intel cpu 
      是ittle-endian,所以显示出来是这样的。对于 big-endian 和 little-endian,请看下面的例子。
      <P>比如一个整形int变量。长为四个字节。<BR>这个变量的地址比如为n。<BR>则这个变量的4个字节地址分别为n,n+1,n+2,n+3。<BR><BR>当 
      这个整形变量 的值为 0x12345678 时,<BR><BR>对于 big-endian 来说<BR>地址n+0的那个字节中的值为 
      0x12<BR>地址n+1的那个字节中的值为 0x34<BR>地址n+2的那个字节中的值为 0x56<BR>地址n+3的那个字节中的值为 
      0x78<BR><BR>按如下方式就会显示为<BR>n n+1 n+2 n+3&nbsp;<BR>12 34 56 78<BR><BR>对于 
      ittle-endian 来说<BR>地址n+0的那个字节中的值为 0x78<BR>地址n+1的那个字节中的值为 
      0x56<BR>地址n+2的那个字节中的值为 0x34<BR>地址n+3的那个字节中的值为 0x12<BR><BR>按如下方式就会显示为<BR>n 
      n+1 n+2 n+3&nbsp;<BR>78 56 34 12<BR><BR>Intel使用的是 ittle-endian 
      。<BR><BR>一个整形 int 变量 
      i,的地址是&amp;i,那么这个i的四个字节是&amp;i,&amp;i+1,&amp;i+2,&amp;i+3。<BR>可以用这样一个程序看到。<BR><BR>#include 
      &lt;stdio.h&gt;<BR>#include &lt;conio.h&gt;<BR><BR>void main()<BR>{<BR>int 
      i;<BR>char* p;<BR>p=(char*)&amp;i;<BR><BR>printf("i: 
      ");<BR>scanf("%x",&amp;i);<BR>printf("\n");<BR><BR>printf("&amp;i+0: 
      %x\n",*p);<BR>printf("&amp;i+1: %x\n",*(p+1));<BR>printf("&amp;i+2: 
      %x\n",*(p+2));<BR>printf("&amp;i+3: 
      %x\n",*(p+3));<BR><BR>printf("\n");<BR>printf("&amp;i-4: 
      %x\n",*(p-4));<BR>printf("&amp;i-3: %x\n",*(p-3));<BR>printf("&amp;i-2: 
      %x\n",*(p-2));<BR>printf("&amp;i-1: 
      %x\n",*(p-1));<BR><BR>printf("\n");<BR>printf("&amp;i+4: 
      %x\n",*(p+4));<BR>printf("&amp;i+5: %x\n",*(p+5));<BR>printf("&amp;i+6: 
      %x\n",*(p+6));<BR>printf("&amp;i+7: 
      %x\n",*(p+7));<BR><BR>getch();<BR><BR>}<BR><BR>当我们输入 12345678 
      的时候可以看到,输出<BR><BR>i: 12345678<BR><BR>&amp;i+0: 78<BR>&amp;i+1: 
      56<BR>&amp;i+2: 34<BR>&amp;i+3: 12<BR><BR>&amp;i-4: 7c<BR>&amp;i-3: 
      ffffffff<BR>&amp;i-2: 12<BR>&amp;i-1: 0<BR><BR>&amp;i+4: 
      ffffffc0<BR>&amp;i+5: ffffffff<BR>&amp;i+6: 12<BR>&amp;i+7: 
      0<BR>正是&amp;i,&amp;i+1,&amp;i+2,&amp;i+3这四个字节中储存了i的值。
      <P>对于int,WORD,DWORD等等都要注意 big-endian 和 little-endian 。
      <P>3.3 RVA (Relative Virtual Address) 相对虚拟地址
      <P>&nbsp;&nbsp;&nbsp; 
      RVA是一个简单的相对于PE载入点的内存偏移。比如,PE载入点为0X400000,那么代码节中的地址0X401000的RVA为(target 
      address) 0x401000 - (load address)0x400000 = (RVA)0x1000.换句话说 
      RVA是0x1000,载入点为0X400000,那么该RVA的在内存中的实际地址就是0X401000。注意一下RVA是指内存中,不是指文件中。是指相对于载入点的偏移而不是一个内存地址,只有RVA加上载入点的地址,才是一个实际的内存地址。
      <P>3.4 三种不同的地址
      <P>&nbsp;&nbsp;&nbsp; 
      PE的各种结构中,涉及到很多地址,偏移。有些是指在文件中的偏移,有的是指在内存中的偏移。一定要搞清楚,这个地址或者是偏移,是指在文件中,还是指在内存中。第一种,文件中的地址。比如用16进制编辑器打开PE文件,看到的地址(偏移)就是文件中的地址,我们使用某个结构的文件地址,就可以在文件中找到该结构。第二种,文件被整个映射到内存时,比如某些PE分析软件,把整个PE文件映射到内存中,这时是内存中的地址,如果知道某一个结构在文件中的地址的话,那么这个PE文件被映射到内存之后该结构的在内存中的地址,可以用文件中的地址加上映射内存的地址,就可以得到在该结构内存中的地址。第三种,执行PE时,PE文件会被载入器载入内存,这时经常需要的是RVA。比如知道一个结构的RVA,那么载入点加上RVA就可以得到内存中该结构的实际地址。比如,某个程序,我们用16进制编辑器打开它,看到PE 
      Header开始在16进制编辑器显示为000000C8的地方。于是我们在16进制编辑器显示为000000FC的地方找到了OptionalHeader的ImageBase,值为400000h,那么当这个程序被执行时,如果内存中400000h处没有使用,该程序就会被载入到那里。而我用CreateFileMapping将这个PE文件映射到内存中时,可以得到块内存的地址为5505024。对于映射入内存的这个PE文件,我们就可以在内存中000000FCh+05505024h=5505120处找到这个PE的OptionalHeader的ImageBase。
      <P>3.5 几个重要结构的说明
      <P>PE Header 的 FileHeader 的 NumberOfSections:这是一个很重要的字段,用来确定文件中节的数目。
      <P>PE Header 的 OptionalHeader 的 IMAGE_DATA_DIRECTORY 
      DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];<BR>#define 
      IMAGE_NUMBEROF_DIRECTORY_ENTRIES 
      16<BR>DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]:一个IMAGE_DATA_DIRECTORY 
      结构数组。到目前为止这个数组的长度是固定的,有16个元素,这16个元素分别代表<BR>#define 
      IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory<BR>#define 
      IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory<BR>#define 
      IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory<BR>#define 
      IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory<BR>#define 
      IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory<BR>#define 
      IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table<BR>#define 
      IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory<BR>// 
      IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage)<BR>#define 
      IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific 
      Data<BR>#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP<BR>#define 
      IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory<BR>#define 
      IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration 
      Directory<BR>#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import 
      Directory in headers<BR>#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import 
      Address Table<BR>#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay 
      Load Import Descriptors<BR>#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 
      // COM Runtime 
      descriptor<BR>每个元素是一个IMAGE_DATA_DIRECTORY结构,IMAGE_DATA_DIRECTORY定义如下。<BR>typedef 
      struct _IMAGE_DATA_DIRECTORY {<BR>DWORD VirtualAddress;<BR>DWORD 
      Size;<BR>} IMAGE_DATA_DIRECTORY, 
      *PIMAGE_DATA_DIRECTORY;<BR>第一个字段是一个RVA,第二个字段是一个大小。
      <P>Section Table 
      节表紧跟在OptionalHeader之后,是一个IMAGE_SECTION_HEADER结构的数组。该数组中成员的个数由 File Header 
      (IMAGE_FILE_HEADER) 结构中 NumberOfSections 
      域的域值来定。节表中的成员是IMAGE_SECTION_HEADER 结构,IMAGE_SECTION_HEADER 
      结构的长度固定,长40个字节。整个Section Table 的长度不固定,等于 
      NumberOfSections*sizeof(IMAGE_SECTION_HEADER)。IMAGE_SECTION_HEADER 
      结构中,<BR>VirtualAddress:本节的RVA(相对虚拟地址)。<BR>PointerToRawData:这是本节基于文件的偏移量。
      <P>3.6 DOS MZ Header 中的 MZ
      <P>&nbsp;&nbsp;&nbsp; MZ是MZ格式的主要作者 Mark Zbikowski 的名字的缩写。
      <P><B>完</B> </P></TD></TR></TBODY></TABLE></DIV></BODY></HTML>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -