zoukankan      html  css  js  c++  java
  • 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,进一步通过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。最关键的部分在于判断调用后%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,否则调用<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

    1. CSAPP
  • 相关阅读:
    connect oralce
    monolog php
    js继承
    前后端交互-一些关于接口设计的思考
    zoom:1;
    H5的新特性及部分API详解
    软文参考
    seo细节
    seo每天要做的事情
    seo(每天要干的哪些事)
  • 原文地址:https://www.cnblogs.com/XSC637/p/11720863.html
Copyright © 2011-2022 走看看