C++程序反編譯筆記(20) 解決掃雷"亂碼"問題
????目前的游戲界面是這樣的

????

繪制地雷數(shù)目的代碼
????前文已經(jīng)分析出?sub_1002AC3 是繪制整個游戲界面的函數(shù).

????并且通過注釋掉某個函數(shù)的觀察界面的方法了解了內(nèi)部調(diào)用的各個函數(shù)的作用. 邊框基本上是沒有什么問題的, 因此從繪制地雷數(shù)目的代碼入手.
????

????知道了函數(shù)是做什么的, 那么就給它起一個易于理解的名字. DrawPixels 也是我起的名字, 因為它只調(diào)用了系統(tǒng)函數(shù)將圖片數(shù)據(jù)繪制到窗口上, 因此起了這個名字.

????這個函數(shù)的看上去是沒什么問題的. 而DrawMineCount函數(shù)需要判斷一下有沒有問題. 觀察原來的好的程序界面.

????可以發(fā)現(xiàn)這個顯示的是 3 個數(shù)字.? 對比代碼, 顯然對DrawPixels的三次調(diào)用時依次繪制3個數(shù)字. 分析 v3 以確定 DrawPixels 的第3個參數(shù), 首先 v3 = dword_1005194 % 100; , 可以得出 v3 是 dword_1005194 的個位和十位, 而 v3 / 10 是?dword_1005194 的十位, v3 % 10 是?dword_100519 的個位, 那么?dword_1005194 肯定就是當前存在的地雷數(shù)目了, DrawPixels 的第3個參數(shù)是要繪制的數(shù)字.
????運行代碼, 在這個調(diào)用DrawPixels的地方下斷點, 可以發(fā)現(xiàn)與分析相符.

尋找"亂碼"源頭
????既然 DrawMineCount 函數(shù)沒有錯誤, 那么DrawPixels肯定有錯, 因此繪制地雷數(shù)目的代碼只調(diào)用到了這兩個.
????可能出錯的變量有2個?g_bmpInfoNumber 和?dword_1005A60.

????g_bmpInfoNumber? 是從資源中讀取的像素數(shù)據(jù), 不太可能出問題. 重點放在dword_1005A60 上.?dword_1005A60 只有一處賦值點.

????還記得最開始解決項目的編譯錯誤時, 有很多個未初始化的變量嗎? 當初的解決方法是將它們?nèi)砍跏蓟癁?, v11 就是這樣的一個變量. 那么幾乎可以確定是v11的初值出現(xiàn)了問題.

內(nèi)嵌匯編
????要知道v11的初值, 只能看匯編代碼了.

????上圖中 ,v11 的初值來自于ecx,? ecx的值可能來自順序執(zhí)行下來的, 也可能來自于跳轉(zhuǎn)過來的. 但是仔細分析, 可以看到, 只有 loc_10024F1 下面的一段代碼給 ecx 賦值了. 觀察函數(shù)代碼, 發(fā)現(xiàn)并沒有對這段匯編代碼的反編譯. 所以, 錯誤的原因是IDA Pro沒有反編譯出這段代碼.
????一個簡單的辦法是將這段匯編代碼內(nèi)嵌到C++代碼中, 這樣就不必了解每個指令的具體含義了. 只提取與ECX有關(guān)的代碼就行了.
????

????????__asm{} 語句就是用于內(nèi)嵌匯編代碼的, 匯編代碼中, 第一句和最后一句是我自己加的, 開始前將ECX的值入棧, 最后出棧, 目的是不修改ECX原來的值, 只使用計算結(jié)果就行了. 要知道的一點是, 源程序的匯編代碼和反編譯后再編譯回去的匯編代碼幾乎是不可能相同的.

成果
? ? 內(nèi)嵌匯編后再運行項目.

????可以看到, 游戲界面已經(jīng)能正常顯示了, 游戲也可以正常運行了.