遊戲修改器製作教程五:OllyDBG和其他除錯工具

炒雞嗨客協管徐發表於2015-12-04

本教程面向有C\C++基礎的人,最好還要懂一些Windows程式設計知識
程式碼一律用Visual Studio 2013編譯,如果你還在用VC6請趁早丟掉它...
寫這個教程只是為了讓玩家更好地體驗所愛的單機遊戲,順便學到些逆向知識,我不會用網路遊戲做示範,請自重

CE也不是專門用來除錯的,本章將介紹幾款除錯工具,並且完善上一章的東方輝針城修改器

本章提到的工具都可以在看雪學院我的網盤下載

OllyDbg

OllyDbg簡稱OD,現在一般用它的修改版OllyICE,其實是一樣的
OD是最常用的Windows程式反彙編偵錯程式,可惜不能除錯64位程式(目前64位版開發中),不能除錯Ring0層(核心態)
OD官方網站
我更習慣1.x版本,但是64位系統只能用2.x版本...

看看這介面,這語法高亮,比WinDbg不知道高到哪裡去了

需要記住幾個常用快捷鍵:
F2下斷點,F7單步步入,F8單步步過,F9執行,Alt+C檢視反彙編,Alt+K檢視堆疊,Alt+B檢視斷點


寫個小程式CrackMe示範一下如何用OD破解

int _tmain(int argc, _TCHAR* argv[])
{
	printf("請輸入密碼或註冊碼或序列號什麼的:");
	TCHAR password[100];
	_getts_s(password);
	
	// 只是為了做個例子,現在不會有這麼簡單驗證密碼的程式了吧
	if (_tcscmp(password, _T("PASSWORD")) == 0)
		MessageBox(NULL, _T("註冊成功!"), _T("CrackMe"), MB_OK);
	else
		MessageBox(NULL, _T("註冊失敗!YOU LOSER!"), _T("CrackMe"), MB_OK);

	return 0;
}

執行CrackMe,用OD附加(在選單-檔案-附加),按F9執行

因為輸入錯誤密碼會彈出對話方塊提示,我們就在MessageBoxW下個斷點(注意MessageBox是個巨集,根據程式是ANSI還是UNICODE字符集決定是MessageBoxA還是MessageBoxW,要是不知道到底呼叫了哪個那就兩個都下斷吧,不過它們最後都會呼叫MessageBoxTimeoutW)
在下面的命令欄裡輸入bp MessageBoxW,回車

按F9執行,隨便輸入個密碼後斷下,按Alt+K檢視堆疊

看到那個呼叫從CrackMe.011F1084了吧,雙擊它來到呼叫MessageBoxW的地方

看011F1074   /75 07           jne     short 011F107D
這條跳轉指令實現了就是註冊失敗,沒實現就是註冊成功,雙擊把它改成nop就永遠不實現了

點右鍵,在選單裡選編輯-複製所有修改到可執行檔案

在彈出的視窗裡再點右鍵選儲存檔案

然後執行儲存好的程式,隨便輸入個密碼都能註冊成功了

IDA Pro

IDA是一個世界頂級的互動式反彙編工具,它的使用者囊括了軟體安全專家,軍事工業,國家安全資訊部門,逆向工程學者,黑客
IDA專門用來靜態除錯,就是不執行程式只看程式碼,缺點是程式加了殼就看不出什麼了

它還有一個強大的外掛,按F5把當前函式翻譯成C語言(不過只在32位版有)

如果用OD看不出什麼就用IDA看吧

WinDbg

WinDbg是微軟釋出的除錯工具,支援32位和64位程式,可以除錯Ring0層(核心態)
要記很多命令,用著很不爽,除了除錯核心我都不用

東方輝針城修改器V3

這次我要給這個修改器加上更強大的功能:無敵和秒殺

無敵功能

用OD附加遊戲,在下面的記憶體視窗按Ctrl+G轉到上一章搜尋到的殘機地址

然後下個硬體斷點或記憶體斷點(記憶體斷點會比較卡,硬體斷點會斷在操作這個記憶體的指令的下一條指令,記憶體斷點會斷在操作這個記憶體的指令,我優先使用硬體斷點)

然後在遊戲中故意撞個敵機或彈幕,斷在44F61D,上面那條就是給殘機賦值的指令
(這時可以把硬體斷點刪除了,在檢視-硬體斷點裡)

看堆疊是哪裡呼叫了這個函式,來到上一層

上面有個跳轉跳過了這個函式,把它改成jmp試試,按F9執行,回到遊戲再撞彈幕
發出了"biu"的聲音,然後就動不了了...
回到OD把剛才的修改撤銷(選中修改後的jmp指令,按Alt+backspace),又復活了
看看這條跳轉的條件,我懷疑[edi+0x690]是玩家狀態的flag(死亡flag(笑))

在44DD8B這裡按F2下斷,再撞彈幕斷下
在下面的記憶體視窗按Ctrl+G轉到edi+0x690

剛撞彈幕就斷下,這裡的值是0,說明0就是死亡flag了

現在要找是哪個程式碼給狀態flag賦值了0,不過OD要做這件事很難(因為總是有寫入這個地址的指令!),還是交給CE做吧

(因為重啟了遊戲,地址變了)

第一條就是一直在寫入的那條,我猜這個flag其實是玩家在當前狀態經過的幀數?
第二條是撞彈幕時出現的,賦值為0,就是它了
後面的是復活後出現的,不用管

在OD程式碼視窗按Ctrl+G跳轉到0x44F853

我看了一下這個函式沒有能跳過賦值[edi+0x690]=0的程式碼,大概這個函式是死亡函式

下個斷點,撞彈幕,看堆疊,往上一層看看有沒有跳過呼叫這個函式的

這條指令剛好跳過了這個呼叫,把它改成jmp再撞彈幕試試

成功了,怎麼撞彈幕和敵機都沒事
趕緊記下地址0x0044F094,原機器碼7F,修改後EB


(⑨又被騎臉了)


然後我發現死亡函式的上一層是碰撞檢測函式,普通彈幕和鐳射用的是不同的碰撞檢測,所以只改這一處對鐳射沒用
可以像上面那樣在死亡函式下斷點然後撞鐳射,不過既然知道了44F7A0是死亡函式可以用神器IDA檢視哪裡呼叫了這個函式

總共有4個引用,全部修改豈不是很麻煩,所以我打算把死亡函式修改成什麼也不做直接返回

地址0x0044F7A0,原機器碼55,修改後C3

測試一下,完美


秒殺功能

首先隨便找個BOSS(⑨)虐一虐,用CE搜減少的數值搜出三個HP地址

那就修改試試吧,修改第一個和第三個都沒用,被同步成第二個,那麼第二個就是真HP(我猜是敵機陣列中的HP,而第一個是BOSS HP,每幀會和第二個同步)

找出修改HP的程式碼(不裝逼用OD了,還是用CE幹這個好)

這是賦值HP的指令,賦值0就可以實現秒殺了
給暫存器賦值0最好的辦法是用異或(xor)指令,只佔2位元組而且速度比mov快

地址0x004214C6,原機器碼2B C1,修改成31 C0


玩一會,連小怪都可以秒殺了,看來這個函式是敵機類的減HP函式,對敵機通用的

等等,怎麼下一個BOSS開符卡後沒有秒殺,再找找哪個程式碼修改了HP

原來上面還有一個修改HP的程式碼,用於開了符卡的BOSS
為了不破壞棧我們不能修改pop指令,修改上面那個add吧
地址0x004214BA,原機器碼03 C6,修改成31 C0

好了,這下對所有的敵機都能秒殺了

實現程式碼(完整原始碼見GitHub):

// 修改關於無敵的程式碼
void CTH14CheatDlg::modifyInvincibleCode()
{
	static const BYTE originalCode[] = { 0x55 };
	static const BYTE modifiedCode[] = { 0xC3 };
	if (m_process != NULL)
	{
		WriteProcessMemory(m_process, (LPVOID)0x0044F7A0, m_invincible ? modifiedCode : originalCode, sizeof(originalCode), NULL);
	}
}

// 修改關於秒殺的程式碼
void CTH14CheatDlg::modifyMortalBlowCode()
{
	static const BYTE originalCode1[] = { 0x2B, 0xC1 };
	static const BYTE modifiedCode1[] = { 0x31, 0xC0 };
	static const BYTE originalCode2[] = { 0x03, 0xC6 };
	static const BYTE modifiedCode2[] = { 0x31, 0xC0 };
	if (m_process != NULL)
	{
		WriteProcessMemory(m_process, (LPVOID)0x004214C6, m_mortalBlow ? modifiedCode1 : originalCode1, sizeof(originalCode1), NULL);
		WriteProcessMemory(m_process, (LPVOID)0x004214BA, m_mortalBlow ? modifiedCode2 : originalCode2, sizeof(originalCode2), NULL);
	}
}


不過做到這種程度了還能好好玩遊戲嗎,所以作弊還是適可而止吧

相關文章