CSAPP实验二—— bomb lab实验
实验前准备
在linux操作系统上安装GDB调试器。由于我是在Ubuntu虚拟机上进行实验,所以只需要在终端输入如下安装指令即可快速安装。
sudo apt install gdb
在可执行程序所在的目录下进入终端,输入gdb bomb
即可启动调试过程(bomb是可执行程序名称),可以输入quit
结束GDB调试。
第一部分(phase_1)
使用disas phase_1
反汇编phase_1函数
0x0000000000400ee0 <+0>: sub $0x8,%rsp
0x0000000000400ee4 <+4>: mov $0x402400,%esi //修改%esi的值作为参数传入后面函数
0x0000000000400ee9 <+9>: callq 0x401338 <strings_not_equal>
0x0000000000400eee <+14>: test %eax,%eax #修改条件码
0x0000000000400ef0 <+16>: je 0x400ef7 <phase_1+23> //根据条件码进行跳转,如果%eax寄存器里存放的是0则跳过爆炸的程序
0x0000000000400ef2 <+18>: callq 0x40143a <explode_bomb>
0x0000000000400ef7 <+23>: add $0x8,%rsp
0x0000000000400efb <+27>: retq
发现第一部分还是很简单的,基本操作就是调用strings_not_equal
函数,根据返回值来决定是否引爆炸弹。至于strings_not_equal
函数,从名字就可以看出来是实现比较两字符串是否相等的功能,通过disas strings_not_equal
也可以验证一下猜想。
比较的两个字符串一个是用户输入的字符串,一个是地址存放在%esi寄存器里的内存中的值,也就是地址为0x402400
的内存值。
可以使用x/s 0x402400
来查看对应的字符串,得到以下内容:
0x402400: “Border relations with Canada have never been better.”
所以只要输入的字符串与之完全一样,则可以通过,进入下一关。
第二部分(phase_2)
使用disas phase_2
反汇编phase_2函数
0x0000000000400efc <+0>: push %rbp
0x0000000000400efd <+1>: push %rbx
0x0000000000400efe <+2>: sub $0x28,%rsp
0x0000000000400f02 <+6>: mov %rsp,%rsi
0x0000000000400f05 <+9>: callq 0x40145c <read_six_numbers>
0x0000000000400f0a <+14>: cmpl $0x1,(%rsp) //将第一个输入与1比较
0x0000000000400f0e <+18>: je 0x400f30 <phase_2+52> //确定第一个数字必须为1
0x0000000000400f10 <+20>: callq 0x40143a <explode_bomb>
0x0000000000400f15 <+25>: jmp 0x400f30 <phase_2+52>
0x0000000000400f17 <+27>: mov -0x4(%rbx),%eax //eax寄存器存放上一个输入数字的值
0x0000000000400f1a <+30>: add %eax,%eax //上一个输入数字*2
0x0000000000400f1c <+32>: cmp %eax,(%rbx) //当前数字与上一个数字*2比较
0x0000000000400f1e <+34>: je 0x400f25 <phase_2+41> //只有相等才不会爆炸
0x0000000000400f20 <+36>: callq 0x40143a <explode_bomb>
0x0000000000400f25 <+41>: add $0x4,%rbx //存放下一个输入数字所在内存位置的指针值,从而进入下一次循环
0x0000000000400f29 <+45>: cmp %rbp,%rbx //看是否到达循环的终点
0x0000000000400f2c <+48>: jne 0x400f17 <phase_2+27> //未到终点,进入下一次循环
0x0000000000400f2e <+50>: jmp 0x400f3c <phase_2+64> //到达终点,结束循环
0x0000000000400f30 <+52>: lea 0x4(%rsp),%rbx //第二个输入存放的内存位置
0x0000000000400f35 <+57>: lea 0x18(%rsp),%rbp //0x18=24,控制循环的终点
0x0000000000400f3a <+62>: jmp 0x400f17 <phase_2+27>
0x0000000000400f3c <+64>: add $0x28,%rsp
0x0000000000400f40 <+68>: pop %rbx
0x0000000000400f41 <+69>: pop %rbp
0x0000000000400f42 <+70>: retq
通过进一步查看read_six_numbers
函数的汇编代码,可以知道其完成的是读取用户输入的六个整数,每个数字占用四个字节大小空间,所以最终循环的终点为栈指针偏移4*6=0x18=24的位置。
这部分代码实现的是一个循环,将输入的六个数字依次与其前一位的两倍进行比较。当且仅当第一个数字为1,后一位依次是前一位的两倍的情况下,才不会引起爆炸。所以得到第二问的答案为
1 2 4 8 16 32
第三部分(phase_3)
前两部分内容都算简单,下面来看第三块
0x0000000000400f43 <+0>: sub $0x18,%rsp
0x0000000000400f47 <+4>: lea 0xc(%rsp),%rcx
0x0000000000400f4c <+9>: lea 0x8(%rsp),%rdx
0x0000000000400f51 <+14>: mov $0x4025cf,%esi
0x0000000000400f56 <+19>: mov $0x0,%eax
0x0000000000400f5b <+24>: callq 0x400bf0 <__isoc99_sscanf@plt>
0x0000000000400f60 <+29>: cmp $0x1,%eax
0x0000000000400f63 <+32>: jg 0x400f6a <phase_3+39>
0x0000000000400f65 <+34>: callq 0x40143a <explode_bomb>
0x0000000000400f6a <+39>: cmpl $0x7,0x8(%rsp) //第一个输入与7比较
0x0000000000400f6f <+44>: ja 0x400fad <phase_3+106> //用ja而不是jg,暗示这一部分可能和switch语句有关
0x0000000000400f71 <+46>: mov 0x8(%rsp),%eax
0x0000000000400f75 <+50>: jmpq *0x402470(,%rax,8) //根据第一个输入的值来进行jmp的间接跳转
0x0000000000400f7c <+57>: mov $0xcf,%eax //第一个输入为0时跳转到这里
0x0000000000400f81 <+62>: jmp 0x400fbe <phase_