本帖最后由 小菜鸟一枚 于 2021-6-24 20:20 编辑
0x1什么是PE文件?  1.PE文件是在windows平台可执行的文件。
包括:.exe(可执行程序),dll(动态链接库).sys(驱动程序)
要想学好逆向PE必须要掌握。

  2.DOS头
DOS头 |
| e_magic//DOS头签名,可执行文件的标志。 |
| e_lfanew//PE头的偏移位置 |
| PE头 |
| 区段1 |
| 区段2 |
| 区段3 |
0x2打开一个exe文件  1.打开vs 2015编译器,写一个helloworld程序:
复制代码 隐藏代码
#include <Windows.h>
int WINAPI WinMain(
HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // command line
int nCmdShow // show state
)
{
DWORD a;
MessageBox(NULL, TEXT("Helloworld!"), TEXT("小菜鸟提醒您:"), MB_OK);
return 0;
}
  2.生成exe文件,这里使用winHex工具打开exe文件。

  3.DOS头开头word,2字节,e_magic就是前四位0x5A4D(从高到低位读),对应ASCII码MZ,再看e_lfanew,看看前面的图,数一下30个word,占了60位偏移,看看typedef unsigned long DWORD;,所以e_lfanew就是0x00000108,相对PE头偏移量就是0x108。

  4.0x108就是PE头,PE头第一个字节就是WORD Magic,根据偏移量得到0x4550,对应的ASCII码正是PE。

0x3解析DOS头  1.要想解析DOS,首先得读文件,这里使用CreateFile,读完之后得解析,在编译器中查到DOS头结构体的声明:
复制代码 隐藏代码
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;
  2.这里可以定义一个IMAGE_DOS_HEADER,也可以使用*PIMAGE_DOS_HEADER类型解析DOS头,这里我使用后者,读取了DOS结构能干嘛呢?
1)判断e_magic是不是0x5A4D开头
2)获取PE结构的偏移地址  3.实现代码如下:
复制代码 隐藏代码
#include<Windows.h>
#include<iostream>
using namespace std;
int main()
{
/*
HANDLE CreateFile(LPCTSTR lpFileName, //普通文件名或者设备文件名
DWORD dwDesiredAccess, //访问模式(写/读)
DWORD dwShareMode, //共享模式
LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向安全属性的指针
DWORD dwCreationDisposition, //如何创建
DWORD dwFlagsAndAttributes, //文件属性
HANDLE hTemplateFile //用于复制文件句柄
);
*/
HANDLE hfile = CreateFile(TEXT("Win32Project0623.exe"),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if (!hfile)
{
cout << "打开Win32Project0623.exe文件失败!" << endl;
return -1;
}
/*
BOOL ReadFile(
HANDLE hFile, //文件的句柄
LPVOID lpBuffer, //用于保存读入数据的一个缓冲区
DWORD nNumberOfBytesToRead, //要读入的字节数
LPDWORD lpNumberOfBytesRead, //指向实际读取字节数的指针
LPOVERLAPPED lpOverlapped
//如文件打开时指定了FILE_FLAG_OVERLAPPED,那么必须,用这个参数引用一个特殊的结构。
//该结构定义了一次异步读取操作。否则,应将这个参数设为NULL
);
*/
DWORD fileSize = GetFileSize(hfile, NULL);//获取文件大小
char *lpBuffer = new char[fileSize];
DWORD lpNumberOfBytesRead = 0;//实际读了多大,这是一个输出变量,需要将地址传递进去,带回结果
BOOL blReadFile = ReadFile(hfile, lpBuffer, fileSize, &lpNumberOfBytesRead, NULL);
PIMAGE_DOS_HEADER pIMAGE_DOS_HEADER = 0;
if (blReadFile)
{
pIMAGE_DOS_HEADER = (PIMAGE_DOS_HEADER) lpBuffer;
if (pIMAGE_DOS_HEADER->e_magic != 0x5A4D)
{
cout <<"这不是一个有效的PE文件" << endl;
}
}
cout << "pIMAGE_DOS_HEADER->e_lfanew:0x"<<hex<< pIMAGE_DOS_HEADER->e_lfanew << endl;
delete[] lpBuffer;
system("pause");
return 0;
}
  4.打个断点,看看是不是解析出来了:

  5.是不是和我们前面在十六进制编辑器里面分析的一样,开头是0X5A4D,结果指向PE头的偏移量是0x108。
0x4总结  1.第一次接触PE结构,一点一点慢慢来,做好笔记。
  2.程序的DOS头开始是MZ,PE头的开始是PE。
0x5参考资料: