二進制炸彈總結(jié)
最近在學(xué)習(xí)gdb的使用和部分匯編指令的意義,在查找資料的過程中發(fā)現(xiàn)南京大學(xué)的mooc質(zhì)量很高,于是跟著老師們學(xué)完了整個mooc,對gdb和匯編指令有了一個大概的了解。最后磕磕絆絆地完成了很有意思的課程大作業(yè):二進制炸彈,下面對整個大作業(yè)進行一個總結(jié)。
本次作業(yè)的環(huán)境為:WSL2 20.04.5 LTS,gcc 9.4.0, gdb 9.2。作業(yè)的下載地址為https://cs.nju.edu.cn/sufeng/course/mooc/0809NJU064_bomblab.tar。二進制炸彈一共有7個普通階段和1個隱藏階段。
phase_0
phase_0 是所有階段里面最簡單的,從反匯編代碼中也能看出來,主要是判斷我們的輸入和地址0x804a198
中保存的字符串是否一樣,直接使用x/s 0x804a198
即可查看應(yīng)該輸入的字符串。然后運行我們的程序,即可看到成功通過phase_0 的提示
phase_1
phase_1 的主要考察內(nèi)容是浮點數(shù)的表示,使用b phase_1
將斷點打在phase_1處,然后使用si
在phase_1 內(nèi)單步執(zhí)行指令然后查看結(jié)果。
這里通過分析匯編指令可以知道,這段代碼的意思是先將0x1a4eccf6
整個16進制數(shù)送到$ebp-0xc
的位置,然后將其中的值送到浮點寄存器棧頂,再從浮點寄存器棧頂把值存到$epb-0x18
的位置。接下來使用sscanf
從命令行讀入,這里有一個明碼地址,由于對sscanf的調(diào)用比較了解,因此這里猜測,這個明碼地址應(yīng)該是輸入格式,使用x/s 0x804a1bd
進行查看,果不其然,這里讓我們從命令行輸入兩個整數(shù),接下來程序判斷輸入的是不是兩個數(shù),如果不是則炸彈爆炸。
了解了上面匯編的意思,下面的代碼就很簡單了。先將這個浮點數(shù)的低32位與第一個輸入判斷,如果不相等炸彈直接爆炸,否則繼續(xù)將高32位與第二個輸入對比,若都成功則通過phase_1。這里如果不想手動計算,可以有取巧的方法,直接將斷點打在0x80494e3
,然后查看寄存器edx
的值,然后第一個輸入即為這個值,接下來如法炮制,將斷點打在0x80494f2
即可獲得第二個輸入值,過程如下所示:
當(dāng)然如果手算也可以,只是會比較麻煩(這里的28是轉(zhuǎn)換成1.x的形式需要移位28位)。
接下來將我們得到的值輸入查看結(jié)果,成功通過phase_1。
phase_2
phase_2主要是讓輸入一串?dāng)?shù)字,數(shù)字之間滿足一定的規(guī)律。老規(guī)矩還是分析匯編代碼,然后使用gdb調(diào)試??梢钥吹皆趐hase_2中是需要調(diào)用read_n_numbers
這個函數(shù)的,這樣的話就先進入read_n_numbers
,看看它是如何實現(xiàn)的。ps:這里直接說吧,在我的程序中是需要輸入8個數(shù),具體可以看匯編代碼,這里就不再分析。這里直接進入read_n_numbers
函數(shù)查看輸入,可以看到內(nèi)部調(diào)用strtok
將從標(biāo)準(zhǔn)輸入讀取數(shù)據(jù)進行分割,然后使用sscanf
讀取數(shù)據(jù)。
此外這里還有兩個明碼地址,我們打印一下,發(fā)現(xiàn)讀入的應(yīng)該是8個整數(shù),且整數(shù)之間需要用,
隔開。
繼續(xù)查看匯編代碼,使用gdb將斷點打在0x8049532
處,因為這里有一個比較$0x8a,%eax
。重新運行程序,設(shè)置輸入為8, 7, 6, 5, 4, 3, 2, 1
,這樣有順序方便后續(xù)檢查,可以看到當(dāng)前eax
的值剛好為我們輸入的第一個值,因此可以得到結(jié)論,第一個輸入的值應(yīng)該是0x81
即138。
后續(xù)可以按照這個方法直接一個一個往下試,這樣就可以得到所有的數(shù)據(jù),最終為138, 137, 134, 129, 122, 113, 102, 89
。不過這樣做太低效了,我們可以繼續(xù)分析匯編代碼,后面可以發(fā)現(xiàn)其實這些輸入的數(shù)據(jù)之間滿足一定的規(guī)律,這里對主要的代碼使用紅框和綠框進行了標(biāo)注。假如我們第一個數(shù)據(jù)輸入正確的話,那么會在$ebp-0xc
的地方存入1,即當(dāng)前讀入數(shù)據(jù)的個數(shù),然后跳轉(zhuǎn)至0x8049584
,比較這個$ebp-0xc
與7的大小,如果小于等于7則會進行一個循環(huán),循環(huán)的主體即為紅框和綠框的內(nèi)容。這個循環(huán)到底是什么意思呢,其實寫成C代碼會很容易看出來
for (int i = 1; i <= 7; i++) {
if (array[i] != array[i - 1] - 2 * i + 1) {
explode_bomb();
}
}
i就是eax寄存器中的內(nèi)容,在紅框內(nèi)將數(shù)據(jù)備份在ebx,然后在綠框中計算-2*i
的值,再和ecx寄存器中的內(nèi)容相加,最后再加1。ecx寄存器的值在紅框中給出了計算過程,即為$ebp+4*$eax-0x2c
地址中保存的值,經(jīng)檢查當(dāng)eax寄存器的值為0時,則edx的值為138,
由此可以推斷出,從$ebp-0x2c
起始存了8個數(shù),然后繼續(xù)分析下面的邏輯就很簡單了,eax-1是為了方便下面獲得首地址,然后將上一個值保存在ecx寄存器中。
接下來將我們獲取得到的數(shù)據(jù)輸入到程序中,很輕松就過了phase_2。
phase_3
來到phase_3 我們首先又遇到了熟悉的sscanf輸入和明碼地址,先查看一下保存的內(nèi)容,發(fā)現(xiàn)又是讓輸入兩個數(shù)。
繼續(xù)查看匯編代碼,碰到第一個需要注意的地方,這里要求我們的第一個輸入減7之后不能比9小,結(jié)合下面代碼不難看出這是一個switch-case
結(jié)構(gòu),數(shù)一下跳轉(zhuǎn)數(shù)量,發(fā)現(xiàn)應(yīng)該有9個case分支,因此這里可以確定下來第一個輸入應(yīng)該為16。在正確輸入第一個數(shù)之后,會跳轉(zhuǎn)至0x8049652
, 然后比較輸入的第二個數(shù)和$ebp-0xc
位置的數(shù) 。
這里我們輸入16, 666
,設(shè)置666方便后面排查錯誤。將斷點打到0x8049652
,然后單步執(zhí)行再查看這個地方的數(shù)據(jù),發(fā)現(xiàn)輸入應(yīng)該為973,重新運行程序并輸入16 973
可以看到,phase_3 已成功通過。
phase_4
phase_4 應(yīng)該是所有關(guān)卡里面最難的一個了,因為設(shè)計遞歸,所以調(diào)試起來會非常痛苦。先來看匯編代碼
熟悉的sscanf,熟悉的輸入兩個數(shù)。我一開始看錯了,直接輸入了4, 30
,結(jié)果還真給它通過了。。。然后就沒管,繼續(xù)做后面的,但是等做到secret_phase的時候回頭一看心態(tài)當(dāng)場爆炸。這里提前爆料一下,secret_phase是跟phase_4相關(guān)的,這里只要再多輸入一個密碼,就能進入隱藏關(guān)卡。誤打誤撞答案正確了,但是過程是什么樣的還是不清楚,這里還是要老老實實地進行調(diào)試。
將斷點打在0x8049734
,這里push了三個值,說明func4函數(shù)有三個形參。第一個是我們輸入的第一個數(shù),第二個是4,第三個是48。然后進入func4繼續(xù)查看該函數(shù)的功能,這一段的主要內(nèi)容就是判斷我們的第一個輸入和(48-4)/2+4哪個大,主要是用移位操作來完成。
接下來我實在分析不動了,直接上ida,通過反匯編可以得到下面的代碼:
int __cdecl func4(int a1, int a2, int a3)
{
int v4; // [esp+Ch] [ebp-Ch]
v4 = (a3 - a2) / 2 + a2;
if ( v4 > a1 )
return func4(a1, a2, v4 - 1) + (v4 >> 1);
if ( v4 >= a1 )
return (a3 - a2) / 2 + a2;
return func4(a1, v4 + 1, a3) + 2 * v4;
}
signed int __cdecl phase_4(int a1)
{
signed int result; // eax
int v2; // [esp+0h] [ebp-18h]
int v3; // [esp+4h] [ebp-14h]
int v4; // [esp+8h] [ebp-10h]
int v5; // [esp+Ch] [ebp-Ch]
v5 = __isoc99_sscanf(a1, "%d %d", &v3, &v2);
if ( v5 == 2 && v3 > 3 && v3 <= 48 )
{
v4 = func4(v3, 4, 48);
if ( v4 == v2 )
{
result = 1;
}
else
{
explode_bomb();
result = 0;
}
}
else
{
explode_bomb();
result = 0;
}
return result;
}
這下差不多明白了,輸入了兩個數(shù)v3和v2,v3要滿足大于3小于等于48的條件,然后調(diào)用func4(v3, 4, 48)
,要它的結(jié)果等于v2。直接看phase_4的結(jié)尾,這里將eax的值和$ebp-0x10
進行對比(func4的返回值),如果相等就成功退出pahse_4。
為了驗證我們的結(jié)論,先輸入一個錯誤的輸入4 32
,直接將斷點打在0x0804973d
,然后查看這個比較的值,發(fā)現(xiàn)它等于30,說明我們輸入的第二個值確實應(yīng)該為30。
然后重新運行程序,輸入4 30
,可以看到已成功解決phase_4。
phase_5
phase_5應(yīng)該是所有的phase里面最有意思的一個了。phase_5要處理的是一個數(shù)組,需要先找到一個入口,然后按照數(shù)組的順序走到f的位置,輸入起始位置和路徑上數(shù)字的總和。老樣子依舊是看匯編代碼,檢查明碼地址,發(fā)現(xiàn)讓輸入兩個整數(shù)。
繼續(xù)往下讀,發(fā)現(xiàn)了我們的輸入每次都要和0xf
進行與位操作,然后一共要進行7次,每次都將當(dāng)前的結(jié)果加到$ebp-0x10
,并且會跳轉(zhuǎn)到下一個節(jié)點,最后比較我們的第二個輸入和這個值是否相等。
這里我們直接看從這個地址開始存了哪些東西
排列一下
10 2 14 7 8 12 15 11 0 4 1 13 3 9 6 5
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
上面已經(jīng)分析了要7次跳轉(zhuǎn)到15,可以倒著找15 -> 6 -> 14 -> 2 -> 1 -> 10 -> 0 -> 8
,所以輸入應(yīng)該是8 48
。重新運行程序,輸入正確的數(shù)對,可以看到結(jié)果正確,成功通過phase_5
phase_6
phase_6主要考察鏈表結(jié)構(gòu),查看匯編代碼,發(fā)現(xiàn)一開始要讀入n個數(shù),和phase_2一樣,因此不做過多分析,繼續(xù)閱讀代碼發(fā)現(xiàn)輸入的應(yīng)該是7個數(shù)。然后從$ebp-0x34
開始依次存入。
繼續(xù)閱讀源碼,我們不難發(fā)現(xiàn)鏈表的起始位置應(yīng)該在$ebp-0x50
處,直接將斷點打在0x80498e2
,然后開始逐步打印節(jié)點的信息。
可以看到節(jié)點的順序為6 -> 0 -> 8 -> 5 -> 7 -> 4 -> 2
,最后需要按照從大到小的順序輸出,因此最終的輸入為2, 7, 6, 4, 1, 5, 3
6 -> 0 -> 8 -> 5 -> 7 -> 4 -> 2
2 7 6 3 1 4 5
phase_secret
最后這一塊是綜合考察,比較考驗?zāi)托?。這里由于老師提前說過,需要在phase_4多輸入一個“密碼”,不然炸彈會自動defuse
,所以我這里直接看phase_defused
這里發(fā)生了什么。
發(fā)現(xiàn)了一共有三個明碼地址,都打印一下,正好是phase_4的輸入。
因此我們得修改一下phase_4階段的輸入,能看到這里已經(jīng)進入了secret_phase
。
繼續(xù)閱讀secret_phase
的源碼,發(fā)現(xiàn)我們輸入需要在一定的范圍內(nèi)
(
0
,
1001
]
(0, 1001]
(0,1001]
然后將輸入帶入到fun7,然后將結(jié)果與1進行比較,之后這里又有一個明碼地址,打印一下,發(fā)現(xiàn)是結(jié)束提示語,說明我們要做的就是讓fun7的返回值等于1。
進入fun7,發(fā)現(xiàn)代碼非常長,于是我直接使用ida來看偽代碼,要讓返回值等于1,那就只能是最后這個條件。最后這個條件又遞歸調(diào)用了fun7一次,現(xiàn)在需要知道的就是a1到底是多少,繼續(xù)閱讀secret_phase的偽代碼,發(fā)現(xiàn)這里輸入了一個n1,直接查看:
找到了n1的地址是0x0804c1d0
使用gdb打印該節(jié)點,因為只需要遞歸一次,所以只用看下一個節(jié)點的值即可,可以看到結(jié)果為50
輸入50,可以看到隱藏炸彈也被拆除了。文章來源:http://www.zghlxwxcb.cn/news/detail-519136.html
以上就是整個作業(yè)的流程了,斷斷續(xù)續(xù)做了一天半才勉強做完,有一說一匯編真的很難,不過如果有像ida這種專業(yè)的工具的話,其實做起來就很輕松啦,這里分享一個我下載的綠色版ida的網(wǎng)址:https://www.32r.com/soft/42075.html,一個匯編指令具體意思的網(wǎng)址:https://shell-storm.org/x86doc/,以及常用的gdb調(diào)試指令的網(wǎng)址:https://jyywiki.cn/pages/OS/manuals/gdb-cheat-sheet.pdf文章來源地址http://www.zghlxwxcb.cn/news/detail-519136.html
到了這里,關(guān)于二進制炸彈的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!