1. 程式人生 > >[教程二]用OD給遊戲打補丁! [2010-06-11]

[教程二]用OD給遊戲打補丁! [2010-06-11]

知識點:OD內編寫彙編,實現硬編碼打補丁功能,OD訊息斷點。

工具:OD,CE,PEID

目標:是男人就撐過20秒.exe

結果:增加按CTRL鍵時切換是否不死,達到就算中彈也不死的目的。

作者:C++

以下正文:

先說說實現的步驟吧:

1、  找到不死地址。

2、  找到遊戲內按鍵處理函式。

3、  查詢程式內空地址準備編寫ASM程式碼。

4、  編寫ASM程式碼實現通過按CTRL鍵來開啟和關閉不死功能。

5、  編寫ASM程式碼加入到遊戲訊息迴圈處理部分,達到實現檢測不死開關來對應動作。

那麼結合上面的步驟,分別講一下如何實現:

先說說找不死地址吧,首先開啟遊戲,用CE附加,然後正常進入遊戲內,當開始遊戲時(出現飛機,並你可以控制),暫停遊戲(回車或空格),目的是為了更好的搜尋,這時CE裡搜尋DWORD型1,搜尋後,繼續遊戲(回車或空格),讓遊戲跑起來,然後等待死亡,當完全死亡並出現統計資訊的介面時,CE搜尋0,這時會剩下不多的地址了,然後在回到遊戲正常進入遊戲,觀察CE裡各地址,進入後變成1的就是我們要的了,一般都是第一個(406D90)。這時在這個地址上查詢“誰寫了他”,然後讓遊戲內死亡,這時會出現幾個地址,我們先看第一個,如下圖:

 

這時,我們就要請OD上場了,(其實CE也可以,個人喜歡用OD,更直觀),用OD附加遊戲,跳轉到0040361f這個地址,我們看到如下程式碼:

0040360F  |.  A1806D4000     mov eax,dword ptrds:[406D80]

00403614  |. 85C0            test eax,eax  ;  判斷是不是0

00403616  |.  743B           je short 是男人就.00403653

00403618  |. 83F8 11         cmp eax,11   ;  如果是11就跳轉到死亡處理

0040361B  |.  7517           jnz short 是男人就.00403634

0040361D  |. 33D2            xor edx,edx

0040361F  |. 8915 906D4000   mov dword ptrds:[406D90],edx   ;  寫入0,表示死亡

通過上面的程式碼分析和測試後,證明406D80這個地址,只要鎖定為0就實現了不死,那麼至此我們也就得到了不死的地址及實現不死的方法,就是始終給這個地址寫入0。

找到了不死地址,我們就可以進行下一步用OD寫ASM程式碼了,找遊戲內可以寫程式碼的空地址。這裡用到了PEID,用PEID開啟遊戲,在EP段那裡點後面的“>”進入節檢視器,如下圖:

然後在上面點右鍵,搜尋全0處,如下圖:

 

接著會出現如下介面:

 

在這裡,我們可以找一段記憶體用來編寫我們的程式碼,就用TEXT段的吧,用00400000+RVA的偏移48F5=004048F5,我們用OD到這個地址看看:

 

好了,這裡可以編寫我們的程式碼了,^_^ 這裡再提一句,為了實現開關的功能,就是按一次CTRL表示開啟不死,再按一次表示關閉不死功能,我們需要找一個全域性變數地址來儲存1和0的狀態開關值,我們就用DATA段的那個空地址吧,00400000+000069C4=004069C4。

好了,下面就開始寫程式碼了,首先我們要找到遊戲的按鍵處理函式,這裡我們通過OD裡的訊息斷點來得到此地址,首先,我們在OD裡按F9讓遊戲完全跑起來,然後點OD上面的W按鈕調出視窗檢視器,這樣我們可以看到如下圖的介面:

這時,我們在第一個行上點右鍵,選擇“訊息斷點設定在CLASSPROC”,如下圖:

 

設定訊息型別為WM_KEYUP,如下圖:

點“確定”按鈕設定訊息斷點。然後到遊戲內,隨便按一個鍵,這時OD會斷下,斷在遊戲鍵盤處理函式上,如下圖:

我們經過分析這個函式(分析過程略)後,確定在4023C2處進行程式碼改寫,然後遊戲處理時跳轉到我們的程式碼上執行完後再返回給遊戲。如下圖:

好了,下面就是來寫我們的程式碼實現CTRL按鍵的功能了,還能記住我們上面找的空資料地址吧,004048F5,我們來看下程式碼是什麼樣子:

004048F6    83F9 11           cmp ecx,11 ;判斷是否是CTRL鍵

004048F9    75 1F             jnz short是男人就.0040491A ;不等於時就跳到原處理程式碼

004048FB    833D C4694000 01  cmp dword ptr ds:[4069C4],1 ;比較我們的全域性變數是否為1,如果為1表示開啟不死功能,否則為關閉不死功能

00404902    74 0C             je short是男人就.00404910

00404904    C705 C4694000 010>mov dword ptrds:[4069C4],1 ;開啟不死功能設定

0040490E    EB 0A             jmp short是男人就.0040491A

00404910    C705 C4694000 000>mov dword ptrds:[4069C4],0 ;關閉不死功能設定

0040491A    83F9 27           cmp ecx,27 ;此處為原處程式碼,照寫

0040491D  ^ 0F8F D5DAFFFF     jg是男人就.004023F8 ;此處為原處程式碼,照寫

00404923  ^ E9 9FDAFFFF       jmp是男人就.004023C7 ;我們的邏輯執行完後跳回到原處程式碼的下一行繼續執行

好了,這部分程式碼就是實現了按CTRL鍵時把我們的全域性變數變成1或0,就是開啟或關閉不死設定。

那麼我們要在4023C2處改寫原來的程式碼來跳轉到我們上面的程式碼,就是把原始碼CMP ECX,27改成

Jmp 004048F6,這裡需要注意一點,就是原來的程式碼當你改寫後,會佔用原來二行的程式碼,既:

cmpecx,27

jg是男人就.004023F8

所以,這二句在我們的程式碼裡要給實現,否則是不行的。

現在我們可以配製CE來檢視我們的全域性變數4069C4,同時通過遊戲來測試下我們加的功能是否正常,經過測試,一切正常。那麼我們就可以進行下一步的操作了,就是把具體實現不死的功能程式碼找一個時時執行的地方進行處理。這個又要如何實現呢?

我們可以想到最好的方式就是放在遊戲的大迴圈內來執行,還記得我們最開始找不死地址時的那段程式碼麼?

0040360F  |.  A1806D4000     mov eax,dword ptrds:[406D80]

00403614  |. 85C0            test eax,eax  ;  判斷是不是0

00403616  |.  743B           je short 是男人就.00403653

00403618  |. 83F8 11         cmp eax,11   ;  如果是11就跳轉到死亡處理

0040361B  |.  7517           jnz short 是男人就.00403634

0040361D  |. 33D2            xor edx,edx

0040361F  |. 8915 906D4000   mov dword ptrds:[406D90],edx   ;  寫入0,表示死亡

我們來想一下這個地方遊戲的邏輯,當死亡時遊戲會呼叫這段程式碼來標記你已經OVER對吧,那麼遊戲是如何知道已經死亡了呢?除非他時時的檢測某個資料或某個資訊才能達到及時對吧?那麼我們可以看一下這層CALL的上一層是個什麼樣子,我們來到當前CALL的第一行位置,如下圖:

然後在00403400的位置反到呼叫他的上一層CALL內,如下圖:

 

我們看到如下程式碼:

我們看到了熟悉的SLEEP,這個函式就是延時函式,我們經過測試斷定這個CALL就是遊戲的大迴圈,好了,那麼我們需要的就是找個地方來跳到我們的程式碼就OK了。

經過多次分析,我決定改寫4020F4這個地址跳轉到我們的程式碼。老規矩我們先找一塊空的資料,上一個寫我們程式碼的地方下面還有好多空資料區,我們就把程式碼寫在那裡吧,我們決定用0040493B這個地址,那麼我們先把4020F4這個程式碼改下,把原來的push 0 改寫成jmp0040493B,這樣就回跳轉到我們的程式碼了,然後我們看下我們要寫的程式碼,如下:

0040493B    833D C4694000 01  cmp dword ptr ds:[4069C4],1 ;比較我們的全域性變數是否是開啟不死設定

00404942    75 0A             jnz short 帶不死版.0040494E ;如果未開啟直接跳轉到原處理

00404944    C705 806D4000 000>mov dword ptr ds:[406D80],0;如果開啟了不死設定,就把406D80這個地址的資料寫入0

0040494E    6A 00             push 0 ;原處程式碼

00404950    E8 79FEFFFF       call <jmp.&KERNEL32.Sleep> ;原處程式碼

00404955  ^ E9 A1D7FFFF       jmp 帶不死版.004020FB ;跳轉到遊戲原處的下一行

由於我們是在大迴圈內寫的程式碼,所以會不斷的被呼叫,就達到了406D80這個地址鎖0的目地,這樣就可以完成開啟/關閉不死的功能了。

這裡需要特別說明一點,就是在我們的程式碼裡寫“call<jmp.&KERNEL32.Sleep> ”的處理,我們知道,在我們的程式碼裡直接寫這句是通不過的,我們需要找到他的實際地址寫進去才可以,怎麼找就比較簡單了,這裡就不多說了。

到這裡,所有的操作都完成了,剩下的就是測試了,我們開啟遊戲,然後按一次CTRL就表示開啟了不死功能,這時,就運算元彈打到了你也不會死的,再按一次,就會關閉不死功能,恢復原態。

最後提一句,其實實現的方法很多,我只是列舉了我實現的過程,寫我們自己程式碼的位置也很多,不是固定的,還是改寫的原始碼的位置也不是固定的,需要自己分析去實現。

好了,教程到此結束,謝謝各位的觀看!

By C++

2010.6.10