PE教程7: Export Table(引出表)
PE教程7: Export Table(引出表)
上一課我們已經學習了動態聯接中關於引入表那部分知識,現在繼續另外一部分,那就是引出表。
下載 範例。
理論:
當PE裝載器執行一個程式,它將相關DLLs都裝入該程式的地址空間。然後根據主程式的引入函式資訊,查詢相關DLLs中的真實函式地址來修正主程式。PE裝載器搜尋的是DLLs中的引出函式。
DLL/EXE要引出一個函式給其他DLL/EXE使用,有兩種實現方法: 透過函式名引出或者僅僅透過序數引出。比如某個DLL要引出名為"GetSysConfig"的函式,如果它以函式名引出,那麼其他DLLs/EXEs若要呼叫這個函式,必須透過函式名,就是GetSysConfig。另外一個辦法就是透過序數引出。什麼是序數呢? 序數是唯一指定DLL中某個函式的16位數字,在所指向的DLL裡是獨一無二的。例如在上例中,DLL可以選擇透過序數引出,假設是16,那麼其他DLLs/EXEs若要呼叫這個函式必須以該值作為GetProcAddress呼叫引數。這就是所謂的僅僅靠序數引出。
我們不提倡僅僅透過序數引出函式這種方法,這會帶來DLL維護上的問題。一旦DLL升級/修改,程式設計師無法改變函式的序數,否則呼叫該DLL的其他程式都將無法工作。
現在我們開始學習引出結構。象引出表一樣,可以透過資料目錄找到引出表的位置。這兒,引出表是資料目錄的第一個成員,又可稱為IMAGE_EXPORT_DIRECTORY。該結構中共有11 個成員,常用的列於下表。
Field Name | Meaning |
---|---|
nName | 模組的真實名稱。本域是必須的,因為檔名可能會改變。這種情況下,PE裝載器將使用這個內部名字。 |
nBase | 基數,加上序數就是函式地址陣列的索引值了。 |
NumberOfFunctions | 模組引出的函式/符號總數。 |
NumberOfNames | 透過名字引出的函式/符號數目。該值不是模組引出的函式/符號總數,這是由上面的NumberOfFunctions給出。本域可以為0,表示模組可能僅僅透過序數引出。如果模組根本不引出任何函式/符號,那麼資料目錄中引出表的RVA為0。 |
AddressOfFunctions | 模組中有一個指向所有函式/符號的RVAs陣列,本域就是指向該RVAs陣列的RVA。簡言之,模組中所有函式的RVAs都儲存在一個陣列裡,本域就指向這個陣列的首地址。 |
AddressOfNames | 類似上個域,模組中有一個指向所有函式名的RVAs陣列,本域就是指向該RVAs陣列的RVA。 |
AddressOfNameOrdinals | RVA,指向包含上述 AddressOfNames陣列中相關函式之序數的16位陣列。 |
上面也許無法讓您完全理解引出表,下面的簡述將助您一臂之力。
引出表的設計是為了方便PE裝載器工作。首先,模組必須儲存所有引出函式的地址以供PE裝載器查詢。模組將這些資訊儲存在AddressOfFunctions域指向的陣列中,而陣列元素數目存放在NumberOfFunctions域中。 因此,如果模組引出40個函式,則AddressOfFunctions指向的陣列必定有40個元素,而NumberOfFunctions值為40。現在如果有一些函式是透過名字引出的,那麼模組必定也在檔案中保留了這些資訊。這些 名字的RVAs存放在一陣列中以供PE裝載器查詢。該陣列由AddressOfNames指向,NumberOfNames包含名字數目。考慮一下PE裝載器的工作機制,它知道函式名,並想以此獲取這些函式的地址。至今為止,模組已有兩個模組: 名字陣列和地址陣列,但兩者之間還沒有聯絡的紐帶。因此我們還需要一些聯絡函式名及其地址的東東。PE參考指出使用到地址陣列的索引作為聯接,因此PE裝載器在名字陣列中找到匹配名字的同時,它也獲取了 指向地址表中對應元素的索引。 而這些索引儲存在由AddressOfNameOrdinals域指向的另一個陣列(最後一個)中。由於該陣列是起了聯絡名字和地址的作用,所以其元素數目必定和名字陣列相同,比如,每個名字有且僅有一個相關地址,反過來則不一定: 每個地址可以有好幾個名字來對應。因此我們給同一個地址取"別名"。為了起到連線作用,名字陣列和索引陣列必須並行地成對使用,譬如,索引陣列的第一個元素必定含有第一個名字的索引,以此類推。
AddressOfNames | AddressOfNameOrdinals | |||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| | | | |||||||||||||||||||
|
|
|
下面舉一兩個例子說明問題。如果我們有了引出函式名並想以此獲取地址,可以這麼做:
- 定位到PE header。
- 從資料目錄讀取引出表的虛擬地址。
- 定位引出表獲取名字數目(NumberOfNames)。
- 並行遍歷AddressOfNames和AddressOfNameOrdinals指向的陣列匹配名字。如果在AddressOfNames 指向的陣列中找到匹配名字,從AddressOfNameOrdinals 指向的陣列中提取索引值。例如,若發現匹配名字的RVA存放在AddressOfNames 陣列的第77個元素,那就提取AddressOfNameOrdinals陣列的第77個元素作為索引值。如果遍歷完NumberOfNames 個元素,說明當前模組沒有所要的名字。
- 從AddressOfNameOrdinals 陣列提取的數值作為AddressOfFunctions 陣列的索引。也就是說,如果值是5,就必須讀取AddressOfFunctions 陣列的第5個元素,此值就是所要函式的RVA。
現在我們在把注意力轉向IMAGE_EXPORT_DIRECTORY 結構的nBase成員。您已經知道AddressOfFunctions 陣列包含了模組中所有引出符號的地址。當PE裝載器索引該陣列查詢函式地址時,讓我們設想這樣一種情況,如果程式設計師在.def檔案中設定起始序數號為200,這意味著AddressOfFunctions 陣列至少有200個元素,甚至這前面200個元素並沒使用,但它們必須存在,因為PE裝載器這樣才能索引到正確的地址。這種方法很不好,所以又設計了nBase 域解決這個問題。如果程式設計師指定起始序數號為200,nBase 值也就是200。當PE裝載器讀取nBase域時,它知道開始200個元素並不存在,這樣減掉一個nBase值後就可以正確地索引AddressOfFunctions 陣列了。有了nBase,就節約了200個空元素。
注意nBase並不影響AddressOfNameOrdinals陣列的值。儘管取名"AddressOfNameOrdinals",該陣列實際包含的是指向AddressOfFunctions 陣列的索引,而不是什麼序數啦。
討論完nBase的作用,我們繼續下一個例子。
假設我們只有函式的序數,那麼怎樣獲取函式地址呢,可以這麼做:
- 定位到PE header。
- 從資料目錄讀取引出表的虛擬地址。
- 定位引出表獲取nBase值。
- 減掉nBase值得到指向AddressOfFunctions 陣列的索引。
- 將該值與NumberOfFunctions作比較,大於等於後者則序數無效。
- 透過上面的索引就可以獲取AddressOfFunctions 陣列中的RVA了。
可以看出,從序數獲取函式地址比函式名快捷容易。不需要遍歷AddressOfNames 和 AddressOfNameOrdinals 這兩個陣列。然而,綜合效能必須與模組維護的簡易程度作一平衡。
總之,如果想透過名字獲取函式地址,需要遍歷AddressOfNames 和 AddressOfNameOrdinals 這兩個陣列。如果使用函式序數,減掉nBase值後就可直接索引AddressOfFunctions 陣列。
如果一函式透過名字引出,那在GetProcAddress中可以使用名字或序數。但函式僅由序數引出情況又怎樣呢?
現在就來看看。
"一個函式僅由序數引出"意味著函式在AddressOfNames 和 AddressOfNameOrdinals
陣列中不存在相關項。記住兩個域,NumberOfFunctions 和 NumberOfNames。這兩個域可以清楚地顯示有時某些函式沒有名字的。函式數目至少等同於名字數目,沒有名字的函式透過序數引出。比如,如果存在70個函式但AddressOfNames陣列中只有40項,這就意味著模組中有30個函式是僅透過序數引出的。現在我們怎樣找出那些僅透過序數引出的函式呢?這不容易,必須透過排除法,比如,AddressOfFunctions
的陣列項在AddressOfNameOrdinals 陣列中不存在相關指向,這就說明該函式RVA只透過序數引出。
示例:
本例類似上課的範例。然而,在顯示IMAGE_EXPORT_DIRECTORY 結構一些成員資訊的同時,也列出了引出函式的RVAs,序數和名字。注意本例沒有列出僅由序數引出的函式。
.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
ShowExportFunctions
proto :DWORD
ShowTheFunctions proto :DWORD,:DWORD
AppendText proto :DWORD,:DWORD
SEH struct
PrevLink dd ?
CurrentHandler dd ?
SafeOffset
dd ?
PrevEsp dd ?
PrevEbp dd ?
SEH ends
.data
AppName db "PE tutorial no.7",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
NoExportTable db "No export information in this
file",0
CRLF db 0Dh,0Ah,0
ExportTable db 0Dh,0Ah,"======[ IMAGE_EXPORT_DIRECTORY
]======",0Dh,0Ah
db "Name of the module: %s",0Dh,0Ah
db "nBase: %lu",0Dh,0Ah
db "NumberOfFunctions: %lu",0Dh,0Ah
db "NumberOfNames: %lu",0Dh,0Ah
db "AddressOfFunctions: %lX",0Dh,0Ah
db "AddressOfNames: %lX",0Dh,0Ah
db "AddressOfNameOrdinals: %lX",0Dh,0Ah,0
Header db "RVA Ord. Name",0Dh,0Ah
db "----------------------------------------------",0
template db "%lX %u %s",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 ShowExportFunctions,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
ShowExportFunctions 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
ShowExportFunctions 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
RVAToFileMap 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
.if edi>=[edx].VirtualAddress
mov
eax,[edx].VirtualAddress
add eax,[edx].SizeOfRawData
.if edi
相關文章
- PE教程6: Import Table(引入表2015-11-15Import
- PE教程5: Section Table(節表)2015-11-15
- PE檔案格式詳細解析(六)-- 基址重定位表(Base Relocation Table)2020-03-10
- mysql 刪表引出的問題2020-04-05MySql
- PE教程2: 檢驗PE檔案的有效性2015-11-15
- PE教程4: Optional Header2015-11-15Header
- PE節表詳細分析2021-11-06
- TableTools Export Excel前Table內容格式的轉換應用2018-04-11ExportExcel
- PE教程3: File Header (檔案頭)2015-11-15Header
- PE檔案結構(四) 輸出表2014-10-06
- @EXPORT and @EXPORT_OK2016-03-20Export
- 7 、shrink table and its dependent segments2007-08-01
- oracle 外部表 external table2008-06-03Oracle
- Oracle外部表 External Table2011-09-09Oracle
- 分割槽表PARTITION table2007-12-25
- SQLAlchemy Table(表)類方式 – Table類和Column類2018-10-25SQL
- 胖爪裝機大師pe硬碟分割槽教程2022-04-15硬碟
- ORA-31633: unable to create master table "SYSTEM.SYS_EXPORT_FULL_XX"2019-06-25ASTExport
- Xamarin圖表開發基礎教程(7)OxyPlot框架2019-11-15框架
- export 和 export default 區別2019-09-07Export
- elementUI table 自定義表頭2018-09-25UI
- Oracle基礎 10 表 table2014-02-12Oracle
- 資料庫表--nested table2013-12-11資料庫
- 資料庫表--temporary table2013-12-11資料庫
- 資料庫表--object table2013-12-12資料庫Object
- 資料庫表--external table2013-12-12資料庫
- Oracle分割槽表(Partition Table)2009-02-13Oracle
- oracle 誤刪表 drop table2010-02-04Oracle
- 分割槽表PARTITION table(轉)2007-03-29
- export2010-03-19Export
- JavaScript中的export、export default、exports和module.exports(export、export default、exports使用詳細)2024-09-03JavaScriptExport
- SAP UI5 表格資料如何匯出成 Excel 檔案(Table Export As Excel)2022-11-27UIExcelExport
- export和export default的區別2020-11-03Export
- export ORACLE_SID=founder export2008-01-26ExportOracle
- Billy Belceb 病毒編寫教程for Win32 ----PE檔案頭2015-11-15Win32
- 透視表pivot_table和交叉表crosstab2020-10-03ROS
- 表、索引遷移表空間alter table move2009-03-26索引
- layui將table轉化表單顯示(即table.render轉為表單展示)2018-10-11UI