新學期新氣象,一般是小學作文的開頭,在此引用一下。
最近有時間坐下來仔細研究一下pe檔案結構了,以前遇到這種問題時總是拆東牆補西牆。學的不夠透徹。幾天來一番研究之後,和大家分享一下。
pe的檔案結構從dos頭開始,其主要作用就兩個乙個是若是在dos環境下輸出一句話。另乙個作用就是找到pe檔案頭的位置,這是我們要關心的,本文只注重所要關心的問題——匯出表。和一些你再眾多網上資料上很難找到的細節,至於整體的pe結構網上的資料中說的很詳細。
首先,pe檔案的載入原理這裡就不多說了,整個複雜的過程都是由pe裝載器完成的,過程不是一兩句話能夠說清楚的,這裡我們只需要注意乙個細節就可以了。pe裝載器將pe檔案已檔案對映的方式從磁碟對映到記憶體,在對映時pe檔案被載入到記憶體的開始位置叫做基址,我們用lpimagebase來表示。我們首先需要知道我們如何將乙個pe檔案載入到記憶體,並獲得基址。
handle hfile = createfile("c:\\example.exe",
generic_read|generic_write,
file_share_read,
null,
open_existing,
file_attribute_normal,
null);
if(hfile==invalid_handle_value)
//記憶體對映檔案的基址
if (lpbaseaddress == null)
『
這裡我們需要關心的是乙個叫image_optional_header的結構,中文名稱叫「可選映象頭部」,說是可選的,其實非常重要。這個結構是在image_nt_herders結構中,並且是第二個成員變數。
typedef struct image_nt_herders
pimage_dos_header pdosheader=(pimage_dos_header)lpbaseaddress;
pimage_nt_headers pntheaders=(pimage_nt_headers)(lpbaseaddress + pdosheader->e_lfanew);
/可選映象頭部
typedef struct _image_optional_header
image_optional_header, *pimage_optional_header;
pntheaders->optionalheader;
然後我們需要注意image_data_directory這個陣列,這個陣列一般有15個元素,每個元素都記錄著這個pe檔案的重要資料結構。我們來看看這個陣列的每個元素的內容。
是不是很讓人吃驚?有了這個陣列,獲取一些pe資訊就非常簡單了,一般我們或者是計算機病毒所關心的多數是匯出表(eat),匯入表(iat),表,分別是這個陣列的第0個元素和第12個元素。這個方法和網上的一些eat iat位址獲取方法有所不同。很是方便。
而pntheader->optionalheader.datadirectory[0]又是乙個結構體,這個結構體的定義如下:
typedef struct _image_data_directory image_data_directory, *pimage_data_directory;
pntheader->optionalheader.datadirectory[0].virtualaddress
到這裡我們已經找到了pe檔案的匯出表相對位址了(rva),現在的問題是,我們如何將匯出表中存放的匯出函式資訊給弄出來。
我們先看看匯出表是個什麼東西:
//匯入位址表
typedef struct _image_export_directory
image_export_directory,*pimage_export_directory;
不難看出addressofnames;就是存放匯出函式的乙個陣列rva了。但是這裡有乙個小問題。導致我在這個問題上花了半天時間。addressofnames;中存放的確實是乙個rva(相對位址),但是不是直接指向函式陣列的,而是指向了乙個存放位址的陣列也就是乙個dword陣列,這個陣列的每乙個元素存放乙個匯出函式的rva,這樣說來有點繞,我們已一張圖說明:
這就給我這種菜鳥造成了一定的麻煩,誒基礎語法還是很重要啊。。。
所以位址需要這樣獲取(這裡已經加上了基址lpimagebase,後面說明)
dword * k=(dword *)(pexport->addressofnames+lpbaseaddress); //rvaêý×éê×µøö·
char * functionname=(char *)(*k+lpbaseaddress);
#include "windows.h"
#include "stdio.h"
void doshow(char * place)
//äú´æó³éäîä¼þµä»ùö·
if (lpbaseaddress == null)
pimage_dos_header pdosheader=(pimage_dos_header)lpbaseaddress;
pimage_nt_headers pntheaders=(pimage_nt_headers)(lpbaseaddress + pdosheader->e_lfanew);
pimage_export_directory pexport=(pimage_export_directory)(pntheaders->optionalheader.datadirectory[0].virtualaddress+lpbaseaddress);
int num=pexport->numberofnames;
printf("dllêç%s\n",pexport->name+lpbaseaddress);
printf("¹²óð%d¸öº¯êý\n",pexport->numberofnames);
printf("º¯êýðòºå º¯êýãû\n");
for (i;iaddressofnames+lpbaseaddress+4*i); //rvaêý×éê×µøö·
char * functionname=(char *)(*k+lpbaseaddress);
printf("%d %s\n",i+1,functionname);
} unmapviewoffile(lpbaseaddress);
closehandle(hfile);
}int main()
另外我還寫了介面,給大家展示一下吧。
下面的任務是嘗試更加困難的iat表的改寫,如插入dll,實現pe病毒的基本功能等等,如果可能,一定會給大家分享我的「成果」。
最後菜鳥言論,僅供娛樂。
PE檔案結構詳解(三)PE匯出表
上篇文章 pe檔案結構詳解 二 可執行檔案頭 的結尾出現了乙個大陣列,這個陣列中的每一項都是乙個特定的結構,通過函式獲取陣列中的項可以用rtlimagedirectoryentrytodata函式,datadirectory中的每一項都可以用這個函式獲取,函式原型如下 base 模組基位址。dire...
PE檔案結構詳解(三)PE匯出表
上篇文章 pe檔案結構詳解 二 可執行檔案頭 的結尾出現了乙個大陣列,這個陣列中的每一項都是乙個特定的結構,通過函式獲取陣列中的項可以用rtlimagedirectoryentrytodata函式,datadirectory中的每一項都可以用這個函式獲取,函式原型如下 base 模組基位址。dire...
PE檔案結構詳解(三)PE匯出表
上篇文章 pe檔案結構詳解 二 可執行檔案頭 的結尾出現了乙個大陣列,這個陣列中的每一項都是乙個特定的結構,通過函式獲取陣列中的項可以用rtlimagedirectoryentrytodata函式,datadirectory中的每一項都可以用這個函式獲取,函式原型如下 base 模組基位址。dire...