CSAPP_BombLab实验报告
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,进一步通过0x400f630x400f65“判断输入个数是否大于1”也能得出相同的结论。最后,0x400f630x400f6f语句表示第一个输入的数字为小于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