PE 文件編外篇之導入函數表

    今天把導入函數表弄明白了,在昨天代碼的基礎上增長列出導入函數部分。c++

    主程序部分略有改動:如今電腦 CPU 快、內存大,操做內存比硬盤快多了,因此直接把整個文件拷貝到內存再分析!編程

/* Typedef.h
   數據類型
   四彩
   2015-11-29
*/


#ifndef _TYPEDEF_H
#define _TYPEDEF_H


#ifndef _STDBOOL_H
    typedef char            BOOL;
    #define TRUE            1
    #define FALSE           0
#endif


typedef unsigned char       BYTE;       // 無符號 1 字節
typedef unsigned short int  WORD;       // 無符號 2 字節
typedef unsigned int        DWORD;      // 無符號 4 字節


// 獲得一個 DWORD 的高位 WORD 和低位 WORD
#define LWordOfDW(value)  ((WORD)((DWORD)(value) & 0xFFFF))
#define HWordOfDW(value)  ((WORD)((DWORD)(value) >> 16))

// 獲得一個 WORD 的高位 BYTE 和低位 BYTE
#define LByteOfW(value)  ((BYTE)((WORD)(value) & 0xFF))
#define HByteOfW(value)  ((BYTE)((WORD)(value) >> 8))

// 把兩個 WORD 轉化爲一個 DWORD
#define Words2DW(HighWord, LowWord) ((((DWORD)(HighWord)) << 16) + (LowWord))

// 把兩個 BYTE 轉化爲一個 WORD
#define Bytes2W(HighByte, LowByte) ((((WORD)(HighByte)) << 8) + (LowByte))


#endif
/*  PE.h
    PE 文件格式
    四彩
    2015-12-02
*/

/*  PE 文件的整體結構:(內存地址有低到高)
        IMAGE_DOS_HEADER(DOS 頭)
        DOS stub
        "PE00"(PE 標誌)
        IMAGE_FILE_HEADER(文件頭)
        IMAGE_OPTIONAL_HEADER(可選頭)
        IMAGE_DATA_DIRECTORY(數據塊目錄)
        IMAGE_SECTION_HEADER(節表)
        .text節區
        .data節區
        其它節區
        不能被映射的其餘數據
*/
/*
    虛擬地址:Virtual Address(VA),保護模式下訪問內存所使用的邏輯地址。
    裝載基址:Image Base,文件裝入內存的基址。
              默認狀況下,EXE 文件的基址爲 0x00400000,DLL 文件的基址爲 0x10000000。
    相對虛擬地址:Relative Virtual Address(RVA),在內存中相對於裝載基址的偏移量。
    文件偏移地址:File Offset Address(FOA),文件在磁盤上存放時相對於文件開頭的偏移量。
                  文件偏移地址從文件的第一個字節開始計數,起始值爲 0。
*/
/*
    IMAGE_DATA_DIRECTORY 直接定義在 IMAGE_NT_HEADER 裏面,而不在 IMAGE_OPTIONAL_HEADER 裏,
    這致使 IMAGE_FILE_HEADER 中的 SizeOfOptionalHeader 字段數值不許。
*/


#ifndef _PE_H
#define _PE_H


#include "Typedef.h"


// =======================================================================================
// DOS 頭結構:偏移地址 = 0
//
#define IMAGE_DOS_SIGNATURE 0x5A4D          // e_magic 預約義值,即字符 "MZ"

typedef struct tag_IMAE_DOS_HEADER
{
    //                                         偏移  說明
    WORD    e_magic;                        // 0x00  DOS 可執行文件標識符(= "MZ")
    WORD    e_cblp;                         // 0x02
    WORD    e_cp;                           // 0x04
    WORD    e_crlc;                         // 0x06
    WORD    e_cparhdr;                      // 0x08
    WORD    e_minalloc;                     // 0x0A
    WORD    e_maxalloc;                     // 0x0C
    WORD    e_ss;                           // 0x0E
    WORD    e_sp;                           // 0x10
    WORD    e_csum;                         // 0x12
    WORD    e_ip;                           // 0x14
    WORD    e_cs;                           // 0x16
    WORD    e_lfarlc;                       // 0x18
    WORD    e_ovno;                         // 0x1A
    WORD    e_res[4];                       // 0x1C
    WORD    e_oemid;                        // 0x24
    WORD    e_oeminfo;                      // 0x26
    WORD    e_res2[10];                     // 0x28
    DWORD   e_lfanew;                       // 0x3C  PE 簽名的文件偏移地址
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
// ***************************************************************************************


// =======================================================================================
// NT 頭結構:偏移地址 = IMAGE_DOS_HEADER.e_lfanew
//
// ---------------------------------------------------------------------------------------
// NT 頭結構 —— File 頭結構
// Machine 字段預約義值:                    Value      Meaning
#define IMAGE_FILE_MACHINE_UNKNOWN          0
#define IMAGE_FILE_MACHINE_I386             0x014c  // x86
#define IMAGE_FILE_MACHINE_AMD64            0x8664  // x64
#define IMAGE_FILE_MACHINE_IA64             0x0200  // Intel Itanium

/*  Characteristics 是一個標誌的集合
    位   預約義值                           含義
    0   IMAGE_FILE_RELOCS_STRIPPED          文件中沒有重定向信息。在可執行文件中沒有使用。
                                            可執行文件用基址重定向目錄表來表示重定向信息。
    1   IMAGE_FILE_EXECUTABLE_IMAGE         可執行文件。
    2   IMAGE_FILE_LINE_NUMS_STRIPPED       沒有行數信息。在可執行文件中沒有使用。
    3   IMAGE_FILE_LOCAL_SYMS_STRIPPED      沒有局部符號信息。在可執行文件中沒有使用。
    4   IMAGE_FILE_AGGRESIVE_WS_TRIM        已無效。
    5   IMAGE_FILE_LARGE_ADDRESS_AWARE      應用程序能夠處理超過 2 GB 的地址。
    6   未使用
    7   IMAGE_FILE_BYTES_REVERSED_LO        已無效。
    8   IMAGE_FILE_32BIT_MACHINE            但願機器爲32位機。這個值永遠爲 1。
    9   IMAGE_FILE_DEBUG_STRIPPED           沒有調試信息。在可執行文件中沒有使用。
    10  IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP  該程序不能運行於可移動介質中(如軟驅或CD)。
    11  IMAGE_FILE_NET_RUN_FROM_SWAP        程序不能在網上運行。(必須拷貝到內存中執行)
    12  IMAGE_FILE_SYSTEM                   系統文件,如驅動程序。在可執行文件中沒有使用。
    13  IMAGE_FILE_DLL                      動態連接庫(DLL)。
    14  IMAGE_FILE_UP_SYSTEM_ONLY           不能運行於多處理器系統中。
    15  IMAGE_FILE_BYTES_REVERSED_HI        已無效。
*/
// Characteristics 字段預約義值
#define IMAGE_FILE_RELOCS_STRIPPED          0x0001
#define IMAGE_FILE_EXECUTABLE_IMAGE         0x0002
#define IMAGE_FILE_LINE_NUMS_STRIPPED       0x0004
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED      0x0008
#define IMAGE_FILE_AGGRESIVE_WS_TRIM        0x0010
#define IMAGE_FILE_LARGE_ADDRESS_AWARE      0x0020
#define IMAGE_FILE_BYTES_REVERSED_LO        0x0080
#define IMAGE_FILE_32BIT_MACHINE            0x0100
#define IMAGE_FILE_DEBUG_STRIPPED           0x0200
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP  0x0400
#define IMAGE_FILE_NET_RUN_FROM_SWAP        0x0800
#define IMAGE_FILE_SYSTEM                   0x1000
#define IMAGE_FILE_DLL                      0x2000
#define IMAGE_FILE_UP_SYSTEM_ONLY           0x4000
#define IMAGE_FILE_BYTES_REVERSED_HI        0x8000

typedef struct tag_IMAGE_FILE_HEADER
{
    WORD    Machine;                        // 0x04  運行所要求的 CPU 類型。
    WORD    NumberOfSections;               // 0x06  節數。
    DWORD   TimeDateStamp;                  // 0x08  文件建立日期和時間
    DWORD   PointerToSymbolTable;           // 0x0C  指向符號表(用於調試)
    DWORD   NumberOfSymbols;                // 0x10  符號表中符號個數(用於調試)
    WORD    SizeOfOptionalHeader;           // 0x14  IMAGE_OPTIONAL_HEADER 結構大小
    WORD    Characteristics;                // 0x16  屬性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;


// ---------------------------------------------------------------------------------------
// NT 頭結構 —— NT 可選頭結構
typedef struct tag_IMAGE_OPTIONAL_HEADER
{
    //                                         偏移  說明
    // Standard fields.
    WORD    Magic;                          // 0x18  標誌字(Win32 下老是 0x010B)
    BYTE    MajorLinkerVersion;             // 0x1A  連接程序的主版本號
    BYTE    MinorLinkerVersion;             // 0x1B  連接程序的次版本號
    DWORD   SizeOfCode;                     // 0x1C  全部含代碼的節的總大小
    DWORD   SizeOfInitializedData;          // 0x20  全部含已初始化數據的節的總大小
    DWORD   SizeOfUninitializedData;        // 0x24  全部含未初始化數據的節的大小
    DWORD   AddressOfEntryPoint;            // 0x28  程序執行入口地址(RVA)
    DWORD   BaseOfCode;                     // 0x2C  代碼的區塊的起始地址(RVA)
    DWORD   BaseOfData;                     // 0x30  數據的區塊的起始地址(RVA)
    // NT additional fields.
    DWORD   ImageBase;                      // 0x34  首選裝載基址
    DWORD   SectionAlignment;               // 0x38  內存中節的對齊單位(32 位下爲 4K)
    DWORD   FileAlignment;                  // 0x3C  磁盤文件中節的對齊單位(一個扇區大小)
    WORD    MajorOperatingSystemVersion;    // 0x40  要求操做系統最低版本號的主版本號
    WORD    MinorOperatingSystemVersion;    // 0x42  要求操做系統最低版本號的副版本號
    WORD    MajorImageVersion;              // 0x44  可運行於操做系統的主版本號
    WORD    MinorImageVersion;              // 0x46  可運行於操做系統的次版本號
    WORD    MajorSubsystemVersion;          // 0x48  要求最低子系統版本的主版本號
    WORD    MinorSubsystemVersion;          // 0x4A  要求最低子系統版本的次版本號
    DWORD   Win32VersionValue;              // 0x4C  不明。通常爲 0 。
    DWORD   SizeOfImage;                    // 0x50  裝入內存後的總大小
    DWORD   SizeOfHeaders;                  // 0x54  全部頭 + 節表的總大小
    DWORD   CheckSum;                       // 0x58  校檢和
    WORD    Subsystem;                      // 0x5C  可執行文件指望的界面子系統
    WORD    DllCharacteristics;             // 0x5E  DllMain() 函數什麼時候被調用,默認爲 0
    DWORD   SizeOfStackReserve;             // 0x60  初始化時的棧大小
    DWORD   SizeOfStackCommit;              // 0x64  初始化時實際提交的棧大小
    DWORD   SizeOfHeapReserve;              // 0x68  初始化時保留的堆大小
    DWORD   SizeOfHeapCommit;               // 0x6C  初始化時實際提交的堆大小
    DWORD   LoaderFlags;                    // 0x70  與調試有關,默認爲 0
    DWORD   NumberOfRvaAndSizes;            // 0x74  數據目錄表的項數
} IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;


// ---------------------------------------------------------------------------------------
// NT 頭結構 —— 數據目錄結構
typedef struct tag_IMAGE_DATA_DIRECTORY
{
    DWORD VirtualAddress;                   // 數據的相對虛擬地址(RVA)
    DWORD Size;                             // 數據的長度
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;


// ---------------------------------------------------------------------------------------
// NT 頭結構
#define IMAGE_NT_SIGNATURE 0x00004550           // Signature 字段預約義值,即字符"PE00"

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16  // 數據目錄表的項數一直是 16
/*  數據目錄表各項的含義
  索引號        預約義值                            對應的數據塊
    0       IMAGE_DIRECTORY_ENTRY_EXPORT            導出函數表,主要用於 DLL 中的導出函數
    1       IMAGE_DIRECTORY_ENTRY_IMPORT            導入函數表,使用外部函數的數據表
    2       IMAGE_DIRECTORY_ENTRY_RESOURCE          資源數據表
    3       IMAGE_DIRECTORY_ENTRY_EXCEPTION         異常處理表(具體資料不詳)
    4       IMAGE_DIRECTORY_ENTRY_SECURITY          安全處理數據表(具體資料不詳)
    5       IMAGE_DIRECTORY_ENTRY_BASERELOC         重定位信息表,通常和 DLL 相關
    6       IMAGE_DIRECTORY_ENTRY_DEBUG             調試信息表
    7       IMAGE_DIRECTORY_ENTRY_ARCHITECTURE      版權信息表
    8       IMAGE_DIRECTORY_ENTRY_GLOBALPTR         機器值(MIPS GP)(具體資料不詳)
    9       IMAGE_DIRECTORY_ENTRY_TLS               線程信息本地存儲表
    10      IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG       裝配信息表(具體資料不詳)
    11      IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT      輸入函數綁定信息表(具體資料不詳)
    12      IMAGE_DIRECTORY_ENTRY_IAT               導入函數地址表(與 ImportTable 對應,
                                                            由 Loader 填寫的輸入函數地址)
    13      IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT      延遲裝入的函數信息表(具體資料不詳)
    14      IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR    公用組件信息表(具體資料不詳)
    15      未使用
*/
#define IMAGE_DIRECTORY_ENTRY_EXPORT            0
#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
#define IMAGE_DIRECTORY_ENTRY_DEBUG             6
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE      7
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR         8
#define IMAGE_DIRECTORY_ENTRY_TLS               9
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG       10
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT      11
#define IMAGE_DIRECTORY_ENTRY_IAT               12
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT      13
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR    14

typedef struct tag_IMAGE_NT_HEADERS
{
    //                                         偏移  說明
    DWORD                   Signature;      // 0x00  PE 文件簽名(= "PE00")
    IMAGE_FILE_HEADER       FileHeader;     // 0x04  文件頭結構
    IMAGE_OPTIONAL_HEADER   OptionalHeader; // 0x18  可選 NT 頭結構
    IMAGE_DATA_DIRECTORY    DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
                                            // 0x78  數據目錄表
} IMAGE_NT_HEADER, *PIMAGE_NT_HEADER;
// ***************************************************************************************


// =======================================================================================
// 節表:偏移地址 = IMAGE_DOS_HEADER.e_lfanew + sizeof(IMAGE_NT_HEADER)
//       項數由 IMAGE_NT_HEADER 中的 FileHeader.NumberOfSections 指定
/*
    一個節中的數據只是屬性相同,並不必定是同一種用途的內容,所以僅依靠節表是沒法肯定和定位
    的,而要由數據目錄表來定位。
    節表是節的目錄,數據目錄表是存儲在節裏的邏輯元素的目錄。

    不管是在內存中仍是在磁盤文件中,節都是按基址排列的,並且都要對齊,但對齊值通常不一樣。
    映射到內存後,全部頭和節表的偏移位置與大小均沒有變化,而各節基址的偏移位置發生了變化。
    無論節是在文件中仍是被加載到內存中,節中數據的位置相對該節的基址是不變的。
        在內存中:數據相對節的起始位置的偏移h = 數據的RVA - 節的RVA
        在文件中:數據相對節的起始位置的偏移h = 數據的FOA – 節的FOA
*/
//
// 節(區塊)頭結構
//
#define IMAGE_SIZEOF_SHORT_NAME     8       // Name 字段最長 8 字節

// Characteristics 字段預約義值
#define IMAGE_SCN_CNT_CODE                  0x20        // 包含代碼
#define IMAGE_SCN_CNT_INITIALIZED_DATA      0x40        // 包含已初始化數據
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA    0x80        // 包含未初始化數據
#define IMAGE_SCN_MEM_DISCARDABLE           0x2000000   // 數據在進程開始後將被丟棄
#define IMAGE_SCN_MEM_NOT_CACHED            0x4000000   // 數據不通過緩存
#define IMAGE_SCN_MEM_NOT_PAGED             0x8000000   // 數據不被交換出內存
#define IMAGE_SCN_MEM_SHARED                0x10000000  // 數據可共享
#define IMAGE_SCN_MEM_EXECUTE               0x20000000  // 可執行
#define IMAGE_SCN_MEM_READ                  0x40000000  // 可讀
#define IMAGE_SCN_MEM_WRITE                 0x80000000  // 可寫

typedef struct tag_IMAGE_SECTION_HEADER
{
    //                                         偏移  說明
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];  // 0x00  節表名稱(僅供編程參考)
    union                                   // 0x08  通常是取後一個,即節的真實長度
    {
        DWORD PhysicalAddress;   // 物理地址(在目標文件中使用,重定位到的地址)
        DWORD VirtualSize;       // 真實長度(在可執行文件中使用,未作對齊處理的實際大小)
    } Misc;
    DWORD   VirtualAddress;                 // 0x0C  裝載到內存的基址(內存對齊後的 RVA)
    DWORD   SizeOfRawData;                  // 0x10  在文件中的大小(在磁盤中對齊後的大小)
    DWORD   PointerToRawData;               // 0x14  在文件中的偏移量(從文件頭開始算起)
    DWORD   PointerToRelocations;           // 0x18  重定位的偏移(在 OBJ 文件中供調試用)
    DWORD   PointerToLinenumbers;           // 0x1C  行號表的偏移(同上)
    WORD    NumberOfRelocations;            // 0x20  重定位項的數目(同上)
    WORD    NumberOfLinenumbers;            // 0x22  行號表中行號的數目(同上)
    DWORD   Characteristics;                // 0x24  節的屬性(如可讀、可寫、可執行等)
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
// ***************************************************************************************

// =======================================================================================
// ---------------------------------------------------------------------------------------
//  導入函數表:偏移地址 = IMAGE_NT_HEADER.IMAGE_DATA_DIRECTORY[1].VirtualAddress
//              以一個全 0 成員的數組項結尾。
/*
    一、導入函數是被某模塊調用,但又不在調用者模塊中(實際位於別的 DLL 文件裏)的函數。
       調用者模塊裏只保留一些函數信息,包括函數名及其駐留的 DLL 文件名。
    二、導入函數表就是一個 IMAGE_IMPORT_DESCRIPTOR 數組。
       每一個數組項(一個 IMAGE_IMPORT_DESCRIPTOR 結構)對應一個導入的 DLL。
    三、每一個 IMAGE_IMPORT_DESCRIPTOR 結構指向兩個不一樣的 IMAGE_THUNK_DATA 結構數組。
       加載前 OriginalFirstThunk 與 FirstThunk 指向的數組內容是相同的,都包含導入信息;
       加載後 FirstThunk 指向的數組指向實際的函數地址。
*/
//
// 導入函數表數組項結構
//
typedef struct tag_IMAGE_IMPORT_BY_NAME
{
    WORD Hint;                      // 本函數在其所駐留 DLL 的導出表中的索引號(非必須)
    char Name[1];                   // 函數名(可變尺寸域)
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

typedef struct tag_IMAGE_THUNK_DATA
{
    union
    {
        DWORD ForwarderString;
        DWORD Function;             // 函數地址
        DWORD Ordinal;              // 函數序號
        DWORD AddressOfData;        // 指向一個 IMAGE_IMPORT_BY_NAME 結構的指針
    } u1;
} IMAGE_THUNK_DATA, *PIMAGE_THUNK_DATA;
/* 經過二進制的最高位判斷是否按序號導入:1,按序號導入(Ordinal 的低16位就是導入序號);
                                         0,按名字導入(AddressOfData 指向名字信息)。
*/
#define IMAGE_SNAP(Ordinal)     ((Ordinal) >> 31)       // 判斷是否按序號導入
#define IMAGE_ORDINAL(Ordinal)  ((Ordinal) & 0xFFFF)    // 獲取導入序號

typedef struct tag_IMAGE_IMPORT_DESCRIPTOR
{
    union                                   // 0x00
    {
        DWORD Characteristics;
        DWORD OriginalFirstThunk;   // 指向一個 IMAGE_THUNK_DATA 數組
    } DUMMYUNIONNAME;
    DWORD TimeDateStamp;
    DWORD ForwarderChain;
    DWORD Name;                             // 0x0C  指向 DLL 文件名的指針
    DWORD FirstThunk;                       // 0x10  指向另外一個 IMAGE_THUNK_DATA 數組(RVA)
} IMAGE_IMPORT_DESCRIPTOR, *PIMAGE_IMPORT_DESCRIPTOR;
// ***************************************************************************************


// =======================================================================================
// 輸出函數表
// ---------------------------------------------------------------------------------------
/*  函數功能:
        相對虛擬地址轉換爲文件偏移地址
    參數表:
        pBase = 將整個文件都拷貝到內存的緩衝區基址
        dwRva = 相對虛擬地址
    返回值:
        文件偏移地址
*/
DWORD Rva2Foa(BYTE *pBase, DWORD dwRva);


/*  函數功能:
        文件偏移地址轉換爲相對虛擬地址
    參數表:
        pBase = 將整個文件都拷貝到內存的緩衝區基址
        dwFoa = 文件偏移地址
    返回值:
        相對虛擬地址
*/
DWORD Foa2Rva(BYTE *pBase, DWORD dwFoa);

// ---------------------------------------------------------------------------------------
/*  函數功能:
        統計 PE 文件所有導入函數的個數
    參數表:
        pBase = 將整個文件都拷貝到內存的緩衝區基址
    返回值:
        所有導入函數的個數
*/
DWORD GetNumOfImpFun(BYTE *pBase);

/*  函數功能:
        取得 PE 文件中指定順序號的導入函數的函數名或導入序號
    入口參數表:
        pBase = 將整個文件都拷貝到內存的緩衝區基址
        iSN   = 順序號(從 0 開始)
    出口參數表:
        sDllName = 包含該函數的 dll 模塊名
        sFunName = 函數名
        wOrd     = 序號
    返回值:
        函數的導入方式:0 = 函數名導入,1 = 序號導入
        -1 = 順序號超過導入函數總數
*/
int GetSnapOfImpFun(BYTE *pBase, DWORD iSN, char *sDllName, char *sFunName, WORD *wOrd);
// ***************************************************************************************


#endif
/*  PE.c
    PE 文件格式輸出函數
    四彩
    2015-12-01
*/

#include <stdlib.h>
#include <string.h>
#include "PE.h"


// =======================================================================================
// RVA 與 FOA 的互相轉換
// ---------------------------------------------------------------------------------------
/*  相對虛擬地址轉換爲文件偏移地址的轉換思路:
    (1)掃描節表,將每一個節在內存中的基址加上節的大小獲得節的終址,
         根據基址、終址判斷 dwRva 是否在該節內範圍。
    (2)用 dwRva 減去該節的內存基址,獲得 dwRva 相對於該節內存基址的偏移量 RVA'。
         將該節在文件中的基址加上 RVA',獲得文件偏移地址。
*/
/*  函數功能:
        相對虛擬地址轉換爲文件偏移地址
    參數表:
        pBase = 將整個文件都拷貝到內存的緩衝區基址
        dwRva = 相對虛擬地址
    返回值:
        文件偏移地址
*/
DWORD Rva2Foa(BYTE *pBase, DWORD dwRva)
{
    WORD i;
    IMAGE_NT_HEADER         *pNtH;
    IMAGE_SECTION_HEADER    *pSH;

    pNtH = (IMAGE_NT_HEADER *)(pBase + ((IMAGE_DOS_HEADER *)pBase)->e_lfanew);
    if(dwRva < pNtH->OptionalHeader.SizeOfHeaders)          // 在頭部
        return dwRva;

    // 遍歷各節
    pSH = (IMAGE_SECTION_HEADER *)((BYTE *)pNtH + sizeof(IMAGE_NT_HEADER));
    for(i = 0; i < pNtH->FileHeader.NumberOfSections; i++)
    {
        if(dwRva >= pSH->VirtualAddress && dwRva < pSH->VirtualAddress + pSH->Misc.VirtualSize)
            return pSH->PointerToRawData + dwRva - pSH->VirtualAddress;
        pSH++;
    }

    return 0;
}

// ---------------------------------------------------------------------------------------
/*  文件偏移地址轉換爲相對虛擬地址的轉換思路:
    (1)掃描節表,將每一個節在文件中的基址加上節的大小獲得節的終址,
         根據基址、終址判斷 dwFoa 是否在該節範圍內。
    (2)用 dwFoa 減去該節的文件基址,獲得 dwFoa 相對於該節文件基址的偏移量 FOA'。
         將該節在內存中的基址加上 FOA',獲得相對虛擬地址。
*/
/*  函數功能:
        文件偏移地址轉換爲相對虛擬地址
    參數表:
        pBase = 將整個文件都拷貝到內存的緩衝區基址
        dwFoa = 文件偏移地址
    返回值:
        相對虛擬地址
*/
DWORD Foa2Rva(BYTE *pBase, DWORD dwFoa)
{
    WORD i;
    IMAGE_NT_HEADER         *pNtH;
    IMAGE_SECTION_HEADER    *pSH;

    pNtH = (IMAGE_NT_HEADER *)(pBase + ((IMAGE_DOS_HEADER *)pBase)->e_lfanew);
    if(dwFoa < pNtH->OptionalHeader.SizeOfHeaders)
        return dwFoa;

    pSH = (IMAGE_SECTION_HEADER *)((BYTE *)pNtH + sizeof(IMAGE_NT_HEADER));
    for(i = 0; i < pNtH->FileHeader.NumberOfSections; i++)
    {
        if(dwFoa >= pSH->PointerToRawData && dwFoa < pSH->PointerToRawData + pSH->Misc.VirtualSize)
            return pSH->VirtualAddress + dwFoa - pSH->PointerToRawData;
        pSH++;
    }
    return 0;
}
// ***************************************************************************************


// =======================================================================================
// 導出函數與導入函數
//
// ---------------------------------------------------------------------------------------
/*  獲取全部導入函數的函數名或序號:
    載入 PE 文件到內存,用內存緩衝區基址定位 IMAGE_NT_HEADER 地址。
    用數據目錄表中第二項的 VirtualAddress 值,定位第一個 IMAGE_IMPORT_DESCRIPTOR 地址。
    檢查 FirstThunk :若不爲 0,用 OriginalFirstThunk 定位第一個 IMAGE_THUNK_DATA 數組。
    檢查 u1.Ordinal 的二進制最高位:
        若爲 1,那麼函數是由序號導入的,能夠從 Ordinal 的低字提取序號;
        若爲 0,那麼函數是由函數名導入的,能夠由 AddressOfData 指向的 IMAGE_IMPORT_BY_NAME
            取得函數名。
*/
/*  函數功能:
        統計 PE 文件所有導入函數的個數
    參數表:
        pBase = 將整個文件都拷貝到內存的緩衝區基址
    返回值:
        所有導入函數的個數
*/
DWORD GetNumOfImpFun(BYTE *pBase)
{
    DWORD i = 0;
    IMAGE_NT_HEADER         *pNtH;
    IMAGE_IMPORT_DESCRIPTOR *pImpDsc;
    IMAGE_THUNK_DATA        *pThk;

    pNtH = (IMAGE_NT_HEADER *)(pBase + ((IMAGE_DOS_HEADER *)pBase)->e_lfanew);
    if(pNtH->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size == 0)
        return 0;

    pImpDsc = (IMAGE_IMPORT_DESCRIPTOR *)(pBase +
        Rva2Foa(pBase, pNtH->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress));
    while(pImpDsc->Name)
    {
        pThk = (IMAGE_THUNK_DATA *)(pBase + Rva2Foa(pBase, pImpDsc->FirstThunk));
        while(pThk->u1.Ordinal)
        {
            pThk++;
            i++;
        }
        pImpDsc++;
    }

    return i;
}

/*  函數功能:
        取得 PE 文件中指定順序號的導入函數的函數名或導入序號
    入口參數表:
        pBase = 將整個文件都拷貝到內存的緩衝區基址
        iSN   = 順序號(從 0 開始)
    出口參數表:
        sDllName = 包含該函數的 dll 模塊名
        sFunName = 函數名
        wOrd     = 序號
    返回值:
        函數的導入方式:0 = 函數名導入,1 = 序號導入
        -1 = 順序號超過導入函數總數
*/
int GetSnapOfImpFun(BYTE *pBase, DWORD iSN, char *sDllName, char *sFunName, WORD *wOrd)
{
    DWORD i = 0;
    IMAGE_NT_HEADER         *pNtH;
    IMAGE_IMPORT_DESCRIPTOR *pImpDsc;
    IMAGE_THUNK_DATA        *pThk;

    pNtH = (IMAGE_NT_HEADER *)(pBase + ((IMAGE_DOS_HEADER *)pBase)->e_lfanew);
    if(pNtH->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size == 0)
        return 0;

    pImpDsc = (IMAGE_IMPORT_DESCRIPTOR *)(pBase +
        Rva2Foa(pBase, pNtH->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress));
    while(pImpDsc->Name)
    {
        pThk = (IMAGE_THUNK_DATA *)(pBase + Rva2Foa(pBase, pImpDsc->FirstThunk));
        while(pThk->u1.Ordinal)
        {
            if(i == iSN)                            // 找到了
            {
                strcpy(sDllName, (char *)(pBase + Rva2Foa(pBase, (pImpDsc->Name))));
                if(IMAGE_SNAP(pThk->u1.Ordinal))    // 序號導入
                {
                    *wOrd = IMAGE_ORDINAL(pThk->u1.Ordinal);
                    return 1;
                }
                else                                // 函數名導入
                {
                    strcpy(sFunName, ((IMAGE_IMPORT_BY_NAME *)(pBase + Rva2Foa(pBase,
                                                        pThk->u1.AddressOfData)))->Name);
                    return 0;
                }
            }

            pThk++;
            i++;
        }
        pImpDsc++;
    }

    return -1;
}
// ***************************************************************************************
/*  PEM.c
    顯示 PE 文件格式信息
    四彩
    2015-11-30
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "PE.h"

#define MAX_PATH    260     // 最長字符串長度

void PrintDosHeader(IMAGE_DOS_HEADER *pDosHeader);
void PrintFileHeader(IMAGE_FILE_HEADER FileHeader);
void PrintOptionalHeader(IMAGE_OPTIONAL_HEADER OptHeader);
void PrintDataDircrory(IMAGE_DATA_DIRECTORY DataDirectory);
void PrintSectionHeader(IMAGE_SECTION_HEADER *pSectionHeader);

int main(int argc, char **argv)
{
    FILE    *fp;
    DWORD   i, dwFileLen ;

    BYTE                    *pBase;
    IMAGE_DOS_HEADER        *pDosHeader;
    IMAGE_NT_HEADER         *pNtHeader;
    IMAGE_SECTION_HEADER    *pFstSctHeader, *pSctHeader;

    DWORD   dwNumOfImptFun;
    int     snap;
    char    sDllName[MAX_PATH], sFunName[MAX_PATH];
    WORD    wOrd;

    if(argc != 2)
    {
        printf("Usage : %s exeFileName\n", argv[0]);
        return -1;
    }

    // 將整個文件都拷貝到內存
    fp = fopen(argv[1], "rb");
    if (fp == NULL)
    {
        printf("Error : open file\n");
        return -1;
    }
    fseek(fp, 0, SEEK_END);
    dwFileLen = ftell(fp);
    pBase = (BYTE *)malloc(dwFileLen);
    if(pBase == NULL)
    {
        printf("Error : malloc memory\n");
        return -1;
    }
    fseek(fp, 0, SEEK_SET);
    fread(pBase, dwFileLen, 1, fp);
    fclose(fp);

    // 檢查是否爲 PE 文件,並給相關結構指針賦值
    pDosHeader = (IMAGE_DOS_HEADER *)pBase;
    if(pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
    {
        printf("Error : NOT DOS file\n");
        free(pBase);
        return -1;
    }
    pNtHeader = (IMAGE_NT_HEADER *)(pBase + ((IMAGE_DOS_HEADER *)pBase)->e_lfanew);
    if(pNtHeader->Signature != IMAGE_NT_SIGNATURE)
    {
        printf("Error : NOT PE file\n");
        free(pBase);
        return -1;
    }
    pFstSctHeader = (IMAGE_SECTION_HEADER *)((BYTE *)pNtHeader + sizeof(IMAGE_NT_HEADER));

    // 顯示 IMAGE_DOS_HEADER 信息
    printf("IMAGE_DOS_HEADER :\n");
    PrintDosHeader(pDosHeader);

    // 顯示 IMAGE_NT_HEADER 信息
    printf("\nIMAGE_NT_HEADER  :\n");               // Signature
    printf("Signature        : %c%c%d%d\n",         // 低低高高
        LByteOfW(LWordOfDW(pNtHeader->Signature)), HByteOfW(LWordOfDW(pNtHeader->Signature)),
        LByteOfW(HWordOfDW(pNtHeader->Signature)), HByteOfW(HWordOfDW(pNtHeader->Signature)));

    printf("\nIMAGE_FILE_HEADER    :\n");           // IMAGE_FILE_HEADER
    PrintFileHeader(pNtHeader->FileHeader);

    printf("\nIMAGE_OPTIONAL_HEADER       :\n");    // IMAGE_OPTIONAL_HEADER
    PrintOptionalHeader(pNtHeader->OptionalHeader);

    printf("\nIMAGE_DATA_DIRECTORY : ");            // 遍歷所有 IMAGE_DATA_DIRECTORY
    printf("\n   NO.  VirtualAddress   Size");
    for(i = 0; i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++)
    {
        printf("\n   %2d", i);
        PrintDataDircrory(pNtHeader->DataDirectory[i]);
    }

    // 遍歷顯示所有 IMAGE_SECTION_HEADER 信息
    pSctHeader = pFstSctHeader;
    printf("\n\nIMAGE_SECTION_HEADER :");
    for(i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++)
    {
        printf("\nSECTION_HEADER NO    : %02d\nFOA                  : 0x%08X\n", i,
                                    Rva2Foa(pBase, (DWORD)((BYTE *)pSctHeader - pBase)));
        PrintSectionHeader(pSctHeader);
        pSctHeader++;
    }

    // 遍歷顯示所有導入函數信息
    dwNumOfImptFun = GetNumOfImpFun(pBase);
    printf("\nIMAGE_IMPORT_DESCRIPTOR : %d", dwNumOfImptFun);
    printf("\n SN    DllName      FunName");
    for(i = 0; i < dwNumOfImptFun; i++)
    {
        snap = GetSnapOfImpFun(pBase, i, sDllName, sFunName, &wOrd);
        if(snap == 0)                       // 函數名導入
        {
            printf("\n %02d  % 12s  %s", i, sDllName, sFunName);
        }
        else if(snap == 1)                  // 序號導入
        {
            printf("\n %02d  % 12s  0x%04X", i, sDllName, wOrd);
        }
    }

    free(pBase);
    return 0;
}



// 顯示 IMAGE_DOS_HEADER 信息
void PrintDosHeader(IMAGE_DOS_HEADER *pDosHeader)
{
    int i;

    printf("e_magic          : %c%c\n", LByteOfW(pDosHeader->e_magic),
                                                           HByteOfW(pDosHeader->e_magic));
    printf("e_cblp           : 0x%04X\n", pDosHeader->e_cblp);
    printf("e_cp             : 0x%04X\n", pDosHeader->e_cp);
    printf("e_crlc           : 0x%04X\n", pDosHeader->e_crlc);
    printf("e_cparhdr        : 0x%04X\n", pDosHeader->e_cparhdr);
    printf("e_minalloc       : 0x%04X\n", pDosHeader->e_minalloc);
    printf("e_maxalloc       : 0x%04X\n", pDosHeader->e_maxalloc);
    printf("e_ss             : 0x%04X\n", pDosHeader->e_ss);
    printf("e_sp             : 0x%04X\n", pDosHeader->e_sp);
    printf("e_csum           : 0x%04X\n", pDosHeader->e_csum);
    printf("e_ip             : 0x%04X\n", pDosHeader->e_ip);
    printf("e_cs             : 0x%04X\n", pDosHeader->e_cs);
    printf("e_lfarlc         : 0x%04X\n", pDosHeader->e_lfarlc);
    printf("e_ovno           : 0x%04X\n", pDosHeader->e_ovno);
    printf("e_res            : ");
    for(i = 0; i < 4; i++)
        printf("0x%X, ", pDosHeader->e_res[i]);
    printf("\ne_oemid          : 0x%04X\n", pDosHeader->e_oemid);
    printf("e_oeminfo        : 0x%04X\n", pDosHeader->e_oeminfo);
    printf("e_res2           : ");
    for(i = 0; i < 10; i++)
        printf("0x%X, ", pDosHeader->e_res2[i]);
    printf("\ne_lfanew         : 0x%04X\n", pDosHeader->e_lfanew);
}

// 顯示 IMAGE_FILE_HEADER 信息
void PrintFileHeader(IMAGE_FILE_HEADER FileHeader)
{
    printf("Machine              : 0x%04X\n", FileHeader.Machine);
    printf("NumberOfSections     : 0x%04X\n", FileHeader.NumberOfSections);
    printf("TimeDateStamp        : 0x%08X\n", FileHeader.TimeDateStamp);
    printf("PointerToSymbolTable : 0x%08X\n", FileHeader.PointerToSymbolTable);
    printf("NumberOfSymbols      : 0x%08X\n", FileHeader.NumberOfSymbols);
    printf("SizeOfOptionalHeader : 0x%04X\n", FileHeader.SizeOfOptionalHeader);
    printf("Characteristics      : 0x%04X\n", FileHeader.Characteristics);
}

// 顯示 IMAGE_OPTIONAL_HEADER 信息
void PrintOptionalHeader(IMAGE_OPTIONAL_HEADER OptHeader)
{
    // Standard fields.
    printf("Magic                       : 0x%04X\n", OptHeader.Magic);
    printf("MajorLinkerVersion          : 0x%02X\n", OptHeader.MajorLinkerVersion);
    printf("MinorLinkerVersion          : 0x%02X\n", OptHeader.MinorLinkerVersion);
    printf("SizeOfCode                  : 0x%08X\n", OptHeader.SizeOfCode);
    printf("SizeOfInitializedData       : 0x%08X\n", OptHeader.SizeOfInitializedData);
    printf("SizeOfUninitializedData     : 0x%08X\n", OptHeader.SizeOfUninitializedData);
    printf("AddressOfEntryPoint         : 0x%08X\n", OptHeader.AddressOfEntryPoint);
    printf("BaseOfCode                  : 0x%08X\n", OptHeader.BaseOfCode);
    printf("BaseOfData                  : 0x%08X\n", OptHeader.BaseOfData);

    // NT additional fields.
    printf("ImageBase                   : 0x%08X\n", OptHeader.ImageBase);
    printf("SectionAlignment            : 0x%08X\n", OptHeader.SectionAlignment);
    printf("FileAlignmen                : 0x%08X\n", OptHeader.FileAlignment);
    printf("MajorOperatingSystemVersion : 0x%04X\n", OptHeader.MajorOperatingSystemVersion);
    printf("MinorOperatingSystemVersion : 0x%04X\n", OptHeader.MinorOperatingSystemVersion);
    printf("MajorImageVersion           : 0x%04X\n", OptHeader.MajorImageVersion);
    printf("MinorImageVersion           : 0x%04X\n", OptHeader.MinorImageVersion);
    printf("MajorSubsystemVersion       : 0x%04X\n", OptHeader.MajorSubsystemVersion);
    printf("MinorSubsystemVersion       : 0x%04X\n", OptHeader.MinorSubsystemVersion);
    printf("Win32VersionValue           : 0x%08X\n", OptHeader.Win32VersionValue);
    printf("SizeOfImage                 : 0x%08X\n", OptHeader.SizeOfImage);
    printf("SizeOfHeaders               : 0x%08X\n", OptHeader.SizeOfHeaders);
    printf("CheckSum                    : 0x%08X\n", OptHeader.CheckSum);
    printf("Subsystem                   : 0x%04X\n", OptHeader.Subsystem);
    printf("DllCharacteristics          : 0x%04X\n", OptHeader.DllCharacteristics);
    printf("SizeOfStackReserve          : 0x%08X\n", OptHeader.SizeOfStackReserve);
    printf("SizeOfStackCommit           : 0x%08X\n", OptHeader.SizeOfStackCommit);
    printf("SizeOfHeapReserve           : 0x%08X\n", OptHeader.SizeOfHeapCommit);
    printf("SizeOfHeapCommit            : 0x%08X\n", OptHeader.SizeOfHeapCommit);
    printf("LoaderFlags                 : 0x%08X\n", OptHeader.LoaderFlags);
    printf("NumberOfRvaAndSizes         : 0x%08X\n", OptHeader.NumberOfRvaAndSizes);
}

// 顯示 IMAGE_DATA_DIRECTORY 信息
void PrintDataDircrory(IMAGE_DATA_DIRECTORY DataDirectory)
{
    printf("   0x%08X       0x%08X", DataDirectory.VirtualAddress, DataDirectory.Size);
}

// 顯示 IMAGE_SECTION_HEADER 信息
void PrintSectionHeader(IMAGE_SECTION_HEADER *pSectionHeader)
{
    if(pSectionHeader->Name)
        printf("Name                 : %s\n", pSectionHeader->Name);
    else
        printf("Name                 : NULL\n");
    printf("Misc.VirtualSize     : 0x%08X\n", pSectionHeader->Misc.VirtualSize);
    printf("VirtualAddress       : 0x%08X\n", pSectionHeader->VirtualAddress);
    printf("SizeOfRawData        : 0x%08X\n", pSectionHeader->SizeOfRawData);
    printf("PointerToRawData     : 0x%08X\n", pSectionHeader->PointerToRawData);
    printf("PointerToRelocations : 0x%08X\n", pSectionHeader->PointerToRelocations);
    printf("PointerToLinenumbers : 0x%08X\n", pSectionHeader->PointerToLinenumbers);
    printf("NumberOfRelocations  : 0x%04X\n", pSectionHeader->NumberOfRelocations);
    printf("NumberOfLinenumbers  : 0x%04X\n", pSectionHeader->NumberOfLinenumbers);
    printf("Characteristics      : 0x%08X\n", pSectionHeader->Characteristics);
}

    照例貼出本程序的分析結果(受字數限制,只貼了今天新增的):數組

IMAGE_IMPORT_DESCRIPTOR : 59
 SN    DllName      FunName
 00  KERNEL32.dll  DeleteCriticalSection
 01  KERNEL32.dll  EnterCriticalSection
 02  KERNEL32.dll  FreeLibrary
 03  KERNEL32.dll  GetCurrentProcess
 04  KERNEL32.dll  GetCurrentProcessId
 05  KERNEL32.dll  GetCurrentThreadId
 06  KERNEL32.dll  GetLastError
 07  KERNEL32.dll  GetModuleHandleA
 08  KERNEL32.dll  GetProcAddress
 09  KERNEL32.dll  GetStartupInfoA
 10  KERNEL32.dll  GetSystemTimeAsFileTime
 11  KERNEL32.dll  GetTickCount
 12  KERNEL32.dll  InitializeCriticalSection
 13  KERNEL32.dll  LeaveCriticalSection
 14  KERNEL32.dll  LoadLibraryA
 15  KERNEL32.dll  QueryPerformanceCounter
 16  KERNEL32.dll  SetUnhandledExceptionFilter
 17  KERNEL32.dll  Sleep
 18  KERNEL32.dll  TerminateProcess
 19  KERNEL32.dll  TlsGetValue
 20  KERNEL32.dll  UnhandledExceptionFilter
 21  KERNEL32.dll  VirtualProtect
 22  KERNEL32.dll  VirtualQuery
 23    msvcrt.dll  __dllonexit
 24    msvcrt.dll  __getmainargs
 25    msvcrt.dll  __initenv
 26    msvcrt.dll  __lconv_init
 27    msvcrt.dll  __set_app_type
 28    msvcrt.dll  __setusermatherr
 29    msvcrt.dll  _acmdln
 30    msvcrt.dll  _amsg_exit
 31    msvcrt.dll  _cexit
 32    msvcrt.dll  _fmode
 33    msvcrt.dll  _initterm
 34    msvcrt.dll  _iob
 35    msvcrt.dll  _lock
 36    msvcrt.dll  _onexit
 37    msvcrt.dll  _unlock
 38    msvcrt.dll  abort
 39    msvcrt.dll  calloc
 40    msvcrt.dll  exit
 41    msvcrt.dll  fclose
 42    msvcrt.dll  fopen
 43    msvcrt.dll  fprintf
 44    msvcrt.dll  fread
 45    msvcrt.dll  free
 46    msvcrt.dll  fseek
 47    msvcrt.dll  ftell
 48    msvcrt.dll  fwrite
 49    msvcrt.dll  malloc
 50    msvcrt.dll  memset
 51    msvcrt.dll  memcpy
 52    msvcrt.dll  printf
 53    msvcrt.dll  puts
 54    msvcrt.dll  signal
 55    msvcrt.dll  strcpy
 56    msvcrt.dll  strlen
 57    msvcrt.dll  strncmp
 58    msvcrt.dll  vfprint
相關文章
相關標籤/搜索