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

📄 jiurl pe 格式学习总结(四)-- pe文件中的资源.htm

📁 关于win2000核心编程的文章
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0062)http://jiurl.cosoft.org.cn/jiurl/document/jiurlpe/jiurlpe4.htm -->
<HTML><HEAD><TITLE>JIURL's PE 4</TITLE>
<META content="text/html; charset=gb2312" http-equiv=Content-Type>
<META content=黑客,安全,网络,编程,黑客软件,安全软件,hacker,UNIX,Linux,FreeBSD name=keywords>
<META content=黑客,安全,网络,编程,黑客软件,安全软件,hacker,UNIX,Linux,FreeBSD name=description>
<STYLE type=text/css>.title {
	FONT-FAMILY: "黑体", Arial, sans-serif; FONT-SIZE: 21px; FONT-WEIGHT: bold; LINE-HEIGHT: 48px; TEXT-DECORATION: none
}
.author {
	FONT-FAMILY: "宋体"; FONT-SIZE: 12px; LINE-HEIGHT: 16px
}
.content {
	FONT-SIZE: 14px; LINE-HEIGHT: 20px
}
</STYLE>

<META content="MSHTML 5.00.2614.3500" name=GENERATOR></HEAD>
<BODY bgColor=#f7f7f7 topMargin=5>
<DIV align=center>
<CENTER>
<TABLE border=0 cellPadding=0 cellSpacing=0 height=29 width="96%">
  <TBODY>
  <TR>
    <TD class=title height=41 width="100%">
      <P align=center><FONT face=宋体>JIURL PE 格式学习总结(四)-- 
  PE文件中的资源</FONT></P></TD></TR></CENTER>
  <TR>
    <TD class=author height=9 width="100%">
      <P align=center>作者: <A href="mailto:jiurl@mail.china.com">JIURL</A> 
  </P></TD></TR>
  <TR>
    <TD class=author height=6 width="100%">
      <P 
      align=center>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      主页: <A href="http://jiurl.yeah.net/">http://jiurl.yeah.net/</A> </P></TD></TR>
  <TR>
    <TD class=author height=2 width="100%">
      <P align=center>&nbsp;&nbsp;&nbsp; 日期: 2003-4-24 
</P></TD></TR></TBODY></TABLE></DIV>
<DIV align=center>
<CENTER>
<TABLE border=0 cellPadding=0 cellSpacing=0 height=1 width="96%">
  <TBODY>
  <TR>
    <TD height=1 width="100%">
      <HR color=#396da5 SIZE=3>
    </TD></TR></TBODY></TABLE></CENTER></DIV>
<DIV align=center>
<TABLE border=0 cellPadding=0 cellSpacing=0 class=content height=3194 
width="96%">
  <TBODY>
  <TR>
    <TD height=7500 vAlign=top width="131%">&nbsp;&nbsp;&nbsp; 程序所用到的各种资源,比如 
      bmp,cursor,menu,对话框等都存在PE文件中。<BR>&nbsp;&nbsp;&nbsp; 
      我们将详细介绍关于资源的各种结构,通过一个例子来说明资源及其相关结构是怎么放在PE文件中的。以及如何在遍历PE文件中的所有资源。我们只最终找到这些资源在文件中的位置和长度。而不具体分析某种资源的格式,比如有个BMP的资源,我们不分析BMP格式。 

      <P><B>一 找到资源在文件中位置。</B></P>
      <P>&nbsp;&nbsp;&nbsp; 
      资源都放在PE文件的某个节中,该节的节表项中的PointerToRawData,就是资源节在文件中的位置。</P>
      <P>1.1 得到PE Header在文件中的位置。<BR>&nbsp;&nbsp;&nbsp; 通过DOS 
      Header结构的成员e_lfanew,可以确定PE Header的在文件中的位置。</P>
      <P>1.2 得到文件中节的数目。<BR>&nbsp;&nbsp;&nbsp; 确定PE Header的在文件中的位置之后,就可以确定PE 
      Header中的成员FileHeader和成员OptionalHeader在文件中的位置。根据 FileHeader 中的 
      成员NumberOfSections 的值,就可以确定文件中节的数目,也就是节表数组中元素的个数。<BR><BR>1.3 
      得到节表在文件中的位置。<BR>&nbsp;&nbsp;&nbsp; PE Header在文件中的位置加上PE 
      Header结构的大小就可以得到节表在文件中的开始位置。PE 
      Header结构的大小可以由Signature的大小加上FileHeader的大小再加上FileHeader中的SizeOfOptionalHeade来确定。其实到目前为止SizeOfOptionalHeade也就是结构Optional 
      Header的大小也是固定的,所以整个PE 
      Header结构的大小也是固定。不过为了安全起见,还是用Signature的大小加上FileHeader的大小再加上FileHeader中的SizeOfOptionalHeade来确定比较保险。</P>
      <P>1.4 得到资源节在文件中的位置。<BR>&nbsp;&nbsp;&nbsp; 
      第1.2步中我们确定了文件中节的数目,第1.3步中我们确定了节表在文件中的位置。<BR>&nbsp;&nbsp;&nbsp; 
      现在有两种方法来确定资源在文件中的位置。<BR>&nbsp;&nbsp;&nbsp; 
      第一种方法,根据节的数目,遍历节表数组。也就是从0到(节表数-1)的每一个节表项。<BR>比较每一个节表项的Name字段,看是否等于".rsrc"。如果等于。就找到了资源节的节表项。<BR>这个节表项中的 
      PointerToRawData 中的值,就是资源节在文件中的位置。<BR>&nbsp;&nbsp;&nbsp; 第二种方法,取得PE 
      Header中的Optional 
      Header中的DataDirectory数组中的第三项,<BR>也就是资源项。DataDirectory[]数组的每项都是IMAGE_DATA_DIRECTORY结构,该结构定义如下。<BR>typedef 
      struct _IMAGE_DATA_DIRECTORY {<BR>DWORD VirtualAddress;<BR>DWORD 
      Size;<BR>} IMAGE_DATA_DIRECTORY, 
      *PIMAGE_DATA_DIRECTORY;<BR>取得DataDirectory数组中的第三项中的成员VirtualAddress的值。这个值就是在内存中资源节的RVA。<BR>然后根据节的数目,遍历节表数组。也就是从0到(节表数-1)的每一个节表项。<BR>每个节在内存中的RVA的范围是从该节表项的成员VirtualAddress字段的值开始(包括这个值),<BR>到VirtualAddress+Misc.VirtualSize的值结束(不包括这个值)。<BR>我们遍历整个节表,看我们取得的资源节的RVA,在哪个节表项的RVA范围之内。<BR>如果在范围之内,就找到了资源节的节表项。<BR>这个节表项中的 
      PointerToRawData 
      中的值,就是资源节在文件中的位置。<BR>如果这个PE文件没有资源的话,DataDirectory数组中的第三项内容为0。<BR><BR>这样我们就得到了资源在文件中开始的位置。<BR><BR><B>二 
      PE文件中的资源。</B></P>
      <P>&nbsp;&nbsp;&nbsp; 
      我们已经得到了资源节在文件中的位置。<BR>资源节最开始是一个IMAGE_RESOURCE_DIRECTORY结构。<BR>在WINNT.H中定义如下。</P>
      <P>typedef struct _IMAGE_RESOURCE_DIRECTORY {<BR>DWORD 
      Characteristics;<BR>DWORD TimeDateStamp;<BR>WORD MajorVersion;<BR>WORD 
      MinorVersion;<BR>WORD NumberOfNamedEntries;<BR>WORD 
      NumberOfIdEntries;<BR>// IMAGE_RESOURCE_DIRECTORY_ENTRY 
      DirectoryEntries[];<BR>} IMAGE_RESOURCE_DIRECTORY, 
      *PIMAGE_RESOURCE_DIRECTORY;<BR>这个结构长度为16字节,共有6个字段。<BR>各字段含义如下:<BR>Characteristics: 
      Resource 
      flags,保留用于以后使用,目前都为0。<BR>TimeDateStamp:资源编译器产生资源的时间。<BR>MajorVersion:<BR>MinorVersion:<BR>NumberOfNamedEntries:用字符串来标示IMAGE_RESOURCE_DIRECTORY_ENTRY项的,紧跟着本结构的IMAGE_RESOURCE_DIRECTORY_ENTRY数组的成员个数。<BR>Number 
      of ID 
      Entries:用整形数字来表示IMAGE_RESOURCE_DIRECTORY_ENTRY项的,紧跟着本结构的IMAGE_RESOURCE_DIRECTORY_ENTRY数组的成员个数。<BR><BR>&nbsp;&nbsp;&nbsp; 
      IMAGE_RESOURCE_DIRECTORY后面一定会紧跟着一个IMAGE_RESOURCE_DIRECTORY_ENTRY数组。<BR>IMAGE_RESOURCE_DIRECTORY_ENTRY结构定义如下。<BR><BR>typedef 
      struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {<BR>union {<BR>struct {<BR>DWORD 
      NameOffset:31;<BR>DWORD NameIsString:1;<BR>};<BR>DWORD Name;<BR>WORD 
      Id;<BR>};<BR>union {<BR>DWORD OffsetToData;<BR>struct {<BR>DWORD 
      OffsetToDirectory:31;<BR>DWORD DataIsDirectory:1;<BR>};<BR>};<BR>} 
      IMAGE_RESOURCE_DIRECTORY_ENTRY, 
      *PIMAGE_RESOURCE_DIRECTORY_ENTRY;<BR><BR>这个结构长度为8个字节。共有两个字段,每个字段4个字节。<BR>根据不同情况,这两个字段的含义不一样。这个结构的定义如果看不懂的话,后面的例子一看就会明白了。<BR>第一个字段,当第一个字段的最高位是1的时候,表示,这个DWORD的剩下31位表明一个相对于资源开始位置的偏移,这个偏移的内容是一个IMAGE_RESOURCE_DIR_STRING,用里面的字符串来标明这个IMAGE_RESOURCE_DIRECTORY_ENTRY。当第一个字段的最高位是0的时候,表示,这个DWORD的低WORD中的值作为id标明这个IMAGE_RESOURCE_DIRECTORY_ENTRY。<BR>第二个字段,当第二个字段的最高位是1的时候,表示,还有下一层的结构。这个DWORD的剩下31位表明一个相对于资源开始位置的偏移,这个偏移的内容会是一个下一层的IMAGE_RESOURCE_DIRECTORY结构,这个请看后面的例子中的说明。<BR>当第二个字段的最高位是0的时候,表示,已经没有下一层的结构了。这个DWORD的剩下31位表明一个相对于资源开始位置的偏移,这个偏移的内容会是一个IMAGE_RESOURCE_DATA_ENTRY结构,IMAGE_RESOURCE_DATA_ENTRY结构会说明资源的位置。</P>
      <P>&nbsp;&nbsp;&nbsp; 
      标示一个IMAGE_RESOURCE_DIRECTORY_ENTRY一般都是使用id,就是一个整数。<BR>但是也有少数的使用IMAGE_RESOURCE_DIR_STRING来标示一个IMAGE_RESOURCE_DIRECTORY_ENTRY。<BR>IMAGE_RESOURCE_DIRECTORY_ENTRY结构定义如下。<BR>typedef 
      struct _IMAGE_RESOURCE_DIR_STRING_U {<BR>WORD Length;<BR>WCHAR NameString[ 
      1 ];<BR>} IMAGE_RESOURCE_DIR_STRING_U, 
      *PIMAGE_RESOURCE_DIR_STRING_U;<BR>这个结构中将有一个Unicode的字符串,是字对齐的。所有这些用来标识的IMAGE_RESOURCE_DIR_STRING都放在一起,这个结构的长度是可变的,由第一个字段Length指明后面的Unicode字符串的长度。<BR><BR>&nbsp;&nbsp;&nbsp; 
      经过3层IMAGE_RESOURCE_DIRECTORY_ENTRY(一般是3层,也有可能更少些。第一层资源类型bmp,menu等等,第二层资源名,第三层是资源的Language。)最终会找到一个IMAGE_RESOURCE_DATA_ENTRY结构,这个结构中存有相应(某资源类型,某资源名,某资源Language)资源的位置和大小,就真正找到资源了。IMAGE_RESOURCE_DATA_ENTRY定义如下。<BR>typedef 
      struct _IMAGE_RESOURCE_DATA_ENTRY {<BR>DWORD OffsetToData;<BR>DWORD 
      Size;<BR>DWORD CodePage;<BR>DWORD Reserved;<BR>} 
      IMAGE_RESOURCE_DATA_ENTRY, 
      *PIMAGE_RESOURCE_DATA_ENTRY;<BR>这个结构长16个字节,有4个字段。<BR>OffsetToData:这是一个内存中的RVA,要转化成文件中的位置,需要用这个值减去资源节的开始RVA,<BR>资源节的开始RVA可以由Optional 
      Header中的DataDirectory数组中的第三项中的VirtualAddress的值得到。<BR>或者节表中,资源节那项中的VirtualAddress的值得到。相减之后,就可以得到相对于资源节开始的偏移。<BR>再加上资源节在文件中的开始位置,节表中资源节那项中的PointerToRawData的值,就是资源在文件中的位置。<BR><BR>Size:资源的大小,以字节为单位。<BR>CodePage:一般来说是Unicode 
      code 
      page。<BR>Reserved:保留,值为0。<BR><BR>上面是资源各种结构的说明,知道这些结构还远远不够,下面我们通过一个例子来看如何通过这些结构找到资源。<BR><BR>我们的例子是Win2k中的可执行文件telnet.exe。为了防止大家版本不同,本文附带了这个PE文件。</P>
      <P>PE文件的资源的各种结构放在一个树型结构中,这个结构一般有3层,如图4.1,就是telnet.exe中的情况。</P>
      <P align=center><IMG border=0 height=332 
      src="JIURL PE 格式学习总结(四)-- PE文件中的资源.files/4.1.gif" width=624> <BR></P>
      <P align=center>图4.1</P>
      <P 
      align=left>图中长的长方形表示一个IMAGE_RESOURCE_DIRECTORY结构,长16个字节,简称directory。<BR>图中短的长方形表示一个IMAGE_RESOURCE_DIRECTORY_ENTRY结构,长8个字节,简称directory_entry。<BR>图中圆圈表示一个IMAGE_RESOURCE_DATA_ENTRY结构,长16个字节,简称data_entry。</P>
      <P 
      align=left>为了以后的叙述方便还给树的每一个节点起了名字,第一层的叫11,第二层的叫21,22,23,24,第三层的叫31,32,33,34,35,36,37,38,39,310,311,312。</P>
      <P 
      align=left>在资源节开始处,是一个directory结构,这个结构中指明了紧跟在它后面的一个directory_entry结构数组中的元素的个数。这个directory结构之后,紧跟着的就是那个directory_entry结构数组。他们一起组成了11。就如图4.1中所示。其他的每个节点,21,22..31,32..312,都是这样,每个 
      directory结构后面紧跟directory_entry 
      结构数组。11中的directory_entry结构数组中的每一个元素,都存有到下一层某个节点的偏移。也就是通过directory_entry结构数组的每个元素可以找到21,22,23,24。其他的节点中情况也是一样。图中看不到的一点是,所有的节点之间都是紧紧的挨在一起存放的,11之后紧跟着的是21,21之后紧跟着的是22,22之后紧跟着的是23。依此类推。directory_entry结构数组中的每一个元素除了有到下一层某节点的偏移,(是下一层的节点,还是已经到了最终的data_entry,后面详细叙述)还有一个Name或者Id字段(是Name还是Id后面详细叙述),根据不同的层,代表的含义也不一样。第一层的每个directory_entry的这个值,代表类型。比如11的第一个directory_entry的Id值为3,3代表icon,从这个directory_entry往下的都是都是图标了(关于不同类型值的定义,后面详细叙述)。第二层每个directory_entry的这个值代表Name,第三层代表Language。11,21,31的左边那个data_entry,的三个值分别为3,1,409(都是16进制),就是说是一个图标类型,Name为1h,Language为409h的资源。</P>
      <P 
      align=left>下面我们来通过telnet.exe中资源节的具体内容来看,用开始讲到的寻找资源节在文件中位置的方法,我们找到了资源节在文件中的位置为00013600h。</P>
      <P align=left>我们为了看起来清楚,每一行是一个结构,并且每个结构的不同成员用 / 分开,例如,<BR>一个directory结构 00 
      00 00 00 / 00 00 00 00 / 04 00 / 00 00 / 00 00 / 04 
      00&nbsp;&nbsp;<BR>可以看到结构成员,Characteristics为0,TimeDateStamp为0,MajorVersion为4,(如果你不明白为什么是0004而不是0400的话,请看 
      《JIURL PE 格式学习总结(一)》中关于 
      big-endian和little-endian的介绍),MinorVersion为0,NumberOfNamedEntries为0,NumberOfIdEntries为4。</P>
      <P align=left>一个directory_entry结构 03 00 00 00 / 30 00 00 

⌨️ 快捷键说明

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