PE头详细分析
目录
- PE头详细分析
- 0x00 前言
- 0x01 PE文件介绍
- 0x02 PE头详细分析
- DOS头解析
- NT头解析
- 标准PE头解析
- 可选PE头解析
- 可选PE头结构
- 基址
- 代码段地址
- 数据段地址
- OEP程序入口点
- 0x03 PE头解析工具编写
PE头详细分析 0x00 前言 最近我在学习Linux PWN相关知识的时候,也是在看《程序员的自我修养(装载->链接->库)》这本书的时候,接触到了可执行文件格式,COFF、ELF、PE,所以也找了Bilibili上海东老师的《滴水逆向三期》视频中的PE课程在学习,刚看完PE头这节课。在此做个笔记也算是整理学习的结果 并且分享给大家,共同学习,如有错误欢迎指正。
0x01 PE文件介绍 PE文件是Windows上的可执行文件,就是我们鼠标双击就能运行的程序,当然也有双击不能运行的程序。其中
.exe
、.dll
、.sys
这些都是PE文件,那么读者可能有个疑问,我双击.txt的文件也是能直接打开运行的啊?Emmmmm....这个其实他是用Notepad记事本加载并打开的,而PE可执行文件他是经过系统加载并运行的。文章图片
PE文件是分块存储的。在图中我们可以看出数据被加载到内存后会不一样,其中PE文件被加载到内存中的时候(相当于一个拉伸的过程),把数据给拉长了。
不过块中的数据还是一样的,我们可以看到最开头的快就是PE头的数据,接下来是节表等其他块的数据,这些我们会在后续文章中介绍。
文章图片
0x02 PE头详细分析 PE主要由3部分构造,(1)、DOS头 (2)、NT头(标准PE头、可选PE头)。
参考图:(OpenRCE.org网站上的PE格式图.pdf)
文章图片
DOS头解析
DOS头中的数据如下,其中带*号的是比较重要的数据,DOS头大小为:64字节。
//--> DOS头(_IMAGE_DOS_HEADER ) <--
struct _IMAGE_DOS_HEADER
{
WORDe_magic;
//*DOS头魔数 Magic*
WORDe_cblp;
//[Bytes on last page]
WORDe_cp;
//[Pages in file]
WORDe_crlc;
//[Relocations]
WORDe_cparhdr;
//[Size of header]
WORDe_minalloc;
//[Minium memory]
WORDe_maxalloc;
//[Maxium Memory]
WORDe_ss;
//[Inital SS value]
WORDe_sp;
//[Inital SP value]
WORDe_csum;
//[Checksum]
WORDe_ip;
//[Inital IP value]
WORDe_cs;
//[Inital CS value]
WORDe_lfarlc;
//[Table offset]
WORDe_ovno;
//[Overlay number]
WORDe_res[4];
//[Reserved words]
WORDe_oemid;
//[OEM id]
WORDe_oeminfo;
//[OEM infomation]
WORDe_res2[10];
//[Reserved words]
DWORD e_lfanew;
//*NT头地址*
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
文章图片
NT头解析
NT头主要由3部分构成,标记、标准PE头、可选PE头。其中NT头魔数就是
PE
字符串,可见上图。//--> NT头(_IMAGE_NT_HEADERS) <--
struct _IMAGE_NT_HREADERS
{
DWORD Signature;
//*NT头魔数
_IMAGE_FILE_HEADER FileHeader;
//标准PE头
_IMAGE_OPTIONAL_HEADER OptionalHeader;
//可选PE头
}IMAGE_NT_HREADERS,*PIMAGE_NT_HREADERS;
标准PE头解析
标志PE头的固定大小是20字节,其中我们可以看见里面有区段数目的数据。
还有比较关注的点是时间戳,这个时间戳我们可以用文章https://www.cnblogs.com/17bdw/p/6412158.html中的方法转换成
文件创建时间
。最后特征的数据也要关注下,因为可以用他来判断PE文件的许多特征信息,比如是否为DLL文件、重定位信息是否被移去、是否为系统文件等等。
//--> 标准PE头(_IMAGE_FILE_HEADER) <--
struct _IMAGE_FILE_HEADER
{
WORD Machine;
//*运行平台
WORD NumberOfSections;
//*区段数目
DWORD TimeDateStamp;
//*时间戳
DWORD PointerToSymbolTable;
//[Pointer to COFF]
DWORD NumberOfSymbols;
//[COFF table size]
WORD SizeOfOptionalHeader;
//*可选PE头大小
WORD Characteristics;
//*特征
}IMAGE_FILE_HEADER,*PIMAGE_FILE_HEADER;
HEX数据
文章图片
解析后
文章图片
文章图片
文章图片
可选PE头解析
可选PE头结构
//--> 可选PE头(_IMAGE_OPTIONAL_HEADER) <--
struct _IMAGE_OPTIONAL_HEADER
{
WORD Magic;
//[可选PE头魔数]
BYTE MajorLinkerVersion;
//[主链接器版本]
BYTE MinorLinkerVersion;
//[副链接器版本]
DWORD SizeOfCode;
//[代码段大小]
DWORD SizeOfInitializedData;
//[初始化数据大小]
DWORD SizeOfUninitializedData;
//[未初始化数据大小]
DWORD AddressOfEntryPoint;
//*[程序入口点]
DWORD BaseOfCode;
//*[代码段地址]
DWORD BaseOfData;
//*[数据段地址]
DWORD ImageBase;
// *[加载到内存的开始地址] -> 一般好像都是0x400000
DWORD SectionAlignment;
//*[内存页对其大小]
DWORD FileAlignment;
//[文件对其大小]
WORD MajorOperatingSystemVersion;
//*[操作系统的主版本号]
WORD MinjorOperatingSystemVersion;
//*[操作系统的次版本号]
WORD MajorImageVersion;
//[程序主版本号]
WORD MinorImageVersion;
//[程序次版本号]
WORD MajorSubsystemVersion;
//[子系统主版本号]
WORD MinorSubsystemVersion;
//[子系统次版本号]
DWORD Win32VersionValue;
//[默认保留]
DWORD SizeOfImage;
//*[加载到内存映像的大小]
DWORD SizeOfHeaders;
//[DOS头 PE头 节头组合大小]
DWORD CheckSum;
//*[获取加载到内存映像的hash]
WORD Subsystem;
//[运行此映像所需要的子系统名称]
WORD DllCharacteristics;
//[DLL映像的特征]
DWORD SizeOfStackReserve;
//*[获取保留堆栈的大小]
DWORD SizeOfStackCommit;
//*[获取要提交堆栈的大小]
DWORD SizeOfHeapReserve;
//*[获取保留堆空间的大小]
DWORD SizeOfHeapCommit;
//*[获取要提交的本地堆空间大小]
DWORD LoaderFlags;
//[之前保留的成员]
DWORD NumberOfRvaAndSizes;
//*[获取 PEHeader 剩余部分中数据目录项的数目 |位置和大小]
_IMAGE_DATA_DIRECTORY DataDirectory[16];
//[指向数据目录中的第一个 IMAGE_DATA_DIRECTORY 结构的指针。]
}IMAGE_OPTIONAL_HEADER,*PIMAGE_OPTIONAL_HEADER;
可以看出可选PE头数据最多,不过这是好事对我们有用的数据也很多,比如下面标红的都是平时在逆向或者脱壳、破解中比较有用的信息。
文章图片
基址 首先第一个魔数0x010B我们就不说了,主要应该是为了区别32位和64位程序吧。
接着我们看程序基地址
0x01000000
,这个基地址的意思就是程序加载到内存时候PE文件所在的位置,Windows他会为每个程序分配一个虚拟的4GB空间。这里有的读者会问那为什么基址非要那么大,为什么不是0呢?因为Windows他有一段内存是用来保护用的,平时我们在写C++代码时候比如:我们引用了一个NULL(空指针)其指向的内存地址就是0的时候,程序就会崩溃就会报错!,没错这就是Windows为了保护程序设计的。
我们也可以用Winhex打开加载在内存中Notepad.exe的数据,可以发现其首地址就是程序基址。
文章图片
代码段地址 接着我们来看代码段的地址
0x001000
,也就是PE文件在这地方开始存的都是程序代码,当然在底层被转为了汇编代码。文章图片
我们可以用radare2套件中的rasm2来将十六进制转换成汇编代码看看。
rasm2 -a x86 -b 32 -d "十六进制"
#-a 代表平台 x86架构平台
#-b 位数32位
#-d 解码解析成汇编
文章图片
数据段地址 接下来看数据段的地址
0x009000
,数据段主要存放数据,比如字符串等数据,在下图中能看到存放了Notepad字符串。文章图片
OEP程序入口点 OEP是可选PE头结构体中第7个成员
AddressOfEntryPoint
的数据,顾名思义指的是程序开始执行的第一行代码位置。由于程序被加载到内存,所以我们还需要加上
基址
才是真实的程序入口点。即:
基址
+OEP
= 0x01000000 + 0x739D = 0x0100739D
。文章图片
我们可以用工具将其转换成汇编,看看第一行汇编代码是什么?
文章图片
我们也可以利用OD来加载程序,OD加载程序默认会自动加载到OEP处。所以可以来验证下我们找的位置对不对。
文章图片
好了可选头PE内容介绍到这里就结束了,其中可选PE头还有最有一个结构体数据
_IMAGE_DATA_DIRECTORY
,这个暂时就先不介绍了,留在后面介绍其他内容的时候在详解。0x03 PE头解析工具编写 知道了PE头结构后,代码写起来也是很方便,而且微软有自带的PE头结构体,我们可以直接open()文件后直接读取内容到结构体解析即可。
/*******************************************************
*
* 学习滴水逆向 PE结构分析代码练习
*
* 海东老师 Bilibili:滴水逆向三期
*********************************************************///头文件定义
#include
#include
#include
#include
using namespace std;
//--------------------------PE结构----------------------------------
//--> DOS头(_IMAGE_DOS_HEADER ) <--
struct _IMAGE_DOS_HEADER_2
{
WORDe_magic;
//*DOS头魔数 Magic*
WORDe_cblp;
//[Bytes on last page]
WORDe_cp;
//[Pages in file]
WORDe_crlc;
//[Relocations]
WORDe_cparhdr;
//[Size of header]
WORDe_minalloc;
//[Minium memory]
WORDe_maxalloc;
//[Maxium Memory]
WORDe_ss;
//[Inital SS value]
WORDe_sp;
//[Inital SP value]
WORDe_csum;
//[Checksum]
WORDe_ip;
//[Inital IP value]
WORDe_cs;
//[Inital CS value]
WORDe_lfarlc;
//[Table offset]
WORDe_ovno;
//[Overlay number]
WORDe_res[4];
//[Reserved words]
WORDe_oemid;
//[OEM id]
WORDe_oeminfo;
//[OEM infomation]
WORDe_res2[10];
//[Reserved words]
DWORD e_lfanew;
//*PE文件头地址*
} IMAGE_DOS_HEADER_2, *PIMAGE_DOS_HEADER_2;
//--> NT头(_IMAGE_NT_HEADERS) <--
struct _IMAGE_NT_HREADERS_2
{
DWORD Signature;
_IMAGE_FILE_HEADER FileHeader;
_IMAGE_OPTIONAL_HEADER OptionalHeader;
}IMAGE_NT_HREADERS_2,*PIMAGE_NT_HREADERS_2;
//--> 标准PE头(_IMAGE_FILE_HEADER) <--
struct _IMAGE_FILE_HEADER_2
{
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
}IMAGE_FILE_HEADER_2,*PIMAGE_FILE_HEADER_2;
//--> 可选PE头(_IMAGE_OPTIONAL_HEADER) <--
struct _IMAGE_OPTIONAL_HEADER_2
{
WORD Magic;
//[可选PE头魔数]
BYTE MajorLinkerVersion;
//[主链接器版本]
BYTE MinorLinkerVersion;
//[副链接器版本]
DWORD SizeOfCode;
//[代码段大小]
DWORD SizeOfInitializedData;
//[初始化数据大小]
DWORD SizeOfUninitializedData;
//[未初始化数据大小]
DWORD AddressOfEntryPoint;
//*[程序入口点]
DWORD BaseOfCode;
//*[代码段地址]
DWORD BaseOfData;
//*[数据段地址]
DWORD ImageBase;
// *[加载到内存的开始地址] -> 一般好像都是0x400000
DWORD SectionAlignment;
//*[内存页对其大小]
DWORD FileAlignment;
//[文件对其大小]
WORD MajorOperatingSystemVersion;
//*[操作系统的主版本号]
WORD MinjorOperatingSystemVersion;
//*[操作系统的次版本号]
WORD MajorImageVersion;
//[程序主版本号]
WORD MinorImageVersion;
//[程序次版本号]
WORD MajorSubsystemVersion;
//[子系统主版本号]
WORD MinorSubsystemVersion;
//[子系统次版本号]
DWORD Win32VersionValue;
//[默认保留]
DWORD SizeOfImage;
//*[加载到内存映像的大小]
DWORD SizeOfHeaders;
//[DOS头 PE头 节头组合大小]
DWORD CheckSum;
//*[获取加载到内存映像的hash]
WORD Subsystem;
//[运行此映像所需要的子系统名称]
WORD DllCharacteristics;
//[DLL映像的特征]
DWORD SizeOfStackReserve;
//*[获取保留堆栈的大小]
DWORD SizeOfStackCommit;
//*[获取要提交堆栈的大小]
DWORD SizeOfHeapReserve;
//*[获取保留堆空间的大小]
DWORD SizeOfHeapCommit;
//*[获取要提交的本地堆空间大小]
DWORD LoaderFlags;
//[之前保留的成员]
DWORD NumberOfRvaAndSizes;
//*[获取 PEHeader 剩余部分中数据目录项的数目 |位置和大小]
_IMAGE_DATA_DIRECTORY DataDirectory[16];
//[指向数据目录中的第一个 IMAGE_DATA_DIRECTORY 结构的指针。]
}IMAGE_OPTIONAL_HEADER_2,*PIMAGE_OPTIONAL_HEADER_2;
//-----------------------------------------------------------------int main(int args,char *argv[])
{
if (args < 2)
{
printf("参数有误,请按照如下格式调用本程序!\n");
printf("PEAnysis.exe 程序名.exe\n");
return 0;
}
//初始化可有颜色终端Handle
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
//
printf("===================PE Anysis-PE文件分析程序工具!==========================\n\n");
FILE* fp = fopen(argv[1], "rb");
if (fp != NULL)
{
//读取DOS头
fread(&IMAGE_DOS_HEADER_2, sizeof(IMAGE_DOS_HEADER_2), 1, fp);
//跳转到NT头
fseek(fp, IMAGE_DOS_HEADER_2.e_lfanew, 0);
//读取NT头
fread(&IMAGE_NT_HREADERS_2, sizeof(IMAGE_NT_HREADERS_2), 1, fp);
printf("---------------PE头数据----------------\n");
//输出DOS头信息
cout << "--> DOS头(_IMAGE_DOS_HEADER ) <--" << endl;
SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED);
char szMagic[3] = { 0 };
memcpy(szMagic, &IMAGE_DOS_HEADER_2.e_magic, 2);
printf("*DOS头魔数:0x%x|%s\n", IMAGE_DOS_HEADER_2.e_magic, szMagic);
SetConsoleTextAttribute(handle, 0x07);
printf("[Bytes on last page]:0x%x\n", IMAGE_DOS_HEADER_2.e_cblp);
printf("[Pages in file]:0x%x\n", IMAGE_DOS_HEADER_2.e_cp);
printf("[Relocations]:0x%x\n", IMAGE_DOS_HEADER_2.e_crlc);
printf("[Size of header]:0x%x\n", IMAGE_DOS_HEADER_2.e_cparhdr);
printf("[Minium memory]:0x%x\n", IMAGE_DOS_HEADER_2.e_minalloc);
printf("[Maxium Memory]:0x%x\n", IMAGE_DOS_HEADER_2.e_maxalloc);
printf("[Inital SS value]:0x%x\n", IMAGE_DOS_HEADER_2.e_ss);
printf("[Inital SP value]:0x%x\n", IMAGE_DOS_HEADER_2.e_sp);
printf("[Checksum]:0x%x\n", IMAGE_DOS_HEADER_2.e_csum);
printf("[Inital IP value]:0x%x\n", IMAGE_DOS_HEADER_2.e_ip);
printf("[Inital CS value]:0x%x\n", IMAGE_DOS_HEADER_2.e_cs);
printf("[Table offset]:0x%x\n", IMAGE_DOS_HEADER_2.e_lfarlc);
printf("[Overlay number]:0x%x\n", IMAGE_DOS_HEADER_2.e_ovno);
printf("[Reserved words]:", IMAGE_DOS_HEADER_2.e_res);
for (size_t i = 0;
i < 4;
i++)
{
printf("0x%x, ", IMAGE_DOS_HEADER_2.e_res[0]);
}
cout << endl;
printf("[OEM id]:0x%x\n", IMAGE_DOS_HEADER_2.e_oemid);
printf("[OEM infomation]:0x%x\n", IMAGE_DOS_HEADER_2.e_oeminfo);
printf("[Reserved words]:", IMAGE_DOS_HEADER_2.e_res2);
for (size_t i = 0;
i < 10;
i++)
{
printf("0x%x, ", IMAGE_DOS_HEADER_2.e_res2[0]);
}
cout << endl;
SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED);
printf("*PE文件头地址:0x%x\n", IMAGE_DOS_HEADER_2.e_lfanew);
SetConsoleTextAttribute(handle, 0x07);
cout << "DOS头大小:" << sizeof(IMAGE_DOS_HEADER_2) << endl;
cout << endl;
//输出标准PE头信息
cout << "--> 标准PE头(_IMAGE_FILE_HEADER) <--" << endl;
char szNTSignature[3] = { 0 };
memcpy(szNTSignature, &IMAGE_NT_HREADERS_2.Signature, 2);
printf("[NT头标识]:%s\n", szNTSignature);
SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED);
printf("*[运行平台]:0x%x\n", IMAGE_NT_HREADERS_2.FileHeader.Machine);
printf("*[节数量]:0x%x\n", IMAGE_NT_HREADERS_2.FileHeader.NumberOfSections);
printf("*[时间戳]:0x%x\n", IMAGE_NT_HREADERS_2.FileHeader.TimeDateStamp);
SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_GREEN);
structtm test_gmtime_s;
errno_t err = gmtime_s(&test_gmtime_s, (time_t*)&IMAGE_NT_HREADERS_2.FileHeader.TimeDateStamp);
printf("文件创建时间:%d年%d月%d日 %02d时:%02d分:%02d秒(周%d)\n", test_gmtime_s.tm_year + 1900, test_gmtime_s.tm_mon, test_gmtime_s.tm_mday,
test_gmtime_s.tm_hour + 8, test_gmtime_s.tm_min, test_gmtime_s.tm_sec, test_gmtime_s.tm_wday);
SetConsoleTextAttribute(handle, 0x07);
printf("[Pointer to COFF]:0x%x\n", IMAGE_NT_HREADERS_2.FileHeader.PointerToSymbolTable);
printf("[COFF table size]:0x%x\n", IMAGE_NT_HREADERS_2.FileHeader.NumberOfSections);
SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED);
printf("*[可选头大小]:0x%x\n", IMAGE_NT_HREADERS_2.FileHeader.SizeOfOptionalHeader);
printf("*[特征/特性]:0x%x\n", IMAGE_NT_HREADERS_2.FileHeader.Characteristics);
SetConsoleTextAttribute(handle, 0x07);
cout << "标准PE头大小:" << sizeof(IMAGE_NT_HREADERS_2.FileHeader) << endl;
cout << endl;
//输出可选PE头信息
cout << "--> 可选PE头(_IMAGE_OPTIONAL_HEADER) <--" << endl;
SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED);
printf("*[程序内存入口点]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.AddressOfEntryPoint + IMAGE_NT_HREADERS_2.OptionalHeader.ImageBase);
printf("*[可选PE头魔数]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.Magic);
printf("*[主链接器版本]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.MajorLinkerVersion);
printf("*[副链接器版本]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.MinorLinkerVersion);
printf("*[代码段大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.SizeOfCode);
printf("*[初始化数据大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.SizeOfInitializedData);
printf("*[未初始化数据大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.SizeOfUninitializedData);
printf("*[代码段地址]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.BaseOfCode);
printf("*[数据段地址]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.BaseOfData);
printf("*[PE文件基地址]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.ImageBase);
printf("*[程序入口点]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.AddressOfEntryPoint);
SetConsoleTextAttribute(handle, 0x07);
printf("[内存对其大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.SectionAlignment);
printf("[文件对其大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.FileAlignment);
printf("[操作系统的主版本号]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.MajorOperatingSystemVersion);
printf("[操作系统的次版本号]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.MinorOperatingSystemVersion);
printf("[程序主版本号]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.MajorImageVersion);
printf("[程序次版本号]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.MinorImageVersion);
printf("[子系统主版本号]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.MajorSubsystemVersion);
printf("[子系统次版本号]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.MinorSubsystemVersion);
printf("[Win32版本值]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.Win32VersionValue);
SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED);
printf("*[内存映像大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.SizeOfImage);
printf("*[DOS|PE|节头大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.SizeOfHeaders);
printf("*[内存映像hash]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.CheckSum);
SetConsoleTextAttribute(handle, 0x07);
printf("[程序可以运行的系统]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.Subsystem);
SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED);
printf("*[DLL映像的特征]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.DllCharacteristics);
printf("*[获取保留堆栈的大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.SizeOfStackReserve);
printf("*[获取要提交堆栈的大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.SizeOfStackCommit);
printf("*[获取保留堆空间的大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.SizeOfHeapReserve);
printf("*[获取要提交的本地堆空间大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.SizeOfHeapCommit);
SetConsoleTextAttribute(handle, 0x07);
printf("[加载标志(已废弃)]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.LoaderFlags);
printf("[获取PEHeader剩余部分数据,位置和大小]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.NumberOfRvaAndSizes);
printf("[指向IMAGE_DATA_DIRECTORY结构指针]:0x%x\n", IMAGE_NT_HREADERS_2.OptionalHeader.DataDirectory);
cout << "可选PE头大小:" << sizeof(IMAGE_NT_HREADERS_2.OptionalHeader) << endl;
cout << endl;
printf("---------------节表数据----------------\n");
printf("===========================================================================\n\n");
}
else
{
printf("文件打开失败,请检查是否被占用!\n");
return 0;
} int x;
cin >> x;
return 0;
}
文章图片
最后欢迎大家加群:1145528880
【PE头详细分析】
文章图片
推荐阅读
- 低头思故乡——只是因为睡不着
- 如何寻找情感问答App的分析切入点
- 村里的故事|村里的故事 --赵大头
- MediaRecorder前后摄像头同时录像
- 石头巷;名垂青史的廉政教材
- 眉头开了
- D13|D13 张贇 Banner分析
- 读司马懿,知人间事,品百味人生
- 2019-1-14
- 怎样用黑谜速冻膜去黑头,|怎样用黑谜速冻膜去黑头, 最有效的去黑头的方法看这!