前言

《深入理解计算机系统》实验二Bomb Lab的下载和官网文档的机翻请看
《深入理解计算机系统》实验二Bomb Lab下载和官方文档机翻

用的调试工具是gdb,用到的指令如下

指令 作用
break 打断点
disassemble 查看汇编语言
run 重新开始运行文件
list 查看源代码
info registers 所有寄存器的值
p $rsp 查看寄存器rsp的值,其他寄存器同
next 单步调试(逐过程,函数直接执行),简写n
step 单步调试(逐语句:跳入自定义函数内部执行),简写s
continue 继续运行,简写c
info breakpoints 查看当前设置的所有断点
x/a 地址,x/s 地址。x/3a 查看地址的十六进制,查看地址的字符形式。3表示输出3个单位

第一炸

根据bomb.c的内容判断出第一个炸弹的函数是phase_1。
在这里插入图片描述
反编译bomb
在这里插入图片描述
找到main()随便看看
在这里插入图片描述
第一个参数通过寄存器%rdi传递
找到关键函数phase_1的信息
在这里插入图片描述
发现有个strings_not_equal函数根据它的名字意思是判断字符串相不相等,上面有个"mov $0x402400,%esi"指令,而寄存器%esi表示第二个参数,第一个参数%rdi是我们输入的字符串,那就应该是判断输入的字符串和寄存器%esi的值相不相等。
用gdb调试bomb
在这里插入图片描述
在phase_1函数打上断点,运行程序,随便输入一个字符串
在这里插入图片描述
到phase_1函数中查看一下0x402400地址中的值
在这里插入图片描述
这个就是我们需要相等的值。
找到ASCII找到0x7220726564726f42对应的字符
在这里插入图片描述
在这里插入图片描述
小端机器所以即

42 6f 72 64 65 72 20 72
B  o  r  d  e  r      r

Border r

直觉告诉我这答案不太对,感觉少了点啥。
试一下,炸了。
在这里插入图片描述
看到文档的
在这里插入图片描述
就试了一下
在这里插入图片描述
划了一下,居然给我看见了
在这里插入图片描述
看来就是这个字符串没错了

Border relations with Canada have never been better.

在这里插入图片描述
第一阶段拆除完毕。
后面发现在这里不应该查看十六进制
在这里插入图片描述
可以直接查看字符
在这里插入图片描述

第二炸

写第二炸之前我们先把第一炸的答案放入一个txt文件中
在这里插入图片描述
按照下面的方式运行就可以直接进入第二炸,不用在输一遍第一炸的答案
在这里插入图片描述
后同。

反汇编bomb找到phase_2函数
在这里插入图片描述
看到跳转去了read_six_numbers函数,看其意是输入六个数字。
在反编译bomb里找到read_six_numbers函数,看到这么多lea而且获取的地址间隔没有那么有规律,有点懵
在这里插入图片描述
查一下
在这里插入图片描述
地址的值。果然是输入六个数字,还是整数
在这里插入图片描述
反过来看phase_2函数,用gdb来调试看清楚点,看输入完六个字符后进行了什么操作。
在这里插入图片描述
把它大概用语言描述出来,还是有点懵

if(%rsp的值==1){
  %rbx=0x4(%rsp)	
  %rbp=0x18(%rsp)	

  do{
    %eax=-0x4(rbx)	
    %eax+=%eax		
    if(%eax==%rbx){	
      %rbx+=4		
    }
  }while(%rbp!=%rbx)
  
  //%rbp==%rbx通关

}else{
  //%rsp的值!=1
  💥
}

额总感觉这些0x4,0x18和read_six_numbers中的有点像。
不太清楚,于是我决定把程序一个一个调试了,然后看看栈中数据
(红色对应红色的数据,左上角是我输入的数据,调试到
0x000000000040148a <+46>: callq 0x400bf0 <__isoc99_sscanf@plt>停止)
在这里插入图片描述
到这里我看出个大概了,刚好6个4字节,这应该就是存放我们输入的整数的地方
在这里插入图片描述
read_six_numbers返回到phase_2时,弹出0x0x7fffffffe3d8内存中存放返回phase_2的地址,返回到phase_2时,此时%rsp指向0x7fffffffe3d0,即第一个整数的内存。
之前写下的输入6个数字phase_2做什么也清楚了

if(%rsp的值==1){
  %rbx=0x4(%rsp)	//第2个
  %rbp=0x18(%rsp)	//第6个后的数据

  do{
  					//第一趟					第二躺
    %eax=-0x4(rbx)	//第1个					第2个
    %eax+=%eax		//第1个加第1个			第2个加第2个
    if(%eax==%rbx){	//第1个加第1个等于第2个	第2个加第2个等于第三个
      %rbx+=4		//取第3个				取第4个
    }
  }while(%rbp!=%rbx)	//%rbp==%rbx 说明六个数据都遍历到了
  
  //%rbp==%rbx通关

}else{
  //%rsp的值!=1
  💥
}

第一个是1
然后 2 4 8 16 32
在这里插入图片描述
艰苦过关。

第三炸

反编译bomb找到phase_3函数。
在这里插入图片描述
用gdb调试bomb,在phase_3函数上打上断点运行。随便输入一个数在这里插入图片描述
在这里插入图片描述
注意到这个
在这里插入图片描述
查看0x4025cf处的值
在这里插入图片描述
这道题应该就是输入两个整数了。
重新运行,输入两个数:1 3
在这里插入图片描述

给sub $0x18,%rsp后打上断点后运行,查看初始栈情况如何
在这里插入图片描述
得到
请添加图片描述
然后在0x400f60打上断点
在这里插入图片描述
看看经过0x400bf0 <__isoc99_sscanf@plt>(根据phase_2和phase_3使用此函数的方式来看,__isoc99_sscanf@plt函数应该接收四个参数,第一个参数是以空格分割的字符(%rdi),第二个参数是格式化的格式(%rsi),第三个参数是要放入数据的起始地址(%rdx),第四个参数是要放入数据的终止地址(%rcx),返回值%rax是数据的个数)处理过的栈如何。
在这里插入图片描述
经过__isoc99_sscanf@plt,%rax的值是2(不用爆炸了)
在这里插入图片描述
把后面的代码大概的用语言表示
在这里插入图片描述
在这里插入图片描述

if(%eax>1){
  if(7 < 0x8(%rsp)){
    %eax = 0x8(%rsp)

    //jmpq *0x402470(,%rax,8)
    switch (%rax){
      case 0:
		%eax=0xcf
		break
      case 2:
        %eax=0x2c3
        break
      case 3:
        %eax=0x100
        break;
      case 4:
        %eax=0x185
        break
      case 5:
		%eax=0xce
		break
      case 6:
		%eax=0x2aa
        break
      case 7:
		%eax=0x147
		break
      case 1:
		%eax=0x137
      if(%eax==0xc(%rsp)){
		//通关
      }else{
		💥
      }
   	 }else{
		💥
    }
  }else{
	💥
  }
}

可以看上面的栈来看出我们输入的1和3分别对应的是0x8(%rsp)=1和0xc(%rsp)=3。
根据代码我们只需要让0x8(%rsp)=1然后去到case 1,让0xc(%rsp)=0x137(331)即可通关
在这里插入图片描述
顺利过关。

第四炸

反编译bomb找到phase_4
在这里插入图片描述

发现前面的代码和phase_3前面的一模一样。
在这里插入图片描述
直接拿初始化栈的(图)来使用
在这里插入图片描述
和第三炸一样,也是输入两个整数。
gdb调试,phase_4打上断点运行,输入1 3
在这里插入图片描述
在这里插入图片描述

在0x401029处打上断点运行
在这里插入图片描述
此时栈情况和第三炸刚开始一样。因为输入了两个数字所以此时%eax=2,免于一炸。
在这里插入图片描述
到这里。0x8(%rsp)的低4字节我们是1<=0xe,所以跳到了炸弹后面。
在这里插入图片描述
可以看出要跳转到func4,我们输入的第一个整数1在寄存器%edi中作为第一个参数,第二个参数%esi=0,第三个参数%edx=0xe,
在这里插入图片描述
func4函数,可以看出它有递归调用
在这里插入图片描述
我发现这里没有使用到我们输入的第二个数3在内存(0xc(%rsp)),返回去在phase_4中看到了
在这里插入图片描述
很明显第二个数是0。这个0让我想到居然是递归那一般来说要么从前往后要么从后往前,然后从这里
在这里插入图片描述
我感觉答案应该是下面的其中一个

e 0
0 0

试一下e 0 炸了,0 0 过了
在这里插入图片描述
蒙混过关。
--------------------------------------
写完之后回过头来看确实有点混了,这个0真是蒙对的。
大概是下面这样(俺也看不懂它做了啥,只知道是一系列递归运算)
在这里插入图片描述
且我试的时候"0 0",“1 0”,“3 0”,"7 0"都是可以通关的。
在打调试的时候发现在这一步,只要在这里查看寄存器$ecx的值就会有7 3 1 0
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
猜测是这一步起了决定性作用
在这里插入图片描述计算机中14(0xe)/2=7,7/2=3,3/2=1,1/2=0。

第五炸

反编译bomb找到phase_5
在这里插入图片描述
注意到下面两个,第一个表明了输入字符的长度是6。
第二个表明了要使%rdi里的字符(0x10(%rsp))==%esi里的字符(0x40245e)。哪肯定不可能是输入的字符是%esi里的字符(0x40245e)就过关了那么简单啦(试过炸了)。
看不懂用gdb看
在gdb里查看了内存0x40245e里的字符是"flyers",这是关键信息
在这里插入图片描述
用gdb查看获取更多的信息。输入“123456”6个字符
在这里插入图片描述
看看初始化的栈
在这里插入图片描述
(经验告诉我,下面这些数据不必细看)
在这里插入图片描述
%rdi的值是我们输入的字符的ASCII十六进制表示
在这里插入图片描述

加了金丝雀(课本P199,一种栈保护机制,从mov %fs:0x18,%rax和后面取出来做对比可以看出,这此题中此代码不是关键代码)的栈
在这里插入图片描述
在这里插入图片描述
下面进入正文——拆炸弹。
看看关键代码(判断输入的是6个字符后进入)
在这里插入图片描述
用语言大概的表示出来

%rbx=%rdi		
%rax=金丝雀
0x18(%rsp)=%rax
%eax=0
if(%rdi的长度==6){
  %eax=0
  while(%rax!=6){
    %ecx=(%rbx,%rax,1)		
    (%rsp)=%cl			
    %rdx=(%rsp)		
    %edx=%edx & 0xf		
    %edx=0x4024b0(%rdx)		
    0x10(%rsp,%rax,1)=%dl	
    %rax++			
  }
  0x16(%rsp)=0
  %esi=$40245e=	"flyers"
  %rdi=0x10(%rsp)
  if(%rsi==%rdi){
    nopl 0x0(%rax,%rax,1)	//不懂
    %rax=0x18(%rsp)
    if(%rax^金丝雀(就是如果栈没有被破坏。这不重要)){
	//通关
    }else{
	call __stack_chk_fail@plt
    }
  }
}

迷迷糊糊!还是一行一行调试。
在这里插入图片描述

40108b:	movzbl (%rbx,%rax,1),%ecx

开始调试,运行到0x40108f(即下一行)停止。
从结果和指令来看,0x40108b获取到了我们输入的第一个字符"1"。可得循环会循环6次,也就是6个字符依次遍历
在这里插入图片描述
运行完

40108f: mov    %cl,(%rsp)

到0x401092(即下一行)停止。从指令来看,获取%ecx的低8位(即31)放入栈中。
请添加图片描述

401092: mov    (%rsp),%rdx

在从栈中取出来放到%rdx

401096:	and    $0xf,%edx

只取低4位的值,也就是0x31变成1

401099:	movzbl 0x4024b0(%rdx),%edx

从0x4024b0内存中的数组取数据,索引是%edx,现在的索引是1
数组内容是
在这里插入图片描述

maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?

到这里就差不多知道这题是要做什么的了,从上面的数组中找到"flyers"对应的六个索引。

4010a0:	88 54 04 10          	mov    %dl,0x10(%rsp,%rax,1)

把取到的数据放入%rsp+%rax+0x10处。索引1对应的数据是"a"
在这里插入图片描述
此时栈如图
请添加图片描述
输入字符“123456”循环完的栈如图
在这里插入图片描述
请添加图片描述
到这里清楚多了

%rbx=%rdi					//输入123456,%rdi=363534333231
%rax=金丝雀
0x18(%rsp)=%rax
%eax=0
if(%rdi的长度==6){
  %eax=0
  while(%rax!=6){
    %ecx=(%rbx,%rax,1)		//获取输入的第一个字符
    (%rsp)=%cl				//它的低字节,8位
    %rdx=(%rsp)		
    %edx=%edx & 0xf			//只取低4位的值,也就是字符转数字1的ascii码0x31变成1(丢高4位)
    %edx=0x4024b0(%rdx)		//从一个数组里面取值索引是%edx
    0x10(%rsp,%rax,1)=%dl	//把取出来的值放入栈中
    %rax++					//%rax作为循环变量
  }
  0x16(%rsp)=0
  %esi=$40245e=	"flyers"
  %rdi=0x10(%rsp)
  if(%rsi==%rdi){
    nopl 0x0(%rax,%rax,1)	//不懂
    %rax=0x18(%rsp)
    if(%rax^金丝雀(就是如果栈没有被破坏。这不重要)){
	//通关
    }else{
	call __stack_chk_fail@plt
    }
  }
}

好了,差不多了,找答案"flyers"

maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?

索引:9fe567。需要注意的是直接打上去会爆炸。
因为这个
在这里插入图片描述
所以我们去"man ascii"里找
在这里插入图片描述
得到"9?>567"
在这里插入图片描述
半途而立通关。
这个答案也是对的,去掉高四位符合"9fe567即可"
在这里插入图片描述
在这里插入图片描述

第六炸

听说这是最难的一关
反编译bomb找到phase_6的函数,有点长…
看到read_six_numbers,应该是输入六个字符
在这里插入图片描述
gdb调试在phase_6打上断点,输入"1 2 3 4 5 6"运行,看看
在这里插入图片描述
运行到这里,看看初始栈的情况
在这里插入图片描述
在这里插入图片描述
执行read_six_numbers前
在这里插入图片描述

read_six_numbers函数的行为有点像是第一个参数(%rdi)是以空格分割的6个整数,第二个参数(%rsi),需要放入的起始地点,把六个整数依次放入。

在这里插入图片描述
代码好长,看的眼花,就把代码写成语言表达。还是挺难看懂,没关系,经过了我几个小时一行一行调试已经搞的七七八八了。

%r13=%rsp
%rsi=%rsp
read_six_numbers(%rdi,%rsi..);	
%r14=%rsp
%r12d=0				
32:
%rbp=%r13			

%eax=0x0(%r13)			
%eax=%eax-1				
if(%eax<=5){			
  %r12d=%r12d+1						
				
  if(%r12d==6){			//0x0000000000401130 <+60>:	je     0x401153 <phase_6+95>	
    %rsi=0x18(%rsp)		
    %rax=%r14			
    %ecx=0x7			

    do{				
      %edx=%ecx
      %edx=%edx-(%rax)
      (%rax)=%edx

      %rax=%rax+4
    }while(%rax != %rsi)	//0x000000000040116d <+121>:	jne    0x401160 <phase_6+108>
				
    %esi=0

    //0x0000000000401174 <+128>:	jmp    0x401197 <phase_6+163>
    %ecx=(%rsp,%rsi,1)		
				
    if(%ecx<=1){		//0x000000000040119d <+169>:	jle    0x401183 <phase_6+143>
      143:
      %edx=0x6032d0		//%edx=0x10000014c
      148:
      0x20(%rsp,%rsi,2)=%rdx	
      %rsi=%rsi+4		
      if(%rsi==0x18){		//0x0000000000401195 <+161>:	je     0x4011ab <phase_6+183>
        %rbx=0x20(%rsp)		
        %rax=lea(0x28(%rsp))	
        %rsi=lea(0x50(%rsp))	
        %rcx=%rbx		

	//<201>
	201:			
        %rdx=(%rax)		
        0x8(%rcx)=%rdx		
        %rax=%rax+8		

	//<212>
	if(%rsi!=%rax){		
          %rcx=%rdx		
          goto 201		//0x00000000004011d0 <+220>:	jmp    0x4011bd <phase_6+201>
        }else
        if(%rsi==%rax){		//0x00000000004011cb <+215>:	je     0x4011d2 <phase_6+222>
          0x8(%rdx)=0
          %ebp=5

	  do{
            %rax=0x8(%rbx)
            %eax=(%rax)
            if((%rbx)>=%eax){	//0x00000000004011e7 <+243>:	jge    0x4011ee <phase_6+250>
              %rbx=0x8(%rbx)
              %ebp=%ebp-1
            }else{💥} 
          }while(%ebp!=0)	//0x00000000004011f5 <+257>:	jne    0x4011df <phase_6+235>
          // 通关...
        }

      }else if(%rsi!=0x18){
        %ecx=(%rsp,%rsi,1)	
				
        if(%ecx<=1){		//0x000000000040119d <+169>:	jle    0x401183 <phase_6+143>
          goto 143
        }else ($ecx>1){		
          %eax=1		
          %edx=0x6032d0		
				//0x00000000004011a9 <+181>:	jmp    0x401176 <phase_6+130>
          do{
            %rdx=0x8(%rdx)	
            %eax=%eax+1
          }while(%eax!=%exc)	
          goto 148
        }
      }

    }else if(%ecx>1){		
      %eax=1

      %edx=0x6032d0
      do{
        %rdx=0x8(%rdx)		
        %eax=%eax+1
      }while(%eax!=%exc)
      goto 148

    }
    
  }else if(%r12d!=6){		
				
    %ebx=%r12d			
    %rax=%ebx			
    %eax=(%rsp,%rax,4)						
				
    if(0x0(%rbp)!=%eax){	//0x000000000040113e <+74>:	jne    0x401145 <phase_6+81>
      81:
      %ebx=%ebx+1		
				
      if(%ebx<=5){		//0x000000000040114b <+87>:	jle    0x401135 <phase_6+65>
        %rax=%ebx				
        %eax=(%rsp,%rax,4)	

        if(%eax!=0x0(%rbp)){	
          goto 81		
        }else{💥}

      }else{			
        %r13=%r13+4		
        goto 32			
      }

    }else if(0x0(%rbp)==%eax){
	💥
    }

  }
}

先看第一部分
前面是初始化。
且可以看出输入的数据不能大于6。
遍历的索引%r12d只有在这里才变动,且是1-2-3-4-5-6,所以刚开始是在%r12d!=6处运行
在这里插入图片描述

%r12d!=6处的代码
第一部分主要是:判断输入的六个数有没有相同的,有相同就会💥
在这里插入图片描述

循环完一趟后就又回到开始下一轮
在这里插入图片描述
当判断完六个数不相同时,开始进入第二部分
第二部分主要是:拿7减去我们输入的数
在这里插入图片描述

输入的数变化如下(注:它不是完成了倒序的操作,这里是因为我输入的数据是1-2-3-4-5-6所以看起来像倒序)
在这里插入图片描述
下面进行下一步,在进行下一步之前我们需要知道后面出现的"%edx=0x6032d0"中的0x6032d0表示的是什么?
查看一下
在这里插入图片描述
看其命名是个结点,实际上也确实是。后面循环就会发现。现在提前了解。
在这里插入图片描述
为什么隔了16个字节?因为它实际上是这样的,还有8位字节存放下一个结点的地址。有链表的感觉了。而且感觉低4字节放的是数据
在这里插入图片描述
记下来

0x6032d0 <node1>:	0x10000014c		0x014c=332
0x6032d8 <node1+8>:	0x6032e8 <node2>

0x6032e0 <node2>:	0x2000000a8		0x00a8=168
0x6032e8 <node2+8>:	0x6032f0 <node3>

0x6032f0 <node3>:	0x30000039c		0x039c=924
0x6032f8 <node3+8>:	0x603300 <node4>

0x603300 <node4>:	0x4000002b3		0x02b3=691
0x603308 <node4+8>:	0x603310 <node5>

0x603310 <node5>:	0x5000001dd		0x01dd=477
0x603318 <node5+8>:	0x603320 <node6>

0x603320 <node6>:	0x6000001bb		0x01bb=443
0x603328 <node6+8>:	0

知道了0x6032d0表示的是什么后就可以进行第三部分了。
第三部分主要是:按照我们上面那个减7之后的数据(6-5-4-3-2-1)的顺序(第6个结点先放然后第5个…)来把结点放入我们的数据的后16个字节里。
在这里插入图片描述

放入后的栈如下:
在这里插入图片描述
经过第三部分的循环,$rsi==0x18满足了,现在进行第四部分。
先来看看第四部分的初始化。
在这里插入图片描述

栈如下
在这里插入图片描述
第四部分主要是:重新设置结点的下一结点,
按照放入的顺序(6结点->5结点->4结点->3结点->2结点->1结点)
在这里插入图片描述

结点设置完毕,终于到了最后一部分,第五部分。
第五部分就是判断当前结点的值是不是比下一结点的值大。全部满足就通过。
我的输入1-2-3-4-5-6带来的结点6->5->4->3->2->1在这一步刚开始就💥了
在这里插入图片描述
总结一下,做了哪些事情

  1. 循环判断输入的六个数是否相同,有相同的就直接💥
  2. 拿7减去我们输入的数
  3. 按照我们栈中的数的顺序来存放结点(注意经过了第2步)
  4. 按照栈中的顺序来链接结点
  5. 判断当前结点的值是不是比下一结点的值大,全部满足就通过

根据我们上面列出的六个结点的值可得大小排序为
3(0x039c=924)->4(0x02b3=691)->5(0x01dd=477)->6(0x1bb=443)->1(0x014c=332)->2(0x00a8=168)
因为要7-。所以最后可得需要输入的数为"4 3 2 1 6 5"。
在这里插入图片描述
有趣通关!最后总结的半汇编半语言代码如下
在这里插入图片描述

%r13=%rsp
%rsi=%rsp
read_six_numbers(%rdi,%rsi..);	
%r14=%rsp						//初始化
%r12d=0				
32:
%rbp=%r13			

%eax=0x0(%r13)					//获取输入的数据			
%eax=%eax-1						//输入的数据减1
if(%eax<=5){					//从这里可以看出输入的数据不能大于6			
  %r12d=%r12d+1					//看起来是要遍历的索引,而且是从第2个数(索引从0开始)开始的。						
				
								//%r12d==6,说明遍历完了最后的一个
  if(%r12d==6){					//0x0000000000401130 <+60>:	je     0x401153 <phase_6+95>	

								//②第二部分这里主要操作是:拿7减去我们输入的数
    %rsi=0x18(%rsp)				//获取我们输入的六个数据的后地址,作为循环的终止条件
    %rax=%r14					//获取我们数据的起始地址
    %ecx=0x7			

    do{				
      %edx=%ecx
      %edx=%edx-(%rax)			//拿7减去遍历的数
      (%rax)=%edx				//在放回去。要记住从这里开始我们的数据已经变了

      %rax=%rax+4				//遍历下一个
    }while(%rax != %rsi)		//0x000000000040116d <+121>:	jne    0x401160 <phase_6+108>
				
    %esi=0						//初始化

    //0x0000000000401174 <+128>:	jmp    0x401197 <phase_6+163>
    %ecx=(%rsp,%rsi,1)			//获取(7-我们输入的第1个数)(上面的7-输入的数)。此时获取数据顺序是6-5-4-3-2-1
				
								//获取的是第1个结点才走这里。其他结点都要通过遍历获取,不能直接获取到。链表
    if(%ecx<=1){				//0x000000000040119d <+169>:	jle    0x401183 <phase_6+143>
      143:
      %edx=0x6032d0				//链表头结点
      148:
      0x20(%rsp,%rsi,2)=%rdx	//③第三部分的主要操作是这个:
								//按照我们栈中的数的顺序来存放结点。现在我们是6-5-4-3-2-1。表示第6个结点先放然后第5个...
								//我们的数据后8字节是0。把结点放入0后面,即我们的数据的后16个字节
      %rsi=%rsi+4				//获取我们的数据的指针

								//我们的数据只占了24(4*6)个字节所以%rsi==0x18时说明遍历了所有数据
      if(%rsi==0x18){			//0x0000000000401195 <+161>:	je     0x4011ab <phase_6+183>
        %rbx=0x20(%rsp)			//%rbx=%rsp+0x20。这是等于值。等于放入的第1个结点
        %rax=lea(0x28(%rsp))	//%rax指向0x28(%rsp)。指向第2个放入的结点
        %rsi=lea(0x50(%rsp))	//%rsi指向0x50(%rsp)。此处等于循环终止条件
        %rcx=%rbx				//%rcx=%rbx。等于放入的第1个结点

	//<201>
	201:						//④第四部分的主要操作是这个:
        %rdx=(%rax)				//%rdx等于第n(n的初始值是2)个放入的结点(%rax指向第n个放入的结点(%rax)就等于第n个放入的结点)
        0x8(%rcx)=%rdx			//让第n-1个放入的结点的下一个结点指向第n个放入的结点。(形成6->5->4->3->2->1)	
        %rax=%rax+8				//指向下一个结点

	//<212>
	if(%rsi!=%rax){				//%rsi==%rax说明所有结点的下一个结点都已经重新设置完毕。
          %rcx=%rdx				//到下一个结点设置

          goto 201				//0x00000000004011d0 <+220>:	jmp    0x4011bd <phase_6+201>
        }else					//还要等上面遍历完6个结点才到这里
        if(%rsi==%rax){			//0x00000000004011cb <+215>:	je     0x4011d2 <phase_6+222>
          0x8(%rdx)=0			//让尾结点下一个结点为0
								//⑤第五部分也是最后一步这里的主要操作是:判断当前结点的值是不是比下一结点的值大。全部满足就通关
          %ebp=5				//循环次数

	  do{
            %rax=0x8(%rbx)		//让%rax等于放入的第n(n的初始值是2)个结点
            %eax=(%rax)			//让%eax等于放入的第n个结点的低4字节的值。也就是结点的值
								//让第n个结点和第n-1的值比较,如果第n-1结点的值大于第n个结点的值就通过,否则就💥
            if((%rbx)>=%eax){	//0x00000000004011e7 <+243>:	jge    0x4011ee <phase_6+250>
              %rbx=0x8(%rbx)
              %ebp=%ebp-1
            }else{💥} 
          }while(%ebp!=0)		//0x00000000004011f5 <+257>:	jne    0x4011df <phase_6+235>
          // 通关...
        }

      }else if(%rsi!=0x18){	
        %ecx=(%rsp,%rsi,1)	
								//除了第1个结点可以直接获取。其他结点都要通过遍历获取。链表
        if(%ecx<=1){			//0x000000000040119d <+169>:	jle    0x401183 <phase_6+143>
          goto 143
        }else ($ecx>1){		
          %eax=1				//第一个结点
          %edx=0x6032d0			//链表头结点
				//0x00000000004011a9 <+181>:	jmp    0x401176 <phase_6+130>
          do{					//循环到第%exc个结点
            %rdx=0x8(%rdx)	
            %eax=%eax+1
          }while(%eax!=%exc)	
          goto 148
        }
      }

    }else if(%ecx>1){			//除了第1个结点可以直接获取。其他结点都要通过遍历获取。链表
      %eax=1					//第一个结点

      %edx=0x6032d0				//链表头结点
      do{						//循环到第%exc个结点
        %rdx=0x8(%rdx)		
        %eax=%eax+1
      }while(%eax!=%exc)
      goto 148

    }
    
  }else if(%r12d!=6){			//①第一部分这里主要操作是:循环判断了输入的六个数是否相同,有相同的就直接💥
								//它是这样的,先拿第1个数来作为基值,然后遍历2-3-4-5-6。
								//         在拿第2个数来作为基值,然后遍历3-4-5-6。...
				
    %ebx=%r12d					//遍历从基值后一个数开始
    %rax=%ebx					//索引
    %eax=(%rsp,%rax,4)			//获取遍历到的数,如遍历到第1个数就获取第2个数			
				
								//0x0(%rbp)表示基值
    if(0x0(%rbp)!=%eax){		//0x000000000040113e <+74>:	jne    0x401145 <phase_6+81>
      81:
      %ebx=%ebx+1				//遍历下一个数
								//遍历次数最多的是第1次,要遍历2-3-4-5-6的数
      if(%ebx<=5){				//0x000000000040114b <+87>:	jle    0x401135 <phase_6+65>
        %rax=%ebx				
        %eax=(%rsp,%rax,4)	

        if(0x0(%rbp)!=%eax){	//不等于就继续遍历
          goto 81		
        }else{💥}				//等于就💥

      }else{					//拿第N个数作为基值,且遍历完了,就拿第N+1个数作为基值来继续遍历。
        %r13=%r13+4		
        goto 32					//0x0(%rbp)表示的基值32中改变的
      }

    }else if(0x0(%rbp)==%eax){	//如果六个数中有相等的数就会💥
	💥
    }

  }
}

这是当时打的断点(没有第一部分检查相不相同和第五部分判断大小。红线表示这个地方已经打过断点了),仅供参考
在这里插入图片描述
最终答案为
在这里插入图片描述

隐藏炸

有个小彩蛋,不过给我拆掉了。
在bomb.c文件中有
在这里插入图片描述
反编译bomb文件,里面找到了
在这里插入图片描述
看名字就是这两货了。而且是先走secret_phase。在文件中找到有secret_phase的地方,发现在phase_defused中有跳转到secret_phase函数的地方。
在这里插入图片描述
而phase_defused是每次拆除炸弹都会走的地方
在这里插入图片描述
看来就是从phase_defused中进入隐藏炸弹。
调试观察phase_defused函数
这里是判断你是不是六个炸弹都已经拆除了,拆除了才可以进入隐藏炸
在这里插入图片描述
出现了数据
在这里插入图片描述
要两个整数,一个字符,现在只有"0 0"两个,后面判断如果不是3个就进入不了隐藏关,看来进入隐藏关的关键是这个
在这里插入图片描述
<input_strings>观其名猜其意,应该就是我们输入的文件的内容
在这里插入图片描述
一看果然如此。"0 0"就是第四题的答案。
在这里插入图片描述
所以需要在"0 0"后面添加一个字符串。
后面的代码有个判断字符的。
在这里插入图片描述
查看内存0x402622处的字符。
在这里插入图片描述
这就是进入隐藏关的字符。
在这里插入图片描述
即可进入隐藏关
在这里插入图片描述
主要看的地方。输入一行数,该数应该是个数字,其小于等于0x3e9(1001)(减1就是0x3e8),
需要查看0x6030f0的值,调用fun7,且fun7的返回值要是2。
在这里插入图片描述
看这名字是个结点啊。
在这里插入图片描述
在看看
在这里插入图片描述
看起来是个二叉树
在这里插入图片描述
0x3e9也出现了,看来输入的是二叉树中的值。看看调用的fun7函数
在这里插入图片描述
fun7函数是这样,标准的二叉树查找
在这里插入图片描述

要想返回2就要先走左边,然后右边,然后命中返回0,回到右边%rax=0,就返回1,回到左边1+1=2.返回2。所以要根结点左边在右边的值,即输入22(0x16)。
在这里插入图片描述
拆弹成功。
最终答案为
在这里插入图片描述

个人总结和感想

拆完第六炸还是感觉挺不错的,随着刚开始看见写出来的代码懵懵懂懂,后面随着一步一步的调试,代码逐渐清晰起来,到最后拆弹成功,真不错。不过我是先做了实验三然后在写实验二的,感觉这两个都是能加深对栈堆的了解,只要知道了数据是怎么存储的,画图一步一步来,还是可以解决的。在写实验三和实验二就想着不百度(还是有那么一丢丢丢丢)自己可不可以解决,还是可以的嘛。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐