• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            S.l.e!ep.¢%

            像打了激速一樣,以四倍的速度運轉,開心的工作
            簡單、開放、平等的公司文化;尊重個性、自由與個人價值;
            posts - 1098, comments - 335, trackbacks - 0, articles - 1
              C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

            作者:ZV(ZVROP)
            郵件:zvrop@163.com
            主頁:(被整頓掉老~~~~~>_<~~~~~~)
            網站:http://www.s8s8.net
            日期:2004-8-18

            轉載請保全文檔完整,謝謝

            寫的倉卒繁多,錯漏難免,還請各位給予斧正.

            有什么問題,可以給我發email.^_^...


            目錄:

            一.寫在前面的話

            二.故事的起因

            三.解決-腹稿

            四.解決-實戰

            五.解決-打造

            六.包裝

            七.小節

            八.后記

            九,參考文獻

            ?


            正文開始:


            一.寫在前面的話

            這篇文檔講述的并不是什么新鮮的技術,我只是起到將他們混和起來實現了自己需要的功能的作用,如果你對批處理和PE格式相當了解,那我的這篇文檔就權且當作瀏覽溫習吧...^_^...
            另外,由于我本人廢話比較多,這也是不想給雜志寫稿子的主要原因,給我干癟癟的3000字能講出什么來,不如這樣沒有限制的爽快(當然也沒什么報酬...一_一..),所以也為了防止你在看文章的途中睡著,請自備小錐子一把.....
            最后,這篇文檔說是用批處理下載文件,其實它包含了很多方面的知識,如果有時間,不妨一看哈,^_^,開始買瓜了..


            二.故事的起因

            最初萌發這個想法的是不久前,在論壇(廣告一下:http://www.s8s8.net)上的UNIX SHELL板塊有個會員發了一篇帖子,內容是用BASH SHELL寫的一個成批下載圖片的腳本(其實是H圖片...一_一..),接下來跟貼那個多啊...,有繁衍出PHP的,VBS的,C的,C#的,JAVA的,甚至交流到多線程,斷點續傳....引用花大哥的一句話"無語,為了MM照片,大家的動力都很足啊!"...汗~~..

            在發了一份PHP和C的代碼后(感覺我動力特足~大色狼...一_一..),覺得很簡單(因為用C或者PHP等腳本來實現文件的下載本來就是很基礎的東西)我就開始想用微軟最原始的腳本--Batch(批處理)來嘗試實現(本文標題中的"閑來無事"就是紀念此處,一_一.),這似乎有點不可思議,因為批處理幾乎沒有實現的支持網絡的功能(當然,如果你說你能用TELNET下載到文件的我是很佩服的..一_一..),但也不是完全沒有辦法,畢竟WINDOWS里面能用的東西這么多,沒有完不成的事情....在這種挑戰的勾引下,我完成了用批處理下載文件的功能....現在讓我一步一步回放我的思路,揭開用批處理下載文件的奧秘...


            三.解決-腹稿

            如果用批處理來下載文件的話,肯定會馬上想到Cscript腳本(或者是JAVA腳本),那是當然,太多的批處理腳本實現一些本身并不可能實現的功能的時候都是采用ECHO出一個其他腳本的方法來解決.可是我們的目的就在于用批處理實現下載的功能,如果要用VBS來幫忙的話不如直接寫VBS了.這個想法順即告吹....

            再來,記得以前有流行過一陣用RUNDLL32來加載DLL中的API,似乎和我們需要的目的沾邊,因為下載文件能用的API太多了,如果RUNDLL能調用,那最好不過了.于是我打開MSDN,找了一個API: URLDownloadToFile

            URLDownloadToFile函數原型:

            代碼?

            HRESULT URLDownloadToFile(?????????
            ?? LPUNKNOWN pCaller,
            ?? LPCTSTR szURL,
            ?? LPCTSTR szFileName,
            ?? DWORD dwReserved,
            ?? LPBINDSTATUSCALLBACK lpfnCB
            );
            ?

            ?


            URLDownloadToFile函數的一些信息:


            引用?

            Header Urlmon.h
            Import library Urlmon.lib
            Minimum availability Internet Explorer 3.0
            Minimum operating systems Windows NT 4.0, Windows 95
            ?

            ?

            根據這些,我們可以知道,這個API是在URLMON.DLL文件中的一個導出函數,簡單的實現了把一個文件從WEB服務器下載本機的功能,其實用這個函數還不錯的,至少它幫我們處理了斷點續傳,緩存等等的功能,比起直接使用SOCKET函數來實現或者用WININET里的函數來實現簡單多得多了.

            URLDownloadToFile有五個參數:

            第一個參數是僅當調用者是一個ActiveX對象才使用,一般為NULL.

            第二個參數就是要下載文件的目標URL,完整路徑.

            第三個是本地保存路徑,也是完整路徑

            第四個是保留,必須為0

            第五個是指向一個IBindStatusCallback接口的指針,這就類似一種回調機制,你可以參考這些來活動當前下載進度,選擇是否繼續下載等等.

            這里面我們只關心第二和第三個參數.其他的通通設置成0.(當然你寫C的時候最好設置為NULL)

            嗯,敲了點鍵盤介紹了這個函數,是因為整篇的文檔都和這個函數息息相關,有了這個函數,就可以呼叫RUNDLL32來調用它,但是很可惜,這個美好的計劃馬上也破裂了...

            我去微軟看了他們的164787號文檔(http://support.microsoft.com/default.aspx?...kb;en-us;164787),該文檔闡述了RUNDLL32的調用方式和能被他調用的函數的格式:

            它們是這么說的:


            引用?

            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.
            ?

            ?

            這個是規定的格式:


            代碼?

            void CALLBACK
            EntryPoint(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);
            ?

            ?

            很不幸,我們的URLDownloadToFile小兄弟并沒有符合這些條件,被RUNDLL32無情的拋棄了(汗一滴..)...但是我們并沒有因此而嫌棄它(汗一滴AGAIN..),畢竟,在后來實現的過程里,它是為我們的工作省下了不少功夫.

            到此,用RUNDLL32運行計劃流產....(寒...)

            想了一根煙功夫,現在URLDownloadToFile有了,怎么才能調用這個函數呢?總不能模仿匯編PUSH 5個參數進棧,然后CALL吧,那這個函數的地址還要用LoadLibrary()和GetProcAddress()計算得來,那這兩個函數的地址.....還是放棄...等等,如果用一個EXE來實現的話就簡單很多了(至少EXE是不需要任何解釋器的),對,寫一個EXE來下載文件.可我們的目的是用BAT來下載呢,BAT文件能包裹EXE的數據嗎?答案是肯定的...往下看..

            記得以前看過一篇文檔<<Do All in Cmd Shell>>里面介紹過一種方法.先賣個關子.大家都知道,如果用ECHO加上重定向符來寫文件的話,只能寫入ASCII的一部分,也就可以顯示出來的那些ASCII(也就是ASCII值小于128的那些),對于那些無法顯示的字符就沒有辦法了.但是這讓我們想起一個工具,一個微軟歷史上同樣古老的,批處理的兄弟--DEBUG!

            現在思路清晰了:可以讓批處理把ECHO不能顯示的字符轉化為16進制數據(比如EXE中的那些數據)保存在批處理中,然后用DEBUG寫道文件里,最后用BAT調用生成的EXE,下載文件!(想完了這里,我感覺還是太麻煩,不知道哪位牛人對這個實現還有什么更加簡單的辦法嗎??)


            四.解決-實戰

            倘若就此編寫一個可下載文件的EXE,然后直接用BAT包裹,定然會被同行恥笑,不單是因為那幾千個字節的數據拖著大大臃腫的BAT文件,更加讓為這種簡單的想法立刻現形,為了不達到這些負面效果,也為了讓這篇文檔不至于干癟癟的讓人感覺沒什么看頭(事實上是因為早些時候看過watercloud的一篇大作感悟頗深),我決定手工寫一串16進制代碼來代替機器編譯的EXE.既美觀了界面,又增強了技術性.....(一_一...簡直是在賣作...)

            現在當務之急是要一個可以下載文件的EXE程序,實現這個目標只要一個URLDownloadToFile即可,放在最后實現,先來寫一個PE框架:大家都知道PE文件的格式吧,不懂的就去看看那個著名的電信黑客羅某某的書.(Who!?...~)

            先給出我們的PE框架,基于XP的FileAlignment對齊大小最小就支持到0x200(也就是10進制的512字節,以下有在前面加上0x的都表示16進制數值),我們的框架就打出512字節(注意,我下面留有空白表示各個PE部分,結合下面的文檔,大家方便理解),這個框架里沒有任何的代碼或者數據:

            (ZV友情提示:下面是最枯燥的部分,各位手握錐子,要有一不怕苦,二不怕痛的精神看完它....)
            (如果定力不高的朋友,或者堆PE文件再熟悉不過的朋友,可以字節轉到"JMP S1"處往下看.)
            (如果只想知道到底怎么回事,或者對這篇作文報瀏覽態度的朋友,可以直接轉到"JMP S2"處繼續瀏覽)
            (睡著了的繼續睡覺....)


            代碼?

            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格式文件可以分為這三個部分(就是上述框架中用"=="分割的三個部分):


            引用?

            ++++++++++++++++++++++++
            +DOS信息部分????????? +
            ++++++++++++++++++++++++

            ++++++++++++++++++++++++
            +PE信息部分??????????? +
            ++++++++++++++++++++++++

            ++++++++++++++++++++++++
            +數據部分????????????? +
            ++++++++++++++++++++++++
            ?

            ?


            下面來簡單介紹每一部分的結構,首先的"DOS信息部分":


            引用?

            +++++++++++++++++++++++++++++++++++++++++++++
            +? +++++++++++++++++++++++++++++++++++++++? +
            +? +[DOS文件頭][0x40]??????????????????? +? +
            +? +++++++++++++++++++++++++++++++++++++++? +
            +????????????????????????????????????????? + <==DOS信息部分
            +? +++++++++++++++++++++++++++++++++++++++? +
            +? +[DOS塊][0x70,可變]????????????????? +? +
            +? +++++++++++++++++++++++++++++++++++++++? +
            +++++++++++++++++++++++++++++++++++++++++++++
            ?

            ?

            這部分我覺得是最冗余的地方,首先DOS文件頭的結構:


            代碼?

            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下運行這個PE文件必須的結構,比如看注解就明白,什么代碼初始化堆棧段,初始化堆棧指針,入口IP,CS等等,都是在WIN32上沒有用的東西,我就不翻譯拉,這些都是說DOS下的,如果這個PE文件一開始就打定在WINDOWS下運行,這些亂寫都無所謂,你甚至可以把你的名字都寫進去(.....一_一..).當然,你這么作后這個文件就不能在DOS下運行了..不然當機是幾乎可以肯定的....(寒....).

            需要記的除了e_lfanew是指向PE頭的指針外還要記得這個DOS文件頭結構長0x40,也就是64個字節.還有第一個參數e_magic,這個地方永遠是"0x40 0x5a",也就是字符的"MZ".

            DOS塊部分保存的就是一段DOS下可以執行的代碼,比如現在大多編譯器就簡單的輸出一個"This program cannot be run in DOS mode"的字符串,和"DOS信息部分"一樣,如果你不打算在DOS執行這個EXE文件,那么這里完全可以刪除,為什么?因為WIN32的PE裝載器只關心"DOS信息部分"的e_lfanew指向的而已.


            本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/2195/archive/2004/08/31/90107.aspx


            綜上所述,"DOS信息部分"對應框架的代碼為:

            代碼?

            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個字節"40000000"也就是00000040H(下面如果直接在數值后加"H"的即表示為16進制)是指向他末尾的指針,也就是說明,我們把"DOS塊"的部分給去掉了.

            接下來是"PE信息部分",他的結構可以用下面的圖來表示:


            引用?

            +++++++++++++++++++++++++++++++++++++++++++++
            +? +++++++++++++++++++++++++++++++++++++++? +
            +? +[PE標志][0x04]????????????????????? +? +
            +? +++++++++++++++++++++++++++++++++++++++? +
            +????????????????????????????????????????? + <==PE信息部分
            +? +++++++++++++++++++++++++++++++++++++++? +
            +? +[PE文件頭][0x18]??????????????????? +? +
            +? +++++++++++++++++++++++++++++++++++++++? +
            +????????????????????????????????????????? +
            +? +++++++++++++++++++++++++++++++++++++++? +
            +? +[自定義數據結構][0x0e]????????????? +? +
            +? +++++++++++++++++++++++++++++++++++++++? +
            +++++++++++++++++++++++++++++++++++++++++++++
            ?

            ?

            整個"PE信息部分"結構是這樣的:


            代碼?

            typedef struct _IMAGE_NT_HEADERS {
            ?DWORD Signature;????????????????????? //"PE標志"段,總是"PE00"
            ?IMAGE_FILE_HEADER FileHeader;???????? //"PE文件頭"段,指向IMAGE_FILE_HEADER結構
            ?IMAGE_OPTIONAL_HEADER OptionalHeader; //"自定義數據"段,指向IMAGE_OPTIONAL_HEADER結構
            } IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;
            ?

            ?

            IMAGE_FILE_HEADER結構(PE文件頭)和IMAGE_OPTIONAL_HEADER結構如下:


            代碼?

            typedef struct _IMAGE_FILE_HEADER {
            ?WORD Machine;???????????????????????? //運行平臺,386的話是104CH
            ?WORD NumberOfSections;??????????????? //文件節數目,最少為2
            ?DWORD TimeDateStamp;????????????????? //文件創建時間,隨便設置(不過為了最后生成方便,隨便設置的地方最好都設置為0)
            ?DWORD PointerToSymbolTable;?????????? //這里兩項記用于調試,也隨便設置
            ?DWORD NumberOfSymbols;
            ?WORD SizeOfOptionalHeader;??????????? //下面那個IMAGE_OPTIONAL_HEADER結構的長度,一般為000EH(包括16個IMAGE_DATA_DIRECTORY結構),我們只要2個結構,所以設置為0070H
            ?WORD Characteristics;???????????????? //文件屬性,PE文件是010H,DLL的話是210H
            } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
            ?

            ?

            IMAGE_FILE_HEADER說明了PE文件的基本運行信息,可是光靠這短短的結構并不能滿足我們的需要,畢竟微軟設計的東西還是考慮的很周全的,于是在它下面跟上了一個長長的結構(OptionHeader結構)來作為附加信息供給系統.


            代碼?

            OptionHeader結構(自定義數據結構)定義如下:
            typedef struct _IMAGE_OPTIONAL_HEADER {
            ?WORD??? Magic;??????????????????????? //EXE文件的話這里是10B
            ?BYTE??? MajorLinkerVersion;?????????? //連接器版本,隨便
            ?BYTE??? MinorLinkerVersion;
            ?DWORD?? SizeOfCode;?????????????????? //所有代碼節總大小,我們就一個節,所以是512,也就是200H
            ?DWORD?? SizeOfInitializedData;??????? //....未初始化數據節....沒有這個,設置為0
            ?DWORD?? SizeOfUninitializedData;????? //....已................................
            ?DWORD?? AddressOfEntryPoint;????????? //代碼執行起始地址,注意,這個是你代碼存放的位置,[這里注意點1]
            ?DWORD?? BaseOfCode;?????????????????? //代碼段......(這里三個都是內存地址),這里是0
            ?DWORD?? BaseOfData;?????????????????? //數據段......(并非硬盤文件地址),這里是0
            ?DWORD?? ImageBase;??????????????????? //建議加載位置,通常是00400000H,9X的系統可能略小于這個值,記不得了..:(
            ?DWORD?? SectionAlignment;???????????? //內存中對齊大小,一般為1000H,也就是NT的一個內存片,4KB
            ?DWORD?? FileAlignment;??????????????? //文件..........,這里設置最小的,200H,兼容全部系統
            ?WORD??? MajorOperatingSystemVersion;? //一下幾個都是系統版本相關的,隨便設置
            ?WORD??? MinorOperatingSystemVersion;
            ?WORD??? MajorImageVersion;
            ?WORD??? MinorImageVersion;
            ?WORD??? MajorSubsystemVersion;??????? //這里要設置為04H
            ?WORD??? MinorSubsystemVersion;
            ?DWORD?? Win32VersionValue;??????????? //未用
            ?DWORD?? SizeOfImage;????????????????? //PE文件占用的內存空間,我們設置為3000H
            ?DWORD?? SizeOfHeaders;??????????????? //PE文件頭大小(含節表),這里是200H
            ?DWORD?? CheckSum;???????????????????? //效驗和(我不知道用來干嘛,PE幾乎都是000000000,可能和其他方面有關,比如調試?)
            ?WORD??? Subsystem;??????????????????? //文件子系統,子系統的含義大家可以去參考NT內核,這里設置為02,03均可(控制臺和窗口子系統)
            ?WORD??? DllCharacteristics;??????????
            ?DWORD?? SizeOfStackReserve;?????????? //一下幾個是有關堆和棧的設置,基本上隨便,不過最好設置夠用就行(不是0啊!)
            ?DWORD?? SizeOfStackCommit;
            ?DWORD?? SizeOfHeapReserve;
            ?DWORD?? SizeOfHeapCommit;
            ?DWORD?? LoaderFlags;????????????????? //未用
            ?DWORD?? NumberOfRvaAndSizes;????????? //下面的IMAGE_DATA_DIRECTORY結構的數量,原來是16個,最少為2個
            ?IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
            } IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;
            ?

            ?

            有了這個IMAGE_OPTIONAL_HEADER結構,PE文件的作用和包含了什么資源都一目了然了.

            IMAGE_DATA_DIRECTORY結構如下,PE文件中包含了很多數據類型,比如導出,導入函數,資源,重定位,調試和版權信息等等,這個結構最多可以有16個,就是用來定位這些數據的:


            代碼?

            typedef struct _IMAGE_DATA_DIRECTORY {
            ?DWORD?? VirtualAddress;???????
            ?DWORD?? Size;
            } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
            ?

            ?

            IMAGE_DATA_DIRECTORY結構就是指出了你每個數據類型的在內存中的裝載位置和長度.注意,這個結構和下面要說道的節表不同,雖然他們可能指向的是同一個地址,但是,IMAGE_DATA_DIRECTORY區分的是嚴格的數據類型,而節表只是根據人為的定義來劃分數據的種類,如果是正常的EXE,通常把各個數據種類分開存放,而這些數據通常又和數據類型用一樣的方法分類,所以IMAGE_DATA_DIRECTORY結構和節表指向的地址可能是一樣的,但是本文這篇例子不同,因為我們手寫的PE必須盡可能的小,所以我吧幾個節表的數據全部放在了一個節,這樣,節表就只有一個,而IMAGE_DATA_DIRECTORY結構要從混和的數據中指向正確的數據類型地址,就和節表指向的不一樣了.

            綜上所述,"PE信息部分"對應框架的代碼為:


            代碼?

            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?
            ?

            ?

            這上面的數據大多都解釋過了,這里要看地址"000000C0"處的"28 11 00 00 28 00 00 00",這個是IMAGE_DATA_DIRECTORY結構的第二個,也就是導入表的地址,"00 00 00 28"這個是長度,不比多說,"00 00 11 28"這個又為何?帶著這個問題看下去...[這里算作注意點2]


            最后要介紹的是"數據部分":


            引用?

            +++++++++++++++++++++++++++++++++++++++++++++
            +? +++++++++++++++++++++++++++++++++++++++? +
            +? +[數據節表][0x24*N+1]??????????????? +? +
            +? +++++++++++++++++++++++++++++++++++++++? +
            +????????????????????????????????????????? + <==PE數據部分
            +? +++++++++++++++++++++++++++++++++++++++? +
            +? +[數據節][不定]????????????????????? +? +
            +? +++++++++++++++++++++++++++++++++++++++? +
            +++++++++++++++++++++++++++++++++++++++++++++
            ?

            ?

            其中IMAGE_SECTION_HEADER結構(數據節表)如下:


            代碼?

            typedef struct _IMAGE_SECTION_HEADER {
            ?BYTE??? Name[IMAGE_SIZEOF_SHORT_NAME];//這個8字節的空間就是給你來定義這個節的名稱,比如大家常見的".text .data .code"等等,我這里為了以后的填充方便,設置了空白..(00000000H),其實這里是可以隨便寫的,比如你定義".zvrop"也可以
            ?union {
            ???? DWORD?? PhysicalAddress;????????? //這是個聯合結構,說明了該節的大小,我們整個PE文件就是一個節,所以是200H
            ???? DWORD?? VirtualSize;
            ?} Misc;
            ?DWORD?? VirtualAddress;?????????????? //定位該節在內存中的地址(相對于加載位置的偏移地址)我們這里是先不說這些.[這里算作注意點3]
            ?DWORD?? SizeOfRawData;??????????????? //文件中的尺寸,這里和上面的聯合結構不同,這里是對齊后的地址,我們設置為200H
            ?DWORD?? PointerToRawData;???????????? //該節在文件中的位置,相對于文件頭,這里可以隨便設置,不過設置了后面的代碼指針也要跟著變動,我們這里設置100H
            ?DWORD?? PointerToRelocations;???????? //下面四個是給連接器用的參數,隨便
            ?DWORD?? PointerToLinenumbers;
            ?WORD??? NumberOfRelocations;
            ?WORD??? NumberOfLinenumbers;
            ?DWORD?? Characteristics;????????????? //節的屬性,自己區查表,基于篇幅,這張表我就不提供了,需要的可以PM我,一般代碼節為60000020H(40000000&2000000&00000020),即是可執行,可讀的代碼段,我們設置為60000060H,因為我們既包含了數據又包含了代碼.
            } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
            ?

            ?

            可以看到這個結構的數量是不定的,也就是你下面有多少個節,就有多少個IMAGE_SECTION_HEADER+1的結構,因為系統需要一個全0的IMAGE_SECTION_HEADER結構來標識已經結束.另外,XP最少要兩個IMAGE_SECTION_HEADER結構,,不然會報非法32位程序的(這個熟悉的提示我在完成這個東西的時候不知道出現了NN次,從此深惡痛絕!),2K則沒有這個限制(引自watercloud的研究,我沒多少時間去深挖這個哈...).

            下面就是具體的"數據節"的內容了(我們這篇文檔整個PE文件就是一個節),整個PE文件結構內容大概就是這么多.


            綜上所述,"數據部分"對應框架的代碼為:


            代碼?

            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]

            繞了這么大個圈子才回到正題....(一_一....其實我是想把問題寫的詳細,這樣大家看了就沒有態度的疑慮嘛..),還記得上面說過的數據類型嗎,其中最重要的就是導入表,我們的URLDownloadToFile小朋友已經在板凳上坐了很久了.......這個導入表就是為他量身定做的.我們的目的就是讓PE文件執行URLDownloadToFile的功能,自然得把URLDownloadToFile這個函數加入導入表.

            說道導入表的定義呢?就不得不先說說WINDOWS加載可執行程序時候對IAT(IMPORT ADDRESS TABLE,導入地址表)的修改,我們知道,各個系統的每個函數在內存中的位置都是不同的(至少2K,XP,2003基本上都不一樣),所以才有很多寫人SHELLCODE的時候,位置計算個半天..這樣來說的話,在我們編譯EXE的時候就不可能確定某個函數的地址.要執行這個函數,必須找到他的入口地址,而這個地址就由系統在加載PE文件的時候幫你"填空",這動態的完成函數地址的填充也就是"動態連接"這個名詞的由來.

            現在我簡單的模擬一下系統轉載PE文件并給出函數地址的步驟,首先,我們給出一個PE文件中的導入表:


            代碼?

            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
            ?

            ?


            可以看到,這個表被分為四個部分,其中中間兩個等長為0x14的兩段就是導入表中的IMAGE_IMPORT_DESCRIPTOR結構,該結構如下:


            代碼?

            typedef struct _IMAGE_IMPORT_DESCRIPTOR {
            ?? union {
            ?????? DWORD?? Characteristics;???????????
            ?????? DWORD?? OriginalFirstThunk;???????????? //指向一個"函數列表的指針結構".
            ?? };
            ?? DWORD?? TimeDateStamp;????????????????????? //暫時可以看作沒用,0
            ?? DWORD?? ForwarderChain;???????????????????? //暫時可以看作沒用,0
            ?? DWORD?? Name;?????????????????????????????? //指向一個DLL,這個結構里面的函數必須都是這個DLL里面的
            ?? DWORD?? FirstThunk;???????????????????????? //指向一個IAT表,最后操作系統修改的就是這個
            } IMAGE_IMPORT_DESCRIPTOR;
            ?

            ?

            注意,該結構也必須有N+1個,因為我們只需要一個函數"URLDownloadToFile",所以我們只有這一個結構,第二個結構是全0的.表示結束.

            這個"函數列表指針結構"就是IMAGE_THUNK_DATA32結構:


            代碼?

            typedef struct _IMAGE_THUNK_DATA32 {
            ?? union {
            ?????? PBYTE?? ForwarderString;
            ?????? PDWORD? Function;
            ?????? DWORD? Ordinal;
            ?????? PIMAGE_IMPORT_BY_NAME? AddressOfData;
            ?? } u1;
            } IMAGE_THUNK_DATA32;
            ?

            ?

            他只有一個雙字類型的值,這個值如果是1XXXXXXXH的,那么說明該函數是一序號方式導入的,序號就是除了1外的剩下的7位,如果是0XXXXXXXH的,那么這個除了0外的7位就是作為一個虛擬地址指向這個函數的名字.

            關于什么是序號導入什么是名字導入,我就不說了,這些涉及到導出表的概念.本文不需要.

            假設我是WINDOWS操作系統的PE裝載器,我從這個PE文件格式的某些參數中定位到了這個00000128H的地址是導入表地址,現在我的目的是要把"58 11 00 00"這個地址替換為正確的函數地址(注意,是00000120H處的,00000150H處的那個"58 11 00 00"是給系統提供"URLDownloadToFile"這個字符串位置的指針,這個地址不會變動,會變的是00000120H處的"58 11 00 00",其實00000120H處的"58 11 00 00"可以隨便設置的.).

            我開始定位到了:


            代碼?

            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
            ?

            ?

            的地方,發現這個函數的位置是"50 11 00 00",相關的DLL是"6E 11 00 00",于是我找到PE文件的這個位置(是內存中的相對位置),發現"50 11 00 00"位置處的IMAGE_THUNK_DATA32結構的值是"58 11 00 00",這個值不是1開頭的,于是我用這個值作為地址查找,發現這個值指向的位置的內容是"31 00 URLDownloadToFile",除去前面的兩個序號,找到了這個函數的名稱,接下來我根據在"6E 11 00 00"位置找到的字符串"URLMON.DLL",用LoadLibrary()和GetProcAddress()找到了函數"URLDownloadToFile"在內存中的位置,假設是"XX XX XX XX",然后把"XX XX XX XX",填入到"20 11 00 00"指向的位置中...完畢.

            這樣來說,大家就明白了,URLDownloadToFile這個函數的存放位置應該根據"50 11 00 00"(確切的說應該是"50 11 00 00"指向的位置的指針)和"6E 11 00 00"來確定(確定這個函數存在的DLL).

            ?

            本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/2195/archive/2004/08/31/90110.aspx

            [THIS IS JMP S2]

            現在我們再回頭整理一下整個過程...結合這張表:

            代碼?

            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文件數據圖就是一個很大的"填空",除去重要的數據部分,我們可以隨便寫入數據的地方有2個(也就是兩個大掛號掛起來的中間).

            第一個是從地址00000002開始的,到地址0000003B結束的56字節.

            第二個是從地址00000160開始的,到PE文件結尾的160字節.(也可以從000000158開始,這樣就有168字節)

            因為我們的程序很短,所以第二個168字節基本上可以滿足要求全部,就不需要第一個56字節的數據了.把數據和在一起也方便呢,不是么?^_^.

            那這些地方具體填寫些什么東西呢?大致來說分為三個部分:

            1.導入表,包括"URLDownloadToFile"這個函數的字符串和"URLMON.DLL"這個DLL的字符串.

            2.文件的可執行機器碼.

            3.函數需要的數據.

            ?

            首先是導入表,根據上一節說的那些,我們可以很容易的判斷出這個"URLDownloadToFile"該填在"58 11 00 00"的位置.當然你可以改這個值,這個值只是我寫的.總之你想吧這個導入表放在什么位置,這個"58 11 00 00"就要指向這個位置.于是我們在PE文件的00000158位置寫入"31 00 URLDownloadToFile"字符串,前面兩個16進制是序號是給轉載器提供信息作為在DLL中導出地址的依據.

            (對了,這里說明一個問題,這篇文檔也注釋了很多"注意點",為什么呢,仔細看看這些注意點,發現都是和位置有關的,那是因為:PE文件中的絕大多數的地址,都是采用文件加載后內存中的地址的,這樣一方面加快了加載速度,另外一方面也省了不少加載器的工作,比如這個"58 11 00 00"的地址,因為我們加載的位置是1000H,所以根據這個位置,我們在文件中的位置就是158H,這里要申明的一點,并不是所有的地址都可以這么計算的,因為我們在PointerToRawData那里設置了100H,為的就是這樣方便的計算相對地址,對于其他的PE文件,如果要根據這種內存地址計算出PE文件地址,還不是這么簡單是事情,^_^..當然,網絡上也有很多這種轉換函數,RVA到OFFSET的)

            然后把URLMON.DLL這個字符串填入"6E 11 00 00"指向的地址,當然這個值也是可以變的.

            最后,我們要用筆記錄一下最后這個函數被導出的地址的存放處,也就是"20 11 00 00".

            [注意,以上的這些操作都和IMAGE_IMPORT_DESCRIPTOR結構和IMAGE_THUNK_DATA32結構相關,看不明白的多看看這兩個結構]


            接下來是可執行碼.我們的目的很簡單,只要這個PE文件能下載文件就行,所以我們只要調用URLDownloadToFile函數即可,寫一小段匯編碼(還記得前面說過的URLDownloadToFile的調用方法嗎,花了點筆墨的那個):


            代碼?

            PUSH 0???????????????? ;6A 00
            PUSH 0???????????????? ;6A 00
            PUSH XXXXXXXX????????? ;68 XXXXXXXX
            PUSH XXXXXXXX????????? ;68 XXXXXXXX
            PUSH 0???????????????? ;6A 00
            CALL XXXXXXXX????????? ;E8 XXXXXXXX
            ?

            ?

            由于函數的調用是符合PASCAL調用,也就是STDCALL,自右向左壓棧,所以我們的參數也是最后一個先入棧.最后CALL出這個URLDownloadToFile函數.

            前兩個XXXXXXXX地址是兩個字符串的地址,也就是URLDownloadToFile函數的兩個重要參數,最后一個XXXXXXXX是這個函數在內存中的地址(操作系統已經幫我們填充了,還記得上面說的那個用筆記錄的"20 11 00 00"么?)

            主要的代碼就是這么多,可是不幸的事情發生了,當我用WINHEX把這些代碼填入PE框架并且保存的時候,居然被殺毒軟件刪除了!!!!他們把這個看作病毒????想來寫病毒原來是這么容易的事情(.....一_一.)....

            幸好有備份(如果沒有,我可是要哭死了.....),我修改了這些代碼,加入了一些垃圾(比如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 ;也就是呼叫下兩個字節的地址,這是機器中調用函數的通常做法
            C9???????????? ;leave
            C3???????????? ;ret
            FF25 20114000? ;jmp 20114000? ;這個跳轉地址就是"20 11 00 00",至于那個"40",
            ????????????????????????????? ;就是程序的建議起始加載地址"00400000".另外,這里是仿機器格式.
            00
            00
            00
            00
            ?

            ?

            將他們寫入哪里呢?這個就隨便你了,不過請翻翻上面說的,有個地址是(也就是注意1所在的位置)AddressOfEntryPoint:這個就是用來定位你代碼的執行入口的,我們就放在導入表的后面,也就是"00000179H"的位置.


            最后就是那兩個字符串的地址了,我們在程序中已經給出


            代碼?

            68 D0114000
            68 A0114000
            ?

            ?

            那這兩個字符串的位置就確定了,一個是"000001D0H",我們要下載的文件地址"/Article/UploadFiles/200408/20040818230017329.JPG"就是保存到這里..這里我每個分配了48字節存儲區域,大家也可以根據具體需要設置.別忘了還有dos頭部可以保存56字節的空白可以寫數據,如果需要的話,修改指向就是.

            對于上面的這一堆廢話,我的目的是想讓大家明白,而故意介紹的格式,即是說,如果讓你換做其他的API函數也能輕易的調用,而不是局限于URLDownloadToFile.^_^...比如那些...那些...功能啊....(我可沒說啊...嘿嘿)..


            OK,這個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?? ................

            ?

            ?


            簡單的運行一下這個PE文件,圖片已經被下載到C盤的GL123文件夾,說明我們的工作還是成功的哈.(哇,好sex的MM啊,口水流啊流.....)

            六.包裝

            到這里開始,我們的EXE是有了,現在開始DEBUG出場,我們的計劃是用E命令寫入整個PE文件數據,然后用W命令保存到臨時文件中,于是就成就了這個原始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
            ?

            ?


            不過這樣很不美觀...于是我又想了一個辦法優化,用F命令向填充512個00,然后再在相對位置寫入需要的數據,于是乎就生成了下載bat腳本的bate1版本,這個是完整的批處砦募?


            代碼?

            ;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
            ?

            ?

            運行這個BAT,在很快的一閃而過的屏幕后,圖片被安然的下載到我的硬盤上....大功告成了...高興那..

            這篇文檔這里要說的東西已經全部說完了,用上面這個批處理文檔就可以實現任意的下載網絡上的東西,但是要注意的幾點就是,下載地址的URL的長度,如果你覺得很長,那么要調整PE格式來達到兼容你URL長度的目的,保存地址基本上和URL是一樣的.


            七.小節

            倘若這樣結束的話,大多數人也沒有意見,可惜這篇文檔就顯得不完整了...為了讓這記錄式的文檔更加完整,我就用下載MM這個事件作示例,來演示如何用上面的這個BAT實現批量下載的功能,作為對這個文檔的小節,以慰岌各位看官辛勤的雙眼.(...^_^)

            首先給出整個批處理代碼:


            代碼?

            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
            ?

            ?

            如果看懂了前面的那個當個下載的批處理代碼,那這個基本上是沒有問題的了.

            這個批處理的工作步驟:

            1.運行后會在你c盤建立一個"gl123"的文件夾,用來保存下載的圖片的(這也是唯一的缺點,我無法寫成自定義文件夾...:( )

            2.接著會在當前目錄派生出兩個子批處理文件和一個"tmp"的臨時目錄...

            3.之后開始循環下載所有的圖片,并顯示進度,完成后顯示"ALL OK".

            4.刪除所有的臨時文件.


            對這個bat混和上一個中的一些地方,我統一解釋一下:

            1.RCX是DEBUG的寫寄存器CX命令,把我們要寫入的文件大小賦值給他,然后調用N命令給出文件名后用W寫入或者L加載,

            2.Q后面要保留回車(你總不想bat文件回不來吧...),

            3.>nul和2>nul是說把輸出和錯誤輸出全部屏蔽...你也不想在下載的時候出現"1 file(s) copy.."這樣的提示吧..

            4.如果是特殊字符要在前面加上轉義的"^"符號方可寫入文件

            5.對于文件地址遞增類型含0的地址,比如http://www.xxx.com/0001.jpg,http://www.xxx.com/0002.jpg......這樣的格式,很多人用判斷這個值是小于9,加三個0,大于9,小于99,就加2個0,大于99,小于999,就加三個0.....而我的方法是統一在這個數值前面加上足夠的0,然后再截取整個字符串的最后4位,相對來說比較省代碼.

            6.for可以嵌套使用,構成N重循環,但是有個缺點,FOR內不可以用SET...(具體看幫助,總之很麻煩...一_一..這也是我用多個BAT實現的原因)

            7.用批處理文件處理文件部分(包括新建和刪除目錄)之前最好先進入當前目錄一次,本批處理用cd\和cd %~d0%~p0來完成

            8.cd %~d0%~p0中的%~d0環境變量是對%0變量的擴展,擴展為當前驅動器盤符,%~p0是擴展為當前目錄,其他的就看window命令行幫助文檔.

            9.養成習慣用SETLOCAL和ENDLOCAL包裹整個批處理.

            10.因為本地字符串在內存中是以雙杠保存的,而在批處理中是以單杠保存的,要實現這個轉換必須要相當多的代碼(批處理對文本的處理能力極弱...一_一..),所以我就沒有寫.


            八.后記

            其實本文的標題完全可以改為:用批處理調用API,但是我覺得這樣太沒事找事了,畢竟調用API還是寫程序來的方便.另外為了塞飽這篇文檔,加入了相當多的PE格式分析,雖然和本文有一點點關系...但還是覺得有點喧賓奪主哈...

            完成了這些功能后,深刻的感受到那些在底層工作的人員是多么的辛苦啊......想來我能用VC寫程序,這已經是一件非常幸福的四兒了...(說到這里ZV偷偷的拿出小手絹擦了擦....要成為高手!就要忍受別人不能忍受的苦,于是ZV用煙頭在手臂上....汗,很痛的,當然沒有~~~哈哈)..

            文章里用到的兩個主要的工具都是微軟歷史上的老前輩.長年不見他們活動,帶出來溜溜腳也算是對這些快被遺忘的技術的懷念(據說當年UCDOS下的WPS就是某牛人用DEBUG寫出來的,PF的緊啊,,一_一...),其實Windows雖然不開放,還是很有意思的,呵呵.

            最后,在這篇冗長的,臃腫的,夾雜著無數錯誤,垃圾,抄襲,糊弄,陳詞濫調,語言不通的1.5萬字里,的的確確包含了作者的一些心血,感謝大家花了這么多時間看到這里,如果覺得有哪怕有那么一點點的收獲,回個帖子算是對我的鼓勵吧,不然寫個"辛苦了",也是對我的一點點安慰..^_^..


            九.參考文獻

            <<Do All in Cmd Shell>> -- zzzEVAzzz (EVA還真是可憐,我多處查找這篇文檔的作者才找到是他寫的...一_一.)
            <<手工打造微型Win32可執行文件>> -- watercloud
            <<Iczelion的PE教程>> -- Iczelion
            <<A Tour of the Win32 Portable Executable File Format>> -- MSDN
            還有一些零散的BAT和DEBUG的使用方法,就是BAIDU和GOOGLE常年搜索的獲得了.

            ?

            全文完.


            本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/2195/archive/2004/08/31/90112.aspx

            久久精品无码专区免费青青| 精品久久久久成人码免费动漫 | 国产福利电影一区二区三区久久老子无码午夜伦不 | 久久精品视频免费| 久久久这里有精品中文字幕| 中文无码久久精品| 久久99精品久久久久子伦| 久久996热精品xxxx| 精品久久久久久亚洲精品| 91久久国产视频| 99久久99久久久精品齐齐| 国产精品久久久久蜜芽| 久久久女人与动物群交毛片| 国产农村妇女毛片精品久久| 无码人妻久久一区二区三区| 久久久久亚洲AV片无码下载蜜桃 | 久久久久久毛片免费看| 久久亚洲精品中文字幕三区| 久久99精品久久久久久hb无码| 久久精品国产只有精品66| 欧美噜噜久久久XXX| 亚洲精品无码久久久久去q| 国产成人精品久久一区二区三区av| 久久99热这里只有精品66| 国产日韩久久久精品影院首页| 欧美一区二区三区久久综合| 2020国产成人久久精品| 国产成人久久777777| 国产V综合V亚洲欧美久久| 97精品依人久久久大香线蕉97| 久久久亚洲精品蜜桃臀| 超级碰久久免费公开视频| 精品国产91久久久久久久| 国内精品久久久久伊人av| 亚洲精品tv久久久久久久久| 欧美日韩精品久久久久| 色播久久人人爽人人爽人人片AV| 久久综合九色综合欧美就去吻| 国产毛片久久久久久国产毛片| 中文精品久久久久国产网址 | 中文字幕精品久久久久人妻|