天天看點

OllyDBG 入門系列(七)-彙編功能

标 題: 【原創】OllyDBG 入門系列(七)-彙編功能

作 者: CCDebuger

時 間: 2006-04-09,16:42

OllyDBG 入門系列(七)-彙編功能

作者:CCDebuger

今天我們的目标程式是 MyUninstaller 1.34 版。這是一個非常小的程式解除安裝工具,VC6編寫,大小隻有61K。我拿到的這個是上次閃電 狼兄弟給我的,附帶在裡面的簡體中文語言檔案是由六芒星制作的。這個程式有個毛病:就是在列出的可解除安裝程式上輕按兩下檢視屬性時,彈出的屬性視窗的字型非常難 看,應該就是系統字型(SYSTEM_FONT):

OllyDBG 入門系列(七)-彙編功能

我們今天的目标就是利用 OllyDBG 的彙編功能把上面顯示的字型改成我們常見的9号(小五)宋體。首先我們用 OllyDBG 載入程式, 按 CTR+N 組合鍵查找一下有哪些 API 函數,隻發現一個和設定字型相關的 CreateFontIndirectA。現在我們按滑鼠右鍵,選擇 “在每個參考上設定斷點”,關掉名稱對話框,F9運作,程式已經運作起來了。我們在程式的清單框中随便找一項輕按兩下一下,很不幸,那個字型難看的界面又出現 了,OllyDBG 沒有任何動作。可見建立這個視窗的時候根本沒調用 CreateFontIndirectA,問題現在就變得有點複雜了。先點确定把 這個字型難看的對話框關閉,現在我們從另一個方面考慮:既然沒有調用設定字型的函數,那我們來看看這個視窗是如何建立的,跟蹤視窗建立過程可能會找到一些 對我們有用的資訊。現在我們再回到我們調試程式的領空,按 CTR+N 看一下,發現 CreateWindowExA 這個 API 函數比較可疑。我 們在 CreateWindowExA 函數的每個參考上設上斷點,在 MyUninstaller 的清單框中再随便找一項輕按兩下一下, 被 OllyDBG 斷下:

  00408F5E  |.  FF15 98B24000   |CALL DWORD PTR DS:[<&USER32.CreateWindowExA>]    ; "斷在這裡

上下翻看一下代碼:

  00408F3B  |.  50              |PUSH EAX                                         ; |hInst

  00408F3C  |.  8B45 C0         |MOV EAX,DWORD PTR SS:[EBP-40]                    ; |

  00408F3F  |.  6A 00           |PUSH 0                                           ; |hMenu = NULL

  00408F41  |.  03C6            |ADD EAX,ESI                                      ; |

  00408F43  |.  FF75 08         |PUSH DWORD PTR SS:[EBP+8]                        ; |hParent

  00408F46  |.  FF75 D0         |PUSH DWORD PTR SS:[EBP-30]                       ; |Height

  00408F49  |.  57              |PUSH EDI                                         ; |Width

  00408F4A  |.  50              |PUSH EAX                                         ; |Y

  00408F4B  |.  FF75 BC         |PUSH DWORD PTR SS:[EBP-44]                       ; |X

  00408F4E  |.  FF75 EC         |PUSH DWORD PTR SS:[EBP-14]                       ; |Style

  00408F51  |.  68 80DE4000     |PUSH myuninst.0040DE80                           ; |WindowName = ""

  00408F56  |.  68 DCD94000     |PUSH myuninst.0040D9DC                           ; |Class = "STATIC"

  00408F5B  |.  FF75 D4         |PUSH DWORD PTR SS:[EBP-2C]                       ; |ExtStyle

  00408F64  |   6A 00           |PUSH 0                                           ;  第一處要修改的地方

  00408F66  |   8945 F4         |MOV DWORD PTR SS:[EBP-C],EAX

  00408F69  |.  E8 A098FFFF     |CALL <myuninst.sub_40280E>

  00408F6E  |.  50              |PUSH EAX                                         ; |hInst

  00408F6F  |.  8B45 DC         |MOV EAX,DWORD PTR SS:[EBP-24]                    ; |

  00408F72  |.  6A 00           |PUSH 0                                           ; |hMenu = NULL

  00408F74  |.  03F0            |ADD ESI,EAX                                      ; |

  00408F76  |.  FF75 08         |PUSH DWORD PTR SS:[EBP+8]                        ; |hParent

  00408F79  |.  FF75 CC         |PUSH DWORD PTR SS:[EBP-34]                       ; |Height

  00408F7C  |.  53              |PUSH EBX                                         ; |Width

  00408F7D  |.  56              |PUSH ESI                                         ; |Y

  00408F7E  |.  FF75 D8         |PUSH DWORD PTR SS:[EBP-28]                       ; |X

  00408F81  |.  FF75 E8         |PUSH DWORD PTR SS:[EBP-18]                       ; |Style

  00408F84  |.  68 80DE4000     |PUSH myuninst.0040DE80                           ; |WindowName = ""

  00408F89  |.  68 D4D94000     |PUSH myuninst.0040D9D4                           ; |Class = "EDIT"

  00408F8E  |.  FF75 B8         |PUSH DWORD PTR SS:[EBP-48]                       ; |ExtStyle

  00408F91  |.  FF15 98B24000   |CALL DWORD PTR DS:[<&USER32.CreateWindowExA>]    ; "CreateWindowExA

  00408F97  |   8945 F0         |MOV DWORD PTR SS:[EBP-10],EAX                    ;  第二處要修改的地方

  00408F9A  |   8B45 F8         |MOV EAX,DWORD PTR SS:[EBP-8]

  00408F9D  |.  FF30            |PUSH DWORD PTR DS:[EAX]                          ; /<%s>

  00408F9F  |.  8D85 B0FEFFFF   |LEA EAX,DWORD PTR SS:[EBP-150]                   ; |

  00408FA5  |.  68 D0D94000     |PUSH myuninst.0040D9D0                           ; |format = "%s:"

  00408FAA  |.  50              |PUSH EAX                                         ; |s

  00408FAB  |.  FF15 90B14000   |CALL DWORD PTR DS:[<&MSVCRT.sprintf>]            ; "sprintf

  00408FB1  |.  8B35 84B24000   |MOV ESI,DWORD PTR DS:[<&USER32.SetWindowTextA>]  ;  USER32.SetWindowTextA

  00408FB7  |.  83C4 0C         |ADD ESP,0C

  00408FBA  |.  8D85 B0FEFFFF   |LEA EAX,DWORD PTR SS:[EBP-150]

  00408FC0  |.  50              |PUSH EAX                                         ; /Text

  00408FC1  |.  FF75 F4         |PUSH DWORD PTR SS:[EBP-C]                        ; |hWnd

  00408FC4  |.  FFD6            |CALL ESI                                         ; "SetWindowTextA

  00408FC6  |.  8D85 ACFAFFFF   |LEA EAX,DWORD PTR SS:[EBP-554]

  00408FCC  |.  50              |PUSH EAX                                         ; /Arg3

  00408FCD  |.  FF75 FC         |PUSH DWORD PTR SS:[EBP-4]                        ; |Arg2

  00408FD0  |.  FF35 00EF4000   |PUSH DWORD PTR DS:[40EF00]                       ; |Arg1 = 00BEADCC

  00408FD6  |.  E8 1884FFFF     |CALL  <myuninst.sub_4013F3>                      ; "sub_4013F3

  00408FDB  |.  83C4 0C         |ADD ESP,0C

  00408FDE  |.  50              |PUSH EAX

  00408FDF  |.  FF75 F0         |PUSH DWORD PTR SS:[EBP-10]

  00408FE2  |.  FFD6            |CALL ESI

  00408FE4  |.  FF45 FC         |INC DWORD PTR SS:[EBP-4]

  00408FE7  |.  8345 F8 14      |ADD DWORD PTR SS:[EBP-8],14

  00408FEB  |.  837D FC 0F      |CMP DWORD PTR SS:[EBP-4],0F

  00408FEF  |.^ 0F8C 32FFFFFF   "JL <myuninst.loc_408F27>

  00408FF5  |.  5F              POP EDI

  00408FF6  |.  5E              POP ESI

  00408FF7  |.  5B              POP EBX

  00408FF8  |.  C9              LEAVE

  00408FF9  ".  C3              RETN

我想上面的代碼我不需多做解釋,OllyDBG 自動給出的注釋已經夠清楚的了。我們輕按兩下 MyUninstaller 清單框中的的某項檢視屬性時,彈 出的屬性視窗上的 STATIC 控件和 EDIT 控件都是由 CreateWindowExA 函數建立的,然後再調 用 SetWindowTextA 來設定文本,根本沒考慮控件上字型顯示的問題,是以我們看到的都是系統預設的字型。我們要設定控件上的字型,可以考慮 在 CreateWindowExA 建立完控件後,在使用 SetWindowTextA 函數設定文本之前調用相關字型建立函數來選擇字型,再調 用 SendMessageA 函數發送 WM_SETFONT 消息來設定控件字型。思路定下來後,我們就開始來實施。首先我們看一下這個程式中的導入 函數,CreateFontIndirectA 這個字型建立函數已經有了,再看看 SendMessageA,呵呵,不錯,原程式也有這個函數。這樣我 們就省事了。有人可能要問,如果原來并沒有這兩個導入函數,那怎麼辦呢?其實這也很簡單,我們可以直接用 LordPE 來在程式中添加我們需要的導入函 數。我這裡用個很小的 PE 工具 zeroadd 來示範一下,這個程式裡面沒 有 CreateFontIndirectA 和 SendMessageA 函數(這裡還有個問題說一下,其實我們程式設計時調用這兩個函數時都是直接 寫 CreateFontIndirect 及 SendMessage,一般不需指定。但在程式中寫更新檔代碼時我們要指定這是什麼類型的函數。這裡在函 數後面加個“A”表示這是 ASCII 版本,同樣 UNICODE 版本在後面加個“W”,如 SendMessageW。在 Win9X 下我們一般 都用 ASCII 版本的函數,UNICODE 版本的函數很多在 Win9X 下是不能運作的。而NT 系統如 WinXP 一般都 是 UNICODE 版本的,但如果我們用了 ASCII 版本的函數,系統會自動轉換調用 UNICODE 版本。這樣我們寫更新檔代碼的時候就可以直接 指定為 ASCII 版本的函數,可以相容各個系統):我們用 LordPE 的 PE 編輯器載入 zeroadd 程式,選擇“目錄”,再在彈出的目 錄表對話框中選擇輸入表後面的那個“...”按鈕,會彈出一個對話框:

OllyDBG 入門系列(七)-彙編功能
因為 SendMessageA 在 USER32.dll 中,我們在右鍵菜單中點選按鈕“添加導入表”,來到下面:
OllyDBG 入門系列(七)-彙編功能
按上面的提示完成後點“确定”,我們回到原先的那個“輸入表”對話框:
OllyDBG 入門系列(七)-彙編功能

從上圖中我們可以看出多出了一個 USER32.dll,這就是我們添加 SendMessageA 的結果。這也是用工具添加的一個缺點。我們一般希望 把添加的函數直接放到已存在的 DLL 中,而不是多出來一個,這樣顯得不好看。但用工具就沒辦法,LordPE 預設是建一個 1K 的新區段來儲存添 加後的結果,由此出現了上圖中的情況。如果你對 PE 結構比較熟悉的話,也可以直接用 16進制編輯工具來添加你需要的函數,這樣改出來的東西好看。如 果想偷懶,就像我一樣用工具吧,呵呵。在上圖中我還标出了要注意 FirstThunk 及那個 ThunkRVA 的值,并且要把“總是查 看 FirstThunk”那個選項選上。有人可能不了解其作用,我這裡也解釋一下:一般講述 PE 格式的文章中對 FirstThunk 的解釋是這 樣的:FirstThunk 包含指向一個 IMAGE_THUNK_DATA 結構數組的 RVA 偏移位址,當把 PE 檔案裝載到記憶體中時,PE裝 載器将查找 IMAGE_THUNK_DATA 和 IMAGE_IMPORT_BY_NAME 這些結構數組來決定導入函數的位址,随後用導入函數真實 位址來替代由 FirstThunk 指向的 IMAGE_THUNK_DATA 數組裡的元素值。這樣說起來還是讓人不明白,我舉個例子:比如你有個很 要好的朋友,他是個大忙人,雖然你知道他的家庭住址,可他很少回家。如果你哪天想找他,直接去他家,很可能吃個閉門羹,找不到他人。怎麼辦?幸好你有他的 手機号碼,你就給他撥了一個電話:“小子,你在哪呢?”,他告訴你:“我正在XXX飯店喝酒呢!”這時你怎麼辦?(當然是殺到他說的那家飯店去蹭飯 了!^_^)這裡的 ThunkRVA 就相當于你朋友的手機号碼, SendMessageA 就相當于你那個朋友。而 FirstThunk 就是你 手機裡的号碼分組。你把你的多個朋友都放在 FirstThunk 這樣的号碼分組裡,每個 ThunkRVA 就是你一個朋友的手機号碼。你要找他們, 就是通過 ThunkRVA 這樣的手機号碼來和他們聯系,直接去他家找他你很可能要碰壁。而移動或聯通就相當于作業系統,他們負責把你的手機号碼和你的 朋友對應上。而 FirstThunk 這樣的号碼分組還有一個好處就是你可以不記你某個朋友的具體号碼,隻要記得 FirstThunk 号碼分組的 值,你的朋友會按順序在裡面排列。比如上圖中 USER32.dll 中的第一個函數是 SendMessageA,它的 ThunkRVA 值就 是 FirstThunk 值。如果還有第二個函數,比如是 MessageBoxA,它的值就是 FirstThunk 值加上 4,其餘類推。你隻要 記住各個函數的位置,也可以通過 FirstThunk 加上位置對應值來找到它。當然這比不上直接看 ThunkRVA 來得友善。說了上面這些,我們 就要考慮怎麼在程式中調用了。你可能會說,我在 OllyDBG 中直接在我們要修改的程式中這樣調用:CALL SendMessageA。哦,别這 樣。這等于我上面說的都是廢話,會讓我感到傷心的。你這裡的 CALL SendMessageA 就相當于也不跟你朋友打個招呼就直接去他家找他,很有 可能你會乘興而去,敗興而歸。别忘了他的手機号碼,我們隻有通過号碼才知道他到底在什麼地方。我們應該這 樣:CALL DWORD PTR [40B01A],這裡的 40B01A 就是上面的 SendMessageA 在程式載入後的所在的地方,由基 址 00400000 加上 ThunkRVA 0000B01A 得到的。這就是你要找的人所在的地方,不管他跑到哪,你有他的手機号碼就能找到他。同 樣道理,你隻要記住了 ThunkRVA 值,就按這個來調用你需要的函數,在别的 Windows 系統下也是沒有問題的。系統會自動把你要找到函數 和 ThunkRVA 值對應上。而你在 OllyDBG 中寫 CALL SendMessageA,可能你在你的系統上成功了,可放到别的系統下就要 出錯了。為什麼?因為你找的人已經不在原來的位置了,他跑到别的地方去了。你還到老地方找他,當然看不見人了。說了這麼多廢話,也不知大家聽明白了沒有, 别越聽越糊塗就行了。總之一句話,别像 CALL SendMessageA 這樣直接調用某個函數,而應該通過 ThunkRVA 值來調用它。下面我 們回到我們要修改的 MyUninstaller 上來,先用 LordPE 打開看一下,呵呵,原 來 CreateFontIndirectA 和 SendMessageA 原程式裡面都有了,省了我們不少事情。看一下這兩個函數 的 ThunkRVA 值,CreateFontIndirectA 在 GDI32.dll 裡面,ThunkRVA 值是 0000B044,這樣我 們就知道在程式中調用它的時候就是 CALL DWORD PTR [0040B044]。同樣,SendMessageA 的ThunkRVA 值 是 0000B23C,調用時應該是這樣:CALL DWORD PTR [0040B23C]。了解了這些東西我們就來考慮怎麼寫代碼了。首先我們來看 一下 CreateFontIndirectA 和 SendMessageA 這兩個函數的定義:

CreateFontIndirectA:

HFONT CreateFontIndirect(

CONST LOGFONT *lplf // pointer to logical font structure

);

CreateFontIndirect的傳回值就是字型的句柄。

對于這個函數我們需要的參數就是給它一個 LOGFONT 的字型結構指針,我們隻要在要修改程式的空白處建一個标準的9号(小五)宋體的 LOGFONT 字型結構,再把指針給 CreateFontIndirectA 就可以了。

SendMessageA:

LRESULT SendMessage(

HWND hWnd, // handle of destination window

UINT Msg, // message to send

WPARAM wParam, // first message parameter

LPARAM lParam // second message parameter

上面的第一個參數是視窗句柄,我們知道 CreateWindowExA 傳回的就是視窗句柄,我們可以直接拿來用。第二個消息參數我們這裡是設定字型, 選WM_SETFONT,這個值是 30H。第三個參數是字型句柄,可以由上面的 CreateFontIndirectA 獲得。第四個參數我們不需 要,留白。現在我們準備開始寫代碼,首先我們要在程式中建一個标準9号宋體的 LOGFONT,以便于我們調用。對于 LOGFONT,我們再來看一下定 義:

typedef struct tagLOGFONT { // lf 

LONG lfHeight; 

LONG lfWidth; 

LONG lfEscapement; 

LONG lfOrientation; 

LONG lfWeight; 

BYTE lfItalic; 

BYTE lfUnderline; 

BYTE lfStrikeOut; 

BYTE lfCharSet; 

BYTE lfOutPrecision; 

BYTE lfClipPrecision; 

BYTE lfQuality; 

BYTE lfPitchAndFamily; 

TCHAR lfFaceName[LF_FACESIZE]; 

} LOGFONT;

這樣我們的标準9号宋體的 LOGFONT 值應該是32位元組,16進制就像這 樣:F4FFFFFF000000000000000000000000900100000000008600000000CBCECCE5。現在在程式 中找個空地。我們用 PEiD 來幫助我們尋找,用 PEiD 打開程式,點 EP 段後面的那個 > 号,随便選擇一個區段右擊,選“搜尋全0處 ”(原版好像是cave什麼的):

OllyDBG 入門系列(七)-彙編功能
我們看到 PEiD 把搜尋到的空間都給我們列出來了:
OllyDBG 入門系列(七)-彙編功能

現在我們用 WinHEX 打開我們要修改的程式,轉到偏移 9815 處,從 9815 處選擇 32 位元組(16進制是0X20)的一個選塊,把光标 定位到 9815 處,右鍵選擇菜單 剪貼闆資料->寫入(從目前位置覆寫),随後的格式選擇 ASCII Hex,把我 們 LOGFONT 的 16 進制值

 F4FFFFFF000000000000000000000000900100000000008600000000CBCECCE5 

寫入儲存。現在我們用 OllyDBG 載入已添加了 LOGFONT 資料的程式,先轉到 VA 40A415 處(從上圖中看到的)往下看一下:

OllyDBG 入門系列(七)-彙編功能

因為 SendMessageA 還要用到一個視窗句柄,我們可以通過前面的 CreateWindowExA 來獲得。現在我們就把前一張圖中 的 .rdata 區段中的位址 0040C56E 作為我們儲存視窗句柄 HWND 值的臨時空間。一切就緒,開始寫代碼。先回顧一下我們最先說的那兩 個要修改的地方:

第一個要改的地方:

  00408F5E  |.  FF15 98B24000 |CALL DWORD PTR DS:[<&USER32.CreateWindowExA>]  ; "CreateWindowExA

  00408F64      6A 00         PUSH 0                                          ;  修改前

  00408F66      8945 F4       MOV DWORD PTR SS:[EBP-C],EAX

  00408F69  |.  E8 A098FFFF   |CALL <myuninst.sub_40280E>

修改後:

  00408F64      E9 D5140000   JMP myuninst.0040A43E                           ;  跳轉到我們的更新檔代碼處

第二個要改的地方:

  00408F91  |.  FF15 98B24000    |CALL DWORD PTR DS:[<&USER32.CreateWindowExA>]    ; "CreateWindowExA

  00408F97      8945 F0           MOV DWORD PTR SS:[EBP-10],EAX                     ;  改這裡

  00408F9A      8B45 F8           MOV EAX,DWORD PTR SS:[EBP-8]

  00408F9D  |.  FF30             |PUSH DWORD PTR DS:[EAX]                          ; /<%s>

  00408F9F  |.  8D85 B0FEFFFF    |LEA EAX,DWORD PTR SS:[EBP-150]                   ; |

  00408FA5  |.  68 D0D94000      |PUSH myuninst.0040D9D0                           ; |format = "%s:"

  00408FAA  |.  50               |PUSH EAX                                         ; |s

  00408FAB  |.  FF15 90B14000    |CALL DWORD PTR DS:[<&MSVCRT.sprintf>]            ; "sprintf

  00408FB1  |.  8B35 84B24000    |MOV ESI,DWORD PTR DS:[<&USER32.SetWindowTextA>]  ;  USER32.SetWindowTextA

  00408F97      E9 D4140000      JMP myuninst.0040A470                             ;  跳到我們的第二部分更新檔代碼處

  00408F9C      90               NOP

這兩個地方的修改都是把原代碼改成跳轉,跳到我們的更新檔代碼那繼續執行。在修改之前先把原代碼複制下來,以便恢複。我們在 OllyDBG 中按 CTR+G 組合鍵,來到 0040A43E 位址處,開始輸更新檔代碼:

OllyDBG 入門系列(七)-彙編功能

同樣,我們也在 0040A470 位址處輸入我們另一部分的更新檔代碼。兩部分的更新檔代碼分别如下:

更新檔代碼1:

  0040A43E      60               PUSHAD                                            ;  保護現場

  0040A43F      A3 6EC54000      MOV DWORD PTR DS:[40C56E],EAX                     ;  儲存視窗句柄

  0040A444      68 15A44000      PUSH myuninst.0040A415                            ;  傳遞字型句柄LOGFONT

  0040A449      FF15 44B04000    CALL DWORD PTR DS:[<&GDI32.CreateFontIndirectA>]  ;  GDI32.CreateFontIndirectA

  0040A44F      6A 00            PUSH 0                                            ;  lParam 參數留白

  0040A451      50               PUSH EAX                                          ;  字型句柄LOGFONT

  0040A452      6A 30            PUSH 30                                           ;  WM_SETFONT

  0040A454      8B0D 6EC54000    MOV ECX,DWORD PTR DS:[40C56E]                     ;  視窗句柄送ECX

  0040A45A      51               PUSH ECX                                          ;  壓入視窗句柄參數

  0040A45B      FF15 3CB24000    CALL DWORD PTR DS:[<&USER32.SendMessageA>]        ;  USER32.SendMessageA

  0040A461      61               POPAD                                             ;  恢複現場

  0040A462      6A 00            PUSH 0                                            ;  恢複原代碼

  0040A464      8945 F4          MOV DWORD PTR SS:[EBP-C],EAX

  0040A467    ^ E9 FDEAFFFF      JMP myuninst.00408F69                             ;  傳回

更新檔代碼2:

  0040A470   > "60            PUSHAD

  0040A471   .  A3 6EC54000   MOV DWORD PTR DS:[40C56E],EAX

  0040A476   .  68 15A44000   PUSH myuninst.0040A415                             ; /pLogfont = myuninst.0040A415

  0040A47B   .  FF15 44B04000 CALL DWORD PTR DS:[<&GDI32.CreateFontIndirectA>]   ; "CreateFontIndirectA

  0040A481   .  6A 00         PUSH 0                                             ; /lParam = 0

  0040A483   .  50            PUSH EAX                                           ; |wParam

  0040A484   .  6A 30         PUSH 30                                            ; |Message = WM_SETFONT

  0040A486   .  8B0D 6EC54000 MOV ECX,DWORD PTR DS:[40C56E]                      ; |

  0040A48C   .  51            PUSH ECX                                           ; |hWnd => NULL

  0040A48D   .  FF15 3CB24000 CALL DWORD PTR DS:[<&USER32.SendMessageA>]         ; "SendMessageA

  0040A493   .  61            POPAD

  0040A494   .  8945 F0       MOV DWORD PTR SS:[EBP-10],EAX

  0040A497   .  8B45 F8       MOV EAX,DWORD PTR SS:[EBP-8]

  0040A49A   .^ E9 FEEAFFFF   JMP myuninst.00408F9D

更新檔代碼2因為與更新檔代碼1類似,我就不做詳細解釋了。現在我們的代碼都寫完了,現在我們開始儲存我們的工作,選中我們修改的代碼,點選滑鼠右鍵,會出來一個菜單:

OllyDBG 入門系列(七)-彙編功能
我們左鍵選所有修改(當然選它了,要不然隻會儲存我們標明的這一部分。關于這個地方還要說一下,有的時候我們修改完程式選“複制到可執行檔案”時隻有“選 擇”菜單,沒有“所有修改”菜單項。按 OllyDBG 幫助裡關于備份功能的說法,好像是受記憶體塊限制的,更新檔功能也同樣是這樣。對于備份及更新檔功能我 用的比較少,并不是很了解,這方面的内容還是大家自己去研究吧,有什麼好的心得也希望能共享一下。我遇到不能儲存所有修改的情況就是先把更新檔代碼全部複制 下來,同時利用二進制功能複制代碼,先選一段更新檔代碼儲存為檔案,再用 OllyDBG 打開儲存後的檔案,轉到相應位置分别把我們複制下來的更新檔二進制 代碼粘貼上去後儲存。純屬笨辦法,當然你也可以用 HexView 這樣的工具來修改代碼),随後會出來一個“把選中的内容複制到可執行檔案”的對話框, 我們選“全部複制”,又出來一個對話框,我們在上面點右鍵,在彈出的菜單上選“儲存檔案”:
OllyDBG 入門系列(七)-彙編功能
這時會出來一個另存檔案的對話框,我們另選一個名字如 myuninst1.exe 來儲存,不要直接覆寫原檔案 myuninst.exe,以便于出錯 後好修改。現在關閉 OllyDBG,先不要急着運作剛剛修改過的檔案,因為我們還有個地方要改一下。大家還記得我們在 .rdata 中用了個地方作為 我們儲存臨時變量的地方吧?原先的 .rdata 段屬性設定是不可寫的,現在我們寫入了資料,運作時是會出錯的。現在我們要修改一下 .rdata 段 的屬性。用 LordPE 的 PE 編輯器打開我們修改後的程式,點“區段”按鈕,在彈出的對話框中點選 .rdata 段,右鍵選擇彈出菜單中的“編 輯區段”:
OllyDBG 入門系列(七)-彙編功能
在彈出的對話框中選标志後面那個“...”按鈕:
OllyDBG 入門系列(七)-彙編功能
現在我們把區段标志添加一個可寫入的屬性:
OllyDBG 入門系列(七)-彙編功能
完成後按确定儲存我們所做的工作,運作一下修改後的程式,呵呵,終于把字型改過來了:
OllyDBG 入門系列(七)-彙編功能

如果你運作出錯也沒關系,用 OllyDBG 調試一下你修改後的程式,看看錯在什麼地方。這一般都是輸入更新檔代碼時造成的,你隻要看一下你更新檔代碼運作 的情況就可以了。到這裡我們的任務似乎也完成了,但細心的朋友可能會發現更新檔代碼1和更新檔代碼2前面的代碼基本上是相同的。一個兩個這樣的更新檔還好,如果 要是多的話,這樣重複就要浪費不少空間了,況且工作量也相應加大了。既然前面有很多代碼都是重複的,為什麼我們不把這些重複的代碼做成一個子程式呢?這樣 調用起來要友善的多。下面我們把前面的更新檔代碼修改一下,我們先把更新檔代碼1的代碼改成這樣:

  0040A43E      60              PUSHAD                                            ;  保護現場

  0040A43F      A3 6EC54000     MOV DWORD PTR DS:[40C56E],EAX                     ;  儲存視窗句柄

  0040A444      68 15A44000     PUSH myuninst.0040A415                            ;  我們建的LOGFONT對應指針

  0040A449      FF15 44B04000   CALL DWORD PTR DS:[<&GDI32.CreateFontIndirectA>]  ;  GDI32.CreateFontIndirectA

  0040A44F      6A 00           PUSH 0                                            ;  lParam 參數留白

  0040A451      50              PUSH EAX                                          ;  字型句柄

  0040A452      6A 30           PUSH 30                                           ;  WM_SETFONT

  0040A454      8B0D 6EC54000   MOV ECX,DWORD PTR DS:[40C56E]                     ;  視窗句柄

  0040A45A      51              PUSH ECX                                          ;  視窗句柄壓棧

  0040A45B      FF15 3CB24000   CALL DWORD PTR DS:[<&USER32.SendMessageA>]        ;  USER32.SendMessageA

  0040A461      61              POPAD                                             ;  恢複現場

  0040A462      C3              RETN                                              ;  傳回

這樣我們的子程式代碼就寫好了。現在我們再在子程式代碼後面寫上兩個更新檔代碼,當然不要忘了改前面原程式中的跳轉:

修改後的更新檔代碼1:

  0040A467      E8 D2FFFFFF     CALL myuninst.0040A43E                            ;  調用子程式

  0040A46C      6A 00           PUSH 0                                            ;  恢複前面修改過的代碼

  0040A46E      8945 F4         MOV DWORD PTR SS:[EBP-C],EAX

  0040A471    ^ E9 F3EAFFFF     JMP myuninst.00408F69                             ;  傳回繼續執行

修改後的更新檔代碼2:

  0040A47A      E8 BFFFFFFF     CALL myuninst.0040A43E

  0040A47F      8945 F0         MOV DWORD PTR SS:[EBP-10],EAX

  0040A482      8B45 F8         MOV EAX,DWORD PTR SS:[EBP-8]

  0040A485    ^ E9 13EBFFFF     JMP myuninst.00408F9D

我在每個更新檔代碼片斷間留了4個位元組來分隔。同樣,我們還要修改一下我們前面的跳轉:

第一個要修改跳轉的地方:

  00408F64      E9 FE140000     JMP myuninst.0040A467                             ;  跳到我們的第一部分更新檔代碼處

第二個要修改跳轉的地方:

  00408F97      E9 DE140000     JMP myuninst.0040A47A                             ;  跳到我們的第二部分更新檔代碼處

  00408F9C      90              NOP

修改好後儲存,同樣不要忘了再修改一下 .rdata 區段的屬性。運作一下,一切OK!

【版權聲明】 本文純屬技術交流, 轉載請注明作者并保持文章的完整, 謝謝!

感謝 smple 朋友指出文中存在的問題!文章已修改。怪我自己粗心,測試不夠,對不起大家了!