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
{
WORD e_magic; //*DOS头魔数 Magic*
WORD e_cblp; //[Bytes on last page]
WORD e_cp; //[Pages in file]
WORD e_crlc; //[Relocations]
WORD e_cparhdr; //[Size of header]
WORD e_minalloc;//[Minium memory]
WORD e_maxalloc;//[Maxium Memory]
WORD e_ss; //[Inital SS value]
WORD e_sp; //[Inital SP value]
WORD e_csum; //[Checksum]
WORD e_ip; //[Inital IP value]
WORD e_cs; //[Inital CS value]
WORD e_lfarlc; //[Table offset]
WORD e_ovno; //[Overlay number]
WORD e_res[4]; //[Reserved words]
WORD e_oemid; //[OEM id]
WORD e_oeminfo; //[OEM infomation]
WORD e_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 <stdio.h>
#include <string.h>
#include <iostream>
#include <Windows.h>
using namespace std; //--------------------------PE结构----------------------------------
//--> DOS头(_IMAGE_DOS_HEADER ) <--
struct _IMAGE_DOS_HEADER_2
{
WORD e_magic; //*DOS头魔数 Magic*
WORD e_cblp; //[Bytes on last page]
WORD e_cp; //[Pages in file]
WORD e_crlc; //[Relocations]
WORD e_cparhdr; //[Size of header]
WORD e_minalloc;//[Minium memory]
WORD e_maxalloc;//[Maxium Memory]
WORD e_ss; //[Inital SS value]
WORD e_sp; //[Inital SP value]
WORD e_csum; //[Checksum]
WORD e_ip; //[Inital IP value]
WORD e_cs; //[Inital CS value]
WORD e_lfarlc; //[Table offset]
WORD e_ovno; //[Overlay number]
WORD e_res[4]; //[Reserved words]
WORD e_oemid; //[OEM id]
WORD e_oeminfo; //[OEM infomation]
WORD e_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);
struct tm 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头详细分析的更多相关文章

  1. PE节表详细分析

    目录 PE节表详细分析 0x00 前言 0x01 PE节表分析 节表结构 节表数量 节表名字 节表大小 节位置 节表属性 0x02 代码编写 PE节表详细分析 0x00 前言 上一篇文章我们学习了PE ...

  2. virut详细分析

    Virut分析 0x00.综合描述 virut样本的执行过程大体可以分为六步:第一步,解密数据代码,并调用解密后的代码:第二步,通过互斥体判断系统环境,解密病毒代码并执行:第三步,创建内存映射文件,执 ...

  3. 一个DOS攻击木马的详细分析过程

    一个DOS攻击木马的详细分析过程 0×01 起因 网路流量里发现了大量的的1.exe的文件,而且一直在持续,第一感觉就像是一个木马程序,而且每个1.exe的MD5都不一样,对比发现只有几个字节不一样( ...

  4. PE文件学习系列笔记四-C++实现PE文件的分析

    合肥程序员群:49313181.    合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入) Q  Q:408365330     E-Mail:egojit@qq.com 综述: 首 ...

  5. PE文件学习系列三-PE头详解

    合肥程序员群:49313181.    合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入) Q  Q:408365330     E-Mail:egojit@qq.com 最近比较忙 ...

  6. LinkedList详细分析

    一.源码解析1. LinkedList类定义2.LinkedList数据结构原理3.私有属性4.构造方法5.元素添加add()及原理6.删除数据remove()7.数据获取get()8.数据复制clo ...

  7. HashMap 源码详细分析(JDK1.8)

    一.概述 本篇文章我们来聊聊大家日常开发中常用的一个集合类 - HashMap.HashMap 最早出现在 JDK 1.2中,底层基于散列算法实现.HashMap 允许 null 键和 null 值, ...

  8. LinkedHashMap 源码详细分析(JDK1.8)

    1. 概述 LinkedHashMap 继承自 HashMap,在 HashMap 基础上,通过维护一条双向链表,解决了 HashMap 不能随时保持遍历顺序和插入顺序一致的问题.除此之外,Linke ...

  9. NTFS文件系统详细分析

    NTFS文件系统详细分析 第一部分 什么是NTFS文件系统 想要了解NTFS,我们首先应该认识一下FAT.FAT(File   Allocation   Table)是“文件分配表”的意思.对我们来说 ...

随机推荐

  1. 前端js单元测试 使用mocha、chai、sinon,karma

    karma(因果报应)  提供在浏览器上测试  可以同时跑在多个浏览器下 mocha测试框架  其他测试框架还有Jasmine chai断言库  expect = chai.expect sinon ...

  2. thinkphp5自带workerman应用

    1.在vendor/workerman/文件夹下建立server.php文件,内容如下: <?php use Workerman\Worker; require_once __DIR__ . ' ...

  3. linux mint 18.1 安装备忘录

    本次全新安装mint18.1,遇到一些问题,全部解决,怕日后忘记,再捣鼓琢磨,浪费时间,特记录在此: 一.楷体字体问题 安装完后的mint18.1,显示都是楷体,经请教薄荷论坛高手,可用以下办法解决: ...

  4. 定要过python二级 选择第3套

    1 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. . 13. 14. 15. 16. 17. (1)说明了一个问题 所谓的方向是从左到右还是从右到左  是看的是步长  步长的 ...

  5. CF917D-Stranger Trees【矩阵树定理,高斯消元】

    正题 题目链接:https://www.luogu.com.cn/problem/CF917D 题目大意 给出\(n\)个点的一棵树,对于每个\(k\)求有多少个\(n\)个点的树满足与给出的树恰好有 ...

  6. YbtOJ#763-攻城略池【线段树合并】

    正题 题目链接:http://www.ybtoj.com.cn/problem/763 题目大意 给出\(n\)个点的一棵树,每个\(d_i=0\)的点每秒会产生一个士兵往根节点走,走到一个节点让一个 ...

  7. 如何一次性add library to classpath

    前言:导入项目时,时常需要手动导包,提示"add library to classpath",需要一个个找报红的类 点击添加本地项目包

  8. Redis三种集群模式介绍

    三种集群模式 redis有三种集群模式,其中主从是最常见的模式. Sentinel 哨兵模式是为了弥补主从复制集群中主机宕机后,主备切换的复杂性而演变出来的.哨兵顾名思义,就是用来监控的,主要作用就是 ...

  9. NOIP 模拟八 考试总结

    T1星际旅行 给出n个点,m条边,求满足一条路径使得m-2条边经过2次,2条边经过1次的方案数.并且题目中给出有自环. 看到题面我以为是个计数DP,可能是计数题做多了吧哈哈.其实仔细朝图的方向想一想就 ...

  10. 实践node.js构建vue项目

    一.首先安装下载node.js 1.Node.js 官方网站下载:https://nodejs.org/en/,自行选择合适自己的下载安装即可 2.验证安装 打开cmd,输入node  –v和 npm ...