因為很懶惰,所以不想自己練等級等技能,就寫了 bot,讓 bot 自己練等級、戰鬥技能、採集材料、製作裝備跟料理、打魔石賣 npc 賺錢等等。
單一 bot
我發現有 bot 以後,遊戲變得好玩多了(不要戰我 XD)我下午把 bot 開好,人跑出去找未來的配偶欄約會,晚上回到家以後,錢多了十幾萬,就可以拿去買裝備、料理、藥水。然後就可以出任務去打魔王了,遊戲真好玩阿(好像有點諷刺?)
一開始我的 bot 只能自己一個人活動,沒辦法組隊,但是魔力寶貝不組隊,能去的地方就很有限,因為比方說 50 級的人物,不組隊的話,一個人頂多走到怪物等級平均 30 的地方,再上去就沒辦法了,會死。但 50 級的人物打 30 的怪物,其實幾乎沒有什麼經驗值,而且 30 的地方,收入也很有限。講有限其實也不對,因為他全自動,我去睡覺十個小時,他就打了十個小時,每小時的收入雖然低,但重點是「持續不斷」,後來我發現這「持續不斷」是很恐怖的事情,因為不管你在睡覺還是在上班,他都在賺錢。基本上賺錢的速度是超過花錢的速度的,沒幾天,我的金錢就到達單一人物的上限了,開始放到別人的身上去。
三隻 bot 組隊
錢可以一直增加,但等級還是原地踏步。接下來我嘗試改進 bot,希望我的 bot 可以彼此溝通,可以組隊。這個開發的過程,當初在交大修的「分散式演算法」在這邊…完全沒有派上用場!因為即使多隻 bot,也都還是跑在同一台電腦上,架構很簡單,並不是分散式系統。我有一隻中央的 Mastermind 監控所有人活動的,由他統一規劃協調所有人的動作。經過一陣子的開發以後,我可以成功組隊,協同動作了。現在 50 級的人物,三隻組隊,我可以去到有 4x 級怪物的地方練等級。
為什麼三隻?因為遊戲限制了多重開啟遊戲的上限,就是三個。
但遊戲內容的設計,一個隊伍最多五個人,也就是說,如果你能五個人組隊,基本上,遊戲內所有的場景你都可以去了。因為他的難度不會設計成五個人都還沒辦法打。那限制你只能三開,你就只能某個程度的「自己玩自己」,因為凡是重要的任務,沒有五個人是沒辦法解的。這樣就強迫你跟別人互動了,個人認為這是很正確的設計,線上遊戲就是要跟別人一起玩嘛(最早只能一開,強迫你要跟另外四個人互動 XD)。
五隻 bot 組隊
問題是我是 bot,我沒辦法跟別人互動。所以凡是重要的任務我都不能解 XD
而且只有三個人的話,能去的地方還是比較受限,只能挑怪物等級稍低的地方,等級經驗值的提昇還是很慢的,但想要突破這點的話,接下來就真的要靠破解的了。
執行檔 instance 三開上限
首先,我發現他開第四隻程式的時候,會跳出一個對話框
經過在強者雲集的 plurk 上面請教以後,決定使用有免費版本的 OllyDbg 來進行破解的動作。雖然還有更強的 debugger,但要收錢,因為我沒有錢,所以我決定暫時先尊重一下智慧財產權。使用 OllyDbg 執行遊戲主程式,當然,馬上就跳出了上面這個對話框。回到 OllyDbg 的視窗,按下快速鍵 F12 ,中斷遊戲的執行。然後看看 call stack。根據我的想像,遊戲的程式碼應該是有一段類似這樣的
if(check_exceed_multi_inst_limit()) {
MessageBox("已經達到最高限制多開數");
}
這個時候,CPU 會執行到 MessageBox 這個 system api 裡面,然後在裡面等待,理論上,我只要往 call stack 往上走幾步,應該就可以看到條件判斷的地方。於是我在 OllyDbg 裡面按下 ALT-K 叫出 call stack 視窗。看到如下
看到 [Called from] 那欄,可以看得出來,灰色那條,還屬於遊戲的部份,再追進去就是 system api 的部份了,於是我就雙點灰色 Called from 那欄,程式跳到了呼叫 MessageBox 的地方。
我已經加上註解了,可以看到呼叫檢查函數的地方,還有 if 的點, JE 就是 jump if equal,如果成立的話,就會跳過 MessageBox() 的部份,所以我猜我要改的就是他,把他從 JE 改成 JMP ,無條件跳躍。雙點那段程式碼 OllyDbg 就會跳出組譯視窗,讓你修改那段組合語言。
改成
然後把改過的東西存起來
然後再執行一次程式!成功了!這個對話框不再出現了!但下一個跳出來!
可惡,不知道他是怎麼曉得我沒有透過領航員執行程式的,不管,照剛剛的方法繼續破解他,成功破除了這個檢查,這邊不寫細節了,因為這個方法是錯的。程式之後繼續跑,跑到一半會說找不到檔案,然後 segfault。我猜想可能是領航員有 pass 某些參數給遊戲主程式。但如果用滑鼠雙點遊戲主程式就拿不到這些參數,所以無法執行。但我要怎麼知道他下了什麼參數?
我寫了一隻程式,來取代遊戲主程式,他什麼都不作,只會把收到的參數印出來。
#include
#include
int main(int argc, char* argv[])
{
std::ofstream fout("args.txt");
fout << argc << std::endl;
for(int i = 0; i < argc; ++i) fout << argv[i] << std::endl;
}
接下來這邊真的是很恐怖的組合語言追蹤地獄,我已經不想再重複一次了,直接點重點,破解最重要的就是抓到關鍵的中斷點,不然程式這麼大,你不可能慢慢追。我花了很多時間,發現他是把 md5 下載存到一個檔案裡面,但這個檔案一瞬間就會被砍掉,我想了想,應該要把中斷點下在「建立檔案」的時候,就可以抓到他寫檔的那瞬間,然後看看檔案內容是什麼。
按下 ALT-E 可以看到所有使用到的 module,選擇遊戲的主程式
然後列出所有「跨模組的函數呼叫」
稍微跑幾步,讓她把檔案寫好,然後打開檔案來看,發現格式竟然是 INI,太好了,這是一個很大的提示,因為 Windows 有現成的 api 讀取 INI 格式,叫做 GetPrivateProfileXXX()。馬上在 inter-modular-call 裡面找到 GetPrivateProfileString() 這個人,對他打中斷點,然後接下來就是無盡的追蹤地獄,最後終於成功找到三個關鍵點,統統改成 JMP。細節不詳述。
現在終於可以成功的跳過 md5 的檢查,順利執行第四個 instance,來登入遊戲!
「連線失敗。」
「連線失敗。」
「連線失敗。」
「連線失敗。」
「連線失敗。」
「連線失敗。」
「連線失敗。」
「連線失敗。」
「……………………」
我太天真了,他在 server 端有作檢查!
一旦發現來自你這台電腦的連線數目超過三個,就不讓你登入。
該死,server 是檢查連線來自的 ip 嗎?那我要怎麼生出白花花的 ip?就算我能用別的電腦生出別的 ip,而且一旦跨 ip,就是跨電腦,我就沒辦法讓我的 bot 都跑在同一台機器上,那 bot 的協同部份就必須要加上網路部份的程式碼,複雜度瞬間高很多。
跟強者我同事 runastorm 討教過後,決定寫一隻 proxy,架在另外一台電腦上,然後遊戲主程式的封包都走這個 proxy,這樣 server 端看到的來源,就是另外一台電腦,也就是 proxy 的 ip。survey 了一下,用 python 寫出一個簡單的 proxy。
接下來的是要作三件事情:
- 要知道 server 的 ip 到底是多少,這樣 proxy 才能連過去。我使用 netstat -n -b 以後,順利看到 server 的 ip。
- 要竄改主程式,讓他連到我跑 proxy 的主機去。
- 要一個 dispatcher 在前端,當 instance 只有三個的時候,使用本來的執行檔,超過三個時候,使用竄改過 server 位置的執行檔。
「連線失敗。」
「連線失敗。」
「連線失敗。」
「連線失敗。」
「連線失敗。」
「連線失敗。」
「連線失敗。」
「連線失敗。」
可惡阿!竟然不是看 ip!
浪費我好多時間,可惡,到底看什麼?如果不是看「連線的ip」,難道是遊戲主程式在執行以後,還有把自己的 ip 送過去?server 看的是連線以後送去的資料裡面的嗎?是的話我就完了,因為遊戲封包有加密,我死定了,我試過很多次了,但無法看出他加密的規則,更別說要破解了。看了一下 proxy 的 log,確定連線有成功,來往兩個封包以後連線就被切斷了。看樣子的確是這幾個封包裡面有送資料,檢查不過以後才斷線。但就像剛剛講的,我破不了加密,如果能破的話,我就可以解開,竄改封包,然後再送,但破不了,就沒辦法。
怎麼辦…
釜底抽薪
既然無法竄改已經加密的封包,我竄改還沒加密的總可以吧!遊戲主程式要得到自己的 ip 一定也要呼叫一些系統 api,但不知道是哪個呢?突然想起來!當初在追蹤遊戲主程式的時候,曾經看到他的字串表裡面有一個 "ipconfig /all > mac.txt" ,哈!九成是用 ipconfig 拿 MAC 碼,而且我發現他還是透過 system() 這個函數來呼叫 shell。這樣的話,裡論上我只要抽掉這個文字檔就可以了。
一樣,使用中斷點,把 system() 打住,偷看一下 mac.txt 檔案內容,確定如我所想,裡面只是很單純的 ipconfig /all 的輸出,偷偷把 MAC 的地方改掉幾個字,存檔。
開啟第四個 instance……
登入……
「請選擇角色」
孝心感動天!!!!!!!
成果來一張
五個視窗,關鍵資訊都馬賽克掉了 ^O^
終於,可以來擴充我 bot 組隊的 script 了 :D
架站的看到不要擋我阿,我只有自己用 Q_Q