日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

PE结构导出表

發布時間:2025/3/21 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 PE结构导出表 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

PE體系

PE結構&整體敘述

PE結構&導入表

PE結構&導出表

PE結構&基址重定位表

PE結構&綁定導入實現

PE結構&延遲加載導入表

導出表的作用

代碼重用機制提供了重用代碼的動態鏈接庫,它會向調用者說明庫里的哪些函數是可以被別人使用的,這些用來說明的信息便組成了導出表。
通常情況下,導出表存在于動態鏈接庫文件里。但不能簡單地認為EXE中沒有導出表,例如WinWord.exe文件里就有;也不能簡單地認為所有的DLL中都有導出表,例如一些專門存放資源位文件的DLL里就沒有導出表。
它的存在可以讓程序開發者很容易清楚PE中到底有多少可以使用的函數,但如果沒有函數使用說明,開發著只能通過函數名稱,反匯編代碼或者運行結果對函數的調用方式,函數的功能等進行猜測。
Windows裝載器在進行PE裝載時,會將導入表中登記的所有DLL一并裝入,然后根據DLL的導出表中對導入函數的描述修正導入表的IAT值。通過導入表,DLL文件向調用它的程序或系統提供導出函數的名稱,序號,以及入口地址等信息。
綜上所述,作用如下:

  • 可以通過導出表分析不認識的動態鏈接庫文件所提供的功能
  • 向調用者提供輸出函數指令在模塊中的起始地址
  • 導出表的定位

    導出表數據為數據目錄中注冊的數據類型之一,其描述信息處于數據目錄的第1個目錄項中如下:



    0x2140轉換成文件偏移后是0x940,

    導出目錄IMAGE_EXPORT_DIRECTORY

    導出數據的第一個結構是IMAGE_EXPORT_DIRECTORY。該結構詳細定義如下:

    IMAGE_EXPORT_DIRECTORY STRUCTCharacteristics DWORD ? ;0000h -標志,未用TimeDateStamp DWORD ? ;0004h -時間戳MajorVersion WORD ? ;0008h -未用MinorVersion WORD ? ;000ah -未用nName DWORD ? ;000ch -指向該導出表的文件名字符串nBase DWORD ? ;0010h -導出函數的起始序號NumberOfFunctions DWORD ? ;0014h -所有的導出函數個數NumberOfNames DWORD ? ;0018h -以函數名導出的函數個數AddressOfFunctions DWORD ? ;001ch -導出函數地址表RVAAddressOfNames DWORD ? ;0020h -函數名稱地址表RVAAddressOfNameOrdinals DWORD ? ;0024h -函數序列地址表 IMAGE_EXPORT_DIRECTORY ENDS

    導入表的IMAGE_IMPORT_DESCRIPTOR個數與調用的動態鏈接庫個數相等,而導出表的IMAGE_EXPORT_DIRECTORY只有一個。解釋如下:

    IMAGE_EXPORT_DIRECTORY.nName

    +000ch,雙字,該字段指示的地址指向了一個以“\0”結尾的字符串,字符串記錄了導出表所在的文件的最初文件名

    IMAGE_EXPORT_DIRECTORY.NumberOfFunctions

    +0014h,雙字。該字段定義了文件中導出函數的總個數

    IMAGE_EXPORT_DIRECTORY.NumberOfNames

    +0018h,雙字。在導出表中,有些函數是定義名字的,有些事沒有定義名字的。該字段記錄了所有定義名字函數的個數。如果此值為0,則表示所有的函數都沒有定義名字。NumberOfNames和NumberOfFunctions的關系是前者小于后者。

    IMAGE_EXPORT_DIRECTORY.AddressOfFunctions

    +001ch,雙字。該指針指向了全部導出函數的入口地址的起始。從入口地址開始為雙字數組,數組的個數由字段IMAGE_EXPORT_DIRECTORY.NumberOfFunctions決定。導出函數的每一個地址按函數的編號順序依次往后排開。

    IMAGE_EXPORT_DIRECTORY.nBase

    +0010h,雙字。導出函數編號的起始值。DLL中的第一個導出函數并不從0開始的,某個導出函數的編號等于從AddressOfFunctions開始的順序號加上這個值,如下:

    如上圖,Fun1的函數編號為nBase+0=200h,Fun2的函數編號為nBase+1=201h

    IMAGE_EXPORT_DIRECTORY.AddressOfNames

    +0020h,雙字,該值為一個指針,該指針指向的位置是一連串的雙字值,這些雙字值均指向了對應的定義了函數名的函數的字符串地址。這一連串的雙字個數為NumberOfNames.

    IMAGE_EXPORT_DIRECTORY.AddressOfNameOrdinals

    +0024h,雙字。該值也是一個指針,與AddressOfNames是一一對應關系(注意,是一一對應),所不同的是,AddressOfNames指向的是字符串的指針數組,而AddressOfNameOrdinals則指向了該函數在AddressOfFunction中的索引值。

    注意:
    索引值是一個字,而非雙字。該值與函數編號是兩個不同的概念,兩者的之間的關系為:索引值=編號-nBase
    導入表中“Hint/Name”中的Hint值是AddressOfFunctions 的索引值,并非編號。

    結構圖:

    0x940

    90 21 00 00

    對應IMAGE_EXPORT_DIRECTORY.nName字段,轉換成文件偏移后0x990

    此字符串為winresult.dll,是動態鏈接庫的最初的名字

    01 00 00 00

    對應IMAGE_EXPORT_DIRECTORY.nBase字段,表示起始編號為1

    04 00 00 00

    對應IMAGE_EXPORT_DIRECTORY.NumberOfFunctions字段,表示共有4個導出函數

    04 00 00 00

    對應IMAGE_EXPORT_DIRECTORY.NumberOfNames字段,表示4個導出函數均為按名稱導出

    68 21 00 00

    轉換為文件偏移是0x968
    對應IMAGE_EXPORT_DIRECTORY.AddressOfFunctions字段。從該位置取出連續4個地址(個數由IMAGE_EXPORT_DIRECTORY.NumberOfFunctions字段決定),這些地址分別對應4個函數的RVA

    索引0:0x00001183
    索引1:0x00001022
    索引2:0x00001082
    索引3:0x00001323
    用圖表示:

    78 21 00 00

    轉換為文件偏移為0x978
    對應IMAGE_EXPORT_DIRECTORY.AddressOfNames字段。從該位置取出的連續4個地址依次為

    0x0000219e—>‘AnimateClose’\0
    0x000021ab—>‘AnimateOpen’\0
    0x000021b7—>‘FadeInOpen’\0
    0x000021c2—>‘FadeOutClose’\0

    88 21 00 00

    轉換為文件偏移為0x988

    對應IMAGE_EXPORT_DIRECTORY.AddressOfNameOrdinals字段。從該位置取出的連續4個單字索引依次為:
    0x0000
    0x0001
    0x0002
    0x0003
    這些索引的值存在于字段IMAGE_EXPORT_DIRECTORY.AddressOfFunctions所指向的函數地址列表中,最終的4個函數的編號將分別是此處的索引值加上nBase的值,函數的索引值可以在調用了該動態鏈接庫的程序FirstWindows.exe的導入表數據中找到

    根據編號查找函數地址:

  • 定位到PE頭
  • 從PE文件中找到數據目錄表,表項的第一個雙字值是導出表的起始RVA
  • 從導出表的nBase字段得到起始序號
  • 函數編號減去起始序號得到的是函數在AddressOfFunctions中的索引號
  • 通過查詢AddressOfFunctions指定索引位置的值,找到虛擬地址
  • 將虛擬地址加上該動態鏈接庫在被導入到地址空間后的基地址,即為函數的真實入口地址
  • 提示:
    不建議使用編號查找函數地址。因為有很多的動態鏈接庫匯總標識的編號與對應的函數并不一致,通過這種方法找到的函數地址往往是錯誤的

    根據名字查找函數地址:

  • 定位到PE頭
  • 從PE文件中找到數據目錄表,表項的第一個雙字值是導出表的起始RVA
  • 從導出表中獲取NumberOfNames字段的值,以便構造一個循環,根據此值確定循環的次數
  • 從AddressOfNames字段指向的函數名稱數組的第一項開始,與給定的函數名字進行匹配;如果匹配成功,則記錄從AddressOfNames開始的索引號
  • 通過索引號再去檢索AddressOfNameOridinals數組,從同樣索引的位置找到函數的地址索引
  • 通過查詢AddressOfFuncs指定函數地址索引位置的值,找到虛擬地址
  • 將虛擬地址加上該動態鏈接庫在被導入到地址空間的基地址,即為函數的真實入口地址。
  • 舉例:
    從庫中獲得函數地址的API為GetProcAddress()函數,該API引用EAT來獲取指定API的地址。其過程大致如下:

  • 利用AddressOfName成員轉到“函數名稱數組”
  • “函數名稱數組”中存儲著字符串地址,通過比較(strcmp)字符串,查找指定的函數名稱(此時數組的索引稱為name_index)
  • 利用AddressOfNameOrdinals成員,轉到ordinal數組
  • 在ordinal數組中通過name_index查找相應ordinal值
  • 利用AddressOfFunctionis成員轉到“函數地址數組”(EAT)
  • 在“函數地址數組”中將剛剛求得的ordinal用作數組索引,獲得指定函數的起始地址
  • kernel32.dll中所有到處函數均有相應名稱,AddressOfNameOrdinals數組的值以index=ordinal的形式存在。但存在一部分dll中的導出函數沒有名稱,所以僅通過ordinal導出,從Ordinal值中減去IMAGE_EXPORT_DIRECTORY.Base 成員后得到一個值,使用該值作為“函數地址數組”的索引即可查找到相應函數的地址

    導出表的應用

    導出函數的覆蓋

    導出表編程中常見的技術是,不需要修改用戶程序,便能將用戶程序中調用的動態鏈接庫函數轉向或者實施代碼覆蓋,實現用戶程序的調用轉移。(這種技術在殺毒軟件對用戶程序防護過程中,針對這種滲透是無效的)

  • 修改導出結構中的函數地址
  • 覆蓋函數地址部分的指令代碼
  • 1. 修改導出結構中的函數地址

    直接利用二進制工具將 AddressOfFunctions索引1和2的地址(分別對應函數AnimateOpen和FadeInOpen)交換位置。

    僅通過函數調用RVA地址0x00001282和0x00001022交換位置,即可實現導出函數的覆蓋。

    需要注意的是,在使用導出函數地址覆蓋技術的時候,首先保證所涉及的兩個函數參數入口要一致,否則調用完成后棧不平衡。這將會導致應用程序調用失敗;其次,要求用戶對兩個函數的內部實現要有充分了解,使得地址轉向后,能夠保證應用程序在功能上可以全面兼容并允許良好

    注意:
    這種操作在實際操作中不贊成大家使用。

    覆蓋函數地址部分的指令代碼

    第二種常見的覆蓋技術,是將AddressOfFunctions指向的地址空間指令字節碼實施覆蓋。這種技術又衍生處兩種:

  • 暴力覆蓋,即將所有的代碼全部替換為新代碼,新代碼可能含有原來代碼的全部功能,也可能不包含原有代碼功能
  • 完美覆蓋,通過構造指令,實施新代碼和原代碼的共存和無遺漏運行。
  • 舉例:


    導出私有函數





    總結

    以上是生活随笔為你收集整理的PE结构导出表的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。