久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

PE結(jié)構(gòu)講解2

 hjd7578 2019-04-06

本文為轉(zhuǎn)載文章,整理自小甲魚老師講的PE結(jié)構(gòu)課程;


一,、導(dǎo)入表的結(jié)構(gòu)

在 PE文件頭的 IMAGE_OPTIONAL_HEADER32 結(jié)構(gòu)中的 DataDirectory(數(shù)據(jù)目錄表) 的第二個成員就是指向輸入表(導(dǎo)入表)的,。而輸入表是以一個 IMAGE_IMPORT_DESCRIPTOR(簡稱IID) 的數(shù)組開始。每個被 PE文件鏈接進(jìn)來的 DLL文件都分別對應(yīng)一個 IID數(shù)組結(jié)構(gòu),。在這個 IID數(shù)組中,,并沒有指出有多少個項(xiàng)(就是沒有明確指明有多少個鏈接文件),但它最后是以一個全為NULL(0) 的 IID 作為結(jié)束的標(biāo)志,。


IMAGE_IMPORT_DESCRIPTOR 結(jié)構(gòu)定義如下:

IMAGE_IMPORT_DESCRIPTOR STRUCT

union

Characteristics DWORD ?

OriginalFirstThunk DWORD ?

ends

TimeDateStamp DWORD ?

ForwarderChain DWORD ?

Name DWORD ?

FirstThunk DWORD ?

IMAGE_IMPORT_DESCRIPTOR ENDS


成員介紹:

OriginalFirstThunk

它指向first thunk,,IMAGE_THUNK_DATA,該 thunk 擁有 Hint 和 Function name 的地址,。


TimeDateStamp

該字段可以忽略,。如果那里有綁定的話它包含時間/數(shù)據(jù)戳(time/data stamp)。如果它是0,,就沒有綁定在被導(dǎo)入的DLL中發(fā)生,。在最近,它被設(shè)置為0xFFFFFFFF以表示綁定發(fā)生,。

ForwarderChain

一般情況下我們也可以忽略該字段,。在老版的綁定中,它引用API的第一個forwarder chain(傳遞器鏈表),。它可被設(shè)置為0xFFFFFFFF以代表沒有forwarder,。


Name

它表示DLL 名稱的相對虛地址(譯注:相對一個用null作為結(jié)束符的ASCII字符串的一個RVA,該字符串是該導(dǎo)入DLL文件的名稱,,如:KERNEL32.DLL),。


FirstThunk

它包含由IMAGE_THUNK_DATA定義的 first thunk數(shù)組的虛地址,PE裝載器(loader)通過用函數(shù)虛地址初始化thunk,。在Orignal First Thunk缺席下,,它指向first thunk:Hints和The Function names的thunks。

0fe20209eea669cfac0f6d43538b2dd3bea8023dThese two import tables illustrate the different between import table with and without the original first thunk.
4229e499124c999c6be89764e778995fd7f611e0eb13285683dbd7b25cab91d6bd1f0f85ebc7510b


我們看到:OriginalFirstThunk 和 FirstThunk 他們都指向兩個類型為IMAGE_THUNK_DATA 的數(shù)組,,它是一個指針大小的聯(lián)合(union)類型,。每一個IMAGE_THUNK_DATA 結(jié)構(gòu)定義一個導(dǎo)入函數(shù)信息(即指向結(jié)構(gòu)為IMAGE_IMPORT_BY_NAME 的家伙,這家伙稍后再議),,然后數(shù)組最后以一個內(nèi)容為0 的 IMAGE_THUNK_DATA 結(jié)構(gòu)作為結(jié)束標(biāo)志,。


我們得到 IMAGE_THUNK_DATA 結(jié)構(gòu)的定義如下:

IMAGE_THUNK_DATA STRUC

union u1

ForwarderString DWORD ? ; 指向一個轉(zhuǎn)向者字符串的RVA

Function DWORD ? ; 被輸入的函數(shù)的內(nèi)存地址

Ordinal DWORD ? ; 被輸入的API 的序數(shù)值

AddressOfData DWORD ? ; 指向 IMAGE_IMPORT_BY_NAME

ends

IMAGE_THUNK_DATA ENDS

我們可以看出由于是union結(jié)構(gòu),所以IMAGE_THUNK_DATA 事實(shí)上是一個雙字大小,。該結(jié)構(gòu)在不同時候賦予不同的意義,。


那我們怎么來區(qū)分何時是何意義呢?

規(guī)定如下:

當(dāng) IMAGE_THUNK_DATA 值的最高位為 1時,,表示函數(shù)以序號方式輸入,,這時候低 31位被看作一個函數(shù)序號,。

當(dāng) IMAGE_THUNK_DATA 值的最高位為 0時,表示函數(shù)以字符串類型的函數(shù)名方式輸入,,這時雙字的值是一個 RVA,,指向一個 IMAGE_IMPORT_BY_NAME 結(jié)構(gòu)。


好,,那接著我們討論下指向的這個 IMAGE_IMPORT_BY_NAME 結(jié)構(gòu),。IMAGE_IMPORT_BY_NAME 結(jié)構(gòu)僅僅只有一個字型數(shù)據(jù)的大小,存有一個輸入函數(shù)的相關(guān)信息結(jié)構(gòu),。其結(jié)構(gòu)如下:

IMAGE_IMPORT_BY_NAME STRUCT

Hint WORD ?

Name BYTE ?

IMAGE_IMPORT_BY_NAME ENDS

結(jié)構(gòu)中的 Hint 字段也表示函數(shù)的序號,,不過這個字段是可選的,有些編譯器總是將它設(shè)置為 0,,Name 字段定義了導(dǎo)入函數(shù)的名稱字符串,,這是一個以 0 為結(jié)尾的字符串。


輸入地址表(IAT)


為什么由兩個并行的指針數(shù)組同時指向 IMAGE_IMPORT_BY_NAME 結(jié)構(gòu)呢,?第一個數(shù)組(由 OriginalFirstThunk 所指向)是單獨(dú)的一項(xiàng),,而且不能被改寫,我們前邊稱為 INT,。第二個數(shù)組(由 FirstThunk 所指向)事實(shí)上是由 PE 裝載器重寫的,。


PE 裝載器首先搜索 OriginalFirstThunk ,找到之后加載程序迭代搜索數(shù)組中的每個指針,,找到每個 IMAGE_IMPORT_BY_NAME 結(jié)構(gòu)所指向的輸入函數(shù)的地址,然后加載器用函數(shù)真正入口地址來替代由 FirstThunk 數(shù)組中的一個入口,,因此我們稱為輸入地址表(IAT),。所以,當(dāng)我們的 PE 文件裝載內(nèi)存后準(zhǔn)備執(zhí)行時,,剛剛的圖就會轉(zhuǎn)化為下圖:

0906f5c1b88171512973554af974302426f156e32e1daac48addbf0fa5aa6651266a402534985a6a

此時,,輸入表中其他部分就不重要了,程序依靠 IAT 提供的函數(shù)地址就可正常運(yùn)行,。




二,、導(dǎo)出表的結(jié)構(gòu)

導(dǎo)出表就是記載著動態(tài)鏈接庫的一些導(dǎo)出信息。通過導(dǎo)出表,,DLL 文件可以向系統(tǒng)提供導(dǎo)出函數(shù)的名稱,、序號和入口地址等信息,以便Windows 加載器通過這些信息來完成動態(tài)連接的整個過程,。


友情提示:擴(kuò)展名為.exe 的PE 文件中一般不存在導(dǎo)出表,,而大部分的.dll 文件中都包含導(dǎo)出表。但注意,,這并不是絕對的,。例如純粹用作資源的.dll 文件就不需要導(dǎo)出函數(shù)啦,,另外有些特殊功能的.exe 文件也會存在導(dǎo)出函數(shù)。所以,,世事無絕對……好了,,我們接下來就對導(dǎo)出表的結(jié)構(gòu)進(jìn)行分析。



導(dǎo)出表(Export Table)中的主要成分是一個表格,,內(nèi)含函數(shù)名稱,、輸出序數(shù)等。序數(shù)是指定DLL 中某個函數(shù)的16位數(shù)字,,在所指向的DLL 文件中是獨(dú)一無二的,。在此我們不提倡僅僅通過序數(shù)來索引函數(shù)的方法,這樣會給DLL 文件的維護(hù)帶來問題,。例如當(dāng)DLL 文件一旦升級或修改就可能導(dǎo)致調(diào)用改DLL 的程序無法加載到需要的函數(shù),。


數(shù)據(jù)目錄表的第一個成員指向?qū)С霰恚且粋€IMAGE_EXPORT_DIRECTORY(以后簡稱IED)結(jié)構(gòu),,IED 結(jié)構(gòu)的定義如下:


IMAGE_EXPORT_DIRECTORY STRUCT

CharacteristicsDWORD ?; 未使用,,總是定義為0

TimeDateStamp DWORD ? ; 文件生成時間

MajorVersion WORD ? ; 未使用,總是定義為0

MinorVersion WORD ? ; 未使用,,總是定義為0

Name DWORD? ; 模塊的真實(shí)名稱

Base DWORD? ; 基數(shù),,加上序數(shù)就是函數(shù)地址數(shù)組的索引值

NumberOfFunctionsDWORD ?; 導(dǎo)出函數(shù)的總數(shù)

NumberOfNames DWORD ? ; 以名稱方式導(dǎo)出的函數(shù)的總數(shù)

AddressOfFunctionsDWORD ?; 指向輸出函數(shù)地址的RVA

AddressOfNamesDWORD ?; 指向輸出函數(shù)名字的RVA

AddressOfNameOrdinalsDWORD ?; 指向輸出函數(shù)序號的RVA

IMAGE_EXPORT_DIRECTORY ENDS




這個結(jié)構(gòu)中的一些字段并沒有被使用,有意義的字段說明如下,。


Name:一個RVA 值,,指向一個定義了模塊名稱的字符串。如即使Kernel32.dll 文件被改名為"Ker.dll",,仍然可以從這個字符串中的值得知其在編譯時的文件名是"Kernel32.dll",。

NumberOfFunctions:文件中包含的導(dǎo)出函數(shù)的總數(shù)。

NumberOfNames:被定義函數(shù)名稱的導(dǎo)出函數(shù)的總數(shù),,顯然只有這個數(shù)量的函數(shù)既可以用函數(shù)名方式導(dǎo)出,。也可以用序號方式導(dǎo)出,剩下 的NumberOfFunctions 減去NumberOfNames 數(shù)量的函數(shù)只能用序號方式導(dǎo)出,。該字段的值只會小于或者等于 NumberOfFunctions 字段的值,,如果這個值是0,表示所有的函數(shù)都是以序號方式導(dǎo)出的,。


AddressOfFunctions:一個RVA 值,,指向包含全部導(dǎo)出函數(shù)入口地址的雙字?jǐn)?shù)組。數(shù)組中的每一項(xiàng)是一個RVA 值,,數(shù)組的項(xiàng)數(shù)等于NumberOfFunctions 字段的值,。

Base:導(dǎo)出函數(shù)序號的起始值,將AddressOfFunctions 字段指向的入口地址表的索引號加上這個起始值就是對應(yīng)函數(shù)的導(dǎo)出 序號,。假如Base 字段的值為x,,那么入口地址表指定的第1個導(dǎo)出函數(shù)的序號就是x,;第2個導(dǎo)出函數(shù)的序號就是x+1??傊?,一個導(dǎo)出函數(shù)的導(dǎo)出序號等 于Base 字段的值加上其在入口地址表中的位置索引值。


AddressOfNames 和 AddressOfNameOrdinals:均為RVA 值,。前者指向函數(shù)名字符串地址表,。這個地址表是一個雙字?jǐn)?shù)組,數(shù)組中的每一項(xiàng)指向一個函數(shù)名稱字符串的RVA,。數(shù)組的項(xiàng)數(shù)等于NumberOfNames 字段的值,,所有有名稱的導(dǎo)出函數(shù)的名稱字符串都定義在這個表中;后者指向另一個word 類型的數(shù)組(注意不是雙字?jǐn)?shù)組),。數(shù)組項(xiàng)目與文件名地址表中的項(xiàng)目一一對應(yīng),,項(xiàng)目值代表函數(shù)入口地址表的索引,這樣函 數(shù)名稱與函數(shù)入口地址關(guān)聯(lián)起來,。(舉個例子說,,加入函數(shù)名稱字符串地址表的第n 項(xiàng)指向一個字符串“MyFunction”,那么可以去查找 AddressOfNameOrdinals 指向的數(shù)組的第n 項(xiàng),,假如第n 項(xiàng)中存放的值是x,,則表示AddressOfFunctions 字段描述的地址表中的第x 項(xiàng)函數(shù)入口地址對應(yīng)的名稱就是“MyFunction”復(fù)雜吧? 沒事,接著看你就懂了,,別放棄哦~)


整個流程跟其他PE 結(jié)構(gòu)一樣說起來復(fù)雜,,但看圖說話倒是挺容易的。所以小甲魚還是本著實(shí)事求是的精神&……%¥#踏踏實(shí)實(shí)畫圖讓大家好理解一點(diǎn)吧,,來,,請上圖:


bdd448d9003d073ea577fb06554bbf26be69113a

1. 從序號查找函數(shù)入口地址

下邊小甲魚帶大家來模擬一下Windows 裝載器查找導(dǎo)出函數(shù)入口地址的整個過程。如果已知函數(shù)的導(dǎo)出序號,,如何得到函數(shù)的入口地址呢 ?


Windows 裝載器的工作步驟如下:

定位到PE 文件頭

從PE 文件頭中的 IMAGE_OPTIONAL_HEADER32 結(jié)構(gòu)中取出數(shù)據(jù)目錄表,,并從第一個數(shù)據(jù)目錄中得到導(dǎo)出表的RVA

從導(dǎo)出表的 Base 字段得到起始序號

將需要查找的導(dǎo)出序號減去起始序號,,得到函數(shù)在入口地址表中的索引

檢測索引值是否大于導(dǎo)出表的 NumberOfFunctions 字段的值,,如果大于后者的話,說明輸入的序號是無效的

用這個索引值在 AddressOfFunctions 字段指向的導(dǎo)出函數(shù)入口地址表中取出相應(yīng)的項(xiàng)目,,這就是函數(shù)入口地址的RVA 值,,當(dāng)函數(shù)被裝入內(nèi)存的時候,這個RVA 值加上模塊實(shí)際裝入的基地址,,就得到了函數(shù)真正的入口地址


2. 從函數(shù)名稱查找入口地址


如果已知函數(shù)的名稱,,如何得到函數(shù)的入口地址呢?與使用序號來獲取入口地址相比,,這個過程要相對復(fù)雜一點(diǎn),!


Windows 裝載器的工作步驟如下:

最初的步驟是一樣的,,那就是首先得到導(dǎo)出表的地址

從導(dǎo)出表的 NumberOfNames 字段得到已命名函數(shù)的總數(shù),并以這個數(shù)字作為循環(huán)的次數(shù)來構(gòu)造一個循環(huán)

從 AddressOfNames 字段指向得到的函數(shù)名稱地址表的第一項(xiàng)開始,,在循環(huán)中將每一項(xiàng)定義的函數(shù)名與要查找的函數(shù)名相比較,,如果沒有任何一個函數(shù)名是符合的,表示文件中沒有指定名稱的函數(shù)

如果某一項(xiàng)定義的函數(shù)名與要查找的函數(shù)名符合,,那么記下這個函數(shù)名在字符串地址表中的索引值,,然后在 AddressOfNamesOrdinals 指向的數(shù)組中以同樣的索引值取出數(shù)組項(xiàng)的值,我們這里假設(shè)這個值是x

最后,,以 x 值作為索引值,,在 AddressOfFunctions 字段指向的函數(shù)入口地址表中獲取的 RVA 就是函數(shù)的入口地址


一幫情況下病毒程序就是通過函數(shù)名稱查找入口地址的,因?yàn)椴《境绦蜃鳛橐欢晤~外的代碼被附加到可執(zhí)行文件中的,,如果病毒代碼中用到某些 API 的話,,這些 API 的地址不可能在宿主文件的導(dǎo)出表中為病毒代碼準(zhǔn)備好。因此只能通過在內(nèi)存中動態(tài)查找的方法來實(shí)現(xiàn)獲取API 的地址,。關(guān)于病毒代碼具體的實(shí)現(xiàn)分析,,小甲魚在今后將跟大家共同研究討論這個話題~

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn),。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多