作者:ZV(ZVROP)
郵件:zvrop@163.com
主頁:(被整頓掉老~~~~~>_<~~~~~~)
網(wǎng)站:http://www.s8s8.net
日期:2004-8-18
轉(zhuǎn)載請保全文檔完整,謝謝
寫的倉卒繁多,錯(cuò)漏難免,還請各位給予斧正.
有什么問題,可以給我發(fā)email.^_^...
目錄:
一.寫在前面的話
二.故事的起因
三.解決-腹稿
四.解決-實(shí)戰(zhàn)
五.解決-打造
六.包裝
七.小節(jié)
八.后記
九,參考文獻(xiàn)
?
正文開始:
一.寫在前面的話
這篇文檔講述的并不是什么新鮮的技術(shù),我只是起到將他們混和起來實(shí)現(xiàn)了自己需要的功能的作用,如果你對批處理和PE格式相當(dāng)了解,那我的這篇文檔就權(quán)且當(dāng)作瀏覽溫習(xí)吧...^_^...
另外,由于我本人廢話比較多,這也是不想給雜志寫稿子的主要原因,給我干癟癟的3000字能講出什么來,不如這樣沒有限制的爽快(當(dāng)然也沒什么報(bào)酬...一_一..),所以也為了防止你在看文章的途中睡著,請自備小錐子一把.....
最后,這篇文檔說是用批處理下載文件,其實(shí)它包含了很多方面的知識,如果有時(shí)間,不妨一看哈,^_^,開始買瓜了..
二.故事的起因
最初萌發(fā)這個(gè)想法的是不久前,在論壇(廣告一下:http://www.s8s8.net)上的UNIX SHELL板塊有個(gè)會員發(fā)了一篇帖子,內(nèi)容是用BASH SHELL寫的一個(gè)成批下載圖片的腳本(其實(shí)是H圖片...一_一..),接下來跟貼那個(gè)多啊...,有繁衍出PHP的,VBS的,C的,C#的,JAVA的,甚至交流到多線程,斷點(diǎn)續(xù)傳....引用花大哥的一句話"無語,為了MM照片,大家的動力都很足啊!"...汗~~..
在發(fā)了一份PHP和C的代碼后(感覺我動力特足~大色狼...一_一..),覺得很簡單(因?yàn)橛肅或者PHP等腳本來實(shí)現(xiàn)文件的下載本來就是很基礎(chǔ)的東西)我就開始想用微軟最原始的腳本--Batch(批處理)來嘗試實(shí)現(xiàn)(本文標(biāo)題中的"閑來無事"就是紀(jì)念此處,一_一.),這似乎有點(diǎn)不可思議,因?yàn)榕幚韼缀鯖]有實(shí)現(xiàn)的支持網(wǎng)絡(luò)的功能(當(dāng)然,如果你說你能用TELNET下載到文件的我是很佩服的..一_一..),但也不是完全沒有辦法,畢竟WINDOWS里面能用的東西這么多,沒有完不成的事情....在這種挑戰(zhàn)的勾引下,我完成了用批處理下載文件的功能....現(xiàn)在讓我一步一步回放我的思路,揭開用批處理下載文件的奧秘...
三.解決-腹稿
如果用批處理來下載文件的話,肯定會馬上想到Cscript腳本(或者是JAVA腳本),那是當(dāng)然,太多的批處理腳本實(shí)現(xiàn)一些本身并不可能實(shí)現(xiàn)的功能的時(shí)候都是采用ECHO出一個(gè)其他腳本的方法來解決.可是我們的目的就在于用批處理實(shí)現(xiàn)下載的功能,如果要用VBS來幫忙的話不如直接寫VBS了.這個(gè)想法順即告吹....
再來,記得以前有流行過一陣用RUNDLL32來加載DLL中的API,似乎和我們需要的目的沾邊,因?yàn)橄螺d文件能用的API太多了,如果RUNDLL能調(diào)用,那最好不過了.于是我打開MSDN,找了一個(gè)API: URLDownloadToFile
URLDownloadToFile函數(shù)原型:
代碼?
HRESULT URLDownloadToFile(?????????
?? LPUNKNOWN pCaller,
?? LPCTSTR szURL,
?? LPCTSTR szFileName,
?? DWORD dwReserved,
?? LPBINDSTATUSCALLBACK lpfnCB
);
?
?
URLDownloadToFile函數(shù)的一些信息:
引用?
Header Urlmon.h
Import library Urlmon.lib
Minimum availability Internet Explorer 3.0
Minimum operating systems Windows NT 4.0, Windows 95
?
?
根據(jù)這些,我們可以知道,這個(gè)API是在URLMON.DLL文件中的一個(gè)導(dǎo)出函數(shù),簡單的實(shí)現(xiàn)了把一個(gè)文件從WEB服務(wù)器下載本機(jī)的功能,其實(shí)用這個(gè)函數(shù)還不錯(cuò)的,至少它幫我們處理了斷點(diǎn)續(xù)傳,緩存等等的功能,比起直接使用SOCKET函數(shù)來實(shí)現(xiàn)或者用WININET里的函數(shù)來實(shí)現(xiàn)簡單多得多了.
URLDownloadToFile有五個(gè)參數(shù):
第一個(gè)參數(shù)是僅當(dāng)調(diào)用者是一個(gè)ActiveX對象才使用,一般為NULL.
第二個(gè)參數(shù)就是要下載文件的目標(biāo)URL,完整路徑.
第三個(gè)是本地保存路徑,也是完整路徑
第四個(gè)是保留,必須為0
第五個(gè)是指向一個(gè)IBindStatusCallback接口的指針,這就類似一種回調(diào)機(jī)制,你可以參考這些來活動當(dāng)前下載進(jìn)度,選擇是否繼續(xù)下載等等.
這里面我們只關(guān)心第二和第三個(gè)參數(shù).其他的通通設(shè)置成0.(當(dāng)然你寫C的時(shí)候最好設(shè)置為NULL)
嗯,敲了點(diǎn)鍵盤介紹了這個(gè)函數(shù),是因?yàn)檎奈臋n都和這個(gè)函數(shù)息息相關(guān),有了這個(gè)函數(shù),就可以呼叫RUNDLL32來調(diào)用它,但是很可惜,這個(gè)美好的計(jì)劃馬上也破裂了...
我去微軟看了他們的164787號文檔(http://support.microsoft.com/default.aspx?...kb;en-us;164787),該文檔闡述了RUNDLL32的調(diào)用方式和能被他調(diào)用的函數(shù)的格式:
它們是這么說的:
引用?
Rundll and Rundll32 programs do not allow you to call any exported function from any DLL. For example, you can not use these utility programs to call the Win32 API (Application Programming Interface) calls exported from the system DLLs. The programs only allow you to call functions from a DLL that are explicitly written to be called by them.
?
?
這個(gè)是規(guī)定的格式:
代碼?
void CALLBACK
EntryPoint(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);
?
?
很不幸,我們的URLDownloadToFile小兄弟并沒有符合這些條件,被RUNDLL32無情的拋棄了(汗一滴..)...但是我們并沒有因此而嫌棄它(汗一滴AGAIN..),畢竟,在后來實(shí)現(xiàn)的過程里,它是為我們的工作省下了不少功夫.
到此,用RUNDLL32運(yùn)行計(jì)劃流產(chǎn)....(寒...)
想了一根煙功夫,現(xiàn)在URLDownloadToFile有了,怎么才能調(diào)用這個(gè)函數(shù)呢?總不能模仿匯編PUSH 5個(gè)參數(shù)進(jìn)棧,然后CALL吧,那這個(gè)函數(shù)的地址還要用LoadLibrary()和GetProcAddress()計(jì)算得來,那這兩個(gè)函數(shù)的地址.....還是放棄...等等,如果用一個(gè)EXE來實(shí)現(xiàn)的話就簡單很多了(至少EXE是不需要任何解釋器的),對,寫一個(gè)EXE來下載文件.可我們的目的是用BAT來下載呢,BAT文件能包裹EXE的數(shù)據(jù)嗎?答案是肯定的...往下看..
記得以前看過一篇文檔<<Do All in Cmd Shell>>里面介紹過一種方法.先賣個(gè)關(guān)子.大家都知道,如果用ECHO加上重定向符來寫文件的話,只能寫入ASCII的一部分,也就可以顯示出來的那些ASCII(也就是ASCII值小于128的那些),對于那些無法顯示的字符就沒有辦法了.但是這讓我們想起一個(gè)工具,一個(gè)微軟歷史上同樣古老的,批處理的兄弟--DEBUG!
現(xiàn)在思路清晰了:可以讓批處理把ECHO不能顯示的字符轉(zhuǎn)化為16進(jìn)制數(shù)據(jù)(比如EXE中的那些數(shù)據(jù))保存在批處理中,然后用DEBUG寫道文件里,最后用BAT調(diào)用生成的EXE,下載文件!(想完了這里,我感覺還是太麻煩,不知道哪位牛人對這個(gè)實(shí)現(xiàn)還有什么更加簡單的辦法嗎??)
四.解決-實(shí)戰(zhàn)
倘若就此編寫一個(gè)可下載文件的EXE,然后直接用BAT包裹,定然會被同行恥笑,不單是因?yàn)槟菐浊€(gè)字節(jié)的數(shù)據(jù)拖著大大臃腫的BAT文件,更加讓為這種簡單的想法立刻現(xiàn)形,為了不達(dá)到這些負(fù)面效果,也為了讓這篇文檔不至于干癟癟的讓人感覺沒什么看頭(事實(shí)上是因?yàn)樵缧r(shí)候看過watercloud的一篇大作感悟頗深),我決定手工寫一串16進(jìn)制代碼來代替機(jī)器編譯的EXE.既美觀了界面,又增強(qiáng)了技術(shù)性.....(一_一...簡直是在賣作...)
現(xiàn)在當(dāng)務(wù)之急是要一個(gè)可以下載文件的EXE程序,實(shí)現(xiàn)這個(gè)目標(biāo)只要一個(gè)URLDownloadToFile即可,放在最后實(shí)現(xiàn),先來寫一個(gè)PE框架:大家都知道PE文件的格式吧,不懂的就去看看那個(gè)著名的電信黑客羅某某的書.(Who!?...~)
先給出我們的PE框架,基于XP的FileAlignment對齊大小最小就支持到0x200(也就是10進(jìn)制的512字節(jié),以下有在前面加上0x的都表示16進(jìn)制數(shù)值),我們的框架就打出512字節(jié)(注意,我下面留有空白表示各個(gè)PE部分,結(jié)合下面的文檔,大家方便理解),這個(gè)框架里沒有任何的代碼或者數(shù)據(jù):
(ZV友情提示:下面是最枯燥的部分,各位手握錐子,要有一不怕苦,二不怕痛的精神看完它....)
(如果定力不高的朋友,或者堆PE文件再熟悉不過的朋友,可以字節(jié)轉(zhuǎn)到"JMP S1"處往下看.)
(如果只想知道到底怎么回事,或者對這篇作文報(bào)瀏覽態(tài)度的朋友,可以直接轉(zhuǎn)到"JMP S2"處繼續(xù)瀏覽)
(睡著了的繼續(xù)睡覺....)
代碼?
Offset????? 0? 1? 2? 3? 4? 5? 6? 7?? 8? 9? A? B? C? D? E? F
00000000?? 4D 5A 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? MZ..............
00000010?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
00000020?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
00000030?? 00 00 00 00 00 00 00 00? 00 00 00 00 40 00 00 00?? ............@...
==============================================================================
00000040?? 50 45 00 00 4C 01 02 00? 00 00 00 00 00 00 00 00?? PE..L...........
00000050?? 00 00 00 00 70 00 0F 01?
??????????????????????????????????? 0B 01 00 00 00 02 00 00?? ....p...........
00000060?? 00 00 00 00 00 00 00 00? 79 01 00 00 00 00 00 00?? ........y.......
00000070?? 00 00 00 00 00 00 40 00? 00 10 00 00 00 02 00 00?? ......@.........
00000080?? 00 00 00 00 00 00 00 00? 04 00 00 00 00 00 00 00?? ................
00000090?? 00 30 00 00 00 02 00 00? 00 00 00 00 02 00 00 00?? .0..............
000000A0?? 00 01 00 00 00 00 00 00? 00 01 00 00 00 10 00 00?? ................
000000B0?? 00 00 00 00 02 00 00 00?
??????????????????????????????????? 00 00 00 00 00 00 00 00?? ................
000000C0?? 28 11 00 00 28 00 00 00?
==============================================================================
??????????????????????????????????? 00 00 00 00 00 00 00 00?? (...(...........
000000D0?? 00 02 00 00 00 10 00 00? 00 02 00 00 00 01 00 00?? ................
000000E0?? 00 00 00 00 00 00 00 00? 00 00 00 00 60 00 00 60?? ............`..`
000000F0?? 00 00 00 00 00 00 00 00? 02 00 00 00 00 20 00 00?? ............. ..
00000100?? 00 02 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
00000110?? 00 00 00 00 60 00 00 60? 00 00 00 00 00 00 00 00?? ....`..`........
00000120?? 58 11 00 00 00 00 00 00? 50 11 00 00 00 00 00 00?? X.......P.......
00000130?? 00 00 00 00 6E 11 00 00? 20 11 00 00 00 00 00 00?? ....n... .......
00000140?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
00000150?? 58 11 00 00 00 00 00 00
??????????????????????????????????? 00 00 00 00 00 00 00 00?? ................
00000160?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
00000170?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
00000180?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
00000190?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
000001A0?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
000001B0?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
000001C0?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
000001D0?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
000001E0?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
000001E0?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
?
?
這里簡單介紹一下PE文件格式的組成:
大致來分呢,PE格式文件可以分為這三個(gè)部分(就是上述框架中用"=="分割的三個(gè)部分):
引用?
++++++++++++++++++++++++
+DOS信息部分????????? +
++++++++++++++++++++++++
++++++++++++++++++++++++
+PE信息部分??????????? +
++++++++++++++++++++++++
++++++++++++++++++++++++
+數(shù)據(jù)部分????????????? +
++++++++++++++++++++++++
?
?
下面來簡單介紹每一部分的結(jié)構(gòu),首先的"DOS信息部分":
引用?
+++++++++++++++++++++++++++++++++++++++++++++
+? +++++++++++++++++++++++++++++++++++++++? +
+? +[DOS文件頭][0x40]??????????????????? +? +
+? +++++++++++++++++++++++++++++++++++++++? +
+????????????????????????????????????????? + <==DOS信息部分
+? +++++++++++++++++++++++++++++++++++++++? +
+? +[DOS塊][0x70,可變]????????????????? +? +
+? +++++++++++++++++++++++++++++++++++++++? +
+++++++++++++++++++++++++++++++++++++++++++++
?
?
這部分我覺得是最冗余的地方,首先DOS文件頭的結(jié)構(gòu):
代碼?
typedef struct _IMAGE_DOS_HEADER {????? // DOS .EXE header
?WORD?? e_magic;?????????????????????? // Magic number
?WORD?? e_cblp;??????????????????????? // Bytes on last page of file
?WORD?? e_cp;????????????????????????? // Pages in file
?WORD?? e_crlc;??????????????????????? // Relocations
?WORD?? e_cparhdr;???????????????????? // Size of header in paragraphs
?WORD?? e_minalloc;??????????????????? // Minimum extra paragraphs needed
?WORD?? e_maxalloc;??????????????????? // Maximum extra paragraphs needed
?WORD?? e_ss;????????????????????????? // Initial (relative) SS value
?WORD?? e_sp;????????????????????????? // Initial SP value
?WORD?? e_csum;??????????????????????? // Checksum
?WORD?? e_ip;????????????????????????? // Initial IP value
?WORD?? e_cs;????????????????????????? // Initial (relative) CS value
?WORD?? e_lfarlc;????????????????????? // File address of relocation table
?WORD?? e_ovno;??????????????????????? // Overlay number
?WORD?? e_res[4];????????????????????? // Reserved words
?WORD?? e_oemid;?????????????????????? // OEM identifier (for e_oeminfo)
?WORD?? e_oeminfo;???????????????????? // OEM information; e_oemid specific
?WORD?? e_res2[10];??????????????????? // Reserved words
?LONG?? e_lfanew;????????????????????? // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
?
?
其中最重要的就是e_lfanew,它指向了下面的"PE信息部分"的起始地址(也就是俗稱的PE頭部).其他的是一些DOS下運(yùn)行這個(gè)PE文件必須的結(jié)構(gòu),比如看注解就明白,什么代碼初始化堆棧段,初始化堆棧指針,入口IP,CS等等,都是在WIN32上沒有用的東西,我就不翻譯拉,這些都是說DOS下的,如果這個(gè)PE文件一開始就打定在WINDOWS下運(yùn)行,這些亂寫都無所謂,你甚至可以把你的名字都寫進(jìn)去(.....一_一..).當(dāng)然,你這么作后這個(gè)文件就不能在DOS下運(yùn)行了..不然當(dāng)機(jī)是幾乎可以肯定的....(寒....).
需要記的除了e_lfanew是指向PE頭的指針外還要記得這個(gè)DOS文件頭結(jié)構(gòu)長0x40,也就是64個(gè)字節(jié).還有第一個(gè)參數(shù)e_magic,這個(gè)地方永遠(yuǎn)是"0x40 0x5a",也就是字符的"MZ".
DOS塊部分保存的就是一段DOS下可以執(zhí)行的代碼,比如現(xiàn)在大多編譯器就簡單的輸出一個(gè)"This program cannot be run in DOS mode"的字符串,和"DOS信息部分"一樣,如果你不打算在DOS執(zhí)行這個(gè)EXE文件,那么這里完全可以刪除,為什么?因?yàn)閃IN32的PE裝載器只關(guān)心"DOS信息部分"的e_lfanew指向的而已.
本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/2195/archive/2004/08/31/90107.aspx
綜上所述,"DOS信息部分"對應(yīng)框架的代碼為:
代碼?
Offset????? 0? 1? 2? 3? 4? 5? 6? 7?? 8? 9? A? B? C? D? E? F
00000000?? 4D 5A 00 5B D5 E2 C0 EF? B6 BC C3 BB D3 C3 2C 2C?? MZ.[這里都沒用,,
00000010?? B1 C8 C8 E7 CE D2 D0 B4? 3A CE D2 D2 B2 D6 BB 2C?? 比如我寫:我也只,
00000020?? CA C7 D2 BB B0 E3 CB A7? 2C B2 BB CA C7 CC D8 2C?? 是一般帥,不是特,8
00000030?? B1 F0 CB A7 B5 C4 C0 B2? 5D 00 00 00 40 00 00 00?? 別帥的啦]...@...
?
?
可以看到最后的4個(gè)字節(jié)"40000000"也就是00000040H(下面如果直接在數(shù)值后加"H"的即表示為16進(jìn)制)是指向他末尾的指針,也就是說明,我們把"DOS塊"的部分給去掉了.
接下來是"PE信息部分",他的結(jié)構(gòu)可以用下面的圖來表示:
引用?
+++++++++++++++++++++++++++++++++++++++++++++
+? +++++++++++++++++++++++++++++++++++++++? +
+? +[PE標(biāo)志][0x04]????????????????????? +? +
+? +++++++++++++++++++++++++++++++++++++++? +
+????????????????????????????????????????? + <==PE信息部分
+? +++++++++++++++++++++++++++++++++++++++? +
+? +[PE文件頭][0x18]??????????????????? +? +
+? +++++++++++++++++++++++++++++++++++++++? +
+????????????????????????????????????????? +
+? +++++++++++++++++++++++++++++++++++++++? +
+? +[自定義數(shù)據(jù)結(jié)構(gòu)][0x0e]????????????? +? +
+? +++++++++++++++++++++++++++++++++++++++? +
+++++++++++++++++++++++++++++++++++++++++++++
?
?
整個(gè)"PE信息部分"結(jié)構(gòu)是這樣的:
代碼?
typedef struct _IMAGE_NT_HEADERS {
?DWORD Signature;????????????????????? //"PE標(biāo)志"段,總是"PE00"
?IMAGE_FILE_HEADER FileHeader;???????? //"PE文件頭"段,指向IMAGE_FILE_HEADER結(jié)構(gòu)
?IMAGE_OPTIONAL_HEADER OptionalHeader; //"自定義數(shù)據(jù)"段,指向IMAGE_OPTIONAL_HEADER結(jié)構(gòu)
} IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;
?
?
IMAGE_FILE_HEADER結(jié)構(gòu)(PE文件頭)和IMAGE_OPTIONAL_HEADER結(jié)構(gòu)如下:
代碼?
typedef struct _IMAGE_FILE_HEADER {
?WORD Machine;???????????????????????? //運(yùn)行平臺,386的話是104CH
?WORD NumberOfSections;??????????????? //文件節(jié)數(shù)目,最少為2
?DWORD TimeDateStamp;????????????????? //文件創(chuàng)建時(shí)間,隨便設(shè)置(不過為了最后生成方便,隨便設(shè)置的地方最好都設(shè)置為0)
?DWORD PointerToSymbolTable;?????????? //這里兩項(xiàng)記用于調(diào)試,也隨便設(shè)置
?DWORD NumberOfSymbols;
?WORD SizeOfOptionalHeader;??????????? //下面那個(gè)IMAGE_OPTIONAL_HEADER結(jié)構(gòu)的長度,一般為000EH(包括16個(gè)IMAGE_DATA_DIRECTORY結(jié)構(gòu)),我們只要2個(gè)結(jié)構(gòu),所以設(shè)置為0070H
?WORD Characteristics;???????????????? //文件屬性,PE文件是010H,DLL的話是210H
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
?
?
IMAGE_FILE_HEADER說明了PE文件的基本運(yùn)行信息,可是光靠這短短的結(jié)構(gòu)并不能滿足我們的需要,畢竟微軟設(shè)計(jì)的東西還是考慮的很周全的,于是在它下面跟上了一個(gè)長長的結(jié)構(gòu)(OptionHeader結(jié)構(gòu))來作為附加信息供給系統(tǒng).
代碼?
OptionHeader結(jié)構(gòu)(自定義數(shù)據(jù)結(jié)構(gòu))定義如下:
typedef struct _IMAGE_OPTIONAL_HEADER {
?WORD??? Magic;??????????????????????? //EXE文件的話這里是10B
?BYTE??? MajorLinkerVersion;?????????? //連接器版本,隨便
?BYTE??? MinorLinkerVersion;
?DWORD?? SizeOfCode;?????????????????? //所有代碼節(jié)總大小,我們就一個(gè)節(jié),所以是512,也就是200H
?DWORD?? SizeOfInitializedData;??????? //....未初始化數(shù)據(jù)節(jié)....沒有這個(gè),設(shè)置為0
?DWORD?? SizeOfUninitializedData;????? //....已................................
?DWORD?? AddressOfEntryPoint;????????? //代碼執(zhí)行起始地址,注意,這個(gè)是你代碼存放的位置,[這里注意點(diǎn)1]
?DWORD?? BaseOfCode;?????????????????? //代碼段......(這里三個(gè)都是內(nèi)存地址),這里是0
?DWORD?? BaseOfData;?????????????????? //數(shù)據(jù)段......(并非硬盤文件地址),這里是0
?DWORD?? ImageBase;??????????????????? //建議加載位置,通常是00400000H,9X的系統(tǒng)可能略小于這個(gè)值,記不得了..:(
?DWORD?? SectionAlignment;???????????? //內(nèi)存中對齊大小,一般為1000H,也就是NT的一個(gè)內(nèi)存片,4KB
?DWORD?? FileAlignment;??????????????? //文件..........,這里設(shè)置最小的,200H,兼容全部系統(tǒng)
?WORD??? MajorOperatingSystemVersion;? //一下幾個(gè)都是系統(tǒng)版本相關(guān)的,隨便設(shè)置
?WORD??? MinorOperatingSystemVersion;
?WORD??? MajorImageVersion;
?WORD??? MinorImageVersion;
?WORD??? MajorSubsystemVersion;??????? //這里要設(shè)置為04H
?WORD??? MinorSubsystemVersion;
?DWORD?? Win32VersionValue;??????????? //未用
?DWORD?? SizeOfImage;????????????????? //PE文件占用的內(nèi)存空間,我們設(shè)置為3000H
?DWORD?? SizeOfHeaders;??????????????? //PE文件頭大小(含節(jié)表),這里是200H
?DWORD?? CheckSum;???????????????????? //效驗(yàn)和(我不知道用來干嘛,PE幾乎都是000000000,可能和其他方面有關(guān),比如調(diào)試?)
?WORD??? Subsystem;??????????????????? //文件子系統(tǒng),子系統(tǒng)的含義大家可以去參考NT內(nèi)核,這里設(shè)置為02,03均可(控制臺和窗口子系統(tǒng))
?WORD??? DllCharacteristics;??????????
?DWORD?? SizeOfStackReserve;?????????? //一下幾個(gè)是有關(guān)堆和棧的設(shè)置,基本上隨便,不過最好設(shè)置夠用就行(不是0啊!)
?DWORD?? SizeOfStackCommit;
?DWORD?? SizeOfHeapReserve;
?DWORD?? SizeOfHeapCommit;
?DWORD?? LoaderFlags;????????????????? //未用
?DWORD?? NumberOfRvaAndSizes;????????? //下面的IMAGE_DATA_DIRECTORY結(jié)構(gòu)的數(shù)量,原來是16個(gè),最少為2個(gè)
?IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;
?
?
有了這個(gè)IMAGE_OPTIONAL_HEADER結(jié)構(gòu),PE文件的作用和包含了什么資源都一目了然了.
IMAGE_DATA_DIRECTORY結(jié)構(gòu)如下,PE文件中包含了很多數(shù)據(jù)類型,比如導(dǎo)出,導(dǎo)入函數(shù),資源,重定位,調(diào)試和版權(quán)信息等等,這個(gè)結(jié)構(gòu)最多可以有16個(gè),就是用來定位這些數(shù)據(jù)的:
代碼?
typedef struct _IMAGE_DATA_DIRECTORY {
?DWORD?? VirtualAddress;???????
?DWORD?? Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
?
?
IMAGE_DATA_DIRECTORY結(jié)構(gòu)就是指出了你每個(gè)數(shù)據(jù)類型的在內(nèi)存中的裝載位置和長度.注意,這個(gè)結(jié)構(gòu)和下面要說道的節(jié)表不同,雖然他們可能指向的是同一個(gè)地址,但是,IMAGE_DATA_DIRECTORY區(qū)分的是嚴(yán)格的數(shù)據(jù)類型,而節(jié)表只是根據(jù)人為的定義來劃分?jǐn)?shù)據(jù)的種類,如果是正常的EXE,通常把各個(gè)數(shù)據(jù)種類分開存放,而這些數(shù)據(jù)通常又和數(shù)據(jù)類型用一樣的方法分類,所以IMAGE_DATA_DIRECTORY結(jié)構(gòu)和節(jié)表指向的地址可能是一樣的,但是本文這篇例子不同,因?yàn)槲覀兪謱懙腜E必須盡可能的小,所以我吧幾個(gè)節(jié)表的數(shù)據(jù)全部放在了一個(gè)節(jié),這樣,節(jié)表就只有一個(gè),而IMAGE_DATA_DIRECTORY結(jié)構(gòu)要從混和的數(shù)據(jù)中指向正確的數(shù)據(jù)類型地址,就和節(jié)表指向的不一樣了.
綜上所述,"PE信息部分"對應(yīng)框架的代碼為:
代碼?
Offset????? 0? 1? 2? 3? 4? 5? 6? 7?? 8? 9? A? B? C? D? E? F
00000040?? 50 45 00 00 4C 01 02 00? 00 00 00 00 00 00 00 00?? PE..L...........
00000050?? 00 00 00 00 70 00 0F 01?
??????????????????????????????????? 0B 01 00 00 00 02 00 00?? ....p...........
00000060?? 00 00 00 00 00 00 00 00? 79 01 00 00 00 00 00 00?? ........y.......
00000070?? 00 00 00 00 00 00 40 00? 00 10 00 00 00 02 00 00?? ......@.........
00000080?? 00 00 00 00 00 00 00 00? 04 00 00 00 00 00 00 00?? ................
00000090?? 00 30 00 00 00 02 00 00? 00 00 00 00 02 00 00 00?? .0..............
000000A0?? 00 01 00 00 00 00 00 00? 00 01 00 00 00 10 00 00?? ................
000000B0?? 00 00 00 00 02 00 00 00?
??????????????????????????????????? 00 00 00 00 00 00 00 00?? ................
000000C0?? 28 11 00 00 28 00 00 00?
?
?
這上面的數(shù)據(jù)大多都解釋過了,這里要看地址"000000C0"處的"28 11 00 00 28 00 00 00",這個(gè)是IMAGE_DATA_DIRECTORY結(jié)構(gòu)的第二個(gè),也就是導(dǎo)入表的地址,"00 00 00 28"這個(gè)是長度,不比多說,"00 00 11 28"這個(gè)又為何?帶著這個(gè)問題看下去...[這里算作注意點(diǎn)2]
最后要介紹的是"數(shù)據(jù)部分":
引用?
+++++++++++++++++++++++++++++++++++++++++++++
+? +++++++++++++++++++++++++++++++++++++++? +
+? +[數(shù)據(jù)節(jié)表][0x24*N+1]??????????????? +? +
+? +++++++++++++++++++++++++++++++++++++++? +
+????????????????????????????????????????? + <==PE數(shù)據(jù)部分
+? +++++++++++++++++++++++++++++++++++++++? +
+? +[數(shù)據(jù)節(jié)][不定]????????????????????? +? +
+? +++++++++++++++++++++++++++++++++++++++? +
+++++++++++++++++++++++++++++++++++++++++++++
?
?
其中IMAGE_SECTION_HEADER結(jié)構(gòu)(數(shù)據(jù)節(jié)表)如下:
代碼?
typedef struct _IMAGE_SECTION_HEADER {
?BYTE??? Name[IMAGE_SIZEOF_SHORT_NAME];//這個(gè)8字節(jié)的空間就是給你來定義這個(gè)節(jié)的名稱,比如大家常見的".text .data .code"等等,我這里為了以后的填充方便,設(shè)置了空白..(00000000H),其實(shí)這里是可以隨便寫的,比如你定義".zvrop"也可以
?union {
???? DWORD?? PhysicalAddress;????????? //這是個(gè)聯(lián)合結(jié)構(gòu),說明了該節(jié)的大小,我們整個(gè)PE文件就是一個(gè)節(jié),所以是200H
???? DWORD?? VirtualSize;
?} Misc;
?DWORD?? VirtualAddress;?????????????? //定位該節(jié)在內(nèi)存中的地址(相對于加載位置的偏移地址)我們這里是先不說這些.[這里算作注意點(diǎn)3]
?DWORD?? SizeOfRawData;??????????????? //文件中的尺寸,這里和上面的聯(lián)合結(jié)構(gòu)不同,這里是對齊后的地址,我們設(shè)置為200H
?DWORD?? PointerToRawData;???????????? //該節(jié)在文件中的位置,相對于文件頭,這里可以隨便設(shè)置,不過設(shè)置了后面的代碼指針也要跟著變動,我們這里設(shè)置100H
?DWORD?? PointerToRelocations;???????? //下面四個(gè)是給連接器用的參數(shù),隨便
?DWORD?? PointerToLinenumbers;
?WORD??? NumberOfRelocations;
?WORD??? NumberOfLinenumbers;
?DWORD?? Characteristics;????????????? //節(jié)的屬性,自己區(qū)查表,基于篇幅,這張表我就不提供了,需要的可以PM我,一般代碼節(jié)為60000020H(40000000&2000000&00000020),即是可執(zhí)行,可讀的代碼段,我們設(shè)置為60000060H,因?yàn)槲覀兗劝藬?shù)據(jù)又包含了代碼.
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
?
?
可以看到這個(gè)結(jié)構(gòu)的數(shù)量是不定的,也就是你下面有多少個(gè)節(jié),就有多少個(gè)IMAGE_SECTION_HEADER+1的結(jié)構(gòu),因?yàn)橄到y(tǒng)需要一個(gè)全0的IMAGE_SECTION_HEADER結(jié)構(gòu)來標(biāo)識已經(jīng)結(jié)束.另外,XP最少要兩個(gè)IMAGE_SECTION_HEADER結(jié)構(gòu),,不然會報(bào)非法32位程序的(這個(gè)熟悉的提示我在完成這個(gè)東西的時(shí)候不知道出現(xiàn)了NN次,從此深惡痛絕!),2K則沒有這個(gè)限制(引自watercloud的研究,我沒多少時(shí)間去深挖這個(gè)哈...).
下面就是具體的"數(shù)據(jù)節(jié)"的內(nèi)容了(我們這篇文檔整個(gè)PE文件就是一個(gè)節(jié)),整個(gè)PE文件結(jié)構(gòu)內(nèi)容大概就是這么多.
綜上所述,"數(shù)據(jù)部分"對應(yīng)框架的代碼為:
代碼?
Offset????? 0? 1? 2? 3? 4? 5? 6? 7?? 8? 9? A? B? C? D? E? F
??????????????????????????????????? 00 00 00 00 00 00 00 00?? (...(...........
000000D0?? 00 02 00 00 00 10 00 00? 00 02 00 00 00 01 00 00?? ................
000000E0?? 00 00 00 00 00 00 00 00? 00 00 00 00 60 00 00 60?? ............`..`
000000F0?? 00 00 00 00 00 00 00 00? 02 00 00 00 00 20 00 00?? ............. ..
00000100?? 00 02 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
00000110?? 00 00 00 00 60 00 00 60? 00 00 00 00 00 00 00 00?? ....`..`........
?
?
[THIS IS JMP S1]
繞了這么大個(gè)圈子才回到正題....(一_一....其實(shí)我是想把問題寫的詳細(xì),這樣大家看了就沒有態(tài)度的疑慮嘛..),還記得上面說過的數(shù)據(jù)類型嗎,其中最重要的就是導(dǎo)入表,我們的URLDownloadToFile小朋友已經(jīng)在板凳上坐了很久了.......這個(gè)導(dǎo)入表就是為他量身定做的.我們的目的就是讓PE文件執(zhí)行URLDownloadToFile的功能,自然得把URLDownloadToFile這個(gè)函數(shù)加入導(dǎo)入表.
說道導(dǎo)入表的定義呢?就不得不先說說WINDOWS加載可執(zhí)行程序時(shí)候?qū)AT(IMPORT ADDRESS TABLE,導(dǎo)入地址表)的修改,我們知道,各個(gè)系統(tǒng)的每個(gè)函數(shù)在內(nèi)存中的位置都是不同的(至少2K,XP,2003基本上都不一樣),所以才有很多寫人SHELLCODE的時(shí)候,位置計(jì)算個(gè)半天..這樣來說的話,在我們編譯EXE的時(shí)候就不可能確定某個(gè)函數(shù)的地址.要執(zhí)行這個(gè)函數(shù),必須找到他的入口地址,而這個(gè)地址就由系統(tǒng)在加載PE文件的時(shí)候幫你"填空",這動態(tài)的完成函數(shù)地址的填充也就是"動態(tài)連接"這個(gè)名詞的由來.
現(xiàn)在我簡單的模擬一下系統(tǒng)轉(zhuǎn)載PE文件并給出函數(shù)地址的步驟,首先,我們給出一個(gè)PE文件中的導(dǎo)入表:
代碼?
Offset????? 0? 1? 2? 3? 4? 5? 6? 7?? 8? 9? A? B? C? D? E? F
00000120?? 58 11 00 00 00 00 00 00?
??????????????????????????????????? 50 11 00 00 00 00 00 00?? X.......P.......
00000130?? 00 00 00 00 6E 11 00 00? 20 11 00 00
??????????????????????????????????????????????? 00 00 00 00?? ....n... .......
00000140?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
00000150?? 58 11 00 00 00 00 00 00
?
?
可以看到,這個(gè)表被分為四個(gè)部分,其中中間兩個(gè)等長為0x14的兩段就是導(dǎo)入表中的IMAGE_IMPORT_DESCRIPTOR結(jié)構(gòu),該結(jié)構(gòu)如下:
代碼?
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
?? union {
?????? DWORD?? Characteristics;???????????
?????? DWORD?? OriginalFirstThunk;???????????? //指向一個(gè)"函數(shù)列表的指針結(jié)構(gòu)".
?? };
?? DWORD?? TimeDateStamp;????????????????????? //暫時(shí)可以看作沒用,0
?? DWORD?? ForwarderChain;???????????????????? //暫時(shí)可以看作沒用,0
?? DWORD?? Name;?????????????????????????????? //指向一個(gè)DLL,這個(gè)結(jié)構(gòu)里面的函數(shù)必須都是這個(gè)DLL里面的
?? DWORD?? FirstThunk;???????????????????????? //指向一個(gè)IAT表,最后操作系統(tǒng)修改的就是這個(gè)
} IMAGE_IMPORT_DESCRIPTOR;
?
?
注意,該結(jié)構(gòu)也必須有N+1個(gè),因?yàn)槲覀冎恍枰粋€(gè)函數(shù)"URLDownloadToFile",所以我們只有這一個(gè)結(jié)構(gòu),第二個(gè)結(jié)構(gòu)是全0的.表示結(jié)束.
這個(gè)"函數(shù)列表指針結(jié)構(gòu)"就是IMAGE_THUNK_DATA32結(jié)構(gòu):
代碼?
typedef struct _IMAGE_THUNK_DATA32 {
?? union {
?????? PBYTE?? ForwarderString;
?????? PDWORD? Function;
?????? DWORD? Ordinal;
?????? PIMAGE_IMPORT_BY_NAME? AddressOfData;
?? } u1;
} IMAGE_THUNK_DATA32;
?
?
他只有一個(gè)雙字類型的值,這個(gè)值如果是1XXXXXXXH的,那么說明該函數(shù)是一序號方式導(dǎo)入的,序號就是除了1外的剩下的7位,如果是0XXXXXXXH的,那么這個(gè)除了0外的7位就是作為一個(gè)虛擬地址指向這個(gè)函數(shù)的名字.
關(guān)于什么是序號導(dǎo)入什么是名字導(dǎo)入,我就不說了,這些涉及到導(dǎo)出表的概念.本文不需要.
假設(shè)我是WINDOWS操作系統(tǒng)的PE裝載器,我從這個(gè)PE文件格式的某些參數(shù)中定位到了這個(gè)00000128H的地址是導(dǎo)入表地址,現(xiàn)在我的目的是要把"58 11 00 00"這個(gè)地址替換為正確的函數(shù)地址(注意,是00000120H處的,00000150H處的那個(gè)"58 11 00 00"是給系統(tǒng)提供"URLDownloadToFile"這個(gè)字符串位置的指針,這個(gè)地址不會變動,會變的是00000120H處的"58 11 00 00",其實(shí)00000120H處的"58 11 00 00"可以隨便設(shè)置的.).
我開始定位到了:
代碼?
Offset????? 0? 1? 2? 3? 4? 5? 6? 7?? 8? 9? A? B? C? D? E? F
??????????????????????????????????? 50 11 00 00 00 00 00 00?? X.......P.......
00000130?? 00 00 00 00 6E 11 00 00? 20 11 00 00
?
?
的地方,發(fā)現(xiàn)這個(gè)函數(shù)的位置是"50 11 00 00",相關(guān)的DLL是"6E 11 00 00",于是我找到PE文件的這個(gè)位置(是內(nèi)存中的相對位置),發(fā)現(xiàn)"50 11 00 00"位置處的IMAGE_THUNK_DATA32結(jié)構(gòu)的值是"58 11 00 00",這個(gè)值不是1開頭的,于是我用這個(gè)值作為地址查找,發(fā)現(xiàn)這個(gè)值指向的位置的內(nèi)容是"31 00 URLDownloadToFile",除去前面的兩個(gè)序號,找到了這個(gè)函數(shù)的名稱,接下來我根據(jù)在"6E 11 00 00"位置找到的字符串"URLMON.DLL",用LoadLibrary()和GetProcAddress()找到了函數(shù)"URLDownloadToFile"在內(nèi)存中的位置,假設(shè)是"XX XX XX XX",然后把"XX XX XX XX",填入到"20 11 00 00"指向的位置中...完畢.
這樣來說,大家就明白了,URLDownloadToFile這個(gè)函數(shù)的存放位置應(yīng)該根據(jù)"50 11 00 00"(確切的說應(yīng)該是"50 11 00 00"指向的位置的指針)和"6E 11 00 00"來確定(確定這個(gè)函數(shù)存在的DLL).
?
本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/2195/archive/2004/08/31/90110.aspx
[THIS IS JMP S2]
現(xiàn)在我們再回頭整理一下整個(gè)過程...結(jié)合這張表:
代碼?
Offset????? 0? 1? 2? 3? 4? 5? 6? 7?? 8? 9? A? B? C? D? E? F
00000000?? 4D 5A 5B 00 00 00 00 00? 00 00 00 00 00 00 00 00?? MZ[.............
00000010?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
00000020?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
00000030?? 00 00 00 00 00 00 00 00? 00 00 00 5D 40 00 00 00?? ...........]@...
00000040?? 50 45 00 00 4C 01 02 00? 00 00 00 00 00 00 00 00?? PE..L...........
00000050?? 00 00 00 00 70 00 0F 01? 0B 01 00 00 00 02 00 00?? ....p...........
00000060?? 00 00 00 00 00 00 00 00? 79 01 00 00 00 00 00 00?? ........y.......
00000070?? 00 00 00 00 00 00 40 00? 00 10 00 00 00 02 00 00?? ......@.........
00000080?? 00 00 00 00 00 00 00 00? 04 00 00 00 00 00 00 00?? ................
00000090?? 00 30 00 00 00 02 00 00? 00 00 00 00 02 00 00 00?? .0..............
000000A0?? 00 01 00 00 00 00 00 00? 00 01 00 00 00 10 00 00?? ................
000000B0?? 00 00 00 00 02 00 00 00? 00 00 00 00 00 00 00 00?? ................
000000C0?? 28 11 00 00 28 00 00 00? 00 00 00 00 00 00 00 00?? (...(...........
000000D0?? 00 02 00 00 00 10 00 00? 00 02 00 00 00 01 00 00?? ................
000000E0?? 00 00 00 00 00 00 00 00? 00 00 00 00 60 00 00 60?? ............`..`
000000F0?? 00 00 00 00 00 00 00 00? 02 00 00 00 00 20 00 00?? ............. ..
00000100?? 00 02 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
00000110?? 00 00 00 00 60 00 00 60? 00 00 00 00 00 00 00 00?? ....`..`........
00000120?? 58 11 00 00 00 00 00 00? 50 11 00 00 00 00 00 00?? X.......P.......
00000130?? 00 00 00 00 6E 11 00 00? 20 11 00 00 00 00 00 00?? ....n... .......
00000140?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
00000150?? 58 11 00 00 00 00 00 00? 5B 00 00 00 00 00 00 00?? ........[.......
00000160?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
00000170?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
00000180?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
00000190?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
000001A0?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
000001B0?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
000001C0?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
000001D0?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
000001E0?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
000001E0?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 5D?? ...............]
?
?
這張PE文件數(shù)據(jù)圖就是一個(gè)很大的"填空",除去重要的數(shù)據(jù)部分,我們可以隨便寫入數(shù)據(jù)的地方有2個(gè)(也就是兩個(gè)大掛號掛起來的中間).
第一個(gè)是從地址00000002開始的,到地址0000003B結(jié)束的56字節(jié).
第二個(gè)是從地址00000160開始的,到PE文件結(jié)尾的160字節(jié).(也可以從000000158開始,這樣就有168字節(jié))
因?yàn)槲覀兊某绦蚝芏?所以第二個(gè)168字節(jié)基本上可以滿足要求全部,就不需要第一個(gè)56字節(jié)的數(shù)據(jù)了.把數(shù)據(jù)和在一起也方便呢,不是么?^_^.
那這些地方具體填寫些什么東西呢?大致來說分為三個(gè)部分:
1.導(dǎo)入表,包括"URLDownloadToFile"這個(gè)函數(shù)的字符串和"URLMON.DLL"這個(gè)DLL的字符串.
2.文件的可執(zhí)行機(jī)器碼.
3.函數(shù)需要的數(shù)據(jù).
?
首先是導(dǎo)入表,根據(jù)上一節(jié)說的那些,我們可以很容易的判斷出這個(gè)"URLDownloadToFile"該填在"58 11 00 00"的位置.當(dāng)然你可以改這個(gè)值,這個(gè)值只是我寫的.總之你想吧這個(gè)導(dǎo)入表放在什么位置,這個(gè)"58 11 00 00"就要指向這個(gè)位置.于是我們在PE文件的00000158位置寫入"31 00 URLDownloadToFile"字符串,前面兩個(gè)16進(jìn)制是序號是給轉(zhuǎn)載器提供信息作為在DLL中導(dǎo)出地址的依據(jù).
(對了,這里說明一個(gè)問題,這篇文檔也注釋了很多"注意點(diǎn)",為什么呢,仔細(xì)看看這些注意點(diǎn),發(fā)現(xiàn)都是和位置有關(guān)的,那是因?yàn)?PE文件中的絕大多數(shù)的地址,都是采用文件加載后內(nèi)存中的地址的,這樣一方面加快了加載速度,另外一方面也省了不少加載器的工作,比如這個(gè)"58 11 00 00"的地址,因?yàn)槲覀兗虞d的位置是1000H,所以根據(jù)這個(gè)位置,我們在文件中的位置就是158H,這里要申明的一點(diǎn),并不是所有的地址都可以這么計(jì)算的,因?yàn)槲覀冊赑ointerToRawData那里設(shè)置了100H,為的就是這樣方便的計(jì)算相對地址,對于其他的PE文件,如果要根據(jù)這種內(nèi)存地址計(jì)算出PE文件地址,還不是這么簡單是事情,^_^..當(dāng)然,網(wǎng)絡(luò)上也有很多這種轉(zhuǎn)換函數(shù),RVA到OFFSET的)
然后把URLMON.DLL這個(gè)字符串填入"6E 11 00 00"指向的地址,當(dāng)然這個(gè)值也是可以變的.
最后,我們要用筆記錄一下最后這個(gè)函數(shù)被導(dǎo)出的地址的存放處,也就是"20 11 00 00".
[注意,以上的這些操作都和IMAGE_IMPORT_DESCRIPTOR結(jié)構(gòu)和IMAGE_THUNK_DATA32結(jié)構(gòu)相關(guān),看不明白的多看看這兩個(gè)結(jié)構(gòu)]
接下來是可執(zhí)行碼.我們的目的很簡單,只要這個(gè)PE文件能下載文件就行,所以我們只要調(diào)用URLDownloadToFile函數(shù)即可,寫一小段匯編碼(還記得前面說過的URLDownloadToFile的調(diào)用方法嗎,花了點(diǎn)筆墨的那個(gè)):
代碼?
PUSH 0???????????????? ;6A 00
PUSH 0???????????????? ;6A 00
PUSH XXXXXXXX????????? ;68 XXXXXXXX
PUSH XXXXXXXX????????? ;68 XXXXXXXX
PUSH 0???????????????? ;6A 00
CALL XXXXXXXX????????? ;E8 XXXXXXXX
?
?
由于函數(shù)的調(diào)用是符合PASCAL調(diào)用,也就是STDCALL,自右向左壓棧,所以我們的參數(shù)也是最后一個(gè)先入棧.最后CALL出這個(gè)URLDownloadToFile函數(shù).
前兩個(gè)XXXXXXXX地址是兩個(gè)字符串的地址,也就是URLDownloadToFile函數(shù)的兩個(gè)重要參數(shù),最后一個(gè)XXXXXXXX是這個(gè)函數(shù)在內(nèi)存中的地址(操作系統(tǒng)已經(jīng)幫我們填充了,還記得上面說的那個(gè)用筆記錄的"20 11 00 00"么?)
主要的代碼就是這么多,可是不幸的事情發(fā)生了,當(dāng)我用WINHEX把這些代碼填入PE框架并且保存的時(shí)候,居然被殺毒軟件刪除了!!!!他們把這個(gè)看作病毒????想來寫病毒原來是這么容易的事情(.....一_一.)....
幸好有備份(如果沒有,我可是要哭死了.....),我修改了這些代碼,加入了一些垃圾(比如MOV EAX,1之列的)...最終的成品代碼是:
代碼?
B8 01000000??? ;mov eax,1
6A 00????????? ;push 0
6A 00????????? ;push 0
68 D0114000??? ;push D0114000 ;指向你保存的本地路徑字符串的位置,本文中是"c:\\gl123\\00204.jpg",注意是雙杠.
68 A0114000??? ;push A0114000 ;指向要下載的URL字符串保存的位置
6A 00????????? ;push 0
E8 02000000??? ;call 02000000 ;也就是呼叫下兩個(gè)字節(jié)的地址,這是機(jī)器中調(diào)用函數(shù)的通常做法
C9???????????? ;leave
C3???????????? ;ret
FF25 20114000? ;jmp 20114000? ;這個(gè)跳轉(zhuǎn)地址就是"20 11 00 00",至于那個(gè)"40",
????????????????????????????? ;就是程序的建議起始加載地址"00400000".另外,這里是仿機(jī)器格式.
00
00
00
00
?
?
將他們寫入哪里呢?這個(gè)就隨便你了,不過請翻翻上面說的,有個(gè)地址是(也就是注意1所在的位置)AddressOfEntryPoint:這個(gè)就是用來定位你代碼的執(zhí)行入口的,我們就放在導(dǎo)入表的后面,也就是"00000179H"的位置.
最后就是那兩個(gè)字符串的地址了,我們在程序中已經(jīng)給出
代碼?
68 D0114000
68 A0114000
?
?
那這兩個(gè)字符串的位置就確定了,一個(gè)是"000001D0H",我們要下載的文件地址"/Article/UploadFiles/200408/20040818230017329.JPG"就是保存到這里..這里我每個(gè)分配了48字節(jié)存儲區(qū)域,大家也可以根據(jù)具體需要設(shè)置.別忘了還有dos頭部可以保存56字節(jié)的空白可以寫數(shù)據(jù),如果需要的話,修改指向就是.
對于上面的這一堆廢話,我的目的是想讓大家明白,而故意介紹的格式,即是說,如果讓你換做其他的API函數(shù)也能輕易的調(diào)用,而不是局限于URLDownloadToFile.^_^...比如那些...那些...功能啊....(我可沒說啊...嘿嘿)..
OK,這個(gè)PE文件最后的成形PE框架是這樣的:
代碼?
Offset????? 0? 1? 2? 3? 4? 5? 6? 7?? 8? 9? A? B? C? D? E? F
00000000?? 4D 5A 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? MZ..............
00000010?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
00000020?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
00000030?? 00 00 00 00 00 00 00 00? 00 00 00 00 40 00 00 00?? ............@...
00000040?? 50 45 00 00 4C 01 02 00? 00 00 00 00 00 00 00 00?? PE..L...........
00000050?? 00 00 00 00 70 00 0F 01? 0B 01 00 00 00 02 00 00?? ....p...........
00000060?? 00 00 00 00 00 00 00 00? 79 01 00 00 00 00 00 00?? ........y.......
00000070?? 00 00 00 00 00 00 40 00? 00 10 00 00 00 02 00 00?? ......@.........
00000080?? 00 00 00 00 00 00 00 00? 04 00 00 00 00 00 00 00?? ................
00000090?? 00 30 00 00 00 02 00 00? 00 00 00 00 02 00 00 00?? .0..............
000000A0?? 00 01 00 00 00 00 00 00? 00 01 00 00 00 10 00 00?? ................
000000B0?? 00 00 00 00 02 00 00 00? 00 00 00 00 00 00 00 00?? ................
000000C0?? 28 11 00 00 28 00 00 00? 00 00 00 00 00 00 00 00?? (...(...........
000000D0?? 00 02 00 00 00 10 00 00? 00 02 00 00 00 01 00 00?? ................
000000E0?? 00 00 00 00 00 00 00 00? 00 00 00 00 60 00 00 60?? ............`..`
000000F0?? 00 00 00 00 00 00 00 00? 02 00 00 00 00 20 00 00?? ............. ..
00000100?? 00 02 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
00000110?? 00 00 00 00 60 00 00 60? 00 00 00 00 00 00 00 00?? ....`..`........
00000120?? 58 11 00 00 00 00 00 00? 50 11 00 00 00 00 00 00?? X.......P.......
00000130?? 00 00 00 00 6E 11 00 00? 20 11 00 00 00 00 00 00?? ....n... .......
00000140?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
00000150?? 58 11 00 00 00 00 00 00? 31 00 55 52 4C 44 6F 77?? X.......1.URLDow
00000160?? 6E 6C 6F 61 64 54 6F 46? 69 6C 65 41 00 00 75 72?? nloadToFileA..ur
00000170?? 6C 6D 6F 6E 2E 64 6C 6C? 00 B8 01 00 00 00 6A 00?? lmon.dll.?...j.
00000180?? 6A 00 68 D0 11 40 00 68? A0 11 40 00 6A 00 E8 02?? j.h?@.h?@.j.?
00000190?? 00 00 00 C9 C3 FF 25 20? 11 40 00 00 00 00 00 00?? ...擅% .@......
000001A0?? 68 74 74 70 3A 2F 2F 77? 77 77 2E 73 65 72 67 65?? http://www.serge
000001B0?? 61 75 72 61 2E 6E 65 74? 2F 54 47 50 2F 30 30 32?? aura.net/TGP/002
000001C0?? 2F 69 6D 61 67 65 73 2F? 30 34 2E 6A 70 67 00 00?? /images/04.jpg..
000001D0?? 43 3A 5C 5C 47 4C 31 32? 33 5C 5C 30 30 32 30 34?? C:\\GL123\\00204
000001E0?? 2E 4A 50 47 00 00 00 00? 00 00 00 00 00 00 00 00?? .JPG............
000001F0?? 00 00 00 00 00 00 00 00? 00 00 00 00 00 00 00 00?? ................
?
?
簡單的運(yùn)行一下這個(gè)PE文件,圖片已經(jīng)被下載到C盤的GL123文件夾,說明我們的工作還是成功的哈.(哇,好sex的MM啊,口水流啊流.....)
六.包裝
到這里開始,我們的EXE是有了,現(xiàn)在開始DEBUG出場,我們的計(jì)劃是用E命令寫入整個(gè)PE文件數(shù)據(jù),然后用W命令保存到臨時(shí)文件中,于是就成就了這個(gè)原始BAT文件:
代碼?
;echo off
;DEBUG<%~s0>nul2>nul
;GOTO BEGIN
E 100 4D 5A 00 00 00 00 00 00 00 00 00 00 00 00 00 00
E 110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
......
......這里省略若干
......
RCX
200
N E:\tmp\tmp99.TMP
W
Q
:BEGIN
rename E:\tmp\tmp99.TMP tmp99.EXE>nul2>nul
call E:\tmp\tmp99.EXE
del E:\tmp\tmp99.EXE>nul2>nul
?
?
不過這樣很不美觀...于是我又想了一個(gè)辦法優(yōu)化,用F命令向填充512個(gè)00,然后再在相對位置寫入需要的數(shù)據(jù),于是乎就生成了下載bat腳本的bate1版本,這個(gè)是完整的批處砦募?
代碼?
;ECHO OFF
;DEBUG<%~s0>nul2>nul
;GOTO BEGIN
E 100 4D 5A
F 102 2FF 00
E 13C 40 00 00 00 50 45 00 00 4C 01 02
E 154 70 00 0F 01 0B 01 00 00 00 02
E 168 79 01
E 176 40 00 00 10 00 00 00 02
E 188 04 00 00 00 00 00 00 00 00 30 00 00 00 02
E 19C 02 00 00 00 00 01
E 1A9 01 00 00 00 10 00 00 00 00 00 00 02
E 1C0 28 11 00 00 28
E 1D1 02 00 00 00 10 00 00 00 02 00 00 00 01
E 1EC 60 00 00 60
E 1F8 02 00 00 00 00 20 00 00 00 02
E 214 60 00 00 60
E 220 58 11 00 00 00 00 00 00 50 11
E 234 6E 11 00 00 20 11
E 250 58 11 00 00 00 00 00 00 31 00 55 52 4C 44 6F 77
E 260 6E 6C 6F 61 64 54 6F 46 69 6C 65 41 00 00 75 72
E 270 6C 6D 6F 6E 2E 64 6C 6C 00 B8 01 00 00 00 6A 00
E 280 6A 00 68 D0 11 40 00 68 A0 11 40 00 6A 00 E8 02
E 293 C9 C3 FF 25 20 11 40
E 2A0 "/Article/UploadFiles/200408/20040818230026641.jpg"
E 2D0 "C:\\GL123\\00204.JPG"
RCX
200
N E:\tmp\tmp99.TMP
W
Q
:BEGIN
rename E:\tmp\tmp99.TMP tmp99.EXE>nul2>nul
call E:\tmp\tmp99.EXE
del E:\tmp\tmp99.EXE>nul2>nul
?
?
運(yùn)行這個(gè)BAT,在很快的一閃而過的屏幕后,圖片被安然的下載到我的硬盤上....大功告成了...高興那..
這篇文檔這里要說的東西已經(jīng)全部說完了,用上面這個(gè)批處理文檔就可以實(shí)現(xiàn)任意的下載網(wǎng)絡(luò)上的東西,但是要注意的幾點(diǎn)就是,下載地址的URL的長度,如果你覺得很長,那么要調(diào)整PE格式來達(dá)到兼容你URL長度的目的,保存地址基本上和URL是一樣的.
七.小節(jié)
倘若這樣結(jié)束的話,大多數(shù)人也沒有意見,可惜這篇文檔就顯得不完整了...為了讓這記錄式的文檔更加完整,我就用下載MM這個(gè)事件作示例,來演示如何用上面的這個(gè)BAT實(shí)現(xiàn)批量下載的功能,作為對這個(gè)文檔的小節(jié),以慰岌各位看官辛勤的雙眼.(...^_^)
首先給出整個(gè)批處理代碼:
代碼?
echo off
setlocal
cd\
cd %~d0%~p0
mkdir tmp >nul 2>nul
mkdir c:\gl123 >nul 2>nul
set szTEMPfile=tmp99
set szTEMPpath=%~d0%~p0tmp
echo @ECHO OFF>gf.bat
echo SETLOCAL>>gf.bat
echo cd\>>gf.bat
echo cd %%^~d0%%^~p0>>gf.bat
echo SET szURLfolder=00%%1>>gf.bat
echo SET szURLfolder=%%szURLfolder:^~-3%%>>gf.bat
echo SET szURLfile=0%%2>>gf.bat
echo SET szURLfile=%%szURLfile:^~-2%%>>gf.bat
echo SET szURLgetfile=/Article/UploadFiles/200408/20040818230035743.jpg>>gf.bat
echo SET szLOCALfile=C:\\GL123\\%%szURLfolder%%%%szURLfile%%.JPG>>gf.bat
echo ECHO;echo off^>dl.bat>>gf.bat
echo ECHO;DEBUG^^^<%%%%^^^~s0^^^>nul2^^^>nul^>^>dl.bat>>gf.bat
echo ECHO;GOTO RUN^>^>dl.bat>>gf.bat
echo ECHO E 100 4D 5A^>^>dl.bat>>gf.bat
echo ECHO F 102 2FF 00^>^>dl.bat>>gf.bat
echo ECHO E 13C 40 00 00 00 50 45 00 00 4C 01 02^>^>dl.bat>>gf.bat
echo ECHO E 154 70 00 0F 01 0B 01 00 00 00 02^>^>dl.bat>>gf.bat
echo ECHO E 168 79 01^>^>dl.bat>>gf.bat
echo ECHO E 176 40 00 00 10 00 00 00 02^>^>dl.bat>>gf.bat
echo ECHO E 188 04 00 00 00 00 00 00 00 00 30 00 00 00 02^>^>dl.bat>>gf.bat
echo ECHO E 19C 02 00 00 00 00 01^>^>dl.bat>>gf.bat
echo ECHO E 1A9 01 00 00 00 10 00 00 00 00 00 00 02^>^>dl.bat>>gf.bat
echo ECHO E 1C0 28 11 00 00 28^>^>dl.bat >>gf.bat
echo ECHO E 1D1 02 00 00 00 10 00 00 00 02 00 00 00 01^>^>dl.bat>>gf.bat
echo ECHO E 1EC 60 00 00 60^>^>dl.bat>>gf.bat
echo ECHO E 1F8 02 00 00 00 00 20 00 00 00 02^>^>dl.bat>>gf.bat
echo ECHO E 214 60 00 00 60^>^>dl.bat>>gf.bat
echo ECHO E 220 58 11 00 00 00 00 00 00 50 11^>^>dl.bat>>gf.bat
echo ECHO E 234 6E 11 00 00 20 11^>^>dl.bat>>gf.bat
echo ECHO E 250 58 11 00 00 00 00 00 00 31 00 55 52 4C 44 6F 77^>^>dl.bat>>gf.bat
echo ECHO E 260 6E 6C 6F 61 64 54 6F 46 69 6C 65 41 00 00 75 72^>^>dl.bat>>gf.bat
echo ECHO E 270 6C 6D 6F 6E 2E 64 6C 6C 00 B8 01 00 00 00 6A 00^>^>dl.bat>>gf.bat
echo ECHO E 280 6A 00 68 D0 11 40 00 68 A0 11 40 00 6A 00 E8 02^>^>dl.bat>>gf.bat
echo ECHO E 293 C9 C3 FF 25 20 11 40^>^>dl.bat>>gf.bat
echo ECHO E 2A0 "%%szURLgetfile%%"^>^>dl.bat>>gf.bat
echo ECHO E 2D0 "%%szLOCALfile%%"^>^>dl.bat>>gf.bat
echo ECHO RCX^>^>dl.bat>>gf.bat
echo ECHO 200^>^>dl.bat>>gf.bat
echo ECHO N %szTEMPpath%\%szTEMPfile%.TMP^>^>dl.bat>>gf.bat
echo ECHO W^>^>dl.bat>>gf.bat
echo ECHO Q^>^>dl.bat>>gf.bat
echo ECHO :RUN^>^>dl.bat>>gf.bat
echo ECHO rename %szTEMPpath%\%szTEMPfile%.TMP %szTEMPfile%.EXE^^^>nul2^^^>nul^>^>dl.bat>>gf.bat
echo ECHO call %szTEMPpath%\%szTEMPfile%.EXE^>^>dl.bat>>gf.bat
echo ECHO del %szTEMPpath%\%szTEMPfile%.EXE^^^>nul2^^^>nul^>^>dl.bat>>gf.bat
echo ECHO DOWNLOAD %%szURLgetfile%% ==^^^> %%szLOCALfile%%>>gf.bat
echo CALL dl.bat>>gf.bat
echo ECHO ...OK!>>gf.bat
echo ENDLOCAL>>gf.bat
:echo @ECHO ON>>gf.bat
for /l %%i in (1,1,162) do for /l %%j in (1,1,12) do call gf.bat %%i %%j
del gf.bat>nul 2>nul
del dl.bat>nul 2>nul
rmdir tmp>nul 2>nul
echo ALL OK!
endlocal
echo on
?
?
如果看懂了前面的那個(gè)當(dāng)個(gè)下載的批處理代碼,那這個(gè)基本上是沒有問題的了.
這個(gè)批處理的工作步驟:
1.運(yùn)行后會在你c盤建立一個(gè)"gl123"的文件夾,用來保存下載的圖片的(這也是唯一的缺點(diǎn),我無法寫成自定義文件夾...:( )
2.接著會在當(dāng)前目錄派生出兩個(gè)子批處理文件和一個(gè)"tmp"的臨時(shí)目錄...
3.之后開始循環(huán)下載所有的圖片,并顯示進(jìn)度,完成后顯示"ALL OK".
4.刪除所有的臨時(shí)文件.
對這個(gè)bat混和上一個(gè)中的一些地方,我統(tǒng)一解釋一下:
1.RCX是DEBUG的寫寄存器CX命令,把我們要寫入的文件大小賦值給他,然后調(diào)用N命令給出文件名后用W寫入或者L加載,
2.Q后面要保留回車(你總不想bat文件回不來吧...),
3.>nul和2>nul是說把輸出和錯(cuò)誤輸出全部屏蔽...你也不想在下載的時(shí)候出現(xiàn)"1 file(s) copy.."這樣的提示吧..
4.如果是特殊字符要在前面加上轉(zhuǎn)義的"^"符號方可寫入文件
5.對于文件地址遞增類型含0的地址,比如http://www.xxx.com/0001.jpg,http://www.xxx.com/0002.jpg......這樣的格式,很多人用判斷這個(gè)值是小于9,加三個(gè)0,大于9,小于99,就加2個(gè)0,大于99,小于999,就加三個(gè)0.....而我的方法是統(tǒng)一在這個(gè)數(shù)值前面加上足夠的0,然后再截取整個(gè)字符串的最后4位,相對來說比較省代碼.
6.for可以嵌套使用,構(gòu)成N重循環(huán),但是有個(gè)缺點(diǎn),FOR內(nèi)不可以用SET...(具體看幫助,總之很麻煩...一_一..這也是我用多個(gè)BAT實(shí)現(xiàn)的原因)
7.用批處理文件處理文件部分(包括新建和刪除目錄)之前最好先進(jìn)入當(dāng)前目錄一次,本批處理用cd\和cd %~d0%~p0來完成
8.cd %~d0%~p0中的%~d0環(huán)境變量是對%0變量的擴(kuò)展,擴(kuò)展為當(dāng)前驅(qū)動器盤符,%~p0是擴(kuò)展為當(dāng)前目錄,其他的就看window命令行幫助文檔.
9.養(yǎng)成習(xí)慣用SETLOCAL和ENDLOCAL包裹整個(gè)批處理.
10.因?yàn)楸镜刈址趦?nèi)存中是以雙杠保存的,而在批處理中是以單杠保存的,要實(shí)現(xiàn)這個(gè)轉(zhuǎn)換必須要相當(dāng)多的代碼(批處理對文本的處理能力極弱...一_一..),所以我就沒有寫.
八.后記
其實(shí)本文的標(biāo)題完全可以改為:用批處理調(diào)用API,但是我覺得這樣太沒事找事了,畢竟調(diào)用API還是寫程序來的方便.另外為了塞飽這篇文檔,加入了相當(dāng)多的PE格式分析,雖然和本文有一點(diǎn)點(diǎn)關(guān)系...但還是覺得有點(diǎn)喧賓奪主哈...
完成了這些功能后,深刻的感受到那些在底層工作的人員是多么的辛苦啊......想來我能用VC寫程序,這已經(jīng)是一件非常幸福的四兒了...(說到這里ZV偷偷的拿出小手絹擦了擦....要成為高手!就要忍受別人不能忍受的苦,于是ZV用煙頭在手臂上....汗,很痛的,當(dāng)然沒有~~~哈哈)..
文章里用到的兩個(gè)主要的工具都是微軟歷史上的老前輩.長年不見他們活動,帶出來溜溜腳也算是對這些快被遺忘的技術(shù)的懷念(據(jù)說當(dāng)年UCDOS下的WPS就是某牛人用DEBUG寫出來的,PF的緊啊,,一_一...),其實(shí)Windows雖然不開放,還是很有意思的,呵呵.
最后,在這篇冗長的,臃腫的,夾雜著無數(shù)錯(cuò)誤,垃圾,抄襲,糊弄,陳詞濫調(diào),語言不通的1.5萬字里,的的確確包含了作者的一些心血,感謝大家花了這么多時(shí)間看到這里,如果覺得有哪怕有那么一點(diǎn)點(diǎn)的收獲,回個(gè)帖子算是對我的鼓勵(lì)吧,不然寫個(gè)"辛苦了",也是對我的一點(diǎn)點(diǎn)安慰..^_^..
九.參考文獻(xiàn)
<<Do All in Cmd Shell>> -- zzzEVAzzz (EVA還真是可憐,我多處查找這篇文檔的作者才找到是他寫的...一_一.)
<<手工打造微型Win32可執(zhí)行文件>> -- watercloud
<<Iczelion的PE教程>> -- Iczelion
<<A Tour of the Win32 Portable Executable File Format>> -- MSDN
還有一些零散的BAT和DEBUG的使用方法,就是BAIDU和GOOGLE常年搜索的獲得了.
?
全文完.
本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/2195/archive/2004/08/31/90112.aspx