检查程序是否是PE文件
检查程序是否是PE文件
说明:
判断一个文件是否是PE文件。本例主要判断DOS头,NT头。利用两种方式进行判断,一种是WIN32的函数;另一种是MFC的方法。但是思路是一致的。
PE结构适用于windows系统的程序,即.exe。
PE(Portable Execute)文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任何扩展名。那Windows是怎么区分可执行文件和非可执行文件的呢?我们调用LoadLibrary传递了一个文件名,系统是如何判断这个文件是一个合法的动态库呢?这就涉及到PE文件结构了。
PE文件的结构一般来说如下图所示:从起始位置开始依次是DOS头,NT头,节表以及具体的节。
概念:
(1)DOS头是用来兼容MS-DOS操作系统的,目的是当这个文件在MS-DOS上运行时提示一段文字,大部分情况下是:This program cannot be run in DOS mode.还有一个目的,就是指明NT头在文件中的位置。(用记事本打开任何一个镜像文件,其头2个字节必为字符串“MZ”,这是Mark Zbikowski的姓名缩写,他是最初的MS-DOS设计者之一。)
(2)NT头包含windows PE文件的主要信息,其中包括一个‘PE’字样的签名,PE文件头(IMAGE_FILE_HEADER)和PE可选头(IMAGE_OPTIONAL_HEADER32),头部的详细结构以及其具体意义在PE文件头文章中详细描述。
(3)节表:是PE文件后续节的描述,windows根据节表的描述加载每个节。
(4)节:每个节实际上是一个容器,可以包含代码、数据等等,每个节可以有独立的内存权限,比如代码节默认有读/执行权限,节的名字和数量可以自己定义,未必是上图中的三个。
对于一个PE文件来说,最开始的位置就是一个DOS程序。DOS程序包含一个DOS头和一个DOS程序体。
DOS头部是由IMAGE_DOS_HEADER结构体来定义的。
该结构体定义如下:
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;
注意:
(1)该结构体中,有两个字段需要注意,分别是第一个字段 e_magic,和最后一个字段 e_lfanew字段。
(2)e_magic 字段是一个DOS可执行文件的标识符,该字段占用两个字节,该位置保存着的字符是“MZ”。
(3)e_lfanew字段是文件的新头部,即到了NT头。或称为偏移量。
步骤:
1.在对话框中,放置好如下图的控件。
2.在打开按钮中,编写文件对话框,选择要判断的程序。
CFileDialog filedlg(TRUE);
if (filedlg.DoModal())
{
strPath=filedlg.GetPathName();
SetDlgItemText(IDC_EDT_PATH, strPath);
}
3.打开文件、读取文件内容
WIN32版:
打开文件
HANDLE hFile = CreateFile(strPath, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
读取文件内容,dosHeader可获取DOS头,dwReadSize获取DOS头大小
::ReadFile(hFile, &dosHeader, sizeof(IMAGE_DOS_HEADER), & &dwReadSize,, NULL);
FMC版:
dwSize = file.Read(&dosHeader, sizeof(IMAGE_DOS_HEADER));
同理,osHeader可获取DOS头,dwSize获取DOS头大小
4.此时已经取出DOS头的内容,判断与系统规定的DOS头是否一致。
dosHeader.e_magic != IMAGE_DOS_SIGNATURE;
IMAGE_DOS_SIGNATURE 宏定义。实质是“MZ”
5.移动文件内部的指针,将指针移动到NT头。
IMAGE_NT_HEADERS32 ntHeader;
SetFilePointer(hFile, dosHeader.e_lfanew, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
注意:IMAGE_NT_HEADERS32 是32位系统。若64位系统用IMAGE_NT_HEADERS64
6.同理,取出NT头的内容和大小,再进行判断。
::ReadFile(hFile, &ntHeader, sizeof(IMAGE_NT_HEADERS32), &dwReadSize, NULL);
NT头判断
ntHeader.Signature != IMAGE_NT_SIGNATURE
7.最后,若DOS和NT头都与系统一致,属于PE结构;否则不属于。
打开按钮的源代码: void CReadFilePEDlg::OnBnClickedBtnOpen() { // TODO: 在此添加控件通知处理程序代码 CFileDialog filedlg(TRUE); if (filedlg.DoModal()) { strPath=filedlg.GetPathName(); SetDlgItemText(IDC_EDT_PATH, strPath); } }
WIN32检查PE结构的源代码: void CReadFilePEDlg::OnBnClickedBtnPe() { // TODO: 在此添加控件通知处理程序代码 //MessageBox(L"ddd"); CString strFlat; if (strPath.IsEmpty()) { OnBnClickedBtnOpen(); } HANDLE hFile = CreateFile(strPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (!hFile) { MessageBox(L"打开文件失败!"); return ; } else { //判断dos部分是否一致 IMAGE_DOS_HEADER dosHeader; DWORD dwReadSize; ::ReadFile(hFile, &dosHeader, sizeof(IMAGE_DOS_HEADER), &dwReadSize, NULL); if (dwReadSize != sizeof(IMAGE_DOS_HEADER)) { MessageBox(L"DOS的长度不一致!"); return; } //e_magic是DOS可执行文件的标识符,存放"MZ", if (dosHeader.e_magic != IMAGE_DOS_SIGNATURE) { MessageBox(L"DOS的标识符不一致!不是PE结构"); return; } //文件移位都NT结构,再判断NT部分是否一致 IMAGE_NT_HEADERS32 ntHeader; if (SetFilePointer(hFile, dosHeader.e_lfanew, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { MessageBox(L"无法移动呢文件内部指针!"); return; } ::ReadFile(hFile, &ntHeader, sizeof(IMAGE_NT_HEADERS32), &dwReadSize, NULL); if (dwReadSize != sizeof(IMAGE_NT_HEADERS32)) { MessageBox(L"NT的长度不一致!"); return; } if (ntHeader.Signature != IMAGE_NT_SIGNATURE) { MessageBox(L"NT的标识符不一致!不是PE结构"); return; } strFlat = L"属于PE结构"; } ::CloseHandle(hFile); MessageBox(strFlat); }
MFC检查PE结构的源代码: void CReadFilePEDlg::OnBnClickedBtnPe2() { // TODO: 在此添加控件通知处理程序代码 CString strFlat; if (strPath.IsEmpty()) { OnBnClickedBtnOpen(); } CFile file; if (!file.Open(strPath, CFile::modeRead)) { MessageBox(L"打开文件失败!"); } else { IMAGE_DOS_HEADER dosHeader; DWORD dwSize; dwSize = file.Read(&dosHeader, sizeof(IMAGE_DOS_HEADER)); if (dwSize != sizeof(IMAGE_DOS_HEADER)) { MessageBox(L"DOS的长度不一致!"); return; } if (dosHeader.e_magic != IMAGE_DOS_SIGNATURE) { MessageBox(L"DOS的标识符不一致!不是PE结构"); return; } IMAGE_NT_HEADERS32 ntHeader; if (!file.Seek(dosHeader.e_lfanew, CFile::begin)) { MessageBox(L"无法移动呢文件内部指针!"); return; } dwSize = file.Read(&ntHeader, sizeof(IMAGE_NT_HEADERS32)); if (dwSize != sizeof(IMAGE_NT_HEADERS32)) { MessageBox(L"NT的长度不一致!"); return; } if (ntHeader.Signature != IMAGE_NT_SIGNATURE) { MessageBox(L"NT的标识符不一致!不是PE结构"); return; } strFlat = L"属于PE结构!"; } MessageBox(strFlat); }