学习感悟,如果错误,还望指出

目录

前言

过程

总结


前言

PE格式是在Windows上的可执行程序需要遵守的格式规范。

接下来介绍一下PE头中的一部分成员的含义。

DOS头

大小是固定的64字节

struct _IMAGE_DOS_HEADER ,

{ 0x00 WORD e_magic;//表明此文件是不是PE文件,如果是PE文件的话,值为4D5A,ASCII值对应MZ。

0x02 WORD e_cblp;

0x04 WORD e_cp;

0x06 WORD e_crlc;

0x08 WORD e_cparhdr;

0x0a WORD e_minalloc;

0x0c WORD e_maxalloc;

0x0e WORD e_ss;

0x10 WORD e_sp;

0x12 WORD e_csum;

0x14 WORD e_ip;

0x16 WORD e_cs;

0x18 WORD e_lfarlc;

0x1a WORD e_ovno;

0x1c WORD e_res[4];

0x24 WORD e_oemid;

0x26 WORD e_oeminfo;

0x28 WORD e_res2[10];

0x3c DWORD e_lfanew; //指明PE标识,也就是NT头的开始位置的偏移是多少,大小不固定,因为在DOS头结束,和NT头开始之间的区域是操作系统留给编译器使用的空闲区域,大小不确定,所以需要这个成员来存储NT头部的偏移。

};

struct _IMAGE_NT_HEADERS

{ 0x00 DWORD Signature; //PE标识,也就是上图中DOS头最后一个成员所指向的地方。

0x04 _IMAGE_FILE_HEADER FileHeader; //标准PE头

0x18 _IMAGE_OPTIONAL_HEADER OptionalHeader;//可选PE头

};

标准PE头

大小为20字节

struct _IMAGE_FILE_HEADER

{

0x00 WORD Machine;//标识此程序可以在什么CPU上允许,如果是0x00的话说明在任何CPU上都 可以允许此程序,如果是014c,标识在386及其之后的CPU上可以执行。

0x02 WORD NumberOfSections;//去掉PE头部的节区数量

0x04 DWORD TimeDateStamp;//时间戳,因为编译器在编译的时候会生成一个MAP文件,MAP文 件,MAP文件里面记录了此程序的函数的名字,和地址,里面有一个时间戳,记录了MAP文件的生成时间,这个时间是和exe程序相匹配的,有的加壳软件在进行加壳的时候,会需要提供MAP文件,识别MAP文件的时间戳和exe是否匹配。

0x08 DWORD PointerToSymbolTable;

0x0c DWORD NumberOfSymbols;

0x10 WORD SizeOfOptionalHeader;//记录了可选PE头的大小,32位默认是E0,64位默认是F0,大小可以调整。

0x12 WORD Characteristics; //每一位都有特定的含义,可执行程序是10F,级0,1,2,3,8位置1

};

可选PE头

大小不确定,32位默认为E0,64系统默认为F0,可以自己定义大小。

struct _IMAGE_OPTIONAL_HEADER

{

0x00 WORD Magic; //说明文件类型,10B说明是32位下的PE文件,20B说明64位下的PE文件

0x02 BYTE MajorLinkerVersion;

0x03 BYTE MinorLinkerVersion;

0x04 DWORD SizeOfCode; //所有代码节的和,必须是FileAlignment的整数倍

0x08 DWORD SizeOfInitializedData;//所有已初始化数据的和,必须是FileAlignment的整数倍

0x0c DWORD SizeOfUninitializedData;//所有未初始化数据的和,必须是FileAlignment的整数倍

0x10 DWORD AddressOfEntryPoint; //简称OEP,程序的入口地址,需要配合ImageBase来定位程序的入口地址。这里相信大家可能会有疑问,为什么不直接定位到入口地址,还需要使用ImageBase+OEP偏移的方式来进行定位,因为一个EXE程序,很可能不止由一个PE文件构成,如果直接写死,而这个位置又已经被别的PE文件占据了,那么就出事了,如果采用这种偏移的方式,ImageBase改变之后,通过OEP进行偏移还是可以进行定位,程序还是可以跑起来。

0x14 DWORD BaseOfCode;//代码节的基址。

0x18 DWORD BaseOfData; //数据节的基址。

0x1c DWORD ImageBase; //程序在加载入内存的一个基址,也就是起始地址。

0x20 DWORD SectionAlignment; //内存对齐,1000字节

0x24 DWORD FileAlignment;//硬盘对齐,200字节

这里说一下,一个exe程序存储到硬盘上,如果直接通过一个16进制的编辑器打开,不会做任何改动,和在硬盘中是一样的,但是这个程序虽然加载如内存,但是不能跑起来,如果硬盘对齐和内存对齐尺寸不一样,那么就存在一个拉伸的过程,举个例子,代码节在硬盘上占389个字节,然后硬盘给他分配400字节(因为要进行对齐),那么允许这个程序时,拉到硬盘中,就会被拉伸到1000字节,如果硬盘对齐和内存对齐是一致的,就没有这个拉伸的过程了。

0x28 WORD MajorOperatingSystemVersion;

0x2a WORD MinorOperatingSystemVersion;

0x2c WORD MajorImageVersion;

0x2e WORD MinorImageVersion;

0x30 WORD MajorSubsystemVersion;

0x32 WORD MinorSubsystemVersion;

0x34 DWORD Win32VersionValue;

0x38 DWORD SizeOfImage; //程序在内存中的映射尺寸,可以设置的比原来的尺寸更长,但是必须是SectionAlignment的整数倍

0x3c DWORD SizeOfHeaders;//所有的头加上节表的大小。必须是文件对齐的整数倍。(DOS头+PE标识+标准PE头+可选PE头+节表)

0x40 DWORD CheckSum; //校验和,其实很简单,就是把数据从开始到结束加起来,存到此成员中,自然溢出。

0x44 WORD Subsystem;

0x46 WORD DllCharacteristics;

0x48 DWORD SizeOfStackReserve; //初始化的时候保留栈的大小

0x4c DWORD SizeOfStackCommit;//初始化时实际提交栈的大小

0x50 DWORD SizeOfHeapReserve; //初始化时保留堆区的大小

0x54 DWORD SizeOfHeapCommit;//初始化时实际提交堆的大小

0x58 DWORD LoaderFlags;

0x5c DWORD NumberOfRvaAndSizes;

0x60 _IMAGE_DATA_DIRECTORY DataDirectory[16];

};

过程

下面是我用C写的解析PE头的数据(只限于PE头),方法比较笨,如果大家有更好的办法请告知我,感谢。

我的思路是,把PE头的每个成员的信息用数组记录下来,然后依次匹配读取。

#Analyze_Of_PE_Header.h

"""Analyze_Of_PE_Header.h"""
#pragma once
typedef char BYTE;
typedef short WORD;
typedef int DWORD;
//DOS头信息
extern const BYTE* _IMAGE_DOS_HEADER[19];
//DOS头每个成员的大小
extern DWORD DOS_LENGTH[21];//标准PE头信息
extern const BYTE* _IMAGE_FILE_HEADER[7];
//标准PE头每个成员的大小
extern DWORD FILE_PE_LENGTH[7];//可选PE头信息
extern const BYTE* _IMAGE_OPTIONAL_HEADER[30];
//可选PE头每个成员的大小
extern DWORD _OPTIONAL_HEADER_LENGTH[30];char* ReadFile(char*);
bool Analyse_PE_Head(char*);

#Analyze_Of_PE_Header.cpp

#define _CRT_SECURE_NO_WARNINGS
#include "Analyze_Of_PE_Header.h"
#include <stdio.h>
#include <malloc.h>
#include <string.h>#DOS头信息
const BYTE* _IMAGE_DOS_HEADER[19]
{"e_magic","e_cblp","e_cp","e_crlc","e_cparhdr","e_minalloc","e_maxalloc","e_ss","e_sp","e_csum","e_ip","e_cs","e_lfarlc","e_ovno","e_res[4]","e_oemid","e_oeminfo","e_res2[10]","e_lfanew"
};DWORD DOS_LENGTH[21]
{2,2,2,2,2,2,2,2,2,2,2,2,2,2,8,2,2,20,4
};#标准PE头信息
const BYTE* _IMAGE_FILE_HEADER[7]
{"Machine","NumberOfSections","TimeDateStamp","PointerToSymbolTable","NumberOfSymbols","SizeOfOptionalHeader","Characteristics"
};DWORD FILE_PE_LENGTH[7]
{2,2,4,4,4,2,2
};//可选PE头信息
const BYTE* _IMAGE_OPTIONAL_HEADER[30]
{"Magic","MajorLinkerVersion","MinorLinkerVersion","SizeOfCode","SizeOfInitializedData","SizeOfUninitializedData","AddressOfEntryPoint","BaseOfCode","BaseOfData","ImageBase","SectionAlignment","FileAlignment","MajorOperatingSystemVersion","MinorOperatingSystemVersion","MajorImageVersion","MinorImageVersion","MajorSubsystemVersion","MinorSubsystemVersion","Win32VersionValue","SizeOfImage","SizeOfHeaders","CheckSum","Subsystem","DllCharacteristics","SizeOfStackReserve","SizeOfStackCommit","SizeOfHeapReserve","SizeOfHeapCommit","LoaderFlags","NumberOfRvaAndSizes"
};
DWORD _OPTIONAL_HEADER_LENGTH[30]
{2,1,1,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,4,4,4,4,2,2,4,4,4,4,4,4
};
char* ReadFile(char* p)
{FILE* fp = fopen(p, "rb");if (fp == NULL){printf("文件打开失败\n");return NULL;}fseek(fp, 0, SEEK_END);int len = ftell(fp);char* buf = (char*)malloc(len);if (buf == NULL){printf("内存分配失败\n");return NULL;}fseek(fp, 0, SEEK_SET);fread(buf, 1, len, fp);fclose(fp);return buf;
}bool Analyse_PE_Head(char* p)
{char* Buf = ReadFile(p);char* BufBackUp = Buf;int Turn = 0;if (Buf == NULL){return false;}//DOS头WORD* Test = (WORD*)Buf;if ((*Test) != 0x5A4D){return false;}printf("DOS头部:\n");int i = 0;while (i < 19){printf("%s: ", _IMAGE_DOS_HEADER[i]);if (DOS_LENGTH[i] == 2){WORD* Ptemp = (WORD*)Buf;Buf += 2;printf("%x\n", (*Ptemp));}else if (DOS_LENGTH[i] == 4){DWORD* Ptemp = (DWORD*)Buf;Buf += 4;//记录PE头偏移Turn = (*Ptemp);printf("%x\n", (*Ptemp));}else{WORD* Ptemp = (WORD*)Buf;int j = 0;while (j < (DOS_LENGTH[i] / 2)){printf("%x", *Ptemp);Buf += 2;Ptemp++;j++;}printf("\n");}i++;}printf("\n-----------------------------------------------\n");//PE头跳转Buf = BufBackUp + Turn;//NT头开始int* temp = (int*)Buf;if ((*temp) != 0x4550){return false;}printf("PE标识:%x\n", (*temp));Buf += 4;//标准PE头printf("标准PE头\n");i = 0;while (i < 7){printf("%s: ", _IMAGE_FILE_HEADER[i]);if (FILE_PE_LENGTH[i] == 2){WORD* Ptemp = (WORD*)Buf;Buf += 2;printf("%x\n", (*Ptemp));}else{DWORD* Ptemp = (DWORD*)Buf;Buf += 4;printf("%x\n", (*Ptemp));}i++;}printf("\n-----------------------------------------------\n");//可选PE头printf("可选PE头\n");i = 0;while (i < 30){printf("%s: ", _IMAGE_OPTIONAL_HEADER[i]);if (_OPTIONAL_HEADER_LENGTH[i] == 2){WORD* Ptemp = (WORD*)Buf;Buf += 2;printf("%x\n", (*Ptemp));}else if (_OPTIONAL_HEADER_LENGTH[i] == 1){BYTE* Ptemp = (BYTE*)Buf;Buf++;printf("%x\n", (*Ptemp));}else{DWORD* Ptemp = (DWORD*)Buf;Buf += 4;printf("%x\n", (*Ptemp));}i++;}printf("\n-----------------------------------------------\n");return true;
}

#main.cpp

#include <stdio.h>
#include "Analyze_Of_PE_Header.h"int main(void)
{char path[] = "C:\\Program Files (x86)\\NetSarang\\Xshell 7\\Xshell.exe";bool result = Analyse_PE_Head(path);if (result == NULL){printf("解析失败\n");}return 0;
}

这是运行结果

总结

但是我还有一个思路就是根据不同的头部,建立不同的结构体,但是在对应结构体成员的查找上遇到了问题,没有想到实现的办法,所以采用了这种办法。

PE头解析(仅限于PE头)相关推荐

  1. PE文件解析(1):Dos头与NT头

    文章目录 DOS头 NT头 标准NT头 可选NT头 什么是PE文件? PE文件是在windows平台可执行的文件. 包括:.exe(可执行程序),dll(动态链接库).sys(驱动程序) 这是PE文件 ...

  2. PE头解析-字段说明

    一.什么是可执行文件 1.可执行文件(executable file)指的是可以由操作系统进行加载执行的文件. 2.可执行文件的格式: Windows平台: PE(Portable Executabl ...

  3. 滴水逆向三期实践1:PE头字段解析,附PE结构下载

    视频资源详见网盘搜索 或 在线的B站滴水逆向三期 其课件也能在CSDN或百度搜索到,以下部分为课件内容摘要,部分为自己的理解 最后附上详细注释的自写代码 PE(Portable Executable) ...

  4. PE文件(一)PE头

    PE文件结构(一)PE头 12/100 发布文章 qq_63329753 未选择任何文件 new PE文件结构 PE(Portble Executable File Format,可移植的执行体文件格 ...

  5. PE文件学习系列二 DOS头分析

    合肥程序员群:49313181.    合肥实名程序员群 :128131462 (不愿透露姓名和信息者勿加入) Q  Q:408365330     E-Mail:egojit@qq.com PE文件 ...

  6. 图解VC++版PE文件解析器源码分析

    该源码下载自 http://download.csdn.net/download/witch_soya/4979587 1 Understand 分析的图表 2 PE结构解析的主要代码简要分析 首先看 ...

  7. windows PE结构解析

    1 基本概念 下表描述了贯穿于本文中的一些概念: 名称 描述 地址 是"虚拟地址"而不是"物理地址".为什么不是"物理地址"呢?因为数据在内 ...

  8. Win32汇编:PE结构解析器

    PE格式是Windows系统下最常用的可执行文件格式,有些应用必须建立在了解PE文件格式的基础之上,如可执行文件的加密与解密,文件型病毒的查杀等,熟练掌握PE文件结构,有助于软件的分析. 在PE文件中 ...

  9. 15.windbg-dds、dps、dqs、PE文件解析

    以下默认windbg加载calc程序 d*s dds.dps和dqs命令显示给定范围内存的内容,它们是把内存区域转储出来,并把内存中每个元素都视为一个符号对其进行解析,dds是四字节视为一个符号,dq ...

最新文章

  1. MySQL面试题 | 附答案解析(三)
  2. 给大家介绍一位中科院师兄,读研时通过实习和比赛收入五十万
  3. oel6mysql_Linux7(CentOS,RHEL,OEL)和 Oracle RAC环境系列4:target(图形
  4. Python 微信机器人:调用电脑摄像头时时监控功能实现演示,调用电脑摄像头进行拍照并保存
  5. 神经网络到底是如何做出决策的?
  6. Ubuntu 16.04 把Dock放到桌面底部
  7. 上个ensp实验只发了配置,这次是命令条目
  8. 知乎上砍手豪关于kaggle的观点(转载)
  9. netbeans java中文_Ubuntu 下jdk安装中文字体 java 解决netbeans 方块字 中文乱码
  10. 关于Netbeans调试PHP
  11. 美国能源局投2100万美元加速光伏软成本下降
  12. CSU 1115: 最短的名字(字典树)
  13. android开发目录结构说明
  14. VirtualBox 6.1.4的共享剪贴板确实有问题,6.1.0正常
  15. wait()和sleep()区别(常见面试题)
  16. jdk8下载与安装教程
  17. 网络编程 upd 发送接收数据
  18. C# PDF转图片(JPG,Png)
  19. 在Mac上使用中国银行和工商银行网银
  20. 模仿百度首页的图片轮播

热门文章

  1. 广州史帝奇轨道环幕影院的优势、特点、应用领域
  2. 数学公式大全--极限、微分、积分
  3. webstorm-主题和配色
  4. Python 与 SQL 这样超强结合,处理数据才是爆赞
  5. 七夕节其实是最古老的异地恋
  6. 1024 祝我们快乐
  7. 在Js和Java自动生成账号的方法
  8. NBA球员生涯数据统计系统(中南大学C语言课设)
  9. Android手机为何不再卡顿?性能优化才是安卓起飞关键
  10. 我整理了50道经典Java算法题,直接进了字节跳动!!