把PE映像檔案從記憶體中DUMP到磁碟

2021-06-19 15:44:26 字數 4787 閱讀 4305

引言: 引用看雪學院《軟體加密技術內幕》開篇的第一段話:「作業系統的可執行檔案格式和資料結構揭示了藏在作業系統內部的秘密,理解exe或dll將有助於對作業系統的深刻理解。了解了exe和dll裡面的奧秘,你將成為一名知識更加淵博的程式設計師!」

可以看到,作為網路安全愛好者的我們,掌握和熟練利用pe(portable executable)檔案格式的知識將必定能為我們學習黑客技術和攻防知識打下良好的基礎。

在網路攻防的對抗中,常常接觸到有關pe檔案格式方面的技術,比如緩衝區溢位技術中編寫win32 shellcode時利用pe檔案結構的特徵查詢api函式位址就是乙個很經典的例子,又比如,令人寒心的pe型病毒都是利用pe檔案結構而大規模感染系統的其他pe檔案。

pe檔案格式的基礎知識,如果讀者不熟悉的話,網上這方面的教程比比皆是。另外,對於習慣看書的讀者們,強烈推薦看雪軟體的《加密解密ii》,《軟體加密技術內幕》這兩本經典之作。

現在,市面上有很多的靜態分析pe檔案方面的工具,像pedump和pe explorer(附件中均有收錄)這兩款工具,可算的上是其中的佼佼者。前者是命令列下的,從它的可選引數就可以看出期功能的強大。後者是圖形化的,介面友好而且功能也很強大。

讀者肯定有疑問了,既然都有這麼現成的工具了,那寫本文目的是什麼呢?這裡說明一下,市面上出現的pe分析工具包括推薦的兩款工具都有乙個共同點,就是他們載入的都是磁碟上的pe檔案,但有時候我們卻要分析記憶體中的pe映象,這個又要如何實現呢?我們下面就自己來分析解決這個問題吧!

首先,要明確的一點是,pe檔案格式在磁碟中的資料結構布局和記憶體中的資料格式布局是一致的,就是說, 知道如何在pe檔案中尋找一些內容,那麼幾乎都能在被裝入到記憶體的對映檔案中找到相同的資訊。

這樣的話,就好辦了,我們可以使用乾坤大挪移,用類似分析磁碟pe檔案的方法來分析記憶體中的pe檔案了。大家可以看到,所有pe檔案(包括32位的dll)都是以乙個簡單mz-dos頭開始,mz格式的檔案頭在winnt.h中有定義,其image_dos_header結構如下(左邊數字是到檔案頭的偏移量):

typedef struct _image_dos_header image_dos_header, *pimage_dos_header;

我只列出了兩個最重要的成員,e_magic是dos可執行檔案標記「mz」,而e_lfanew指向pe檔案頭「pe」,0,0。執行程式在執行的時候,pe檔案裝載器將從mz-dos頭的e_lfanew欄位找到pe頭起始偏移,再跳到真正的pe檔案頭處。image_nt_headers的資料結構如下:

typedef struct _image_nt_headers image_nt_headers, *pimage_nt_headers;

pe檔案頭和原始資料之間存在乙個塊表,塊表包含每個塊在映象中的資訊。塊表image_section_header包含了pe檔案中的 sectin的重要資料,其結構如下:

typedef struct _image_section_header misc;

+0ch dword virtualaddress; // 該塊的rva

+10h dword sizeofrawdata; // 在檔案中對齊後的尺寸 

+14h dword pointertorawdata; // 在檔案中偏移

… …+28h dword characteristics; 

} image_section_header, *pimage_section_header;

出於學習的目的,我們就只分析這三個結構,你完全可以獲得其他你感興趣的結構字段。

下面我們具體到程式上來。

在我的程式裡,首先定義了乙個showhelp()的幫助函式,主要用來顯示用法。

可以看到,我所實現的pedump程式,必須帶乙個程序pid的引數,你可以從任務管理器裡檢視每個你想要dump的程序id號。

而第二個引數address是可選的,「*」表示dump所有模組在記憶體中的pe映像檔案,也可選任一有效的位址來dump,而如果不加第二個引數,則預設dump第乙個模組的pe映象。

主要功能的實現原理說來很簡單, 就是利用了readprocessmemory()這個函式叢記憶體中讀取資訊,然後利用pe映像在記憶體中和在磁碟中資料結構的布局一致性來分析。我們主要定義了的以下幾個函式,功能及注釋如下:

1)pagesize():獲得記憶體頁大小

dword pagesize()

2) getmodulename(): 獲得模組檔名

char* getmodulename(handle handle, dword address)

break;}}

}return ptr;

}注意,這裡用到了psapi.dll庫的兩個api函式enumprocessmodules()和getmodulebasename(),我這裡新增了richard shupak 寫的psapi.h的標頭檔案(附件內提供),並通過預處理#pragma comment(lib, 「psapi.dll」)載入psapi.dll,如果你的sdk沒有包含該標頭檔案,也許你需要利用loadlibrary()和getprocaddress()來動態載入。

3) dumppe(): 主功能函式, dump記憶體中的pe檔案資料

unsigned char* dumppe(handle hhprocess, unsigned char *szaddress, int *ilen)

printf("映象在0x%p/n", szaddress);

pdos = (pimage_dos_header)szheader; 

// ms-dos頭的最後乙個成員e_lfanew指向nt映象頭

pnt = (pimage_nt_headers)(szheader+pdos->e_lfanew);

// 獲得所需分配記憶體空間的大小

*ilen = pnt->optionalheader.sizeofimage +

pnt->optionalheader.sizeofheaders + pagesize();

// 分配虛擬記憶體空間, 注意後面的保護屬性為page_readwrite

if ( !(dwret = (dword)virtualalloc(null,

pnt->optionalheader.sizeofimage +

pnt->optionalheader.sizeofheaders + pagesize(),

mem_reserve|mem_commit, page_readwrite)) ) 

// 列印dump的pe頭重要資訊

printf("dumping 頭.../n"

" 頭大小 : 0x%p/n"

" 虛擬位址 : 0x%p/n"

" 映象位址 : 0x%p/n",

pnt->optionalheader.sizeofheaders, szaddress, 0);

memcpy((char*)dwret, szheader, pnt->optionalheader.sizeofheaders);

psection = image_first_section(pnt);

for(int i=0; ifileheader.numberofsections; i++)

// 從程序記憶體裡讀映像塊

if ( !readprocessmemory(hhprocess, szaddress+psection[i].virtualaddress,

szsection, psection[i].misc.virtualsize, null))

memcpy((char*)dwret+psection[i].pointertorawdata, szsection, psection[i].misc.virtualsize);

// 釋放申請到的記憶體

virtualfree(szsection, 0, mem_release);

}return (unsigned char *)dwret;

}4) dumpaddress(): dump記憶體位址

void dumpaddress(handle process, dword address, unsigned char *pe, int len, int pid)

memset(szfilename, 0, max_path);

//按照 「pid-記憶體位址-模組名.dat」 構造磁碟檔名

sprintf(szfilename, "%d-%p-%s.dat", pid, address, szmoudulename);

free(szmoudulename); // 釋放strup()申請來的資源

// 寫pe dump結果到磁碟

writepedump(szfilename, pe, len);

}5) writepedump():寫到磁碟檔案中

void writepedump(char *outfile, unsigned char*pe, int len)

// 寫檔案操作

writefile(hfile, pe, len, &cbwritten, null);

if ( len != cbwritten )

}到這,乙個簡單的從記憶體dump pe檔案映像到磁碟的程式就完成了。

編譯後我選取了金山詞霸的pid作為引數,得到的測試結果。

和pedump或pe explorer對比發現,結果是一樣的。呵呵。 是不是很有成就感呢。

小結:本文主要簡單討論了如何從記憶體中分析pe檔案的相關技術, 實現了很簡陋的功能, 但理解了思路,你完全可以加入很多誘人的功能。

無論是病毒技術,破解技術還是溢位技術,熟練掌握pe檔案格式的分析技術是很有用的,別以為網路上那些琳琅滿目的pe分析工具有多麼的神奇,只要你掌握了pe檔案格式的基本架構和基本的c/c++程式設計功底,你也能打造自己的pe工具的。記住,伴隨著黑防的努力,我們也在一步步成長!

把PE映像檔案從記憶體中DUMP到磁碟

了解了exe和dll裡面的奧秘,你將成為一名知識更加淵博的程式設計師!可以看到,作為網路安全愛好者的我們,掌握和熟練利用pe portable executable 檔案格式的知識將必定能為我們學習黑客技術和攻防知識打下良好的基礎。在網路攻防的對抗中,常常接觸到有關pe檔案格式方面的技術,比如緩衝區...

pe檔案of映像檔案頭

typedef struct image file header image file header,pimage file header 結構體的第乙個成員說明cpu的型別。需要了解的話看win32 sdk的巨集定義。結構體的第三個成員使用的是格林尼治時間 gmt 計算的秒數。結構體最後乙個成員說...

Java 如何匯出記憶體映像檔案

記憶體溢位自動匯出 xx heapdumponoutofmemoryerror 當記憶體溢位的時候自動匯出映像檔案 xx heapdumppath 匯出映像檔案的路徑上面的這兩個jvm 引數就可以在發生記憶體溢位的時候,自動將 heap 匯出到設定的路徑,後續進行記憶體分析的時候就可以檢視 使用ja...