CSAPP--Bomb Lab实验记录

CSAPP–Bomb Lab实验记录

前言

本实验是《深入理解计算机系统》一书中的附带实验——二进制炸弹实验。二进制炸弹是一个作为目标代码文件提供给学生们的程序。运行时,它提示用户输入6个不同的字符串。如果其中的任何一个不正确,炸弹就会“爆炸”,打印出一条错误信息,并且在一个分级服务器上记录事件日志。学生们必须通过对程序反汇编和逆向工程来测定应该是哪6个串,从而解除他们各自炸弹的雷管。 该实验教会学生理解汇编语言,并且强制他们学习怎么使用调试器。

一、实验准备

1.1 配置实验环境

  • linux 环境(ubuntu)
  • gdb(调试程序使用)

1.2 实验必备知识

  • 汇编语法
  • gdb基本指令使用

1.3 实验资源

实验官网 下载 Self-Study Handout

或者

Github 这是其他人的,有自己的实验记录

二、实验内容

1. phase_1

将bomb程序 反编译 得到汇编代码,放到bomb.ams文件中

$ objdump -d bomb > bomb.asm

使用编辑器(vscode)打开bomb.ams文件,定位到 main函数在的地方,找到phase_1函数的地址 400ee0。这行前面的可以对着看 bomb.c文件对应的代码。

0000000000400da0 <main>:
  400da0:	53                   	push   %rbx
  400da1:	83 ff 01             	cmp    $0x1,%edi


  ......

  <read_line>
  400e37:	48 89 c7             	mov    %rax,%rdi
  400e3a:	e8 a1 00 00 00       	callq  400ee0 <phase_1>
  400e3f:	e8 80 07 00 00       	callq  4015c4 
  ......

然后 搜索 400ee0的位置,得到 phase_1函数的汇编代码

0000000000400ee0 <phase_1>:
  400ee0:	48 83 ec 08          	sub    $0x8,%rsp  # 栈指针减去8(分配空间)
  400ee4:	be 00 24 40 00       	mov    $0x402400,%esi # 将0x402400作为参数传入
  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> # 如果返回值不为0,爆炸
  400ef7:	48 83 c4 08          	add    $0x8,%rsp
  400efb:	c3                   	retq

其实从<strings_not_equal> 可以大致猜到这个函数是字符串比较的函数,为了验证猜想是否正确,我们继续跳到这个函数地址401338看看

0000000000401338 <strings_not_equal>:
  401338:	41 54                	push   %r12 
  40133a:	55                   	push   %rbp 
  40133b:	53                   	push   %rbx 
  40133c:	48 89 fb             	mov    %rdi,%rbx 
  40133f:	48 89 f5             	mov    %rsi,%rbp
  401342:	e8 d4 ff ff ff       	callq  40131b <string_length>
  401347:	41 89 c4             	mov    %eax,%r12d
  40134a:	48 89 ef             	mov    %rbp,%rdi
  40134d:	e8 c9 ff ff ff       	callq  40131b <string_length>
  401352:	ba 01 00 00 00       	mov    $0x1,%edx
  401357:	41 39 c4             	cmp    %eax,%r12d
  40135a:	75 3f                	jne    40139b <strings_not_equal+0x63>
  40135c:	0f b6 03             	movzbl (%rbx),%eax
  40135f:	84 c0                	test   %al,%al
  401361:	74 25                	je     401388 <strings_not_equal+0x50>
  401363:	3a 45 00             	cmp    0x0(%rbp),%al
  401366:	74 0a                	je     401372 <strings_not_equal+0x3a>
  401368:	eb 25                	jmp    40138f <strings_not_equal+0x57>
  40136a:	3a 45 00             	cmp    0x0(%rbp),%al
  40136d:	0f 1f 00             	nopl   (%rax)
  401370:	75 24                	jne    401396 <strings_not_equal+0x5e>
  401372:	48 83 c3 01          	add    $0x1,%rbx
  401376:	48 83 c5 01          	add    $0x1,%rbp
  40137a:	0f b6 03             	movzbl (%rbx),%eax
  40137d:	84 c0                	test   %al,%al
  40137f:	75 e9                	jne    40136a <strings_not_equal+0x32>
  401381:	ba 00 00 00 00       	mov    $0x0,%edx
  401386:	eb 13                	jmp    40139b <strings_not_equal+0x63>
  401388:	ba 00 00 00 00       	mov    $0x0,%edx
  40138d:	eb 0c                	jmp    40139b <strings_not_equal+0x63>
  40138f:	ba 01 00 00 00       	mov    $0x1,%edx
  401394:	eb 05                	jmp    40139b <strings_not_equal+0x63>
  401396:	ba 01 00 00 00       	mov    $0x1,%edx
  40139b:	89 d0                	mov    %edx,%eax
  40139d:	5b                   	pop    %rbx
  40139e:	5d                   	pop    %rbp
  40139f:	41 5c                	pop    %r12
  4013a1:	c3                   	retq

可以看到,这个strings_not_equal函数接收两个参数,然后接下来就是比较这两个参数的长度是否相等,若相等则继续比较每个位置对应的字符是否相同,最后返回结果

那现在phase_1函数的内容很明显了,就是比较我们输入的字符串与0x402400这个位置的字符串是否相等,不相等就会bomb

接下来我们看看0x402400这个位置究竟存了什么内容。

  1. 首先gdb调试bomb程序
  2. 在这个phase_1函数设置断点
  3. 输出这个位置的内容
$ gdb bomb

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from bomb...
(gdb) b *0x400ee4
Breakpoint 1 at 0x400ee4
(gdb) run
Starting program: /home/lincx/文档/CSAPP-Labs-master/labs/source/bomb/bomb 
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!


asd   

Breakpoint 1, 0x0000000000400ee4 in phase_1 ()
(gdb) x/s 0x402400
0x402400:	"Border relations with Canada have never been better."
(gdb) 

其中 b*0x400ee4表示设置断点,x/s 0x402400表示输出该位置的内容为字符串形式。 可以看到内容是 “Border relations with Canada have never been better.” 这也是我们需要找的答案了。

2. phase_2

首先也是在bomb.asm文件中,定位到phase_2的位置,然后逐句分析

0000000000400efc <phase_2>:
  400efc:	55                   	push   %rbp #栈帧
  400efd:	53                   	push   %rbx #栈帧
  400efe:	48 83 ec 28          	sub    $0x28,%rsp #栈指针减40
  400f02:	48 89 e6             	mov    %rsp,%rsi # 将栈指针作为第二个参数
  400f05:	e8 52 05 00 00       	callq  40145c <read_six_numbers> #调用函数
  400f0a:	83 3c 24 01          	cmpl   $0x1,(%rsp) # 栈指针内存位置的内容与1比较
  400f0e:	74 20                	je     400f30 <phase_2+0x34> # 相等跳到 400f30
  400f10:	e8 25 05 00 00       	callq  40143a <explode_bomb> # 不相等就会爆炸
  400f15:	eb 19                	jmp    400f30 <phase_2+0x34> # 跳到400f30
  400f17:	8b 43 fc             	mov    -0x4(%rbx),%eax # 将 %rbx-0x4 内存位置的内容 赋值给%eax
  400f1a:	01 c0                	add    %eax,%eax # %eax = %eax+%eax 相当于 乘2
  400f1c:	39 03                	cmp    %eax,(%rbx) # 比较 %eax与(%rbx)
  400f1e:	74 05                	je     400f25 <phase_2+0x29> #相等就跳到 400f25
  400f20:	e8 15 05 00 00       	callq  40143a <explode_bomb> #不相等就会爆炸
  400f25:	48 83 c3 04          	add    $0x4,%rbx # %rbx+0x4
  400f29:	48 39 eb             	cmp    %rbp,%rbx # 比较%rbp 与 %rbx
  400f2c:	75 e9                	jne    400f17 <phase_2+0x1b> #不相等就跳到400f17(是不是有点像循环)
  400f2e:	eb 0c                	jmp    400f3c <phase_2+0x40> # 跳到400f3c
  400f30:	48 8d 5c 24 04       	lea    0x4(%rsp),%rbx # 栈指针+4后赋值给%rbx
  400f35:	48 8d 6c 24 18       	lea    0x18(%rsp),%rbp # 栈指针+24后赋值给%rbp
  400f3a:	eb db                	jmp    400f17 <phase_2+0x1b> #跳到400f17
  400f3c:	48 83 c4 28          	add    $0x28,%rsp #可以理解为释放栈?
  400f40:	5b                   	pop    %rbx
  400f41:	5d                   	pop    %rbp
  400f42:	c3                   	retq   

分析完后,会发现,这个函数的功能是比较我们输入的6个数是否为等比数列,从 cmpl $0x1,(%rsp)可以看出,最先开始的值为1,然后add %eax,%eax这个表示,等比数列的比值为2,由此可以推出我们需要输入的六个数字为

1 2 4 8 16 32

3. phase_3

首先也是在bomb.asm文件中,定位到phase_3的位置,然后逐句分析

0000000000400f43 <phase_3>:
  400f43:	48 83 ec 18          	sub    $0x18,%rsp #栈帧相关
  400f47:	48 8d 4c 24 0c       	lea    0xc(%rsp),%rcx # 将栈指针地址赋值给 %rcx
  400f4c:	48 8d 54 24 08       	lea    0x8(%rsp),%rdx # 将栈指针地址+0x8 赋值给 %rdx
  400f51:	be cf 25 40 00       	mov    $0x4025cf,%esi # 将0x4025cf的内容赋值给%esi
  400f56:	b8 00 00 00 00       	mov    $0x0,%eax #将0赋值给 %eax
  400f5b:	e8 90 fc ff ff       	callq  400bf0 <__isoc99_sscanf@plt>
  400f60:	83 f8 01             	cmp    $0x1,%eax #比较1 与 %eax的内容
  400f63:	7f 05                	jg     400f6a <phase_3+0x27> #大于1 则跳到 400f6a
  400f65:	e8 d0 04 00 00       	callq  40143a <explode_bomb> # 否则爆炸
  400f6a:	83 7c 24 08 07       	cmpl   $0x7,0x8(%rsp) # 比较 (%rsp+8)与 7
  400f6f:	77 3c                	ja     400fad <phase_3+0x6a> # 大于则跳转400fad,爆炸
  400f71:	8b 44 24 08          	mov    0x8(%rsp),%eax # 将(%rsp+8) 赋值给 %eax,也就是第一个
  400f75:	ff 24 c5 70 24 40 00 	jmpq   *0x402470(,%rax,8)  #并跳转至%rax*8+0x402470的内存保存的地址上
  400f7c:	b8 cf 00 00 00       	mov    $0xcf,%eax 
  400f81:	eb 3b                	jmp    400fbe <phase_3+0x7b>
  400f83:	b8 c3 02 00 00       	mov    $0x2c3,%eax
  400f88:	eb 34                	jmp    400fbe <phase_3+0x7b>
  400f8a:	b8 00 01 00 00       	mov    $0x100,%eax
  400f8f:	eb 2d                	jmp    400fbe <phase_3+0x7b>
  400f91:	b8 85 01 00 00       	mov    $0x185,%eax
  400f96:	eb 26                	jmp    400fbe <phase_3+0x7b>
  400f98:	b8 ce 00 00 00       	mov    $0xce,%eax
  400f9d:	eb 1f                	jmp    400fbe <phase_3+0x7b>
  400f9f:	b8 aa 02 00 00       	mov    $0x2aa,%eax
  400fa4:	eb 18                	jmp    400fbe <phase_3+0x7b>
  400fa6:	b8 47 01 00 00       	mov    $0x147,%eax
  400fab:	eb 11                	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>
  400fc9:	48 83 c4 18          	add    $0x18,%rsp
  400fcd:	c3                   	retq

分析到jmpq *0x402470(,%rax,8)这一步,就得停下来看看,这个跳转的内容了。而且前面mov $0x4025cf,%esi这个也需要看看其中的内容是什么。

$ gdb bomb
......

(gdb) x/s 0x4025cf
0x4025cf:	"%d %d"

可以看到 0x4025cf这个地址的内容是两个整数,再看到下面有scanf函数,可以推测我们需要输入两个整数。 栈顶 %rsp 的前 8 位存的是函数的返回地址所在栈地址,从 8(%rsp)开始存第一个参数。由cmpl $0x7,0x8(%rsp)这一步可以看到,第一个数需要比7小才不会爆炸。所以有 0~7可以选择。

接着 看看*0x402470(,%rax,8),这个可以看作是 %rax*8+0x402470。已经把第一个参数传到 %eax 里,x86-64规定低位四字节作整体传的时候,高四字节置 0 。把 0x402470里的东西输出看一下:

(gdb) x/8a 0x402470
0x402470:	0x400f7c <phase_3+57>	0x400fb9 <phase_3+118>
0x402480:	0x400f83 <phase_3+64>	0x400f8a <phase_3+71>
0x402490:	0x400f91 <phase_3+78>	0x400f98 <phase_3+85>
0x4024a0:	0x400f9f <phase_3+92>	0x400fa6 <phase_3+99>

可以看到出现了八个地址,分别对应这上面未分析的汇编代码的八个地址,有点像switch,再往下跳转,cmp 0xc(%rsp),%eax,也就是说第二个参数只要是跟其中一个相等,就会避开炸弹。 列出这八个数,以及对应的第一个参数。

十六进制   十进制  第一个参数
 0xcf      207      0
 0x2c3     707      2
 0x100     256      3
 0x185     389      4
 0xce      206      5
 0x2aa     682      6
 0x147     327      7
 0x137     311      1

所以,第一个参数可以从 0~7选择,第二个参数是它对应的十进制数。验证

1 311

Halfway there!

成功!

4. phase_4

可以看到phase_4前面的部分,跟phase_3很相似,都是输入两个参数。接着还是逐句分析。


0000000000400fce <func4>:
  400fce:	48 83 ec 08          	sub    $0x8,%rsp
  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 #逻辑右移31位
  400fdb:	01 c8                	add    %ecx,%eax 
  400fdd:	d1 f8                	sar    %eax
  400fdf:	8d 0c 30             	lea    (%rax,%rsi,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
  401007:	48 83 c4 08          	add    $0x8,%rsp
  40100b:	c3                   	retq

000000000040100c <phase_4>:
  40100c:	48 83 ec 18          	sub    $0x18,%rsp
  401010:	48 8d 4c 24 0c       	lea    0xc(%rsp),%rcx
  401015:	48 8d 54 24 08       	lea    0x8(%rsp),%rdx
  40101a:	be cf 25 40 00       	mov    $0x4025cf,%esi
  40101f:	b8 00 00 00 00       	mov    $0x0,%eax
  401024:	e8 c7 fb ff ff       	callq  400bf0 <__isoc99_sscanf@plt>
  401029:	83 f8 02             	cmp    $0x2,%eax
  40102c:	75 07                	jne    401035 <phase_4+0x29>
  40102e:	83 7c 24 08 0e       	cmpl   $0xe,0x8(%rsp) #比较输入的第一个参数与 0xe
  401033:	76 05                	jbe    40103a <phase_4+0x2e> #<= 跳转
  401035:	e8 00 04 00 00       	callq  40143a <explode_bomb>
  40103a:	ba 0e 00 00 00       	mov    $0xe,%edx #第三个参数设为 0xe
  40103f:	be 00 00 00 00       	mov    $0x0,%esi #第二个参数设为 0x0
  401044:	8b 7c 24 08          	mov    0x8(%rsp),%edi 第一个参数为输入的第一个数
  401048:	e8 81 ff ff ff       	callq  400fce <func4>
  40104d:	85 c0                	test   %eax,%eax #判断返回值正负
  40104f:	75 07                	jne    401058 <phase_4+0x4c> #非0跳转爆炸
  401051:	83 7c 24 0c 00       	cmpl   $0x0,0xc(%rsp) #比较输入的第二个参数
  401056:	74 05                	je     40105d <phase_4+0x51> #相等则跳过爆炸
  401058:	e8 dd 03 00 00       	callq  40143a <explode_bomb>
  40105d:	48 83 c4 18          	add    $0x18,%rsp
  401061:	c3                   	retq

由分析可知,当调用func4返回值为0的时候,才能不触发炸弹。所以接着分析func4。 为了方便分析,将func4 翻译成c 语言代码:

int func4(int x,int y,int z){
  int res = z-y;
  int tmp = res>>31;
  res= (res+tmp)>>1;
  tmp = res+y;
  if(x>tmp){
    z = tmp-1;
    res=func4(x,y,z);
    res+=res;
    return res;
  }else{
    res=0;
    if(x>=tmp){ /*目标*/
      return res;
    }
    y=tmp+1;
    res=func4(x,y,z);
    res = res*res+1;
    return res;
  } 

}

可以看到 当x>=tmp的时候,函数会返回我们预期的0。 由上边分析可知道,函数func4的三个参数分别为 x(我们输入的第一个参数)、0、14。代入上边代码,可以得到tmp=7(第一次)所以,当x=7的时候,就刚好可以满足这个条件。所以暂时确定第一个数为 7.

往下继续分析 cmpl $0x0,0xc(%rsp) 这一句就表明了,第二个输入的参数为 0

最终答案为:

7 0
So you got that one.  Try this one.

5. phase_5

On x86_64, segmented addressing is no longer used, but the both the FS and GS registers can be used as base-pointer addresses in order to access special operating system data-structures. So what you’re seeing is a value loaded at an offset from the value held in the FS register, and not bit manipulation of the contents of the FS register. Specifically what’s taking place, is that FS:0x28 on Linux is storing a special sentinel stack-guard value, and the code is performing a stack-guard check. For instance, if you look further in your code, you’ll see that the value at FS:0x28 is stored on the stack, and then the contents of the stack are recalled and an XOR is performed with the original value at FS:0x28. If the two values are equal, which means that the zero-bit has been set because XOR’ing two of the same values results in a zero-value, then we jump to the test routine, otherwise we jump to a special function that indicates that the stack was somehow corrupted, and the sentinel value stored on the stack was changed. 在 x86_64 上,不再使用分段寻址,但 FS 和 GS 寄存器都可以用作基址指针地址,以便访问特殊的操作系统数据结构。所以你看到的是一个加载的值与 FS 寄存器中保存的值有一个偏移量,而不是对 FS 寄存器内容的位操作。 具体发生的事情是,Linux 上的 FS:0x28 正在存储一个特殊的哨兵堆栈保护值,并且代码正在执行堆栈保护检查。例如,如果您进一步查看代码,您会看到 FS:0x28 处的值存储在堆栈中,然后调用堆栈中的内容并与 FS:0x28 处的原始值执行 XOR .如果两个值相等,这意味着已经设置了零位,因为对两个相同的值进行异或会产生一个零值,那么我们跳转到测试例程,否则我们跳转到一个特殊的函数,表明堆栈以某种方式损坏,并且存储在堆栈中的标记值已更改。

上面是自己查了一下%fs:0x28指令的用法。

下面开始解题。

老方法,逐句分析

0000000000401062 <phase_5>:
  401062:	53                   	push   %rbx
  401063:	48 83 ec 20          	sub    $0x20,%rsp
  401067:	48 89 fb             	mov    %rdi,%rbx # 将输入的参数1赋值给 %rbx
  40106a:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax 
  401071:	00 00 
  401073:	48 89 44 24 18       	mov    %rax,0x18(%rsp) #作为一个栈保护值
  401078:	31 c0                	xor    %eax,%eax #异或 置零
  40107a:	e8 9c 02 00 00       	callq  40131b <string_length> #调用函数计算字符串长度
  40107f:	83 f8 06             	cmp    $0x6,%eax
  401082:	74 4e                	je     4010d2 <phase_5+0x70> #输入的字符串长度等于6,继续
  401084:	e8 b1 03 00 00       	callq  40143a <explode_bomb>#否则,爆炸
  401089:	eb 47                	jmp    4010d2 <phase_5+0x70>
  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>
  4010ae:	c6 44 24 16 00       	movb   $0x0,0x16(%rsp)
  4010b3:	be 5e 24 40 00       	mov    $0x40245e,%esi
  4010b8:	48 8d 7c 24 10       	lea    0x10(%rsp),%rdi
  4010bd:	e8 76 02 00 00       	callq  401338 <strings_not_equal>
  4010c2:	85 c0                	test   %eax,%eax
  4010c4:	74 13                	je     4010d9 <phase_5+0x77>
  4010c6:	e8 6f 03 00 00       	callq  40143a <explode_bomb>
  4010cb:	0f 1f 44 00 00       	nopl   0x0(%rax,%rax,1)
  4010d0:	eb 07                	jmp    4010d9 <phase_5+0x77>
  4010d2:	b8 00 00 00 00       	mov    $0x0,%eax #将%eax置0
  4010d7:	eb b2                	jmp    40108b <phase_5+0x29> #跳转至40108b
  4010d9:	48 8b 44 24 18       	mov    0x18(%rsp),%rax
  4010de:	64 48 33 04 25 28 00 	xor    %fs:0x28,%rax
  4010e5:	00 00 
  4010e7:	74 05                	je     4010ee <phase_5+0x8c>
  4010e9:	e8 42 fa ff ff       	callq  400b30 <__stack_chk_fail@plt>
  4010ee:	48 83 c4 20          	add    $0x20,%rsp
  4010f2:	5b                   	pop    %rbx
  4010f3:	c3                   	retq   

这段代码最关键的部分是在下面这里。

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>

首先 对 %rax+%rbx 的值进行高位置零拓展,接着 mov %cl,(%rsp)将截取低16位,然后 and $0xf %edx,截取低四位。这里可以理解成是,将我们输入的字符,对应ASCII码的第四位,也就是十六进制最后一位。

movzbl 0x4024b0(%rdx),%edx 0x4024b0+%rdx的值赋值给%edx。 这里出现了一个地址 0x4024b0,我们看看它的内容是啥。

(gdb) x/s 0x4024b0
0x4024b0 <array.3449>:	"maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?"

maduiersnfotvbyl...这是这个地址里边的内容。暂时不知道有什么用,接着往下看。

mov %dl,0x10(%rsp,%rax,1) 这里就是将 %edx的低八位 赋值给 %rsp+%rax+16这个地址。注意这里的地址,是栈的地址。

然后 %rax自增,如果%rax==6,则结束循环。 所以 %rax 从0增加到6,进行了6次循环,每次循环都从我们输入的字符中按序取一个,然后获取它的低四位(十六进制最后一位)作为下标,到0x4024b0 这个地址存放的字符串maduiersnfotvbyl...中取出字符放到%rsp+%rax+16中。

再往下看,当循环结束后,movb $0x0,0x16(%rsp) 将 0 赋值给 %rsp+22 这里的22可以看成 16+6 则为 %rsp+6+16刚好跟前面的对应了。这里0的作用可以看成字符串结尾。

4010ae:	c6 44 24 16 00       	movb   $0x0,0x16(%rsp)
  4010b3:	be 5e 24 40 00       	mov    $0x40245e,%esi
  4010b8:	48 8d 7c 24 10       	lea    0x10(%rsp),%rdi
  4010bd:	e8 76 02 00 00       	callq  401338 <strings_not_equal>
  4010c2:	85 c0                	test   %eax,%eax
  4010c4:	74 13                	je     4010d9 <phase_5+0x77>
  4010c6:	e8 6f 03 00 00       	callq  40143a <explode_bomb>

然后,将地址0x40245e的内容作为第二个参数。这里可以看看内容是啥。

(gdb) x/s 0x40245e
0x40245e:	"flyers"

接着,将栈中存放第一个字符的地址赋值给%rdi作为第一个参数。调用字符串比较函数,比较这两个字符串是否相等。若不相等则爆炸,相等则顺利完成任务。

所以,我们比较的内容是 flyers,这几个字符在maduiersnfotvbyl...中出现的位置为 9EF567 那么我们只需要到ASCII码的表中找到,十六进制末位为这几个数对应的字符,即可。

下面是我找到的其中一个。

IONEFG

6. phase_6

终于到最后一个了,也是最长最难的一个。老规矩先上代码:

00000000004010f4 <phase_6>:
  4010f4:	41 56                	push   %r14
  4010f6:	41 55                	push   %r13
  4010f8:	41 54                	push   %r12
  4010fa:	55                   	push   %rbp
  4010fb:	53                   	push   %rbx
  4010fc:	48 83 ec 50          	sub    $0x50,%rsp
  401100:	49 89 e5             	mov    %rsp,%r13
  401103:	48 89 e6             	mov    %rsp,%rsi
  401106:	e8 51 03 00 00       	callq  40145c <read_six_numbers>
  40110b:	49 89 e6             	mov    %rsp,%r14
  40110e:	41 bc 00 00 00 00    	mov    $0x0,%r12d
  401114:	4c 89 ed             	mov    %r13,%rbp
  401117:	41 8b 45 00          	mov    0x0(%r13),%eax
  40111b:	83 e8 01             	sub    $0x1,%eax
  40111e:	83 f8 05             	cmp    $0x5,%eax
  401121:	76 05                	jbe    401128 <phase_6+0x34> #%eax<=5 输入的六个数必须小于6
  401123:	e8 12 03 00 00       	callq  40143a <explode_bomb>
  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>
  40114d:	49 83 c5 04          	add    $0x4,%r13
  401151:	eb c1                	jmp    401114 <phase_6+0x20> # 这个循环检验输入的六个数是否互异,存在相等的两个数就是会爆炸
  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> #这个循环是 用7分别减去这六个数
  40116f:	be 00 00 00 00       	mov    $0x0,%esi
  401174:	eb 21                	jmp    401197 <phase_6+0xa3>
  401176:	48 8b 52 08          	mov    0x8(%rdx),%rdx # ptr=ptr->next;
  40117a:	83 c0 01             	add    $0x1,%eax
  40117d:	39 c8                	cmp    %ecx,%eax
  40117f:	75 f5                	jne    401176 <phase_6+0x82>
  401181:	eb 05                	jmp    401188 <phase_6+0x94>
  401183:	ba d0 32 60 00       	mov    $0x6032d0,%edx 
  401188:	48 89 54 74 20       	mov    %rdx,0x20(%rsp,%rsi,2) #将节点放到栈 %rsp+%rsi*2+32
  40118d:	48 83 c6 04          	add    $0x4,%rsi
  401191:	48 83 fe 18          	cmp    $0x18,%rsi
  401195:	74 14                	je     4011ab <phase_6+0xb7> #进行六次
  401197:	8b 0c 34             	mov    (%rsp,%rsi,1),%ecx
  40119a:	83 f9 01             	cmp    $0x1,%ecx
  40119d:	7e e4                	jle    401183 <phase_6+0x8f> #%ecx<=1 每个数都跟1比较
  40119f:	b8 01 00 00 00       	mov    $0x1,%eax
  4011a4:	ba d0 32 60 00       	mov    $0x6032d0,%edx # 一个节点
  4011a9:	eb cb                	jmp    401176 <phase_6+0x82>
  4011ab:	48 8b 5c 24 20       	mov    0x20(%rsp),%rbx
  4011b0:	48 8d 44 24 28       	lea    0x28(%rsp),%rax
  4011b5:	48 8d 74 24 50       	lea    0x50(%rsp),%rsi
  4011ba:	48 89 d9             	mov    %rbx,%rcx
  4011bd:	48 8b 10             	mov    (%rax),%rdx
  4011c0:	48 89 51 08          	mov    %rdx,0x8(%rcx)
  4011c4:	48 83 c0 08          	add    $0x8,%rax
  4011c8:	48 39 f0             	cmp    %rsi,%rax
  4011cb:	74 05                	je     4011d2 <phase_6+0xde># 将栈存的地址转换成地址内容
  4011cd:	48 89 d1             	mov    %rdx,%rcx
  4011d0:	eb eb                	jmp    4011bd <phase_6+0xc9>
  4011d2:	48 c7 42 08 00 00 00 	movq   $0x0,0x8(%rdx)
  4011d9:	00 
  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>
  4011f7:	48 83 c4 50          	add    $0x50,%rsp
  4011fb:	5b                   	pop    %rbx
  4011fc:	5d                   	pop    %rbp
  4011fd:	41 5c                	pop    %r12
  4011ff:	41 5d                	pop    %r13
  401201:	41 5e                	pop    %r14
  401203:	c3                   	retq 

首先前面callq 40145c <read_six_numbers>这里跟前面phase_2的一样,都是输入6个数,分别存在%rsp、%rsp+4、%rsp+8、%rsp+12、%rsp+16、%rsp+20。做这道题最有效的方法就是准备好草稿纸,跟着指令,记录下每个寄存器的 值,这样子思路会清晰一些.

401114:	4c 89 ed             	mov    %r13,%rbp
  401117:	41 8b 45 00          	mov    0x0(%r13),%eax
  40111b:	83 e8 01             	sub    $0x1,%eax
  40111e:	83 f8 05             	cmp    $0x5,%eax
  401121:	76 05                	jbe    401128 <phase_6+0x34> #%eax<=5 输入的六个数必须小于6
  401123:	e8 12 03 00 00       	callq  40143a <explode_bomb>
  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>
  40114d:	49 83 c5 04          	add    $0x4,%r13
  401151:	eb c1                	jmp    401114 <phase_6+0x20> # 这个循环检验输入的六个数是否互异,存在相等的两个数就是会爆炸

接下来会进入第一个循环,这个循环主要是判断输入的这六个数是否互不相等,若存在两个相等的数,就会爆炸。所以输入限定条件1:六个互不相等的数

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> #这个循环是 用7分别减去这六个数

接着,这个循环就是对输入的六个数分别用7减去自身,并将结果覆盖原来的值。(这里标重点,后面会考)

 401176:	48 8b 52 08          	mov    0x8(%rdx),%rdx # ptr=ptr->next;
  40117a:	83 c0 01             	add    $0x1,%eax
  40117d:	39 c8                	cmp    %ecx,%eax
  40117f:	75 f5                	jne    401176 <phase_6+0x82>
  401181:	eb 05                	jmp    401188 <phase_6+0x94>
  401183:	ba d0 32 60 00       	mov    $0x6032d0,%edx 
  401188:	48 89 54 74 20       	mov    %rdx,0x20(%rsp,%rsi,2) #将节点放到栈 %rsp+%rsi*2+32
  40118d:	48 83 c6 04          	add    $0x4,%rsi
  401191:	48 83 fe 18          	cmp    $0x18,%rsi
  401195:	74 14                	je     4011ab <phase_6+0xb7> #进行六次
  401197:	8b 0c 34             	mov    (%rsp,%rsi,1),%ecx
  40119a:	83 f9 01             	cmp    $0x1,%ecx
  40119d:	7e e4                	jle    401183 <phase_6+0x8f> #%ecx<=1 每个数都跟1比较
  40119f:	b8 01 00 00 00       	mov    $0x1,%eax
  4011a4:	ba d0 32 60 00       	mov    $0x6032d0,%edx # 一个节点
  4011a9:	eb cb                	jmp    401176 <phase_6+0x82>

这里是一个双重循环,第一层循环进行六次,每次更新0x20(%rsp,%rsi,2)的值,也就是 %rsp+32、%rsp+40、%rsp+48、%rsp+56、%rsp+64、%rsp+72 当cmp $0x1,%ecx比较(7-输入的数)与1,若大于1则会进入内层循环,可以看到 401176

内层循环中,mov 0x8(%rdx),%rdx 这个是不是可以看成ptr=ptr->next;因为指针大小跟0x8有点像,可以推测这应该是个链表。链表头节点指针为0x6032d0。这里循环次数也跟(7-输入的数)有关。

我们可以看看0x6032d0这有什么内容,暂时还看不出什么名堂,那就继续往下走吧。

(gdb) x/24w 0x6032d0
0x6032d0 <node1>:	0x0000014c	0x00000001	0x006032e0	0x00000000
0x6032e0 <node2>:	0x000000a8	0x00000002	0x006032f0	0x00000000
0x6032f0 <node3>:	0x0000039c	0x00000003	0x00603300	0x00000000
0x603300 <node4>:	0x000002b3	0x00000004	0x00603310	0x00000000
0x603310 <node5>:	0x000001dd	0x00000005	0x00603320	0x00000000
0x603320 <node6>:	0x000001bb	0x00000006	0x00000000	0x00000000

接下来又是一个循环:

4011ab:	48 8b 5c 24 20       	mov    0x20(%rsp),%rbx
  4011b0:	48 8d 44 24 28       	lea    0x28(%rsp),%rax
  4011b5:	48 8d 74 24 50       	lea    0x50(%rsp),%rsi
  4011ba:	48 89 d9             	mov    %rbx,%rcx
  4011bd:	48 8b 10             	mov    (%rax),%rdx
  4011c0:	48 89 51 08          	mov    %rdx,0x8(%rcx)
  4011c4:	48 83 c0 08          	add    $0x8,%rax
  4011c8:	48 39 f0             	cmp    %rsi,%rax
  4011cb:	74 05                	je     4011d2 <phase_6+0xde># 将栈存的地址转换成地址内容
  4011cd:	48 89 d1             	mov    %rdx,%rcx
  4011d0:	eb eb                	jmp    4011bd <phase_6+0xc9>

这个循环主要的作用就是将上面循环保存到对应位置的指针修改为对应的内容。

终于到了最后一个循环,也是决定我们输入的重要内容之一:

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>

可以看到这个循环其实就是在做一个链表前后值之间的比较,只有前面的值比后面的值大的时候才不会爆炸,也就是说这个链表其实是一个单调递减的链表,那我们需要看到构造这个链表的前两个循环。

认真看的话,其实就是每次将次序为(7-%ecx)的链表节点,放到指定的地址,这个地址又是递增的。所以我们可以给节点按值由大到小进行排序

 3 <node1>
 4 <node2>
 5 <node3>
 6 <node4>
 1 <node5>
 2 <node6>

但是这个顺序是由 (7-输入的数)得到的,所以我们需要再用7减一次,就得到我们想要的结果了。

4 3 2 1 6 5

运行一下看看结果

(gdb) run

Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
Border relations with Canada have never been better.
Phase 1 defused. How about the next one?
1 2 4 8 16 32
That's number 2.  Keep going!
1 311
Halfway there!
7 0
So you got that one.  Try this one.
IONEFG
Good work!  On to the next...
4 3 2 1 6 5
Congratulations! You've defused the bomb!

至此,该实验的几个任务算基本完成了。后面其实还有一个secret_phase

secret_phase

首先,从bomb.c文件里面的内容可以看到,这个函数是没有直接显示入口的,需要我们找一下。查找以下secret_phase出现的地方,发现pahase_defused这个函数有调用到。我们看看这个函数的内容:

00000000004015c4 <phase_defused>:
  4015c4:	48 83 ec 78          	sub    $0x78,%rsp
  4015c8:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax
  4015cf:	00 00 
  4015d1:	48 89 44 24 68       	mov    %rax,0x68(%rsp)
  4015d6:	31 c0                	xor    %eax,%eax
  4015d8:	83 3d 81 21 20 00 06 	cmpl   $0x6,0x202181(%rip)        # 603760 <num_input_strings>
  4015df:	75 5e                	jne    40163f <phase_defused+0x7b>
  4015e1:	4c 8d 44 24 10       	lea    0x10(%rsp),%r8
  4015e6:	48 8d 4c 24 0c       	lea    0xc(%rsp),%rcx
  4015eb:	48 8d 54 24 08       	lea    0x8(%rsp),%rdx
  4015f0:	be 19 26 40 00       	mov    $0x402619,%esi 
  4015f5:	bf 70 38 60 00       	mov    $0x603870,%edi
  4015fa:	e8 f1 f5 ff ff       	callq  400bf0 <__isoc99_sscanf@plt>
  4015ff:	83 f8 03             	cmp    $0x3,%eax
  401602:	75 31                	jne    401635 <phase_defused+0x71>
  401604:	be 22 26 40 00       	mov    $0x402622,%esi
  401609:	48 8d 7c 24 10       	lea    0x10(%rsp),%rdi
  40160e:	e8 25 fd ff ff       	callq  401338 <strings_not_equal>
  401613:	85 c0                	test   %eax,%eax
  401615:	75 1e                	jne    401635 <phase_defused+0x71>
  401617:	bf f8 24 40 00       	mov    $0x4024f8,%edi
  40161c:	e8 ef f4 ff ff       	callq  400b10 <puts@plt>
  401621:	bf 20 25 40 00       	mov    $0x402520,%edi
  401626:	e8 e5 f4 ff ff       	callq  400b10 <puts@plt>
  40162b:	b8 00 00 00 00       	mov    $0x0,%eax
  401630:	e8 0d fc ff ff       	callq  401242 <secret_phase>
  401635:	bf 58 25 40 00       	mov    $0x402558,%edi
  40163a:	e8 d1 f4 ff ff       	callq  400b10 <puts@plt>
  40163f:	48 8b 44 24 68       	mov    0x68(%rsp),%rax
  401644:	64 48 33 04 25 28 00 	xor    %fs:0x28,%rax
  40164b:	00 00 
  40164d:	74 05                	je     401654 <phase_defused+0x90>
  40164f:	e8 dc f4 ff ff       	callq  400b30 <__stack_chk_fail@plt>
  401654:	48 83 c4 78          	add    $0x78,%rsp
  401658:	c3                   	retq   
  401659:	90                   	nop
  40165a:	90                   	nop
  40165b:	90                   	nop
  40165c:	90                   	nop
  40165d:	90                   	nop
  40165e:	90                   	nop
  40165f:	90                   	nop

打印一下0x402619的内容:

(gdb) x/s 0x402619
0x402619:	"%d %d %s"
cmp    $0x3,%eax
jne    401635 <phase_defused+0x71>

这里显示 两个整形数与一个字符串格式,接着就调用scanf函数输入数据。紧接着,返回值与3进行比较,如果不相等就会跳过下面的部分,可以看到刚好跳到了call secret_phase的后面一行。这个返回值也是我们输入参数的个数,也就是说当我们输入参数为3 ,且按照"%d %d %s"的格式输入就会继续往下执行,不会跳过。

再往下,打印0x402622的内容:

‵‵‵

0000000000401204 <fun7>:
  401204:	48 83 ec 08          	sub    $0x8,%rsp
  401208:	48 85 ff             	test   %rdi,%rdi
  40120b:	74 2b                	je     401238 <fun7+0x34>
  40120d:	8b 17                	mov    (%rdi),%edx
  40120f:	39 f2                	cmp    %esi,%edx
  401211:	7e 0d                	jle    401220 <fun7+0x1c>
  401213:	48 8b 7f 08          	mov    0x8(%rdi),%rdi
  401217:	e8 e8 ff ff ff       	callq  401204 <fun7>
  40121c:	01 c0                	add    %eax,%eax
  40121e:	eb 1d                	jmp    40123d <fun7+0x39>
  401220:	b8 00 00 00 00       	mov    $0x0,%eax
  401225:	39 f2                	cmp    %esi,%edx
  401227:	74 14                	je     40123d <fun7+0x39>
  401229:	48 8b 7f 10          	mov    0x10(%rdi),%rdi
  40122d:	e8 d2 ff ff ff       	callq  401204 <fun7>
  401232:	8d 44 00 01          	lea    0x1(%rax,%rax,1),%eax
  401236:	eb 05                	jmp    40123d <fun7+0x39>
  401238:	b8 ff ff ff ff       	mov    $0xffffffff,%eax
  40123d:	48 83 c4 08          	add    $0x8,%rsp
  401241:	c3                   	retq

0000000000401242 <secret_phase>:
  401242:	53                   	push   %rbx
  401243:	e8 56 02 00 00       	callq  40149e <read_line>
  401248:	ba 0a 00 00 00       	mov    $0xa,%edx
  40124d:	be 00 00 00 00       	mov    $0x0,%esi
  401252:	48 89 c7             	mov    %rax,%rdi
  401255:	e8 76 f9 ff ff       	callq  400bd0 <strtol@plt> #strtol函数     
  # 会将参数nptr字符串根据参数base来转换成长整型数,参数base范围从2至36
  40125a:	48 89 c3             	mov    %rax,%rbx
  40125d:	8d 40 ff             	lea    -0x1(%rax),%eax
  401260:	3d e8 03 00 00       	cmp    $0x3e8,%eax #1000比较
  401265:	76 05                	jbe    40126c <secret_phase+0x2a>
  401267:	e8 ce 01 00 00       	callq  40143a <explode_bomb> # 输入的值比1000大会爆炸
  40126c:	89 de                	mov    %ebx,%esi 
  40126e:	bf f0 30 60 00       	mov    $0x6030f0,%edi 
  401273:	e8 8c ff ff ff       	callq  401204 <fun7>
  401278:	83 f8 02             	cmp    $0x2,%eax 
  40127b:	74 05                	je     401282 <secret_phase+0x40># 当func7返回值为2才算成功
  40127d:	e8 b8 01 00 00       	callq  40143a <explode_bomb>
  401282:	bf 38 24 40 00       	mov    $0x402438,%edi
  401287:	e8 84 f8 ff ff       	callq  400b10 <puts@plt>
  40128c:	e8 33 03 00 00       	callq  4015c4 <phase_defused>
  401291:	5b                   	pop    %rbx
  401292:	c3                   	retq   
  401293:	90                   	nop
  401294:	90                   	nop
  401295:	90                   	nop
  401296:	90                   	nop
  401297:	90                   	nop
  401298:	90                   	nop
  401299:	90                   	nop
  40129a:	90                   	nop
  40129b:	90                   	nop
  40129c:	90                   	nop
  40129d:	90                   	nop
  40129e:	90                   	nop
  40129f:	90                   	nop

输出 0x6030f0这个位置的内容,如下,可以看到是一棵二叉树。其中n1为根节点,nxy为第x层第y个节点。

(gdb) x/120a 0x6030f0
0x6030f0 <n1>:	0x24	0x603110 <n21>
0x603100 <n1+16>:	0x603130 <n22>	0x0
0x603110 <n21>:	0x8	0x603190 <n31>
0x603120 <n21+16>:	0x603150 <n32>	0x0
0x603130 <n22>:	0x32	0x603170 <n33>
0x603140 <n22+16>:	0x6031b0 <n34>	0x0
0x603150 <n32>:	0x16	0x603270 <n43>
0x603160 <n32+16>:	0x603230 <n44>	0x0
0x603170 <n33>:	0x2d	0x6031d0 <n45>
0x603180 <n33+16>:	0x603290 <n46>	0x0
0x603190 <n31>:	0x6	0x6031f0 <n41>
0x6031a0 <n31+16>:	0x603250 <n42>	0x0
0x6031b0 <n34>:	0x6b	0x603210 <n47>
0x6031c0 <n34+16>:	0x6032b0 <n48>	0x0
0x6031d0 <n45>:	0x28	0x0
0x6031e0 <n45+16>:	0x0	0x0
0x6031f0 <n41>:	0x1	0x0
0x603200 <n41+16>:	0x0	0x0
0x603210 <n47>:	0x63	0x0
0x603220 <n47+16>:	0x0	0x0
0x603230 <n44>:	0x23	0x0
0x603240 <n44+16>:	0x0	0x0
0x603250 <n42>:	0x7	0x0

那么我们可以将func7 转换为c语言代码:

int func7(node *root,int num){
  if(root==NULL)return 0;
  if(root->val==num)return 0;
  else if(root->val < num)return 2*func7(root->right)+1;
  return 2*func7(root->left);
}

当我们想要func7返回的结果为2的时候,应当执行的结果为 2*(1+(0))。所以 num < root->val&&num > root->left->val&&num==root->left->right->val。查看<n32> :0x16 这就是我们要的答案。0x16 转10进制 22。

总结

这次实验对自己汇编语言这块的掌握巩固有很大促进作用,通过实验,自己学习到了汇编语言的一些语法,学会看一些汇编代码了。同时也开始接触到gdb这个工具,以前就是写c++或者c语言代码,用这个做断点,然后按F5调试使用,没有用它来做过其他工作,也不知了解它的强大的功能,在实验 中也是多次用到了,例如查看某个地址的内容等。实验虽然结束了,但是对汇编以及GDB的学习还需要继续下去!