PE教程6:
Import Table(引入表)本課我們將學習引入表。先警告一下,,對于不熟悉引入表的讀者來說,,這是一堂又長又難的課,所以需要多讀幾遍,,最好再打開調試器來好好分析相關結構,。各位,努力??! 下載范例。 理論:首先,,您得了解什么是引入函數,。一個引入函數是被某模塊調用的但又不在調用者模塊中的函數,因而命名為"import(引入)",。引入函數實際位于一個或者更多的DLL里,。調用者模塊里只保留一些函數信息,包括函數名及其駐留的DLL名?,F在,,我們怎樣才能找到PE文件中保存的信息呢?
轉到 data
directory 尋求答案吧。再回顧一把,,下面就是 PE
header: IMAGE_NT_HEADERS
STRUCT Signature dd ? FileHeader IMAGE_FILE_HEADER
<> OptionalHeader IMAGE_OPTIONAL_HEADER <>
IMAGE_NT_HEADERS ENDS optional
header 最后一個成員就是 data
directory(數據目錄): IMAGE_OPTIONAL_HEADER32
STRUCT .... LoaderFlags dd ?
NumberOfRvaAndSizes dd ? DataDirectory IMAGE_DATA_DIRECTORY
16 dup(<>) IMAGE_OPTIONAL_HEADER32 ENDS data
directory 是一個 IMAGE_DATA_DIRECTORY
結構數組,,共有16個成員,。如果您還記得節(jié)表可以看作是PE文件各節(jié)的根目錄的話,,也可以認為
data directory 是存儲在這些節(jié)里的邏輯元素的根目錄。明確點,,data
directory 包含了PE文件中各重要數據結構的位置和尺寸信息,。
每個成員包含了一個重要數據結構的信息,。
Member | Info inside | 0 | Export symbols | 1 | Import symbols | 2 | Resources | 3 | Exception | 4 | Security | 5 | Base relocation | 6 | Debug | 7 | Copyright string | 8 | Unknown | 9 | Thread local storage (TLS) |
10 | Load configuration | 11 | Bound Import | 12 | Import Address Table |
13 | Delay Import | 14 | COM descriptor | 上面那些金色顯示的是我熟悉的。了解
data directory 包含域后,我們可以仔細研究它們了,。data
directory 的每個成員都是 IMAGE_DATA_DIRECTORY
結構類型的,,其定義如下所示: IMAGE_DATA_DIRECTORY
STRUCT VirtualAddress dd ? isize dd ? IMAGE_DATA_DIRECTORY
ENDS VirtualAddress
實際上是數據結構的相對虛擬地址(RVA)。比如,,如果該結構是關于import
symbols的,,該域就包含指向IMAGE_IMPORT_DESCRIPTOR
數組的RVA。
isize 含有VirtualAddress所指向數據結構的字節(jié)數,。 下面就是如何找尋PE文件中重要數據結構的一般方法: - 從 DOS
header 定位到 PE
header
- 從 optional
header 讀取 data
directory 的地址。
- IMAGE_DATA_DIRECTORY
結構尺寸乘上找尋結構的索引號:
比如您要找尋import
symbols的位置信息,,必須用IMAGE_DATA_DIRECTORY
結構尺寸(8
bytes)乘上1(import
symbols在data
directory中的索引號),。
- 將上面的結果加上data
directory地址,我們就得到包含所查詢數據結構信息的 IMAGE_DATA_DIRECTORY
結構項,。
現在我們開始真正討論引入表了,。data
directory數組第二項的VirtualAddress包含引入表地址。引入表實際上是一個
IMAGE_IMPORT_DESCRIPTOR
結構數組,。每個結構包含PE文件引入函數的一個相關DLL的信息,。比如,如果該PE文件從10個不同的DLL中引入函數,,那么這個數組就有10個成員,。該數組以一個全0的成員結尾。下面詳細研究結構組成: IMAGE_IMPORT_DESCRIPTOR
STRUCT union Characteristics dd ?
OriginalFirstThunk dd ? ends TimeDateStamp
dd ? ForwarderChain dd ? Name1 dd ? FirstThunk
dd ? IMAGE_IMPORT_DESCRIPTOR ENDS 結構第一項是一個union子結構,。
事實上,,這個union子結構只是給
OriginalFirstThunk
增添了個別名,您也可以稱其為"Characteristics",。
該成員項含有指向一個 IMAGE_THUNK_DATA
結構數組的RVA,。
什么是 IMAGE_THUNK_DATA?
這是一個dword類型的集合。通常我們將其解釋為指向一個
IMAGE_IMPORT_BY_NAME
結構的指針,。注意 IMAGE_THUNK_DATA
包含了指向一個 IMAGE_IMPORT_BY_NAME
結構的指針:
而不是結構本身,。 請看這里:
現有幾個 IMAGE_IMPORT_BY_NAME
結構,我們收集起這些結構的RVA
(IMAGE_THUNK_DATAs)組成一個數組,,并以0結尾,,然后再將數組的RVA放入
OriginalFirstThunk。
此 IMAGE_IMPORT_BY_NAME
結構存有一個引入函數的相關信息,。再來研究 IMAGE_IMPORT_BY_NAME
結構到底是什么樣子的呢: IMAGE_IMPORT_BY_NAME
STRUCT Hint dw ? Name1 db ? IMAGE_IMPORT_BY_NAME
ENDS Hint
指示本函數在其所駐留DLL的引出表中的索引號,。該域被PE裝載器用來在DLL的引出表里快速查詢函數。該值不是必須的,,一些連接器將此值設為0,。
Name1 含有引入函數的函數名。函數名是一個ASCIIZ字符串。注意這里雖然將Name1的大小定義成字節(jié),,其實它是可變尺寸域,,只不過我們沒有更好方法來表示結構中的可變尺寸域。The
structure is provided so that you can refer to the data structure with descriptive
names. TimeDateStamp
和 ForwarderChain
可是高級東東:
讓我們精通其他成員后再來討論它們吧,。 Name1
含有指向DLL名字的RVA,,即指向DLL名字的指針,也是一個ASCIIZ字符串,。 FirstThunk
與 OriginalFirstThunk
非常相似,,它也包含指向一個 IMAGE_THUNK_DATA
結構數組的RVA(當然這是另外一個IMAGE_THUNK_DATA
結構數組)。
好了,,如果您還在犯糊涂,,就朝這邊看過來:
現在有幾個 IMAGE_IMPORT_BY_NAME
結構,同時您又創(chuàng)建了兩個結構數組,,并同樣寸入指向那些 IMAGE_IMPORT_BY_NAME
結構的RVAs,,這樣兩個數組就包含相同數值了(可謂相當精確的復制啊)。
最后您決定將第一個數組的RVA賦給
OriginalFirstThunk,,第二個數組的RVA賦給
FirstThunk,,這樣一切都很清楚了。
OriginalFirstThunk | | IMAGE_IMPORT_BY_NAME | | FirstThunk |
| | | | | | |
IMAGE_THUNK_DATA
| IMAGE_THUNK_DATA
| IMAGE_THUNK_DATA
| IMAGE_THUNK_DATA
| ...
| IMAGE_THUNK_DATA
| | ---> | ---> | ---> | ---> | ---> | ---> | |
Function
1 | Function
2 | Function
3 | Function
4 | ...
| Function
n | | <--- | <--- | <--- | <--- | <--- | <--- | |
IMAGE_THUNK_DATA
| IMAGE_THUNK_DATA
| IMAGE_THUNK_DATA
| IMAGE_THUNK_DATA
| ...
| IMAGE_THUNK_DATA
| | 現在您應該明白我的意思,。不要被IMAGE_THUNK_DATA這個名字弄糊涂:
它僅是指向 IMAGE_IMPORT_BY_NAME
結構的RVA,。
如果將 IMAGE_THUNK_DATA
字眼想象成RVA,就更容易明白了,。OriginalFirstThunk
和 FirstThunk
所指向的這兩個數組大小取決于PE文件從DLL中引入函數的數目,。比如,如果PE文件從kernel32.dll中引入10個函數,,那么IMAGE_IMPORT_DESCRIPTOR
結構的 Name1域包含指向字符串"kernel32.dll"的RVA,,同時每個IMAGE_THUNK_DATA
數組有10個元素。 下一個問題是:
為什么我們需要兩個完全相同的數組?
為了回答該問題,,我們需要了解當PE文件被裝載到內存時,,PE裝載器將查找IMAGE_THUNK_DATA
和 IMAGE_IMPORT_BY_NAME
這些結構數組,以此決定引入函數的地址,。然后用引入函數真實地址來替代由FirstThunk指向的
IMAGE_THUNK_DATA
數組里的元素值,。因此當PE文件準備執(zhí)行時,上圖已轉換成:
OriginalFirstThunk | | IMAGE_IMPORT_BY_NAME | | FirstThunk |
| | | | | |
| IMAGE_THUNK_DATA
| IMAGE_THUNK_DATA
| IMAGE_THUNK_DATA
| IMAGE_THUNK_DATA
| ...
| IMAGE_THUNK_DATA
| |
---> | ---> | ---> | ---> | ---> | ---> | |
Function
1 | Function
2 | Function
3 | Function
4 | ...
| Function
n | | | Address
of Function 1 | Address
of Function 2 | Address
of Function 3 | Address
of Function 4 | ...
| Address
of Function n | | 由OriginalFirstThunk
指向的RVA數組始終不會改變,,所以若還反過頭來查找引入函數名,,PE裝載器還能找尋到。
當然再簡單的事物都有其復雜的一面,。有些情況下一些函數僅由序數引出,,也就是說您不能用函數名來調用它們:
您只能用它們的位置來調用,。此時,調用者模塊中就不存在該函數的 IMAGE_IMPORT_BY_NAME
結構,。不同的,,對應該函數的 IMAGE_THUNK_DATA
值的低位字指示函數序數,而最高二進位 (MSB)設為1,。例如,,如果一個函數只由序數引出且其序數是1234h,那么對應該函數的
IMAGE_THUNK_DATA
值是80001234h,。Microsoft提供了一個方便的常量來測試dword值的MSB位,,就是
IMAGE_ORDINAL_FLAG32,其值為80000000h,。
假設我們要列出某個PE文件的所有引入函數,,可以照著下面步驟走: - 校驗文件是否是有效的PE。
- 從
DOS header 定位到
PE header,。
- 獲取位于
OptionalHeader 數據目錄地址。
- 轉至數據目錄的第二個成員提取其VirtualAddress值,。
- 利用上值定位第一個
IMAGE_IMPORT_DESCRIPTOR
結構,。
- 檢查
OriginalFirstThunk值。若不為0,,順著
OriginalFirstThunk
里的RVA值轉入那個RVA數組,。若
OriginalFirstThunk
為0,就改用FirstThunk值,。有些連接器生成PE文件時會置OriginalFirstThunk值為0,,這應該算是個bug。不過為了安全起見,,我們還是檢查
OriginalFirstThunk值先,。
- 對于每個數組元素,我們比對元素值是否等于IMAGE_ORDINAL_FLAG32,。如果該元素值的最高二進位為1,,
那么函數是由序數引入的,可以從該值的低字節(jié)提取序數,。
- 如果元素值的最高二進位為0,,就可將該值作為RVA轉入
IMAGE_IMPORT_BY_NAME
數組,跳過 Hint 就是函數名字了,。
- 再跳至下一個數組元素提取函數名一直到數組底部(它以null結尾)?,F在我們已遍歷完一個DLL的引入函數,接下去處理下一個DLL,。
- 即跳轉到下一個
IMAGE_IMPORT_DESCRIPTOR
并處理之,,如此這般循環(huán)直到數組見底。(IMAGE_IMPORT_DESCRIPTOR
數組以一個全0域元素結尾)。
示例:本例程打開一PE文件,,將所有引入函數名讀入一編輯控件,,同時顯示
IMAGE_IMPORT_DESCRIPTOR
結構各域值。 .386
.model flat,stdcall option casemap:none include \masm32\include\windows.inc
include \masm32\include\kernel32.inc include \masm32\include\comdlg32.inc
include \masm32\include\user32.inc includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib includelib \masm32\lib\comdlg32.lib
IDD_MAINDLG equ 101 IDC_EDIT equ 1000 IDM_OPEN equ 40001
IDM_EXIT equ 40003 DlgProc proto :DWORD,:DWORD,:DWORD,:DWORD ShowImportFunctions
proto :DWORD ShowTheFunctions proto :DWORD,:DWORD AppendText proto :DWORD,:DWORD
SEH struct PrevLink dd ? ; the address of the previous seh structure
CurrentHandler dd ? ; the address of the new exception handler SafeOffset
dd ? ; The offset where it's safe to continue execution PrevEsp dd ? ; the
old value in esp PrevEbp dd ? ; The old value in ebp SEH ends
.data AppName db "PE tutorial no.6",0 ofn OPENFILENAME <>
FilterString db "Executable Files (*.exe, *.dll)",0,"*.exe;*.dll",0
db "All Files",0,"*.*",0,0 FileOpenError db "Cannot
open the file for reading",0 FileOpenMappingError db "Cannot open
the file for memory mapping",0 FileMappingError db "Cannot map
the file into memory",0 NotValidPE db "This file is not a valid
PE",0 CRLF db 0Dh,0Ah,0 ImportDescriptor db 0Dh,0Ah,"================[
IMAGE_IMPORT_DESCRIPTOR ]=============",0 IDTemplate db "OriginalFirstThunk
= %lX",0Dh,0Ah
db "TimeDateStamp = %lX",0Dh,0Ah
db "ForwarderChain = %lX",0Dh,0Ah
db "Name = %s",0Dh,0Ah
db "FirstThunk = %lX",0 NameHeader db 0Dh,0Ah,"Hint Function",0Dh,0Ah
db "-----------------------------------------",0
NameTemplate db "%u %s",0 OrdinalTemplate db "%u (ord.)",0
.data? buffer db 512 dup(?) hFile dd ? hMapping dd ?
pMapping dd ? ValidPE dd ? .code start: invoke GetModuleHandle,NULL
invoke DialogBoxParam, eax, IDD_MAINDLG,NULL,addr DlgProc, 0 invoke
ExitProcess, 0 DlgProc proc hDlg:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
.if uMsg==WM_INITDIALOG invoke SendDlgItemMessage,hDlg,IDC_EDIT,EM_SETLIMITTEXT,0,0
.elseif uMsg==WM_CLOSE invoke EndDialog,hDlg,0 .elseif uMsg==WM_COMMAND
.if lParam==0 mov eax,wParam
.if ax==IDM_OPEN invoke ShowImportFunctions,hDlg
.else ; IDM_EXIT invoke
SendMessage,hDlg,WM_CLOSE,0,0 .endif .endif
.else mov eax,FALSE ret .endif mov
eax,TRUE ret DlgProc endp SEHHandler proc uses edx pExcept:DWORD,
pFrame:DWORD, pContext:DWORD, pDispatch:DWORD mov edx,pFrame
assume edx:ptr SEH mov eax,pContext assume
eax:ptr CONTEXT push [edx].SafeOffset pop [eax].regEip
push [edx].PrevEsp pop [eax].regEsp push
[edx].PrevEbp pop [eax].regEbp mov ValidPE,
FALSE mov eax,ExceptionContinueExecution ret
SEHHandler endp ShowImportFunctions proc uses edi hDlg:DWORD
LOCAL seh:SEH mov ofn.lStructSize,SIZEOF
ofn mov ofn.lpstrFilter, OFFSET FilterString mov ofn.lpstrFile, OFFSET
buffer mov ofn.nMaxFile,512 mov ofn.Flags, OFN_FILEMUSTEXIST
or OFN_PATHMUSTEXIST or OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY
invoke GetOpenFileName, ADDR ofn .if eax==TRUE
invoke CreateFile, addr buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL .if eax!=INVALID_HANDLE_VALUE
mov hFile, eax
invoke CreateFileMapping, hFile, NULL, PAGE_READONLY,0,0,0
.if eax!=NULL mov hMapping, eax
invoke MapViewOfFile,hMapping,FILE_MAP_READ,0,0,0
.if eax!=NULL
mov pMapping,eax assume
fs:nothing push fs:[0]
pop seh.PrevLink
mov seh.CurrentHandler,offset
SEHHandler mov seh.SafeOffset,offset
FinalExit lea eax,seh
mov fs:[0], eax
mov seh.PrevEsp,esp
mov seh.PrevEbp,ebp
mov edi, pMapping
assume edi:ptr IMAGE_DOS_HEADER
.if [edi].e_magic==IMAGE_DOS_SIGNATURE
add edi,
[edi].e_lfanew
assume edi:ptr IMAGE_NT_HEADERS
.if [edi].Signature==IMAGE_NT_SIGNATURE mov
ValidPE, TRUE .else
mov ValidPE, FALSE
.endif .else
mov ValidPE,FALSE
.endif FinalExit:
push seh.PrevLink
pop fs:[0]
.if ValidPE==TRUE
invoke ShowTheFunctions, hDlg, edi
.else
invoke MessageBox,0, addr NotValidPE, addr AppName, MB_OK+MB_ICONERROR .endif
invoke UnmapViewOfFile,
pMapping .else
invoke MessageBox, 0, addr FileMappingError, addr AppName, MB_OK+MB_ICONERROR
.endif invoke
CloseHandle,hMapping .else
invoke MessageBox, 0, addr FileOpenMappingError, addr AppName, MB_OK+MB_ICONERROR
.endif invoke CloseHandle, hFile
.else invoke MessageBox, 0, addr FileOpenError,
addr AppName, MB_OK+MB_ICONERROR .endif .endif
ret ShowImportFunctions endp AppendText proc hDlg:DWORD,pText:DWORD
invoke SendDlgItemMessage,hDlg,IDC_EDIT,EM_REPLACESEL,0,pText
invoke SendDlgItemMessage,hDlg,IDC_EDIT,EM_REPLACESEL,0,addr
CRLF invoke SendDlgItemMessage,hDlg,IDC_EDIT,EM_SETSEL,-1,0
ret AppendText endp RVAToOffset PROC uses
edi esi edx ecx pFileMap:DWORD,RVA:DWORD mov esi,pFileMap
assume esi:ptr IMAGE_DOS_HEADER add esi,[esi].e_lfanew
assume esi:ptr IMAGE_NT_HEADERS mov edi,RVA
; edi == RVA mov edx,esi add edx,sizeof IMAGE_NT_HEADERS
mov cx,[esi].FileHeader.NumberOfSections movzx
ecx,cx assume edx:ptr IMAGE_SECTION_HEADER
.while ecx>0 ; check all sections .if edi>=[edx].VirtualAddress
mov eax,[edx].VirtualAddress
add eax,[edx].SizeOfRawData .if edi<eax
; The address is in this section mov
eax,[edx].VirtualAddress
sub edi,eax mov eax,[edx].PointerToRawData
add eax,edi ; eax == file
offset ret
.endif .endif add
edx,sizeof IMAGE_SECTION_HEADER dec ecx
.endw assume edx:nothing assume esi:nothing
mov eax,edi ret RVAToOffset endp
ShowTheFunctions proc uses esi ecx ebx hDlg:DWORD, pNTHdr:DWORD
LOCAL temp[512]:BYTE invoke SetDlgItemText,hDlg,IDC_EDIT,0
invoke AppendText,hDlg,addr buffer mov edi,pNTHdr
assume edi:ptr IMAGE_NT_HEADERS mov edi, [edi].OptionalHeader.DataDirectory[sizeof
IMAGE_DATA_DIRECTORY].VirtualAddress invoke RVAToOffset,pMapping,edi
mov edi,eax add edi,pMapping
assume edi:ptr IMAGE_IMPORT_DESCRIPTOR .while !([edi].OriginalFirstThunk==0
&& [edi].TimeDateStamp==0 && [edi].ForwarderChain==0 &&
[edi].Name1==0 && [edi].FirstThunk==0) invoke
AppendText,hDlg,addr ImportDescriptor invoke RVAToOffset,pMapping,
[edi].Name1 mov edx,eax
add edx,pMapping invoke wsprintf, addr temp, addr
IDTemplate, [edi].OriginalFirstThunk,[edi].TimeDateStamp,[edi].ForwarderChain,edx,[edi].FirstThunk
invoke AppendText,hDlg,addr temp
.if [edi].OriginalFirstThunk==0
mov esi,[edi].FirstThunk .else
mov esi,[edi].OriginalFirstThunk .endif
invoke RVAToOffset,pMapping,esi add eax,pMapping
mov esi,eax invoke
AppendText,hDlg,addr NameHeader .while dword ptr
[esi]!=0 test dword ptr [esi],IMAGE_ORDINAL_FLAG32
jnz ImportByOrdinal
invoke RVAToOffset,pMapping,dword ptr [esi]
mov edx,eax add edx,pMapping
assume edx:ptr IMAGE_IMPORT_BY_NAME
mov cx, [edx].Hint movzx ecx,cx
invoke wsprintf,addr temp,addr NameTemplate,ecx,addr
[edx].Name1 jmp ShowTheText ImportByOrdinal:
mov edx,dword ptr [esi]
and edx,0FFFFh invoke wsprintf,addr
temp,addr OrdinalTemplate,edx ShowTheText:
invoke AppendText,hDlg,addr temp add
esi,4 .endw add edi,sizeof IMAGE_IMPORT_DESCRIPTOR
.endw ret ShowTheFunctions endp end start
分析:本例中,,用戶點擊打開菜單顯示文件打開對話框,,檢驗文件的PE有效性后調用
ShowTheFunctions。 ShowTheFunctions
proc uses esi ecx ebx hDlg:DWORD, pNTHdr:DWORD LOCAL temp[512]:BYTE
保留512字節(jié)堆??臻g用于字符串操作,。
invoke SetDlgItemText,hDlg,IDC_EDIT,0 清除編輯控件內容。
invoke AppendText,hDlg,addr buffer 將PE文件名插入編輯控件,。
AppendText 通過傳遞一個
EM_REPLACESEL 消息以通知向編輯控件添加文本,。然后它又向編輯控件發(fā)送一個設置了
wParam=-1和lParam=0的EM_SETSEL
消息,使光標定位到文本末,。
mov edi,pNTHdr assume edi:ptr IMAGE_NT_HEADERS
mov edi, [edi].OptionalHeader.DataDirectory[sizeof IMAGE_DATA_DIRECTORY].VirtualAddress
獲取import
symbols的RVA,。edi起初指向
PE header,以此我們可以定位到數據目錄數組的第二個數組元素來得到虛擬地址值,。
invoke RVAToOffset,pMapping,edi mov edi,eax
add edi,pMapping 這兒對PE編程初學者來說可能有點困難,。在PE文件中大多數地址多是RVAs
而 RVAs只有當PE文件被PE裝載器裝入內存后才有意義。
本例中,,我們直接將文件映射到內存而不是通過PE裝載器載入,,因此我們不能直接使用那些RVAs。必須先將那些RVAs轉換成文件偏移量,,RVAToOffset函數就起到這個作用,。
這里不準備詳細分析。指出的是,,它還將給定的RVA和PE文件所有節(jié)的始末RVA作比較(檢驗RVA的有效性),,然后通過IMAGE_SECTION_HEADER
結構中的PointerToRawData域(當然是所在節(jié)的那個PointerToRawData域啦)將RVA轉換成文件偏移量。
函數使用需要傳遞兩個參數: 內存映射文件指針和所要轉換的RVA,。eax里返回文件偏移量,。上面代碼中,我們必須將文件偏移量加上內存映射文件指針以轉換成虛擬地址,。是不是有點復雜?
:) assume edi:ptr
IMAGE_IMPORT_DESCRIPTOR .while !([edi].OriginalFirstThunk==0
&& [edi].TimeDateStamp==0 && [edi].ForwarderChain==0 &&
[edi].Name1==0 && [edi].FirstThunk==0) edi現在指向第一個
IMAGE_IMPORT_DESCRIPTOR
結構,。接下來我們遍歷整個結構數組直到遇上一個全0結構,這就是數組末尾了,。
invoke AppendText,hDlg,addr ImportDescriptor invoke
RVAToOffset,pMapping, [edi].Name1 mov edx,eax
add edx,pMapping 我們要顯示當前
IMAGE_IMPORT_DESCRIPTOR
結構的值,。Name1
不同于其他結構成員,它含有指向相關dll名的RVA,。因此必須先將其轉換成虛擬地址,。
invoke wsprintf, addr temp, addr IDTemplate, [edi].OriginalFirstThunk,[edi].TimeDateStamp,[edi].ForwarderChain,edx,[edi].FirstThunk
invoke AppendText,hDlg,addr temp 顯示當前
IMAGE_IMPORT_DESCRIPTOR
結構的值,。
.if [edi].OriginalFirstThunk==0
mov esi,[edi].FirstThunk .else
mov esi,[edi].OriginalFirstThunk .endif 接下來準備遍歷
IMAGE_THUNK_DATA
數組。通常我們會選擇OriginalFirstThunk指向的那個數組,,不過,,如果某些連接器錯誤地將OriginalFirstThunk
置0,這可以通過檢查OriginalFirstThunk值是否為0判斷,。這樣的話,,只要選擇FirstThunk指向的數組了。
invoke RVAToOffset,pMapping,esi add eax,pMapping
mov esi,eax 同樣的,,OriginalFirstThunk/FirstThunk值是一個RVA,。必須將其轉換為虛擬地址。
invoke AppendText,hDlg,addr NameHeader .while dword
ptr [esi]!=0 現在我們準備遍歷 IMAGE_THUNK_DATAs
數組以查找該DLL引入的函數名,,直到遇上全0項,。
test dword ptr [esi],IMAGE_ORDINAL_FLAG32
jnz ImportByOrdinal 第一件事是校驗IMAGE_THUNK_DATA
是否含有IMAGE_ORDINAL_FLAG32標記。檢查IMAGE_THUNK_DATA
的MSB是否為1,,如果是1,,則函數是通過序數引出的,所以不需要更進一步處理了,。直接從
IMAGE_THUNK_DATA
提取低字節(jié)獲得序數,,然后是下一個IMAGE_THUNK_DATA
雙字。
invoke RVAToOffset,pMapping,dword ptr [esi]
mov edx,eax add edx,pMapping
assume edx:ptr IMAGE_IMPORT_BY_NAME 如果IMAGE_THUNK_DATA
的MSB是0,,那么它包含了IMAGE_IMPORT_BY_NAME
結構的RVA。需要先轉換為虛擬地址,。
mov cx, [edx].Hint movzx ecx,cx
invoke wsprintf,addr temp,addr NameTemplate,ecx,addr
[edx].Name1 jmp ShowTheText Hint
是字類型,,所以先轉換為雙字后再傳遞給wsprintf,然后我們將hint和函數名都顯示到編輯控件中,。 ImportByOrdinal:
mov edx,dword ptr [esi]
and edx,0FFFFh invoke wsprintf,addr
temp,addr OrdinalTemplate,edx 在僅用序數引出函數的情況中,,先清空高字再顯示序數。 ShowTheText:
invoke AppendText,hDlg,addr temp
add esi,4 在編輯控件中插入相應的函數名/序數后,,跳轉到下個
IMAGE_THUNK_DATA,。
.endw add edi,sizeof IMAGE_IMPORT_DESCRIPTOR 處理完當前IMAGE_THUNK_DATA
數組里的所有雙字,跳轉到下個IMAGE_IMPORT_DESCRIPTOR
開始處理其他DLLs的引入函數了,。 附錄:讓我們再來討論一下bound
import,。當PE裝載器裝入PE文件時,檢查引入表并將相關DLLs映射到進程地址空間,。然后象我們這樣遍歷IMAGE_THUNK_DATA
數組并用引入函數的真實地址替換IMAGE_THUNK_DATAs
值,。這一步需要很多時間。如果程序員能事先正確預測函數地址,,PE裝載器就不用每次裝入PE文件時都去修正IMAGE_THUNK_DATAs
值了,。Bound
import就是這種思想的產物,。
為了方便實現,Microsoft出品的類似Visual
Studio的編譯器多提供了bind.exe這樣的工具,,由它檢查PE文件的引入表并用引入函數的真實地址替換IMAGE_THUNK_DATA
值,。當文件裝入時,PE裝載器必定檢查地址的有效性,,如果DLL版本不同于PE文件存放的相關信息,,或則DLLs需要重定位,那么裝載器認為原先計算的地址是無效的,,它必定遍歷OriginalFirstThunk指向的數組以獲取引入函數新地址,。
Bound import在本課中并非很重要,我們確省就是用到了OriginalFirstThunk,。 翻譯:iamgufeng
[Iczelion's Win32 Assembly Homepage][LuoYunBin's
Win32 ASM Page]
|