大名鼎鼎bomblab实验
必要时需要自己画出函数栈帧!
必要时需要自己画出函数栈帧!
必要时需要自己画出函数栈帧!
由于不会作图,这里就不画了。
phase_1
通过反编译bomb文件得到phase_1的汇编代码
观察phase_1的汇编代码
观察将0x402400传入%esi, %esi是函数调用时的参数存放的寄存器,因为函数调用所用的寄存器都是依次有顺序的。%rdi, %rsi, %rdx, %rcx , %r8, %r9,发现直接%esi,可以推测%edi存放输入的字符串的地址。c语言中的字符串实际是字符指针。
进入函数strings_not_equal,由名字可以看出来是比较两个字符串是否相等的。%eax寄存器存放函数返回值。
test指令测试%eax是否为0,如果为0就证明该两个字符串相等。
关键是0x402400存放的字符串是什么。利用gdb可以查看存放在该地址的字符串
输入该字符串解决phase_1。
phase_2
汇编代码
我们先来看第一部分
两条push指令将后面要用的的寄存器存放在栈上,然后栈底指针%rsp减0x28。
将%rsp的值给%rsi,作为参数存放的地址调用read_six_numbers函数。
第二部分
read_six_numbers的汇编代码
从0x401460——0x40147c,这些指令都是在为调用__isoc99_sscanf@plt函数做准备。
0x401460该地址的指令将%rsi存放在%rdx,要知道%rsi存放的是调用<read_six_numbers>前的栈顶指针。
0x401467和0x40146b该地址的指令获得最后一个数的地址。并存入目前函数的栈帧中。(函数的参数传递所需要的6个寄存器,而%rdi,%rsi,被占用,所以只能有四个寄存器用来传递参数。分别是%rdx,%rcx,%r8,%r9。多余的两个就需要存入目前函数的栈帧)
在这里我们就知道了就该函数就是读6个值存入phase_2的栈帧。地址从%rsp到%rsp + 0x14(这时候的%rsp的地址是调用<read_six_number>之前的值。)
第三部分
从指令看出输入的第一个数要等于1,不然就会爆炸。
让我们看看0x400f30地址的指令。可以发现是一条lea指令,加载了输入的第二个的地址存放在寄存器%rbx中。下一条而将%rsp + 0x18存放在了%rbp。
然后又跳回了0x400f17。在接下来的三条指令,获取了前一个数,并将这个数乘2,再来比较目前的数。
如果相等(也就是后一个数是前一个数的两倍),那么就跳转到0x400f25。可以看到该指令是修改寄存器,使之保存下一个数的地址。
至此,我们知道是一个循环。
所以有结论
-
- 输入的第一个数是1
- 2.后一个数是前一个数的两倍。
所以答案就是1 2 4 8 16 32
phase_3
分部分来记是为了解释做记录每一跳指令。实际上做这个需要看整体。
第一部分
加载两个地址然后调用输入函数__isoc99_sscanf@plt。
然后输入的第一个数和7比较,如果满足大于7就跳到0x400fad,往下看,可以看到这是一个调用炸弹函数的指令的地址。也就是输入的第一个数要小于7。
往下看
第二部分
将第一个输入的值存入了%rax,然后最关键的来了。
0x400f75地址这一条指令,很关键。可以看出,跳转的位置根据你的输入的第一个数来决定。
带星号的跳转是间接跳转,就是根据地址0x402470 + %rax * 8存放的数作为跳转目的。
我们继续读下去。又发现有好几组的指令都是存值,然后就是直接跳转到0x400fbe,发现就是存的值和输入的值作比较。那么就可以做出一个猜测,就是需要根据你输入的第一个数跳转到指定的地方,然后输入的第二个数就要和跳转之后存放的数相等。
关键就是地址0x402475 + %rax * 8存放了什么。
打开gdb。
这也证实了我的猜测。至此,就可以解决了。有好几个答案。