[教程二]用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