一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

PE教程6: Import Table(引入表)

 ZackEdge 2015-04-04

 

PE教程6: Import Table(引入表)

本課我們將學(xué)習(xí)引入表。先警告一下,對(duì)于不熟悉引入表的讀者來(lái)說(shuō),這是一堂又長(zhǎng)又難的課,所以需要多讀幾遍,最好再打開調(diào)試器來(lái)好好分析相關(guān)結(jié)構(gòu)。各位,努力?。?/font>

下載范例。

理論:

首先,您得了解什么是引入函數(shù)。一個(gè)引入函數(shù)是被某模塊調(diào)用的但又不在調(diào)用者模塊中的函數(shù),因而命名為"import(引入)"。引入函數(shù)實(shí)際位于一個(gè)或者更多的DLL里。調(diào)用者模塊里只保留一些函數(shù)信息,包括函數(shù)名及其駐留的DLL名?,F(xiàn)在,我們?cè)鯓硬拍苷业?/font>PE文件中保存的信息呢? 轉(zhuǎn)到 data directory 尋求答案吧。再回顧一把,下面就是 PE header:

IMAGE_NT_HEADERS STRUCT
   Signature dd ?
   FileHeader IMAGE_FILE_HEADER <>
   OptionalHeader IMAGE_OPTIONAL_HEADER <>
IMAGE_NT_HEADERS ENDS

optional header 最后一個(gè)成員就是 data directory(數(shù)據(jù)目錄):

IMAGE_OPTIONAL_HEADER32 STRUCT
   ....
   LoaderFlags dd ?
   NumberOfRvaAndSizes dd ?
   DataDirectory IMAGE_DATA_DIRECTORY 16 dup(<>)
IMAGE_OPTIONAL_HEADER32 ENDS

data directory 是一個(gè) IMAGE_DATA_DIRECTORY 結(jié)構(gòu)數(shù)組,共有16個(gè)成員。如果您還記得節(jié)表可以看作是PE文件各節(jié)的根目錄的話,也可以認(rèn)為 data directory 是存儲(chǔ)在這些節(jié)里的邏輯元素的根目錄。明確點(diǎn),data directory 包含了PE文件中各重要數(shù)據(jù)結(jié)構(gòu)的位置和尺寸信息。 每個(gè)成員包含了一個(gè)重要數(shù)據(jù)結(jié)構(gòu)的信息。

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 包含域后,我們可以仔細(xì)研究它們了。data directory 的每個(gè)成員都是 IMAGE_DATA_DIRECTORY 結(jié)構(gòu)類型的,其定義如下所示:

IMAGE_DATA_DIRECTORY STRUCT
  VirtualAddress dd ?
  isize dd ?
IMAGE_DATA_DIRECTORY ENDS

VirtualAddress 實(shí)際上是數(shù)據(jù)結(jié)構(gòu)的相對(duì)虛擬地址(RVA)。比如,如果該結(jié)構(gòu)是關(guān)于import symbols的,該域就包含指向IMAGE_IMPORT_DESCRIPTOR 數(shù)組的RVA。
isize 含有VirtualAddress所指向數(shù)據(jù)結(jié)構(gòu)的字節(jié)數(shù)。

下面就是如何找尋PE文件中重要數(shù)據(jù)結(jié)構(gòu)的一般方法:

  1. DOS header 定位到 PE header
  2. optional header 讀取 data directory 的地址。
  3. IMAGE_DATA_DIRECTORY 結(jié)構(gòu)尺寸乘上找尋結(jié)構(gòu)的索引號(hào): 比如您要找尋import symbols的位置信息,必須用IMAGE_DATA_DIRECTORY 結(jié)構(gòu)尺寸(8 bytes)乘上1import symbolsdata directory中的索引號(hào))。
  4. 將上面的結(jié)果加上data directory地址,我們就得到包含所查詢數(shù)據(jù)結(jié)構(gòu)信息的 IMAGE_DATA_DIRECTORY 結(jié)構(gòu)項(xiàng)。

現(xiàn)在我們開始真正討論引入表了。data directory數(shù)組第二項(xiàng)的VirtualAddress包含引入表地址。引入表實(shí)際上是一個(gè) IMAGE_IMPORT_DESCRIPTOR 結(jié)構(gòu)數(shù)組。每個(gè)結(jié)構(gòu)包含PE文件引入函數(shù)的一個(gè)相關(guān)DLL的信息。比如,如果該PE文件從10個(gè)不同的DLL中引入函數(shù),那么這個(gè)數(shù)組就有10個(gè)成員。該數(shù)組以一個(gè)全0的成員結(jié)尾。下面詳細(xì)研究結(jié)構(gòu)組成:

IMAGE_IMPORT_DESCRIPTOR STRUCT
  union
    Characteristics dd ?
    OriginalFirstThunk dd ?
  ends
  TimeDateStamp dd ?
  ForwarderChain dd ?
  Name1 dd ?
  FirstThunk dd ?
IMAGE_IMPORT_DESCRIPTOR ENDS

結(jié)構(gòu)第一項(xiàng)是一個(gè)union子結(jié)構(gòu)。 事實(shí)上,這個(gè)union子結(jié)構(gòu)只是給 OriginalFirstThunk 增添了個(gè)別名,您也可以稱其為"Characteristics"。 該成員項(xiàng)含有指向一個(gè) IMAGE_THUNK_DATA 結(jié)構(gòu)數(shù)組的RVA。
什么是
IMAGE_THUNK_DATA? 這是一個(gè)dword類型的集合。通常我們將其解釋為指向一個(gè) IMAGE_IMPORT_BY_NAME 結(jié)構(gòu)的指針。注意 IMAGE_THUNK_DATA 包含了指向一個(gè) IMAGE_IMPORT_BY_NAME 結(jié)構(gòu)的指針: 而不是結(jié)構(gòu)本身。
請(qǐng)看這里
: 現(xiàn)有幾個(gè) IMAGE_IMPORT_BY_NAME 結(jié)構(gòu),我們收集起這些結(jié)構(gòu)的RVA (IMAGE_THUNK_DATAs)組成一個(gè)數(shù)組,并以0結(jié)尾,然后再將數(shù)組的RVA放入 OriginalFirstThunk。
IMAGE_IMPORT_BY_NAME 結(jié)構(gòu)存有一個(gè)引入函數(shù)的相關(guān)信息。再來(lái)研究 IMAGE_IMPORT_BY_NAME 結(jié)構(gòu)到底是什么樣子的呢:

IMAGE_IMPORT_BY_NAME STRUCT
  Hint dw ?
  Name1 db ?
IMAGE_IMPORT_BY_NAME ENDS

Hint 指示本函數(shù)在其所駐留DLL的引出表中的索引號(hào)。該域被PE裝載器用來(lái)在DLL的引出表里快速查詢函數(shù)。該值不是必須的,一些連接器將此值設(shè)為0
Name1 含有引入函數(shù)的函數(shù)名。函數(shù)名是一個(gè)ASCIIZ字符串。注意這里雖然將Name1的大小定義成字節(jié),其實(shí)它是可變尺寸域,只不過我們沒有更好方法來(lái)表示結(jié)構(gòu)中的可變尺寸域。The structure is provided so that you can refer to the data structure with descriptive names.

TimeDateStamp ForwarderChain 可是高級(jí)東東: 讓我們精通其他成員后再來(lái)討論它們吧。

Name1 含有指向DLL名字的RVA,即指向DLL名字的指針,也是一個(gè)ASCIIZ字符串。

FirstThunk OriginalFirstThunk 非常相似,它也包含指向一個(gè) IMAGE_THUNK_DATA 結(jié)構(gòu)數(shù)組的RVA(當(dāng)然這是另外一個(gè)IMAGE_THUNK_DATA 結(jié)構(gòu)數(shù)組)。
好了,如果您還在犯糊涂,就朝這邊看過來(lái)
: 現(xiàn)在有幾個(gè) IMAGE_IMPORT_BY_NAME 結(jié)構(gòu),同時(shí)您又創(chuàng)建了兩個(gè)結(jié)構(gòu)數(shù)組,并同樣寸入指向那些 IMAGE_IMPORT_BY_NAME 結(jié)構(gòu)的RVAs,這樣兩個(gè)數(shù)組就包含相同數(shù)值了(可謂相當(dāng)精確的復(fù)制啊)。 最后您決定將第一個(gè)數(shù)組的RVA賦給 OriginalFirstThunk,第二個(gè)數(shù)組的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

現(xiàn)在您應(yīng)該明白我的意思。不要被IMAGE_THUNK_DATA這個(gè)名字弄糊涂: 它僅是指向 IMAGE_IMPORT_BY_NAME 結(jié)構(gòu)的RVA。 如果將 IMAGE_THUNK_DATA 字眼想象成RVA,就更容易明白了。OriginalFirstThunk FirstThunk 所指向的這兩個(gè)數(shù)組大小取決于PE文件從DLL中引入函數(shù)的數(shù)目。比如,如果PE文件從kernel32.dll中引入10個(gè)函數(shù),那么IMAGE_IMPORT_DESCRIPTOR 結(jié)構(gòu)的 Name1域包含指向字符串"kernel32.dll"RVA,同時(shí)每個(gè)IMAGE_THUNK_DATA 數(shù)組有10個(gè)元素。

下一個(gè)問題是: 為什么我們需要兩個(gè)完全相同的數(shù)組? 為了回答該問題,我們需要了解當(dāng)PE文件被裝載到內(nèi)存時(shí),PE裝載器將查找IMAGE_THUNK_DATA IMAGE_IMPORT_BY_NAME 這些結(jié)構(gòu)數(shù)組,以此決定引入函數(shù)的地址。然后用引入函數(shù)真實(shí)地址來(lái)替代由FirstThunk指向的 IMAGE_THUNK_DATA 數(shù)組里的元素值。因此當(dāng)PE文件準(zhǔn)備執(zhí)行時(shí),上圖已轉(zhuǎn)換成:

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數(shù)組始終不會(huì)改變,所以若還反過頭來(lái)查找引入函數(shù)名,PE裝載器還能找尋到。
當(dāng)然再簡(jiǎn)單的事物都有其復(fù)雜的一面。有些情況下一些函數(shù)僅由序數(shù)引出,也就是說(shuō)您不能用函數(shù)名來(lái)調(diào)用它們: 您只能用它們的位置來(lái)調(diào)用。此時(shí),調(diào)用者模塊中就不存在該函數(shù)的 IMAGE_IMPORT_BY_NAME 結(jié)構(gòu)。不同的,對(duì)應(yīng)該函數(shù)的 IMAGE_THUNK_DATA 值的低位字指示函數(shù)序數(shù),而最高二進(jìn)位 (MSB)設(shè)為1。例如,如果一個(gè)函數(shù)只由序數(shù)引出且其序數(shù)是1234h,那么對(duì)應(yīng)該函數(shù)的 IMAGE_THUNK_DATA 值是80001234hMicrosoft提供了一個(gè)方便的常量來(lái)測(cè)試dword值的MSB位,就是 IMAGE_ORDINAL_FLAG32,其值為80000000h。
假設(shè)我們要列出某個(gè)
PE文件的所有引入函數(shù),可以照著下面步驟走:

  1. 校驗(yàn)文件是否是有效的PE。
  2. DOS header 定位到 PE header
  3. 獲取位于 OptionalHeader 數(shù)據(jù)目錄地址。
  4. 轉(zhuǎn)至數(shù)據(jù)目錄的第二個(gè)成員提取其VirtualAddress值。
  5. 利用上值定位第一個(gè) IMAGE_IMPORT_DESCRIPTOR 結(jié)構(gòu)。
  6. 檢查 OriginalFirstThunk值。若不為0,順著 OriginalFirstThunk 里的RVA值轉(zhuǎn)入那個(gè)RVA數(shù)組。若 OriginalFirstThunk 0,就改用FirstThunk值。有些連接器生成PE文件時(shí)會(huì)置OriginalFirstThunk值為0,這應(yīng)該算是個(gè)bug。不過為了安全起見,我們還是檢查 OriginalFirstThunk值先。
  7. 對(duì)于每個(gè)數(shù)組元素,我們比對(duì)元素值是否等于IMAGE_ORDINAL_FLAG32。如果該元素值的最高二進(jìn)位為1, 那么函數(shù)是由序數(shù)引入的,可以從該值的低字節(jié)提取序數(shù)。
  8. 如果元素值的最高二進(jìn)位為0,就可將該值作為RVA轉(zhuǎn)入 IMAGE_IMPORT_BY_NAME 數(shù)組,跳過 Hint 就是函數(shù)名字了。
  9. 再跳至下一個(gè)數(shù)組元素提取函數(shù)名一直到數(shù)組底部(它以null結(jié)尾)。現(xiàn)在我們已遍歷完一個(gè)DLL的引入函數(shù),接下去處理下一個(gè)DLL。
  10. 即跳轉(zhuǎn)到下一個(gè) IMAGE_IMPORT_DESCRIPTOR 并處理之,如此這般循環(huán)直到數(shù)組見底。(IMAGE_IMPORT_DESCRIPTOR 數(shù)組以一個(gè)全0域元素結(jié)尾)。

示例:

本例程打開一PE文件,將所有引入函數(shù)名讀入一編輯控件,同時(shí)顯示 IMAGE_IMPORT_DESCRIPTOR 結(jié)構(gòu)各域值。

.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

分析:

本例中,用戶點(diǎn)擊打開菜單顯示文件打開對(duì)話框,檢驗(yàn)文件的PE有效性后調(diào)用 ShowTheFunctions。

ShowTheFunctions proc uses esi ecx ebx hDlg:DWORD, pNTHdr:DWORD
   LOCAL temp[512]:BYTE

保留512字節(jié)堆??臻g用于字符串操作。

   invoke SetDlgItemText,hDlg,IDC_EDIT,0

清除編輯控件內(nèi)容。

   invoke AppendText,hDlg,addr buffer

PE文件名插入編輯控件。 AppendText 通過傳遞一個(gè) EM_REPLACESEL 消息以通知向編輯控件添加文本。然后它又向編輯控件發(fā)送一個(gè)設(shè)置了 wParam=-1lParam=0EM_SETSEL 消息,使光標(biāo)定位到文本末。

   mov edi,pNTHdr
   assume edi:ptr IMAGE_NT_HEADERS
   mov edi, [edi].OptionalHeader.DataDirectory[sizeof IMAGE_DATA_DIRECTORY].VirtualAddress

獲取import symbolsRVAedi起初指向 PE header,以此我們可以定位到數(shù)據(jù)目錄數(shù)組的第二個(gè)數(shù)組元素來(lái)得到虛擬地址值。

   invoke RVAToOffset,pMapping,edi
   mov edi,eax
   add edi,pMapping

這兒對(duì)PE編程初學(xué)者來(lái)說(shuō)可能有點(diǎn)困難。在PE文件中大多數(shù)地址多是RVAs 而 RVAs只有當(dāng)PE文件被PE裝載器裝入內(nèi)存后才有意義。 本例中,我們直接將文件映射到內(nèi)存而不是通過PE裝載器載入,因此我們不能直接使用那些RVAs。必須先將那些RVAs轉(zhuǎn)換成文件偏移量,RVAToOffset函數(shù)就起到這個(gè)作用。 這里不準(zhǔn)備詳細(xì)分析。指出的是,它還將給定的RVA和PE文件所有節(jié)的始末RVA作比較(檢驗(yàn)RVA的有效性),然后通過IMAGE_SECTION_HEADER 結(jié)構(gòu)中的PointerToRawData域(當(dāng)然是所在節(jié)的那個(gè)PointerToRawData域啦)將RVA轉(zhuǎn)換成文件偏移量。
函數(shù)使用需要傳遞兩個(gè)參數(shù): 內(nèi)存映射文件指針和所要轉(zhuǎn)換的RVA。eax里返回文件偏移量。上面代碼中,我們必須將文件偏移量加上內(nèi)存映射文件指針以轉(zhuǎn)換成虛擬地址。是不是有點(diǎn)復(fù)雜? :)

   assume edi:ptr IMAGE_IMPORT_DESCRIPTOR
   .while !([edi].OriginalFirstThunk==0 && [edi].TimeDateStamp==0 && [edi].ForwarderChain==0 && [edi].Name1==0 && [edi].FirstThunk==0)

edi現(xiàn)在指向第一個(gè) IMAGE_IMPORT_DESCRIPTOR 結(jié)構(gòu)。接下來(lái)我們遍歷整個(gè)結(jié)構(gòu)數(shù)組直到遇上一個(gè)全0結(jié)構(gòu),這就是數(shù)組末尾了。

     invoke AppendText,hDlg,addr ImportDescriptor
     invoke RVAToOffset,pMapping, [edi].Name1
     mov edx,eax
     add edx,pMapping

我們要顯示當(dāng)前 IMAGE_IMPORT_DESCRIPTOR 結(jié)構(gòu)的值。Name1 不同于其他結(jié)構(gòu)成員,它含有指向相關(guān)dll名的RVA。因此必須先將其轉(zhuǎn)換成虛擬地址。

     invoke wsprintf, addr temp, addr IDTemplate, [edi].OriginalFirstThunk,[edi].TimeDateStamp,[edi].ForwarderChain,edx,[edi].FirstThunk      invoke AppendText,hDlg,addr temp

顯示當(dāng)前 IMAGE_IMPORT_DESCRIPTOR 結(jié)構(gòu)的值。

     .if [edi].OriginalFirstThunk==0
        mov esi,[edi].FirstThunk
     .else
        mov esi,[edi].OriginalFirstThunk
     .endif

接下來(lái)準(zhǔn)備遍歷 IMAGE_THUNK_DATA 數(shù)組。通常我們會(huì)選擇OriginalFirstThunk指向的那個(gè)數(shù)組,不過,如果某些連接器錯(cuò)誤地將OriginalFirstThunk 0,這可以通過檢查OriginalFirstThunk值是否為0判斷。這樣的話,只要選擇FirstThunk指向的數(shù)組了。

     invoke RVAToOffset,pMapping,esi
     add eax,pMapping
     mov esi,eax

同樣的,OriginalFirstThunk/FirstThunk值是一個(gè)RVA。必須將其轉(zhuǎn)換為虛擬地址。

     invoke AppendText,hDlg,addr NameHeader
     .while dword ptr [esi]!=0

現(xiàn)在我們準(zhǔn)備遍歷 IMAGE_THUNK_DATAs 數(shù)組以查找該DLL引入的函數(shù)名,直到遇上全0項(xiàng)。

       test dword ptr [esi],IMAGE_ORDINAL_FLAG32
       jnz ImportByOrdinal

第一件事是校驗(yàn)IMAGE_THUNK_DATA 是否含有IMAGE_ORDINAL_FLAG32標(biāo)記。檢查IMAGE_THUNK_DATA MSB是否為1,如果是1,則函數(shù)是通過序數(shù)引出的,所以不需要更進(jìn)一步處理了。直接從 IMAGE_THUNK_DATA 提取低字節(jié)獲得序數(shù),然后是下一個(gè)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 MSB0,那么它包含了IMAGE_IMPORT_BY_NAME 結(jié)構(gòu)的RVA。需要先轉(zhuǎn)換為虛擬地址。

       mov cx, [edx].Hint
       movzx ecx,cx
       invoke wsprintf,addr temp,addr NameTemplate,ecx,addr [edx].Name1
       jmp ShowTheText

Hint 是字類型,所以先轉(zhuǎn)換為雙字后再傳遞給wsprintf,然后我們將hint和函數(shù)名都顯示到編輯控件中。

ImportByOrdinal:
       mov edx,dword ptr [esi]
       and edx,0FFFFh
       invoke wsprintf,addr temp,addr OrdinalTemplate,edx

在僅用序數(shù)引出函數(shù)的情況中,先清空高字再顯示序數(shù)。

ShowTheText:
       invoke AppendText,hDlg,addr temp
       add esi,4

在編輯控件中插入相應(yīng)的函數(shù)名/序數(shù)后,跳轉(zhuǎn)到下個(gè) IMAGE_THUNK_DATA。

    .endw
    add edi,sizeof IMAGE_IMPORT_DESCRIPTOR

處理完當(dāng)前IMAGE_THUNK_DATA 數(shù)組里的所有雙字,跳轉(zhuǎn)到下個(gè)IMAGE_IMPORT_DESCRIPTOR 開始處理其他DLLs的引入函數(shù)了。

附錄:

讓我們?cè)賮?lái)討論一下bound import。當(dāng)PE裝載器裝入PE文件時(shí),檢查引入表并將相關(guān)DLLs映射到進(jìn)程地址空間。然后象我們這樣遍歷IMAGE_THUNK_DATA 數(shù)組并用引入函數(shù)的真實(shí)地址替換IMAGE_THUNK_DATAs 值。這一步需要很多時(shí)間。如果程序員能事先正確預(yù)測(cè)函數(shù)地址,PE裝載器就不用每次裝入PE文件時(shí)都去修正IMAGE_THUNK_DATAs 值了。Bound import就是這種思想的產(chǎn)物。
為了方便實(shí)現(xiàn),Microsoft出品的類似Visual Studio的編譯器多提供了bind.exe這樣的工具,由它檢查PE文件的引入表并用引入函數(shù)的真實(shí)地址替換IMAGE_THUNK_DATA 值。當(dāng)文件裝入時(shí),PE裝載器必定檢查地址的有效性,如果DLL版本不同于PE文件存放的相關(guān)信息,或則DLLs需要重定位,那么裝載器認(rèn)為原先計(jì)算的地址是無(wú)效的,它必定遍歷OriginalFirstThunk指向的數(shù)組以獲取引入函數(shù)新地址。
Bound import
在本課中并非很重要,我們確省就是用到了OriginalFirstThunk。

翻譯:iamgufeng [Iczelion's Win32 Assembly Homepage][LuoYunBin's Win32 ASM Page]

 

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

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    扒开腿狂躁女人爽出白浆av| 欧美偷拍一区二区三区四区| 亚洲欧美中文字幕精品| 男女一进一出午夜视频| 一区二区免费视频中文乱码国产| 国产精品免费视频专区| 91人妻人人做人碰人人九色| 国产女性精品一区二区三区| 深夜少妇一区二区三区| 国产内射一级一片内射高清| 日韩熟妇人妻一区二区三区 | 神马午夜福利免费视频| 中文字幕有码视频熟女| 激情三级在线观看视频| 小草少妇视频免费看视频| 麻豆91成人国产在线观看| 日韩一区二区三区高清在| 黄色在线免费高清观看| 丝袜破了有美女肉体免费观看 | 精品国产亚洲一区二区三区| 亚洲伊人久久精品国产| 日韩欧美综合中文字幕| 永久福利盒子日韩日韩| 日本二区三区在线播放| 91精品蜜臀一区二区三区| 黑人粗大一区二区三区| 国产精品亚洲精品亚洲| 亚洲综合伊人五月天中文| 亚洲国产精品国自产拍社区| 粉嫩国产一区二区三区在线| 91欧美亚洲精品在线观看| 一区二区三区免费公开| 日韩日韩欧美国产精品| 日韩成人高清免费在线| 日韩欧美亚洲综合在线| 美女露小粉嫩91精品久久久| 亚洲国产91精品视频| 日韩欧美国产精品中文字幕| 成人精品网一区二区三区| 亚洲综合一区二区三区在线| 日韩色婷婷综合在线观看|