PE基础1
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的更多相关文章
- PE基础2
PE课程002 怎么找到Nt头? (PIMAGE_NT_HEADER)(DOS.e_lfanew + (DWORD)m_pBuff) 怎么找到第一个区段表? 区段头位置 = pNt + 4 + 文件头的 ...
- 【PE结构】由浅入深PE基础学习-菜鸟手动查询导出表、相对虚拟地址(RVA)与文件偏移地址转换(FOA)
0 前言 此篇文章想写如何通过工具手查导出表.PE文件代码编程过程中的原理.文笔不是很好,内容也是查阅了很多的资料后整合出来的.希望借此加深对PE文件格式的理解,也希望可以对看雪论坛有所贡献.因为了解 ...
- PE文件解析 基础篇
PE文件解析 基础篇 来源 https://bbs.pediy.com/thread-247114.htm 前言 之前学习了PE格式,为了更好的理解,决定写一个类似LoadPE的小工具. 编译器是VS ...
- [系统安全] 十六.PE文件逆向基础知识(PE解析、PE编辑工具和PE修改)
[系统安全] 十六.PE文件逆向基础知识(PE解析.PE编辑工具和PE修改) 文章来源:https://masterxsec.github.io/2017/05/02/PE%E6%96%87%E4%B ...
- 基础篇-初步认识PE格式
1 PE(Portable Executable)格式,是Win32环境可移植可执行文件(如exe.dll.vxd.sys和vdm等)的标准文件格式.PE格式衍生于早期建立在VAX(R)VMS(R)上 ...
- Windows PE入门基础知识:Windows PE的作用、命名规则、启动方式、启动原理
Windows PE的全名是WindowsPreinstallationEnvironment(WinPE)直接从字面上翻译就 是"Windows预安装环境".微软的本意是:Win ...
- 【调试基础】Part 5 PE格式
PE概念.区块分类
- PE文件基础
① PE (Portable Executable):微软参考COFF(Common Object File Format)规范,在Windows NT系统上制定的一种标准, 用于exe可执行文件.o ...
- .NET面试题系列[2] - .NET框架基础知识(2)
3 程序集 面试出现频率:虽然很重要但不怎么出现,可能会考你定义,以及程序集包括什么,然后自然的话题就跑到反射上去了. 重要程度:8/10,很重要 需要理解的程度:知道程序集包括IL和元数据.知道元数 ...
随机推荐
- AtCoder Beginner Contest 057
A题 分析:24进制转换 #include<iostream> using namespace std; int main() { int a,b; while(cin>>a& ...
- 【扬中集训 DAY4T3】holiday
[题目链接] 点击打开链接 [算法] 建C棵线段树即可,注意要用前缀和优化 [代码] 这是标程 #include <bits/stdc++.h> using namespace std; ...
- leetcode 字符串动态规划总结
问题1:leetcode 正则表达式匹配 请实现一个函数用来匹配包括'.'和'*'的正则表达式.模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次). 在本题中,匹配 ...
- 用Xtrabackup实现MySQL全库备份与恢复
xtrabackup包含两个主要的工具,即xtrabackup和innobackupex,二者区别如下: (1)xtrabackup只能备份innodb和xtradb两种引擎的表,而不能备份myisa ...
- nodejs实现百度实时推送
想要加快百度收录,肯定免不了链接提交吧,当然链接提交的方式有很多种,今天来说一下百度的实时推送.. 第一次看到这post请求确实有点萌逼,我自己是做前端的对后台接触确实不多,见到的前端发送post请求 ...
- J20170426-hm
ジェネリクス Generics 泛型 バルーン balloon 气球 アングルブラケット Angle bracket 尖括号 プレースホルダ Placeholder 占位符
- 洛谷 P2296 寻找道路【bfs+spfa】
反向建边bfs出不能到t的点,然后对每个能到这些点的点打上del标记,然后spfa的时候不经过这些点即可 #include<iostream> #include<cstdio> ...
- (DP)51NOD 1007正整数分组
将一堆正整数分为2组,要求2组的和相差最小. 例如:1 2 3 4 5,将1 2 4分为1组,3 5分为1组,两组和相差1,是所有方案中相差最少的. 输入 第1行:一个数N,N为正整数的数量. 第 ...
- JQuery动态添加表格,然后动态删除不成功问题
背景: 自己做了一个测试网页,想动态添加表格,然后删除,按照网上的教程写完,发现点击"删除参数"按钮没用 源码: function addtr() { var trinfo = & ...
- python堆排序实现TOPK问题
# 构建小顶堆跳转def sift(li, low, higt): tmp = li[low] i = low j = 2 * i + 1 while j <= higt: # 情况2:i已经是 ...