綜述:程序員
首先說明我也只是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,一分頁大小。
這一節就記到這裏,後續的功能等咱們學習到的時候再去添加。