首页 > 代码库 > C读取PE信息

C读取PE信息

  PE(Portable Execute)文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任何扩展名。相信来看本文的多少对于PE结构有一定的了解,那么今天就直接贴代码,以尽量简短的代码来实现PE的读取,没有使用一些windows.h中的很方便的操作。

  首先给出一张PE结构的图片,是之前学习时从别的博客中下载下来的,忘记出处了,另外对一些比较重要的项用红色粗线在下方标注

技术分享

 

 

 

  代码部分首先就是包含文件和一些数据类型,因为结构中多采用BYTE,WORD, DWORD,LONG这样的别名,所以:

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

typedef unsigned char BYTE; //1
typedef unsigned short WORD; //2
typedef unsigned int DWORD;    //4
typedef unsigned long LONG;    //4

  接着就是整个PE文件的开始部分,DOS

//DOS
typedef struct _IMAGE_DOS_HEADER
{
    WORD   e_magic;                     // Magic number
    WORD   e_cblp;                      // Bytes on last page of file
    WORD   e_cp;                        // Pages in file
    WORD   e_crlc;                      // Relocations
    WORD   e_cparhdr;                   // Size of header in paragraphs
    WORD   e_minalloc;                  // Minimum extra paragraphs needed
    WORD   e_maxalloc;                  // Maximum extra paragraphs needed
    WORD   e_ss;                        // Initial (relative) SS value
    WORD   e_sp;                        // Initial SP value
    WORD   e_csum;                      // Checksum
    WORD   e_ip;                        // Initial IP value
    WORD   e_cs;                        // Initial (relative) CS value
    WORD   e_lfarlc;                    // File address of relocation table
    WORD   e_ovno;                      // Overlay number
    WORD   e_res[4];                    // Reserved words
    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                   // OEM information; e_oemid specific
    WORD   e_res2[10];                  // Reserved words
    LONG   e_lfanew;                    // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

  NT头部分,包含标准PE头和可选PE头:

//NT
typedef struct _IMAGE_NT_HEADERS
{
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

  NT头中的IMAGE_FILE_HEADER(标准PE) 和 IMAGE_OPTIONAL_HEADER32(可选PE)的定义在下方:

//标准PE
typedef struct _IMAGE_FILE_HEADER
{
    WORD    Machine;
    WORD    NumberOfSections;
    DWORD   TimeDateStamp;
    DWORD   PointerToSymbolTable;
    DWORD   NumberOfSymbols;
    WORD    SizeOfOptionalHeader;
    WORD    Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

//可选PE
typedef struct _IMAGE_OPTIONAL_HEADER
{
    WORD    Magic;
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;
    DWORD   SizeOfUninitializedData;
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;
    DWORD   ImageBase;
    DWORD   SectionAlignment;
    DWORD   FileAlignment;
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;
    DWORD   SizeOfHeaders;
    DWORD   CheckSum;
    WORD    Subsystem;
    WORD    DllCharacteristics;
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    DWORD   SizeOfHeapReserve;
    DWORD   SizeOfHeapCommit;
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes;

    //IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
    IMAGE_DATA_DIRECTORY DataDirectory[16];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

  在可选PE头中的最后一项是数据目录表,16个元素的数组

//数据目录表
typedef struct _IMAGE_DATA_DIRECTORY
{
    DWORD VirtualAddress;
    DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

  最后,在跳过DOS,(NT中包含PE和可选PE)PE和可选PE之后就是区段表(也有叫节表的),定义如下:

#define IMAGE_SIZEOF_SHORTT_NAME 8
typedef struct _IMAGE_SECTION_HEADER
{
    BYTE Name[IMAGE_SIZEOF_SHORTT_NAME];
    union
    {
        DWORD PhysicalAddress;
        DWORD VirtualSize;
    } Misc;
    DWORD VirtualAddress;
    DWORD SizeOfRawData;
    DWORD PointerToRawData;
    DWORD PointerToRelocations;
    DWORD PointerToLinenumbers;
    WORD  NumberOfRelocations;
    WORD  MumberOfLinenumbers;
    DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

  以上部分都是PE的结构,固定的结构,从图中可也看出来,接下来的部分就是读取了,首先打开一个exe程序,以记事本为例,常用的fopen和fread即可:

BYTE* openFile()
{
    int fileLen;
    BYTE* p;
    FILE *fp = fopen("C:\\WINDOWS\\system32\\notepad.exe", "rb");
    fseek(fp, 0, SEEK_END);

    fileLen = ftell(fp);
    p = (BYTE*)malloc(fileLen);
    memset(p, 0, sizeof(p));
    fseek(fp, 0, SEEK_SET);

    fread(p, fileLen, 1, fp);

    return p;
}

  接下来的主函数就是计算篇一部分,尽量简短:

  因为可选PE在位数不同的情况下默认值是不同的,并且是可以改的,而区段表在可选PE的后面,因此采用NT的地址加上可选PE在NT中的偏移,再加上可选PE的大小就到了区段表的位置,可选PE的大小有标准PE中的SizeOfOptionalHeader指定。

int main()
{
    //读取到内存
    BYTE*  pBuf = openFile();

    //DOS
    IMAGE_DOS_HEADER*        pDos = (IMAGE_DOS_HEADER *)pBuf;

    //NT
    IMAGE_NT_HEADERS32*        pNt = (IMAGE_NT_HEADERS32 *)(pBuf + pDos->e_lfanew);

    //PE     标准PE
    IMAGE_FILE_HEADER*        pFile = (IMAGE_FILE_HEADER *)&pNt->FileHeader;

    //Option   可选PE
    IMAGE_OPTIONAL_HEADER32*   pOption = (IMAGE_OPTIONAL_HEADER32 *)&pNt->OptionalHeader;

    //DataDirectory   数据目录表
    IMAGE_DATA_DIRECTORY*    pDataDir = pOption->DataDirectory;

    //Section table    区段表
    IMAGE_SECTION_HEADER* pSection = (IMAGE_SECTION_HEADER *)(  (long)pNt + 
                 (long)&(((IMAGE_NT_HEADERS32 *)0)->OptionalHeader) +
                 pFile->SizeOfOptionalHeader ); free(pBuf); return 0; }

  找到了上面地址之后,就可以读取其中的数据,对照图中结构的项,加一些printf语句即可,这里就不写了,代码已用PE读取工具对照过。

 

C读取PE信息