CSAPP第二章的第一个实验BombLab实验记录。
实验内容主要使用 gdb 、objdump 等指令在终端调试程序,利用反汇编查看各个函数运行的汇编代码,了解各个函数的执行过程。
附gdb指令精简手册:http://csapp.cs.cmu.edu/public/students.html
Phase_1
首先打开终端输入 gdb bomb 进入调试界面。
查看 phase_1 的汇编指令:
(gdb) disas phase_1
Dump of assembler code for function phase_1:
0x0000000000400ee0 <+0>: sub $0x8,%rsp
0x0000000000400ee4 <+4>: mov $0x402400,%esi
0x0000000000400ee9 <+9>: callq 0x401338 <strings_not_equal>
0x0000000000400eee <+14>: test %eax,%eax
0x0000000000400ef0 <+16>: je 0x400ef7 <phase_1+23>
0x0000000000400ef2 <+18>: callq 0x40143a <explode_bomb>
0x0000000000400ef7 <+23>: add $0x8,%rsp
0x0000000000400efb <+27>: retq
End of assembler dump.
函数首先接收户输入的字符串,再将立即数 0x402400 输入到寄存器 esi ,接着调用函数<string_not_equal>,很容易猜到寄存器 esi 中保存了要和用户输入的字符串进行比较的字符串的首地址,即立即数 0x402400 。
(gdb) x/s 0x402400
0x402400: "Border relations with Canada have never been better."
通过地址直接查看字符串,这就是我们需要输入的内容!
(gdb) run
Starting program: /home/liuyuan/Downloads/CSAPP/bomb/bomb
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?
通过测试!
进一步细致理解函数运行过程:
将函数 phase_1 设置断点,运行程序,随便输入一些内容( "Nice day!" ),程序会停留在进入函数体的过程之前:
(gdb) break phase_1
Breakpoint 1 at 0x400ee0
(gdb) run
Starting program: /home/liuyuan/Downloads/CSAPP/bomb/bomb
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
Nice day!
Breakpoint 1, 0x0000000000400ee0 in phase_1 ()
此时查看各个寄存器的数据:
(gdb) info registers
rax 0x603780 6305664
rbx 0x0 0
rcx 0x9 9
rdx 0x1 1
rsi 0x603780 6305664
rdi 0x603780 6305664
rbp 0x402210 0x402210 <__libc_csu_init>
rsp 0x7fffffffdd98 0x7fffffffdd98
r8 0x60467a 6309498
r9 0x7ffff7fe5540 140737354028352
r10 0x3 3
r11 0x7ffff7a14890 140737347930256
r12 0x400c90 4197520
r13 0x7fffffffde80 140737488346752
r14 0x0 0
r15 0x0 0
rip 0x400ee0 0x400ee0 <phase_1>
eflags 0x202 [ IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
这是并没有出现 0x402400 。猜测刚刚输入的数据保存地址为 0x603780:
(gdb) x/s 0x603780
0x603780 <input_strings>: "Nice day!"
回到前面的汇编指令,第二条指令将立即数 0x402400 移到寄存器 esi,让程序执行两条汇编指令,再查看寄存器的状态:
(gdb) stepi 2
0x0000000000400ee9 in phase_1 ()
(gdb) i r
rax 0x603780 6305664
rbx 0x0 0
rcx 0x9 9
rdx 0x1 1
rsi 0x402400 4203520
rdi 0x603780 6305664
rbp 0x402210 0x402210 <__libc_csu_init>
rsp 0x7fffffffdd90 0x7fffffffdd90
r8 0x60467a 6309498
r9 0x7ffff7fe5540 140737354028352
r10 0x3 3
r11 0x7ffff7a14890 140737347930256
r12 0x400c90 4197520
r13 0x7fffffffde80 140737488346752
r14 0x0 0
r15 0x0 0
rip 0x400ee9 0x400ee9 <phase_1+9>
eflags 0x206 [ PF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
看到寄存器 esi (即 rsi 的低 32 位)被更新了!
Phase_2
首先查看 Phase_2 的汇编代码:
(gdb) disas phase_2
Dump of assembler code for function 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)
0x0000000000400f0e <+18>: je 0x400f30 <phase_2+52>
0x0000000000400f10 <+20>: callq 0x40143a <explode_bomb>
0x0000000000400f15 <+25>: jmp 0x400f30 <phase_2+52>
0x0000000000400f17 <+27>: mov -0x4(%rbx),%eax
0x0000000000400f1a <+30>: add %eax,%eax
0x0000000000400f1c <+32>: cmp %eax,(%rbx)
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
0x0000000000400f3a <+62>: jmp 0x400f17 <phase_2+27>
0x0000000000400f3c <+64>: add $0x28,%rsp
0x0000000000400f40 <+68>: pop %rbx
0x0000000000400f41 <+69>: pop %rbp
0x0000000000400f42 <+70>: retq
End of assembler dump.
函数基本栈帧处理后,调用了一个名为 <read_six_numbers> 的函数,顾名思义,应该是读取六个数字,我们也可以反汇编这个函数的机器指令,查看其汇编指令:
(gdb) disas 0x40145c
Dump of assembler code