自己實(shí)現(xiàn)讀取可執(zhí)行文件 DOS 頭和 NT 頭

看了 《逆向工程核心原理》以后決定自己實(shí)現(xiàn)一個(gè) PE 頭讀取的代碼猖败,將 PE 的 DOS 頭和 NT 頭放到一個(gè)結(jié)構(gòu)體的內(nèi)存布局里。

一、結(jié)構(gòu)體定義

首先定義一個(gè)簡(jiǎn)單的 HEADERS,將內(nèi)存布局做一個(gè)整體上的劃分,主要?jiǎng)澐殖?dos_header 頭遏片、 dos_stub 代碼段和 nt 頭:

typedef struct _PE_HEADERS {
    CUSTOM_IMAGE_DOS_HEADER dos_header;
    CUSTOM_IMAGE_DOS_STUB dos_stub;
    CUSTOM_IMAGE_NT_HEADERS nt_header;
}PE_HEADERS, * PPE_HEADERS;

1、DOS 頭

DOS 頭里面比較關(guān)鍵的兩個(gè)段分別是 e_magic 和 e_lfanew撮竿,這個(gè) e_magic 是 DOS 可執(zhí)行文件的設(shè)計(jì)者的名字縮寫(xiě)吮便,而 e_lfanew 是 NT 頭的偏移,結(jié)構(gòu)體定義如下:

typedef struct _CUSTOM_IMAGE_DOS_HEADER {      // DOS 頭幢踏,用于兼容 DOS 文件
    WORD e_magic;                       // 設(shè)計(jì) DOS 可執(zhí)行文件的微軟開(kāi)發(fā)人員的名字縮寫(xiě) "MZ" 
    WORD e_cblp;                        //
    WORD e_cp;                          //
    WORD e_crlc;                        //
    WORD e_cparhdr;                     //
    WORD e_minalloc;                    //
    WORD e_maxalloc;                    //
    WORD e_ss;                          //
    WORD e_sp;                          //
    WORD e_csum;                        //
    WORD e_ip;                          //
    WORD e_cs;                          //
    WORD e_lfarlc;                      //
    WORD e_ovno;                        //
    WORD e_res[4];                      //
    WORD e_oemid;                       //
    WORD oeminfo;                       //
    WORD e_res2[10];                    //
    LONG e_lfanew;                      // NT 頭的偏移
} CUSTOM_IMAGE_DOS_HEADER;

e_magic 作為一個(gè)魔數(shù)髓需,在實(shí)現(xiàn)讀取的過(guò)程中有一個(gè)理解就是它用來(lái)指明讀入數(shù)據(jù)的時(shí)候是否讀到了正確的偏移,之后的 NT Header 也會(huì)有對(duì)應(yīng)的魔數(shù)房蝉,如果對(duì)應(yīng)結(jié)構(gòu)體的對(duì)應(yīng)魔數(shù)是正確的僚匆,就意味著我們的讀取邏輯讀到的這個(gè)地址偏移是正確的。

舉個(gè)例子搭幻,假如內(nèi)存中有一塊 20Bytes 的數(shù)據(jù)咧擂,我們要把這個(gè)數(shù)據(jù)復(fù)制到自己的結(jié)構(gòu)體中,這塊數(shù)據(jù)的第一個(gè) BYTE 長(zhǎng)度的數(shù)據(jù)值是 0x20檀蹋,如果我們復(fù)制之后結(jié)構(gòu)體的第一個(gè) BYTE 不是 0x20松申,那很大可能我們復(fù)制的地址值之類(lèi)的偏移寫(xiě)錯(cuò)了~

2、DOS Stub

這塊數(shù)據(jù)是用來(lái)兼容 DOS 運(yùn)行的,在 DOS 中運(yùn)行這個(gè)文件將會(huì)跑 DOS Stub 下的代碼贸桶,我定義的結(jié)構(gòu)體如下:

typedef struct _CUSTOM_IMAGE_DOS_STUB {        // DOS 存根舅逸,大小不固定,目前看來(lái)是 dos 下執(zhí)行的代碼
    LPBYTE e_dos_code;                  // 或許是 dos 下的執(zhí)行代碼
    LONG   e_dos_code_size;             // *非 PE 內(nèi)數(shù)據(jù)皇筛,用來(lái)記錄 dos 代碼的尺寸
} CUSTOM_IMAGE_DOS_STUB;

其中 e_dos_code 指向?qū)?yīng) dos_code 的首地址堡赔。

3、NT 頭

DOS stub 之后跟著的是 NT 頭设联,NT 頭的數(shù)據(jù)比較多,同樣灼捂,取重要部分加注釋?zhuān)?/p>

typedef struct _CUSTOM_IMAGE_NT_HEADERS {              // NT 頭數(shù)據(jù)
    DWORD                          nt_signature;       // NT 簽名結(jié)構(gòu)體
    CUSTOM_IMAGE_FILE_HEADER       nt_file_header;     // NT 文件頭
    CUSTOM_IMAGE_OPTIONAL_HEADER32 nt_optional_header; // NT 可選頭
} CUSTOM_IMAGE_NT_HEADERS;

1) NT 頭簽名

這個(gè)數(shù)同樣是一個(gè)魔數(shù)离例,值為 10B 或者 20B。

2) NT 文件頭

typedef struct _CUSTOM_IMAGE_FILE_HEADER {             // 文件頭數(shù)據(jù)
    WORD nt_machine;                            // cpu 架構(gòu) Machine 碼數(shù)據(jù)悉稠,用以標(biāo)識(shí)比如 INTEL 386 架構(gòu)等等
    WORD nt_number_of_sections;                 // 指出文件中存在的節(jié)區(qū)數(shù)量宫蛆,這里的節(jié)區(qū)指的是代碼、數(shù)據(jù)或者資源等數(shù)據(jù)分區(qū)
    DWORD nt_time_date_stamp;                    // 文件創(chuàng)建時(shí)間的猛,但有些開(kāi)發(fā)工具提供了設(shè)置的手段耀盗,所以不一定準(zhǔn)
    DWORD nt_pointer_to_symbol_table;            // 符號(hào)表的指針
    DWORD nt_number_of_symbols;                  // 符號(hào)的數(shù)量
    WORD nt_size_of_optional_header;            // 用來(lái)指出可選頭的長(zhǎng)度
    WORD nt_characteristics;                    // 用于標(biāo)識(shí)文件的屬性,文件是否是可運(yùn)行的形態(tài)卦尊、是否為 DLL 文件等等信息叛拷,bit OR 的形式
} CUSTOM_IMAGE_FILE_HEADER;

3)NT 可選頭

typedef struct _CUSTOM_IMAGE_DATA_DIRECTORY {                                            // 該結(jié)構(gòu)體僅僅用于定義一段數(shù)據(jù)的存儲(chǔ)
    DWORD virtual_address;                                                        // 數(shù)據(jù)的虛擬地址
    DWORD size;                                                                   // 數(shù)據(jù)的長(zhǎng)度
} CUSTOM_IMAGE_DATA_DIRECTORY;

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16                                       // 為什么定義在這里,因?yàn)橥獠靠赡芤?
typedef struct _CUSTOM_IMAGE_OPTIONAL_HEADER {                                           // 可選頭內(nèi)容
    WORD                 opt_magic;                                               // 可選頭魔數(shù)岂却,HEADER32 是 10B忿薇,如果是 HEADER64 就是 20B
    BYTE                 opt_major_linker_version;                                // 
    BYTE                 opt_minor_linker_version;                                //
    DWORD                opt_size_of_code;                                        //
    DWORD                opt_size_of_initialized_data;                            //
    DWORD                opt_size_of_uninitialized_data;                          //
    DWORD                opt_address_of_entry_point;                              // 程序最先執(zhí)行的代碼起始地址,程序入口點(diǎn) OEP
    DWORD                opt_base_of_code;                                        //
    DWORD                opt_base_of_data;                                        //
    DWORD                opt_image_base;                                          // 程序裝入虛擬內(nèi)存空間時(shí)優(yōu)先選擇的虛擬內(nèi)存地址躏哩,裝載之后優(yōu)先設(shè)置 EIP 為 ImageBase+AddressOfEntryPoint
    DWORD                opt_section_alignment;                                   // 指定每個(gè)節(jié)區(qū)在內(nèi)存中的最小單位署浩,內(nèi)存中節(jié)區(qū)大小必須是該值的整數(shù)倍
    DWORD                opt_file_alignment;                                      // 指定每個(gè)節(jié)區(qū)在文件中的最小單位,文件中節(jié)區(qū)大小必須是該值的整數(shù)倍
    WORD                 opt_major_operating_system_version;                      // 
    WORD                 opt_minor_operating_system_version;                      //
    WORD                 opt_major_image_version;                                 //
    WORD                 opt_minor_image_version;                                 //
    WORD                 opt_major_subsystem_version;                             //
    WORD                 opt_minor_subsystem_version;                             //
    DWORD                opt_win32_version_value;                                 //
    DWORD                opt_size_of_image;                                       // 指定 PE Image 在虛擬內(nèi)存中所占空間大小扫尺。一般而言筋栋,文件大小與加載到內(nèi)存中的大小是不同的
    DWORD                opt_size_of_headers;                                     // 指出整個(gè) PE 頭的大小,必須是 FileAlignment 的整數(shù)倍
    DWORD                opt_check_sum;                                           //
    WORD                 opt_subsystem;                                           // 值為 1 時(shí)代表系統(tǒng)驅(qū)動(dòng)(.sys)正驻,值為 2 時(shí)代表 GUI 文件窗口應(yīng)用程序(.exe)弊攘,值為 3 時(shí)代表 CUI 文件控制臺(tái)應(yīng)用程序(.exe)
    WORD                 opt_dll_characteristics;                                 //
    DWORD                opt_size_of_stack_reverse;                               //
    DWORD                opt_size_of_stack_commit;                                //
    DWORD                opt_size_of_heap_reverse;                                //
    DWORD                opt_size_of_heap_commit;                                 //
    DWORD                opt_loader_flags;                                        //
    DWORD                opt_number_of_rva_and_sizes;                             // 指定 data_directory 數(shù)組的個(gè)數(shù),雖然指定的常數(shù)是 16,但實(shí)際數(shù)組大小可能不是 16
    CUSTOM_IMAGE_DATA_DIRECTORY opt_data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];    //
} CUSTOM_IMAGE_OPTIONAL_HEADER32;

二拨拓、讀取邏輯

靜態(tài)大小的數(shù)據(jù)我們直接讀取對(duì)應(yīng)內(nèi)存肴颊,動(dòng)態(tài)大小的數(shù)據(jù)則先從數(shù)據(jù)中拿出大小,然后指定大小去讀對(duì)應(yīng)的數(shù)據(jù)渣磷。
為了讓流程清晰婿着,我的實(shí)現(xiàn)還是按照每一個(gè)小塊進(jìn)行復(fù)制,如果是靜態(tài)塊,直接復(fù)制整塊竟宋,不用像我這樣一塊塊復(fù)制提完。

三、全部代碼

void LoadHeader(PPE_HEADERS _peHeaders, HANDLE _peBaseAddress) {
    // GET DOS HEADER
    LONG peBlockSize = 0x3c;
    HANDLE peSrcHeadersAddress = _peBaseAddress;
    HANDLE peCurHeadersAddress = _peHeaders;
    memcpy(peCurHeadersAddress, peSrcHeadersAddress, peBlockSize);
    peCurHeadersAddress = (BYTE*)peCurHeadersAddress + peBlockSize;
    peSrcHeadersAddress = (BYTE*)peSrcHeadersAddress + peBlockSize;

    // GET NT HEADER
    peBlockSize = sizeof(LONG);
    LONG* dosStubSizeHandle = (LONG*)peSrcHeadersAddress;
    memcpy(peCurHeadersAddress, peSrcHeadersAddress, peBlockSize);
    peCurHeadersAddress = (BYTE*)peCurHeadersAddress + peBlockSize;
    peSrcHeadersAddress = (BYTE*)peSrcHeadersAddress + peBlockSize;
    
    // GET DOS STUB
    if (dosStubSizeHandle != nullptr) {
        peBlockSize = *dosStubSizeHandle - 0x3c - sizeof(LONG);
        LPBYTE dosCode = (BYTE*)malloc(peBlockSize);
        if (dosCode != nullptr) {
            memcpy(dosCode, peSrcHeadersAddress, peBlockSize);
            memcpy(peCurHeadersAddress, &dosCode, sizeof(LPBYTE));
        }
        peCurHeadersAddress = (BYTE*)peCurHeadersAddress + sizeof(LPBYTE);
        peSrcHeadersAddress = (BYTE*)peSrcHeadersAddress + peBlockSize;
    }

    // SET DOS CODE SIZE
    memcpy(peCurHeadersAddress, &peBlockSize, sizeof(LONG));
    peCurHeadersAddress = (BYTE*)peCurHeadersAddress + sizeof(LONG);

    // GET NT SIGNATURE
    peBlockSize = sizeof(DWORD);
    memcpy(peCurHeadersAddress, peSrcHeadersAddress, peBlockSize);
    peCurHeadersAddress = (BYTE*)peCurHeadersAddress + peBlockSize;
    peSrcHeadersAddress = (BYTE*)peSrcHeadersAddress + peBlockSize;

    // GET NT FILE HEADER
    peBlockSize = sizeof(WORD) * 10;
    LONG nt_optional_header_size = *((WORD*)peSrcHeadersAddress + 8);
    memcpy(peCurHeadersAddress, peSrcHeadersAddress, peBlockSize);
    peCurHeadersAddress = (BYTE*)peCurHeadersAddress + peBlockSize;
    peSrcHeadersAddress = (BYTE*)peSrcHeadersAddress + peBlockSize;

    // GET NT OPTIONAL HEADER OTHER DATA
    peBlockSize = nt_optional_header_size - IMAGE_NUMBEROF_DIRECTORY_ENTRIES * sizeof(CUSTOM_IMAGE_DATA_DIRECTORY) - sizeof(DWORD);
    memcpy(peCurHeadersAddress, peSrcHeadersAddress, peBlockSize);
    peCurHeadersAddress = (BYTE*)peCurHeadersAddress + peBlockSize;
    peSrcHeadersAddress = (BYTE*)peSrcHeadersAddress + peBlockSize;

    // GET NT DATA DIRECTORIES COUNT
    peBlockSize = sizeof(DWORD);
    LONG directoriesCount = *((PDWORD)peSrcHeadersAddress);
    memcpy(peCurHeadersAddress, peSrcHeadersAddress, peBlockSize);
    peCurHeadersAddress = (BYTE*)peCurHeadersAddress + peBlockSize;
    peSrcHeadersAddress = (BYTE*)peSrcHeadersAddress + peBlockSize;

    // GET DATA DIRECTORIES DATAS
    peBlockSize = sizeof(IMAGE_DATA_DIRECTORY) * directoriesCount;
    memcpy(peCurHeadersAddress, peSrcHeadersAddress, peBlockSize);
}


PPE_HEADERS LoadFilePeHeaders(const wchar_t * _fileName) {
    // Open File
    HANDLE fHandle = CreateFile(_fileName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
    if (INVALID_HANDLE_VALUE == fHandle) {
        return nullptr;
    }
    DWORD fSize = GetFileSize(fHandle, 0);
    BYTE* fContent = (BYTE*)malloc(fSize * sizeof(BYTE));
    DWORD fReadSize;
    BOOL succ = ReadFile(fHandle, fContent, fSize, &fReadSize, 0);
    if (!succ) {
        CloseHandle(fHandle);
        return nullptr;
    }
    // Load Pe Head
    PE_HEADERS headers;
    LoadHeader((PPE_HEADERS)&headers, fContent);
    CloseHandle(fHandle);
    return &headers;
}


int main() {
    const wchar_t * filePath = TEXT("C:/Windows/system32/notepad.exe");

    PPE_HEADERS headers = LoadFilePeHeaders(filePath);
    if (headers == nullptr) {
        std::cout << "File open failed! Last error: " << GetLastError() <<  std::endl;
        return 0;
    }
    std::cout << headers->dos_header.e_magic << std::endl;
    return 0;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末丘侠,一起剝皮案震驚了整個(gè)濱河市徒欣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蜗字,老刑警劉巖打肝,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異挪捕,居然都是意外死亡粗梭,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)级零,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)断医,“玉大人,你說(shuō)我怎么就攤上這事奏纪〖停” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵序调,是天一觀的道長(zhǎng)醉锅。 經(jīng)常有香客問(wèn)我,道長(zhǎng)发绢,這世上最難降的妖魔是什么荣挨? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮朴摊,結(jié)果婚禮上默垄,老公的妹妹穿的比我還像新娘。我一直安慰自己甚纲,他們只是感情好口锭,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著介杆,像睡著了一般鹃操。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上春哨,一...
    開(kāi)封第一講書(shū)人閱讀 49,764評(píng)論 1 290
  • 那天荆隘,我揣著相機(jī)與錄音,去河邊找鬼赴背。 笑死椰拒,一個(gè)胖子當(dāng)著我的面吹牛晶渠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播燃观,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼褒脯,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了缆毁?” 一聲冷哼從身側(cè)響起番川,我...
    開(kāi)封第一講書(shū)人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎脊框,沒(méi)想到半個(gè)月后颁督,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡浇雹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年适篙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片箫爷。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖聂儒,靈堂內(nèi)的尸體忽然破棺而出虎锚,到底是詐尸還是另有隱情,我是刑警寧澤衩婚,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布窜护,位于F島的核電站,受9級(jí)特大地震影響非春,放射性物質(zhì)發(fā)生泄漏柱徙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一奇昙、第九天 我趴在偏房一處隱蔽的房頂上張望护侮。 院中可真熱鬧,春花似錦储耐、人聲如沸羊初。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)长赞。三九已至,卻和暖如春闽撤,著一層夾襖步出監(jiān)牢的瞬間得哆,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工哟旗, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贩据,地道東北人栋操。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像乐设,于是被迫代替她去往敵國(guó)和親讼庇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容