?? lion-petut-c07.htm
字號(hào):
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=gb_2312-80">
<meta name="GENERATOR" content="Microsoft FrontPage Express 2.0">
<title>Iczelion的PE教程7: Export Table(引出表)</title>
</head>
<body bgcolor="#000066" text="#FFFFFF" link="#FFFFCC"
vlink="#FFCCCC" alink="#CCFFCC">
<h1 align="center"><font color="#FFFFCC">PE教程7: Export Table(引出表)</font></h1>
<p><font size="2" face="MS Sans Serif">上一課我們已經(jīng)學(xué)習(xí)了動(dòng)態(tài)聯(lián)接中關(guān)于引入表那部分知識(shí),現(xiàn)在繼續(xù)另外一部分,那就是引出表。</font></p>
<p><font size="2">下載 </font><a href="files/pe-tut07.zip"
style="text-decoration:none"><font size="2"><b>范例</b></font></a><font
size="2">。</font></p>
<h3>理論<font face="Arial, Helvetica, sans-serif">:</font></h3>
<p><font size="2" face="MS Sans Serif">當(dāng)PE裝載器執(zhí)行一個(gè)程序,它將相關(guān)DLLs都裝入該進(jìn)程的地址空間。然后根據(jù)主程序的引入函數(shù)信息,查找相關(guān)DLLs中的真實(shí)函數(shù)地址來(lái)修正主程序。PE裝載器搜尋的是DLLs中的引出函數(shù)。</font></p>
<p><font size="2" face="MS Sans Serif">DLL/EXE要引出一個(gè)函數(shù)給其他DLL/EXE使用,有兩種實(shí)現(xiàn)方法: 通過(guò)函數(shù)名引出或者僅僅通過(guò)序數(shù)引出。比如某個(gè)DLL要引出名為"GetSysConfig"的函數(shù),如果它以函數(shù)名引出,那么其他DLLs/EXEs若要調(diào)用這個(gè)函數(shù),必須通過(guò)函數(shù)名,就是GetSysConfig。另外一個(gè)辦法就是通過(guò)序數(shù)引出。什么是序數(shù)呢?
序數(shù)是唯一指定DLL中某個(gè)函數(shù)的16位數(shù)字,在所指向的DLL里是獨(dú)一無(wú)二的。例如在上例中,DLL可以選擇通過(guò)序數(shù)引出,假設(shè)是16,那么其他DLLs/EXEs若要調(diào)用這個(gè)函數(shù)必須以該值作為GetProcAddress調(diào)用參數(shù)。這就是所謂的僅僅靠序數(shù)引出。</font></p>
<p><font size="2" face="MS Sans Serif">我們不提倡僅僅通過(guò)序數(shù)引出函數(shù)這種方法,這會(huì)帶來(lái)DLL維護(hù)上的問(wèn)題。一旦DLL升級(jí)/修改,程序員無(wú)法改變函數(shù)的序數(shù),否則調(diào)用該DLL的其他程序都將無(wú)法工作。</font></p>
<p><font size="2" face="MS Sans Serif">現(xiàn)在我們開(kāi)始學(xué)習(xí)引出結(jié)構(gòu)。象引出表一樣,可以通過(guò)數(shù)據(jù)目錄找到引出表的位置。這兒,引出表是數(shù)據(jù)目錄的第一個(gè)成員,又可稱為IMAGE_EXPORT_DIRECTORY。該結(jié)構(gòu)中共有11
個(gè)成員,常用的列于下表。</font></p>
<table border="1" cellpadding="2">
<tr>
<th bgcolor="#006666"><font size="2" face="MS Sans Serif"><b>Field
Name</b></font></th>
<th bgcolor="#006666"><font size="2" face="MS Sans Serif">Meaning</font></th>
</tr>
<tr>
<td align="center" bgcolor="#006666"><font size="2"
face="MS Sans Serif"><b>nName</b></font></td>
<td align="center" bgcolor="#006666"><font size="2"
face="MS Sans Serif">模塊的真實(shí)名稱。本域是必須的,因?yàn)槲募赡軙?huì)改變。這種情況下,PE裝載器將使用這個(gè)內(nèi)部名字。</font></td>
</tr>
<tr>
<td align="center" bgcolor="#006666"><font size="2"
face="MS Sans Serif"><b>nBase</b></font></td>
<td align="center" bgcolor="#006666"><font size="2"
face="MS Sans Serif">基數(shù),加上序數(shù)就是函數(shù)地址數(shù)組的索引值了。</font></td>
</tr>
<tr>
<td align="center" bgcolor="#006666"><font size="2"
face="MS Sans Serif"><b>NumberOfFunctions</b></font></td>
<td align="center" bgcolor="#006666"><font size="2"
face="MS Sans Serif">模塊引出的函數(shù)/符號(hào)總數(shù)。</font></td>
</tr>
<tr>
<td align="center" bgcolor="#006666"><font size="2"
face="MS Sans Serif"><b>NumberOfNames</b></font></td>
<td align="center" bgcolor="#006666"><font size="2"
face="MS Sans Serif">通過(guò)名字引出的函數(shù)/符號(hào)數(shù)目。該值</font><font
color="#CC9900" size="2" face="MS Sans Serif"><b>不是</b></font><font size="2"
face="MS Sans Serif">模塊引出的函數(shù)/符號(hào)總數(shù)</font>,這是由上面的<font color="#FFFFCC" size="2"
face="MS Sans Serif"><b>NumberOfFunctions</b></font><font
size="2" face="MS Sans Serif">給出。本域可以為0,表示模塊可能僅僅通過(guò)序數(shù)引出。如果模塊根本不引出任何函數(shù)/符號(hào),那么數(shù)據(jù)目錄中引出表的RVA為0。</font></td>
</tr>
<tr>
<td align="center" bgcolor="#006666"><font size="2"
face="MS Sans Serif"><b>AddressOfFunctions</b></font></td>
<td align="center" bgcolor="#006666"><font size="2"
face="MS Sans Serif">模塊中有一個(gè)指向所有函數(shù)/符號(hào)的RVAs數(shù)組,本域就是指向該RVAs數(shù)組的RVA。簡(jiǎn)言之,模塊中所有函數(shù)的RVAs都保存在一個(gè)數(shù)組里,本域就指向這個(gè)數(shù)組的首地址。</font></td>
</tr>
<tr>
<td align="center" bgcolor="#006666"><font size="2"
face="MS Sans Serif"><b>AddressOfNames</b></font></td>
<td align="center" bgcolor="#006666"><font size="2"
face="MS Sans Serif">類似上個(gè)域,模塊中有一個(gè)指向所有函數(shù)名的RVAs數(shù)組,本域就是指向該RVAs數(shù)組的RVA。</font></td>
</tr>
<tr>
<td align="center" bgcolor="#006666"><font size="2"
face="MS Sans Serif"><b>AddressOfNameOrdinals</b></font></td>
<td align="center" bgcolor="#006666"><font size="2"
face="MS Sans Serif">RVA,指向包含上述 AddressOfNames數(shù)組中相關(guān)函數(shù)之序數(shù)的16位數(shù)組。</font></td>
</tr>
</table>
<p><font size="2" face="MS Sans Serif">上面也許無(wú)法讓您完全理解引出表,下面的簡(jiǎn)述將助您一臂之力。</font></p>
<p><font size="2" face="MS Sans Serif">引出表的設(shè)計(jì)是為了方便PE裝載器工作。首先,模塊必須保存所有引出函數(shù)的地址以供PE裝載器查詢。模塊將這些信息保存在</font><font color="#FFFFCC" size="2"
face="MS Sans Serif"><b>AddressOfFunctions</b></font><font
size="2" face="MS Sans Serif">域指向的數(shù)組中,而數(shù)組元素?cái)?shù)目存放在</font><font color="#FFFFCC" size="2"
face="MS Sans Serif"><b>NumberOfFunctions</b></font><font
size="2" face="MS Sans Serif">域中。 因此,如果模塊引出40個(gè)函數(shù),則</font><font
color="#FFFFCC" size="2" face="MS Sans Serif"><b>AddressOfFunctions</b></font><font size="2" face="MS Sans Serif">指向的數(shù)組必定有40個(gè)元素,而</font><font
color="#FFFFCC" size="2" face="MS Sans Serif"><b>NumberOfFunctions</b></font><font
size="2" face="MS Sans Serif">值為40。現(xiàn)在如果有一些函數(shù)是通過(guò)名字引出的,那么模塊必定也在文件中保留了這些信息。這些 名字的RVAs存放在一數(shù)組中以供PE裝載器查詢。該數(shù)組由</font><font
color="#FFFFCC" size="2" face="MS Sans Serif"><b>AddressOfNames</b></font><font
size="2" face="MS Sans Serif">指向,</font><font
color="#FFFFCC" size="2" face="MS Sans Serif"><b>NumberOfNames</b></font><font
size="2" face="MS Sans Serif">包含名字?jǐn)?shù)目。考慮一下PE裝載器的工作機(jī)制,它知道函數(shù)名,并想以此獲取這些函數(shù)的地址。至今為止,模塊已有兩個(gè)模塊:
名字?jǐn)?shù)組和地址數(shù)組,但兩者之間還沒(méi)有聯(lián)系的紐帶。因此我們還需要一些聯(lián)系函數(shù)名及其地址的東東。PE參考指出使用到地址數(shù)組的索引作為聯(lián)接,因此PE裝載器在名字?jǐn)?shù)組中找到匹配名字的同時(shí),它也獲取了</font><font color="#999900" size="2"
face="MS Sans Serif"><b> 指向地址表中對(duì)應(yīng)元素的索引</b></font><font
size="2" face="MS Sans Serif">。 而這些索引保存在由</font><font
color="#FFFFCC" size="2" face="MS Sans Serif"><b>AddressOfNameOrdinals</b></font><font
size="2" face="MS Sans Serif">域指向的另一個(gè)數(shù)組(最后一個(gè))中。由于該數(shù)組是起了聯(lián)系名字和地址的作用,所以其元素?cái)?shù)目必定和名字?jǐn)?shù)組相同,比如,每個(gè)名字有且僅有一個(gè)相關(guān)地址,反過(guò)來(lái)則不一定</font><font
size="2" face="MS Sans Serif">: 每個(gè)地址可以有好幾個(gè)名字來(lái)對(duì)應(yīng)。因此我們給同一個(gè)地址取"別名"。為了起到連接作用,名字?jǐn)?shù)組和索引數(shù)組必須并行地成對(duì)使用,譬如,索引數(shù)組的第一個(gè)元素必定含有第一個(gè)名字的索引,以此類推。
</font></p>
<table border="0" cellpadding="2">
<tr>
<th bgcolor="#006666"><font size="2" face="MS Sans Serif">AddressOfNames</font></th>
<th> </th>
<th bgcolor="#006666"><font size="2" face="MS Sans Serif">AddressOfNameOrdinals</font></th>
</tr>
<tr>
<td align="center"><font size="2" face="MS Sans Serif">|</font>
</td>
<td align="center"> </td>
<td align="center"><font size="2" face="MS Sans Serif">|</font>
</td>
</tr>
<tr>
<td align="center"><table border="1" cellpadding="2">
<tr>
<td align="center" bgcolor="#660066"><font
size="2" face="MS Sans Serif">RVA of Name 1</font></td>
</tr>
<tr>
<td align="center" bgcolor="#660066"><font
size="2" face="MS Sans Serif">RVA of Name 2</font></td>
</tr>
<tr>
<td align="center" bgcolor="#660066"><font
size="2" face="MS Sans Serif">RVA of Name 3</font></td>
</tr>
<tr>
<td align="center" bgcolor="#660066"><font
size="2" face="MS Sans Serif">RVA of Name 4</font></td>
</tr>
<tr>
<td align="center" bgcolor="#660066"><font
size="2" face="MS Sans Serif">...</font> </td>
</tr>
<tr>
<td align="center" bgcolor="#660066"><font
size="2" face="MS Sans Serif">RVA of Name N</font></td>
</tr>
</table>
</td>
<td align="center"><table border="0" cellpadding="2">
<tr>
<td align="center"><font size="2"
face="MS Sans Serif"><--></font></td>
</tr>
<tr>
<td align="center"><font size="2"
face="MS Sans Serif"><--></font></td>
</tr>
<tr>
<td align="center"><font size="2"
face="MS Sans Serif"><--></font></td>
</tr>
<tr>
<td align="center"><font size="2"
face="MS Sans Serif"><--></font></td>
</tr>
<tr>
<td align="center"><font size="2"
face="MS Sans Serif">...</font> </td>
</tr>
<tr>
<td align="center"><font size="2"
face="MS Sans Serif"><--></font></td>
</tr>
</table>
</td>
<td align="center"><table border="1" cellpadding="2">
<tr>
<td align="center" bgcolor="#003300"><font
size="2" face="MS Sans Serif">Index of Name 1</font></td>
</tr>
<tr>
<td align="center" bgcolor="#003300"><font
size="2" face="MS Sans Serif">Index of Name 2</font></td>
</tr>
<tr>
<td align="center" bgcolor="#003300"><font
size="2" face="MS Sans Serif">Index of Name 3</font></td>
</tr>
<tr>
<td align="center" bgcolor="#003300"><font
size="2" face="MS Sans Serif">Index of Name 4</font></td>
</tr>
<tr>
<td align="center" bgcolor="#003300"><font
size="2" face="MS Sans Serif">...</font> </td>
</tr>
<tr>
<td align="center" bgcolor="#003300"><font
size="2" face="MS Sans Serif">Index of Name N</font></td>
</tr>
</table>
</td>
</tr>
</table>
<p><font size="2" face="MS Sans Serif">下面舉一兩個(gè)例子說(shuō)明問(wèn)題。如果我們有了引出函數(shù)名并想以此獲取地址,可以這么做:</font></p>
<ol>
<li><font size="2" face="MS Sans Serif">定位到PE header。</font></li>
<li><font size="2" face="MS Sans Serif">從數(shù)據(jù)目錄讀取引出表的虛擬地址。</font></li>
<li><font size="2" face="MS Sans Serif">定位引出表獲取名字?jǐn)?shù)目(</font><font
color="#FFFFCC" size="2" face="MS Sans Serif"><b>NumberOfNames</b></font><font
size="2" face="MS Sans Serif">)。</font></li>
<li><font size="2" face="MS Sans Serif">并行遍歷</font><font color="#FFFFCC" size="2"
face="MS Sans Serif"><b>AddressOfNames</b></font><font
size="2" face="MS Sans Serif">和</font><font
color="#FFFFCC" size="2" face="MS Sans Serif"><b>AddressOfNameOrdinals</b></font><font size="2" face="MS Sans Serif">指向的數(shù)組匹配名字。如果在</font><font
color="#FFFFCC" size="2" face="MS Sans Serif"><b>AddressOfNames</b></font><font
size="2" face="MS Sans Serif"> 指向的數(shù)組中找到匹配名字,從</font><font
color="#FFFFCC" size="2" face="MS Sans Serif"><b>AddressOfNameOrdinals</b></font><font
size="2" face="MS Sans Serif"> 指向的數(shù)組中提取索引值。例如,若發(fā)現(xiàn)匹配名字的RVA存放在</font><font
color="#FFFFCC" size="2" face="MS Sans Serif"><b>AddressOfNames</b></font><font
size="2" face="MS Sans Serif"> 數(shù)組的第77個(gè)元素,那就提取</font><font
color="#FFFFCC" size="2" face="MS Sans Serif"><b>AddressOfNameOrdinals</b></font><font
size="2" face="MS Sans Serif">數(shù)組的第77個(gè)元素</font><font
size="2" face="MS Sans Serif">作為索引值。如果遍歷完</font><font color="#FFFFCC" size="2"
face="MS Sans Serif"><b>NumberOfNames</b></font><font
size="2" face="MS Sans Serif"> 個(gè)元素,說(shuō)明當(dāng)前模塊沒(méi)有所要的名字。</font></li>
<li><font size="2" face="MS Sans Serif">從</font><font color="#FFFFCC" size="2"
face="MS Sans Serif"><b>AddressOfNameOrdinals</b></font><font
size="2" face="MS Sans Serif"> 數(shù)組提取的數(shù)值作為</font><font color="#FFFFCC" size="2"
face="MS Sans Serif"><b>AddressOfFunctions</b></font><font
size="2" face="MS Sans Serif"> 數(shù)組的索引。也就是說(shuō),如果值是5,就必須讀取</font><font color="#FFFFCC" size="2"
face="MS Sans Serif"><b>AddressOfFunctions</b></font><font
size="2" face="MS Sans Serif"> 數(shù)組的第5個(gè)元素,此值就是所要函數(shù)的RVA。</font></li>
</ol>
<p><font size="2" face="MS Sans Serif">現(xiàn)在我們?cè)诎炎⒁饬D(zhuǎn)向</font><font color="#FFFFCC"
size="2" face="MS Sans Serif"><b>IMAGE_EXPORT_DIRECTORY</b></font><font
size="2" face="MS Sans Serif"> 結(jié)構(gòu)的</font><font color="#FFFFCC" size="2"
face="MS Sans Serif"><b>nBase</b>成員。您已經(jīng)知道</font><font color="#FFFFCC" size="2" face="MS Sans Serif"><b>AddressOfFunctions</b></font><font
size="2" face="MS Sans Serif"> 數(shù)組包含了模塊中所有引出符號(hào)的地址。當(dāng)PE裝載器索引該數(shù)組查詢函數(shù)地址時(shí),讓我們?cè)O(shè)想這樣一種情況,如果程序員在.def文件中設(shè)定起始序數(shù)號(hào)為200,這意味著</font><font
color="#FFFFCC" size="2" face="MS Sans Serif"><b>AddressOfFunctions</b></font><font
size="2" face="MS Sans Serif"> 數(shù)組至少有200個(gè)元素,甚至這前面200個(gè)元素并沒(méi)使用,但它們必須存在,因?yàn)镻E裝載器這樣才能索引到正確的地址。這種方法很不好,所以又設(shè)計(jì)了</font><font color="#FFFFCC" size="2"
face="MS Sans Serif"><b>nBase</b></font><font size="2"
face="MS Sans Serif"> 域解決這個(gè)問(wèn)題。如果程序員指定起始序數(shù)號(hào)為200,</font><font
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -