Lab_2實驗報告
屏幕截圖

考察內容
本次lab主要考察的是對各種匯編指令的熟悉程度和對gdb的掌握程度。
各題答案
bomb1
Border relations with Canada have never been better.
該答案唯一。
bomb2
1 2 4 8 16 32
該答案唯一。
bomb3
6 682
該答案不唯一。
答案表:
| x | y |
|---|---|
| 0 | 207(0xcf) |
| 1 | 311(0x137) |
| 2 | 707(0x2c3) |
| 3 | 256(0x100) |
| 4 | 389(0x185) |
| 5 | 206(0xce) |
| 6 | 682(0x2aa) |
| 7 | 327(0x147) |
bomb4
0 0
該答案不唯一。
答案表:
| x | y |
|---|---|
| 0 | 0 |
| 1 | 0 |
| 3 | 0 |
| 7 | 0 |
bomb5
9/.567
該答案不唯一。
答案規律:
一個長度為6的字符串,其中從左到右每個字符的十六進制形式下ASCII碼的末位依次為:9、e、f、5、6、7。
bomb6
4 3 2 1 6 5
該答案唯一。
secret_phase
22
該答案唯一性不確定。
解題思路
bomb1
核心代碼部分只有一個:
400ee4: be 00 24 40 00 mov $0x402400,%esi
400ee9: e8 4a 04 00 00 callq 401338 <strings_not_equal>
400eee: 85 c0 test %eax,%eax
400ef0: 74 05 je 400ef7 <phase_1+0x17>
400ef2: e8 43 05 00 00 callq 40143a <explode_bomb>
400ef7: 48 83 c4 08 add $0x8,%rsp
查看函數<strings_not_equal>,發現其功能是比較以%rdi和%rsi為首地址的兩個字符串是否相等,如果相等返回0,不相等返回1。
因此結合bomb1中的情景可以發現,調用<explode_bomb>的條件是輸入的字符串和以%rsi為首地址的字符串不相等。通過gdb得知以%rsi為首地址的字符串為"Border relations with Canada have never been better.",得出答案。
bomb2
核心代碼以及翻譯:
400f0a: 83 3c 24 01 cmpl $0x1,(%rsp)
400f0e: 74 20 je 400f30 <phase_2+0x34>
400f10: e8 25 05 00 00 callq 40143a <explode_bomb>
400f15: eb 19 jmp 400f30 <phase_2+0x34>
400f17: 8b 43 fc mov -0x4(%rbx),%eax
400f1a: 01 c0 add %eax,%eax
400f1c: 39 03 cmp %eax,(%rbx)
400f1e: 74 05 je 400f25 <phase_2+0x29>
400f20: e8 15 05 00 00 callq 40143a <explode_bomb>
400f25: 48 83 c3 04 add $0x4,%rbx
400f29: 48 39 eb cmp %rbp,%rbx
400f2c: 75 e9 jne 400f17 <phase_2+0x1b>
400f2e: eb 0c jmp 400f3c <phase_2+0x40>
400f30: 48 8d 5c 24 04 lea 0x4(%rsp),%rbx
400f35: 48 8d 6c 24 18 lea 0x18(%rsp),%rbp
400f3a: eb db jmp 400f17 <phase_2+0x1b>
if(*rsp!=1)explode_bomb;
rbx=rsp+4;
rbp=rsp+24;
do
eax=rbx-4;
eax*=2;
if(eax!=(*rbx))explode_bomb;
rbx+=4;
while(rbp!=rbx);
phase_2在調用<read_six_numbers>,讀入六個數存放在rsp至rsp+24后,判斷第一個數是否為1,如果不是則調用<explode_bomb>。然后循環遍歷六個數,%eax為%rbx的前驅。如果%rbx不為%eax的兩倍,則調用<explode_bomb>。因此可以得出答案為長度為6,首項為1,公比為2的等比數列,即"1,2,4,8,16,32"。
bomb3
輸入部分
400f51: be cf 25 40 00 mov $0x4025cf,%esi
400f56: b8 00 00 00 00 mov $0x0,%eax
400f5b: e8 90 fc ff ff callq 400bf0 <__isoc99_sscanf@plt>
400f60: 83 f8 01 cmp $0x1,%eax
400f63: 7f 05 jg 400f6a <phase_3+0x27>
400f65: e8 d0 04 00 00 callq 40143a <explode_bomb>
400f6a: 83 7c 24 08 07 cmpl $0x7,0x8(%rsp)
400f6f: 77 3c ja 400fad <phase_3+0x6a>
通過gdb查看首地址為0x4025cf的字符串,結果為"%d %d",結合下文的__isoc99_sscanf@plt可知,要求輸入的數字個數為2,進一步通過0x400f63~0x400f65“判斷輸入個數是否大於1”也能得出相同的結論。最后,0x400f63~0x400f6f語句表示第一個輸入的數字為小於7的非負數。
分支部分
400f71: 8b 44 24 08 mov 0x8(%rsp),%eax
400f75: ff 24 c5 70 24 40 00 jmpq *0x402470(,%rax,8)
400f7c: b8 cf 00 00 00 mov $0xcf,%eax
400f81: eb 3b jmp 400fbe <phase_3+0x7b>
...
400fad: e8 88 04 00 00 callq 40143a <explode_bomb>
400fb2: b8 00 00 00 00 mov $0x0,%eax
400fb7: eb 05 jmp 400fbe <phase_3+0x7b>
400fb9: b8 37 01 00 00 mov $0x137,%eax
400fbe: 3b 44 24 0c cmp 0xc(%rsp),%eax
400fc2: 74 05 je 400fc9 <phase_3+0x86>
400fc4: e8 71 04 00 00 callq 40143a <explode_bomb>
該部分代碼較長,未完全展示。
通過觀察發現,該部分中有大量內容為"jmp 400fbe <phase_3+0x7b>"的語句,判斷應該是分支結構,對應到源代碼應該是一個switch語句。實現條件跳轉的關鍵在於0x400f75語句:“jmpq *0x402470(,%rax,8)”。這條語句的意思是跳轉到以0x402470+8*%rax指向的數為地址的語句處。通過gdb可得出上述答案表。
bomb4
遞歸部分
遞歸部分就是整個
確定中點
400fd2: 89 d0 mov %edx,%eax
400fd4: 29 f0 sub %esi,%eax
400fd6: 89 c1 mov %eax,%ecx
400fd8: c1 e9 1f shr $0x1f,%ecx
400fdb: 01 c8 add %ecx,%eax
400fdd: d1 f8 sar %eax
400fdf: 8d 0c 30 lea (%rax,%rsi,1),%ecx
在該部分中,%esi為閉區間左端點,%edx為閉區間右端點。語句0x400fd2~0x400fdb的本質即是%eax=%edx-%esi,隨后通過%ecx=%rsi+(%eax>>1)這條語句可以得知,%ecx為區間中點。
比較大小&縮小區間
400fe2: 39 f9 cmp %edi,%ecx
400fe4: 7e 0c jle 400ff2 <func4+0x24>
400fe6: 8d 51 ff lea -0x1(%rcx),%edx
400fe9: e8 e0 ff ff ff callq 400fce <func4>
400fee: 01 c0 add %eax,%eax
400ff0: eb 15 jmp 401007 <func4+0x39>
400ff2: b8 00 00 00 00 mov $0x0,%eax
400ff7: 39 f9 cmp %edi,%ecx
400ff9: 7d 0c jge 401007 <func4+0x39>
400ffb: 8d 71 01 lea 0x1(%rcx),%esi
400ffe: e8 cb ff ff ff callq 400fce <func4>
401003: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax
%edi為要查找的數。比較%edi和%ecx的大小,如果%ecx>%edi,表示%edi落在左半區間。因此將右端點%edx修改為%rcx-1,向下一層遞歸,回溯時將%eax修改為2*%eax。否則,表示%edi落在右半區間,此時判斷%edi和%ecx是否相等,如果相等則直接退出該遞歸部分。如果不相等則將左端點%esi修改為%rcx+1,向下一層遞歸,回溯時將%eax修改為2*%eax+1。
輸入&判斷部分
這部分主要在<phase_4>里。主要內容就是判斷是否讀入了兩個以上的數字,以及規定了第一個數字不能大於14,第二個數字必須為0。最關鍵的部分在於判斷調用
bomb5
phase_5的本質是以輸入字符串的從左到右每個字符的ASCII碼的十六進制末位為索引,查表得到新字符串后與目標字符串匹配。
索引部分
40108b: 0f b6 0c 03 movzbl (%rbx,%rax,1),%ecx
40108f: 88 0c 24 mov %cl,(%rsp)
401092: 48 8b 14 24 mov (%rsp),%rdx
401096: 83 e2 0f and $0xf,%edx
401099: 0f b6 92 b0 24 40 00 movzbl 0x4024b0(%rdx),%edx
4010a0: 88 54 04 10 mov %dl,0x10(%rsp,%rax,1)
4010a4: 48 83 c0 01 add $0x1,%rax
4010a8: 48 83 f8 06 cmp $0x6,%rax
4010ac: 75 dd jne 40108b <phase_5+0x29>
翻譯代碼:
do
ecx=rax+rbx; //movzbl
*rsp=cl;
rdx=*rsp;
edx&=0xf;
edx=*(rdx+4024b0); //movzbl
*(rsp+rax+4*4)=dl;
rax++;
while(rax!=6);
該部分就是遍歷所有字符,%rbx是讀入字符串的首地址,%edx用來存放字符的ASCII碼的十六進制末位,隨后將%edx索引至%rdx+0x4024b0處,並存放在%rsp+%rax+16處。
通過gdb調試得知以0x4024b0為首地址的字符串為:"maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?",其中有效項(前16個)為"maduiersnfotvbyl"。
檢驗部分
檢驗部分就是判斷新字符串是否等於目標字符串,即以0x40245e為首地址的字符串。通過gdb調試得知目標字符串為"flyers",其中每個字符分別在表的第9、14、15、5、6、7位。因此倒推出輸入字符串的ASCII碼的十六進制末位也應該是9,e,f,5,6,7。
bomb6
PS:(這個phase真的是太太太太長了……里面jmp指令也是非常的多,不用紙打印出來真的沒辦法看啊QAQ)
phase_6的本質就是將輸入的排列取反后以此為基准重新排列一個序列,使其成降序。
判斷是否為排列
401128: 41 83 c4 01 add $0x1,%r12d
40112c: 41 83 fc 06 cmp $0x6,%r12d
401130: 74 21 je 401153 <phase_6+0x5f>
401132: 44 89 e3 mov %r12d,%ebx
401135: 48 63 c3 movslq %ebx,%rax
401138: 8b 04 84 mov (%rsp,%rax,4),%eax
40113b: 39 45 00 cmp %eax,0x0(%rbp)
40113e: 75 05 jne 401145 <phase_6+0x51>
401140: e8 f5 02 00 00 callq 40143a <explode_bomb>
401145: 83 c3 01 add $0x1,%ebx
401148: 83 fb 05 cmp $0x5,%ebx
40114b: 7e e8 jle 401135 <phase_6+0x41>
翻譯代碼:
do
r12d++;
ebx=r12d;
do
rax=ebx; //movslq
eax=rsp+4*rax;
if(eax==*rbp)explode_bomb;
ebx++;
while(ebx<=5);
while(r12d!=6);
讀入6個數字后,首先規定每個數不大於6,否則調用<explode_bomb>,然后通過上述雙重循環規定元素兩兩不等。這樣就能夠保證讀入的6個數字為6的一個排列。
取反部分
401153: 48 8d 74 24 18 lea 0x18(%rsp),%rsi
401158: 4c 89 f0 mov %r14,%rax
40115b: b9 07 00 00 00 mov $0x7,%ecx
401160: 89 ca mov %ecx,%edx
401162: 2b 10 sub (%rax),%edx
401164: 89 10 mov %edx,(%rax)
401166: 48 83 c0 04 add $0x4,%rax
40116a: 48 39 f0 cmp %rsi,%rax
40116d: 75 f1 jne 401160 <phase_6+0x6c>
翻譯代碼:
rsi=*(rsp+4*6);
rax=r14;
do
ecx=7;
edx=ecx;
edx-=*rax;
*rax=edx; //*rax=7-*rax
rax+=4;
while(rax!=rsi);
該部分利用一個循環,將元素x轉變為7-x。
索引部分
PS:(這部分的條件跳轉指令特別多……所以這部分我是在紙上完成的,沒辦法貼出翻譯代碼了)
該部分以每個元素值x為索引,索引至0x6032d0+16*x處,將該值賦給%rsp+8*x+32。用gdb調試后發現,0x6032d0+16*x指向的值分別為332,168,924,691,477,443。
檢驗部分
4011da: bd 05 00 00 00 mov $0x5,%ebp
4011df: 48 8b 43 08 mov 0x8(%rbx),%rax
4011e3: 8b 00 mov (%rax),%eax
4011e5: 39 03 cmp %eax,(%rbx)
4011e7: 7d 05 jge 4011ee <phase_6+0xfa>
4011e9: e8 4c 02 00 00 callq 40143a <explode_bomb>
4011ee: 48 8b 5b 08 mov 0x8(%rbx),%rbx
4011f2: 83 ed 01 sub $0x1,%ebp
4011f5: 75 e8 jne 4011df <phase_6+0xeb>
翻譯代碼:
ebp=5;
do
rax=*(rbx+8);
eax=*rax;
if(*rbx<eax)explode_bomb;
rbx=*(rbx+8);
while(--ebp!=0);
該部分利用一個循環,判斷序列是否為降序,如果不是則調用<explode_bomb>。對照原表可知,符合條件的索引排列為3,4,5,6,1,2。考慮到這是經過取反后的排列,因此答案排列應為4,3,2,1,6,5。
secret_phase
入口部分
容易發現<secret_phase>在<bomb_defuse>處被調用。一路檢索過去可以發現,觸發<secret_phase>的條件在於讀入的字符串是否等於"DrEvil"。可是在哪里輸入該字符串呢?調用gdb調試,查看0x402619處字符串,發現等於"%d %d %s",則可以判斷應在之前輸入兩個數字的phase時在末尾輸入一個"DrEvil"觸發<secret_phase>。
fun7
該函數是一個遞歸調用的函數。%rdi初始值為0x6030f0,%esi初始值為%ebx,也就是讀入的整數。
整個函數的意思是將(%rdi)與%esi比較,如果(%rdi)>%esi,%rdi=0x8(%rdi),即%rdi被更新為%rdi+8指向的值,遞歸進入下一層,回溯時將%eax更新為原來的兩倍。
如果(%rdi)==%esi,返回%eax=0。
如果(%rdi)<=%esi,%rdi被更新為%rdi+16指向的值,回溯時將%eax更新為2*%eax+1。
返回<secret_phase>查看<explode_bomb>被調用的條件,發現是%eax!=2。那么反推回去,可以發現如果想要%eax=2,在進入fun7時,需要先使(%rdi)>%esi,再使(%rdi<%esi),最后使(%rdi==%esi)。這樣在回溯時,初值為0的%eax先變為2*0+1=1,再變成2*1=2。
調用gdb查看0x6030f0后的若干字節:
0x6030f0 <n1>: 0x0000000000000024 0x0000000000603110
0x603100 <n1+16>: 0x0000000000603130 0x0000000000000000
0x603110 <n21>: 0x0000000000000008 0x0000000000603190
0x603120 <n21+16>: 0x0000000000603150 0x0000000000000000
0x603130 <n22>: 0x0000000000000032 0x0000000000603170
0x603140 <n22+16>: 0x00000000006031b0 0x0000000000000000
0x603150 <n32>: 0x0000000000000016 0x0000000000603270
發現0x8(%rdi)=(0x603110)=8,0x18(%rdi)=(0x603150)=0x16=22。
將22作為%esi,反推回去,發現第一層0x24>0x16,第二層0x8<0x16,滿足條件。因此22為答案。
Reference
- CSAPP
