今天,準備要開始逆向程式碼。在介紹工具之前,先來解說逆向工程的邏輯。基本上,對於逆向,有很多現成的工具可以直接套用,但許多有其極限(當然,有工具可以提升效率,我沒有說要完全摒棄工具)。但就對於想透過組合語言來解釋程式碼而言,不仰賴工具更可以練習自己的組合語言語感。因此,很多時候透過交叉比對
,更可以解釋狀況。以下,我將講解如何將程式碼一步步拼湊回來。
我會先提供一份已經逆向過的程式碼截圖,讀者可以挑戰可否在不使用工具下,理解程式碼的運作邏輯?接著會提供透過 GDB 輔助動態追蹤的過程與解答,讓讀者比較是否與自己的理解相同。
情境引導:在通往偉大的航道,你撿到一支程式碼
,執行後發現它是一支會驗證使用者輸入的程式(如下圖)。到底驗證過後能獲得什麼秘辛呢?好奇的你,真的好想知道。於是,你隨意輸入一段內文(LET ME IN),發現錯誤。果然沒那那麼簡單就可以拿到擁有世界上的一切的「海賊王」哥爾·D·羅傑的寶藏。你忽然想到,最近不是有學過逆向嗎?何不來逆逆看?
下圖是透過 GDB 逆向的程式碼,想請問讀者這段程式碼到底在做什麼?
讀者可以拿出紙與筆,畫出以下程式碼的運作流程圖。花一點時間,消化這幾天所看到的知識,若忘記可以往回翻,或查閱相關資料。重點是,若想提升組合語言的識別能力,不要輕易的直接看解答
,先讓大腦思考一下。想跟大家分享,我去成大上課的時候,聽到 Jserv 大大的名言:
「如果你把游泳池當作浴缸泡著,再泡幾年還是不會游泳」
– jserv
經過與海軍奮鬥的一番掙扎後,我們現在來講解一下如何肉眼識別程式碼。
先看下圖,其實我們看到組合語言密密麻麻,也不是每一個都是重點。讀者可能會問,那什麼是重點?
我把重點用紅筆標示起來於下圖 - 重點就在每次的比較、跳躍與呼叫其他函式。比方說記憶體結尾是 5cc 的地方,有一個 cmp 指令,接著 5d0 的 jne;還有 5ea 的地方,有一個 call 的指令,後面還寫著 <printf@plt>。這些都是重點,因為我們逆向的時候,想知道到底什麼條件下會觸發這些事情?觸發後做了什麼事?
於是,透過上述方法,就可以簡略畫出程式運作圖(如下圖)
。可以知道,總共有三個區域,分別是白色、紅色、綠色的區塊。分別對應三種情況。首先,白色區塊代表的就是驗證全部通過後的結果(找到寶藏),而紅色代表的是第一個驗證沒過的下場,綠色表的是第二個驗證沒過的下場。簡單來說,就是要走在白色這條主線上,才能通關成功!
透過與 GDB 的動態追蹤,可以得到下圖結果。紅色的 1 代表,沒通過參數個數驗證,綠色的 2 代表通過參數個數驗證,卻沒通過驗證碼的驗證,紫色的 3 代表驗證全部,安全達陣!
以下是哥爾·D·羅傑的寶藏。解答公布:
#include <string.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
if(argc==2) {
printf("Checking License: %s\n", argv[1]);
if(strcmp(argv[1], "AAAA-Z10N-42-OK")==0) {
printf("Access Granted!\n");
} else {
printf("WRONG!\n");
}
} else {
printf("Usage: <key>\n");
}
return 0;
}
恭喜找到 ONE PIECE!動態追蹤程式程式碼蠻有趣的,雖然有點累、甚至有很多看不懂的情況,但了解背後邏輯94爽。明天,將會講解如何透過工具解讀程式碼、提升逆向效率。