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

该答案唯一。

解题思路

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

查看函数 ,发现其功能是比较以%rdi和%rsi为首地址的两个字符串是否相等,如果相等返回0,不相等返回1。

因此结合bomb1中的情景可以发现,调用 的条件是输入的字符串和以%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在调用 ,读入六个数存放在rsp至rsp+24后,判断第一个数是否为1,如果不是则调用 。然后循环遍历六个数,%eax为%rbx的前驱。如果%rbx不为%eax的两倍,则调用 。因此可以得出答案为长度为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

输入&判断部分

这部分主要在 里。主要内容就是判断是否读入了两个以上的数字,以及规定了第一个数字不能大于14,第二个数字必须为0。最关键的部分在于判断调用 后%eax是否为0。由于%edi落在左半区间和右半区间递归时回溯的条件不同,结合%eax初值为0可以得出以下结论:%edi在任何递归层数下都不能大于%ecx,即落在区间的右半部分(不包括中点)。根据%rsi和%rdx初值为0和14,可以得出上述答案表。

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,否则调用 ,然后通过上述双重循环规定元素两两不等。这样就能够保证读入的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);

该部分利用一个循环,判断序列是否为降序,如果不是则调用 。对照原表可知,符合条件的索引排列为3,4,5,6,1,2。考虑到这是经过取反后的排列,因此答案排列应为4,3,2,1,6,5。

Reference

  1. CSAPP

猜你喜欢

转载自www.cnblogs.com/XSC637/p/11720863.html
今日推荐