当前位置: 移动技术网 > 网络运营>安全>加解密 > 分析PE文件资源

分析PE文件资源

2018年03月24日  | 移动技术网网络运营  | 我要评论
文/图 df PE文件,全称Portable Executable文件,是Windows系统可执行文件采用的普遍格式,像我们平时接触的EXE、DLL、OCX,甚至SYS文件都属
文/图 df

PE文件,全称Portable Executable文件,是Windows可执行文件采用的普遍格式,像我们平时接触的EXE、DLL、OCX,甚至SYS文件都属于PE文件的范畴。为了在可执行文件中方便的引用其它类型资源内容,PE文件有一个独立的资源段,将程序执行所需要的全部资源文件链接到PE文件内部方便使用。今天我们就来研究一下PE文件资源段的内容与格式。
内存映射文件
俗话说,欲善其功,先利其器。为了分析PE文件结构,我们必须将PE文件映射入进程的内存空间,这里隆重推荐Windows 2000及其以后版本Windows支持的新特性,内存映射文件,这种机制可以将一个文件完全或部分映射入当前进程的地址空间,由系统托管全部的磁盘IO操作,自动实现诸如缓冲区管理,读写控制等相关功能,效率极高。
这里提供本人基于该项技术写的一个文件读取类,该类在构造函数中通过引用返回特定文件的内存映射首地址指针和大小,然后程序就可以像使用本地内存一样使用该指针了。类的代码如下。

//声明
class CMapFile
{
public:
CMapFile(LPCTSTR pPath,bool bWrite,PVOID &pMap,DWORD &dwFileSize);
~CMapFile(void);
private :
HANDLE hFileHandle;
HANDLE hFileMapHandle;
PVOID &pImageView;
DWORD &dwSize;
};
//构造函数
CMapFile::CMapFile(LPCTSTR pPath,bool bWrite,PVOID &pMap,DWORD &dwFileSize)
:pImageView(pMap)
,dwSize(dwFileSize)
{
this->hFileHandle=INVALID_HANDLE_VALUE;
this->hFileMapHandle=INVALID_HANDLE_VALUE;
this->pImageView =NULL;
this->dwSize =0;
try
{
if(bWrite)
{
this->hFileHandle = CreateFile(pPath, GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL , NULL);
}
else
{
this->hFileHandle = CreateFile(pPath,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
}
if(this->hFileHandle==INVALID_HANDLE_VALUE)
{
throw (0);
}
this->dwSize=GetFileSize(this->hFileHandle,NULL);
if(this->dwSize==0)
{
throw (1);
}
if(bWrite)
{
this->hFileMapHandle=CreateFileMapping(this->hFileHandle, NULL,PAGE_READWRITE,0,0,NULL);
}
else
{
this->hFileMapHandle=CreateFileMapping(this->hFileHandle,NULL, PAGE_READONLY,0,0,NULL);
}
if(this->hFileMapHandle==INVALID_HANDLE_VALUE)
{
throw (2);
}
CloseHandle(this->hFileHandle);
this->hFileHandle= INVALID_HANDLE_VALUE;
if(bWrite)
{
this->pImageView=MapViewOfFile(this->hFileMapHandle, FILE_MAP_WRITE|FILE_MAP_READ,0,0,0);
}
else
{
this->pImageView=MapViewOfFile(this->hFileMapHandle, FILE_MAP_READ,0,0,0);
}
if(this->pImageView==NULL)
{
throw (4);
}
}
catch(...)
{
this->pImageView=NULL;
}

}
//析构函数
CMapFile::~CMapFile(void)
{
try
{
if(this->pImageView!=NULL)
{
UnmapViewOfFile(this->pImageView);
}
if(this->hFileMapHandle!=INVALID_HANDLE_VALUE)
{
CloseHandle(this->hFileMapHandle);
}
if(this->hFileHandle!=INVALID_HANDLE_VALUE)
{
CloseHandle(this->hFileHandle);
}
}
catch(...)
{
}
}

在研究PE文件资源段之前,我们必须了解PE文件头结构。下面我们来分析PE文件头的相关格式。PE文件头总体结构如图1所示,可见PE文件的格式是相对复杂的。我将在下文依次分析各个部分的含义。

图1 PE文件结构

MS-DOS头部
第一个结构是MS-DOS 头,这个是为了兼容旧的DOS程序而设计的,如果一个Win32程序在DOS模式下运行(所谓的DOS模式是指纯DOS环境,而不是Windows控制台),DOS头部会把执行定位到MS-DOS实模式残余程序,该程序会调用int 21中断输出一个字符串“This program cannot be run in DOS mode”,然后直接退出。MS-DOS头部在“winnt.h”里面有定义。
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;

第一成员变量e_magic被称为魔术数字,它被用于表示一个MS-DOS兼容的文件类型。所有MS-DOS兼容的可执行文件都将这个值设为0x5A4D,表示ASCII字符MZ。MS-DOS头部之所以有的时候被称为MZ头部,就是这个缘故。
至于其余的成员变量,基本上都是为了DOS下实模式设计,如今已经没有什么实际作用,除了最后一个成员变量e_lfanew。这个成员变量用来表示PE头部在这个PE文件中的偏移量。通过如下代码可以获得PE头部地址:

BYTE *pFileImage = (BYTE*)pPeImage;
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileImage;
PIMAGE_FILE_HEADER pFileHeader =(PIMAGE_FILE_HEADER)(pFileImage+pDosHeader->e_lfanew+4);

注意,在计算偏移地址的时候除了偏移量pDosHeader->e_lfanew,还有一个DWORD的偏移,这个DWORD 是存储PE文件标志的,值为0x4550,对应ASCII字符“PE”。
PE头部
下面我们来介绍PE头部的内容。PE头部在“winnt.h”中定义如下。

typedef struct _IMAGE_FILE_HEADER {
WORDMachine;
WORDNumberOfSections;
DWORD   TimeDateStamp;
DWORD   PointerToSymbolTable;
DWORD   NumberOfSymbols;
WORDSizeOfOptionalHeader;
WORDCharacteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

这个结构比较简单,Machine表示这个可执行文件被构建的目标机器种类,本程序获得的Machine值是0x14c,代表i386;NumberOfSection表示本PE文件具有多少个有多少个段头部和多少个段实体,每一个段头部和段实体都在文件中连续地排列着,所以要决定段头部和段实体在哪里结束的话,段的数目是必需的;TimeDataStamp是一个时间戳变量;PointerToSymbolTable和NumberOfSymbols确定了符号表的位置和大小。SizeOfOptionalHeader表示选项头部的大小,选项头部就在PE文件头部后面线性排列,这个结构容后介绍,但是大家不要被名称迷惑,选项头部是对PE文件执行至关重要的结构,并非“Optional”。Characteristics表示文件的一些特征,比如对于一个可执行文件而言,分离调试文件是如何操作的。
选项头
选项头在“winnt.h”中的定义如下。

typedef struct _IMAGE_OPTIONAL_HEADER {
// Standard fields.
WORDMagic;
BYTEMajorLinkerVersion;
BYTEMinorLinkerVersion;
DWORD   SizeOfCode;
DWORD   SizeOfInitializedData;
DWORD   SizeOfUninitializedData;
DWORD   AddressOfEntryPoint;
DWORD   BaseOfCode;
DWORD   BaseOfData;
// NT additional fields.
DWORD   ImageBase;
DWORD   SectionAlignment;
DWORD   FileAlignment;
WORDMajorOperatingSystemVersion;
WORDMinorOperatingSystemVersion;
WORDMajorImageVersion;
WORDMinorImageVersion;
WORDMajorSubsystemVersion;
WORDMinorSubsystemVersion;
DWORD   Win32VersionValue;
DWORD   SizeOfImage;
DWORD   SizeOfHeaders;
DWORD   CheckSum;
WORDSubsystem;
WORDDllCharacteristics;
DWORD   SizeOfStackReserve;
DWORD   SizeOfStackCommit;
DWORD   SizeOfHeapReserve;
DWORD   SizeOfHeapCommit;
DWORD   LoaderFlags;
DWORD   NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IM

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网