在本文中, 我并不打算大讲特讲PE文件的构成是什么,每个字段是什么意思, 这些资料可以说在网上是浩瀚如海,到处都是的, 用google一搜, 打开一看, 基本讲解的都是大同小异。 由于pe文件的结构比较庞大, 结构中套结构, 有的结构多达30多个字段, 光看这些字段都让人够晕的, 在加上有的字段是一个32位的DWORD值, 而每一位都有其特殊的含义,这样, 光把每个字段的含义看一遍过来, 估计也得2个小时。 而实际上, 这里面很多字段, 我们都不需要去了解, 也不需要去关注, 掌握那些关键字段就行了,所以如果大家有那个字段不懂的,可以上网搜一下。这次讲一下是否PE格式文件的判断。
(这个图我是从鱼C论坛转的)。
首先要明确PE文件格式的特征是什么,这样我们才能根据这个特征来判断是否是PE文件。PE文件的格式如上图所示,那么我们怎么判断呢?PE文件有DOS文件头和PE文件头,在DOS文件头前两个字节是“MZ",PE文件头的前4个字节是“PE”,所以我们可以根据这个特征来进行判断,我们怎么找到“MZ”和“PE”位置呢,PIMAGE_DOS_HEADER的e_magic字段表示“MZ”,PIMAGE_DOS_HEADER的e_lfanew表示PE头相对于文件的偏移。(我用的VS2012编译环境)
代码如下:
/************************************************************************/
/*
功能:判断是否是PE文件。
参数:指向存放文件数据内存首地址
返回:TRUE ;是PE文件
FALSE:不是PE文件
*/
/************************************************************************/
BOOL IsPEFile(LPVOID lpFileBuf)
{
//获取DOS头并判断"MZ"标志
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpFileBuf;
//e_magic exe标志 "MZ"
if (IMAGE_DOS_SIGNATURE != pDos->e_magic)
return FALSE;
//获取PE头,并判断"PE\0\0"标志
//e_lfanew PE头相对于文件的偏移地址
size_t stPEHeadAddr = (size_t)lpFileBuf + pDos->e_lfanew;
PIMAGE_NT_HEADERS32 pNT = (PIMAGE_NT_HEADERS32)stPEHeadAddr;
if (IMAGE_NT_SIGNATURE != pNT->Signature)
{
return FALSE;
}
return TRUE;
}
int _tmain(int argc, _TCHAR* argv[])
{
//文件句柄
HANDLE hFile = INVALID_HANDLE_VALUE;
//文件路径
LPCTSTR strInPath = _T("G:\\15pb培训课后作业\\01 VMView\\Debug\\VMView.exe");
DWORD dwSize = 0;
PVOID lpFillBuf = NULL;
STARTUPINFO si = {0};
GetStartupInfo(&si);
PROCESS_INFORMATION pi = {0};
if (!CreateProcess( /* 创建调试线程 */
strInPath, //可执行模块路径
NULL, //命令行
NULL, //安全描述符
NULL, //线程属性是否可继承
FALSE, //否从调用进程处继承了句柄
DEBUG_ONLY_THIS_PROCESS | CREATE_NEW_CONSOLE, //以“只”调试的方式启动
NULL, //新进程的环境块
NULL, //新进程的当前工作路径(当前目录)
&si, //指定进程的主窗口特性
&pi)) //接收新进程的识别信息
{
printf("CreateProcess Failed!\n");
}
//获取文件句柄
if (INVALID_HANDLE_VALUE == (hFile = CreateFile(strInPath, //文件路径
GENERIC_READ, //访问权限
FILE_SHARE_READ, //共享模式
NULL, //安全描述符
OPEN_EXISTING, //创建标志
FILE_ATTRIBUTE_NORMAL, //文件标志和属性
NULL))) //临时文件句柄
{
return 0;
}
//获取文件大小
if (INVALID_FILE_SIZE == (dwSize = GetFileSize(hFile,NULL)))
{
CloseHandle(hFile);
return 0;
}
//开辟一块内存用于存放文件数据
if ( NULL == (lpFillBuf =VirtualAlloc(NULL,dwSize,MEM_COMMIT,PAGE_EXECUTE_READWRITE)))
{
CloseHandle(hFile);
return 0;
}
//把文件数据读取到内存中
DWORD dwRet ;
if (!ReadFile(hFile,lpFillBuf,dwSize,&dwRet,NULL))
{
CloseHandle(hFile);
VirtualFree(lpFillBuf,0,MEM_RELEASE);
return 0;
}
if (IsPEFile(lpFillBuf))
{
MessageBox(NULL,_T("该文件是PE文件"),_T("PE文件格式判断"),MB_OK);
}
else
{
MessageBox(NULL,_T("该文件不是PE文件"),_T("PE文件格式判断"),MB_OK);
}
VirtualFree(lpFillBuf,0,MEM_RELEASE);
CloseHandle(hFile);
_tsystem(_T("pause"));
return 0;
}
通过以上就可以进行判断了。
下面是一个可执行文件的二进制截图,我把IMAGE_DOS_HEADER.e_magic,IMAGE_DOS_HEADER.e_lfanew和IMAGE_NT_HEADER.Signature标记出来了。