反編譯記事本(2) 預(yù)處理源代碼
? ? 根據(jù)前面文章所說的, 創(chuàng)建記事本項目, 導(dǎo)入資源后, 下一步應(yīng)該是解決編譯錯誤了. 但是, 我現(xiàn)在發(fā)現(xiàn)先預(yù)處理一下代碼是很有必要的.
????前面反編譯掃雷時, 導(dǎo)出的 C 文件有2800行左右, 代碼量是比較少的, 因此, 整個項目只有一個文件也是合理的. 但是, 記事本有5000行左右, 屬于有點大了. 對于更大的程序, 幾十萬行是很正常的, 如果都放在一個文件中, 編譯會花費非常多的時間, 而且 Visual Studio也會很卡.
????所以, 我決定加入預(yù)處理這一步. 主要的工作是 : 劃分文件 . 次要工作是, 修復(fù)一些簡單的 IDA Pro 導(dǎo)出錯誤, 去掉一些無用注釋等.

劃分文件
????對于C/C++項目, 一個好的項目結(jié)構(gòu)是將聲明與定義分開. 具體的做法就是將聲明放到 頭文件中, 將定義放到頭文件(.h, .hpp)中, 源文件(.c, .cpp)只包含需要用到的頭文件. 這樣可以極大地減少編譯時間, 也可以減少Visual Studio分析代碼的時間.
????還有一點需要說明的是, 一份源文件不要太多行, 5000行以下是比較合理的.
????對于 記事本項目, 開頭部分就是函數(shù)聲明, 需要將它們放到一個頭文件中. 這里, 我創(chuàng)建了 notepad.h 文件, 并將這些函數(shù)聲明全部移到其中. 目前只是簡單劃分, 后面分析出函數(shù)的具體功能后, 再將不同功能的函數(shù)放到不同模塊中.
????


IDA Pro 語法錯誤
????由 IDA Pro 導(dǎo)出的偽C代碼文件是不能直接通過編譯的, 因為其中包含了許多IDA Pro自己定義的格式, 這也是 偽C代碼 這個詞的由來.

重定義 __thiscall
????__thiscall 是 C++ 中成員函數(shù)的參數(shù)傳遞方式, Visual Studio中是不支持直接聲明 __thiscall 修飾的函數(shù)的. 需要使用宏定義將 __thiscall 改為 __cdecl, __cdecl 是C語言函數(shù)的默認(rèn)參數(shù)傳遞方式. 當(dāng)然, 也可以使用文本替換的方式. 具體做法是在源文件開頭聲明如下宏 :? ??

????如果已經(jīng)有了這個宏, 那么可以忽略這一步(IDA Pro新版本中導(dǎo)出的代碼似乎會自動加上這一句).?

文本替換 this
????this 是 C++ 中的關(guān)鍵字, 是不能用作函數(shù)參數(shù)的標(biāo)識符的. 因此, 需要將 this 替換為其他合法標(biāo)識符. 這里我將 this 替換為了 self .

去掉start函數(shù)
? ? IDA Pro會導(dǎo)出一個名為start的函數(shù).
????start函數(shù)是可執(zhí)行文件中的啟動函數(shù), start函數(shù)會調(diào)用main(main, WinMain, wWinMain等)函數(shù). 我們項目的入口函數(shù)是main函數(shù), start函數(shù)是編譯器自動生成的, 里面的一些數(shù)據(jù)結(jié)構(gòu)我們無需關(guān)心, 可能也找不到聲明, 因此, start函數(shù)可能會導(dǎo)致編譯錯誤.
? ?對于start函數(shù), 我們需要做的是通過它找到main函數(shù), 然后直接去掉它. 根據(jù)程序類型, Win32的WinMain, wWinMain有4個參數(shù), 控制臺的main函數(shù)有0個或2個參數(shù). main函都是在GetStartupInfo 函數(shù)后面的第一個非API函數(shù).

????對于記事本, 可以知道sub_1002936是main函數(shù). 根據(jù)API函數(shù)GetModuleHandleA函數(shù)的最后一個字母 A (ASCII)可以知道這是一個多字節(jié)項目, main函數(shù)具體是WinMain. 如果最后一個字母是W, 那么就是wWinMain.

去掉庫函數(shù)聲明
????源文件開頭還有一大串被注釋了的庫函數(shù)聲明, 有強迫癥的話, 可以把這一段刪除.


后話
????可以通過 Git 的 preprocess 分支查看本篇文章每個步驟所做的事.

????