PE文件概述

文件格式

.png 、.mp4、.gif、.dll等等,这些文件都具有不同格式 不能随意修改这些文件,否则将无法打开 PE文件(可执行文件)

学习PE文件目标

掌握PE文件就掌握winodws运行秘密 掌握PE文件对逆向,病毒分析,调试,漏洞.....不可替代作

PE文件常见术语

字段:结构体中某个成员

头: 说明书本的目录或者前言

区段:书本中的章节

偏移:用于文件偏移

镜像:磁盘上的文件

映像:将这个镜像加载到内存

DOS头

其中有用的就两个字段e_magic ,e_lfanew

e_magic必须实时IMAGE_DOS_SINGATURE 0x5A4D e_lfanew指向NT头

typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE 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头

结构

typedef struct _IMAGE_NT_HEADERS 
{    DWORD Signature;                        // [0x00]PE标识  
IMAGE_FILE_HEADER FileHeader;           // [0x04]文件头  
IMAGE_OPTIONAL_HEADER32 OptionalHeader; // [0x18]扩展头
}IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

第一个字段 signature是一个 PE00标志 这个标志始终都是0x00004550, 宏 IMAGE_NT_SIGNATURE表示。 同它可以知道这个文件是否是PE文件

bool ispe_file(char * pbuff) { 
   PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pbuff;
   if(pDos.e_magic != IMAGE_DOS_SIGNATRUE )  
  {
       return false;  
  }  
   PMIAGE_NT_HEADER pNt = (PMIAGE_NT_HEADER)(pDos.e_lfanew + (DWORD)pbuff);  
   if(pNt.signature != IMAGE_NT_SIGNATURE)  
  {
       return false;  
  }  
   return true;
}

第二个字段是一个文件头结构体

typedef struct _IMAGE_FILE_HEADER {  
   WORD     Machine;               //[0x04] (1)运行平台  
   WORD     NumberOfSections;      //[0x06] (2)区段的数量*  
   DWORD   TimeDateStamp;          //[0x08] (3)文件创建时间  
   DWORD   PointerToSymbolTable;   //[0x0C] (4)符号表指针  
   DWORD   NumberOfSymbols;        //[0x10] (5)符号的数量  
   WORD     SizeOfOptionalHeader;  //[0x14] (6)扩展头大小* 32大小E0,64位F0  
   WORD     Characteristics;       //[0x16] (7)文件属性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

运行平台有很多宏,常见的两个i386(x86) 值0x14c, AMD64(x64) 值0x8666;

基本术语

加载基址:内存中起始位置

虚拟地址:在进程中的空间某个位置

相对虚拟地址: 相对于加载基址 公式 虚拟地址(VA) = 加载基址(imagebase) + 相对虚拟地址(RVA)

对齐 文件对齐:区段在文件中对齐,一般以0x200

内存对齐: 区段在内存中对齐,一般以0x1000(刚好一页4k)

扩展头

扩展头位于NT头的后一个字段 IMAGE_OPTIONAL_HEADER

typedef struct _IMAGE_OPTIONAL_HEADER {    // 标准域    
   WORD     Magic;     //[0x18] (1) 标志位  
   BYTE       MajorLinkerVersion;  //[0x1A] (2) 连接器主版本号  
   BYTE       MinorLinkerVersion;  //[0x1B] (3) 连接器子版本号    
   DWORD   SizeOfCode;         //[0x1C] (4) 所有代码段 的总大小    
   DWORD   SizeOfInitializedData;  //[0x20] (5) 所有初始化段总大小    
   DWORD   SizeOfUninitializedData;    //[0x24] (6) 所有未初始化段总大小  
   DWORD   AddressOfEntryPoint;    //[0x28] (7) 程序执行入口RVA*  
   DWORD   BaseOfCode;         //[0x2C] (8) 代码段起始RVA  
   DWORD   BaseOfData;         //[0x30] (9) 数据段起始RVA    // NT 附加域  
   DWORD   ImageBase;      //[0x34] (10) 程序默认载入基地址*    
   DWORD   SectionAlignment;   //[0x38] (11) 内存中的段对齐值  
   DWORD   FileAlignment;      //[0x3C] (12) 文件中的段对齐值  
   WORD    MajorOperatingSystemVersion; //[0x40] (13) 系统主版本号
   WORD    MinorOperatingSystemVersion; //[0x42] (14) 系统子版本号    
   WORD    MajorImageVersion;  //[0x44] (15) 自定义的主版本号  
   WORD    MinorImageVersion;  //[0x46] (16) 自定义的子版本号  
   WORD    MajorSubsystemVersion; //[0x48] (17) 所需子系统主版本号  
   WORD    MinorSubsystemVersion; //[0x4A] (18) 所需子系统子版本号    
   DWORD   Win32VersionValue;//[0x4C] (19) 保留,通常为0x00    
   DWORD   SizeOfImage;    //[0x50] (20) 内存中映像总尺寸*    
   DWORD   SizeOfHeaders;  //[0x54] (21) 各个文件头的总尺寸*  
   DWORD   CheckSum;       //[0x58] (22) 映像文件校验和  
   WORD     Subsystem;                           //[0x5C] (23) 文件子系统  
   WORD     DllCharacteristics;    //[0x5E] (24) DLL标志位  
   DWORD   SizeOfStackReserve;         //[0x60] (25) 初始化栈大小  
   DWORD   SizeOfStackCommit;         //[0x64] (26) 初始化实际提交栈大小  
   DWORD   SizeOfHeapReserve;         //[0x68] (27) 初始化保留栈大小  
   DWORD   SizeOfHeapCommit;         //[0x6C] (28) 初始化实际保留栈大小  
   DWORD   LoaderFlags;                       //[0x70] (29) 调试相关,默认0x00  
   DWORD   NumberOfRvaAndSizes;  //[0x74] (30) 数据目录表的数量*  
   IMAGE_DATA_DIRECTORY DataDirectory[0x10]; //[0x78] (31) 数据目录表*
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

扩展头后一个字段是一个数据目录表,默认都是16项 每一项都指向IMAGE_DATA_DIRECTORY 结构体

typedef struct _IMAGE_DATA_DIRECTORY {   
   DWORD   VirtualAddress; // 数据块的起始RVA地址*  
   DWORD   Size;       // 数据块的长度
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
#define IMAGE_DIRECTORY_ENTRY_EXPORT    0x0     //[0x78] (1)导出表 
#define IMAGE_DIRECTORY_ENTRY_IMPORT   0x1     //[0x80] (2)导入表
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 0x2     //[0x88] (3)资源表
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 0x3     //[0x90] (4)
#define IMAGE_DIRECTORY_ENTRY_SECURITY 0x4     //[0x98] (5)
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 0x5     //[0xA0] (6)重定位表
#define IMAGE_DIRECTORY_ENTRY_DEBUG 0x6     //[0xA8] (7)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 0x7     //[0xB0] (8)
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 0x8     //[0xB8] (9)
#define IMAGE_DIRECTORY_ENTRY_TLS       0x9     //[0xC0] (10)TLS表
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG   0xA     //[0xC8] (11)
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 0xB     //[0xD0] (12)
#define IMAGE_DIRECTORY_ENTRY_IAT       0xC     //[0xD8] (13)
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 0xD     //[0xE0] (14)延迟加载表
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR     0xE     //[0xE8] (15)

区段头

扩展头后就是区段头,通过IMAGE_FIRST_SECTION32(NtHeader) 可以获取区段头

typedef struct _IMAGE_SECTION_HEADER 
{  
   BYTE    Name[0x8];                         // (1) 区段名    
   union {          
       DWORD   PhysicalAddress;          
       DWORD   VirtualSize;                  // (2) *区段大小
  } Misc;                      
   DWORD   VirtualAddress;     // (3)区段的RVA地址*  
   DWORD   SizeOfRawData;      // (4) 文件中的区段对齐大小*    
   DWORD   PointerToRawData;       // (5) 区段在文件中的偏移*  
   DWORD   PointerToRelocations;   // (6) 重定位的偏移(OBJ)    
   DWORD   PointerToLinenumbers;   // (7) 行号表的偏移(调试)    
   WORD    NumberOfRelocations;    // (8) 重定位项数量(OBJ)  
   WORD    NumberOfLinenumbers;    // (9) 行号表项数量  
   DWORD   Characteristics;        // (10) 区段的属性
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

使用LOADPE查看区段信息

使用010edit查看区段

代码解析 NT头->区段头

//显示区段信息 
void Show_SectionInfo(char * pbuff)
{    //1.获取DOS  
   //2.获取NT  
   //3.获取区段头
   //4.遍历区段显示信息
   PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pbuff;  
   PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + (DWORD)pbuff);
   // 区段头位置= pNT+4+sizeof(IMAGE_FILE_HEADERS)+sizeof(IMAGE_OPTIONAL_HEADERS)  
   PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);
   for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++)  
  {      
       //输出区段信息        
       char name[9];      
       strncpy_s(name, (char*)pSection[i].Name, 8);  
       printf("区段名字:%s\n", name);    
       printf("区段RVA: %08x\n", pSection[i].VirtualAddress);  
  }
}

RVA TO FOA

逆向的时候在OD找到了内存位置,那么通过这个位置找到文件对应的位置。

//RVA 转 FOA 
DWORD RvaToOffset(char * pbuff, DWORD RVA)
{    
   //1. 遍历判断RVA落在哪个区段  
   //2. 计算FOA = RVA - VOffset + ROffset;  
   PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pbuff;  
   PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + (DWORD)pbuff);
   // 区段头位置= pNT+4+sizeof(IMAGE_FILE_HEADERS)+sizeof(IMAGE_OPTIONAL_HEADERS)
   PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);
   for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++)  
  {      
       //判断落在什么区段
         if (RVA >= pSection[i].VirtualAddress &&
             RVA < (pSection[i].VirtualAddress + pSection[i].SizeOfRawData))
        {          
             //返回计算后的FOA        
             return RVA - pSection[i].VirtualAddress + pSection[i].PointerToRawData;
        }  
  }  
   return -1;
}

010Edit模板 安装EXE模板

例子

main.cpp

#include "pch.h"
#include <iostream>
#include "PE.h"
int main()
{
PE pe;
pe.ReadPe(L"..\\Debug\\test-PE.exe");

DWORD RVA = pe.GetNtHeader()->OptionalHeader.AddressOfEntryPoint;
DWORD FOA = pe.RvaToFoa(RVA);
printf("RVA = %08X FOA = %08X\n",RVA,FOA);

pe.ShowNTInfo();
pe.ShowSectionInfo();
}

PE.h

#pragma once
#include<windows.h>

class PE
{
public:
PE();
~PE();
//读取PE文件
char * ReadPe(const TCHAR * szPath);

//获取DOS头
PIMAGE_DOS_HEADER GetDosHeader();

//获取NT头
PIMAGE_NT_HEADERS GetNtHeader();

//显示NT头信息
void ShowNTInfo();

//获取区段头
PIMAGE_SECTION_HEADER GetSectionHeader();

//显示区段信息
void ShowSectionInfo();

//Rva To FoA
DWORD RvaToFoa(DWORD dwRva);



//PE文件指针
char *m_pBuff;

};

PE.cpp

#include "pch.h"
#include "PE.h"
#include <cstdio>


PE::PE()
{
}


PE::~PE()
{
}

char * PE::ReadPe(const TCHAR * szPath)
{
//1.打开一个文件
HANDLE hFile = CreateFile(
szPath, //打开的文件名
GENERIC_READ, //读方式打开
FILE_SHARE_READ,//共享方式
NULL, //安全属性
OPEN_EXISTING, //创建标志
FILE_ATTRIBUTE_NORMAL, //属性
NULL);

//2. 读取到内存中
//2.1获取文件大小
DWORD dwSize;
dwSize = GetFileSize(hFile, );

//2.2 开辟空间
m_pBuff= new char[dwSize];

//2.3 读取文件
DWORD dwReadSize;
ReadFile(hFile, m_pBuff, dwSize, &dwReadSize, );

//3.关闭文件
CloseHandle(hFile);

return m_pBuff;



return nullptr;
}

PIMAGE_DOS_HEADER PE::GetDosHeader()
{
return (PIMAGE_DOS_HEADER)(m_pBuff);
}

PIMAGE_NT_HEADERS PE::GetNtHeader()
{
//计算 nt头 = DOS.e_lfnew + m_pbuff

//获取dos头
PIMAGE_DOS_HEADER pDos = GetDosHeader();

//获取NT头
return (PIMAGE_NT_HEADERS)(pDos->e_lfanew + (DWORD)m_pBuff);
}




void PE::ShowNTInfo()
{
//获取NT头
PIMAGE_NT_HEADERS pNt = GetNtHeader();

// pNt->Signature PE00,0x00004550
printf("NT标志:%08X \n", pNt->Signature);

//文件头 区段数量 运行平台 扩展头大小
//运行平台 0x014c i386 ; 0x8664 amd64
//#define IMAGE_FILE_MACHINE_I386 0x014c
//#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8)

printf("区段个数:%08X \n", pNt->FileHeader.NumberOfSections);
printf("运行平台:%08X \n", pNt->FileHeader.Machine);
printf("扩展头大小:%08X \n", pNt->FileHeader.SizeOfOptionalHeader);

//输出扩展头信息
printf("程序入口点:%08X \n", pNt->OptionalHeader.AddressOfEntryPoint);
//对齐方式
//文件中:默认对齐0x200
//内存中:默认对齐0x1000
printf("文件对齐:%08X \n", pNt->OptionalHeader.FileAlignment);
printf("内存对齐:%08X \n", pNt->OptionalHeader.SectionAlignment);
//默认exe加载基址 0x00400000
//默认dll加载基址 0x10000000
printf("加载基址:%08X \n", pNt->OptionalHeader.ImageBase); //数据目录表
for (int i = ; i < ; i++)
{
printf(" 数据目录表 %d RVA:%08X Size:%08X\n",
i,
pNt->OptionalHeader.DataDirectory[i].VirtualAddress,
pNt->OptionalHeader.DataDirectory[i].Size
);
}

}



PIMAGE_SECTION_HEADER PE::GetSectionHeader()
{
//区段头在 NT头后面
//1. 获取NT头
PIMAGE_NT_HEADERS pNt = GetNtHeader();

//2.计算NT头后的区段头
return IMAGE_FIRST_SECTION(pNt);
}

void PE::ShowSectionInfo()
{
//1.获取区段个数 文件头中保存区段个数
DWORD dwCount = GetNtHeader()->FileHeader.NumberOfSections;

//2. 输出区段信息
PIMAGE_SECTION_HEADER pSection = GetSectionHeader();
for (DWORD i = ; i < dwCount; i++)
{
char szName[] = {};
strncpy_s(szName, (char*)pSection[i].Name, );
printf("区段名: %s\n", szName);

printf(" VOffset: %08X\n", pSection[i].VirtualAddress);
printf(" VSize: %08X\n", pSection[i].Misc.VirtualSize);
printf(" ROffset: %08X\n", pSection[i].PointerToRawData);
//文件中的大小 0x200对齐后的大小
printf(" RSize: %08X\n", pSection[i].SizeOfRawData);
}
}

DWORD PE::RvaToFoa(DWORD dwRva)
{
//1. 获取区段头表
PIMAGE_SECTION_HEADER pSection = GetSectionHeader();

//2. 遍历区段
DWORD dwCount = GetNtHeader()->FileHeader.NumberOfSections;
for (DWORD i = ; i < dwCount; i++)
{
//3. 判断这个RVA落在什么区段上
if (dwRva >= pSection[i].VirtualAddress
&& dwRva < (pSection[i].VirtualAddress + pSection[i].SizeOfRawData)
)
{
//3.1 FOA = RVA - 内存中区段位置 + 文件中区段位置
return dwRva - pSection[i].VirtualAddress + pSection[i].PointerToRawData;
}
}
return ;
}

PE基础1的更多相关文章

  1. PE基础2

    PE课程002 怎么找到Nt头? (PIMAGE_NT_HEADER)(DOS.e_lfanew + (DWORD)m_pBuff) 怎么找到第一个区段表? 区段头位置 = pNt + 4 + 文件头的 ...

  2. 【PE结构】由浅入深PE基础学习-菜鸟手动查询导出表、相对虚拟地址(RVA)与文件偏移地址转换(FOA)

    0 前言 此篇文章想写如何通过工具手查导出表.PE文件代码编程过程中的原理.文笔不是很好,内容也是查阅了很多的资料后整合出来的.希望借此加深对PE文件格式的理解,也希望可以对看雪论坛有所贡献.因为了解 ...

  3. PE文件解析 基础篇

    PE文件解析 基础篇 来源 https://bbs.pediy.com/thread-247114.htm 前言 之前学习了PE格式,为了更好的理解,决定写一个类似LoadPE的小工具. 编译器是VS ...

  4. [系统安全] 十六.PE文件逆向基础知识(PE解析、PE编辑工具和PE修改)

    [系统安全] 十六.PE文件逆向基础知识(PE解析.PE编辑工具和PE修改) 文章来源:https://masterxsec.github.io/2017/05/02/PE%E6%96%87%E4%B ...

  5. 基础篇-初步认识PE格式

    1 PE(Portable Executable)格式,是Win32环境可移植可执行文件(如exe.dll.vxd.sys和vdm等)的标准文件格式.PE格式衍生于早期建立在VAX(R)VMS(R)上 ...

  6. Windows PE入门基础知识:Windows PE的作用、命名规则、启动方式、启动原理

    Windows PE的全名是WindowsPreinstallationEnvironment(WinPE)直接从字面上翻译就 是"Windows预安装环境".微软的本意是:Win ...

  7. 【调试基础】Part 5 PE格式

    PE概念.区块分类

  8. PE文件基础

    ① PE (Portable Executable):微软参考COFF(Common Object File Format)规范,在Windows NT系统上制定的一种标准, 用于exe可执行文件.o ...

  9. .NET面试题系列[2] - .NET框架基础知识(2)

    3 程序集 面试出现频率:虽然很重要但不怎么出现,可能会考你定义,以及程序集包括什么,然后自然的话题就跑到反射上去了. 重要程度:8/10,很重要 需要理解的程度:知道程序集包括IL和元数据.知道元数 ...

随机推荐

  1. 在 Ubuntu 系统中有三种设置环境变量 PATH 的方法。(ZT) repost

    来源地址: http://blog.csdn.net/jernymy/article/details/6547671 第一种适用于为单一用户设置PATH.第二种是为全局设置 PATH.第三种方法适合于 ...

  2. Eclipse的maven工程不小心移除了Maven Dependencies,如何添加回来?

    转自:https://blog.csdn.net/eininotop/article/details/71124533 选择该工程--> 点击右键--> 选择Properties--> ...

  3. Table View Programming Guide for iOS---(三)----Overview of the Table View API

    Overview of the Table View API 表格视图API概述 The table view programming interface includes several UIKit ...

  4. Docker容器日志管理最佳实践

    目录 一 .Docker 引擎日志 二.容器日志 2.1.常用查看日志命令--docker logs 2.2 .Docker 日志 驱动 三. 生产环境中该如何储存容器中的日志 一.当是完全是标准输出 ...

  5. spoj LCS2 - Longest Common Substring II && LCS - Longest Common Substring【SAM】

    多串LCS很适合SA但是我要学SAM 对第一个串求SAM,然后把剩下的串在SAM上跑,也就是维护p和len,到一个点,如果有ch[p][c],就p=ch[p][c],len++,否则向fa找最下的有c ...

  6. Django学习:模板语法

    一.什么是模板? 只要是在html里面有模板语法就不是html文件了,这样的文件就叫做模板. 二.模板语法分类 一.模板语法之变量:语法为 {{ }}: 在 Django 模板中遍历复杂数据结构的关键 ...

  7. Luogu P1663 山【二分答案/实数域】By cellur925

    题目传送门 现在要在山上的某个部位装一盏灯,使得这座山的任何一个部位都能够被看到. 给出最小的y坐标,如图的+号处就是y坐标最小的安装灯的地方. 这个题嘛...今年省选前学姐来我们(破烂)的机房串门的 ...

  8. 寻找项目中顶级Vue对象 (一)

    个人博客首发博客园: http://www.cnblogs.com/zhangrunhao/ 参考 感谢作者 从一个奇怪的错误出发理解 Vue 基本概念 安装 - Vue.js 渲染函数 - Vue. ...

  9. 515 Find Largest Value in Each Tree Row 在每个树行中找最大值

    在二叉树的每一行中找到最大的值.示例:输入:           1         /  \        3   2       /  \    \        5   3    9 输出: [ ...

  10. 123 Best Time to Buy and Sell Stock III 买卖股票的最佳时机 III

    假设你有一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格.设计一个算法来找到最大的利润.你最多可以完成两笔交易.注意:你不可同时参与多笔交易(你必须在再次购买前出售掉之前的股票).详见: ...