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和元数据.知道元数 ...
随机推荐
- 在 Ubuntu 系统中有三种设置环境变量 PATH 的方法。(ZT) repost
来源地址: http://blog.csdn.net/jernymy/article/details/6547671 第一种适用于为单一用户设置PATH.第二种是为全局设置 PATH.第三种方法适合于 ...
- Eclipse的maven工程不小心移除了Maven Dependencies,如何添加回来?
转自:https://blog.csdn.net/eininotop/article/details/71124533 选择该工程--> 点击右键--> 选择Properties--> ...
- 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 ...
- Docker容器日志管理最佳实践
目录 一 .Docker 引擎日志 二.容器日志 2.1.常用查看日志命令--docker logs 2.2 .Docker 日志 驱动 三. 生产环境中该如何储存容器中的日志 一.当是完全是标准输出 ...
- 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 ...
- Django学习:模板语法
一.什么是模板? 只要是在html里面有模板语法就不是html文件了,这样的文件就叫做模板. 二.模板语法分类 一.模板语法之变量:语法为 {{ }}: 在 Django 模板中遍历复杂数据结构的关键 ...
- Luogu P1663 山【二分答案/实数域】By cellur925
题目传送门 现在要在山上的某个部位装一盏灯,使得这座山的任何一个部位都能够被看到. 给出最小的y坐标,如图的+号处就是y坐标最小的安装灯的地方. 这个题嘛...今年省选前学姐来我们(破烂)的机房串门的 ...
- 寻找项目中顶级Vue对象 (一)
个人博客首发博客园: http://www.cnblogs.com/zhangrunhao/ 参考 感谢作者 从一个奇怪的错误出发理解 Vue 基本概念 安装 - Vue.js 渲染函数 - Vue. ...
- 515 Find Largest Value in Each Tree Row 在每个树行中找最大值
在二叉树的每一行中找到最大的值.示例:输入: 1 / \ 3 2 / \ \ 5 3 9 输出: [ ...
- 123 Best Time to Buy and Sell Stock III 买卖股票的最佳时机 III
假设你有一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格.设计一个算法来找到最大的利润.你最多可以完成两笔交易.注意:你不可同时参与多笔交易(你必须在再次购买前出售掉之前的股票).详见: ...