PE文件學習系列筆記四-C++實現PE文件的分析

合肥程序員羣:49313181。    合肥實名程序員羣:128131462 (不肯透露姓名和信息者勿加入)
Q  Q:408365330     E-Mail:egojit@qq.com

綜述:程序員

首先說明我也只是PE文件的初學者,我所寫的都是本身的學習記錄。前3節學習了PE的一些結構,其中包括DOS頭和PE頭部分。老是這樣去學習這些機構和理論的分析我想你們和我同樣毫無興趣,提不起精神。因此我嘗試着在本身對PE瞭解的基礎上用C++寫一個小程序分析PE結構文件。我所接觸的教程都是Win32彙編去實現一些小工具對PE文件進行分析。我只是能看懂彙編,讓我去寫那沒什麼勁,因此我仍是用C++去實現。我只是對C比較瞭解,至於面向對象的知識是在學習C#時候接觸的。因此這裏的案例的C++代碼很大的可能仍是繼承了C的風格。爲何是C++,主要是MFC寫界面比Windows API寫界面方便多了。固然若是你們有興趣,能夠留言我寫一個C#版本的。固然這一節也不可能實現全部的功能,暫時展現一些小功能。廢話很少說了,上代碼:編程

C++代碼實現:小程序

PEc_1

首先實現這些如圖程序的功能,其中包括識別是不是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打開:

PE標誌

在WinNT.h頭文件中被定義爲IMAGE_DOS_SIGNATURE和IMAGE_NT_SIGNATURE,顧名思義,DOS頭標誌和PE頭標誌。這樣即便你隨便將一個文件修改爲.exe後綴或者.dll後綴的PE格式文件依然不經過驗證。

PEyz

經過這個動態圖片咱們很容易看到我想打開一個.txt文本這是不行的。我如今去把他改爲exe再打開:

peyz4

能夠看見修改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,一分頁大小。

這一節就記到這裏,後續的功能等咱們學習到的時候再去添加。

版權:歸博客園和Egojit全部,轉載請標明出處。
相關文章
相關標籤/搜索