diy pe教學3 (28千字)

看雪資料發表於2002-08-14

diy 你的pe 完結篇

上兩篇我教給大家如何diy pe,由於種種原因,都是沒有太實際的例子。今天我就講述一個實際的例子。
我想看雪論壇的人沒有不用w32asm的吧,這個實用的工具大家都愛它。但是w32asm也有些不完善的地方。
首先它不支援滾輪滑鼠的滾動(當然是在你沒有用輔助滑鼠軟體的情況下),然後是不支援檔案的拖放
你開啟w32asm後拖個檔案進去,滑鼠是個禁止拖放的圖示。想實現這些功能麼,那就帶上你的老虎鉗(trw)
,扳手(hiew)我們出發修理機床(w32asm,我修理的w32asm是killer 修改過的w32asm10,原檔案用pecompack壓縮過,
自己脫殼修改),(宣告:我的系統是98,2000下我沒有測試。看完我這篇文章的有興趣的人,可以在2000下試試)

好,我們先做第一個功能吧加入滑鼠滾輪功能(這時候我的想法是假如我有w32asm的源程式的話多好啊,沒有?那只有從反編譯的pe檔案幹活了,
感覺像在修裡煤氣管道漏氣)

第一步:分析問題(每個diy者都應該養成這個習慣,不要上來就bpx 斷點亂下一通)
不支援滾輪是什麼原因造成的?
windows是個訊息系統,w32asm不支援滾輪是因為它接受到滾輪的訊息但是根本不處理它,
我們的目標就是找到w32asm處理windows訊息的地方,然後加入處理滾輪訊息,如果你問我怎麼處理滾輪訊息,很簡單,
我們把滾輪的訊息轉化成按鍵的訊息,w32asm不是可以按上下鍵來滾動麼,我們把滾輪的上下滾動的訊息轉化為上下按鍵
的訊息,然後其他的事情交給w32asm自己去處理就ok,好了修理思路定好了,開始動工

第二步:找到win32asm處理訊息的地方
怎麼找處理訊息,我在上一篇文章已經講過了,這裡我就不在重複了。
找到處理訊息的地方如下
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045E471(U)
|
:0045261E 8B4604                  mov eax, dword ptr [esi+04]===》【這裡是函式引數的傳遞,esi+04就是msg 的unit】
:00452621 3D21010000              cmp eax, 00000121======>這裡eax就是windows訊息的程式碼
:00452626 7F41                    jg 00452669
:00452628 0F84A30D0000            je 004533D1
:0045262E 3D11010000              cmp eax, 00000111===>看過我上篇文章的人就熟悉這個吧,就是wm_command
:00452633 7F1B                    jg 00452650            *處理訊息的地方很長,因為w32asm有很多訊息要處理*
:00452635 7463                    je 0045269A            *限於篇幅,我只貼關鍵地方,有興趣的朋友可以自己*
:00452637 2DA0000000              sub eax, 000000A0        *反彙編原檔案觀察                  *
:0045263C 0F84530D0000            je 00453395            *                          *
:00452642 83E860                  sub eax, 00000060
:00452645 0F8493000000            je 004526DE
:0045264B E92F120000              jmp 0045387F

好我們下斷點bpx 45261e
這時候你會發現不斷的中斷停在這個地方,這很正常,這裡是w32asm處理訊息的核心部分,每一個給w32asm的訊息都經過這裡過濾。
頻繁的windows訊息當然就會頻繁的中斷了:)。這個沒有關係,每次中斷我們就f5,然後在按f5的時候的間隔就滾動滑鼠的滾輪,看看
滾輪的訊息是多少(注:這個在windows程式設計手冊裡可以查到wm_mousewhell訊息的程式碼是20A,我這樣來的目的是因為我不知道其waram的
子引數,也就是如何判斷滾輪向上滾動和向下滾動的子引數是如何定義的,而且手頭上沒有類似的滾輪程式,我只有自己測試了,這個方法
是比較笨了一點,但是我們想人家郭靖都是大智若愚,安慰一下自己),當看到esi+04的值為20a的時候我們d esi+08(一般子參量都是這個
位置,windows 訊息本身就是這麼定義的),比較向上滾動和向下滾動esi+08地址有什麼不同,我這裡看到的是當向上滾動的時候esi+0a是7800
當向下滾動的時候esi+0a是88ff,我就初步確定向上滾動和向下滾動的判斷了。知道了滾輪的滾動,我們還要測試一下按鍵的子引數,按鍵的訊息
是wm_keydown 100,wm_keyup 101,wm_char 102這裡w32asm只處理wm_keydown的訊息(看w32asm訊息處理的地方,我沒有貼出來,有興趣可以自己反
編譯自己往45264B下面看),好既然只處理wm_keydown的訊息,我們還是老辦法,f5加上在間隔的時候按下向上,向下的按鍵,然後觀察esi+08的地址
的子參量,我這裡當按向上鍵esi+08是26,向下鍵esi+08是28,好現在我們知道了所有要知道的東西了,開始用扳手去修理機床吧
首先找到在程式裡空出來的地方
把原來的
:00452621 3D21010000              cmp eax, 00000121
改為
00452621: E90ACD0500                  jmp        .0004AF330 ==》跳到我們自己的訊息處理補丁上
|
自己的訊息處理補丁:
004AF330: 3D0A020000                  cmp        eax,00000020A ;"  ==》比較訊息是否是滾輪
"
004AF335: 7415                        je        .0004AF34C  ----- (1)==是的話轉到滾輪處理
004AF337: 3D33020000                  cmp        eax,000000233 ;"  3"==》比較訊息是否是拖放檔案(這個是補丁第二個功能用的,在下面有闡述)
004AF33C: 7400                        je        .0004AF33E  ----- (2)==》是的話就轉到(拖放檔案的處理)
004AF33E: 90                          nop=====》空出這麼多nop是因為我怕等一下補丁 je        .0004AF33E以後會變成長跳轉所以預留這些位元組
004AF33F: 90                          nop
004AF340: 90                          nop
004AF341: 90                          nop
004AF342: 3D21010000                  cmp        eax,000000121 ;"  !"==》恢復原程式動作
004AF347: E9DA32FAFF                  jmp        .000452626  ----- (3)==》跳回原程式地方
004AF34C: 668B460A                    mov        ax,[esi][0A]==》取出滾輪的子引數
004AF350: 663DFF00                    cmp        ax,000FF ;" "==》比較是向上滾還是向下滾
004AF354: 720A                        jb        .0004AF360  ----- (4)
004AF356: B828000000                  mov        eax,000000028 ;"  ("==》向下滾動,改動訊息子引數為按鍵下
004AF35B: 894608                      mov        [esi][08],eax
004AF35E: EB08                        jmps      .0004AF368  ----- (5)
004AF360: B826000000                  mov        eax,000000026 ;"  &"==》向上滾動,改動訊息子引數為按鍵上
004AF365: 894608                      mov        [esi][08],eax
004AF368: B800010000                  mov        eax,000000100 ;"  "==》改動訊息為按鍵訊息
004AF36D: 894604                      mov        [esi][04],eax
004AF370: EBD0                        jmps      .0004AF342  ----- (6)==》跳回原程式
好到現在為止,我們測試看看,哈哈,果然滾動了,成功!什麼?你說滾動的太慢!)◎)◎※¥)※)(◎,如果嫌慢的話,自己把按鍵訊息子引數改成pgdn。和pgup一次滾動一頁,快了吧。什麼?你說能不能不要這麼快,一次滾動3行,或者5行,最好加個定義視窗想滾動幾行就幾行?我要暈倒了,
大哥,我是改動pe檔案,不是改源程式。如果你想這樣的話,我告訴你思路,你自己做.首先自己在w32asm的menu裡面加個定義滾輪滾動的子選項,我的第二篇
文章有說怎麼做的,然後加入點選這個子選項的訊息處理,讓點選這個子選項的時候彈出一個dialog,可以用CreateDialog的api函式,當然你的先做好這個dialog的資源,然後加入這個dialog的的訊息函式,在dialog裡面做個edit的控制元件和一個button控制元件當點選button的時候就把edit裡面的數值儲存到一個地址
,然後你的滾輪判斷的地方讀入這個地址的值,根據這個值判斷用sendmessage函式向w32asm傳送多少個按鍵訊息,1就發1個,50就發50個,這樣你樂意滾動多少行就多少行,還可以自定義:)。我是很累,不做了,那個大哥不滿意我做的就自己就做一個想滾動幾行就幾行的吧,記得做好了發一個給我用用就行了!好了
到現在為止我們的滾輪版已經做好了。休息一下眼睛,我們要開始大動干戈,開始做拖放版了(如果是初學者就不要往下看了,講述的專業比較多)

現在繼續我們的diy之旅
開啟w32asm試試拖放一個檔案,呵呵,滑鼠是禁止拖放的圖示,證明是w32asm是根本不支援拖放,說白了也就是不處理拖放的訊息。
好,既然我們要是這個w32asm支援拖放,首先我們先了解一下拖放的知識,windows是個圖形介面系統,各種各樣的程式都是基於圖形介面的,
這個介面是我們也可以稱為視窗,視窗很多屬性,比如大家都瞭解的enablewindow就是設定視窗的屬性的,不過這個函式是設定視窗是否可用的
屬性。現在我們需要的是視窗是否接受拖放的屬性,我告訴大家,這個函式就是DragAcceptFiles,它是shell32.dll的函式,與拖放有關的函式還有DragFinish和DragQueryFile,我們要補丁也就要用到這個三個函式,
首先我們看看DragAcceptFiles這個函式是設定視窗是否能接受拖放的訊息,也就是windows是否傳送WM_DROPFILES訊息給這個視窗。使用的方法是api手冊上查到為:
VOID DragAcceptFiles(

    HWND hWnd,    // handle to the registering window 註冊視窗的hwnd
    BOOL fAccept    // acceptance option  是否接受拖放的訊息
  );
知道了DragAcceptFiles的用法後.我的思路是把w32asm的所有的視窗都加上一個
invoke DragAcceptFiles,hwnd,TRUE
當然這個是在彙編裡實現,但是在已經連線好的pe檔案裡如何實現
invoke DragAcceptFiles,hDlg,TRUE呢?
要達到這個目的
第一:首先我們需要pe檔案的import表(注:如果不懂import表的可以自己先學習一下pe檔案的格式,不難,只是有點煩而已)有DragAcceptFiles這個引入函式
第二:然後就是我們需要在pe檔案中的到視窗的hwnd
達到以上條件以後
只要在pe空白空間用以下程式碼就可實現
push 1
push hwnd
call DragAcceptFiles

我們現在先來達到第一個條件
拿出我脫殼的w32asm(注:我自己脫殼的有兩個版本,第一個是在入口點dump完全的pe,然後手動修復import,這個版本很遺憾,只能在98下執行,不能在2000下執行,原因是需要修補的import太多可能有遺漏,或者我也不知道,第二個版本,是情狼大哥在入口點用trw2000的pedump命令dump出來版本,這個版本能跨平臺,但是也遺憾,用pe編輯器看不到任何的import表)
為了能跨平臺,我決定用第二個版本開始改造,由於沒有任何的import表,我們根本不知道是否包含有DragAcceptFiles這個函式,用stud_pe開啟自己做的第一個版本(也就是修復了import表的那個版本),點選function按鈕,觀察import的函式資訊發現其函式有kernell32、gdi32.dll
等,沒有包含shell32.dll,也就是說,不可能有DragAcceptFiles這個我們需要的函式了,沒有函式,我們又想用,那就只有自己手動構造了,三個相關的函式構造起來,還是比較容易的,不過我還是教大家怎麼使用lordpe構造吧(用stud_pe和peeditor都差不多,手動也行,如果來個二三十個函式的話,我看手動的可就吃虧了)

建構函式篇:
用lordpe開啟第二個脫殼版本,點選directories按鈕,然後點選importtabl按鈕,只能看到一個kernell32.dll,在上面點選右鍵,選擇 add import,彈出視窗,在dll填入SHELL32.dll,api填入DragAcceptFiles點選那個加號的按鈕看到DragAcceptFiles已經新增進去了然後用這個方法新增DragFinish和DragQueryFile函式新增完成後點選ok,看到import表多了一個shell32的dll了吧,裡面有我們新增的三個函式,記下這個三個函式的thunkrva值(以後要用到),我這裡如下:
ThunkRva    ThunkOffset    ThunkValue    Hint    ApiName
0015803B    0015803B    0015800C    0000    DragAcceptFiles
0015803F    0015803F    0015801E    0000    DragQueryFile
00158043    00158043    0015802E    0000    DragFinish
其實你也可以自己用winhex構造這個三個函式,不難,看看相關的pe格式文件就能做到,自己構造的有個好處就是結構比較分明,看起來比較舒服,軟體構造的是重新定位過import所以有點混亂,不過你想偷懶就用軟體構造吧,這個方便
構造完了函式,我們的給它一個firshthunk,否則在程式裡面怎麼call那裡呢?這裡就用到了thunkrva的值了,現在工具換成了hiew,用hiew開啟脫殼後的w32asm我們來到004AF29C
如下:
004AF29C: FF25244B4D00                jmp        d,[004D4B24]
004AF2A2: FF252C4B4D00                jmp        d,[004D4B2C]
004AF2A8: FF25304B4D00                jmp        d,[004D4B30]
004AF2AE: FF25344B4D00                jmp        d,[004D4B34]==>這些jmp都是定位api函式的,每個jmp代表程式要用到api函式地址
004AF2B4: 0000                        add        [eax],al    *************************************************************
004AF2B6: 0000                        add        [eax],al    *你問,怎麼找到這個地方的,很簡單,隨便下個程式要用到的api函*
004AF2B8: 0000                        add        [eax],al    *數,看看其call的是那個地址,就能找到這個地方             *
004AF2BA: 0000                        add        [eax],al    *************************************************************
我們現在就要加入DragAcceptFiles、DragQueryFile、DragFinish三個函式的jmp,
jmp到那裡,其實就是jmp那個ThunkRva+imagebase的值,imagebase值是4000000
所以我們要在004AF2B4處填入:
004AF2B4:jmp d,[0055803B]  *55803b=15803B(ThunkRva)+4000000(imagebase)以下一樣計算
      jmp d,[0055803f]
      jmp d,[00558043]
添完後如下:
004AF29C: FF25244B4D00                jmp        d,[004D4B24]
004AF2A2: FF252C4B4D00                jmp        d,[004D4B2C]
004AF2A8: FF25304B4D00                jmp        d,[004D4B30]
004AF2AE: FF25344B4D00                jmp        d,[004D4B34]
004AF2B4: FF253B805500                jmp        DragAcceptFiles ;SHELL32.dl==>看到我們構造的函式了麼,真是happy啊
004AF2BA: FF253F805500                jmp        DragQueryFile ;SHELL32.dll
004AF2C0: FF2543805500                jmp        DragFinish ;SHELL32.dll
004AF2C6: 0000                        add        [eax],al
004AF2C8: 0000                        add        [eax],al
現在記下004AF2B4、004AF2BA、004AF2C0這三個值.以後call 004AF2B4就是call    DragAcceptFiles了,呼叫DragQueryFile 也就是call 004AF2BA了,以此類推

函式構造完成第一個條件也就滿足了,開始我們第二個條件hwnd的尋找吧

尋找hwnd打補丁篇:
如何找到hwnd值呢,對windows程式設計熟悉的人(你不熟悉就慢慢學,總有一天會熟悉的)知道GetWindowRect函式也需要一個hwnd值,我們截獲
getwindowsrect函式,儲存這個hwnd,再使用這個hwnd來DragAcceptFiles 不就ok,好確定思路,開啟trw然後bpx getwindowsrect
啟動w32asm發現其在這裡呼叫getwindowsrect:
0167:00492941 52              PUSH    EDX=========>這個是一個rect的struct的point,也就是一個矩陣結構的指標
0167:00492942 FF700C          PUSH    DWORD [EAX+0C]===>這裡就是我們需要的hwnd
0167:00492945 E810C90100      CALL    `USER32!GetWindowRect===>呼叫這個函式了也就是call      .0004AF25A
0167:0049294A EB1B            JMP      SHORT 00492967        
0167:0049294C 8B582C          MOV      EBX,[EAX+2C]
程式啟動呼叫了很多次GetWindowRect函式,每次hwnd都不同,這是因為w32asm視窗分為好幾個,edit視窗,menu視窗,toolbar,狀態列等等
我們不管它,把每個視窗的DragAcceptFiles 值都設定為ture就行了,管它有多少個呢,有多少個,就ture多少個,個個都能接受拖放,想往
那裡拖就往那裡拖
把這句:
0167:00492945 E810C90100      CALL    `USER32!GetWindowRect
改為:
00492945: E996C90100                  jmp        .0004AF2E0  -----這裡是我們自己的補丁程式了

自己補丁的地方:
004AF2E0: 58                          pop        eax----》取出壓入堆疊的hwnd
004AF2E1: A3C6F24A00                  mov        [004AF2C6],eax---》儲存到一個地址,我選擇的是004AF2C6
004AF2E6: 50                          push        eax---》壓hwnd入堆疊
004AF2E7: E86EFFFFFF                  call      .0004AF25A  -----》 CALL    `USER32!GetWindowRect
004AF2EC: 6A01                        push        001===傳遞ture
004AF2EE: FF35C6F24A00                push        d,[004AF2C6]==》傳遞hwnd
004AF2F4: E8BBFFFFFF                  call        DragAcceptFiles ;SHELL32.dll===》也就是call 004AF2B4我們在上面構造的地方
004AF2F9: E96936FEFF                  jmp        .000492967  -----》回到程式原來的地方

現在開啟補丁完的程式,拖個檔案試試,是不是圖示已經變了,你說,變是變了,怎麼沒有反應啊,還沒有加入事件怎麼會有反應?真是的,路要
一步一步的走。不要太急

現在開始我們加入事件的過程了,首先找到接受訊息的地方,加入判斷拖放訊息的程式碼,上面已經說過了在
:0045261E 8B4604                  mov eax, dword ptr [esi+04]===》【這裡是函式引數的傳遞,esi+04就是msg 的unit】
:00452621 3D21010000              cmp eax, 00000121======>這裡eax就是windows訊息的程式碼
而且
:00452621 3D21010000              cmp eax, 00000121
已經改為:
00452621: E90ACD0500                  jmp        .0004AF330
也就是訊息處理已經在我們自己的補丁地方了

004AF330: 3D0A020000                  cmp        eax,00000020A ;"  ==》比較訊息是否是滾輪
"
004AF335: 7415                        je        .0004AF34C  ----- (1)==是的話轉到滾輪處理
004AF337: 3D33020000                  cmp        eax,000000233 ;"  3"==》比較訊息是否是拖放檔案
004AF33C: 7400                        je        .0004AF33E  ----- (2)==》是的話就轉到(拖放檔案的處理)
好現在我們可以把004AF33C: 7400                        je        .0004AF33E轉到我們自己拖放處理的地方了
現在程式也能判斷拖放訊息,我們就需要根據這個訊息做相關的動作了,也就是需要做開啟這個拖放檔案並且反彙編它。
學習到這裡,大家是不是已經比較有思路了,首先我們必須得到拖放檔案的檔名,這就需要DragQueryFile 函式了
The DragQueryFile function retrieves the filenames of dropped files.

UINT DragQueryFile(

    HDROP hDrop,    // handle to structure for dropped files===》拖放的hdrop
    UINT iFile,    // index of file to query==》為0就可以了
    LPTSTR lpszFile,    // buffer for returned filename==》放檔名的地址
    UINT cch     // size of buffer for filename==》buffer的尺寸
  );
我們看看這個函式需要什麼引數,現在需要找的就是hdrop這個類似於windows視窗的hwnd用來唯一標識拖放的,這個也就是msg的子引數
:0045261E 8B4604                  mov eax, dword ptr [esi+04]===》【這裡是函式引數的傳遞,esi+04就是msg 的unit】
esi+04是訊息unit,那麼這個子引數就在esi+08處
好了相關的引數都搞定了,下一步就是看看w32asm怎麼開啟一個根據檔名開啟檔案的動作的;從那裡下手呢?從disassembler的open file下手?這個會彈出一個框子出來,不好,那就從recent files下手,rencent files可以開啟你最近開啟的檔案,好我們看看它是怎麼開啟的
(跟蹤這個動作很複雜,我要詳細的說,可能打一天的字也打不完,我打字到現在已經很累了,大家就將就一下,我僅僅講跟蹤的關鍵地方)
在訊息判斷(訊息是111 wm_command)的地方下斷點,然後點選recent files的按鈕,發現其子參量為bd02然後又呼叫自己,這時候的子參量變成
0c5f,當你點選open file時候發現子參量也是0c5f,看來是在點選recent files檔案的時候會post一個0c5f訊息回來,那麼rencent裡面的最近開啟的檔案是存在那個地方呢?經過跟蹤可以發現是存在c:\windows\w32dasm8.ini檔案裡面,
windows用來開啟ini檔案的api是GetPrivateProfileString,下bpx GetPrivateProfileString這個斷點,然後點選recent 裡面的最近開啟的檔案程式中斷在這裡:(我們就來分析這裡是什麼意思)
|:100411F0 
|
:1004310C 55                      push ebp
:1004310D 8BEC                    mov ebp, esp===》這是vc程式設計傳遞引數的形式,用vc都是這樣,和編譯器有關係
:1004310F 53                      push ebx==》這裡傳遞的引數是視窗的hwnd
:10043110 56                      push esi
:10043111 8B450C                  mov eax, dword ptr [ebp+0C]==》這裡是訊息的子引數,這裡是最近五個檔案的id
:10043114 2DBD020000              sub eax, 000002BD
:10043119 D1E0                    shl eax, 1

* Possible StringData Ref from Data Obj ->"12345"
                                  |
:1004311B BB3B5A0410              mov ebx, 10045A3B
:10043120 03D8                    add ebx, eax===》把eax做轉換變成了table表,就知道是點選了那個檔案,[ebx]會變成1-5之間

* Possible StringData Ref from Data Obj ->"W32dasm8.ini"
                                  |
:10043122 684A5A0410              push 10045A4A==>引數,檔名
:10043127 6880000000              push 00000080==>buffer size
:1004312C 68575A0410              push 10045A57==>讀入內容地址指標

* Possible StringData Ref from Data Obj ->"NULL"
                                  |
:10043131 68455A0410              push 10045A45==》預設字串的指標
:10043136 53                      push ebx==>key name

* Possible StringData Ref from Data Obj ->"RECENT"
                                  |
:10043137 68345A0410              push 10045A34===>段名

* Reference To: KERNEL32.GetPrivateProfileStringA, Ord:0125h
                                  |
:1004313C E8AB0B0000              Call 10043CEC==>得到key name下的內容
:10043141 A3305A0410              mov dword ptr [10045A30], eax==>返回的到位元組
:10043146 68575A0410              push 10045A57

* Possible StringData Ref from Data Obj ->"NULL"
                                  |
:1004314B 68455A0410              push 10045A45

* Reference To: KERNEL32.lstrcmpiA, Ord:02D9h
                                  |
:10043150 E8F70B0000              Call 10043D4C==>比較上面兩個字串(這個比較的作用是看是不是要調整最近開啟的檔案的順序)
:10043155 0BC0                    or eax, eax
:10043157 745E                    je 100431B7
:10043159 BF575A0410              mov edi, 10045A57
:1004315E 8B0D305A0410            mov ecx, dword ptr [10045A30]
:10043164 8BF7                    mov esi, edi
:10043166 03F9                    add edi, ecx
:10043168 FD                      std
:10043169 B05C                    mov al, 5C
:1004316B F2                      repnz
:1004316C AE                      scasb
:1004316D 85C9                    test ecx, ecx
:1004316F 741C                    je 1004318D
:10043171 83C702                  add edi, 00000002
:10043174 C647FF00                mov [edi-01], 00
:10043178 3BFE                    cmp edi, esi
:1004317A 7411                    je 1004318D===》這一段的作用是把d:\1\hiew.exe變成d:\1的形式,為SetCurrentDirectoryA做準備
:1004317C 56                      push esi==>esi的地址就是X:\XXX\的形式了

* Reference To: KERNEL32.SetCurrentDirectoryA, Ord:023Eh==》設定現在的路徑為X:\XXX\
                                  |
:1004317D E8A60B0000              Call 10043D28
:10043182 57                      push edi
:10043183 68575A0410              push 10045A57

* Reference To: KERNEL32.lstrcpyA, Ord:02DCh
                                  |
:10043188 E8C50B0000              Call 10043D52

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:1004316F(C), :1004317A(C)
|
:1004318D 68575A0410              push 10045A57
:10043192 68515E0410              push 10045E51

* Reference To: KERNEL32.lstrcpyA, Ord:02DCh
                                  |
:10043197 E8B60B0000              Call 10043D52===》上面一段的作用是調整最近檔案開啟的順序
:1004319C C605505E041002          mov byte ptr [10045E50], 02==》這個可是標誌位哦,如果為零的話,那就會彈出那個開啟檔案的視窗
:100431A3 6A00                    push 00000000

* Possible Ref to Menu: MenuID_00CB, Item: "Open File to Disassemble.."
                                  |
:100431A5 680C5F0000              push 00005F0C
:100431AA 6811010000              push 00000111
:100431AF FF7508                  push [ebp+08]==>視窗的hwnd

* Reference To: USER32.PostMessageA, Ord:01DBh
                                  |
:100431B2 E8E70A0000              Call 10043C9E==》我果然沒有猜錯,就是post一個點選open file訊息給自己

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:10043157(C)
|
:100431B7 5E                      pop esi
:100431B8 5B                      pop ebx
:100431B9 C9                      leave
:100431BA C20800                  ret 0008


這段程式碼我已經分析完畢,現在我們要做的事情就是在的到DragQueryFile 的訊息後,做一樣的處理動作,然後post一個open file訊息給自己
,就達到目的了,關鍵的地方是如何得到PostMessageA的hwnd呢?這個hwnd是w32asm mune的id,windows程式設計師都知道setmenu的函式吧,我們下斷點bpx setmenu,看到原程式是在這裡setmenu

:0049CD86 8BF7                    mov esi, edi
:0049CD88 56                      push esi===》舊hwnd
:0049CD89 8B0B                    mov ecx, dword ptr [ebx]
:0049CD8B FF710C                  push [ecx+0C]==》新hwnd

* Reference To: user32.SetMenu, Ord:0221h
                                  |
:0049CD8E E865230100              Call 004AF0F8
:0049CD93 85C0                    test eax, eax

我們要做的事情就是儲存這個新的hwnd,以變PostMessageA的時候用到
:0049CD8E E865230100              Call 004AF0F8
改為:
0049CD8E: E97D250100                  jmp        .0004AF310  -----到我們自己儲存hwnd的程式碼

自己儲存hwnd的程式碼:
004AF310: 58                          pop        eax==》取出hwnd
004AF311: A3CAF24A00                  mov        [004AF2CA],eax==》儲存在[004AF2CA],以便用到的時候在這裡取回
004AF316: 50                          push        eax
004AF317: E8DCFDFFFF                  call      .0004AF0F8  -----call setmenu
004AF31C: E972DAFEFF                  jmp        .00049CD93  -----回到程式原來地方
004AF321: 0000                        add        [eax],al
004AF323: 0000                        add        [eax],al
004AF325: 0000                        add        [eax],al

現在hwnd也有了,而且DragQueryFile會返回開啟檔案的位元組,完全和GetPrivateProfileStringA返回的一樣,也就是在原來GetPrivateProfileStringA處改為DragQueryFile就ok,那麼其他的PostMessageA、lstrcpyA、SetCurrentDirectoryA、lstrcmpiA的call到那裡去找呢,如果能看到import表就直接從import表找,但是這個脫殼版的無import表真是麻煩,沒有辦法,下斷點一個一個看,先來bpx postmessagea
原程式中斷在
:00453402 E85DBD0500              Call 004AF164==》這個就是call postmessagea
其他幾個函式如下:
postmessagea        :00453402 E85DBD0500              Call 004AF164
lstrcpyA        :00451C49 E80CD00500              Call 004AEC5A
SetCurrentDirectoryA    :0043E679 E8B8050700              Call 004AEC36
lstrcmpiA        :0043E468 E815070700              Call 004AEB82
好了現在需要的函式地址也有了,我們開始補丁
004AF330: 3D0A020000                  cmp        eax,00000020A ;"  ==》比較訊息是否是滾輪
"
004AF335: 7415                        je        .0004AF34C  ----- (1)==是的話轉到滾輪處理
004AF337: 3D33020000                  cmp        eax,000000233 ;"  3"==》比較訊息是否是拖放檔案
004AF33C: 7400                        je        .0004AF33E  ----- (2)==》是的話就轉到(拖放檔案的處理)
先把
004AF33C: 7400                        je        .0004AF33E  ----- (2)==》是的話就轉到(拖放檔案的處理)
改成:
004AF33C: 7442                        je        .0004AF380  -----調到處理拖放的子程式中

處理拖放的子程式:
*這段程式就是參照原來處理程式寫的
004AF380: FF7608                      push        d,[esi][08]
004AF383: 8F05CEF24A00                pop        d,[004AF2CE]==>儲存拖放的hDrop到[004AF2CE]
004AF389: 6800010000                  push        000000100 ;"  "==>緩衝區為100所以不要拖動路徑長度大於256的檔案哦,可能要出錯
004AF38E: 68575A0410                  push        010045A57 ;"ZW"==》儲存檔名稱的地址
004AF393: 6A00                        push        000==》UINT iFile
004AF395: FF35CEF24A00                push        d,[004AF2CE]==》push hDrop
004AF39B: E81AFFFFFF                  call        DragQueryFile ;SHELL32.dll==》得到拖動的檔名
004AF3A0: A3305A0410                  mov        [10045A30],eax==》返回的位元組數儲存,以下都是模仿原來程式的動作,上面已經有分析,我就不詳細的說明了
004AF3A5: 68575A0410                  push        010045A57 ;"ZW"
004AF3AA: 68455A0410                  push        010045A45 ;"ZE"
004AF3AF: E8CEF7FFFF                  call      .0004AEB82  ----- (2)==》call lstrcmpiA
004AF3B4: 0BC0                        or          eax,eax
004AF3B6: 0F846A32FAFF                je        .000452626  ----- (3)==》如果為零就直接退出,原動作是直接ret,我們就直接返回
004AF3BC: 6660                        pusha==》儲存所有的暫存器,因為下面要用到esi等,所以現在先儲存之
004AF3BE: BF575A0410                  mov        edi,010045A57 ;"ZW"
004AF3C3: 8B0D305A0410                mov        ecx,[10045A30]
004AF3C9: 8BF7                        mov        esi,edi
004AF3CB: 03F9                        add        edi,ecx
004AF3CD: FD                          std
004AF3CE: B05C                        mov        al,05C ;"\"
004AF3D0: F2AE                        repne      scasb
004AF3D2: 85C9                        test        ecx,ecx
004AF3D4: 741D                        je        .0004AF3F3  ----- (4)
004AF3D6: 83C702                      add        edi,002 ;""
004AF3D9: C647FF00                    mov        b,[edi][-01],000 ;" "
004AF3DD: 3BFE                        cmp        edi,esi
004AF3DF: 7412                        je        .0004AF3F3  ----- (1)
004AF3E1: 56                          push        esi
004AF3E2: E84FF8FFFF                  call      .0004AEC36  ----- (2)就是call SetCurrentDirectoryA   
004AF3E7: 57                          push        edi
004AF3E8: 68575A0410                  push        010045A57 ;"ZW"
004AF3ED: 90                          nop
004AF3EE: E867F8FFFF                  call      .0004AEC5A  ----- (3)就是 call lstrcpyA
004AF3F3: 68575A0410                  push        010045A57 ;"ZW"
004AF3F8: 68515E0410                  push        010045E51 ;"^Q"
004AF3FD: E858F8FFFF                  call      .0004AEC5A  ----- (4)就是 call lstrcpyA
004AF402: C605505E041002              mov        b,[10045E50],002 ;""
004AF409: 6A00                        push        000
004AF40B: 680C5F0000                  push        000005F0C ;"  _
004AF410: 6811010000                  push        000000111 ;"  "
004AF415: A1CAF24A00                  mov        eax,[004AF2CA]==》這個就是我們在setmenu儲存的hwnd
004AF41A: 50                          push        eax
004AF41B: E844FDFFFF                  call      .0004AF164  ----- (5)就是 call postmessagea
004AF420: FF35CEF24A00                push        d,[004AF2CE]==》這個是儲存的 hDrop,
004AF426: E895FEFFFF                  call        DragFinish ;SHELL32.dll==》拖放處理完後要釋放hDrop,呼叫DragFinish
004AF42B: 6661                        popa    恢復堆疊
004AF42D: E9F431FAFF                  jmp        .000452626  ----- (7)跳回原處理地方

好了到現在為止我們diy加強版的w32asm已經出來了,大家享受一下勞動成果吧,如果有興趣的話,還可給w32asm做很多改造,使之更加適合與
我們自己的工作習慣。我已經把方法和原理都教給大家了,有誰做出更加power的版本,記得給我一份哦,
此程式在98下測試成功,2000下我想應該不會成功,因為2000下的拖放函式不同,有興趣的人可以在裡面加上getversion函式判斷是98還是2000
如果是2000下用另外一套函式實現。我自己很少在2000下使用w32asm所以我就不做了:(,就當留給大家的作業吧



後記:寫到這裡已經是兩手發酸,希望這片文章成為diy pe的經典教程,如果看雪出一本diy pe的書是不是考慮選上我這篇文章:)



                                                        你的朋友:pll621
                                                        2002.8.24

相關文章