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, 0);
​
    //2.2 开辟空间
    m_pBuff= new char[dwSize];
​
    //2.3 读取文件
    DWORD dwReadSize;
    ReadFile(hFile, m_pBuff, dwSize, &dwReadSize, 0);
​
    //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 = 0; i < 16; 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 = 0; i < dwCount; i++)
    {
        char szName[9] = {0};
        strncpy_s(szName, (char*)pSection[i].Name, 8);
        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 = 0; 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 0;
}

 

猜你喜欢

转载自www.cnblogs.com/ltyandy/p/11077846.html
PE
今日推荐