1. 程式人生 > >BombLab Phase-3 & Phase-4 &Phase-5

BombLab Phase-3 & Phase-4 &Phase-5

Phase_3

關於風格改變:因為一段段分析程式碼可以邏輯清晰有序一點,那麼就摒棄了以前先上彙編程式碼再畫棧幀圖的方式,我們直接給出函式呼叫的所有棧幀,這樣讀者有個大概印象後再看後面的彙編程式碼會邏輯清晰一點。

  • Phase_3棧幀圖

  • Scanf棧幀

因為scanf函式是動態連結庫的函式,比較特殊,不方便直接檢視反彙編,但我們可以畫出它的棧幀圖如下:

 反彙編phase_3函式

1.

開棧

首先,是標準的ebp進棧和棧擴張操作,我們看到棧擴張了40個位元組。

之後我們可以看到這段程式碼將 ebp-0x10 、ebp-0xc 、 0x804a23e 、ebp+8(也就是輸入字串的地址) 依此放在了 esp+0xc、esp+0x8、esp+0x4、esp的位置,把這些值都放在esp上面的位置,我們可以猜想這肯定是為了呼叫scanf函式用的。

那麼0x804a23e是什麼?放在scanf上什麼作用呢?我們檢視下記憶體:

第一個引數是我們傳入的是我們的輸入,第二個是“%d %d”,說明我們輸入的是兩個正整數,存放在了ebp-0x10 、ebp-0xc這兩個位置。

2.

scanf函式呼叫返回結果

Scanf函式返回的結果儲存在eax中,至於scanf函式返回的結果到底是什麼我們之後再談。現在我們知道返回結果 eax>1 炸彈才不會爆炸。

3.

這邊說明輸入的第一個值一定要<=7,否則就爆炸。

4.

我們可以看到這個跳轉語句是根據eax的取值進行跳轉。而eax裡存放的是ebp+0xc地址的值,也就是我們輸入的第一個數。那麼當我們輸入不同的數它將跳轉到不同的位置開始。而我們輸入的數只可能是 0 1 2 3 4 5 6 7。那麼我們依此看看這幾個數對應地址存放了什麼東西:

我們可以看到是7個地址,而且七個跳轉地址分別也可以在程式中找到。

那假設我們輸入的是0,那麼我們將跳轉到0x8048f12的位置。我們從這個位置繼續往下運算。

5.

我們發現eax賦值完畢後,又進行了跳轉。

之後又進行了一些列的加減運算:

Eax=0x314-0x35a+0x2ef-0x216+0x216-0x216+0x216-0x216=147

最後eax中的值為147.之後又進行跳轉。

到這裡我們突然發現輸入的第一個引數的值必須要 <5 ,所以之前那個《=7 還不夠,還好我們輸入的是0,不會爆炸。

接著,它又拿我們輸入的第二個數和eax中的值進行比較,如果不相等就爆炸,也就是說我們輸入的第二個數必須等於147.

函式結束!

我們測試結果:

成功!

注:

但是之前我們也提到了,輸入的第一個數的值只要<5就可以了,所以這關不僅僅是真麼一個解,還有對應的另外4個解,而每個輸入的第一個數都回決定第二個數的值,經過計算,分別的對應的幾個解為:

0,147),(1,-641),(2,217),(3,-534),(4,0),(5,-534

Phase_4

  • phase_4棧幀

  • Scanf函式棧幀

可以看到,第四階段的棧幀和第三階段是一摸一樣的。

Phase_4和phase_3都是一開始要求輸入兩個整數。

下面進phase_4的具體分析

1.

常規得,這些都是為了呼叫scanf函式準備,可以看見,輸入的兩個數儲存在了ebp-0x10和 ebp-0xc的位置。

2.

從這裡也可以看出,輸入不輸入兩個數,就爆炸了。

3.

有兩個跳轉,第一是輸入的第一個數為負數爆炸,輸入的數>15也爆炸。

所以現在看來輸入的第一個數應該在[0,15]之間。

4.

可以看到又要呼叫func4函數了,所以這裡也是為了在做引數傳遞給func4。可以看到傳給func4的三個引數分別為(你輸入的第一個數,0,14)

5.

Func返回後,要求返回值必須為1,不然就爆照。

當返回值為1,又會拿你輸入的第二個數(ebp-0x10)和1比較,如果等於1,則函式結束,不為1,則又爆炸。

總結來看,我們要輸入兩個數,第二個數必須為1,並且我們輸入的第一個數又要使func4的返回值為1,並且這個數在0~15之間,那麼現在的目標轉移到func4上面。

6.

func4進行反彙編

沒想到func4函式彙編還要長,我們還是先給出棧幀:

      

麻煩的是這是一個遞迴呼叫,並且我們觀察到函式裡面遞迴的地方不只一個,情況比較複雜,所以我們簡略畫一個棧幀圖,而讀者暫且明白這是一個遞迴呼叫即可。

7.Func4程式碼分析

標準函式開頭,注意,暫存器%ebx, %esi, %edi被劃分為由被呼叫者儲存的暫存器。之後,分別將引數1,引數2,引數3賦給了 edx(你輸入的數),eax(0),ebx(14)。

8.

這裡全是算術運算,為表述清楚,我們將寫出算術過程:

Ecx=ebx=14;

Ecx=ecx-eax=14-0=14;

Esi=ecx=14;

Esi=0=esi>>31;(邏輯右移)

Ecx=14+0=14;

Ecx=7=ecx/2=ecx>>1;(算術右移)

Ecx=ecx+eax=7+0=7;

將7和你輸入的第一個數比較,

如果輸入數>7,跳轉到func4+64;

那麼當大於7,跳轉到這裡:

eax置為0,即返回值為0,然後比較ecx和edx,

7>=輸入的數則又跳轉

這裡函式就返回了,並且返回值為0;

7<輸入的數則進行下面這步:

又是依此遞迴入口:func4(你輸入的數,7+1,14)

但是一定要注意返回值這個遞迴的返回值最後還要+1,因為有個lea語句!

那麼如果 <=7則到這裡:

 

Ecx=7-1=6;

然後又遞迴func4(edx=輸入的數, 0 , 6);

假設遞迴完成,得到返回值之後,

返現他要將返回值*2再返回。

注意,以上都是我第一層的遞迴分析,當遞迴深入下去,其中的7和其他常數都會不同。鑑於以上的程式碼分析,直覺告訴我們,通過彙編反推結果幾乎不可能,你幾乎肯定會出錯,所以我們轉化為C程式碼:

反推沒有思路,所以又要走另一條路:列舉

因為我們知道了x的範圍是0-15,所以我們只要從0-15來列舉返回值,找到返回值為1的情況即可。

所以,我們要用如下程式碼測試返回值:

輸出結果:

終於找到了三個符合的數 8、9、11,我們測試看看:

Phase_5

  • Phase_5 棧幀

由於scanf函式棧幀和前面一摸一樣,就不重複了

1.Phase_5彙編程式碼分析

如常,這也是常規的開棧操作。

2.

這個樣式的程式碼我們也見過了很多,這個是為scanf函式呼叫做準備,唯一注意的是需要輸入幾位數,但根據臨時變數的數量來看,我們幾乎確定就是要輸入兩個數。

不信,我們檢視格式控制串內容:

的確是輸入兩個數。

3.

將scanf返回值和1比較,即如果只輸入了一個數,那麼炸彈爆炸。說明輸入兩個數。

4.

第一二行程式碼是取出了我們輸入的第一個數,然後將這個數和 0xf進行與運算,這個與運算的意義是取出該數二進位制表示的低四位的值,然後又賦值回去,將我們輸入的數進行了改變。例如:

0xf  & 31 = 1111 & 11111=1111;

之後,我們發現它又將 運算結果和0xf做比較,如果相等的話就跳轉到+106的地方,就爆炸了,所以我們輸入的第一個數的二進位制表達的低四位一定不能是1111.

5.

這裡我們觀察到了最後一個跳轉語句是往上跳轉,說明這裡開始是一個迴圈語句。

我們可以這樣來看這個迴圈:

這裡說明了如果eax的最終運算結果不是15就會一直迴圈,並且我們知道eax的初值是<15的,而eax每次迴圈之後的值為4*eax+ebx地址存放的值。

6.

這裡,我們將第二個變數值賦值為15了,然後我們看edx的值是多少,從5中我們可以明顯看出edx是迴圈計數的臨時變數,相當於c中的i,如果edx!=15,也要爆炸,所以這個迴圈必須執行滿15次才不會爆照。

那這樣,綜合5中的資訊,我們得到的就是  這個迴圈我們必須要執行15次,並且執行完畢後eax的值必須為15,否則失敗。

7.

這裡將ecx與我們輸入的第一個數進行比較(從5中我們知道ecx是我們儲存計算結果的一個變數),如果相等則成功,,否則,爆炸。

8.

好了,程式碼我們大致分析完了,這道題的意圖就是讓我們輸入兩個數,我們要對輸入的第二個數進行一系列操作——與操作+迴圈,並且迴圈裡參與運算的還有一個變數,在迴圈完畢後,這個變數值要和輸入的第一個數值相等。就是讓我們找到這樣的一個數對。那麼怎麼找呢?

關鍵點就在於我們之前看到的那個5中賦給ebx地址值,我們看到ecx的最終取值,都是基於ebx這個地址再+eax*4的偏移量去取值,那麼我們猜想ebx這個地址值之後肯定有一個值等於15;

所以我們開始就檢視一個ebx地址之後存放了哪些值?

當我們去檢視它後面20個數字的時候,是不是發現了什麼不得了的事?原來這些值都被儲存在了一個數組裡!而且一共16個數字!

我們正著去找怎麼得到15這很難,因為我們都不知道輸入的數是多少,那麼我們就因該轉化一下思路,反過來找!

我們的目的是15,我們發現15在距離陣列首地址偏移了6個位置,而我們知道一個int佔4位元組,所以怪不得它eax要乘以4!那麼我們就可以確定上一個eax的值一定為6.

逆推得到:15->6

好,現在eax=6,那麼我們在陣列又去找6,發現6在首地址偏移14個位元組,同理,我們可以確定下一個eax=14;

更新:15->6->14

同理,我們可以一直尋找,並且尋找15次,更新這個序列,我們得到逆推序列為:

15->6->14->2->1->10->0->8->4->9->13->11->7->3->12->5

最後,原來eax=5,這就說明我們輸入的第一個數二進位制表達的低四位一定是:

0101.

推完了第一個數字,現在看看第二個數字,第二個數字是同時在迴圈進行時進行運算的,並且也就是對eax的一個累加過程,所以我們現在有了運算序列,可以很輕鬆的計算出ecx的值:

但是要注意:

Eax是先變化之後,才被ecx累加,所以eax的初值5並沒有被加到ecx中!

Ecx=12+3+7+11+13+9+4+8+0+10+1+2+14+6+15=115;

所以第二個數為115;

綜,第一個數,我們只要湊出一個低位為0101的值即可,那麼很簡單我們就取5,第二個數已經固定是115,那麼就輸入115.

結果:

Bingo!

正確。

提示:此題的第一個數的答案可以變化,因為我們只要最低的四位數保持不變,所以每次你可以在5的基礎上加上16的整數倍,因為0101既然不能變,那麼最低的累加單位只能是10000,即16了。