?? jiurl pe 格式學(xué)習(xí)總結(jié)(四)-- pe文件中的資源.htm
字號(hào):
<!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=黑客,安全,網(wǎng)絡(luò),編程,黑客軟件,安全軟件,hacker,UNIX,Linux,FreeBSD name=keywords>
<META content=黑客,安全,網(wǎng)絡(luò),編程,黑客軟件,安全軟件,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 格式學(xué)習(xí)總結(jié)(四)--
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>
主頁(yè): <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> 日期: 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%"> 程序所用到的各種資源,比如
bmp,cursor,menu,對(duì)話框等都存在PE文件中。<BR>
我們將詳細(xì)介紹關(guān)于資源的各種結(jié)構(gòu),通過(guò)一個(gè)例子來(lái)說(shuō)明資源及其相關(guān)結(jié)構(gòu)是怎么放在PE文件中的。以及如何在遍歷PE文件中的所有資源。我們只最終找到這些資源在文件中的位置和長(zhǎng)度。而不具體分析某種資源的格式,比如有個(gè)BMP的資源,我們不分析BMP格式。
<P><B>一 找到資源在文件中位置。</B></P>
<P>
資源都放在PE文件的某個(gè)節(jié)中,該節(jié)的節(jié)表項(xiàng)中的PointerToRawData,就是資源節(jié)在文件中的位置。</P>
<P>1.1 得到PE Header在文件中的位置。<BR> 通過(guò)DOS
Header結(jié)構(gòu)的成員e_lfanew,可以確定PE Header的在文件中的位置。</P>
<P>1.2 得到文件中節(jié)的數(shù)目。<BR> 確定PE Header的在文件中的位置之后,就可以確定PE
Header中的成員FileHeader和成員OptionalHeader在文件中的位置。根據(jù) FileHeader 中的
成員NumberOfSections 的值,就可以確定文件中節(jié)的數(shù)目,也就是節(jié)表數(shù)組中元素的個(gè)數(shù)。<BR><BR>1.3
得到節(jié)表在文件中的位置。<BR> PE Header在文件中的位置加上PE
Header結(jié)構(gòu)的大小就可以得到節(jié)表在文件中的開(kāi)始位置。PE
Header結(jié)構(gòu)的大小可以由Signature的大小加上FileHeader的大小再加上FileHeader中的SizeOfOptionalHeade來(lái)確定。其實(shí)到目前為止SizeOfOptionalHeade也就是結(jié)構(gòu)Optional
Header的大小也是固定的,所以整個(gè)PE
Header結(jié)構(gòu)的大小也是固定。不過(guò)為了安全起見(jiàn),還是用Signature的大小加上FileHeader的大小再加上FileHeader中的SizeOfOptionalHeade來(lái)確定比較保險(xiǎn)。</P>
<P>1.4 得到資源節(jié)在文件中的位置。<BR>
第1.2步中我們確定了文件中節(jié)的數(shù)目,第1.3步中我們確定了節(jié)表在文件中的位置。<BR>
現(xiàn)在有兩種方法來(lái)確定資源在文件中的位置。<BR>
第一種方法,根據(jù)節(jié)的數(shù)目,遍歷節(jié)表數(shù)組。也就是從0到(節(jié)表數(shù)-1)的每一個(gè)節(jié)表項(xiàng)。<BR>比較每一個(gè)節(jié)表項(xiàng)的Name字段,看是否等于".rsrc"。如果等于。就找到了資源節(jié)的節(jié)表項(xiàng)。<BR>這個(gè)節(jié)表項(xiàng)中的
PointerToRawData 中的值,就是資源節(jié)在文件中的位置。<BR> 第二種方法,取得PE
Header中的Optional
Header中的DataDirectory數(shù)組中的第三項(xiàng),<BR>也就是資源項(xiàng)。DataDirectory[]數(shù)組的每項(xiàng)都是IMAGE_DATA_DIRECTORY結(jié)構(gòu),該結(jié)構(gòu)定義如下。<BR>typedef
struct _IMAGE_DATA_DIRECTORY {<BR>DWORD VirtualAddress;<BR>DWORD
Size;<BR>} IMAGE_DATA_DIRECTORY,
*PIMAGE_DATA_DIRECTORY;<BR>取得DataDirectory數(shù)組中的第三項(xiàng)中的成員VirtualAddress的值。這個(gè)值就是在內(nèi)存中資源節(jié)的RVA。<BR>然后根據(jù)節(jié)的數(shù)目,遍歷節(jié)表數(shù)組。也就是從0到(節(jié)表數(shù)-1)的每一個(gè)節(jié)表項(xiàng)。<BR>每個(gè)節(jié)在內(nèi)存中的RVA的范圍是從該節(jié)表項(xiàng)的成員VirtualAddress字段的值開(kāi)始(包括這個(gè)值),<BR>到VirtualAddress+Misc.VirtualSize的值結(jié)束(不包括這個(gè)值)。<BR>我們遍歷整個(gè)節(jié)表,看我們?nèi)〉玫馁Y源節(jié)的RVA,在哪個(gè)節(jié)表項(xiàng)的RVA范圍之內(nèi)。<BR>如果在范圍之內(nèi),就找到了資源節(jié)的節(jié)表項(xiàng)。<BR>這個(gè)節(jié)表項(xiàng)中的
PointerToRawData
中的值,就是資源節(jié)在文件中的位置。<BR>如果這個(gè)PE文件沒(méi)有資源的話,DataDirectory數(shù)組中的第三項(xiàng)內(nèi)容為0。<BR><BR>這樣我們就得到了資源在文件中開(kāi)始的位置。<BR><BR><B>二
PE文件中的資源。</B></P>
<P>
我們已經(jīng)得到了資源節(jié)在文件中的位置。<BR>資源節(jié)最開(kāi)始是一個(gè)IMAGE_RESOURCE_DIRECTORY結(jié)構(gòu)。<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>這個(gè)結(jié)構(gòu)長(zhǎng)度為16字節(jié),共有6個(gè)字段。<BR>各字段含義如下:<BR>Characteristics:
Resource
flags,保留用于以后使用,目前都為0。<BR>TimeDateStamp:資源編譯器產(chǎn)生資源的時(shí)間。<BR>MajorVersion:<BR>MinorVersion:<BR>NumberOfNamedEntries:用字符串來(lái)標(biāo)示IMAGE_RESOURCE_DIRECTORY_ENTRY項(xiàng)的,緊跟著本結(jié)構(gòu)的IMAGE_RESOURCE_DIRECTORY_ENTRY數(shù)組的成員個(gè)數(shù)。<BR>Number
of ID
Entries:用整形數(shù)字來(lái)表示IMAGE_RESOURCE_DIRECTORY_ENTRY項(xiàng)的,緊跟著本結(jié)構(gòu)的IMAGE_RESOURCE_DIRECTORY_ENTRY數(shù)組的成員個(gè)數(shù)。<BR><BR>
IMAGE_RESOURCE_DIRECTORY后面一定會(huì)緊跟著一個(gè)IMAGE_RESOURCE_DIRECTORY_ENTRY數(shù)組。<BR>IMAGE_RESOURCE_DIRECTORY_ENTRY結(jié)構(gòu)定義如下。<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>這個(gè)結(jié)構(gòu)長(zhǎng)度為8個(gè)字節(jié)。共有兩個(gè)字段,每個(gè)字段4個(gè)字節(jié)。<BR>根據(jù)不同情況,這兩個(gè)字段的含義不一樣。這個(gè)結(jié)構(gòu)的定義如果看不懂的話,后面的例子一看就會(huì)明白了。<BR>第一個(gè)字段,當(dāng)?shù)谝粋€(gè)字段的最高位是1的時(shí)候,表示,這個(gè)DWORD的剩下31位表明一個(gè)相對(duì)于資源開(kāi)始位置的偏移,這個(gè)偏移的內(nèi)容是一個(gè)IMAGE_RESOURCE_DIR_STRING,用里面的字符串來(lái)標(biāo)明這個(gè)IMAGE_RESOURCE_DIRECTORY_ENTRY。當(dāng)?shù)谝粋€(gè)字段的最高位是0的時(shí)候,表示,這個(gè)DWORD的低WORD中的值作為id標(biāo)明這個(gè)IMAGE_RESOURCE_DIRECTORY_ENTRY。<BR>第二個(gè)字段,當(dāng)?shù)诙€(gè)字段的最高位是1的時(shí)候,表示,還有下一層的結(jié)構(gòu)。這個(gè)DWORD的剩下31位表明一個(gè)相對(duì)于資源開(kāi)始位置的偏移,這個(gè)偏移的內(nèi)容會(huì)是一個(gè)下一層的IMAGE_RESOURCE_DIRECTORY結(jié)構(gòu),這個(gè)請(qǐng)看后面的例子中的說(shuō)明。<BR>當(dāng)?shù)诙€(gè)字段的最高位是0的時(shí)候,表示,已經(jīng)沒(méi)有下一層的結(jié)構(gòu)了。這個(gè)DWORD的剩下31位表明一個(gè)相對(duì)于資源開(kāi)始位置的偏移,這個(gè)偏移的內(nèi)容會(huì)是一個(gè)IMAGE_RESOURCE_DATA_ENTRY結(jié)構(gòu),IMAGE_RESOURCE_DATA_ENTRY結(jié)構(gòu)會(huì)說(shuō)明資源的位置。</P>
<P>
標(biāo)示一個(gè)IMAGE_RESOURCE_DIRECTORY_ENTRY一般都是使用id,就是一個(gè)整數(shù)。<BR>但是也有少數(shù)的使用IMAGE_RESOURCE_DIR_STRING來(lái)標(biāo)示一個(gè)IMAGE_RESOURCE_DIRECTORY_ENTRY。<BR>IMAGE_RESOURCE_DIRECTORY_ENTRY結(jié)構(gòu)定義如下。<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>這個(gè)結(jié)構(gòu)中將有一個(gè)Unicode的字符串,是字對(duì)齊的。所有這些用來(lái)標(biāo)識(shí)的IMAGE_RESOURCE_DIR_STRING都放在一起,這個(gè)結(jié)構(gòu)的長(zhǎng)度是可變的,由第一個(gè)字段Length指明后面的Unicode字符串的長(zhǎng)度。<BR><BR>
經(jīng)過(guò)3層IMAGE_RESOURCE_DIRECTORY_ENTRY(一般是3層,也有可能更少些。第一層資源類型bmp,menu等等,第二層資源名,第三層是資源的Language。)最終會(huì)找到一個(gè)IMAGE_RESOURCE_DATA_ENTRY結(jié)構(gòu),這個(gè)結(jié)構(gòu)中存有相應(yīng)(某資源類型,某資源名,某資源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>這個(gè)結(jié)構(gòu)長(zhǎng)16個(gè)字節(jié),有4個(gè)字段。<BR>OffsetToData:這是一個(gè)內(nèi)存中的RVA,要轉(zhuǎn)化成文件中的位置,需要用這個(gè)值減去資源節(jié)的開(kāi)始RVA,<BR>資源節(jié)的開(kāi)始RVA可以由Optional
Header中的DataDirectory數(shù)組中的第三項(xiàng)中的VirtualAddress的值得到。<BR>或者節(jié)表中,資源節(jié)那項(xiàng)中的VirtualAddress的值得到。相減之后,就可以得到相對(duì)于資源節(jié)開(kāi)始的偏移。<BR>再加上資源節(jié)在文件中的開(kāi)始位置,節(jié)表中資源節(jié)那項(xiàng)中的PointerToRawData的值,就是資源在文件中的位置。<BR><BR>Size:資源的大小,以字節(jié)為單位。<BR>CodePage:一般來(lái)說(shuō)是Unicode
code
page。<BR>Reserved:保留,值為0。<BR><BR>上面是資源各種結(jié)構(gòu)的說(shuō)明,知道這些結(jié)構(gòu)還遠(yuǎn)遠(yuǎn)不夠,下面我們通過(guò)一個(gè)例子來(lái)看如何通過(guò)這些結(jié)構(gòu)找到資源。<BR><BR>我們的例子是Win2k中的可執(zhí)行文件telnet.exe。為了防止大家版本不同,本文附帶了這個(gè)PE文件。</P>
<P>PE文件的資源的各種結(jié)構(gòu)放在一個(gè)樹(shù)型結(jié)構(gòu)中,這個(gè)結(jié)構(gòu)一般有3層,如圖4.1,就是telnet.exe中的情況。</P>
<P align=center><IMG border=0 height=332
src="JIURL PE 格式學(xué)習(xí)總結(jié)(四)-- PE文件中的資源.files/4.1.gif" width=624> <BR></P>
<P align=center>圖4.1</P>
<P
align=left>圖中長(zhǎng)的長(zhǎng)方形表示一個(gè)IMAGE_RESOURCE_DIRECTORY結(jié)構(gòu),長(zhǎng)16個(gè)字節(jié),簡(jiǎn)稱directory。<BR>圖中短的長(zhǎng)方形表示一個(gè)IMAGE_RESOURCE_DIRECTORY_ENTRY結(jié)構(gòu),長(zhǎng)8個(gè)字節(jié),簡(jiǎn)稱directory_entry。<BR>圖中圓圈表示一個(gè)IMAGE_RESOURCE_DATA_ENTRY結(jié)構(gòu),長(zhǎng)16個(gè)字節(jié),簡(jiǎn)稱data_entry。</P>
<P
align=left>為了以后的敘述方便還給樹(shù)的每一個(gè)節(jié)點(diǎn)起了名字,第一層的叫11,第二層的叫21,22,23,24,第三層的叫31,32,33,34,35,36,37,38,39,310,311,312。</P>
<P
align=left>在資源節(jié)開(kāi)始處,是一個(gè)directory結(jié)構(gòu),這個(gè)結(jié)構(gòu)中指明了緊跟在它后面的一個(gè)directory_entry結(jié)構(gòu)數(shù)組中的元素的個(gè)數(shù)。這個(gè)directory結(jié)構(gòu)之后,緊跟著的就是那個(gè)directory_entry結(jié)構(gòu)數(shù)組。他們一起組成了11。就如圖4.1中所示。其他的每個(gè)節(jié)點(diǎn),21,22..31,32..312,都是這樣,每個(gè)
directory結(jié)構(gòu)后面緊跟directory_entry
結(jié)構(gòu)數(shù)組。11中的directory_entry結(jié)構(gòu)數(shù)組中的每一個(gè)元素,都存有到下一層某個(gè)節(jié)點(diǎn)的偏移。也就是通過(guò)directory_entry結(jié)構(gòu)數(shù)組的每個(gè)元素可以找到21,22,23,24。其他的節(jié)點(diǎn)中情況也是一樣。圖中看不到的一點(diǎn)是,所有的節(jié)點(diǎn)之間都是緊緊的挨在一起存放的,11之后緊跟著的是21,21之后緊跟著的是22,22之后緊跟著的是23。依此類推。directory_entry結(jié)構(gòu)數(shù)組中的每一個(gè)元素除了有到下一層某節(jié)點(diǎn)的偏移,(是下一層的節(jié)點(diǎn),還是已經(jīng)到了最終的data_entry,后面詳細(xì)敘述)還有一個(gè)Name或者Id字段(是Name還是Id后面詳細(xì)敘述),根據(jù)不同的層,代表的含義也不一樣。第一層的每個(gè)directory_entry的這個(gè)值,代表類型。比如11的第一個(gè)directory_entry的Id值為3,3代表icon,從這個(gè)directory_entry往下的都是都是圖標(biāo)了(關(guān)于不同類型值的定義,后面詳細(xì)敘述)。第二層每個(gè)directory_entry的這個(gè)值代表Name,第三層代表Language。11,21,31的左邊那個(gè)data_entry,的三個(gè)值分別為3,1,409(都是16進(jìn)制),就是說(shuō)是一個(gè)圖標(biāo)類型,Name為1h,Language為409h的資源。</P>
<P
align=left>下面我們來(lái)通過(guò)telnet.exe中資源節(jié)的具體內(nèi)容來(lái)看,用開(kāi)始講到的尋找資源節(jié)在文件中位置的方法,我們找到了資源節(jié)在文件中的位置為00013600h。</P>
<P align=left>我們?yōu)榱丝雌饋?lái)清楚,每一行是一個(gè)結(jié)構(gòu),并且每個(gè)結(jié)構(gòu)的不同成員用 / 分開(kāi),例如,<BR>一個(gè)directory結(jié)構(gòu) 00
00 00 00 / 00 00 00 00 / 04 00 / 00 00 / 00 00 / 04
00 <BR>可以看到結(jié)構(gòu)成員,Characteristics為0,TimeDateStamp為0,MajorVersion為4,(如果你不明白為什么是0004而不是0400的話,請(qǐng)看
《JIURL PE 格式學(xué)習(xí)總結(jié)(一)》中關(guān)于
big-endian和little-endian的介紹),MinorVersion為0,NumberOfNamedEntries為0,NumberOfIdEntries為4。</P>
<P align=left>一個(gè)directory_entry結(jié)構(gòu) 03 00 00 00 / 30 00 00
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -