CSAPP Bomb Lab

参考博客
知识点
gdb
-
jump 函数名 / * 地址名jump 能够很灵活地在gdb调试汇编代码时跳转
当一不小心错过了关键信息时,我们便可以使用jump
-
run(简写为r) 参数1 参数2 ...当我们在调试gdb想要携带参数时,通过
gdb bomb input.txt是不可以行的应该在
run后加上参数
-
layout src显示源码layout asm显示汇编代码layout split源码和汇编代码都显示ctrl+x+a快捷退出layout
-
x/[count][format] [address]打印内存值,从所给地址(address)处开始,以指定格式(format)显示 count 个值
比较强大的用法是
x/s address可以打印出字符串出来x/100i address可以从address开始打印出100条指令出来
-
info register (指定寄存器名,如rax)打印寄存器的值
info register rax指定打印rax的值i r rax可以这样简写
炸弹2:Phase_2 栈中探险
这个主打的就是一个静下心来分析栈中的内容
解决这个问题的关键我总结为3个:
-
知道参数传递时寄存器的时候以及在栈的使用
-
在汇编代码中对内存地址有敏感性

开始我看到这个
0x4022b4我是一点反应都没有,后来看题解才知道用x/s 0x4022b4 -
知道sscanf的使用方法
sscanf 第一个参数是char *, 第二个参数是想要格式化输入的格式,后面的参数是接受数据的变量,返回值是接受到数据的个数
还有就是我发现汇编中调用函数后,都喜欢把返回值放到%rax或%eax中,这点很重要
知识点
在函数a中调用函数b,我们函数a中要将参数放到指定的6个寄存器上。
(有个有趣的点是:调用者保存寄存器都是用来保存参数的寄存器)
当参数超过6个,函数a就要在自己的栈帧上分配空间用来放参数,且参数越在后面,越先入栈
这点是符合直觉的,因为参数越在后面可能越后别用到。P169

如上图,在本帧中具体顺序是(从上往下):
-
被保存的寄存器
即保存用到的
被调用者保存寄存器 -
局部变量
即在函数内部定义的一些变量有事会用栈来保存
-
参数构造区
也就是在函数中调用其他函数,在传递参数时,寄存器不够用了,将过多的参数放到栈中
-
返回地址
使用
call指令时,将本函数中下一条要执行的指令地址(在%rip中)压栈我们还需要注意:
callret这些指令在执行的时候 携带了push %rip和pop %rip操作的
P164
在通过栈存放参数时,所有数据都是向8的倍数对齐 P169
但是我们通过栈存放局部变量,大多数情况并不是这样 P170(栈上的局部存储)P196(数组存放到栈中)P190(数据对齐)
在x86-64中,我们的处理器一次能够处理64个位,一次也能够取到64位
我们的内存(注意,栈也在内存中),是以1字节(8位)作为单位编号,所以 64位/8位=8
所以我们经常能够看到如下栈表示(内存表示):

但是我觉得实际分析起来还是如下更好分析:

还有一件关于寄存器的事,下面以%rax举例:
如 movl 0x40000000 %eax, 这个时候%rax的高32为都会被置0 P124
Phase_5 内存寻值
这里主要有几个知识点:
-
test %eax,%eax这条指令的作用等价于
And %eax,%eax,同时当%eax结果为0,置ZF=0所以我们还经常能够在这条语句的下面看到
je,jne等,因为我们知道函数常常将结果放到%rax,判断是否相同的函数也是这样
当相同时,函数返回0,放到%rax中(或者1) -
movq %fs:40,%rax看到这个指令开始还觉得奇怪,其实他是'金丝雀值' P199
是一种栈保护机制 -
p 0x14快速打印出十六进制0x14的十进制
-
x/s $rax在gdb中要取寄存器中的值用
$,而不是%;(也可以直接rax)x/s,x/x等相当于c中的printf,都是取地址后访存
phase_6 层层访址
这个炸弹让我知道了:
如果硬刚汇编代码一条一条看下来,即使做出来了,自己也脱了一层皮吧(悲)
所以我转换思路了,我将断点设置到跳出循环处,然后观察内存和栈的变化,能够很容易知道汇编代码在做什么
b * 地址layout asmlayout regcontinue(或者简写为c)
这道题目的关键在于:

当我输入1 2 3 4 5 6时,发现保存在栈中(从高地址到低地址)的值为:
0x603320
0x603310
0x603300
0x6032f0
0x6032e0
0x6032d0
设置关键断点(慢慢分析就知道关键断点是什么了,一般是循环退出时),continue,发现得到如下结果:

我将在栈中保存的值设为argn,其中n越小表示越在栈底
那么循环做的事(我就只看了一点循环中的代码,根据结果),我猜到应该是:
*(arg(n-1)+0x8) = arg(n)
最后的判断是要求*arg(n-1)<=arg(n)!
暗雷

汇编代码在phase_6往下翻翻就能看到,这已经是明示了,secret_phase都写出来了

浙公网安备 33010602011771号