此係列是本人一個字一個字碼出來的,包括範例和實驗截圖。本人非計算機專業,可能對本教學涉及的事物沒有了解的足夠深入,如有錯誤,歡迎批評指正。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閒錢,可以打賞支援我的創作。如想轉載,請把我的轉載資訊附在文章後面,並宣告我的個人資訊和本人部落格地址即可,但必須事先通知我。
你如果是從中間插過來看的,請仔細閱讀 跟羽夏學 Ghidra ——簡述 ,方便學習本教學。請認準 部落格園 的 寂靜的羽夏 ,目前僅在該平臺釋出。
導航瀏覽是逆向者的基本能力,在原始碼完全未知的情況下,該能力是十分重要的。在介紹完如何導航分析二進位制之後,我們會以逆向的角度,來審視crakeMe
這個函數。
在我們在不同的函數之間進行切換的時候,有時候錯過想回去看看的時候,再重新點回去一是麻煩,二是不準,那麼咋辦呢?
圖中,高亮的兩個按鈕就是負責這個事情的。向左的箭頭是回到上一次的位置Alt + 左方向鍵
,向右的反之Alt + 右方向鍵
。這兩個功能會在之後經常用到。
搜尋和蒐集資訊的能力是在逆向中最常用也是最重要的能力之一。
在程式中,我們可以搜尋字串,定位函數位置:
裡面有很多選項,英文不會的話翻譯一下,然後記住,因為專業常用的單詞就幾個,會了就會了。
其次,我們還可以搜記憶體的資料,來進行定位:
當然,這些功能應該不會特別常用,不過在特殊的場合還是有用的,比如通過特徵碼搜尋指定函數、字串等等。這所有的功能全部在Search
選單當中。
在逆向過程中,蒐集資訊也是十分重要的,比如字串。
在Search
選單中選中For Strings...
,將會彈出表單:
這個是設定搜尋屬性的,如果有特殊的自行設定,如果沒有,直接預設搜尋,在本範例會得到如下內容:
Ghidra
也能搜尋常數,在Search
選單中選中For Scalars...
:
選好常數範圍後,然後點選搜尋,會得到如下結果:
不過,搜尋常數似乎用處不大。
對於某個位置的參照,我想獲得分析得到的所有直接參照,並選擇跳轉到哪一個,在Search
選單中選中For Direct Reference...
:
不過查參照的時候一般不會用這個,我們更偏愛使用這種方式查詢:
我們既可以獲取該地址的參照,如果是函數地址,也能獲取到函數的參照。
Ghidra
與此同時也支援指令模式搜尋,在Search
選單中選中For Instruction Patterns...
:
不過,要使用這個,首先需要要點選編輯按鈕(筆的圖示),輸入寫死之後確認,然後點選搜尋即可:
不知道你有沒有注意到工具列上幾個小小的字母圖示:
這幾個圖示有自己的作用,不過通過工具英文提示也明白啥作用,這裡就總結一下(從左到右依次):
The Ghidra Book
並沒有解釋清楚。 學習了這些,我們開始從逆向者的角度,來開始分析。
在開始破解實驗的時候,我們看到顯示了一個字串請輸入金鑰:
,這個是我們的一個關鍵突破口,如果能找到,會有一定的概率破解(不排除有假的)。
然後,我們開始選單Search
-For Strings...
,開始搜尋,最終我們定位到位置:
跳轉到這個位置:
s__004021ff XREF[2]: getKey:004014b4(*),
getKey:004014b4(*)
004021ff e8 af ds u8"請輸入金鑰:"
b7 e8
be 93
//
// .eh_frame_hdr
// SHT_PROGBITS [0x402214 - 0x40228f]
// ram:00402214-ram:0040228f
//
************************************************
* Exception Handler Frame Header *
************************************************
__GNU_EH_FRAME_HDR XREF[2]: 00400210(*),
_elfSectionHeaders::00000
00402214 01 1b eh_fra Exception Handler Frame H
03 3b
我們看到這裡有交叉參照,是從getKey
函數來的,跟過去:
int getKey(void)
{
int local_c;
puts("請輸入金鑰:");
__isoc99_scanf("%d",&local_c);
return local_c;
}
根據反編譯結果,理解了該函數的功能,我們需要分析一下該函數的參照:
通過簡單的分析,我們推測CALL getKey
這個地址最有嫌疑,我們跟過去:
bool crackMe(void)
{
int iVar1;
iVar1 = getKey();
return iVar1 == 0x123456;
}
可以看出,拿到返回值之後,會判斷是否和0x123456
相等,並將比較結果返回。
我們如法炮製,最終跟到了這個位置(區域性):
iVar1 = crackMe();
if (iVar1 == 0) {
puts("抱歉,沒成功哦,再試一次!");
}
else {
puts(">> 祝賀破解成功!");
}
此時,我們明白了結果。只要輸入的值與0x123456
相等就通過,由於是十進位制輸入,就是1193046
。輸進去就是下面的結果:
至此,該實驗結束。
本篇博文我們介紹瞭如何導航並蒐集自己所需要的資訊。不過逆向並沒有這麼簡單,該範例並不難,且有原始碼,有前面教學分析的基礎上,看起來相當簡單,不信?你直接搜字串都不一定搜得到。