综述:程序员
首先说明我也只是PE文件的初学者,我所写的都是本身的学习记录。前3节学习了PE的一些结构,其中包括DOS头和PE头部分。老是这样去学习这些机构和理论的分析我想你们和我同样毫无兴趣,提不起精神。因此我尝试着在本身对PE了解的基础上用C++写一个小程序分析PE结构文件。我所接触的教程都是Win32汇编去实现一些小工具对PE文件进行分析。我只是能看懂汇编,让我去写那没什么劲,因此我仍是用C++去实现。我只是对C比较了解,至于面向对象的知识是在学习C#时候接触的。因此这里的案例的C++代码很大的可能仍是继承了C的风格。为何是C++,主要是MFC写界面比Windows API写界面方便多了。固然若是你们有兴趣,能够留言我写一个C#版本的。固然这一节也不可能实现全部的功能,暂时展现一些小功能。废话很少说了,上代码:编程
C++代码实现:小程序
首先实现这些如图程序的功能,其中包括识别是不是PE文件,其次是给出,PE文件在磁盘中的对齐尺寸和内存中的对齐尺寸,另外一个就是实现知道程序的装载入口地址,都是以16进制的方式表现出来的。app
//选择文件按钮 void CPEinfoDlg::OnBnClickedBtnSelectfile() { CFileDialog dilog(TRUE); dilog.m_ofn.lpstrTitle=_T("请选择PE文件"); if(IDOK==dilog.DoModal()){ CString fileName= dilog.GetFileName(); CString filePath= dilog.GetPathName(); //给路径文本框赋值 this->GetDlgItem(IDC_EDIT1)->SetWindowTextW(filePath); HANDLE fileHandle= CreateFile(filePath,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); if(INVALID_HANDLE_VALUE!=fileHandle){ HANDLE mapHandle= CreateFileMapping(fileHandle,NULL,PAGE_READONLY,0,0,NULL); if(mapHandle==NULL){ AfxMessageBox(L"打开文件映射对象失败!"); } else { strContet= MapViewOfFile(mapHandle,FILE_MAP_READ,0,0,0); LPBYTE lpBaseAddress = (LPBYTE)strContet; PIMAGE_DOS_HEADER dosHead=(PIMAGE_DOS_HEADER)lpBaseAddress; PIMAGE_NT_HEADERS ntHead=(PIMAGE_NT_HEADERS)(lpBaseAddress+dosHead->e_lfanew); //根据DOS头和PE头判断是不是PE文件 if(dosHead->e_magic==IMAGE_DOS_SIGNATURE&&ntHead->Signature==IMAGE_NT_SIGNATURE){ //AfxMessageBox(L"说明是正常的PE!"); CString show=L"0x"; wchar_t r[10]=L""; int h=ntHead->OptionalHeader.FileAlignment; _itow_s(h,r,16); show+=r; this->GetDlgItem(IDC_STATIC_FileA)->SetWindowTextW(show); //内存对齐尺寸 int ss= ntHead->OptionalHeader.SectionAlignment; _itow_s(ss,r,16); show=L"0x"; show+=r; this->GetDlgItem(IDC_STATIC_SESSIONSIZE)->SetWindowTextW(show); //入口地址 int ept= ntHead->OptionalHeader.AddressOfEntryPoint; _itow_s(ept,r,16); show=L"0x"; show+=r; this->GetDlgItem(IDC_STATIC_BaseEntry)->SetWindowTextW(show); } else { AfxMessageBox(L"请打开PE格式文件!"); } //撤销映射 UnmapViewOfFile(strContet); //关闭文件映射对象句柄 CloseHandle(mapHandle); } //关闭文件对象 CloseHandle(fileHandle); } else { AfxMessageBox(L"打开文件句柄失败!"); } } delete dilog; // TODO: 在此添加控件通知处理程序代码 }
我就主要上主要代码,固然在这里咱们最起码得知道MFC框架的简单开发。框架
这些小功能的原理就是读文件,不过我这里用的读文件的方式是文件映射的方式,直接将文件映射到内存中。工具
1.首先是打开文件,返回一个文件内核对象(什么是内核对象?这个我就很少作解释了,要想了解请参照《Windows 核心编程》。这绝对是Windows开发的一本葵花宝典),学习
HANDLE fileHandle= CreateFile(filePath,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
2.而后建立一个文件映射对象this
HANDLE mapHandle= CreateFileMapping(fileHandle,NULL,PAGE_READONLY,0,0,NULL);
固然是以读方式打开。spa
3.而后就是文件内存视图映射3d
strContet= MapViewOfFile(mapHandle,FILE_MAP_READ,0,0,0);
其中strContet是LPVOID类型,这个指针就指向了PE文件被映射到内存中的起始地址
4.找DOS头和PE头
PIMAGE_DOS_HEADER dosHead=(PIMAGE_DOS_HEADER)lpBaseAddress;
PIMAGE_NT_HEADERS ntHead=(PIMAGE_NT_HEADERS)(lpBaseAddress+dosHead->e_lfanew);
PIMAGE_DOS_HEADER 和PIMAGE_NT_HEADERS结构体就是广义上的DOS头和PE头结构。这两个结构体在WinNT.h头文件中能找到。
5.判断是不是PE文件
if(dosHead->e_magic==IMAGE_DOS_SIGNATURE&&ntHead->Signature==IMAGE_NT_SIGNATURE)
就和咱们在前面所说的dosHead->e_magic是DOS头的标志,里面是MZ因此DOS头又称做MZ头,ntHead->Signature就是PE头的标志,内容是ASIIC码PE00。我用UE打开:
在WinNT.h头文件中被定义为IMAGE_DOS_SIGNATURE和IMAGE_NT_SIGNATURE,顾名思义,DOS头标志和PE头标志。这样即便你随便将一个文件修改为.exe后缀或者.dll后缀的PE格式文件依然不经过验证。
经过这个动态图片咱们很容易看到我想打开一个.txt文本这是不行的。我如今去把他改为exe再打开:
能够看见修改txt后缀为.exe是不行的。
6.显示内存对齐尺寸程序入口地址等信息
int h=ntHead->OptionalHeader.FileAlignment; _itow_s(h,r,16); show+=r; this->GetDlgItem(IDC_STATIC_FileA)->SetWindowTextW(show); //内存对齐尺寸 int ss= ntHead->OptionalHeader.SectionAlignment; _itow_s(ss,r,16); show=L"0x"; show+=r; this->GetDlgItem(IDC_STATIC_SESSIONSIZE)->SetWindowTextW(show); //入口地址 int ept= ntHead->OptionalHeader.AddressOfEntryPoint; _itow_s(ept,r,16); show=L"0x"; show+=r; this->GetDlgItem(IDC_STATIC_BaseEntry)->SetWindowTextW(show);
其中没什么复杂度,只是将结构成员数据处理显示出来。这里也验证了。通常PE文件在磁盘中的对齐粒度是200H在内存中的对齐粒度是1000H也就是4K,一分页大小。
这一节就记到这里,后续的功能等咱们学习到的时候再去添加。