青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

道。道。道

安全特性不等于安全的特性

   :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理

常用鏈接

搜索

  •  

最新評論

摘要

   Windows NT 3.1引入了一種名為PE文件格式的新可執(zhí)行文件格式。PE文件格式的規(guī)范包含在了MSDN的CD中(Specs and Strategy, Specifications, Windows NT File Format Specifications),但是它非常之晦澀。
   然而這一的文檔并未提供足夠的信息,所以開發(fā)者們無法很好地弄懂PE格式。本文旨在解決這一問題,它會對整個的PE文件格式作一個十分徹底的解釋,另外,本文中還帶有對所有必需結(jié)構(gòu)的描述以及示范如何使用這些信息的源碼示例。
   為了獲得PE文件中所包含的重要信息,我編寫了一個名為PEFILE.DLL的動態(tài)鏈接庫,本文中所有出現(xiàn)的源碼示例亦均摘自于此。這個DLL和它的源代碼都作為PEFile示例程序的一部分包含在了CD中(譯注:示例程序請在MSDN中尋找,本站恕不提供),你可以在你自己的應(yīng)用程序中使用這個DLL;同樣,你亦可以依你所愿地使用并構(gòu)建它的源碼。在本文末尾,你會找到PEFILE.DLL的函數(shù)導(dǎo)出列表和一個如何使用它們的說明。我覺得你會發(fā)現(xiàn)這些函數(shù)會讓你從容應(yīng)付PE文件格式的。

介紹

   Windows操作系統(tǒng)家族最近增加的Windows NT為開發(fā)環(huán)境和應(yīng)用程序本身帶來了很大的改變,這之中一個最為重大的當(dāng)屬PE文件格式了。新的PE文件格式主要來自于UNIX操作系統(tǒng)所通用的COFF規(guī)范,同時為了保證與舊版本MS-DOS及Windows操作系統(tǒng)的兼容,PE文件格式也保留了MS-DOS中那熟悉的MZ頭部。
   在本文之中,PE文件格式是以自頂而下的順序解釋的。在你從頭開始研究文件內(nèi)容的過程之中,本文會詳細(xì)討論P(yáng)E文件的每一個組成部分。
   許多單獨(dú)的文件成分定義都來自于Microsoft Win32 SDK開發(fā)包中的WINNT.H文件,在這個文件中你會發(fā)現(xiàn)用來描述文件頭部和數(shù)據(jù)目錄等各種成分的結(jié)構(gòu)類型定義。但是,在WINNT.H中缺少對PE文件結(jié)構(gòu)足夠的定義,在這種情況下,我定義了自己的結(jié)構(gòu)來存取文件數(shù)據(jù)。你會在PEFILE.DLL工程的PEFILE.H中找到這些結(jié)構(gòu)的定義,整套的PEFILE.H開發(fā)文件包含在PEFile示例程序之中。
   本文配套的示例程序除了PEFILE.DLL示例代碼之外,還有一個單獨(dú)的Win32示例應(yīng)用程序,名為EXEVIEW.EXE。創(chuàng)建這一示例目的有二:首先,我需要測試PEFILE.DLL的函數(shù),并且某些情況要求我同時查看多個文件;其次,很多解決PE文件格式的工作和直接觀看數(shù)據(jù)有關(guān)。例如,要弄懂導(dǎo)入地址名稱表是如何構(gòu)成的,我就得同時查看.idata段頭部、導(dǎo)入映像數(shù)據(jù)目錄、可選頭部以及當(dāng)前的.idata段實體,而EXEVIEW.EXE就是查看這些信息的最佳示例。
   閑話少敘,讓我們開始吧。

PE文件結(jié)構(gòu)

   PE文件格式被組織為一個線性的數(shù)據(jù)流,它由一個MS-DOS頭部開始,接著是一個是模式的程序殘余以及一個PE文件標(biāo)志,這之后緊接著PE文件頭和可選頭部。這些之后是所有的段頭部,段頭部之后跟隨著所有的段實體。文件的結(jié)束處是一些其它的區(qū)域,其中是一些混雜的信息,包括重分配信息、符號表信息、行號信息以及字串表數(shù)據(jù)。我將所有這些成分列于圖1。

圖1.PE文件映像結(jié)構(gòu)
   從MS-DOS文件頭結(jié)構(gòu)開始,我將按照PE文件格式各成分的出現(xiàn)順序依次對其進(jìn)行討論,并且討論的大部分是以示例代碼為基礎(chǔ)來示范如何獲得文件的信息的。所有的源碼均摘自PEFILE.DLL模塊的PEFILE.C文件。這些示例都利用了Windows NT最酷的特色之一——內(nèi)存映射文件,這一特色允許用戶使用一個簡單的指針來存取文件中所包含的數(shù)據(jù),因此所有的示例都使用了內(nèi)存映射文件來存取PE文件中的數(shù)據(jù)。
   注意:請查閱本文末尾關(guān)于如何使用PEFILE.DLL的那一段。

MS-DOS頭部/實模式頭部

   如上所述,PE文件格式的第一個組成部分是MS-DOS頭部。在PE文件格式中,它并非一個新概念,因為它與MS-DOS 2.0以來就已有的MS-DOS頭部是完全一樣的。保留這個相同結(jié)構(gòu)的最主要原因是,當(dāng)你嘗試在Windows 3.1以下或MS-DOS 2.0以上的系統(tǒng)下裝載一個文件的時候,操作系統(tǒng)能夠讀取這個文件并明白它是和當(dāng)前系統(tǒng)不相兼容的。換句話說,當(dāng)你在MS-DOS 6.0下運(yùn)行一個Windows NT可執(zhí)行文件時,你會得到這樣一條消息:“This program cannot be run in DOS mode.”如果MS-DOS頭部不是作為PE文件格式的第一部分的話,操作系統(tǒng)裝載文件的時候就會失敗,并提供一些完全沒用的信息,例如:“The name specified is not recognized as an internal or external command, operable program or batch file.”
   MS-DOS頭部占據(jù)了PE文件的頭64個字節(jié),描述它內(nèi)容的結(jié)構(gòu)如下:
//WINNT.H

typedef struct _IMAGE_DOS_HEADER { // DOS的.EXE頭部
  USHORT e_magic; // 魔術(shù)數(shù)字
  USHORT e_cblp; // 文件最后頁的字節(jié)數(shù)
  USHORT e_cp; // 文件頁數(shù)
  USHORT e_crlc; // 重定義元素個數(shù)
  USHORT e_cparhdr; // 頭部尺寸,以段落為單位
  USHORT e_minalloc; // 所需的最小附加段
  USHORT e_maxalloc; // 所需的最大附加段
  USHORT e_ss; // 初始的SS值(相對偏移量)
  USHORT e_sp; // 初始的SP值
  USHORT e_csum; // 校驗和
  USHORT e_ip; // 初始的IP值
  USHORT e_cs; // 初始的CS值(相對偏移量)
  USHORT e_lfarlc; // 重分配表文件地址
  USHORT e_ovno; // 覆蓋號
  USHORT e_res[4]; // 保留字
  USHORT e_oemid; // OEM標(biāo)識符(相對e_oeminfo)
  USHORT e_oeminfo; // OEM信息
  USHORT e_res2[10]; // 保留字
  LONG e_lfanew; // 新exe頭部的文件地址
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
第一個域e_magic,被稱為魔術(shù)數(shù)字,它被用于表示一個MS-DOS兼容的文件類型。所有MS-DOS兼容的可執(zhí)行文件都將這個值設(shè)為0x5A4D,表示ASCII字符MZ。MS-DOS頭部之所以有的時候被稱為MZ頭部,就是這個緣故。還有許多其它的域?qū)τ贛S-DOS操作系統(tǒng)來說都有用,但是對于Windows NT來說,這個結(jié)構(gòu)中只有一個有用的域——最后一個域e_lfnew,一個4字節(jié)的文件偏移量,PE文件頭部就是由它定位的。對于Windows NT的PE文件來說,PE文件頭部是緊跟在MS-DOS頭部和實模式程序殘余之后的。

實模式殘余程序

   實模式殘余程序是一個在裝載時能夠被MS-DOS運(yùn)行的實際程序。對于一個MS-DOS的可執(zhí)行映像文件,應(yīng)用程序就是從這里執(zhí)行的。對于Windows、OS/2、Windows NT這些操作系統(tǒng)來說,MS-DOS殘余程序就代替了主程序的位置被放在這里。這種殘余程序通常什么也不做,而只是輸出一行文本,例如:“This program requires Microsoft Windows v3.1 or greater.”當(dāng)然,用戶可以在此放入任何的殘余程序,這就意味著你可能經(jīng)常看到像這樣的東西:“You can''t run a Windows NT application on OS/2, it''s simply not possible.”
   當(dāng)為Windows 3.1構(gòu)建一個應(yīng)用程序的時候,鏈接器將向你的可執(zhí)行文件中鏈接一個名為WINSTUB.EXE的默認(rèn)殘余程序。你可以用一個基于MS-DOS的有效程序取代WINSTUB,并且用STUB模塊定義語句指示鏈接器,這樣就能夠取代鏈接器的默認(rèn)行為。為Windows NT開發(fā)的應(yīng)用程序可以通過使用-STUB:鏈接器選項來實現(xiàn)。

PE文件頭部與標(biāo)志

   PE文件頭部是由MS-DOS頭部的e_lfanew域定位的,這個域只是給出了文件的偏移量,所以要確定PE頭部的實際內(nèi)存映射地址,就需要添加文件的內(nèi)存映射基地址。例如,以下的宏是包含在PEFILE.H源文件之中的:
//PEFILE.H

#define NTSIGNATURE(a) ((LPVOID)((BYTE *)a + \
                       ((PIMAGE_DOS_HEADER)a)->e_lfanew))
在處理PE文件信息的時候,我發(fā)現(xiàn)文件之中有些位置需要經(jīng)常查閱。既然這些位置僅僅是對文件的偏移量,那么用宏來實現(xiàn)這些定位就比較容易,因為它們較之函數(shù)有更好的表現(xiàn)。
   請注意這個宏所獲得的是PE文件標(biāo)志,而并非PE文件頭部的偏移量。那是由于自Windows與OS/2的可執(zhí)行文件開始,.EXE文件都被賦予了目標(biāo)操作系統(tǒng)的標(biāo)志。對于Windows NT的PE文件格式而言,這一標(biāo)志在PE文件頭部結(jié)構(gòu)之前。在Windows和OS/2的某些版本中,這一標(biāo)志是文件頭的第一個字。同樣,對于PE文件格式,Windows NT使用了一個DWORD值。
   以上的宏返回了文件標(biāo)志的偏移量,而不管它是哪種類型的可執(zhí)行文件。所以,文件頭部是在DWORD標(biāo)志之后,還是在WORD標(biāo)志處,是由這個標(biāo)志是否Windows NT文件標(biāo)志所決定的。要解決這個問題,我編寫了ImageFileType函數(shù)(如下),它返回了映像文件的類型:
//PEFILE.C

DWORD WINAPI ImageFileType (LPVOID lpFile)
{
  /* 首先出現(xiàn)的是DOS文件標(biāo)志 */
  if (*(USHORT *)lpFile == IMAGE_DOS_SIGNATURE)
  {
    /* 由DOS頭部決定PE文件頭部的位置 */
    if (LOWORD (*(DWORD *)NTSIGNATURE (lpFile)) ==
        IMAGE_OS2_SIGNATURE ||
        LOWORD (*(DWORD *)NTSIGNATURE (lpFile)) ==
        IMAGE_OS2_SIGNATURE_LE)
      return (DWORD)LOWORD(*(DWORD *)NTSIGNATURE (lpFile));
    else if (*(DWORD *)NTSIGNATURE (lpFile) ==
      IMAGE_NT_SIGNATURE)
    return IMAGE_NT_SIGNATURE;
    else
      return IMAGE_DOS_SIGNATURE;
  }
  else
    /* 不明文件種類 */
    return 0;
}
以上列出的代碼立即告訴了你NTSIGNATURE宏有多么有用。對于比較不同文件類型并且返回一個適當(dāng)?shù)奈募N類來說,這個宏就會使這兩件事變得非常簡單。WINNT.H之中定義的四種不同文件類型有:
//WINNT.H

#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
#define IMAGE_OS2_SIGNATURE 0x454E // NE
#define IMAGE_OS2_SIGNATURE_LE 0x454C // LE
#define IMAGE_NT_SIGNATURE 0x00004550 // PE00
  
首先,Windows的可執(zhí)行文件類型沒有出現(xiàn)在這一列表中,這一點(diǎn)看起來很奇怪。但是,在稍微研究一下之后,就能得到原因了:除了操作系統(tǒng)版本規(guī)范的不同之外,Windows的可執(zhí)行文件和OS/2的可執(zhí)行文件實在沒有什么區(qū)別。這兩個操作系統(tǒng)擁有相同的可執(zhí)行文件結(jié)構(gòu)。
   現(xiàn)在把我們的注意力轉(zhuǎn)向Windows NT PE文件格式,我們會發(fā)現(xiàn)只要我們得到了文件標(biāo)志的位置,PE文件之后就會有4個字節(jié)相跟隨。下一個宏標(biāo)識了PE文件的頭部:
//PEFILE.C

#define PEFHDROFFSET(a) ((LPVOID)((BYTE *)a + \
                        ((PIMAGE_DOS_HEADER)a)->e_lfanew + \
                        SIZE_OF_NT_SIGNATURE))
  
這個宏與上一個宏的唯一不同是這個宏加入了一個常量SIZE_OF_NT_SIGNATURE。不幸的是,這個常量并未定義在WINNT.H之中,于是我將它定義在了PEFILE.H中,它是一個DWORD的大小。
   既然我們知道了PE文件頭的位置,那么就可以檢查頭部的數(shù)據(jù)了。我們只需要把這個位置賦值給一個結(jié)構(gòu),如下:
PIMAGE_FILE_HEADER pfh;
pfh = (PIMAGE_FILE_HEADER)PEFHDROFFSET(lpFile);
在這個例子中,lpFile表示一個指向可執(zhí)行文件內(nèi)存映像基地址的指針,這就顯出了內(nèi)存映射文件的好處:不需要執(zhí)行文件的I/O,只需使用指針pfh就能存取文件中的信息。PE文件頭結(jié)構(gòu)被定義為:
//WINNT.H

typedef struct _IMAGE_FILE_HEADER {
  USHORT Machine;
  USHORT NumberOfSections;
  ULONG TimeDateStamp;
  ULONG PointerToSymbolTable;
  ULONG NumberOfSymbols;
  USHORT SizeOfOptionalHeader;
  USHORT Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

#define IMAGE_SIZEOF_FILE_HEADER 20
請注意這個文件頭部的大小已經(jīng)定義在這個包含文件之中了,這樣一來,想要得到這個結(jié)構(gòu)的大小就很方便了。但是我覺得對結(jié)構(gòu)本身使用sizeof運(yùn)算符(譯注:原文為“function”)更簡單一些,因為這樣的話我就不必記住這個常量的名字IMAGE_SIZEOF_FILE_HEADER,而只需要記住結(jié)構(gòu)IMAGE_FILE_HEADER的名字就可以了。另一方面,記住所有結(jié)構(gòu)的名字已經(jīng)夠有挑戰(zhàn)性的了,尤其在是這些結(jié)構(gòu)只有WINNT.H中才有的情況下。
   PE文件中的信息基本上是一些高級信息,這些信息是被操作系統(tǒng)或者應(yīng)用程序用來決定如何處理這個文件的。第一個域是用來表示這個可執(zhí)行文件被構(gòu)建的目標(biāo)機(jī)器種類,例如DEC(R) Alpha、MIPS R4000、Intel(R) x86或一些其它處理器。系統(tǒng)使用這一信息來在讀取這個文件的其它數(shù)據(jù)之前決定如何處理它。
   Characteristics域表示了文件的一些特征。比如對于一個可執(zhí)行文件而言,分離調(diào)試文件是如何操作的。調(diào)試器通常使用的方法是將調(diào)試信息從PE文件中分離,并保存到一個調(diào)試文件(.DBG)中。要這么做的話,調(diào)試器需要了解是否要在一個單獨(dú)的文件中尋找調(diào)試信息,以及這個文件是否已經(jīng)將調(diào)試信息分離了。我們可以通過深入可執(zhí)行文件并尋找調(diào)試信息的方法來完成這一工作。要使調(diào)試器不在文件中查找的話,就需要用到IMAGE_FILE_DEBUG_STRIPPED這個特征,它表示文件的調(diào)試信息是否已經(jīng)被分離了。這樣一來,調(diào)試器可以通過快速查看PE文件的頭部的方法來決定文件中是否存在著調(diào)試信息。
   WINNT.H定義了若干其它表示文件頭信息的標(biāo)記,就和以上的例子差不多。我把研究這些標(biāo)記的事情留給讀者作為練習(xí),由你們來看看它們是不是很有趣,這些標(biāo)記位于WINNT.H中的IMAGE_FILE_HEADER結(jié)構(gòu)之后。
   PE文件頭結(jié)構(gòu)中另一個有用的入口是NumberOfSections域,它表示如果你要方便地提取文件信息的話,就需要了解多少個段——更明確一點(diǎn)來說,有多少個段頭部和多少個段實體。每一個段頭部和段實體都在文件中連續(xù)地排列著,所以要決定段頭部和段實體在哪里結(jié)束的話,段的數(shù)目是必需的。以下的函數(shù)從PE文件頭中提取了段的數(shù)目:
PEFILE.C
int WINAPI NumOfSections(LPVOID lpFile)
{
  /* 文件頭部中所表示出的段數(shù)目 */
  return (int)((PIMAGE_FILE_HEADER)
    PEFHDROFFSET (lpFile))->NumberOfSections);
}
如你所見,PEFHDROFFSET以及其它宏用起來非常方便。

PE可選頭部

   PE可執(zhí)行文件中接下來的224個字節(jié)組成了PE可選頭部。雖然它的名字是“可選頭部”,但是請確信:這個頭部并非“可選”,而是“必需”的。OPTHDROFFSET宏可以獲得指向可選頭部的指針:
//PEFILE.H

#define OPTHDROFFSET(a) ((LPVOID)((BYTE *)a + \
                        ((PIMAGE_DOS_HEADER)a)->e_lfanew + \
                        SIZE_OF_NT_SIGNATURE + \
                        sizeof(IMAGE_FILE_HEADER)))
  
可選頭部包含了很多關(guān)于可執(zhí)行映像的重要信息,例如初始的堆棧大小、程序入口點(diǎn)的位置、首選基地址、操作系統(tǒng)版本、段對齊的信息等等。IMAGE_OPTIONAL_HEADER結(jié)構(gòu)如下:
//WINNT.H

typedef struct _IMAGE_OPTIONAL_HEADER {
  //
  // 標(biāo)準(zhǔn)域
  //
  USHORT Magic;
  UCHAR MajorLinkerVersion;
  UCHAR MinorLinkerVersion;
  ULONG SizeOfCode;
  ULONG SizeOfInitializedData;
  ULONG SizeOfUninitializedData;
  ULONG AddressOfEntryPoint;
  ULONG BaseOfCode;
  ULONG BaseOfData;
  //
  // NT附加域
  //
  ULONG ImageBase;
  ULONG SectionAlignment;
  ULONG FileAlignment;
  USHORT MajorOperatingSystemVersion;
  USHORT MinorOperatingSystemVersion;
  USHORT MajorImageVersion;
  USHORT MinorImageVersion;
  USHORT MajorSubsystemVersion;
  USHORT MinorSubsystemVersion;
  ULONG Reserved1;
  ULONG SizeOfImage;
  ULONG SizeOfHeaders;
  ULONG CheckSum;
  USHORT Subsystem;
  USHORT DllCharacteristics;
  ULONG SizeOfStackReserve;
  ULONG SizeOfStackCommit;
  ULONG SizeOfHeapReserve;
  ULONG SizeOfHeapCommit;
  ULONG LoaderFlags;
  ULONG NumberOfRvaAndSizes;
  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;
如你所見,這個結(jié)構(gòu)中所列出的域?qū)嵲谑侨唛L得過分。為了不讓你對所有這些域感到厭煩,我會僅僅討論有用的——就是說,對于探究PE文件格式而言有用的。

標(biāo)準(zhǔn)域

   首先,請注意這個結(jié)構(gòu)被劃分為“標(biāo)準(zhǔn)域”和“NT附加域”。所謂標(biāo)準(zhǔn)域,就是和UNIX可執(zhí)行文件的COFF格式所公共的部分。雖然標(biāo)準(zhǔn)域保留了COFF中定義的名字,但是Windows NT仍然將它們用作了不同的目的——盡管換個名字更好一些。
   ·Magic。我不知道這個域是干什么的,對于示例程序EXEVIEW.EXE示例程序而言,這個值是0x010B或267(譯注:0x010B為.EXE,0x0107為ROM映像,這個信息我是從eXeScope上得來的)。
   ·MajorLinkerVersion、MinorLinkerVersion。表示鏈接此映像的鏈接器版本。隨Window NT build 438配套的Windows NT SDK包含的鏈接器版本是2.39(十六進(jìn)制為2.27)。
   ·SizeOfCode。可執(zhí)行代碼尺寸。
   ·SizeOfInitializedData。已初始化的數(shù)據(jù)尺寸。
   ·SizeOfUninitializedData。未初始化的數(shù)據(jù)尺寸。
   ·AddressOfEntryPoint。在標(biāo)準(zhǔn)域中,AddressOfEntryPoint域是對PE文件格式來說最為有趣的了。這個域表示應(yīng)用程序入口點(diǎn)的位置。并且,對于系統(tǒng)黑客來說,這個位置就是導(dǎo)入地址表(IAT)的末尾。以下的函數(shù)示范了如何從可選頭部獲得Windows NT可執(zhí)行映像的入口點(diǎn)。
//PEFILE.C

LPVOID WINAPI GetModuleEntryPoint(LPVOID lpFile)
{
  PIMAGE_OPTIONAL_HEADER poh;
  poh = (PIMAGE_OPTIONAL_HEADER)OPTHDROFFSET(lpFile);
  if (poh != NULL)
    return (LPVOID)poh->AddressOfEntryPoint;
  else
    return NULL;
}
·BaseOfCode。已載入映像的代碼(“.text”段)的相對偏移量。
   ·BaseOfData。已載入映像的未初始化數(shù)據(jù)(“.bss”段)的相對偏移量。

Windows NT附加域

   添加到Windows NT PE文件格式中的附加域為Windows NT特定的進(jìn)程行為提供了裝載器的支持,以下為這些域的概述。
   ·ImageBase。進(jìn)程映像地址空間中的首選基地址。Windows NT的Microsoft Win32 SDK鏈接器將這個值默認(rèn)設(shè)為0x00400000,但是你可以使用-BASE:linker開關(guān)改變這個值。
   ·SectionAlignment。從ImageBase開始,每個段都被相繼的裝入進(jìn)程的地址空間中。SectionAlignment則規(guī)定了裝載時段能夠占據(jù)的最小空間數(shù)量——就是說,段是關(guān)于SectionAlignment對齊的。
   Windows NT虛擬內(nèi)存管理器規(guī)定,段對齊不能少于頁尺寸(當(dāng)前的x86平臺是4096字節(jié)),并且必須是成倍的頁尺寸。4096字節(jié)是x86鏈接器的默認(rèn)值,但是它可以通過-ALIGN: linker開關(guān)來設(shè)置。
   ·FileAlignment。映像文件首先裝載的最小的信息塊間隔。例如,鏈接器將一個段實體(段的原始數(shù)據(jù))加零擴(kuò)展為文件中最接近的FileAlignment邊界。早先提及的2.39版鏈接器將映像文件以0x200字節(jié)的邊界對齊,這個值可以被強(qiáng)制改為512到65535這么多。
   ·MajorOperatingSystemVersion。表示W(wǎng)indows NT操作系統(tǒng)的主版本號;通常對Windows NT 1.0而言,這個值被設(shè)為1。
   ·MinorOperatingSystemVersion。表示W(wǎng)indows NT操作系統(tǒng)的次版本號;通常對Windows NT 1.0而言,這個值被設(shè)為0。
   ·MajorImageVersion。用來表示應(yīng)用程序的主版本號;對于Microsoft Excel 4.0而言,這個值是4。
   ·MinorImageVersion。用來表示應(yīng)用程序的次版本號;對于Microsoft Excel 4.0而言,這個值是0。
   ·MajorSubsystemVersion。表示W(wǎng)indows NT Win32子系統(tǒng)的主版本號;通常對于Windows NT 3.10而言,這個值被設(shè)為3。
   ·MinorSubsystemVersion。表示W(wǎng)indows NT Win32子系統(tǒng)的次版本號;通常對于Windows NT 3.10而言,這個值被設(shè)為10。
   ·Reserved1。未知目的,通常不被系統(tǒng)使用,并被鏈接器設(shè)為0。
   ·SizeOfImage。表示載入的可執(zhí)行映像的地址空間中要保留的地址空間大小,這個數(shù)字很大程度上受SectionAlignment的影響。例如,考慮一個擁有固定頁尺寸4096字節(jié)的系統(tǒng),如果你有一個11個段的可執(zhí)行文件,它的每個段都少于4096字節(jié),并且關(guān)于65536字節(jié)邊界對齊,那么SizeOfImage域?qū)辉O(shè)為11 * 65536 = 720896(176頁)。而如果一個相同的文件關(guān)于4096字節(jié)對齊的話,那么SizeOfImage域的結(jié)果將是11 * 4096 = 45056(11頁)。這只是個簡單的例子,它說明每個段需要少于一個頁面的內(nèi)存。在現(xiàn)實中,鏈接器通過個別地計算每個段的方法來決定SizeOfImage確切的值。它首先決定每個段需要多少字節(jié),并且最后將頁面總數(shù)向上取整至最接近的SectionAlignment邊界,然后總數(shù)就是每個段個別需求之和了。
   ·SizeOfHeaders。這個域表示文件中有多少空間用來保存所有的文件頭部,包括MS-DOS頭部、PE文件頭部、PE可選頭部以及PE段頭部。文件中所有的段實體就開始于這個位置。
   ·CheckSum。校驗和是用來在裝載時驗證可執(zhí)行文件的,它是由鏈接器設(shè)置并檢驗的。由于創(chuàng)建這些校驗和的算法是私有信息,所以在此不進(jìn)行討論。
   ·Subsystem。用于標(biāo)識該可執(zhí)行文件目標(biāo)子系統(tǒng)的域。每個可能的子系統(tǒng)取值列于WINNT.H的IMAGE_OPTIONAL_HEADER結(jié)構(gòu)之后。
   ·DllCharacteristics。用來表示一個DLL映像是否為進(jìn)程和線程的初始化及終止包含入口點(diǎn)的標(biāo)記。
   ·SizeOfStackReserve、SizeOfStackCommit、SizeOfHeapReserve、SizeOfHeapCommit。這些域控制要保留的地址空間數(shù)量,并且負(fù)責(zé)棧和默認(rèn)堆的申請。在默認(rèn)情況下,棧和堆都擁有1個頁面的申請值以及16個頁面的保留值。這些值可以使用鏈接器開關(guān)-STACKSIZE:與-HEAPSIZE:來設(shè)置。
   ·LoaderFlags。告知裝載器是否在裝載時中止和調(diào)試,或者默認(rèn)地正常運(yùn)行。
   ·NumberOfRvaAndSizes。這個域標(biāo)識了接下來的DataDirectory數(shù)組。請注意它被用來標(biāo)識這個數(shù)組,而不是數(shù)組中的各個入口數(shù)字,這一點(diǎn)非常重要。
   ·DataDirectory。數(shù)據(jù)目錄表示文件中其它可執(zhí)行信息重要組成部分的位置。它事實上就是一個IMAGE_DATA_DIRECTORY結(jié)構(gòu)的數(shù)組,位于可選頭部結(jié)構(gòu)的末尾。當(dāng)前的PE文件格式定義了16種可能的數(shù)據(jù)目錄,這之中的11種現(xiàn)在在使用中。

數(shù)據(jù)目錄

WINNT.H之中所定義的數(shù)據(jù)目錄為:
//WINNT.H
 
// 目錄入口
// 導(dǎo)出目錄
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0
// 導(dǎo)入目錄
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1
// 資源目錄
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2
// 異常目錄
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3
// 安全目錄
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4
// 重定位基本表
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5
// 調(diào)試目錄
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6
// 描述字串
#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7
// 機(jī)器值(MIPS GP)
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8
// TLS目錄
#define IMAGE_DIRECTORY_ENTRY_TLS 9
// 載入配置目錄
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10
  
基本上,每個數(shù)據(jù)目錄都是一個被定義為IMAGE_DATA_DIRECTORY的結(jié)構(gòu)。雖然數(shù)據(jù)目錄入口本身是相同的,但是每個特定的目錄種類卻是完全唯一的。每個數(shù)據(jù)目錄的定義在本文的以后部分被描述為“預(yù)定義段”。
//WINNT.H

typedef struct _IMAGE_DATA_DIRECTORY {
  ULONG VirtualAddress;
  ULONG Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
每個數(shù)據(jù)目錄入口指定了該目錄的尺寸和相對虛擬地址。如果你要定義一個特定的目錄的話,就需要從可選頭部中的數(shù)據(jù)目錄數(shù)組中決定相對的地址,然后使用虛擬地址來決定該目錄位于哪個段中。一旦你決定了哪個段包含了該目錄,該段的段頭部就會被用于查找數(shù)據(jù)目錄的精確文件偏移量位置。
   所以要獲得一個數(shù)據(jù)目錄的話,那么首先你需要了解段的概念。我在下面會對其進(jìn)行描述,這個討論之后還有一個有關(guān)如何定位數(shù)據(jù)目錄的示例。

PE文件段

   PE文件規(guī)范由目前為止定義的那些頭部以及一個名為“段”的一般對象組成。段包含了文件的內(nèi)容,包括代碼、數(shù)據(jù)、資源以及其它可執(zhí)行信息,每個段都有一個頭部和一個實體(原始數(shù)據(jù))。我將在下面描述段頭部的有關(guān)信息,但是段實體則缺少一個嚴(yán)格的文件結(jié)構(gòu)。因此,它們幾乎可以被鏈接器按任何的方法組織,只要它的頭部填充了足夠能夠解釋數(shù)據(jù)的信息。

段頭部

   PE文件格式中,所有的段頭部位于可選頭部之后。每個段頭部為40個字節(jié)長,并且沒有任何的填充信息。段頭部被定義為以下的結(jié)構(gòu):
//WINNT.H

#define IMAGE_SIZEOF_SHORT_NAME 8
typedef struct _IMAGE_SECTION_HEADER {
UCHAR Name[IMAGE_SIZEOF_SHORT_NAME];
  union {
    ULONG PhysicalAddress;
    ULONG VirtualSize;
  } Misc;
  ULONG VirtualAddress;
  ULONG SizeOfRawData;
  ULONG PointerToRawData;
  ULONG PointerToRelocations;
  ULONG PointerToLinenumbers;
  USHORT NumberOfRelocations;
  USHORT NumberOfLinenumbers;
  ULONG Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
  
你如何才能獲得一個特定段的段頭部信息?既然段頭部是被連續(xù)的組織起來的,而且沒有一個特定的順序,那么段頭部必須由名稱來定位。以下的函數(shù)示范了如何從一個給定了段名稱的PE映像文件中獲得一個段頭部:
//PEFILE.C

BOOL WINAPI GetSectionHdrByName(LPVOID lpFile, IMAGE_SECTION_HEADER *sh, char *szSection)
{
  PIMAGE_SECTION_HEADER psh;
  int nSections = NumOfSections (lpFile);
  int i;
  if ((psh = (PIMAGE_SECTION_HEADER)SECHDROFFSET(lpFile))
      != NULL)
  {
    /* 由名稱查找段 */
    for (i = 0; i < nSections; i++)
    {
      if (!strcmp(psh->Name, szSection))
      {
        /* 向頭部復(fù)制數(shù)據(jù) */
        CopyMemory((LPVOID)sh, (LPVOID)psh,
            sizeof(IMAGE_SECTION_HEADER));
        return TRUE;
      }
      else
        psh++;
    }
  }
  return FALSE;
}
這個函數(shù)通過SECHDROFFSET宏將第一個段頭部定位,然后它開始在所有段中循環(huán),并將要尋找的段名稱和每個段的名稱相比較,直到找到了正確的那一個為止。當(dāng)找到了段的時候,函數(shù)將內(nèi)存映像文件的數(shù)據(jù)復(fù)制到傳入函數(shù)的結(jié)構(gòu)中,然后IMAGE_SECTION_HEADER結(jié)構(gòu)的各域就能夠被直接存取了。

段頭部的域

   ·Name。每個段都有一個8字符長的名稱域,并且第一個字符必須是一個句點(diǎn)。
   ·PhysicalAddress或VirtualSize。第二個域是一個union域,現(xiàn)在已不使用了。
   ·VirtualAddress。這個域標(biāo)識了進(jìn)程地址空間中要裝載這個段的虛擬地址。實際的地址由將這個域的值加上可選頭部結(jié)構(gòu)中的ImageBase虛擬地址得到。切記,如果這個映像文件是一個DLL,那么這個DLL就不一定會裝載到ImageBase要求的位置。所以一旦這個文件被裝載進(jìn)入了一個進(jìn)程,實際的ImageBase值應(yīng)該通過使用GetModuleHandle來檢驗。
   ·SizeOfRawData。這個域表示了相對FileAlignment的段實體尺寸。文件中實際的段實體尺寸將少于或等于FileAlignment的整倍數(shù)。一旦映像被裝載進(jìn)入了一個進(jìn)程的地址空間,段實體的尺寸將會變得少于或等于FileAlignment的整倍數(shù)。
   ·PointerToRawData。這是一個文件中段實體位置的偏移量。
   ·PointerToRelocations、PointerToLinenumbers、NumberOfRelocations、NumberOfLinenumbers。這些域在PE格式中不使用。
   ·Characteristics。定義了段的特征。這些值可以在WINNT.H及本光盤(譯注:MSDN的光盤)的PE格式規(guī)范中找到。

值 ????????定義
0x00000020 代碼段
0x00000040 已初始化數(shù)據(jù)段
0x00000080 未初始化數(shù)據(jù)段
0x04000000 該段數(shù)據(jù)不能被緩存
0x08000000 該段不能被分頁
0x10000000 共享段
0x20000000 可執(zhí)行段
0x40000000 可讀段
0x80000000 可寫段

定位數(shù)據(jù)目錄

   數(shù)據(jù)目錄存在于它們相應(yīng)的數(shù)據(jù)段中。典型地來說,數(shù)據(jù)目錄是段實體中的第一個結(jié)構(gòu),但不是必需的。由于這個緣故,如果你需要定位一個指定的數(shù)據(jù)目錄的話,就需要從段頭部和可選頭部中獲得信息。
   為了讓這個過程簡單一點(diǎn),我編寫了以下的函數(shù)來定位任何一個在WINNT.H之中定義的數(shù)據(jù)目錄。
// PEFILE.C

LPVOID WINAPI ImageDirectoryOffset(LPVOID lpFile,
    DWORD dwIMAGE_DIRECTORY)
{
  PIMAGE_OPTIONAL_HEADER poh;
  PIMAGE_SECTION_HEADER psh;
  int nSections = NumOfSections(lpFile);
  int i = 0;
  LPVOID VAImageDir;
  /* 必須為0到(NumberOfRvaAndSizes-1)之間 */
  if (dwIMAGE_DIRECTORY >= poh->NumberOfRvaAndSizes)
    return NULL;
  /* 獲得可選頭部和段頭部的偏移量 */
  poh = (PIMAGE_OPTIONAL_HEADER)OPTHDROFFSET(lpFile);
  psh = (PIMAGE_SECTION_HEADER)SECHDROFFSET(lpFile);
  /* 定位映像目錄的相對虛擬地址 */
  VAImageDir = (LPVOID)poh->DataDirectory
      [dwIMAGE_DIRECTORY].VirtualAddress;
  /* 定位包含映像目錄的段 */
  while (i++ < nSections)
  {
    if (psh->VirtualAddress <= (DWORD)VAImageDir &&
        psh->VirtualAddress + 
        psh->SizeOfRawData > (DWORD)VAImageDir)
      break;
    psh++;
  }
  if (i > nSections)
    return NULL;
  /* 返回映像導(dǎo)入目錄的偏移量 */
  return (LPVOID)(((int)lpFile + 
    (int)VAImageDir. psh->VirtualAddress) +
    (int)psh->PointerToRawData);
}
  
該函數(shù)首先確認(rèn)被請求的數(shù)據(jù)目錄入口數(shù)字,然后它分別獲取指向可選頭部和第一個段頭部的兩個指針。它從可選頭部決定數(shù)據(jù)目錄的虛擬地址,然后它使用這個值來決定數(shù)據(jù)目錄定位在哪個段實體之中。如果適當(dāng)?shù)亩螌嶓w已經(jīng)被標(biāo)識了,那么數(shù)據(jù)目錄特定的位置就可以通過將它的相對虛擬地址轉(zhuǎn)換為文件中地址的方法來找到。
posted on 2006-11-18 17:01 獨(dú)孤九劍 閱讀(254) 評論(0)  編輯 收藏 引用 所屬分類: Win32Visual C++ 8.0
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            一本色道久久88综合亚洲精品ⅰ| 亚洲视频电影图片偷拍一区| 亚洲手机成人高清视频| 国产一区二区三区丝袜| 国产亚洲精品aa| 一区二区三区在线免费观看| 久久裸体艺术| 久久精品视频免费| 老司机午夜精品视频| 久久人人97超碰国产公开结果 | 日韩视频在线观看一区二区| 亚洲人妖在线| 日韩一级黄色av| 性高湖久久久久久久久| 久久综合999| 欧美视频官网| 激情一区二区| 亚洲一区二区在线免费观看视频| 久久久www免费人成黑人精品 | 日韩特黄影片| 欧美在线国产| 欧美日韩日本视频| 国产精品久久久久秋霞鲁丝| 影音先锋国产精品| 欧美无乱码久久久免费午夜一区| 亚洲电影在线| 亚洲伦理一区| 久久久国产精品一区二区中文| 久久亚洲综合色一区二区三区| 女人天堂亚洲aⅴ在线观看| 一本综合精品| 欧美电影免费| 在线观看av一区| 欧美亚洲在线视频| 亚洲精品在线观看视频| 久久久美女艺术照精彩视频福利播放| 欧美日韩国产综合视频在线观看 | 亚洲国产精品国自产拍av秋霞 | 国产精品国产精品国产专区不蜜| 久久精品午夜| 欧美日韩一区二区在线观看视频| 欧美成人国产va精品日本一级| 亚洲国产精品久久人人爱蜜臀| 亚洲欧美日韩国产综合在线| 欧美国产精品v| 曰韩精品一区二区| 久久精品在线免费观看| 99国产精品私拍| 欧美精品一区三区| 亚洲激情成人网| 欧美成人精精品一区二区频| 久久av一区二区三区漫画| 国产精品v欧美精品v日韩精品| 久久大逼视频| 久久激情视频久久| 亚洲精品中文字幕在线观看| 久久国产福利| 国产精品久久久久久av下载红粉| 99re8这里有精品热视频免费| 免费成人av| 久久精品视频网| 黑人巨大精品欧美一区二区小视频| 欧美制服第一页| 亚洲欧美日韩精品久久亚洲区| 国产精品捆绑调教| 性色av一区二区三区| 亚洲欧美中文日韩v在线观看| 国产精品一区二区在线| 欧美亚洲免费高清在线观看| 亚洲欧美中文日韩在线| 国产午夜亚洲精品羞羞网站| 亚洲女人av| 欧美成人在线网站| 亚洲欧美成aⅴ人在线观看| 最新日韩在线视频| 亚洲成色www8888| 久久一区二区三区四区| 亚洲国产婷婷香蕉久久久久久| 欧美国产先锋| 欧美日韩国产专区| 欧美一进一出视频| 久久精品亚洲精品国产欧美kt∨| 一区在线播放| 亚洲破处大片| 国产精品伦理| 免费看黄裸体一级大秀欧美| 免费91麻豆精品国产自产在线观看| 亚洲精品精选| 亚洲一区日韩| 亚洲国产精品一区二区三区| 性久久久久久| 一本色道久久精品| 久久大综合网| 国产午夜精品久久| 嫩草成人www欧美| 欧美日韩国产不卡在线看| 亚洲美女黄网| 久久午夜电影网| 国产精品久久久久一区二区| 欧美一区二区在线观看| 欧美在线啊v一区| 亚洲另类一区二区| 亚洲在线一区二区三区| 欧美一级理论片| 老司机精品视频网站| 亚洲一区二区三区中文字幕| 久久精品国产成人| 亚洲资源在线观看| 免费成人性网站| 美女在线一区二区| 一区二区高清在线| 亚洲精品国产拍免费91在线| 久久久午夜精品| 亚洲天堂网在线观看| 久久黄金**| 欧美一区高清| 欧美亚一区二区| 猛男gaygay欧美视频| 国产精品国产自产拍高清av王其| 欧美高清自拍一区| 国产一区二区高清不卡| 9i看片成人免费高清| 有码中文亚洲精品| 香蕉久久国产| 亚洲欧美另类国产| 欧美精品日本| 欧美成人精品| 激情文学一区| 久久久亚洲午夜电影| 欧美在线视频导航| 国产精品久久久久久久浪潮网站| 亚洲日本一区二区| 99在线精品视频在线观看| 蜜臀a∨国产成人精品| 久久精品国产成人| 国产精品久久久久秋霞鲁丝| 日韩视频在线免费| 亚洲一区久久久| 国产精品成人一区二区三区夜夜夜 | 久久中文字幕导航| 玖玖精品视频| 亚洲高清免费视频| 欧美黄色aa电影| 亚洲精品一品区二品区三品区| 亚洲精品欧美日韩专区| 欧美a一区二区| 亚洲精品日韩一| 亚洲一区二区免费在线| 国产精品五区| 久久精品日韩| 亚洲国产日韩在线一区模特| 一区二区三区你懂的| 欧美性天天影院| 欧美在线综合视频| 欧美黄色aa电影| 一区二区三区精品视频在线观看| 国产精品国产三级国产aⅴ入口 | 欧美成人一区二区在线| 国产中文一区| 久久天天狠狠| 亚洲三级色网| 亚洲欧美影院| 极品日韩av| 欧美绝品在线观看成人午夜影视 | 一区二区三区在线视频观看| 久久久人人人| 亚洲另类视频| 久久久亚洲人| 国产精品99久久不卡二区| 国产欧美一区二区三区国产幕精品| 久久视频在线看| 在线视频精品一区| 麻豆精品精品国产自在97香蕉| 99国产精品久久久久久久| 国产欧美精品一区二区色综合| 蜜臀va亚洲va欧美va天堂| 国产精品99久久不卡二区| 久久最新视频| 亚洲欧美日韩在线观看a三区| 永久久久久久| 国产精品视频yy9099| 老司机久久99久久精品播放免费| 亚洲天堂偷拍| 亚洲国产精品视频| 久久久午夜电影| 亚洲图片欧美一区| 在线观看欧美日本| 国产精品日韩欧美一区二区三区| 男女视频一区二区| 亚洲综合色婷婷| 日韩午夜精品视频| 欧美电影免费观看网站| 久久国产精品黑丝| 亚洲午夜精品久久久久久app| 在线看视频不卡| 国产精品一二一区| 欧美风情在线观看| 欧美xx视频| 久久综合免费视频影院| 午夜欧美电影在线观看|