APP Bomb LabXCOPY愛高貝cb3

實驗題目:CS:APP Bomb Lab
實驗目的:
binary bomb is a Linux executable C program that consists of six
phases. Each phase expects the student to enter a particular string
on stdin. If the student enters the expected string, then that phase
is defused. Otherwise the bomb explodes by printing BOOM!!!.
The goal for the students is to defuse as many phases as possible.:
實驗環境:ubuntu12.04 (32位)環境
實驗內容及操作步驟
剛開始拿到題目的時候我試着用./bomb指令運行,但是它提示權限不夠,在網上查了一些解決方法試着用sudo,但是還是不行,助教老師告訴我可以用chmod 777 bomb,提供權限,順利解決。然後執行如下指令將可執行文件反彙編,將結果輸出到文件中:
Linux:> Objdump -D bomb >test.txt
1
一切準備就緒,開始逐個分析phase函數。
Phase_1:字符串比較
08048b50 <phase_1>:
8048b50: 83 ec 1c sub $0x1c,%esp
8048b53: c7 44 24 04 64 a2 04 movl $0x804a264,0x4(%esp) //字符串參數 When I get angry, Mr. Bigglesworth gets upset.
8048b5a: 08
8048b5b: 8b 44 24 20 mov 0x20(%esp),%eax //原字符串
8048b5f: 89 04 24 mov %eax,(%esp)
8048b62: e8 1d 05 00 00 call 8049084 <strings_not_equal> //判斷輸入的字符串
8048b67: 85 c0 test %eax,%eax
8048b69: 74 05 je 8048b70 <phase_1+0x20> //如果結果爲0,引爆炸彈,否則跳轉到8048b70
8048b6b: e8 26 06 00 00 call 8049196 <explode_bomb>
8048b70: 83 c4 1c add $0x1c,%esp //將esp+0x1c返回
8048b73: c3 ret
123456789101112
①查看Phase_1函數內容,可以看到函數將輸入的字符串0x20(%esp)和一個立即數指向的字符串傳入一個叫做<strings_not_equal>的函數中,這個函數的功能是判斷兩個字符串是否相等。我啓用gdb調試,輸入指令:
Linux:> x/s 0x804a264
1

可以看到輸出結果爲When I get angry, Mr . Bigglesworth gets upset,我將結果輸入驗證,發現答案正確。
Phase_2:a[i+1]=a[i]+i序列
08048b74 <phase_2>:
8048b74: 53 push %ebx
8048b75: 83 ec 38 sub $0x38,%esp
8048b78: 8d 44 24 18 lea 0x18(%esp),%eax //18(esp)爲讀入參數
8048b7c: 89 44 24 04 mov %eax,0x4(%esp) //
8048b80: 8b 44 24 40 mov 0x40(%esp),%eax //
8048b84: 89 04 24 mov %eax,(%esp) //read_six_numbers(0x40(%esp),0x18(%esp))
8048b87: e8 3f 07 00 00 call 80492cb <read_six_numbers> //讀入6個數據
8048b8c: 83 7c 24 18 00 cmpl $0x0,0x18(%esp) //比較0x18(%esp)和0
8048b91: 79 05 jns 8048b98 <phase_2+0x24> //Nonnegative 非負數,跳轉到8048b98,所以第一個參數爲非負數
8048b93: e8 fe 05 00 00 call 8049196 <explode_bomb> //
8048b98: bb 01 00 00 00 mov $0x1,%ebx //ebx=1初始化ebx=1
123456789101112
①首先我們看到這一段彙編,read_six_numbers(0x40(%esp),0x18(%esp)) 讀入6個數據,其中cmpl $0x0,0x18(%esp)指令比較序列第一個數和0的大小,如果Nonnegative 非負數,跳轉到8048b98否則爆炸,所以第一個數必須爲非負數,接着我們繼續往下查看
/循環部分
8048b9d: 89 d8 mov %ebx,%eax //eax=i
8048b9f: 03 44 9c 14 add 0x14(%esp,%ebx,4),%eax //eax=
(esp+14+14)+eax,eax=a[i]+i
8048ba3: 39 44 9c 18 cmp %eax,0x18(%esp,%ebx,4) //比較eax和a[i+1]
8048ba7: 74 05 je 8048bae <phase_2+0x3a> //相等則跳轉到8048bae
8048ba9: e8 e8 05 00 00 call 8049196 <explode_bomb> //否則爆炸
8048bae: 83 c3 01 add $0x1,%ebx //ebx=i+1
8048bb1: 83 fb 06 cmp $0x6,%ebx //比較ebx和6
8048bb4: 75 e7 jne 8048b9d <phase_2+0x29> //如果ebx!=6則跳轉到8048B9D,循環‬5次,逐一比較
/
12345678910
②這段代碼add 0x14(%esp,%ebx,4),%eax計算eax=a[i]+i,然後cmp %eax,0x18(%esp,%ebx,4) 比較eax和a[i+1] 相等則跳轉到8048ba,否則將會爆炸,所以說想要避開炸彈就必須滿足兩個條件,第一a[0]爲非負數,第二a[i+1]=a[i]+i,按照這個規則我構造序列:1 2 4 7 11 16 21,經驗證結果正確。
Phase_3:switch分支
08048bbb <phase_3>:
8048bbb: 83 ec 3c sub $0x3c,%esp
8048bbe: 8d 44 24 28 lea 0x28(%esp),%eax
8048bc2: 89 44 24 10 mov %eax,0x10(%esp)
8048bc6: 8d 44 24 2f lea 0x2f(%esp),%eax
8048bca: 89 44 24 0c mov %eax,0xc(%esp)
8048bce: 8d 44 24 24 lea 0x24(%esp),%eax
8048bd2: 89 44 24 08 mov %eax,0x8(%esp)
8048bd6: c7 44 24 04 ba a2 04 movl $0x804a2ba,0x4(%esp) //字符串參數
8048bdd: 08
8048bde: 8b 44 24 40 mov 0x40(%esp),%eax
8048be2: 89 04 24 mov %eax,(%esp)
8048be5: e8 86 fc ff ff call 8048870 <isoc99_sscanf@plt> //輸入"%d %c %d",順序爲(0x24,0x2f,0x28)
8048bea: 83 f8 02 cmp $0x2,%eax //返回結果和2比較
8048bed: 7f 05 jg 8048bf4 <phase_3+0x39> //大於2跳轉到8048bf4,所以輸入參數個數必須大於2
8048bef: e8 a2 05 00 00 call 8049196 <explode_bomb>
8048bf4: 83 7c 24 24 07 cmpl $0x7,0x24(%esp) //第1個數必須小於0x7
8048bf9: 0f 87 fc 00 00 00 ja 8048cfb <phase_3+0x140> //大等於7跳轉到8048CFB‬,爆炸
8048bff: 8b 44 24 24 mov 0x24(%esp),%eax //將第一個數存入eax中
8048c03: ff 24 85 e0 a2 04 08 jmp 0x804a2e0(,%eax,4) //switch跳轉,假設第一個數爲0,那麼將會跳轉到 0x804a2e0=8048c0a
8048c0a: b8 63 00 00 00 mov $0x63,%eax //eax=63
8048c0f: 81 7c 24 28 82 01 00 cmpl $0x182,0x28(%esp) //比較第3個參數和0x182=386
12345678910111213141516171819202122
①看到這個函數,一開始是一個
isoc99_sscanf@plt函數,這一定是輸入一些數據,但是這些數據的格式要看函數前的參數準備。看到一個立即數0x804a2ba,啓用gdb調試,在函數phase_3處設置斷點,執行指令
(gdb) x/s 0x804a2ba 發現這個字符串的結果是"%d %c %d"br/>1
結合前面的參數,我們可以知道__isoc99_sscanf@plt調用順序爲(0x24,0x2f,0x28),所以說函數將會輸入一個整數,一個字符串一個整數。
8048bde: 8b 44 24 40 mov 0x40(%esp),%eax
8048be2: 89 04 24 mov %eax,(%esp)
8048be5: e8 86 fc ff ff call 8048870 <isoc99_sscanf@plt> //輸入"%d %c %d",順序爲(0x24,0x2f,0x28)
8048bea: 83 f8 02 cmp $0x2,%eax //返回結果和2比較
8048bed: 7f 05 jg 8048bf4 <phase_3+0x39> //大於2跳轉到8048bf4,所以輸入參數個數必須大於2
8048bef: e8 a2 05 00 00 call 8049196 <explode_bomb>
8048bf4: 83 7c 24 24 07 cmpl $0x7,0x24(%esp) //第1個數必須小於0x7
8048bf9: 0f 87 fc 00 00 00 ja 8048cfb <phase_3+0x140> //大等於7跳轉到8048CFB‬,爆炸
8048bff: 8b 44 24 24 mov 0x24(%esp),%eax //將第一個數存入eax中
8048c03: ff 24 85 e0 a2 04 08 jmp 0x804a2e0(,%eax,4) //switch跳轉,假設第一個數爲0,那麼將會跳轉到 0x804a2e0=8048c0a
8048c0a: b8 63 00 00 00 mov $0x63,%eax //eax=63
8048c0f: 81 7c 24 28 82 01 00 cmpl $0x182,0x28(%esp) //比較第3個參數和0x182=386
8048c16: 00
8048c17: 0f 84 e8 00 00 00 je 8048d05 <phase_3+0x14a> //=182,跳轉到8048d05
1234567891011121314
②這一段中cmpl $0x7,0x24(%esp),ja 8048cfb <phase_3+0x140>大等於7跳轉到8048CFB‬,爆炸,所以第1個數必須小於0x7;x804a2e0(,%eax,4)是典型的switch跳轉,假設第一個數爲0,那麼將會跳轉到 *0x804a2e0=8048c0a,
找到8048c0a 發現指令mov $0x63,%eax cmpl $0x182,0x28(%esp)比較了第3個參數和0x182=386,只有參數3等於386才能避免爆炸,因此第一個參數爲0的時候參數3必須爲386.
8048cf9: eb 0a jmp 8048d05 <phase_3+0x14a>
8048cfb: e8 96 04 00 00 call 8049196 <explode_bomb> //爆炸
8048d00: b8 63 00 00 00 mov $0x63,%eax
8048d05: 3a 44 24 2f cmp 0x2f(%esp),%al //比較eax的低8位,0110 0011=c
8048d09: 74 05 je 8048d10 <phase_3+0x155>//相等則通過
8048d0b: e8 86 04 00 00 call 8049196 <explode_bomb>
123456
③第二個參數確定後跳轉到8048d0,這個時候會將eax的低8位取出01100011,然後與0x2f(%esp)比較,這裏存放的恰好是第2個參數,所以第二個參數ascii=0110 0011=c,因此三個參數可以確定爲0 c 386,經驗證,答案正確。但是由於這裏的跳轉表有7個所以答案不唯一。
Phase_4:過程遞歸調用
08048d81 <phase_4>:
8048d81: 83 ec 2c sub $0x2c,%esp
8048d84: 8d 44 24 1c lea 0x1c(%esp),%eax //參數2 0x1c(%esp)
8048d88: 89 44 24 0c mov %eax,0xc(%esp)
8048d8c: 8d 44 24 18 lea 0x18(%esp),%eax //參數1 0x18(%esp)
8048d90: 89 44 24 08 mov %eax,0x8(%esp) //存放在0x8(%esp)
8048d94: c7 44 24 04 a3 a4 04 movl $0x804a4a3,0x4(%esp) //字符串參數"%d %d"
8048d9b: 08
8048d9c: 8b 44 24 30 mov 0x30(%esp),%eax
8048da0: 89 04 24 mov %eax,(%esp)
8048da3: e8 c8 fa ff ff call 8048870 <
isoc99_sscanf@plt>
8048da8: 83 f8 02 cmp $0x2,%eax //輸入個數等於2
8048dab: 75 0d jne 8048dba <phase_4+0x39>
8048dad: 8b 44 24 18 mov 0x18(%esp),%eax //eax=參數1
8048db1: 85 c0 test %eax,%eax //參數1
8048db3: 78 05 js 8048dba <phase_4+0x39> //參數1不能爲負數
8048db5: 83 f8 0e cmp $0xe,%eax //比較參數1和0xe=14
8048db8: 7e 05 jle 8048dbf <phase_4+0x3e> //參數1必須小於等於0xe
8048dba: e8 d7 03 00 00 call 8049196 <explode_bomb> //爆炸
8048dbf: c7 44 24 08 0e 00 00 movl $0xe,0x8(%esp) //0x8(%esp)=0xe,func4的第3個參數
8048dc6: 00
8048dc7: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp) //0x4(%esp)=0,func4的第2個參數
8048dce: 00
8048dcf: 8b 44 24 18 mov 0x18(%esp),%eax //eax=參數1,func4的第一個參數
8048dd3: 89 04 24 mov %eax,(%esp)
8048dd6: e8 39 ff ff ff call 8048d14 <func4> //調用func4(arg1,0,14),返回值要爲1
8048ddb: 83 f8 01 cmp $0x1,%eax //將結果與1比較
8048dde: 75 07 jne 8048de7 <phase_4+0x66> //如果結果不等於1那麼爆炸,func4結果必須爲1
8048de0: 83 7c 24 1c 01 cmpl $0x1,0x1c(%esp) //將參數2與1比較
8048de5: 74 05 je 8048dec <phase_4+0x6b> //如果參數2=1,跳轉,說明參數2必須等於1
123456789101112131415161718192021222324252627282930
①我們看到這裏調用了__isoc99_sscanf@plt函數,在這之前有一個立即數0x804a4a3,在gdb調試的時候x/s 0x804a4a3查看字符串,結果爲"%d %d",說明要傳入兩個整型參數。mov 0x18(%esp),%eax和test %eax,%eax對第一個參數做判斷如果爲負數則會爆炸,說明第一個參數必須不爲負數。cmp $0xe,%eax和jle判斷如果參數1小於等於0xe才能避免爆炸,所以說第一個參數還需要滿足小於0xe的條件。然後進行函數調用,將兩個參數傳入func4。調用結束之後,cmp $0x1,%eax將結果與1比較jne 8048de7如果結果不等於1那麼爆炸,func4結果必須爲1.cmpl $0x1,0x1c(%esp)將參數2與1比較,如果參數2=1,跳轉,說明參數2必須等於1
08048d14 <func4>:
8048d14: 83 ec 1c sub $0x1c,%esp //與1比較
8048d17: 89 5c 24 14 mov %ebx,0x14(%esp)
8048d1b: 89 74 24 18 mov %esi,0x18(%esp)
8048d1f: 8b 54 24 20 mov 0x20(%esp),%edx //參數1
8048d23: 8b 44 24 24 mov 0x24(%esp),%eax //參數2
8048d27: 8b 5c 24 28 mov 0x28(%esp),%ebx //參數3
8048d2b: 89 d9 mov %ebx,%ecx
8048d2d: 29 c1 sub %eax,%ecx //參數3-參數2->ecx
8048d2f: 89 ce mov %ecx,%esi //ecx->esi
8048d31: c1 ee 1f shr $0x1f,%esi //esi>>31位,取到符號位
8048d34: 01 f1 add %esi,%ecx //將符號位加到ecx
8048d36: d1 f9 sar %ecx //sar $1 %ecx將ecx算數右移1位,以上三點整合起來就是 (ecx>>31 + ecx3) >> 1
8048d38: 01 c1 add %eax,%ecx //ecx+=arg2
8048d3a: 39 d1 cmp %edx,%ecx //比較arg2+(arg3>>31 + arg3) >> 1和參數1
8048d3c: 7e 17 jle 8048d55 <func4+0x41> //小於等於參數1,跳轉,否則遞歸調用函數
8048d3e: 83 e9 01 sub $0x1,%ecx //ecx-1
8048d41: 89 4c 24 08 mov %ecx,0x8(%esp) //arg3
8048d45: 89 44 24 04 mov %eax,0x4(%esp) //arg2
8048d49: 89 14 24 mov %edx,(%esp) //arg1
8048d4c: e8 c3 ff ff ff call 8048d14 <func4> //func4(edx,eax,ecx),改變的是ecx,第三個參數
8048d51: 01 c0 add %eax,%eax //將結果2
8048d53: eb 20 jmp 8048d75 <func4+0x61> //函數結束
8048d55: b8 00 00 00 00 mov $0x0,%eax //8048d3c跳轉到這裏,eax=0
8048d5a: 39 d1 cmp %edx,%ecx //比較ecx-edx參數1
8048d5c: 7d 17 jge 8048d75 <func4+0x61> //如果ecx>=edx,返回
8048d5e: 89 5c 24 08 mov %ebx,0x8(%esp) //否則,將參數3放回arg3
8048d62: 83 c1 01 add $0x1,%ecx //將ecx+1,作爲arg2
8048d65: 89 4c 24 04 mov %ecx,0x4(%esp)
8048d69: 89 14 24 mov %edx,(%esp) //edx參數一作爲arg1
8048d6c: e8 a3 ff ff ff call 8048d14 <func4> //遞歸調用函數
8048d71: 8d 44 00 01 lea 0x1(%eax,%eax,1),%eax //返回2
eax+1
8048d75: 8b 5c 24 14 mov 0x14(%esp),%ebx //返回
8048d79: 8b 74 24 18 mov 0x18(%esp),%esi
12345678910111213141516171819202122232425262728293031323334
②查看func4,一開始看到這個函數將參數3存放在ecx中,然後將ecx放入esi,接着shr $0x1f,%esi 將esi>>31位,取到符號位,add %esi,%ecx將符號位加到ecx,sar %ecx sar $1 %ecx將ecx算數右移1位。以上三點整合起來就是 (ecx>>31 + ecx) >> 1,最add %eax,%ecx則是將結果加上第二個參數存放在ecx中。接着cmp %edx,%ecx比較arg2+(arg3>>31 + arg3) >> 1和參數1的大小,如果小於等於,那麼跳轉到8048d55行,繼續判斷,如果相等直接返回0;如果小於那麼遞歸調用func4,lea 0x1(%eax,%eax,1),%eax則是計算返回值func4(x,tmp+1,z)2+1。如果小大於執行sub $0x1,%ecx 將ecx-1然後調用func4(x,z,tmp-1)2。
③按照以上的分支條件以及調用規則,可以寫出C代碼:
int func4(int x, int y, int z)
{
int tmp=(((z-y)+((z-y)>>31))>>1)+y;
if (tmp<=x)
{
if (tmp==x) return 0;
else return func4(x,tmp+1,z)2+1;
}
else return func4(x,z,tmp-1)
2;
}
12345678910
③最後我們回到phase_4函數,要求func4的結果爲1,因此我寫了一個程序來生成結果爲1的數據:
int main()
{
int x;
for(int i=0;i<14;i++)
{
printf("(%d,0,14)=%d\n",i,func4(i,0,14));
}
system("pause");
return 0;
}
(0,0,14)=0 (1,0,14)=0 (2,0,14)=0 (3,0,14)=0 (4,0,14)=0 (5,0,14)=0 (6,0,14)=0
(8,0,14)=1 (9,0,14)=1 (10,0,14)=1 (11,0,14)=1 (12,0,14)=3 (13,0,14)=3 (7,0,14)=0
123456789101112
以上爲1的結果的第一個參數都能作爲參數1解決phase4,我選用8 1這一個組合,經過驗證答案正確。
Phase_5構造低四位和序列
08048df0 <phase_5>:
8048df0: 53 push %ebx
8048df1: 83 ec 18 sub $0x18,%esp
8048df4: 8b 5c 24 20 mov 0x20(%esp),%ebx
8048df8: 89 1c 24 mov %ebx,(%esp)
8048dfb: e8 6b 02 00 00 call 804906b <string_length> //讀入字符串長度
8048e00: 83 f8 06 cmp $0x6,%eax //長度必須等於6,否則爆炸
8048e03: 74 05 je 8048e0a <phase_5+0x1a> //等於6,跳轉到8048e0a
8048e05: e8 8c 03 00 00 call 8049196 <explode_bomb>
8048e0a: ba 00 00 00 00 mov $0x0,%edx
8048e0f: b8 00 00 00 00 mov $0x0,%eax
1234567891011
①Phase_5第一部分調用string_length函數,對返回值len進行判斷cmp $0x6,%eax長度必須等於6,否則爆炸,所以說輸入的字符必須只能是6個。
8048e14: 0f be 0c 03 movsbl (%ebx,%eax,1),%ecx //計算(eax+ebx)->ecx,也就是長度爲6的字符串的第eax個字符
//movzbl指令負責拷貝一個字節,並用0填充其目的操作數中的其餘各位,這種擴展方式叫“零擴展”
//movsbl指令負責拷貝一個字節,並用源操作數的最高位填充其目的操作數中的其餘各位,這種擴展方式叫“符號擴展”
8048e18: 83 e1 0f and $0xf,%ecx //取ecx低4位
8048e1b: 03 14 8d 00 a3 04 08 add 0x804a300(,%ecx,4),%edx //將 0x804a300(,%ecx,4)加到edx上
8048e22: 83 c0 01 add $0x1,%eax //eax計數
8048e25: 83 f8 06 cmp $0x6,%eax //比較6
8048e28: 75 ea jne 8048e14 <phase_5+0x24>

8048e2a: 83 fa 2f cmp $0x2f,%edx //比較0x2f和edx的值,要求最後等於0x2f
8048e2d: 74 05 je 8048e34 <phase_5+0x44> //相等則成功
8048e2f: e8 62 03 00 00 call 8049196 <explode_bomb>
8048e34: 83 c4 18 add $0x18,%esp
8048e37: 5b pop %ebx
8048e38: c3 ret
123456789101112131415
②Phase_5的第二部分,對首先將edx和eax進行了初始化,根據後面movsbl (%ebx,%eax,1),%ecx的尋址方式和跳轉條件可以判斷這是一個循環過程movzbl指令負責拷貝一個字節,並用0填充其目的操作數中的其餘各位。and $0xf,%ecx則是取ecx低4位,add 0x804a300(,%ecx,4),%edx將 0x804a300(,%ecx,4)加到edx上,在循環執行結束後cmp $0x2f,%edx比較了0x2f和edx的值,要求最後等於0x2f,否則將會爆炸。所以說我們的任務是輸入6個字符,取這個字符的低4位,按照0x804a300(,%ecx,4)找到0x804a300數組中的值,累加後結果必須爲0x2f。
③啓用gdb調試,用x/16wx查看0x804a300中的數值如下圖,我們要在下表中可重複選擇6個數據,和滿足0x2f,我構造的一組值是10+10+10+10+6+1應的數組下標是1 1 1 1 2 3 在ascii碼錶中只要找到低4位的數據爲0001/010/0011的三個值即可:

因此得到解結果爲aaaabc,經驗證答案正確
Phase_6:鏈表
08048e39 <phase_6>: //phase_6
8048e39: 56 push %esi
8048e3a: 53 push %ebx
8048e3b: 83 ec 44 sub $0x44,%esp
8048e3e: 8d 44 24 10 lea 0x10(%esp),%eax
8048e42: 89 44 24 04 mov %eax,0x4(%esp)
8048e46: 8b 44 24 50 mov 0x50(%esp),%eax
8048e4a: 89 04 24 mov %eax,(%esp)
8048e4d: e8 79 04 00 00 call 80492cb <read_six_numbers> //讀取6個數據,手動輸入
8048e52: be 00 00 00 00 mov $0x0,%esi //執行循環
8048e57: 8b 44 b4 10 mov 0x10(%esp,%esi,4),%eax
8048e5b: 83 e8 01 sub $0x1,%eax //x-1
8048e5e: 83 f8 05 cmp $0x5,%eax //x-1後與5比較
8048e61: 76 05 jbe 8048e68 <phase_6+0x2f> //x-1小於等於5,也就是x<=6
8048e63: e8 2e 03 00 00 call 8049196 <0>
8048e68: 83 c6 01 add $0x1,%esi
8048e6b: 83 fe 06 cmp $0x6,%esi //判斷下標esi
8048e6e: 74 1b je 8048e8b <phase_6+0x52> //esi=6,跳轉到8048e8b,否則繼續執行循環
8048e70: 89 f3 mov %esi,%ebx //
8048e72: 8b 44 9c 10 mov 0x10(%esp,%ebx,4),%eax //發現esi賦值給了ebx,後面執行了ebx+1,所以我斷定這是一個2層循環
8048e76: 39 44 b4 0c cmp %eax,0xc(%esp,%esi,4) //比較a[eip]和a[ebx]
8048e7a: 75 05 jne 8048e81 <phase_6+0x48> //滿足a[eip]不等於a[ebx]
8048e7c: e8 15 03 00 00 call 8049196 <explode_bomb> //
8048e81: 83 c3 01 add $0x1,%ebx
8048e84: 83 fb 05 cmp $0x5,%ebx //
8048e87: 7e e9 jle 8048e72 <phase_6+0x39>
8048e89: eb cc jmp 8048e57 <phase_6+0x1e>//for(eip for(ebx))
//得到兩個條件就是a[i]<=6,a[i]!=a[j]
12345678910111213141516171819202122232425262728
①一開始我看到彙編代碼是真的長,沒有頭緒,但是注意到<read_six_numbers>這個函數,但是我往下看,可以發現整個過程中,前面一部分對輸入的6個數據做了基礎的檢測,這兩段代碼cmp$0x5,%eax//x-1後與5比較,jbe8048e68 <phase_6+0x2f>要求是x必須大於等於6,否則將會發生爆炸。cmp%eax,0xc(%esp,%esi,4) 比較a[eip]和a[ebx],jne 8048e81 <phase_6+0x48>滿足a[eip]不等於a[ebx],因此我發現輸入的數據必須要滿足a[i]<=6,a[i]!=a[j]
②輸入範圍是數據必須小於等於6,並且互不相等,我確定下來爲1,2,3,4,5,6這6個數據,並且用gdb進行調試,在phase_6設置斷點.
③找到一個立即數,這十分關鍵,在gdb調試時,我輸出這個立即數的連續數據,發現,居然是這樣的結構體。而且比較有特點是的,這些數據三個三個爲一組,第2個爲一個順序遞增的下標,第三個則是後一個元素節點的地址,這顯然是一個鏈表,因此我推測第一個元素應該是這個節點的信息值——他的權值。

整理後,得到的結構體數據:
0x0000035d 0x00000001 0x0804c148
0x000002eb 0x00000002 0x0804c154
0x000002bb 0x00000003 0x0804c160
0x000000eb 0x00000004 0x0804c16c
0x00000380 0x00000005 0x0804c178
0x0000009f 0x00000006 0x00000000
123456
權重+下標+地址
④ 繼續往後看,中間有一大堆彙編代碼我直接跳過了,因爲他們沒有出現引爆函數
8049196 <explode_bomb>的調用,但是,很關鍵的一點來了,在phase_6將要結束之前的一段彙編代碼中,遍歷的整個鏈表!做了什麼?首先從遍歷了5個元素,esi存儲下標,然後mov0x8(%ebx),%eax每次都將ebx對應的值存放在eax,這個基地址偏移的結果就是下一個node的權重!而且要滿足cmp %edx,(%ebx) jge 因此這個序列必須是遞減的才能不引爆炸彈!
//驗證數據是否是遞減順序
8048f07: be 05 00 00 00 mov $0x5,%esi //循環遍歷整個鏈表節點
8048f0c: 8b 43 08 mov 0x8(%ebx),%eax //將ebx的值也就是鏈表對應的下一個地址放在eax
8048f0f: 8b 10 mov (%eax),%edx //取數(eax)->edx
8048f11: 39 13 cmp %edx,(%ebx) //比較edx和ebx,edx記錄前一個值
8048f13: 7d 05 jge 8048f1a <phase_6+0xe1> //當(%ebx)>=%edx,交換到ebx
8048f15: e8 7c 02 00 00 call 8049196 <explode_bomb> //否則爆炸
8048f1a: 8b 5b 08 mov 0x8(%ebx),%ebx //將新的值放入ebx
8048f1d: 83 ee 01 sub $0x1,%esi
8048f20: 75 ea jne 8048f0c <phase_6+0xd3>

8048f22: 83 c4 44 add $0x44,%esp
8048f25: 5b pop %ebx
8048f26: 5e pop %esi
8048f27: c3 ret
123456789101112131415
⑤所以我斷定這個函數大概率按照每個節點的權重對節點進行了排序。因此我對這些節點按照權重遞減的順序進行排序,可以得到新的序列:
0x00000380 0x00000005 0x00000000
0x0000035d 0x00000001 0x0804c154
0x000002eb 0x00000002 0x0804c160
0x000002bb 0x00000003 0x0804c16c
0x000000eb 0x00000004 0x0804c178
0x0000009f 0x00000006 0x00000000
123456
⑥5 1 2 3 4 6,驗證發現還是引爆這炸彈!這是爲什麼呢?看來中間跳過的一段代碼不能省略,繼續往後看,我發現sub (%eax),%edx每個下標都用7減去了,一開始我不太理解後來才明白這是一個坑!我趕緊把序列換成7-x的版本:2 6 5 4 3 1經過驗證,這是正確的!

Secret_pause
什麼?還有祕密關卡,一開始我是完全拒絕的。但是隻能硬着頭皮上了?找到Secret_pause
函數,發現其中調用了func7,但是Secret_pause祕密關卡怎麼才能進入呢?我在phase_defused函數內部發現了它,但是有一些奇怪的立即數,我習慣性的用gdb x/s查看
804934e: c7 44 24 04 a9 a4 04 movl $0x804a4a9,0x4(%esp) //字符串參數%d %d %s
8049355: 08
8049356: c7 04 24 d0 c4 04 08 movl $0x804c4d0,(%esp) //這是參數所在位置,"8 1"
804935d: e8 0e f5 ff ff call 8048870 <__isoc99_sscanf@plt>
8049362: 83 f8 03 cmp $0x3,%eax //輸入數據等於3
8049365: 75 35 jne 804939c <phase_defused+0x81>
8049367: c7 44 24 04 b2 a4 04 movl $0x804a4b2,0x4(%esp) //將0x804a4b2存入0x4(%esp)第二個參數,DrEvil
804936e: 08
804936f: 8d 44 24 2c lea 0x2c(%esp),%eax
8049373: 89 04 24 mov %eax,(%esp)
8049376: e8 09 fd ff ff call 8049084 <strings_not_equal>
804937b: 85 c0 test %eax,%eax
804937d: 75 1d jne 804939c <phase_defused+0x81> //判斷輸入是否相等
804937f: c7 04 24 78 a3 04 08 movl $0x804a378,(%esp) //0x804a378
8049386: e8 75 f4 ff ff call 8048800 <puts@plt>
804938b: c7 04 24 a0 a3 04 08 movl $0x804a3a0,(%esp) //But finding it and solving it are quite different...
8049392: e8 69 f4 ff ff call 8048800 <puts@plt>
8049397: e8 dd fb ff ff call 8048f79 <secret_phase>
123456789101112131415161718
①並沒有發現什麼突破性的東西,除了兩段字符串DrEvil和一些提示消息,但是我發現想要輸出提示消息(也就是進入祕密關卡)必須要滿足兩個條件,首先是cmpl $0x6,0x804c3cc這個立即數中的數字必須要等於6,其次在比較<strings_not_equal>函數之前有一個參數準備的階段,第2個參數就是我們上面得到的地址$0x804a4b2中的字符串DrEvil.第一個參數是什麼?

②啓用gdb調試,設置斷點爲b *0x8049356也就是立即數0x804c4d0所在行,用x/s 0x804c4d0查看地址上的內容,發現居然居然他就是在phase_4中輸入的內容“8 1”,我猜測這必然和phase有着某種聯繫。

③注意到<strings_not_equal>函數的參數一個是DrEvil另一個是什麼呢?難道有我們輸入?他放在esp中,在此之前通過eax臨時存放了,參數的值,我直接打印eax發現爲空。是什麼原因呢?爲什麼會爲空,注意到8048870 __isoc99_sscanf@plt需要三個參數,而且是%d %d %s的格式,那麼這個輸入點只能是phase_4因爲再無其他輸入口,我果斷在phase_4處輸入8 1 hello World!然後gdb調試的時候打印<strings_not_equal>的第一個參數,p $eax,結果居然就是helloWorld!,只要<strings_not_equal>判斷相等那麼就能進入secret_phase,所以邏輯就很清晰了!
Pahse4輸入%d %d %s------>s==DrEvil?----------> stringsEqual---->secretPhase
1

④Secret_pause順利激活,接着看下去,我們看到讀入先讀入一行,返回值%eax作爲函數strtol@plt的參數之一,另外兩個參數分別是0xa和0x0由lea -0x1(%eax),%eax 和cmp $0x3e8,%eax 這兩句知輸入的十進制數要小於等於 1001。接着調用func7,函數的三個參數分別是p/x 0x804c088查看是0x24和讀入的int數據。
8048f91: 00
8048f92: 89 04 24 mov %eax,(%esp) //參數1,readline讀入
8048f95: e8 46 f9 ff ff call 80488e0 <strtol@plt>
8048f9a: 89 c3 mov %eax,%ebx
8048f9c: 8d 40 ff lea -0x1(%eax),%eax //eax-1
8048f9f: 3d e8 03 00 00 cmp $0x3e8,%eax //1000
8048fa4: 76 05 jbe 8048fab <secret_phase+0x32> //eax-1<=1000,也就是說eax<=1001
8048fa6: e8 eb 01 00 00 call 8049196 <explode_bomb>
8048fab: 89 5c 24 04 mov %ebx,0x4(%esp)
8048faf: c7 04 24 88 c0 04 08 movl $0x804c088,(%esp) //參數1,p/x
0x804c088 爲0x24
8048fb6: e8 6d ff ff ff call 8048f28 <fun7>
8048fbb: 83 f8 07 cmp $0x7,%eax //返回值必須要爲0x7
8048fbe: 74 05 je 8048fc5 <secret_phase+0x4c>
8048fc0: e8 d1 01 00 00 call 8049196 <explode_bomb>
8048fc5: c7 04 24 94 a2 04 08 movl $0x804a294,(%esp)
8048fcc: e8 2f f8 ff ff call 8048800 <puts@plt>
8048fd1: e8 45 03 00 00 call 804931b <phase_defused>
8048fd6: 83 c4 18 add $0x18,%esp
8048fd9: 5b pop %ebx
12345678910111213141516171819
① Func7函數
08048f28 <fun7>:
8048f28: 53 push %ebx
8048f29: 83 ec 18 sub $0x18,%esp
8048f2c: 8b 54 24 20 mov 0x20(%esp),%edx //arg1
8048f30: 8b 4c 24 24 mov 0x24(%esp),%ecx //arg2
8048f34: 85 d2 test %edx,%edx
8048f36: 74 37 je 8048f6f <fun7+0x47> //arg1=null,返回0xffffffff
8048f38: 8b 1a mov (%edx),%ebx //ebx=(arg1)
8048f3a: 39 cb cmp %ecx,%ebx //比較
(arg1)和arg2
8048f3c: 7e 13 jle 8048f51 <fun7+0x29>
8048f3e: 89 4c 24 04 mov %ecx,0x4(%esp) //(arg1)>arg2
8048f42: 8b 42 04 mov 0x4(%edx),%eax
8048f45: 89 04 24 mov %eax,(%esp)
8048f48: e8 db ff ff ff call 8048f28 <fun7>
8048f4d: 01 c0 add %eax,%eax
8048f4f: eb 23 jmp 8048f74 <fun7+0x4c>
8048f51: b8 00 00 00 00 mov $0x0,%eax //
(arg1)<=arg2
8048f56: 39 cb cmp %ecx,%ebx
8048f58: 74 1a je 8048f74 <fun7+0x4c> //*(arg1)<arg2
8048f5a: 89 4c 24 04 mov %ecx,0x4(%esp)
8048f5e: 8b 42 08 mov 0x8(%edx),%eax

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章