win32下PE文件分析之DOS头
(一).win32下的PE文件:
公司主营业务:成都网站设计、成都做网站、移动网站开发等业务。帮助企业客户真正实现互联网宣传,提高企业的竞争能力。创新互联建站是一支青春激扬、勤奋敬业、活力青春激扬、勤奋敬业、活力澎湃、和谐高效的团队。公司秉承以“开放、自由、严谨、自律”为核心的企业文化,感谢他们对我们的高要求,感谢他们从不同领域给我们带来的挑战,让我们激情的团队有机会用头脑与智慧不断的给客户带来惊喜。创新互联建站推出龙口免费做网站回馈大家。
PE是Portable Execute的缩写,是可移植可执行的意思,只要文件的数据结构遵循PE结构,就属于PE文件,windows中常见的PE文件有
*.sys驱动类文件
*.dll动态链接库文件
*.exe可执行文件
*.ocx对象类别扩充组建
*.obj目标文件等.
同样,linux中使用的是ELF格式,和windows的PE格式有一定的差别,如:
可重定位文件*.o
可执行文件如/bin/ls等
共享目标文件*.so
核心转储文件core
都遵循ELF数据结构. unix从system v4开始也使用ELF了,而他们的始祖都是unix system v3的中COFF.如下图:
(二).win32中的PE文件二进制数据结构:
二进制数据结构如下图,看起来就比较复杂,但是当你亲自动手解析一波,那可能会改变你的世界观,前提是初学者.为了全部显示出来,看不太清,放附件里了.
(三).win32中PE的逻辑图:
一个标准的PE文件由DOS头,stub,NT头(包含PE标识,标准PE头和可选PE头三个成员),节表,节的内容以及一些为了内存对齐而填充的0.
以上就是一个PE文件的大体逻辑图,它里面的内容虽然是二进制,但绝不是随意填充的数据,而是严格遵循一定格式生成的,比如C语言写的一段代码,通过预处理, 汇编, 编译, 链接后生成的一个exe文件(PE文件中的一种),生成过程是由编译器来完成的.
(四).DOS头中的数据结构:
Visual C++ 6.0中winnt头文件中的定义:
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;
(五).C语言实现对win32中notepad.exe的DOS头的简单解析:
该代码中只是输出了DOS头中两个较为有用的数据,第一个和最后一个(e_magic和e_lfanew),代码如下:
Dos_Header_Analyze.cpp:
// Dos_Header_Analyze.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "dos.h" //包含进自己写的dos.h文件 #define filepath "notepad.exe" //指定好notepad.exe的位置,写绝对路径,或放于源代码目录中 int main(int argc, char* argv[]) { void* pbuff = NULL; //方便后面当参数使用 pbuff = ReadFileToBuff(filepath); Output_Dos(pbuff); //解析DOS头 free(pbuff); //释放空间 return 0; }
dos.h:
void* ReadFileToBuff(char* file) //将文件读取到内存 { FILE* fp = fopen(file, "rb"); //以二进制只读形式打开文件 void* buff = NULL; //用来指向申请的内存缓冲区 unsigned long sz = 0; //用来存放文件的大小 if(!fp) { printf("Failed to open file \"%s\"\n", file); exit(-1); } fseek(fp, 0, SEEK_END); //让文件指针fp指向文件的末位位置,用于计算文件的大小 sz = ftell(fp); //获取当前文件指针相对于起始位置的偏移 fseek(fp, 0, SEEK_SET); //让文件指针fp指向文件的起始位置 printf("File \"%s\" size: %ld KB\n", file, sz / 1024); //输出文件大小(单位KB) buff = malloc(sz); //申请一块与文件大小相等的内存,用作文件缓冲区 if(!buff) { printf("Alloc memery failed!\n"); exit(-2) } memset(buff, 0, sz); //置零缓冲区 //将文件中的数据写入文件缓冲区,每次读取128字节,读取sz/128次,正好读取sz字节 if(!fread(buff, 128, sz/128, fp)) { printf("Read file \"%s\" error!\n", file); exit(-3); } fclose(fp); //关闭刚刚打开的文件 return buff; //返回文件缓冲的内存地址 } //输出DOS头的重要信息 void Output_Dos(void* buffer) { void* buf = buffer; //定义一个指向文件缓冲的DOS头的指针 IMAGE_DOS_HEADER* pdos = (IMAGE_DOS_HEADER*)buf; printf("DOS Header:\n"); //MZ标记,用于判断该文件是否为可执行文件(其值与MZ的ascii相对应) printf("Magic Number: %#X\n", pdos->e_magic); //PE标识相对于文件其实位置的偏移 (单位字节) printf("PE Offset: %#X\n", pdos->e_lfanew); }
stdafx.h:
#if !defined(AFX_STDAFX_H__BBCA9272_49A3_4E1E_9262_9F0211C5BA05__INCLUDED_) #define AFX_STDAFX_H__BBCA9272_49A3_4E1E_9262_9F0211C5BA05__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers //主要是把相应的头文件包含进去 #include#include #include #include
执行结果如下图:
如果有空会继续更新.
名称栏目:win32下PE文件分析之DOS头
链接分享:http://ybzwz.com/article/pdsops.html