這個實驗曾經做過
這次又做了一遍,對之前的過程作了補充
binary bomb
一、實驗目的:
增強對程序機器級表示、匯編語言、調試器和逆向工程等理解。
二、實驗要求:
1熟練使用gdb調試器和objdump;
2單步跟蹤調試每一階段的機器代碼;
3理解匯編語言代碼的行為或作用;
4“推斷”拆除炸彈所需的目標字符串。
5在各階段的開始代碼前和引爆炸彈函數前設置斷點,便於調試。
三、實驗內容:
准備:
使用objdump –d bomb > asm.txt命令,將bomb程序逆成匯編代碼,分析匯編代碼。
Phase_1:
匯編代碼如圖
代碼分析:
這是一個字符串比較,所以應該是和內存中的已有內容比較。
有一個內存地址,我們去看一下內存0x804a124的內容
找到字符串,輸入之后
通過。
phase_2:
代碼分析:
push %esi
push %ebx
sub $0x34,%esp
lea 0x18(%esp),%eax
mov %eax,0x4(%esp)
mov 0x40(%esp),%eax
mov %eax,(%esp)
call 804911c <read_six_numbers> ;讀入6個數
cmpl $0x0,0x18(%esp) ;第一個數和0比較
jne 8048bdb <phase_2+0x27> ;如果不等於0就爆炸,所以第一個為0
cmpl $0x1,0x1c(%esp) ;第二個數和1比較
je 8048bfa <phase_2+0x46> ;如果等於1就跳走了,不等於就爆炸了
call 80490f5 <explode_bomb> ;爆炸
jmp 8048bfa <phase_2+0x46>
mov -0x8(%ebx),%eax ;eax = a[i-2] 存上上個數
add -0x4(%ebx),%eax ;eax = a[i - 2] + a[i - 1] 存上上個數與
cmp %eax,(%ebx) ;相當於把eax賦給ebx,也就是把當前位置的相鄰
je 8048bf1 <phase_2+0x3d> ;ebx必須和eax相等,否則就爆炸
call 80490f5 <explode_bomb>
add $0x4,%ebx ;ebx+4,ebx指向了新的位置
cmp %esi,%ebx ;ebx是否和ebx+30相等,不等就跳回到8048be2,循環
jne 8048be2 <phase_2+0x2e>
jmp 8048c04 <phase_2+0x50>;相等,結束
lea 0x20(%esp),%ebx ;ebx指向新的地址
lea 0x30(%esp),%esi ;esi始終存的是地址esp+30
jmp 8048be2 <phase_2+0x2e>
add $0x34,%esp
pop %ebx
pop %esi
ret
根據規律得出最后是a[i] = a[i - 2] + a[i - 1],而且第一個數是0,第二個數是1,遞推出
0 1 1 2 3 5
輸入
通過。
Phase_3:
截取部分代碼:
代碼分析:
sub $0x2c,%esp
lea 0x1c(%esp),%eax
mov %eax,0xc(%esp)
lea 0x18(%esp),%eax
mov %eax,0x8(%esp)
movl $0x804a2ef,0x4(%esp)
mov 0x30(%esp),%eax
mov %eax,(%esp)
call 8048860 <__isoc99_sscanf@plt> ; 輸入
cmp $0x1,%eax ;至少輸入2個數
jg 8048c3b <phase_3+0x31> ;否則就爆炸
call 80490f5 <explode_bomb>
cmpl $0x7,0x18(%esp); <=7
ja 8048c7e <phase_3+0x74> ;如果第一個操作數大於7 ,就爆炸
mov 0x18(%esp),%eax
jmp *0x804a180(,%eax,4) ;后面有截圖,可以看到,在這個地址處存放着一些地址,根據輸入的eax的值,跳到相應位置
;下面是8種情況 后面依次根據第一個數的內容跳到相應位置
mov $0x90,%eax ;第一個數是0,跳到這里,將0x90放到eax中
jmp 8048c8f <phase_3+0x85>
mov $0x166,%eax
jmp 8048c8f <phase_3+0x85>
mov $0x160,%eax
jmp 8048c8f <phase_3+0x85>
mov $0x1e2,%eax
jmp 8048c8f <phase_3+0x85>
mov $0x2c5,%eax
jmp 8048c8f <phase_3+0x85>
mov $0x2e0,%eax
jmp 8048c8f <phase_3+0x85>
mov $0x3d8,%eax
jmp 8048c8f <phase_3+0x85>
call 80490f5 <explode_bomb>
mov $0x0,%eax
jmp 8048c8f <phase_3+0x85>
mov $0x15d,%eax
;---------------------------------------------------------所有的條件最后都匯總到這里,判斷第二個數
cmp 0x1c(%esp),%eax ;將輸入的第二個數字和從對應地址賦值的數比較,不相等就爆炸
je 8048c9a <phase_3+0x90>
call 80490f5 <explode_bomb>
add $0x2c,%esp
ret
用gdb看看內存里位置0x804a180位置處的內容是:
里面存的都是代碼的地址,也就是要跳的位置
這個是條件判斷,根據輸入的第一個數,看第二個數是不是和程序的賦給的內容一樣。
所以一共有8種情況,任意輸入一種即可
如果第一個數是0,跳到0x8048c4d位置,把0x90給了eax,所以第二個就是0x90,也就是十進制的144
輸入 0 144
通過
Phase_4:
這個是一個遞歸
代碼截圖:
代碼分析:
sub $0x2c,%esp
lea 0x1c(%esp),%eax
mov %eax,0xc(%esp)
lea 0x18(%esp),%eax
mov %eax,0x8(%esp)
movl $0x804a2ef,0x4(%esp)
mov 0x30(%esp),%eax
mov %eax,(%esp)
call 8048860 <__isoc99_sscanf@plt>;輸入
cmp $0x2,%eax ;輸入兩個數,不是的話就爆炸
jne 8048d2e <phase_4+0x33>
cmpl $0xe,0x18(%esp) ;第一個數小於等於14,否則爆炸
jbe 8048d33 <phase_4+0x38>
call 80490f5 <explode_bomb>
movl $0xe,0x8(%esp) ;舊[esp+8] = e
movl $0x0,0x4(%esp) ;舊[esp+4] = 0
mov 0x18(%esp),%eax
mov %eax,(%esp)
call 8048c9e <func4> ;調用func4
cmp $0xb,%eax ;func4返回的一定是11,否則爆炸
jne 8048d5b <phase_4+0x60>
cmpl $0xb,0x1c(%esp) ;第二個數一定是11,否則就爆炸
je 8048d60 <phase_4+0x65>
call 80490f5 <explode_bomb>
add $0x2c,%esp
ret
phase_4函數其中還調用了一個函數func4(),func4()返回的值是11,我們進行推斷可以得出輸入時0 11或者1 11
分析着很麻煩,發現第二個數必須是0xb,一定是11,前面有第一個數必須小於15,一共沒幾個情況,一個個試一下唄!發現很幸運的輸入 0 11就過了!
Phase5:
代碼如下:
代碼分析:
08048d64 <phase_5>:
8048d64: 53 push %ebx
8048d65: 83 ec 18 sub $0x18,%esp
8048d68: 8b 5c 24 20 mov 0x20(%esp),%ebx
8048d6c: 89 1c 24 mov %ebx,(%esp)
8048d6f: e8 57 02 00 00 call 8048fcb <string_length>
8048d74: 83 f8 06 cmp $0x6,%eax ;輸入字符串長度一定是6,否則爆炸
8048d77: 74 05 je 8048d7e <phase_5+0x1a>
8048d79: e8 77 03 00 00 call 80490f5 <explode_bomb>
8048d7e: ba 00 00 00 00 mov $0x0,%edx ;賦初值
8048d83: b8 00 00 00 00 mov $0x0,%eax ;賦初值
8048d88: 0f b6 0c 03 movzbl (%ebx,%eax,1),%ecx ;0擴展,
; ecx = ebx + eax
8048d8c: 83 e1 0f and $0xf,%ecx
;ecx = ecx & 0xf ,和0xf按位與,控制在0~15之間
8048d8f: 03 14 8d a0 a1 04 08 add 0x804a1a0(,%ecx,4),%edx ;
;在內存中相應位置取出數字加到edx 中
8048d96: 83 c0 01 add $0x1,%eax
;eax自加
8048d99: 83 f8 06 cmp $0x6,%eax
;eax和6比較,一共循環6次
8048d9c: 75 ea jne 8048d88 <phase_5+0x24>
8048d9e: 83 fa 2d cmp $0x2d,%edx
;最后的edx中一定是0x2d,我們要找到這樣的組合即可
8048da1: 74 05 je 8048da8 <phase_5+0x44>
8048da3: e8 4d 03 00 00 call 80490f5 <explode_bomb>
8048da8: 83 c4 18 add $0x18,%esp
8048dab: 5b pop %ebx
8048dac: c3 ret
經過分析得出,我們要輸入6個字符,然后對每個字符對16取模之后得到[0,15]的數,推測內存里應該有16個數,代碼里還有一個內存地址,把取模后的數作為偏移地址,在內存中找到相應的值,加到一起,和是45就可以。
看一下內存地址有啥
前16個看起來靠譜,和我們得到的結論一樣。
現在只需要湊出6個數之和是45就行,湊個777888,然后在內存里這個數組中看一下,有7和8,下標分別是9和13,然后這6個數都是對16取模之后的下標,讓他們幾個都同時加32,得到41 41 41 45 45 45,對應的ascii表中的字符就是 )))--- ,輸入后通過
同理,然后還可以加32 + 16,得到 57 57 57 61 61 61,對應的ascii碼表中的字符就是===999,輸入后:
通過
Phase_6:
代碼分析:
輸入6個數后,先進行了兩個判斷:1、判斷是否都是在[1,6]上的數 2、判斷有沒有重復
;判斷是否在[1,6]內
8048dcf: 83 e8 01 sub $0x1,%eax ;eax--
8048dd2: 83 f8 05 cmp $0x5,%eax ;如果eax是小於等於0的數,減一后和5無符號比較,是大於5的;
;如果eax是大於6的數,減一后還是大於5;
;所以 輸入的數字一定要 在區間 [ 1 , 6 ] 內
8048dd5: 76 05 jbe 8048ddc <phase_6+0x2f> ;無符號比較<=
8048dd7: e8 19 03 00 00 call 80490f5 <explode_bomb>
8048ddc: 83 c6 01 add $0x1,%esi ;esi作為偏移
8048ddf: 83 fe 06 cmp $0x6,%esi ;一共循環6次
8048de2: 75 07 jne 8048deb <phase_6+0x3e>
8048de4: bb 00 00 00 00 mov $0x0,%ebx ;6次循環結束
8048de9: eb 38 jmp 8048e23 <phase_6+0x76> ;跳出循環
;判斷是都有重復的
8048deb: 89 f3 mov %esi,%ebx
8048ded: 8b 44 9c 10 mov 0x10(%esp,%ebx,4),%eax ;根據ebx取出一個輸入的數
8048df1: 39 44 b4 0c cmp %eax,0xc(%esp,%esi,4) ;取出的每個數都依次和其他的每個數字比較
8048df5: 75 05 jne 8048dfc <phase_6+0x4f>
8048df7: e8 f9 02 00 00 call 80490f5 <explode_bomb> ;每個數字不能重復,否則爆炸
8048dfc: 83 c3 01 add $0x1,%ebx
8048dff: 83 fb 05 cmp $0x5,%ebx
8048e02: 7e e9 jle 8048ded <phase_6+0x40>
8048e04: eb c5 jmp 8048dcb <phase_6+0x1e>
;判重結束;
接着根據輸入構建一個鏈表
8048e12: ba 3c c1 04 08 mov $0x804c13c,%edx ;將第一個元素的下標放到edx里
8048e17: 89 54 b4 28 mov %edx,0x28(%esp,%esi,4) ;把下一個的地址放到棧里
8048e1b: 83 c3 01 add $0x1,%ebx ;ebx++
8048e1e: 83 fb 06 cmp $0x6,%ebx ;循環6次
8048e21: 74 17 je 8048e3a <phase_6+0x8d>
;兩個判斷結束后跳到這里
8048e23: 89 de mov %ebx,%esi ;esi = ebx
8048e25: 8b 4c 9c 10 mov 0x10(%esp,%ebx,4),%ecx ;依次取出輸入的幾個數
8048e29: 83 f9 01 cmp $0x1,%ecx
8048e2c: 7e e4 jle 8048e12 <phase_6+0x65> ;如果取出的數是1,跳到0x8048e12
8048e2e: b8 01 00 00 00 mov $0x1,%eax
8048e33: ba 3c c1 04 08 mov $0x804c13c,%edx ;將第一個元素的下標放到edx里
8048e38: eb cc jmp 8048e06 <phase_6+0x59>
;根據輸入的6個數構建一個鏈表
8048e3a: 8b 5c 24 28 mov 0x28(%esp),%ebx ;取出下一個地址
8048e3e: 8d 44 24 2c lea 0x2c(%esp),%eax
8048e42: 8d 74 24 40 lea 0x40(%esp),%esi
8048e46: 89 d9 mov %ebx,%ecx
8048e48: 8b 10 mov (%eax),%edx ;將下一個地址放到edx中
8048e4a: 89 51 08 mov %edx,0x8(%ecx) ;將這個地址給ecx+8
8048e4d: 83 c0 04 add $0x4,%eax ;指向下一個元素
8048e50: 39 f0 cmp %esi,%eax ;走到最后一個跳出
8048e52: 74 04 je 8048e58 <phase_6+0xab>
8048e54: 89 d1 mov %edx,%ecx ;把下一個地址給ecx
8048e56: eb f0 jmp 8048e48 <phase_6+0x9b>
8048e58: c7 42 08 00 00 00 00 movl $0x0,0x8(%edx)
8048e5f: be 05 00 00 00 mov $0x5,%esi
要求鏈表的值是遞減的
;這個鏈表中的值一定要是降序的
8048e64: 8b 43 08 mov 0x8(%ebx),%eax ;下個元素的地址給eax
8048e67: 8b 00 mov (%eax),%eax ;取出下個元素的地址的值
8048e69: 39 03 cmp %eax,(%ebx) ;ebx的值>=eax 成功跳轉,否則爆炸;也就是一定要滿足降序規則
8048e6b: 7d 05 jge 8048e72 <phase_6+0xc5>
8048e6d: e8 83 02 00 00 call 80490f5 <explode_bomb>
8048e72: 8b 5b 08 mov 0x8(%ebx),%ebx ;讀入下一個地址
8048e75: 83 ee 01 sub $0x1,%esi
8048e78: 75 ea jne 8048e64 <phase_6+0xb7> ;zf=0跳,也就是esi不等於1時跳
;esi等於1時結束,循環5次,相繼讀入下一個元素的值
8048e7a: 83 c4 44 add $0x44,%esp
8048e7d: 5b pop %ebx
8048e7e: 5e pop %esi
8048e7f: c3 ret
用gdb看一下內存里這幾個元素里的東西
每個node的結構應該是這樣
struct{
int value;
int id;
int* next;
}
根據降序排列,順序是0x3a9 0x39f 0x32f 0x2a0 0x22f 0x5c,對應的順序是6 3 4 5 2 1
輸入:
通過。
secret phase:
代碼分析:
Defuse中:
movl $0x804c4d0,(%esp)
80492a8: e8 b3 f5 ff ff call 8048860 <__isoc99_sscanf@plt>
80492ad: 83 f8 03 cmp $0x3,%eax ;如果檢測到輸入的內容是3個,就會將內存中的內容取出來進行比較。有內存地址0x804a352
80492b0: 75 35 jne 80492e7 <phase_defused+0x81>
80492b2: c7 44 24 04 52 a3 04 movl $0x804a352,0x4(%esp)
80492b9: 08
80492ba: 8d 44 24 2c lea 0x2c(%esp),%eax
80492be: 89 04 24 mov %eax,(%esp)
80492c1: e8 24 fd ff ff call 8048fea <strings_not_equal>
80492c6: 85 c0 test %eax,%eax
80492c8: 75 1d jne 80492e7 <phase_defused+0x81>
80492ca: c7 04 24 18 a2 04 08 movl $0x804a218,(%esp)
80492d1: e8 1a f5 ff ff call 80487f0 <puts@plt>
80492d6: c7 04 24 40 a2 04 08 movl $0x804a240,(%esp)
80492dd: e8 0e f5 ff ff call 80487f0 <puts@plt>
80492e2: e8 ea fb ff ff call 8048ed1 <secret_phase>
Gdb查看0x804a352:
特殊字符串是 DrEvil
Secrer_phase:
在第四關加上該字符串,在解決6個之后就會出現提示破解秘密phase
調用phase
Fun4函數分析:
經過推測,輸入1001
通過。
四、實驗結果: