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读取工具对照过。

时间: 2024-08-04 10:10:47

C读取PE信息的相关文章

JavaWEB中读取配置信息

第一种方法是使用java.io和java.util包,缺点是路径的概念要清晰, 例子: Properties prop = new Properties(); InputStream in = getClass().getResourceAsStream("/common.properties"); try { prop.load(in); pool = new JedisPool(config, prop.getProperty("pay.redis.url"))

解惑:NFC手机如何轻松读取银行卡信息?

自支付宝钱包8.0推出了NFC新功能,只要将支持NFC功能的手机靠近公交卡.银行卡等带有芯片的IC卡上,可迅速读取卡内余额.卡的信息,还可以给卡进行充值,非常贴心实用. 但是很多网友表示担忧,要是别人用手机紧贴着我的银行卡,那么信息不就轻易泄露了,这样会威胁我的资金安全吗?并有不少伪专家宣称,NFC手机有可能成为黑客的"提款机",可以实现转账操作,风险很大,网友表示很担心.真实情况是什么样的呢?让我从专业的角度,给大家道出内幕. NFC(近场通信,NearFieldCommunicat

iOS通过app读取通讯录信息(整理)

iOS通过app读取通讯录信息,读取通讯录信息时需要加载AddressBookUI 和AddressBook两个包,并且引入头文件 #import <AddressBook/AddressBook.h> #import <AddressBookUI/AddressBookUI.h> 具体实现如下: -(void)readAllPeoples { //定义通讯录名字为addressbook ABAddressBookRef tmpAddressBook = nil; //根据系统版本

基于C++的WMI应用编程初探-读取BIOS信息

虽然VBScript等脚本语言实现WMI编程更加方便,但有些时候我们还是不得不使用C++来编程,比如说要追求更好的性能或者是一个基于C++的项目中需要这样的功能等等. 下面是用C++实现WMI编程的基本步骤,在这里,我们通过读取BIOS信息来演示如何实现.可以对照参考: http://www.qingfengju.com/article.asp?id=60(通过一个具体实例来理解WMI脚本编程-读取BIOS信息). 1.初始化COM WMI提供的API是基于COM的,所以必须首先执行CoInit

PHP文件操作 之读取目录信息

//定义一个函数 读取目录信息的函数 function dirInfo($dirName) { //判断目录是否存在 if (!file_exists($dirName)) { die('目录不存在!'); } //判断是否是目录 if (!is_dir($dirName)) { die('您所遍历的不是目录!'); } //打开目录 $d = opendir($dirName); //判断打开目录是否成功 if (!$d) { die('打开目录失败!'); } //读取目录 while ($

读取proc信息的可扩展实现

需求 1. 将内存.线程数等信息注册到zk上进行监控 2. 统计信息,为下一步做负载均衡做准备. 实现 本文只解决问题1. 从网上查询了下,这些信息可以从proc文件系统中获取,如果不知道proc的,可以Google下. 网上有读取proc信息的lib——libproc,即 procps, 据说htop等实现就是基于它的. 我下载下来了,include和lib都生成了,好不容易找到一篇教程,结果在 stackoverflow上,见有人说有内存泄露,需要如下方法做. int main(int ar

js读取cookie信息

1. 第一种方式读取cookie信息:用document.cookie.split(“; “)的方式把字符串分割成几个段,然后遍历整个数组 //javascript方法 function getCookie(name){ var arr = document.cookie.split("; "); for(var i=0,len=arr.length;i<len;i++){ var item = arr[i].split("="); if(item[0]==n

EWS API 2.0读取日历信息-读取内容注意事项

[from] http://www.cnblogs.com/love007/archive/2013/06/26/3156852.html 采用模拟账号的方式读取日历信息,注意下日历的内容读取(Body)读取.代码如下:(采用 EWS API 2.0版本) 1.读取内容前必须设置如下属性:否则会提示:You must load or assign this property before you can read its value  Body 如下: //*******************

跟王老师学注解(五):利用反射读取注解信息

跟王老师学注解(五):读取注解信息 主讲教师:王少华   QQ群号:483773664 一.注解被读取 (一)条件 当一个注解类型被定义为运行时注解后,该注解才是运行时可以见,当class文件被装载时被保存在class文件中的注解才会被Java虚拟机所读取. 要把@Retention注解的value成员变量的值设为RetentionPolicy.RUNTIME (二)办法 我们已知所有的注解都是继承的java.lang.Annotation接口,也就是说Annotation是所有接口的父接口.除