PE 文件格式分析

来源:互联网 发布:鸢尾花数据集 编辑:程序博客网 时间:2024/04/29 12:40
PE 文件格式分析

     PE文件是任何可执行模块或者DLL的文件格式。PE文件以64字节的DOS文件头(IMAGE_DOS_HEADER,)开始,之后是一小段DOS程序(DOS头的概念是从16位的WINDOWS可执行程序(NE格式)中来的,这个部分主要用在OS/2可执行程序、自解压文档及其他应用程序),然后是248字节的NT文件头(IMAGE_NT_HEADERS),NT文件头的偏移地址由IMAGE_DOS_HEADER结构的e_lfanew给出。PE文件通常保留了16个数据目录,最常见就是导入表,导出表,资源和重定位表。

windowsDOS文件标记和PE文件标记都定义了宏标记:

#difine IMAGE_DOS_SIGNATURE 0x5A4D  //MZ(可以用ultraedit看到)

#difine IMAGE_NT_SIGNATURE  0x00004550 // PE00

这两个宏,主要用来判断该文件是否位PE文件。

 

列举出该可执行文件包含的模块中的导入函数,及其导入函数的地址:

       HMODULE hMod = ::GetModuleHandle(NULL);

       IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)hMod;

       IMAGE_OPTIONAL_HEADER * pOptHeader =

              (IMAGE_OPTIONAL_HEADER *)((BYTE*)hMod + pDosHeader->e_lfanew + 24);

 

       IMAGE_IMPORT_DESCRIPTOR* pImportDesc = (IMAGE_IMPORT_DESCRIPTOR*)

              ((BYTE*)hMod + pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);

       while(pImportDesc->FirstThunk)

       {

              char* pszDllName = (char*)((BYTE*)hMod +pImportDesc->Name);

              printf("/n模块名称:%s /n", pszDllName);

             

              // 一个IMAGE_THUNK_DATA就是一个双字,它指定了一个导入函数

              IMAGE_THUNK_DATA* pThunk = (IMAGE_THUNK_DATA*)

                     ((BYTE*)hMod + pImportDesc->OriginalFirstThunk);

              int n = 0;

              while(pThunk->u1.Function)

              {

                     // 取得函数名称。hint/name表前两个字节是函数的序号,后4个字节是函数名称字符串的地址

                     char* pszFunName = (char*)

                            ((BYTE*)hMod + (DWORD)pThunk->u1.AddressOfData + 2);

                     // 取得函数地址。IAT表就是一个DWORD类型的数组,每个成员记录一个函数的地址

                     PDWORD lpAddr = (DWORD*)((BYTE*)hMod + pImportDesc->FirstThunk) + n;

                     // 打印出函数名称和地址

                     printf("  从此模块导入的函数:%-25s", pszFunName);

                     printf("函数地址:%X /n", lpAddr);

                     n++; pThunk++;

              }

              pImportDesc++;

       }

PE文件中的重要结构:

typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header

...........

.........

    LONG   e_lfanew;                    // File address of new exe header

  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

 

typedef struct _IMAGE_NT_HEADERS {

    DWORD Signature;

    IMAGE_FILE_HEADER FileHeader;

    IMAGE_OPTIONAL_HEADER32 OptionalHeader;

} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

 

typedef struct _IMAGE_OPTIONAL_HEADER {

.....

.....

    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];

} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

 

typedef struct _IMAGE_DATA_DIRECTORY {

    DWORD   VirtualAddress; // 指向IMAGE_IMPORT_DESCRIPTOR的指针值

    DWORD   Size;

} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

 

 

typedef struct _IMAGE_IMPORT_DESCRIPTOR {

    union {

        DWORD   Characteristics;            // 0 for terminating null import descriptor

        DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)

    };                

    DWORD   TimeDateStamp;                  // 0 if not bound,

                                            // -1 if bound, and real date/time stamp

                                            //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)

                                            // O.W. date/time stamp of DLL bound to (Old BIND)

 

    DWORD   ForwarderChain;                 // -1 if no forwarders

    DWORD   Name;

    DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)

} IMAGE_IMPORT_DESCRIPTOR;

 

注意:  
1PE格式文件中经常用到RVA,即相关虚拟地址,用在不知道基地址的情况下
表示一个内存地址。它需要加上基地址才能得到线性地址(Linear address)。

2DWORD   OriginalFirstThunk;表示函数(hint/name)表的偏移量,记录导入函数名称

3DWORD   Name; 模块的名称

4DWORD   FirstThunk;IAT(导入地址表)的偏移量,记录导入函数地址

 

typedef struct _IMAGE_THUNK_DATA32 {

    union {

        PBYTE  ForwarderString;

        PDWORD Function;

        DWORD Ordinal;

        PIMAGE_IMPORT_BY_NAME  AddressOfData;

    } u1;

} IMAGE_THUNK_DATA32;

 

 

typedef struct _IMAGE_IMPORT_BY_NAME {

    WORD    Hint;

    BYTE    Name[1]; //函数名称

} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

 

 
原创粉丝点击