LUEVELSMEYER的PE文件格式

PE文件格式
(翻譯:QduWg,原作LUEVELSMEYER)
說明:希望本文能夠對初級入門CRACKER有一定幫助,翻譯存在疏漏或者不準確,希望來信指出。感謝您的指導!感謝看雪爲我們提供這個交流平臺,讓我們技術與時俱進!!
前言:
PE("portable executable")文件格式是針對MS windows NT, windows 95 and
win32s的可執行二進制代碼(DLLs and programs) 。在windows NT內, 驅動程序也是這個格式。也可以用於對象文件和庫。

這個格式是Microsoft設計的,並在1993經過TIS (tool interface standard)委員會(Microsoft, Intel, Borland, Watcom, IBM等)標準化了的。它基於在UNIX和VMS上運行的對象文件和可執行文件的COFF"common object file format"格式。

win32 SDK 包括一個頭文件<winnt.h>包括對PE格式的定義。我將提及成員名和定義。你也可能發現DLL文件"imagehelp.dll" 非常有用。它是NT的一部分,但文檔很少。它的一些函數在"Developer Network"被描述。

總覽:
在PE文件的開始,我們可以發現MSDOS執行部分("stub"); 這使得任何一個PE文件是有效的DOS執行文件。在DOS-stub之後是32位的魔數0x00004550(IMAGE_NT_SIGNATURE).然後是一個COFF格式的文件頭,指明在何種機器上運行,多少個節在裏面,連接的時間,是否是可執行文件或者DLL等。DLL和可執行文件的區別:DLL不能夠啓動,只可以被其他可執行文件使用,一個可執行文件不能夠連接到另一個可執行文件。

接着,我們看到一個可選文件頭optional header(雖然叫“可選”,它實際上一直存在)。
COFF把可選文件頭用於庫,不用於目標文件。這裏告訴我們文件如何被調入:起始地址,預留堆棧數,數據段尺寸。

一個有趣的部分是尾巴上的數組數據目錄data directories,這些目錄包含指向節內數據的指針。例如,如果文件有輸出目錄,可以在數組成員IMAGE_DIRECTORY_ENTRY_EXPORT內發現一個指針指向那個目錄(目錄描述結構->THUNKDATA結構->BYNAME結構)。他將指向一個節。

在頭的後面是節頭,實際上,節的內容就是真正需要運行一個程序所需要的東西,所有的頭和目錄成員就是幫你找到它。每個節有幾個標誌:對齊,包含的數據類型(初始化數據等),是否可以共享等,及數據自身。多數節含有一個或多個通過“可選頭”內的數據目錄項引用的目錄。沒有目錄類型的內容是初始化數據或者可執行代碼。(節是物理意義上的內容組織,目錄是邏輯意義上的內容組織,兩者互相配合才能找到需要的東西。節是存儲內容的地方,區域的安排,目錄是如何對裏面東西進行查找,目的是尋找裏面的內容)
  +-------------------+
  | DOS-stub       |
  +-------------------+
  | file-header     |
  +- - - - - - - - - -+
  | optional header   |
  |- - - - - - - - - -|
  |             |
  | data directories |
  |             |
  +-------------------+
  |             |
  | section headers   |
  |             |
  +-------------------+
  |             |
  | section 1       |
  |             |
  +-------------------+
  |             |
  | section 2       |
  |             |
  +-------------------+
  |             |
  | ...           |
  |             |
  +-------------------+
  |             |
  | section n       |
  |             |
  +-------------------+
DOS-stub and Signature
----------------------
DOS STUB的概念在16位WINDOWS可執行文件內就已經被熟知了,STUB是用於OS/2可執行文件,自解壓文檔和其他程序。對於PE文件,它是DOS2兼容可執行文件,總是包含100字節內容,輸出一個錯誤信息:比如"this program needs windows NT".
你認識到一個DOSSTUB通過驗證DOS-header,就是一個IMAGE_DOS_HEADER結構,前兩個字節必須使"MZ"(有一個定義針對這個WORD,IMAGE_DOS_SIGNATURE )。你通過尾部的'e_lfanew' 給出的偏移量所確定的簽名區別一個PE文件。對於PE文件,它是一個32位,按照8字節對齊邊界。其值0x00004550由IMAGE_NT_SIGNATURE 定義.

IMAGE_NT_HEADERS STRUCT
Signature       DWORD             ?
FileHeader     IMAGE_FILE_HEADER     <>
OptionalHeader   IMAGE_OPTIONAL_HEADER32 <>
IMAGE_NT_HEADERS ENDS

文件頭File Header
-----------
IMAGE_FILE_HEADER STRUCT
Machine           WORD   ?
NumberOfSections     WORD   ?
TimeDateStamp       DWORD   ?
PointerToSymbolTable DWORD   ?
NumberOfSymbols     DWORD   ?
SizeOfOptionalHeader WORD   ?
Characteristics     WORD   ?
IMAGE_FILE_HEADER ENDS

要得到IMAGE_FILE_HEADER,確認DOS頭的前2個字節"MZ",然後找到'e_lfanew'成員,然後從文件開始跳過許多字節,驗證你找到的簽名。文件頭作爲一個IMAGE_FILE_HEADER結構,就從它後面開始。從上到下描述其成員。

第1:Machine, 16位值,指明可執行文件所需要的系統。已知合法值如下:
  IMAGE_FILE_MACHINE_I386
  0x014c Intel 80386 處理器。
  0x014d Intel 80486處理器
  0x014e Pentium 處理器
  0x0160 R3000 (MIPS)處理器
  IMAGE_FILE_MACHINE_R3000 (0x162) R3000 (MIPS)處理器
  IMAGE_FILE_MACHINE_R4000 (0x166) R4000 (MIPS)處理器
  IMAGE_FILE_MACHINE_R10000 (0x168)R10000 (MIPS)處理器
  IMAGE_FILE_MACHINE_ALPHA (0x184) DEC Alpha AXP處理器
  IMAGE_FILE_MACHINE_POWERPC (0x1F0)IBM Power PC處理器
第2:NumberOfSections,16位值,它是跟隨於頭後面的節數。我們在後面討論。
第3:TimeDateStamp,32位值,文件創建的時間。可以通過該值區分不同的文件版本。時間戳用於綁定輸入目錄,後面講到。有些連接器設置該值爲荒唐的值。
第3:PointerToSymbolTable和NumberOfSymbols,都是32位的。用於調試信息。一般都是0。
第4:SizeOfOptionalHeader,16位,是IMAGE_OPTIONAL_HEADER的尺寸.可以用它確認PE文件結構的正確性。
第5:Characteristics,16位值,包括一個標誌集合,多數只對目標文件和庫有效。
  Bit 0 (IMAGE_FILE_RELOCS_STRIPPED) 如果文件內沒有重定位信息該位置1。這裏指的是每個節內的重定位信息。不用於可執行文件,可執行文件的重定位信息在後面提到的base relocation目錄。
  Bit 1 (IMAGE_FILE_EXECUTABLE_IMAGE) 如果文件是可執行的則置1,例如不是一個目標文件或者庫文件。如果連接器試圖創建可執行文件,但由於某種原因失敗了,也置1。
  Bit 2 (IMAGE_FILE_LINE_NUMS_STRIPPED)如果行數信息剝離,置1,對可執行文件無效。
  Bit 3 (IMAGE_FILE_LOCAL_SYMS_STRIPPED) 如果沒有本地符號信息該位置1。對可執行文件無效。
  Bit 4 (IMAGE_FILE_AGGRESIVE_WS_TRIM) 如果操作系統被假定通過頁換出搶佔式修剪進程的工作集(進程使用的內存數),該位置1。
  Bits 7 (IMAGE_FILE_BYTES_REVERSED_LO)和15(IMAGE_FILE_BYTES_REVERSED_HI) 如果文件的的endianess不是機器期望的,則置1,於是讀之前必須交換字節。對可執行文件不可靠。
  Bit 8 (IMAGE_FILE_32BIT_MACHINE) 如果機器被期望是32位機器,置1。
  Bit 9 (IMAGE_FILE_DEBUG_STRIPPED)如果沒有調試信息在文件內,置1。對可執行文件無效。
  Bit 10 (IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP) 如果程序不能夠從可移動媒體比如軟盤或光驅,置1。操作系統建議拷貝文件到交換文件然後執行。
  Bit 11 (IMAGE_FILE_NET_RUN_FROM_SWAP) 如果不能夠才網絡運行,置1。操作系統建議拷貝文件到交換文件然後執行。
  Bit 12 (IMAGE_FILE_SYSTEM) 如果文件是類似驅動程序的系統文件,置1。對執行文件無效。
  Bit 13 (IMAGE_FILE_DLL) 如果文件是DLL,置1.
  Bit 14 (IMAGE_FILE_UP_SYSTEM_ONLY) 如果文件不是爲多處理器設計的,置1 。

相對虛擬地址Relative Virtual Addresses
--------------------------
PE格式使用所謂的RVA。用於描述內存地址,如果你不知道基地址的話。需要你加上基地址得到線性地址。基地址是PE映像加載的地址。例如:假如可執行文件加載到0x400000,可執行文件的RVA是0x1560. 有效執行起始地址爲0x401560.如果被加載到0x100000,則執行起始位置在0x101560.

情況變的複雜起來,由於節不必按照加載的映像那樣對齊。例如,節一般按照512字節對齊。加載的映像可能按照4096字節對齊。參看'SectionAlignment' and 'FileAlignment' 。對齊的意思就是地址值=對齊長度的倍數。

於是爲了找到一個特殊的RVA指向的信息,你必須計算偏移量好象文件被加載一樣。假如知道執行起點在RVA 0x1560, 想從這反彙編代碼。要找到文件內的地址,你必須找出在RAM內的按照4096對齊的節,".code"節自內存RVA 0x1000開始,16384字節長,你知道RVA 0x1560的偏移量在那個節內是0x560.找出節在文件內按照512字節對齊,且".code"從0x800開始,那麼在文件內的代碼執行起點是0x800+0x560=0xd60。
然後反彙編,並發現一個存取地址0x1051d0處的變量.線性地址在加載執行文件時重新分配,並給出優先加載地址。你發現優先加載地址是0x100000,於是我們處理RVA 0x51d0. 這是一個開始於RVA 0x5000的數據區,2048字節長。它開始於文件偏移量0x4800.變量可以在文件偏移量0x4800+0x51d0-0x5000=0x49d0處發現。

可選頭Optional Header
---------------
緊跟在文件頭的後面是IMAGE_OPTIONAL_HEADER,儘管名字是可選,實際一直存在。包含關於如何精確處理PE文件的信息。從上到下介紹成員。
IMAGE_OPTIONAL_HEADER32 STRUCT
Magic                 WORD     ?
MajorLinkerVersion         BYTE     ?
MinorLinkerVersion         BYTE     ?
SizeOfCode             DWORD     ?
SizeOfInitializedData       DWORD     ?
SizeOfUninitializedData     DWORD     ?
AddressOfEntryPoint       DWORD     ?
BaseOfCode             DWORD     ?
BaseOfData             DWORD     ?
ImageBase               DWORD     ?
SectionAlignment         DWORD     ?
FileAlignment           DWORD     ?
MajorOperatingSystemVersion   WORD     ?
MinorOperatingSystemVersion   WORD     ?
MajorImageVersion         WORD     ?
MinorImageVersion         WORD     ?
MajorSubsystemVersion       WORD     ?
MinorSubsystemVersion       WORD     ?
Win32VersionValue         DWORD     ?
SizeOfImage             DWORD     ?
SizeOfHeaders           DWORD     ?
CheckSum               DWORD     ?
Subsystem               WORD     ?
DllCharacteristics         WORD     ?
SizeOfStackReserve         DWORD     ?
SizeOfStackCommit         DWORD     ?
SizeOfHeapReserve         DWORD     ?
SizeOfHeapCommit         DWORD     ?
LoaderFlags             DWORD     ?
NumberOfRvaAndSizes       DWORD     ?
DataDirectory           IMAGE_DATA_DIRECTORY IMAGE_NUMBEROF_DIRECTORY_ENTRIES dup(<> )
IMAGE_OPTIONAL_HEADER32 ENDS
IMAGE_OPTIONAL_HEADER equ <IMAGE_OPTIONAL_HEADER32>

第1個16位字是'Magic',總是0x010b.

下面2個字節是連接器的版本號'MajorLinkerVersion'和'MinorLinkerVersion',這些值都不可靠,不能總是妥當的反映連接器版本。有些連接器不設置該域。

下面3個longwords(32位)指定執行代碼尺寸('SizeOfCode'),初始化數據尺寸
'SizeOfInitializedData', 所謂的數據段"data segment", 未初始化數據尺寸
'SizeOfUninitializedData',所謂的"bss segment".這些數值也不可靠。

往下一個32位的RVA.是入口點的偏移量。('AddressOfEntryPoint').執行從此開始。

下面2個32位是可執行代碼('BaseOfCode')和初始化數據('BaseOfData')的RVAs 我們對它沒有興趣,因爲可以通過節來查看更可靠的信息。非初始化數據沒有RVA。

下面是一個32位值,ImageBase'作爲整個文件的優先加載地址,包括所有頭在內。該值總是
64KB的倍數,文件已經被連接器重定位,如果文件能夠真正加載到這個地址,加載器不必重定位文件。如果另一個映像已經被加載到那個地址,則優先地址不可使用。這種情況下,映像被加載到其他地址,需要重定位。如果映像是DLL,還有更多結果,因爲"bound imports"不再有效,需要對使用DLL的執行文件進行修正。參見'import directory' 。

下面2個32位是當映像文件加載後,PE文件的節在內存內的對齊,'SectionAlignment', 以及在文件內的對齊'FileAlignment'. 一般文件對齊是512,節對齊是4096.

下面2個16位的字是期望的操作系統版本,'MajorOperatingSystemVersion'和 'MinorOperatingSystemVersion'。

下面2個16位的字是期望的可執行文件版本,'MajorImageVersion'和
'MinorImageVersion'. 許多連接器不正確設置這些信息。

下面2個16位的字是期望的子系統版本,'MajorSubsystemVersion和MinorSubsystemVersion. 這個必須是Win32版本或者POSIX版本。該版本需要正確提供,因爲它被檢查並使用。如果程序是Win32-GUI並運行在NT4,子系統版本不是4.0,對話框不是3D效果。

然後是Win32VersionValue,32位。大部分情況下是0。

下面是32位的映像需要的內存數量'SizeOfImage'.是所有的頭和節的總和,如果節已經對齊。它是給加載器的線索,需要多少頁加載映像。

下面一個是32位的所有頭的總和,包括數據目錄和節頭。'SizeOfHeaders'.它也是才文件開始到第一節的偏移量。

然後是32位的校驗碼'CheckSum'.對當前版本的NT,只校驗映像是否是NT驅動程序。對於其他可執行文件類型,不必提供這個碼,可能爲0。

然後是16的子系統Subsystem'表明在什麼系統上運行:
  IMAGE_SUBSYSTEM_NATIVE (1)執行文件不需要子系統,用於驅動程序。
  IMAGE_SUBSYSTEM_WINDOWS_GUI (2)映像是Win32圖形程序可以打開控制檯  
  IMAGE_SUBSYSTEM_WINDOWS_CUI (3)映像是Win32控制檯程序,可以得到缺省控制檯。
  IMAGE_SUBSYSTEM_OS2_CUI (5)映像是OS/2 控制檯,程序是OS/2格式。  
  IMAGE_SUBSYSTEM_POSIX_CUI (7)映像使用POSIX控制檯子系統
Windows 95可執行文件總是使用Win32 subsystem,於是合法值是2和3。

下面是16位,DllCharacteristics,表明是否是DLL,如果0位置1,DLL被通知進程結合。位1置1,DLL被通知線程脫離。位2置1,DLL被通知線程結合。位3置1,DLL被通知進程脫離。

下面4個 32 位預留堆棧大小'SizeOfStackReserve',提交的堆棧大小'SizeOfStackCommit',預留的堆的大小'SizeOfHeapReserve'和提交的的堆的的大小'SizeOfHeapCommit'.

預留數量是地址空間不是真實的RAM,程序啓動時,提交的數量是真正的分配的內存。這個值也是堆和棧根據需要增長的一個數量。
例如:一個程序預留1 MB的堆並提交的堆時64KB,該堆就從64KB開始,並保證可以加大到1MB.堆將以64KB塊增長。該堆在這裏是主要堆,默認堆。一個進程可以創建多個堆如果需要的話。棧是第一個線程的棧,進程可以創建許多線程,每個都有自己的堆棧,DLLs 沒有棧或者堆,於是在其映像內該值被忽略。

下面是32位的LoaderFlags, 沒有用。

然後是32位的NumberOfRvaAndSizes,在隨後的目錄內的有效項目數。最好使用
IMAGE_NUMBEROF_DIRECTORY_ENTRIES,即16。

下面是具有IMAGE_NUMBEROF_DIRECTORY_ENTRIES(16)個成員的IMAGE_DATA_DIRECTORYs結構數組.

IMAGE_DATA_DIRECTORY STRUCT
VirtualAddress   DWORD     ?
isize         DWORD     ?
IMAGE_DATA_DIRECTORY ENDS

每個目錄描述了節內特定信息位置,32 bits RVA VirtualAddress 和尺寸32 bit,各個成員索引如下(括號內爲索引值):
  IMAGE_DIRECTORY_ENTRY_EXPORT (0)輸出符號目錄用於DLL  
  IMAGE_DIRECTORY_ENTRY_IMPORT (1)輸入符號目錄  
  IMAGE_DIRECTORY_ENTRY_RESOURCE (2)資源目錄  
  IMAGE_DIRECTORY_ENTRY_EXCEPTION (3)異常目錄  
  IMAGE_DIRECTORY_ENTRY_SECURITY (4)安全目錄  
  IMAGE_DIRECTORY_ENTRY_BASERELOC (5)重定位表  
  IMAGE_DIRECTORY_ENTRY_DEBUG (6)調試目錄
  IMAGE_DIRECTORY_ENTRY_COPYRIGHT (7)描述版權串  
  IMAGE_DIRECTORY_ENTRY_GLOBALPTR (8)機器值  
  IMAGE_DIRECTORY_ENTRY_TLS (9)Thread local storage目錄
  IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG (10)Load configuration 目錄  
  IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (11)Bound import directory目錄  
  IMAGE_DIRECTORY_ENTRY_IAT (12)Import Address Table輸入地址表目錄

例如,如果我們找到索引7的2個值0x12000和33, 加載地址是0x10000, 我們知道版權數據是在 0x10000+0x12000,版權字數爲33。如果一個特定類型的目錄沒有被使用,地址和尺寸都爲0。

節目錄Section directories
-------------------

節包含2個部分:節頭IMAGE_SECTION_HEADER,節數據。在數據目錄之後,我們看到一個具有NumberOfSections個節頭成員的數組,按RVA排序。

節頭包括:
IMAGE_SECTION_HEADER STRUCT
  Name1 db IMAGE_SIZEOF_SHORT_NAME dup(?)
  union Misc
    PhysicalAddress dd ?
    VirtualSize dd     ?
  ends
  VirtualAddress dd     ?
  SizeOfRawData dd     ?
  PointerToRawData dd   ?
  PointerToRelocations dd ?
  PointerToLinenumbers dd ?
  NumberOfRelocations dw ?
  NumberOfLinenumbers dw ?
  Characteristics dd     ?
IMAGE_SECTION_HEADER ENDS


IMAGE_SIZEOF_SHORT_NAME(8)個字節的數組,組成節的名字。如果所有8個字節被用掉,沒有0做爲結尾。典型的名字如".data"或者".text"或者".bss". 沒有必要前導'.',可以是是"CODE"或 "IAT" .注意名字不全部跟節內容有關。一個".code"節可能或沒有可能包括可執行代碼,可能只包括輸入地址表。可能包含代碼和地址表和初始化數據。要找到在節內的信息,必須通過“可選頭”內的數據目錄查找他們。不要依賴名字,不要假定節的原始數據起始於節的開始。

下面一個成員是'PhysicalAddress'和'VirtualSize'的32位聯合體. 在目標文件,該地址是內容被重定位的地址,在可執行文件內是內容的尺寸。實際上該域好像沒有被使用,有的鏈接器填入尺寸有的鏈接器填入地址,有的鏈接器填入0。
下一個成員是'VirtualAddress',32位,保存當節的數據加載入內存時的RVA。
然後是32位'SizeOfRawData',是四捨五入到下個FileAlignment'倍數的大小。
下一個是'PointerToRawData',32位,它是從文件起始到節數據的偏移量。如果是0,節數據不包含在文件內,在加載時被確定。
然後是'PointerToRelocations',32位,和'PointerToLinenumbers',32位,'NumberOfRelocations',16位,'NumberOfLinenumbers',16位.所有這些信息僅僅用於目標文件。可執行文件有一個特殊的基準重定位目錄,如果存在行號信息,一般包含在特殊目的的調試段或其他。
最後一個是32位的'Characteristics',它是一組標誌描述如何處理節的內存。
  bit 5 (IMAGE_SCN_CNT_CODE),置1,節內包含可執行代碼。  
  bit 6 (IMAGE_SCN_CNT_INITIALIZED_DATA)置1,節內包含的數據在執行前是確定的。  
  bit 7 (IMAGE_SCN_CNT_UNINITIALIZED_DATA) 置1,本節包含未初始化的數據,執行前即將被初始化爲0。一般是BSS.
  bit 9 (IMAGE_SCN_LNK_INFO) 置1,節內不包含映象數據除了註釋,描述或者其他文檔外,是一個目標文件的一部分,可能是針對鏈接器的信息。比如哪個庫被需要。
  bit 11 (IMAGE_SCN_LNK_REMOVE) 置1,在可執行文件鏈接後,作爲文件一部分的數據被清除。
  bit 12 (IMAGE_SCN_LNK_COMDAT) 置1,節包含公共塊數據,是某個順序的打包的函數。
  bit 15 (IMAGE_SCN_MEM_FARDATA) 置1,不確定。
  bit 17 (IMAGE_SCN_MEM_PURGEABLE) 置1,節的數據是可清除的。
  bit 18 (IMAGE_SCN_MEM_LOCKED) 置1,節不可以在內存內移動。
  bit 19 (IMAGE_SCN_MEM_PRELOAD)置1, 節必須在執行開始前調入。
  Bits 20 to 23指定對齊。一般是庫文件的對象對齊。
  bit 24 (IMAGE_SCN_LNK_NRELOC_OVFL) 置1, 節包含擴展的重定位。
  bit 25 (IMAGE_SCN_MEM_DISCARDABLE) 置1,進程開始後節的數據不再需要。
  bit 26 (IMAGE_SCN_MEM_NOT_CACHED) 置1,節的 數據不得緩存。
  bit 27 (IMAGE_SCN_MEM_NOT_PAGED) 置1,節的 數據不得交換出去。
  bit 28 (IMAGE_SCN_MEM_SHARED) 置1,節的數據在所有映象例程內共享,如DLL的初始化數據。
  bit 29 (IMAGE_SCN_MEM_EXECUTE) 置1,進程得到“執行”訪問節內存。
  bit 30 (IMAGE_SCN_MEM_READ) 置1,進程得到“讀出”訪問節內存。
  bit 31 (IMAGE_SCN_MEM_WRITE)置1,進程得到“寫入”訪問節內存。

在節頭後面我們看到節自身,他們在文件內按照'FileAlignment'字節數對齊。即在可選頭和每個節的後面將添加0。節按照他們的RVAs排序.當加載到RAM,節的對齊按照SectionAlignment。

例如:一個可選頭在偏移量981處結束,文件對齊爲512,第一個節起始於1024。你可以藉助'PointerToRawData'或者'VirtualAddress'找到節,不必用對齊找節。(節頭哪裏去了?此段有問題!!)

  +-------------------+
  | DOS-stub       |
  +-------------------+
  | file-header     |
  +-------------------+
  | optional header   |
  |- - - - - - - - - -|
  |             |----------------+
  | data directories |           |
  |             |           |
  |(RVAs to direc-   |-------------+ |
  |tories in sections)|         | |
  |             |---------+   | |目錄在節內的相對地址
  |             |       |   | |
  +-------------------+       |   | |
  |             |-----+   |   | |
  | section headers   |   |   |   | |
  | (RVAs to section |--+ |   |   | |節邊界的相對地址
  | borders)       | | |   |   | |
  +-------------------+<-+ |   |   | |
  |             |   | <-+   | |
  | section data 1   |   |     | |節數據
  |             |   | <-----+ |
  +-------------------+<----+       |
  |             |           |
  | section data 2   |           |
  |             | <--------------+
  +-------------------+

每個節都有一個節頭,每個數據目錄將指向一個節(幾個數據目錄可能指向同一個節),有的節可能沒有數據目錄指向它。

節的原始數據Sections' raw data
------------------
概要-------
所有的節調入內存後按照'SectionAlignment'對齊,FileAlignment'是節在文件內對齊字節數。 節由節頭內的項目來描述,可以通過'PointerToRawData'在文件內找到節,在內存內通過'VirtualAddress'找到節,長度是'SizeOfRawData'.
根據他們包含的內容,有幾種節。一般至少有一個數據目錄指向的內容保存在一個節內。

代碼節code section
------------
本節至少含有一個標誌位'IMAGE_SCN_CNT_CODE', 'IMAGE_SCN_MEM_EXECUTE' and
'IMAGE_SCN_MEM_READ'的集合,並且“可選頭”的成員'AddressOfEntryPoint'指向這個節內的某個地方。“可選頭”的成員'BaseOfCode'將指向這個節的開始,如果把非代碼放在代碼之前,也有可能指向後面某個地方。一般除了可執行代碼,沒有其他東西,一般只有一個代碼節。一般的名字如:".text", ".code", "AUTO"。

數據節data section
------------
本節包含初始化過的靜態變量,例如 "static int i = 5;".他含有這些位'IMAGE_SCN_CNT_INITIALIZED_DATA','IMAGE_SCN_MEM_READ'及'IMAGE_SCN_MEM_WRITE' 等.有的鏈接器把常量數據放在沒有可寫標誌位的節內。如果部分數據是共享的,或者有其他特性的話,節將包含更多的特徵位集。節一般在'BaseOfData'到'BaseOfData'+'SizeOfInitializedData'的範圍內.典型名字如:'.data', '.idata', 'DATA'。

bss section
-----------
還有未初始化的數據,例如"static int k;"該節的'PointerToRawData'爲0,表明其內容不在文件內,特徵位'IMAGE_SCN_CNT_UNINITIALIZED_DATA'指明所有內容必須在加載時間置0。這意味着有節頭,但沒有節在文件內,該節被加載器創建,並且包含全0字節。其長度是'SizeOfUninitializedData'.典型名字如'.bss', 'BSS'。

有的節數據沒有被數據目錄指向,其內容和結果被編譯器支持,不是被鏈接器支持。堆棧段和堆段不是可執行文件的節,但是被加載器創建。其大小爲optional header內的stacksize和heapsize
.

版權copyright
---------
開始於一個簡單的目錄'IMAGE_DIRECTORY_ENTRY_COPYRIGHT'.其內容是一個版權或者一個非0結尾的串,象"Gonkulator control application, copyright (c) 1848 Hugendubel & Cie".
該串被使用命令行或者描述文件提供給鏈接器。該串不是必須的,可以被捨棄。他不是可寫的,實際上程序不必訪問他。鏈接器將找出是否有一個可以捨棄的不可寫的段,創建一個名字爲'.descr'的段。然後把串填入該段,讓版權目錄指針指向他。該節的特徵字'IMAGE_SCN_CNT_INITIALIZED_DATA' 必須置1。

輸出符號exported symbols
----------------
下面一個簡單的事情是輸出目錄'IMAGE_DIRECTORY_ENTRY_EXPORT'. 該目錄是DLL內的典型目錄。他包括輸出函數的入口點,輸出對象的地址等。該節必須是初始化數據和可讀的。不可以是可廢棄的,因爲進程可能在運行時間調用"GetProcAddress()"找出函數的入口。該節一般稱爲'.edata' . 它一般被併入其他節。
IMAGE_EXPORT_DIRECTORY STRUCT
Characteristics       DWORD     ?
TimeDateStamp         DWORD     ?
MajorVersion         WORD     ?
MinorVersion         WORD     ?
nName               DWORD     ?
nBase               DWORD     ?
NumberOfFunctions       DWORD     ?
NumberOfNames         DWORD     ?
AddressOfFunctions     DWORD     ?
AddressOfNames         DWORD     ?
AddressOfNameOrdinals   DWORD     ?
IMAGE_EXPORT_DIRECTORY ENDS

輸出表結構('IMAGE_EXPORT_DIRECTORY')包含一個頭和輸出數據,即符號名,序號及到入口點的偏移。
首先,是一個32位的'Characteristics',一般爲0。然後是32位的'TimeDateStamp',輸出表創建的時間,並非總是有效的,有些鏈接器置0。然後是2個16位的版本信息MajorVersion和MinorVersion,一般置0.

下面是32位的'Name',這是一個指向以0結尾的字符串的RVA,名字是必須的,防止DLL文件改名。
然後是32位的'Base'.下一個是32位的輸出項目總數'NumberOfFunctions'.除了序數外,也可能以名字輸出。下面一個是32位的輸出名字的總數'NumberOfNames'.在多數情況,每個輸出項有一個確切的名字,將以名字使用它。但一個項目可能有多個名字,或者沒有名字,這種情況只能夠以序號來訪問,不贊成只以序號輸出,帶來維護問題。

下面32位是AddressOfFunctions,是輸出項目列表的RVA,它指向具有'NumberOfFunctions'個數元素的數組。每個成員是輸出函數或變量的RVA。該列表有兩個怪事,輸出的RVA可能是0,他沒有被使用。第二如果RVA指向包含輸出目錄的節,這是一個轉發輸出。轉發輸出是一個指向另一個文件輸出的指針。這樣就使用另一個文件內的被指向的輸出。輸出序號是AddressOfFunctions數組的索引+上面提到的BASE值。多數情況下,'Base'=1,意思是第一個輸出的序號爲1,第二個是2。

下面是指向其成員爲指向符號名的32位RVA的數組'AddressOfNames',和一個指向16位序數的數組的32位RVA AddressOfNameOrdinals'. 兩個數組都含有'NumberOfNames'個元素.符號名可能全部丟失,所以'AddressOfNames'是0. 否則被指向的數組並行運行,'AddressOfNames'數組包含指向0結尾的輸出名的RVAs。名字都按照字母順序保存,便於高效檢索名字。根據PE規範,AddressOfNameOrdinals數組含有相應名字的序數,但實際上含有AddressOfFunctions數組的索引。序數=BASE+INDEX。

上述3表的示意圖:

  AddressOfFunctions 函數地址指向一個地址數組
      |
      |
      v
  exported RVA with ordinal 'Base' //帶有序號的輸出函數的RVA
  exported RVA with ordinal 'Base'+1
  ...
  exported RVA with ordinal 'Base'+'NumberOfFunctions'-1

  AddressOfNames             AddressOfNameOrdinals
      |                     |
      |                     |
      v                     v
  RVA to first name       <-> Index of export for first name
  RVA to second name       <-> Index of export for second name
  ...                   ...
  RVA to name 'NumberOfNames' <-> Index of export for name 'NumberOfNames'
名字地址指向名字RVA數組           “名字序數地址”指向名字序數數組

要按照序數找出輸出符號:減去'Base'得到索引,跟隨AddressOfFunctions RVA找到輸出數組,用索引定位輸出符號RVA所在元素。如果沒有指向輸出節,你就完成任務了。否則,如果指向一個描述輸出DLL名字和序數的串,你必須查找被轉發的輸出項。
要按照名字找出輸出符號,沿着AddressOfNames'RVA,找到指向包含輸出名字的RVAs的數組,其中每個RVA指向一個名字。搜索名字,使用名字的索引在AddressOfNameOrdinals數組得到與相應名字對應的16位數(按照PE規範,此數值爲輸出函數的序號,需要減去BASE得到索引)。根據經驗,這就是索引,不是序號,不必減去BASE,直接使用該索引在'AddressOfFunctions'數組找出輸出函數的的RVA或者是一個指向轉發串。

輸入表imported symbols
----------------
當編譯器發現一個調用的函數在其他可執行文件(一般是DLL)內,它將不知道這個情況,簡單輸出一個調用符號的指令,其地址由鏈接器添上。鏈接器使用輸入庫查詢哪個DLL哪個符號被引入。爲所有引入的符號產生STUB,每個由一個跳轉指令構成,STUB是真正的調用目標,這些跳轉指令真正跳到一個從地址表內取出的地址。在複雜的應用中,當"__declspec(dllimport)" 被使用的時候,編譯器知道函數被引入,並輸出一個對輸入表內的地址的調用。無論如何,DLL內的函數地址總是必須的,來自輸出DLL的輸出目錄的地址被提供給加載器,加載器知道哪個符號需要查找,通過搜索輸入目錄修正地址。下面是一個例子:

一個帶有/不帶有聲明__declspec(dllimport)的調用如下:

源程序:
    int symbol(char *);
    __declspec(dllimport) int symbol2(char*);
    void foo(void)
    {
        int i=symbol("bar");
        int j=symbol2("baz");
    }
 
彙編程序:
    ...
    call _symbol           ; 無 declspec(dllimport)
    ...
    call [__imp__symbol2]     ; 有 declspec(dllimport)
    ...
第一種情況,沒有聲明,編譯器不知道'_symbol'在一個DLL,於是鏈接器必須提供此函數。因爲函數不在那裏,它將提供一個STUB函數對應輸入符號,作爲一個間接跳轉。所有的輸入STUB的集合稱作轉換區或跳板,因爲跳到那裏爲的是跳到另一個地方。該跳板典型的位於代碼區,不是輸入目錄的一部分。每個函數的STUB是一個跳轉到DLL的實際函數的JUMP,跳板區看起來如下:
  _symbol:     jmp [__imp__symbol]
  _other_symbol: jmp [__imp__other__symbol]
  ...
這就是意味着,如果你使用不帶聲明的輸入符號,鏈接器就產生包含間接跳轉的跳板區,如果指定了聲明"__declspec(dllimport)", 編譯器就自己做這個工作,跳板區是不必要的。它也意味着,如果你引入變量或其他東西,你必須指定"__declspec(dllimport)",因爲帶有JMP的STUB只適合於函數,不適合於變量等。

在任何情況下,符號的地址存在於一個地址'__imp_x'.所有這些地址組成所謂的輸入地址表,通過DLL的引入庫提供給鏈接器。輸入地址表如下:
  __imp__symbol:   0xBEFFA100
  __imp__symbol2: 0x40100
  __imp__symbol3: 0x300100
  ...

輸入地址表是數據目錄的一部分,被IMAGE_DIRECTORY_ENTRY_IAT目錄指針指向。一些鏈接器不設置這個目錄項,照樣工作,顯然加載器可以不使用目錄IMAGE_DIRECTORY_ENTRY_IAT就可以解決問題。鏈接器不知道這個表內的地址,鏈接器插入啞元(即函數名的RVAs),在加載時,加載器用輸出DLL的輸出目錄修補這些啞元。注意這是C規範,有其他程序編譯環境不需要輸入庫。他們需要產生輸入地址表,C編譯器傾向於使用輸入庫,方便於它們的鏈接器。

我們要看一下一個輸入目錄如何建立,於是加載器纔可以使用。
輸入目錄必須駐留在一個節,該節是初始化的和可讀的,輸入目錄是一個IMAGE_IMPORT_DESCRIPTORs數組,每個DLL都使用一個元素,該列表中止於一個全0的數組元素。
一個IMAGE_IMPORT_DESCRIPTOR是一個具有如下元素的結構:

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

  OriginalFirstThunk 32位RVA ,指向一個以0結尾的IMAGE_THUNK_DATAs數組。每個成員描述一個輸入函數。
  TimeDateStamp 32位時間戳。
  ForwarderChain 輸入函數第一個轉發鏈的32位索引
  Name 指向DLL名字的32位RVA。    
  FirstThunk 指向IMAGE_THUNK_DATAs數組的32位RVA,它是輸入地址表的一部分,將發生變化。Thunk的意思就是查找,虛實變換的意思。

IMAGE_THUNK_DATA32 STRUCT
  union u1
    ForwarderString dd ?
    Function dd       ?
    Ordinal dd       ?
    AddressOfData dd   ?
  ends
IMAGE_THUNK_DATA32 ENDS

以下是VC98,WINNT.h內的定義:
typedef struct _IMAGE_THUNK_DATA64 {
  union {
    PBYTE ForwarderString;
    PDWORD Function;
    ULONGLONG Ordinal;
    PIMAGE_IMPORT_BY_NAME AddressOfData;
  } u1;
} IMAGE_THUNK_DATA64;
typedef IMAGE_THUNK_DATA64 * PIMAGE_THUNK_DATA64;

於是每個IMAGE_IMPORT_DESCRIPTOR成員給出輸出DLL的名字,2個RVAs指向IMAGE_THUNK_DATAs數組,數組的最後一個成員全部填充0表示結束。每個IMAGE_THUNK_DATA成員有一個指向IMAGE_IMPORT_BY_NAME的RVA,用以描述輸入函數。有趣的是這些數組並行運行,他們指向相同的
IMAGE_IMPORT_BY_NAMEs(按名輸入).

這是IMAGE_IMPORT_DESCRIPTOR必須的內容:

  OriginalFirstThunk     FirstThunk
        |             |
        |             |
        |             |
        V             V
        0-->   func1   <--0
        1-->   func2   <--1
        2-->   func3   <--2
        3-->   foo     <--3
        4-->   mumpitz   <--4
        5-->   knuff   <--5
        6-->0         0<--6     /* 最後的RVA是0 */

IMAGE_IMPORT_BY_NAME STRUCT
  Hint dw   ?
  Name1 db   ?
IMAGE_IMPORT_BY_NAME ENDS

中間的名字是要討論的IMAGE_IMPORT_BY_NAMEs.每個包括一個16位的hint,一個沒有確定字節數的name,hint是一個輸出DLL函數名字表(見前面輸出目錄)的索引,索引處的名字被測試,如果不匹配,則用二進制搜索來找到名字。有的鏈接器只是簡單置1,或者其他任意數字。只是導致第一次搜索企圖失敗,然後強迫使用二進制搜索名字。
如果想從DLL "knurr"查找輸入函數"foo"的信息,首先數據目錄內找到IMAGE_DIRECTORY_ENTRY_IMPORT項,得到RVA,在raw section data找到那個地址,現在我們有了一個數組IMAGE_IMPORT_DESCRIPTORs. 通過檢查由NAME指向的的字符串,得到與DLL "knurr"有關的數組成員。當你發現了正確的IMAGE_IMPORT_DESCRIPTOR,沿着它的OriginalFirstThunk得到一個指針指向一個IMAGE_THUNK_DATAs數組;檢查每個RVAs找到"foo".

爲什麼有兩個指針列表指向IMAGE_IMPORT_BY_NAMEs?因爲運行時,程序不需要輸入函數名,需要的是地址,這裏是地址表再次出現的地方,加載器在每個涉及的DLL輸出目錄內查找輸入符號,使用DLL入口點的線性地址替換掉FirstThunk列表內的IMAGE_THUNK_DATA元素。記住帶有標號"__imp__symbol的地址列表、被數據目錄項
IMAGE_DIRECTORY_ENTRY_IAT指向的輸入地址表,也確切地被'FirstThunk'指向. (假如輸入來自幾個不同的DLL,輸入地址表由所有DLL的'FirstThunk'數組構成,目錄項IMAGE_DIRECTORY_ENTRY_IAT可能丟失,輸入依然工作的很好。'OriginalFirstThunk'數組保持不變,可以通過它查找輸入名的的原始列表。

現在輸入已經用正確線性地址打了補丁,如下所示:
  OriginalFirstThunk     FirstThunk
        |             |
        |             |
        |             |
        V             V
        0-->   func1     0--> exported func1
        1-->   func2     1--> exported func2
        2-->   func3     2--> exported func3
        3-->   foo       3--> exported foo
        4-->   mumpitz     4--> exported mumpitz
        5-->   knuff     5--> exported knuff
        6-->0         0<--6

這是最簡單情況的基本結構,下面學習輸入目錄。

首先,IMAGE_THUNK_DATA數組內的IMAGE_ORDINAL_FLAG的最高位可以置1,這種情況沒有符號名信息在列表內,符號純粹用序號引入。可以通過檢查IMAGE_THUNK_DATA.低字得到序號。通過序號引入的方式不提倡,以名字引入更安全,因爲輸出序號可能因輸出DLL的版本不同而改變。
第二,存在所謂的綁定輸入"bound imports".考慮加載器的工作,當一個可執行文件需要運行一個DLL內的函數時,加載器加載DLL,找到輸出目錄,查找函數的RVA,計算函數的入口點,然後把所謂的地址補入FirstThunk'列表內。考慮到程序員是聰明的,爲不發生衝突的DLL提供唯一的優先加載地址,我們假定函數入口點總是一樣的,在鏈接時他們可以被計算然後補入'FirstThunk'列表,這就是所謂的綁定輸入。

必須注意一點:用戶的DLL可能是不同的版本或者需要重定位DLL,這樣就作廢先前補入的'FirstThunk'列表,這種情況下,加載器依然能夠遍歷'OriginalFirstThunk'列表,找出輸入符號並重新補入'FirstThunk'列表內.加載器知道這是必須的,如果a) DLL版本不相符,b)DLL必須重定位。

確定是否有重定位,對加載器來說不是問題,但是如何知道版本的不同?這是IMAGE_IMPORT_DESCRIPTOR 中的'TimeDateStamp'出現的地方,如果是0,輸入表沒有綁定,加載器必須總是修復入口點,否則輸入表被綁定,'TimeDateStamp'必須與DLL的文件頭的時間戳相符,如果不相符,加載器假定執行文件綁定了一個錯誤的DLL,將重新補入輸入列表。//輸入列表就是FirstThunk所指的地址列表。

還有一個怪事就是輸入表內的轉發器"forwarders"。一個DLL可以輸出一個沒有在本DLL內定義的符號,而是從另一個DLL引入的,這樣的符號成爲被轉發的,現在,通過檢查沒有包含真正入口點的DLL的時間戳,很明顯不能確定入口點是否有效。所以被轉發的符號的入口點必須總是被修復,在輸入列表內,必須被找到被轉發符號的輸入,加載器可以補入它。這個靠'ForwarderChain'.這是thunk列表的索引,在索引位置的輸入是一個被轉發的輸出,在'FirstThunk'列表的該位置的內容是下一個轉發的輸出索引,直到-1爲止,表明沒有轉發了。如果根本沒有轉發,'ForwarderChain' 置-1 。
這是所謂的老式綁定。我們總結一下:

我假定你已經找到了IMAGE_DIRECTORY_ENTRY_IMPORT,沿着它找到輸入目錄,在某個節內。現在你在IMAGE_IMPORT_DESCRIPTORs數組的開始了,最後一個是全0。要解碼一個IMAGE_IMPORT_DESCRIPTORs, 首先找到'Name',循着RVA可以找到輸出的DLL名字。下面決定是否輸入綁定與否了。如果時間戳非0,說明綁定。如果綁定了,現在通過比較時間戳檢查是否DLL版本匹配。現在循着'OriginalFirstThunk'的RVA到達IMAGE_THUNK_DATA數組,順着往下走,每個成員是一個指向IMAGE_IMPORT_BY_NAME結構的RVA,除非高位置1表示沒有名字,只有序號可用。循着這個RVA,跳過2個字節(hint), 現在我們得到一個以0結尾的引入函數名了。
如果輸入綁定了,要找到提供的入口點地址,沿着'FirstThunk'並行遍歷'OriginalFirstThunk'數組;數組成員是入口點的線性地址。

有的鏈接器設置IMAGE_IMPORT_DESCRIPTOR內的'OriginalFirstThunk'爲0,只創建'FirstThunk'數組,非常明顯,這個輸入目錄不能發現,因爲無法找到函數名,必要的用於修復輸入輸入的信息丟失了。這種情況下,必須沿着'FirstThunk'數組得到輸入符號名,永遠得不到預先打過補丁的入口地址。

最後一個關於輸入目錄的新式綁定。當用這個的時候,'TimeDateStamp'設置爲全1,沒有轉發鏈forwarderchain;所有引入符號的地址被修補,無論是否被轉發。依然需要DLL的版本,需要區別轉發符號和普通符號。爲了這個目的,IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 目錄被建立,不在節內,而是在頭內,在節頭的後面,第一節的前面。這個目錄告訴我們,對應每個用到的DLL,另外哪個DLL有轉發輸出。
該結構是一個IMAGE_BOUND_IMPORT_DESCRIPTOR, 組成如下:

IMAGE_BOUND_IMPORT_DESCRIPTOR STRUCT
  TimeDateStamp dd   ?
  OffsetModuleName dw ?
  NumberOfModuleForwarderRefs dw ?
IMAGE_BOUND_IMPORT_DESCRIPTOR ENDS

DLL的32位'TimeDateStamp' ;
16位的'OffsetModuleName',從目錄的開始到DLL名字的偏移量。
16位的'NumberOfModuleForwarderRefs'給出該DLL用於轉發的DLL個數。

緊跟這個結構,是'IMAGE_BOUND_FORWARDER_REF's結構,告訴你本DLL轉發的DLL的名字和版本。該結構如下:

IMAGE_BOUND_FORWARDER_REF STRUCT
  TimeDateStamp dd   ?
  OffsetModuleName dw ?
  Reserved dw       ?
IMAGE_BOUND_FORWARDER_REF ENDS

32位時間戳 'TimeDateStamp';
16位的'OffsetModuleName', 被轉發的DLL名字的偏移量。
16位保留未用的。

跟隨'IMAGE_BOUND_FORWARDER_REF's後面的是下一個'IMAGE_BOUND_IMPORT_DESCRIPTOR'。該列表以全0結束。

現在你有一個新綁定的輸入目錄,加載所有的DLL,使用目錄指針IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT找到IMAGE_BOUND_IMPORT_DESCRIPTOR, 掃描它檢查時間戳是否與加載的DLL相符,如果不的話,修復輸入目錄內的FirstThunk'數組內容即函數地址。


資源resources
---------
資源比如對話框,菜單,圖標等等保存在由IMAGE_DIRECTORY_ENTRY_RESOURCE指向的數據目錄內. 那是一個至少含有一個位集合'IMAGE_SCN_CNT_INITIALIZED_DATA'和'IMAGE_SCN_MEM_READ'的節。
資源的根基是一個'IMAGE_RESOURCE_DIRECTORY';它包含幾個'IMAGE_RESOURCE_DIRECTORY_ENTRY'項 ,每個指向一個'IMAGE_RESOURCE_DIRECTORY'.這樣你得到一個'IMAGE_RESOURCE_DIRECTORY'樹。 'IMAGE_RESOURCE_DIRECTORY_ENTRY'是樹葉,指向實際的資源數據。
層次是這樣的,一個目錄是根,指向一些目錄,每一個針對一個資源類型。這些目錄指向子目錄,每個含有一個名字或者ID,並指向一個爲該資源提供的語言目錄。對於每個語言你會發現資源入口,它指向數據。
下面是沒有指向數據的樹:
                  (root)
                    |
        +----------------+------------------+
        |           |             |
        menu         dialog         icon
        |           |             |
    +-----+-----+     +-+----+       +-+----+----+
    |       |     |     |       |     |   |
  "main"     "popup"   0x10   "maindlg"   0x100 0x110 0x120
    |         |     |     |       |     |   |
  +---+-+       |     |     |       |     |   |
  |   |   default   english   default   def.   def. def.
german english

IMAGE_RESOURCE_DIRECTORY STRUCT
  Characteristics dd     ?
  TimeDateStamp dd     ?
  MajorVersion dw       ?
  MinorVersion dw       ?
  NumberOfNamedEntries dw ?
  NumberOfIdEntries dw   ?
IMAGE_RESOURCE_DIRECTORY ENDS

IMAGE_RESOURCE_DIRECTORY結構包括:
32位沒有用到的'Characteristics';
32位'TimeDateStamp'給出資源創建時間。
16位'MajorVersion'和16位'MinorVersion', 維護資源版本。
16位'NumberOfNamedEntries,帶名字的項目個數和16位'NumberOfIdEntries',ID項目個數.

緊接在該結構後面的是'NumberOfNamedEntries'+'NumberOfIdEntries'個結構。他們是 'IMAGE_RESOURCE_DIRECTORY_ENTRY'目錄項,他們可能指向下一個IMAGE_RESOURCE_DIRECTORY'或者實際的資源數據。

IMAGE_RESOURCE_DIRECTORY_ENTRY結構:

IMAGE_RESOURCE_DIRECTORY_ENTRY STRUCT
  union
    rName RECORD NameIsString:1,NameOffset:31
    Name1 dd ?       //名字是名字,串是串,各不相同的。
    Id dw ?
  ends
  union
    OffsetToData dd ?
    rDirectory RECORD DataIsDirectory:1,OffsetToDirectory:31
  ends
IMAGE_RESOURCE_DIRECTORY_ENTRY ENDS

32位資源標識或者其描述的目錄
32位數據偏移量或者下個子目錄的偏移量。

ID的意義依賴與在樹內的層次,ID可能是一個數字(高位清0)或者名字(高位置1),如果是名字,低31位是從資源節的原始數據開始到名字的偏移量。名字是16位長度,以寬字符結尾,不是0。
如果在根目錄下,如果ID是數字,是資源類型:
  1: cursor
  2: bitmap
  3: icon
  4: menu
  5: dialog
  6: string table
  7: font directory
  8: font
  9: accelerators
  10: unformatted resource data
  11: message table
  12: group cursor
  14: group icon
  16: version information
任何其他數字都是自定義的,任何帶類型名字的資源類型都是自定義的。更深一層的話,ID是資源ID或者資源名字。
如果再深入一層,ID必須是數字,它是特定資源實例的語言ID,例如,可以具有不同本地化的語言的對話框。他們使用統一的資源ID,系統會選擇基於線程的地域加載對話框,反過來反應用戶的區域設置。如果對於線程本地沒有資源發現,系統首先試圖發現中立語言爲資源的區域。如果還不能夠發現,帶有最小語言ID的實例將被使用。解碼語言ID,拆分爲主語言ID和子語言ID,使用宏PRIMARYLANGID() 和SUBLANGID(),分別是0-9,10-15。其值定義在"winresrc.h".
語言資源只被加速鍵,對話框,菜單,串表支持,其他資源類型必須是LANG_NEUTRAL/SUBLANG_NEUTRAL.

要找出資源目錄的下級目錄是否是另一個目錄,檢查偏移量的高位,如果置1,其餘31位是從資源原始數據開始到下一個目錄的偏移量。格式還是IMAGE_RESOURCE_DIRECTORY 後面跟着IMAGE_RESOURCE_DIRECTORY_ENTRYs項目.

如果高位清0,偏移量是從節開始到資源原始數據描述結構(一個IMAGE_RESOURCE_DATA_ENTRY)的偏移量。一個IMAGE_RESOURCE_DATA_ENTRY包括
IMAGE_RESOURCE_DATA_ENTRY STRUCT
  OffsetToData dd ?
  Size1 dd     ?
  CodePage dd   ?
  Reserved dd   ?
IMAGE_RESOURCE_DATA_ENTRY ENDS

32位'OffsetToData' 從資源節開始到原始數據的偏移量,32位數據大小Size,32位'CodePage'
和32保留。
原始數據格式依賴於資源類型,任何字符串資源都是UNICODE格式。

重定位relocations
-----------
最後一個數據目錄是重定位,基重定位目錄被IMAGE_DIRECTORY_ENTRY_BASERELOC指向,典型的包括一個自己的節,名字是".reloc"以及IMAGE_SCN_CNT_INITIALIZED_DATA,IMAGE_SCN_MEM_DISCARDABLE和
IMAGE_SCN_MEM_READ位集合。

如果映象沒有被加載器調入優先地址,則該數據被加載器使用。這種情況下給鏈接器填入的地址無效了。加載器必須修復用於靜態變量的絕對地址,串等。

IMAGE_BASE_RELOCATION STRUCT
  VirtualAddress dd   ?
  SizeOfBlock dd     ?
IMAGE_BASE_RELOCATION ENDS
重定位目錄是一系列塊,每個塊包括重定位信息針對4K的映象。一個塊起始於一個
'IMAGE_BASE_RELOCATION'結構,包括32位的'VirtualAddress'和32位的'SizeOfBlock'.隨後是實際的重定位數據每個都是16位的。'VirtualAddress'是本塊要應用的基地址。'SizeOfBlock' 是整個塊的大小。後面的重定位的數目是('SizeOfBlock'-sizeof(IMAGE_BASE_RELOCATION))/2,重定位信息以VirtualAddress'=0的IMAGE_BASE_RELOCATION結構結束。

每個16位重定位信息包括低12位的重定位位置和高4位的重定位類型。要得到重定位的RVA,IMAGE_BASE_RELOCATION'的'VirtualAddress'需要加上12位位置偏移量. 類型是下列之一:

  IMAGE_REL_BASED_ABSOLUTE (0) 使塊按照32位對齊,位置爲0。
  IMAGE_REL_BASED_HIGH (1) 高16位必須應用於偏移量所指高字16位。
  IMAGE_REL_BASED_LOW (2) 低16位必須應用於偏移量所指低字16位。
  IMAGE_REL_BASED_HIGHLOW (3) 全部32位應用於所有32位。.
  IMAGE_REL_BASED_HIGHADJ (4) 需要32位,高16位位於偏移量,低16位位於下一個偏移量數組元素,組合爲一個帶符號數,加上32位的一個數,然後加上8000然後把高16位保存在偏移量的16位域內。
  IMAGE_REL_BASED_MIPS_JMPADDR (5)     Unknown
  IMAGE_REL_BASED_SECTION (6)     Unknown
  IMAGE_REL_BASED_REL32 (7)     Unknown

舉例:
  0x00004000     (32 bits, starting RVA)
  0x00000010     (32 bits, size of chunk)
  0x3012       (16 bits reloc data)
  0x3080       (16 bits reloc data)
  0x30f6       (16 bits reloc data)
  0x0000       (16 bits reloc data)
  0x00000000     (next chunk's RVA)
  0xff341234
第一塊描述重定位起始於RVA 0x4000長度16字節。因爲頭用去8字節,一個重定位用2個字節,總共(16-8)/2=4個重定位,第一個重定位被用於0x4012,下一個重定位於4080,第三個定位於0x40f6. 最後一個無用。最後一個結束。

附錄:Appendix: hello world
---------------------
The program will be the equivalent of

  #include <stdio.h>
  int main(void)
  {
    puts(hello,world);
    return 0;
  }

用Win32函數代替C runtime:
  #define STD_OUTPUT_HANDLE -11UL
  #define hello "hello, world/n"
  __declspec(dllimport) unsigned long __stdcall
  GetStdHandle(unsigned long hdl);
  __declspec(dllimport) unsigned long __stdcall
  WriteConsoleA(unsigned long hConsoleOutput,
            const void *buffer,
            unsigned long chrs,
            unsigned long *written,
            unsigned long unused
            );

  static unsigned long written;
  void startup(void)
  {
    WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE),hello,sizeof(hello)-1,&written,0);
    return;
  }

彙編語言
  startup:
          ; 參數WriteConsole(), 逆向
  6A 00               push     0x00000000
  68 ?? ?? ?? ??         push     offset _written
  6A 0D               push     0x0000000d
  68 ?? ?? ?? ??         push     offset hello
          ; 參數GetStdHandle()
  6A F5               push     0xfffffff5
  2E FF 15 ?? ?? ?? ??     call     dword ptr cs:__imp__GetStdHandle@4
          ; result is last parameter for WriteConsole()
  50                 push     eax
  2E FF 15 ?? ?? ?? ??     call     dword ptr cs:__imp__WriteConsoleA@20
  C3                 ret    

  hello:
  68 65 6C 6C 6F 2C 20 77 6F 72 6C 64 0A   "hello, world/n"
  _written:
  00 00 00 00

下面是鏈接器:
我們需要找出函數WriteConsoleA() and GetStdHandle().他們在"kernel32.dll". 此爲輸入庫部分。我們現在生成可執行文件。問號在後面被找出。

DOS-stub, 起始於0x0,0x40字節:
  00 | 4d 5a 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  10 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  20 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  30 | 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00
只是一個頭,前面帶有簽字"MZ"後面是e_lfanew 指針。然後是PE簽名,起始於0x40,0x4字節:
      50 45 00 00

下面是文件頭,起始於0x44,0x14字節:
  Machine               4c 01     ; i386
  NumberOfSections         02 00     ; code and data
  TimeDateStamp           00 00 00 00 ; who cares?
  PointerToSymbolTable     00 00 00 00 ; unused
  NumberOfSymbols         00 00 00 00 ; unused
  SizeOfOptionalHeader     e0 00     ; constant
  Characteristics         02 01     ; executable on 32-bit-machine

下面是optional header,起始於0x58,0x60 bytes long:
  Magic               0b 01     ; constant
  MajorLinkerVersion       00       ; I'm version 0.0 :-)
  MinorLinkerVersion       00       ;
  SizeOfCode             20 00 00 00 ; 32 bytes of code
  SizeOfInitializedData     ?? ?? ?? ?? ; yet to find out
  SizeOfUninitializedData   00 00 00 00 ; we don't have a BSS
  AddressOfEntryPoint       ?? ?? ?? ?? ; yet to find out
  BaseOfCode             ?? ?? ?? ?? ; yet to find out
  BaseOfData             ?? ?? ?? ?? ; yet to find out
  ImageBase             00 00 10 00 ; 1 MB, chosen arbitrarily
  SectionAlignment         20 00 00 00 ; 32-bytes-alignment
  FileAlignment           20 00 00 00 ; 32-bytes-alignment
  MajorOperatingSystemVersion 04 00     ; NT 4.0
  MinorOperatingSystemVersion 00 00     ;
  MajorImageVersion       00 00     ; version 0.0
  MinorImageVersion       00 00     ;
  MajorSubsystemVersion     04 00     ; Win32 4.0
  MinorSubsystemVersion     00 00     ;
  Win32VersionValue       00 00 00 00 ; unused?
  SizeOfImage           ?? ?? ?? ?? ; yet to find out
  SizeOfHeaders           ?? ?? ?? ?? ; yet to find out
  CheckSum             00 00 00 00 ; not used for non-drivers
  Subsystem             03 00     ; Win32 console
  DllCharacteristics       00 00     ; unused (not a DLL)
  SizeOfStackReserve       00 00 10 00 ; 1 MB stack
  SizeOfStackCommit       00 10 00 00 ; 4 KB to start with
  SizeOfHeapReserve       00 00 10 00 ; 1 MB heap
  SizeOfHeapCommit         00 10 00 00 ; 4 KB to start with
  LoaderFlags           00 00 00 00 ; unknown
  NumberOfRvaAndSizes       10 00 00 00 ; constant

計劃2個節,一個是代碼,一個是數據(包括數據,常量和輸入目錄),沒有重定位,沒有其他資源。沒有BSS段。變量'written'填入初始化數據。節對齊和文件對齊都是32字節。
現在建立數據目錄,開始於0xb8,0x80 字節長。只有引入目錄被使用。
  Address     Size
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_EXPORT (0)
  ?? ?? ?? ??   ?? ?? ?? ??       ; IMAGE_DIRECTORY_ENTRY_IMPORT (1)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_RESOURCE (2)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_EXCEPTION (3)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_SECURITY (4)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_BASERELOC (5)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_DEBUG (6)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_COPYRIGHT (7)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_GLOBALPTR (8)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_TLS (9)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG (10)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (11)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_IAT (12)
  00 00 00 00   00 00 00 00       ; 13
  00 00 00 00   00 00 00 00       ; 14
  00 00 00 00   00 00 00 00       ; 15

下面是節頭,首先是代碼節,包括上述彙編,32字節長,開始於0x138,長度x28:
  Name         2e 63 6f 64 65 00 00 00   ; ".code"
  VirtualSize       00 00 00 00         ; unused
  VirtualAddress     ?? ?? ?? ??         ; yet to find out
  SizeOfRawData     20 00 00 00         ; size of code
  PointerToRawData   ?? ?? ?? ??         ; yet to find out
  PointerToRelocations 00 00 00 00         ; unused
  PointerToLinenumbers 00 00 00 00         ; unused
  NumberOfRelocations 00 00             ; unused
  NumberOfLinenumbers 00 00             ; unused
  Characteristics   20 00 00 60         ; code, executable, readable

第二個節包括數據,起始於0x160,長度x28:
  Name         2e 64 61 74 61 00 00 00   ; ".data"
  VirtualSize       00 00 00 00         ; unused
  VirtualAddress     ?? ?? ?? ??         ; yet to find out
  SizeOfRawData     ?? ?? ?? ??         ; yet to find out
  PointerToRawData   ?? ?? ?? ??         ; yet to find out
  PointerToRelocations 00 00 00 00         ; unused
  PointerToLinenumbers 00 00 00 00         ; unused
  NumberOfRelocations 00 00             ; unused
  NumberOfLinenumbers 00 00             ; unused
  Characteristics   40 00 00 c0         ; initialized, readable, writeable

下一個是0x188,由於節必須按照32字節對齊,所有填0到0x1a0:
  00 00 00 00 00 00     ; padding
  00 00 00 00 00 00
  00 00 00 00 00 00
  00 00 00 00 00 00
現在第一節代碼節來到了,開始於0x1a0,0x20字節長。
  6A 00             ; push     0x00000000
  68 ?? ?? ?? ??       ; push     offset _written
  6A 0D             ; push     0x0000000d
  68 ?? ?? ?? ??       ; push     offset hello_string
  6A F5             ; push     0xfffffff5
  2E FF 15 ?? ?? ?? ??   ; call     dword ptr cs:__imp__GetStdHandle@4
  50               ; push     eax
  2E FF 15 ?? ?? ?? ??   ; call     dword ptr cs:__imp__WriteConsoleA@20
  C3               ; ret    
由於前面節的長度,不需要填0。數據節接着出現:起始於0x1c0:
  68 65 6C 6C 6F 2C 20 77 6F 72 6C 64 0A ; "hello, world/n"
  00 00 00                     ; padding to align _written//爲什麼要對齊??文件對齊而已。
  00 00 00 00                   ; _written

現在剩下的是輸入目錄了,引入2個函數從"kernel32.dll",緊隨2個變量。首先對齊到32個字節,文件對齊。
  00 00 00 00 00 00 00 00 00 00 00 00   ; padding

IMAGE_IMPORT_DESCRIPTOR從0x1e0開始:只有一個DLL所以只有一個數組元素。
  OriginalFirstThunk     ?? ?? ?? ??   ; yet to find out
  TimeDateStamp       00 00 00 00   ; unbound
  ForwarderChain       ff ff ff ff   ; no forwarders
  Name             ?? ?? ?? ??   ; yet to find out
  FirstThunk         ?? ?? ?? ??   ; yet to find out

我們需要結束輸入目錄用0填充,0x1f4:
  OriginalFirstThunk     00 00 00 00   ; terminator
  TimeDateStamp       00 00 00 00   ;
  ForwarderChain       00 00 00 00   ;
  Name             00 00 00 00   ;
  FirstThunk         00 00 00 00   ;

現在剩下DLL名字和2個thunks,及thunk-data,函數名。DLL名字從0x208開始:
  6b 65 72 6e 65 6c 33 32 2e 64 6c 6c 00 ; "kernel32.dll"
  00 00 00                     ; padding to 32-bit-boundary
originalfirstthunk數組,2個成員,起始於0x218:
  AddressOfData   ?? ?? ?? ??         ; RVA to function name "WriteConsoleA"
  AddressOfData   ?? ?? ?? ??         ; RVA to function name "GetStdHandle"
            00 00 00 00         ; terminator
firstthunk含有同樣的列表,起始於0x224:
(__imp__WriteConsoleA@20, at 0x224)
  AddressOfData   ?? ?? ?? ??         ; RVA to function name "WriteConsoleA"
(__imp__GetStdHandle@4, at 0x228)
  AddressOfData   ?? ?? ?? ??         ; RVA to function name "GetStdHandle"
            00 00 00 00         ; terminator
現在只剩下2個函數名在IMAGE_IMPORT_BY_NAME.從0x230.
  01 00                         ; ordinal, need not be correct
  57 72 69 74 65 43 6f 6e 73 6f 6c 65 41 00 ; "WriteConsoleA"
  02 00                         ; ordinal, need not be correct
  47 65 74 53 74 64 48 61 6e 64 6c 65 00   ; "GetStdHandle"

下面填充到0x260:
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; padding
  00

------------
現在結束,我們知道所有字節偏移量,我們可以實施修補地址和尺寸了。
DOS-header, starting at 0x0:
  00 | 4d 5a 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  10 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  20 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  30 | 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00
signature, starting at 0x40:
    50 45 00 00
file-header, starting at 0x44:
  Machine               4c 01     ; i386
  NumberOfSections         02 00     ; code and data
  TimeDateStamp           00 00 00 00 ; who cares?
  PointerToSymbolTable     00 00 00 00 ; unused
  NumberOfSymbols         00 00 00 00 ; unused
  SizeOfOptionalHeader     e0 00     ; constant
  Characteristics         02 01     ; executable on 32-bit-machine

optional header, starting at 0x58:
  Magic               0b 01     ; constant
  MajorLinkerVersion       00       ; I'm version 0.0 :-)
  MinorLinkerVersion       00       ;
  SizeOfCode             20 00 00 00 ; 32 bytes of code
  SizeOfInitializedData     a0 00 00 00 ; data section size
  SizeOfUninitializedData   00 00 00 00 ; we don't have a BSS
  AddressOfEntryPoint       a0 01 00 00 ; beginning of code section
  BaseOfCode             a0 01 00 00 ; RVA to code section
  BaseOfData             c0 01 00 00 ; RVA to data section
  ImageBase             00 00 10 00 ; 1 MB, chosen arbitrarily
  SectionAlignment         20 00 00 00 ; 32-bytes-alignment
  FileAlignment           20 00 00 00 ; 32-bytes-alignment
  MajorOperatingSystemVersion 04 00     ; NT 4.0
  MinorOperatingSystemVersion 00 00     ;
  MajorImageVersion       00 00     ; version 0.0
  MinorImageVersion       00 00     ;
  MajorSubsystemVersion     04 00     ; Win32 4.0
  MinorSubsystemVersion     00 00     ;
  Win32VersionValue       00 00 00 00 ; unused?
  SizeOfImage           c0 00 00 00 ; sum of all section sizes//沒有包含頭的尺寸,跟前面所說矛盾了。
  SizeOfHeaders           a0 01 00 00 ; offset to 1st section
  CheckSum             00 00 00 00 ; not used for non-drivers
  Subsystem             03 00     ; Win32 console
  DllCharacteristics       00 00     ; unused (not a DLL)
  SizeOfStackReserve       00 00 10 00 ; 1 MB stack
  SizeOfStackCommit       00 10 00 00 ; 4 KB to start with
  SizeOfHeapReserve       00 00 10 00 ; 1 MB heap
  SizeOfHeapCommit         00 10 00 00 ; 4 KB to start with
  LoaderFlags           00 00 00 00 ; unknown
  NumberOfRvaAndSizes       10 00 00 00 ; constant

data directories, starting at 0xb8:
  Address     Size
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_EXPORT (0)
  e0 01 00 00   6f 00 00 00       ; IMAGE_DIRECTORY_ENTRY_IMPORT (1)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_RESOURCE (2)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_EXCEPTION (3)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_SECURITY (4)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_BASERELOC (5)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_DEBUG (6)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_COPYRIGHT (7)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_GLOBALPTR (8)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_TLS (9)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG (10)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (11)
  00 00 00 00   00 00 00 00       ; IMAGE_DIRECTORY_ENTRY_IAT (12)
  00 00 00 00   00 00 00 00       ; 13
  00 00 00 00   00 00 00 00       ; 14
  00 00 00 00   00 00 00 00       ; 15

section header (code), starting at 0x138:
  Name         2e 63 6f 64 65 00 00 00   ; ".code"
  VirtualSize       00 00 00 00         ; unused
  VirtualAddress     a0 01 00 00         ; RVA to code section
  SizeOfRawData     20 00 00 00         ; size of code
  PointerToRawData   a0 01 00 00         ; file offset to code section
  PointerToRelocations 00 00 00 00         ; unused
  PointerToLinenumbers 00 00 00 00         ; unused
  NumberOfRelocations 00 00             ; unused
  NumberOfLinenumbers 00 00             ; unused
  Characteristics   20 00 00 60         ; code, executable, readable

section header (data), starting at 0x160:
  Name         2e 64 61 74 61 00 00 00   ; ".data"
  VirtualSize       00 00 00 00         ; unused
  VirtualAddress     c0 01 00 00         ; RVA to data section
  SizeOfRawData     a0 00 00 00         ; size of data section
  PointerToRawData   c0 01 00 00         ; file offset to data section
  PointerToRelocations 00 00 00 00         ; unused
  PointerToLinenumbers 00 00 00 00         ; unused
  NumberOfRelocations 00 00             ; unused
  NumberOfLinenumbers 00 00             ; unused
  Characteristics   40 00 00 c0         ; initialized, readable, writeable

(padding)
  00 00 00 00 00 00     ; padding
  00 00 00 00 00 00
  00 00 00 00 00 00
  00 00 00 00 00 00

code section, starting at 0x1a0:
  6A 00             ; push     0x00000000
  68 d0 01 10 00       ; push     offset _written
  6A 0D             ; push     0x0000000d
  68 c0 01 10 00       ; push     offset hello_string
  6A F5             ; push     0xfffffff5
  2E FF 15 28 02 10 00   ; call     dword ptr cs:__imp__GetStdHandle@4
  50               ; push     eax
  2E FF 15 24 02 10 00   ; call     dword ptr cs:__imp__WriteConsoleA@20
  C3               ; ret    

data section, beginning at 0x1c0:
  68 65 6C 6C 6F 2C 20 77 6F 72 6C 64 0A ; "hello, world/n"
  00 00 00                     ; padding to align _written
  00 00 00 00                   ; _written
padding:
  00 00 00 00 00 00 00 00 00 00 00 00   ; padding
IMAGE_IMPORT_DESCRIPTOR, starting at 0x1e0:
  OriginalFirstThunk     18 02 00 00   ; RVA to orig. 1st thunk
  TimeDateStamp       00 00 00 00   ; unbound
  ForwarderChain       ff ff ff ff   ; no forwarders
  Name             08 02 00 00   ; RVA to DLL name
  FirstThunk         24 02 00 00   ; RVA to 1st thunk
terminator (0x1f4):
  OriginalFirstThunk     00 00 00 00   ; terminator
  TimeDateStamp       00 00 00 00   ;
  ForwarderChain       00 00 00 00   ;
  Name             00 00 00 00   ;
  FirstThunk         00 00 00 00   ;
The DLL name, at 0x208:
  6b 65 72 6e 65 6c 33 32 2e 64 6c 6c 00 ; "kernel32.dll"
  00 00 00                     ; padding to 32-bit-boundary
original first thunk, starting at 0x218:
  AddressOfData   30 02 00 00         ; RVA to function name "WriteConsoleA"
  AddressOfData   40 02 00 00         ; RVA to function name "GetStdHandle"
            00 00 00 00         ; terminator
first thunk, starting at 0x224:
  AddressOfData   30 02 00 00         ; RVA to function name "WriteConsoleA"
  AddressOfData   40 02 00 00         ; RVA to function name "GetStdHandle"
            00 00 00 00         ; terminator
IMAGE_IMPORT_BY_NAME, at byte 0x230:
  01 00                         ; ordinal, need not be correct
  57 72 69 74 65 43 6f 6e 73 6f 6c 65 41 00 ; "WriteConsoleA"
IMAGE_IMPORT_BY_NAME, at byte 0x240:
  02 00                         ; ordinal, need not be correct
  47 65 74 53 74 64 48 61 6e 64 6c 65 00   ; "GetStdHandle"
(padding)
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; padding
  00
First unused byte: 0x260

--------------

因爲是32字節的節對齊,所以在 windows 98下該程序不工作,可以在NT下工作,要想在WIN98下工作,必須插入許多0並修改RVA。

QduWg翻譯,2005年12月5日埃塞
後記:爲了讓剛剛入門的朋友對PE文件結構有一個比較清晰的瞭解,所以我翻譯了此文。

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章