CSAPP lab2 二進制拆彈 binary bombs phase_1


這個實驗從開始到完成大概花了三天的時間,由於我們還沒有學習編譯原理、匯編語言等課程,為了完成這個實驗我投機取巧了太多,看了網上很多的解題方法,為了更加深入學習編譯反編譯,覺得需要從頭開始好好梳理一下。這個系列的博客我將按照拆彈個數一個個的分析,應該會有七篇。。。。。。

給出對應於7個階段的7篇博客

phase_1  https://www.cnblogs.com/wkfvawl/p/10632044.html
phase_2  https://www.cnblogs.com/wkfvawl/p/10636214.html
phase_3  https://www.cnblogs.com/wkfvawl/p/10651205.html
phase_4  https://www.cnblogs.com/wkfvawl/p/10672680.html
phase_5  https://www.cnblogs.com/wkfvawl/p/10703941.html
phase_6  https://www.cnblogs.com/wkfvawl/p/10742405.html
secret_phase  https://www.cnblogs.com/wkfvawl/p/10745307.html

 

解題前准備

Step1將下載的炸彈包拷貝到Linux主機上;

Step2:使用tar -xvf bomb名”進行解壓;

 

解壓后生成3個文件:

1README:炸彈所屬的用戶信息;

2bomb:二進制炸彈文件;

3bomb.c:二進制炸彈文件的框架源文件,供解題者參考。

Step3:使用objdump -d bomb對二進制炸彈進行反匯編,並將其保存到一個文本文件中。

 

注:

1、這里將反匯編生成的文件重定向到asm.txt,后續的解題過程均通過分析該文件進行。

2、本例所有的分析過程均在vim中進行,大家可以使用自己熟悉的工具。

phase_1

phase_1要求輸入一個字符串,二進制炸彈會判斷輸入的字符串是否與目標字符串相等。

觀察框架源文件bomb.c:

 

從上可以看出:

 

1、首先調用了read_line()函數,用於輸入炸彈秘鑰,輸入放置在char* input中。

2、調用phase_1函數,輸入參數即為input,可以初步判斷,phase_1函數將輸入的input字符串與程序內部的炸彈秘鑰進行比較。

因此下一步的主要任務是從asm.txt中查找在哪個地方調用了readline函數以及phase_1函數。

1.1 尋找並分析調用phase_1函數的代碼

打開asm.txt,在其中搜索phase_1

 

從上圖可以看出一些信息:

1、第330行:調用了read_line函數;read_line的返回結果(char* input)放置在eax累加器寄存器中。(從函數返回的結果一般都放置在eax寄存器中

2、第331行:將read_line函數的返回結果放置在當前esp棧指針寄存在指針指向的棧頂。

3、第332行:在邏輯地址0x8048b47位置調用了phase_1函數。同時也說明了phase_1函數的入口地址為0x8048c00

4、結合前面bomb.c的分析,從上可以看出第331行,是在為調用phase_1准備參數,我們可以分析出此時函數調用棧的情況:

5、從上面可以看出,phase_1函數入口在虛擬地址0x8048c00,下一步需要分析phase_1函數。

 

1.2 phase_1函數分析

asm.txt中尋找8048c00(或者繼續尋找phase_1)。

從上圖可以看出一些信息:

1、第378行:sub $0x1c, %esp,將函數棧空間擴展了0x1c字節(28個字節)
2、第379行:將0x804a3ec 放置到了esp+4的地方。
3、第381/382行:將input的內容放置到了esp的地方。注:20(%esp)正好是棧中存放input的內容。
4、第383行:調用strings_not_equal函數。
5、顯然,第379行以及第381/382行是在為調用strings_not_equal函數准備參數。在調用strings_not_equal函數之前(即382行執行之后,383行執行之前),

函數棧幀變成如下:

 

6、第384行:test %eax %eax,是對eax寄存器里的內容(string_not_equal函數的返回內容)進行位與操作,如果為0,則置zf標志(零標志)為1

7、第385行:是一個je指令,je指令判斷zf標志(零標志)為1時(也即strings_not_equal函數返回的是0的情況下),跳轉到phase_2 + 0x20的地方,即0x8048c20的地方,說明炸彈拆除成功。否則,call 804939b <explode_bomb>,顧名思義,是爆炸炸彈,即拆除炸彈失敗。

8、從上面的分析來看,上圖中顯示的棧幀中,esp的內容是輸入的字符串的首地址,而esp + 4的內容是0x804a3ec,應該是在程序中保存的被比較的字符串(即拆彈字符串)的首地址,而按照strings_not_equal的名字來看,如果是不等,則返回1,等則返回0。如果等,代表輸入的拆彈字符串是正確的。

C語言偽代碼:

 

int32_t strings_not_equal(int32_t a1, int32_t a2);

void explode_bomb(int32_t a1, int32_t a2);

void phase_1(int32_t a1) {
    int32_t eax2;
    int32_t v3;
    eax2 = strings_not_equal(a1, "Why make trillions when we could make... billions?");
    if (eax2 != 0) {
        explode_bomb(v3, a1);
    }
    return;
}

 

所以下一步應該在運行的時候,查看0x804a3ec地址的內容,這即是我們要輸入的拆彈字符串。

但為進一步判斷我們上面的分析,下面再大致分析一下strings_not_equal函數。

 

1.3 strings_not_equal函數分析

 

根據上面的代碼,可以看出strings_not_equal函數的地址在0x80490ba的地方。搜索80490ba或者strings_not_equal

 

 

 

執行第762 - 765行之后,函數棧幀為:

注意:

 

1、第766行,將esp + 0x14的內容(input(輸入字符串首地址))送入到了ebx寄存器,第767行,將esp + 0x18的內容(0x804a3ec)送入到了esi寄存器。驗證了我們前面所介紹的0x804a3ec地址所在的地方應該是拆彈字符串所在的首地址。

 2、768-770行:求input字符串的長度,結果送入到edi寄存器。

 3、771-772行:求0x804a3ec字符串的長度,結果保存在eax寄存器中。

 4773行:將1送入edx,通過后面的分析,可以知道edx存放的是返回結果,也即默認返回結果為1,即不等。

5、774-775行:比較edieax的內容,input字符串與0x804a3ec為首地址的字符串長度進行比較,如果不等,則跳轉到strings_not_equal + 0x63的地方:0x80490ba + 0x63 = 0x804911d(此地的指令是將edx的內容送入到eax,並返回,注意第773行,edx的內容被賦值為1),也即返回1,代表兩個字符串不等

6、后面的匯編代碼,是逐一比較兩個字符串的內容,如果相等,則返回0,如果不等則返回1

綜合前面的分析,以C語言來表示strings_not_equal,其大致含義是:

int32_t string_length(signed char* a1);

int32_t strings_not_equal(signed char* a1, signed char* a2) {
    signed char* ebx3;
    signed char* esi4;
    int32_t eax5;
    int32_t eax6;
    int32_t edx7;
    int32_t eax8;
    int32_t eax9;
    ebx3 = a1;
    esi4 = a2;
    eax5 = string_length(ebx3);
    eax6 = string_length(esi4);
    edx7 = 1;
    if (eax5 != eax6) {
        addr_0x804911d_2:
        return edx7;
    } else {
        eax8 = (int32_t)(uint32_t)(unsigned char)*ebx3;
        if (*(signed char*)&eax8 == 0) {
            edx7 = 0;
            goto addr_0x804911d_2;
        } else {
            if (*(signed char*)&eax8 == *esi4) {
                do {
                    ++ebx3;
                    ++esi4;
                    eax9 = (int32_t)(uint32_t)(unsigned char)*ebx3;
                    if (*(signed char*)&eax9 == 0) 
                        break;
                } while (*(signed char*)&eax9 == *esi4);
                goto addr_0x8049118_8;
            } else {
                edx7 = 1;
                goto addr_0x804911d_2;
            }
        }
    }
    edx7 = 0;
    goto addr_0x804911d_2;
    addr_0x8049118_8:
    edx7 = 1;
    goto addr_0x804911d_2;
}

 

以上C語言代碼基本和匯編代碼相對應,可以對照理解。

 

1.4 尋找拆彈字符串

 使用objdump --start-address=0x804a3ec -s bomb,即可查看以0x804a3ec開頭的段信息。下圖是一個示例,我們可以看出0x804a3ec開頭的字符串,正是前面找到的拆彈字符串!

從這里我們也可以看出,所有直接硬編碼進入代碼的字符串,以只讀數據的形式存放在只讀數據段中。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM