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. 3.8学习总结——Android保存信息

    为了保存软件的设置参数,Android平台为我们提供了一个SharedPreferences接口,它是一个轻量级的存储类,特别适合用于保存软件配置参数.使用SharedPreferences保存数据, ...

  2. 变着花样来接参,PHP中接收外部参数的方式

    对于PHP这样一个web语言来说,接参是非常重要的一个能力.毕竟从前端表单或异步请求传递上来的数据都要获取到才能进行正常的交互展示.当然,这也是所有能够进行web开发的语言的必备能力.今天我们就来看看 ...

  3. CI框架页面打开空白,无报错为解决方法新环境

    1.打开错误显示,可以在控制controllers的首页入口加入以下代码,查看错误 error_reporting(-1); ini_set('display_errors', 1); //插入显示所 ...

  4. ecshop调用指定分类热销-新品-精品

    在模板页里首页写上代码:         <?php$children = get_children(16);//此处为产品分类ID$smarty->assign( 'bestGoods1 ...

  5. 使用ELK监控Nginx日志实现接口流量访问统计

    前段时间自己看书学习了一下elasticSearch,后面自己实践了使用elasticSearch.logStash.kibana搭建一个网站接口流量访问统计的监控看板.在这里做一些记录学习. 先看一 ...

  6. [转载]Samba 4实现windows匿名访问Linux共享!

    SMB(Server Messages Block,信息服务块). 由于NFS(网络文件系统)可以很好的完成Linux与Linux之间的数据共享,因而 Samba较多的用在了Linux与windows ...

  7. 整理常用的 vim 命令

    vim 是一款功能强大的文本编辑器,它是Linux下常用的编辑器之一,对于熟练掌握了 vim 的人来说,用它编辑文件,方便又快捷,能极大的提高工作效率 vim 功能强大,对应的命令也非常的多,对于初学 ...

  8. P4428-[BJOI2018]二进制【树状数组,set】

    正题 题目链接:https://www.luogu.com.cn/problem/P4428 题目大意 长度为\(n\)的\(0/1\)串要求支持 修改一个位置 求区间\([l,r]\)有多少个子区间 ...

  9. 测试工程需要明白的Monkey测试

    App稳定性测试 稳定性测试就是指软件长时间的持续运行,系统版本是否稳定,是否能否持续的为用户提供服务. 指标: 异常的次数 异常的频率 App的稳定性测试如何实施? 首选Monkey Monkey是 ...

  10. 洛谷3721 HNOI2017单旋(LCT+set+思维)

    这题难道不是spaly裸题吗? 言归正传QWQ 一看到这个题目,其实第一反应是很懵X的 从来没有见过类似的题目啊,什么\(spaly\),单旋.QWQ很懵逼啊 不过,我们可以注意到这么一件事情,就是我 ...