《深入理解计算机系统》实验二Bomb Lab
《深入理解计算机系统》实验二Bomb Lab个人思路和过程
前言
《深入理解计算机系统》实验二Bomb Lab的下载和官网文档的机翻请看
《深入理解计算机系统》实验二Bomb Lab下载和官方文档机翻
用的调试工具是gdb,用到的指令如下
| 指令 | 作用 |
|---|---|
| break | 打断点 |
| disassemble | 查看汇编语言 |
| run | 重新开始运行文件 |
| list | 查看源代码 |
| info registers | 所有寄存器的值 |
| p $rsp | 查看寄存器rsp的值,其他寄存器同 |
| next | 单步调试(逐过程,函数直接执行),简写n |
| step | 单步调试(逐语句:跳入自定义函数内部执行),简写s |
| continue | 继续运行,简写c |
| info breakpoints | 查看当前设置的所有断点 |
| x/a 地址,x/s 地址。x/3a | 查看地址的十六进制,查看地址的字符形式。3表示输出3个单位 |
第一炸
根据bomb.c的内容判断出第一个炸弹的函数是phase_1。
反编译bomb
找到main()随便看看
第一个参数通过寄存器%rdi传递
找到关键函数phase_1的信息
发现有个strings_not_equal函数根据它的名字意思是判断字符串相不相等,上面有个"mov $0x402400,%esi"指令,而寄存器%esi表示第二个参数,第一个参数%rdi是我们输入的字符串,那就应该是判断输入的字符串和寄存器%esi的值相不相等。
用gdb调试bomb
在phase_1函数打上断点,运行程序,随便输入一个字符串
到phase_1函数中查看一下0x402400地址中的值
这个就是我们需要相等的值。
找到ASCII找到0x7220726564726f42对应的字符

小端机器所以即
42 6f 72 64 65 72 20 72
B o r d e r r
Border r
直觉告诉我这答案不太对,感觉少了点啥。
试一下,炸了。
看到文档的
就试了一下
划了一下,居然给我看见了
看来就是这个字符串没错了
Border relations with Canada have never been better.

第一阶段拆除完毕。
后面发现在这里不应该查看十六进制
可以直接查看字符
第二炸
写第二炸之前我们先把第一炸的答案放入一个txt文件中
按照下面的方式运行就可以直接进入第二炸,不用在输一遍第一炸的答案
后同。
反汇编bomb找到phase_2函数
看到跳转去了read_six_numbers函数,看其意是输入六个数字。
在反编译bomb里找到read_six_numbers函数,看到这么多lea而且获取的地址间隔没有那么有规律,有点懵
查一下
地址的值。果然是输入六个数字,还是整数
反过来看phase_2函数,用gdb来调试看清楚点,看输入完六个字符后进行了什么操作。
把它大概用语言描述出来,还是有点懵
if(%rsp的值==1){
%rbx=0x4(%rsp)
%rbp=0x18(%rsp)
do{
%eax=-0x4(rbx)
%eax+=%eax
if(%eax==%rbx){
%rbx+=4
}
}while(%rbp!=%rbx)
//%rbp==%rbx通关
}else{
//%rsp的值!=1
💥
}
额总感觉这些0x4,0x18和read_six_numbers中的有点像。
不太清楚,于是我决定把程序一个一个调试了,然后看看栈中数据
(红色对应红色的数据,左上角是我输入的数据,调试到
0x000000000040148a <+46>: callq 0x400bf0 <__isoc99_sscanf@plt>停止)
到这里我看出个大概了,刚好6个4字节,这应该就是存放我们输入的整数的地方
read_six_numbers返回到phase_2时,弹出0x0x7fffffffe3d8内存中存放返回phase_2的地址,返回到phase_2时,此时%rsp指向0x7fffffffe3d0,即第一个整数的内存。
之前写下的输入6个数字phase_2做什么也清楚了
if(%rsp的值==1){
%rbx=0x4(%rsp) //第2个
%rbp=0x18(%rsp) //第6个后的数据
do{
//第一趟 第二躺
%eax=-0x4(rbx) //第1个 第2个
%eax+=%eax //第1个加第1个 第2个加第2个
if(%eax==%rbx){ //第1个加第1个等于第2个 第2个加第2个等于第三个
%rbx+=4 //取第3个 取第4个
}
}while(%rbp!=%rbx) //%rbp==%rbx 说明六个数据都遍历到了
//%rbp==%rbx通关
}else{
//%rsp的值!=1
💥
}
第一个是1
然后 2 4 8 16 32
艰苦过关。
第三炸
反编译bomb找到phase_3函数。
用gdb调试bomb,在phase_3函数上打上断点运行。随便输入一个数

注意到这个
查看0x4025cf处的值
这道题应该就是输入两个整数了。
重新运行,输入两个数:1 3
给sub $0x18,%rsp后打上断点后运行,查看初始栈情况如何
得到
然后在0x400f60打上断点
看看经过0x400bf0 <__isoc99_sscanf@plt>(根据phase_2和phase_3使用此函数的方式来看,__isoc99_sscanf@plt函数应该接收四个参数,第一个参数是以空格分割的字符(%rdi),第二个参数是格式化的格式(%rsi),第三个参数是要放入数据的起始地址(%rdx),第四个参数是要放入数据的终止地址(%rcx),返回值%rax是数据的个数)处理过的栈如何。
经过__isoc99_sscanf@plt,%rax的值是2(不用爆炸了)
把后面的代码大概的用语言表示

if(%eax>1){
if(7 < 0x8(%rsp)){
%eax = 0x8(%rsp)
//jmpq *0x402470(,%rax,8)
switch (%rax){
case 0:
%eax=0xcf
break
case 2:
%eax=0x2c3
break
case 3:
%eax=0x100
break;
case 4:
%eax=0x185
break
case 5:
%eax=0xce
break
case 6:
%eax=0x2aa
break
case 7:
%eax=0x147
break
case 1:
%eax=0x137
if(%eax==0xc(%rsp)){
//通关
}else{
💥
}
}else{
💥
}
}else{
💥
}
}
可以看上面的栈来看出我们输入的1和3分别对应的是0x8(%rsp)=1和0xc(%rsp)=3。
根据代码我们只需要让0x8(%rsp)=1然后去到case 1,让0xc(%rsp)=0x137(331)即可通关
顺利过关。
第四炸
反编译bomb找到phase_4
发现前面的代码和phase_3前面的一模一样。
直接拿初始化栈的(图)来使用
和第三炸一样,也是输入两个整数。
gdb调试,phase_4打上断点运行,输入1 3

在0x401029处打上断点运行
此时栈情况和第三炸刚开始一样。因为输入了两个数字所以此时%eax=2,免于一炸。
到这里。0x8(%rsp)的低4字节我们是1<=0xe,所以跳到了炸弹后面。
可以看出要跳转到func4,我们输入的第一个整数1在寄存器%edi中作为第一个参数,第二个参数%esi=0,第三个参数%edx=0xe,
func4函数,可以看出它有递归调用
我发现这里没有使用到我们输入的第二个数3在内存(0xc(%rsp)),返回去在phase_4中看到了
很明显第二个数是0。这个0让我想到居然是递归那一般来说要么从前往后要么从后往前,然后从这里
我感觉答案应该是下面的其中一个
e 0
0 0
试一下e 0 炸了,0 0 过了
蒙混过关。
--------------------------------------
写完之后回过头来看确实有点混了,这个0真是蒙对的。
大概是下面这样(俺也看不懂它做了啥,只知道是一系列递归运算)
且我试的时候"0 0",“1 0”,“3 0”,"7 0"都是可以通关的。
在打调试的时候发现在这一步,只要在这里查看寄存器$ecx的值就会有7 3 1 0




猜测是这一步起了决定性作用
计算机中14(0xe)/2=7,7/2=3,3/2=1,1/2=0。
第五炸
反编译bomb找到phase_5
注意到下面两个,第一个表明了输入字符的长度是6。
第二个表明了要使%rdi里的字符(0x10(%rsp))==%esi里的字符(0x40245e)。哪肯定不可能是输入的字符是%esi里的字符(0x40245e)就过关了那么简单啦(试过炸了)。
在gdb里查看了内存0x40245e里的字符是"flyers",这是关键信息
用gdb查看获取更多的信息。输入“123456”6个字符
看看初始化的栈
(经验告诉我,下面这些数据不必细看)
%rdi的值是我们输入的字符的ASCII十六进制表示
加了金丝雀(课本P199,一种栈保护机制,从mov %fs:0x18,%rax和后面取出来做对比可以看出,这此题中此代码不是关键代码)的栈

下面进入正文——拆炸弹。
看看关键代码(判断输入的是6个字符后进入)
用语言大概的表示出来
%rbx=%rdi
%rax=金丝雀
0x18(%rsp)=%rax
%eax=0
if(%rdi的长度==6){
%eax=0
while(%rax!=6){
%ecx=(%rbx,%rax,1)
(%rsp)=%cl
%rdx=(%rsp)
%edx=%edx & 0xf
%edx=0x4024b0(%rdx)
0x10(%rsp,%rax,1)=%dl
%rax++
}
0x16(%rsp)=0
%esi=$40245e= "flyers"
%rdi=0x10(%rsp)
if(%rsi==%rdi){
nopl 0x0(%rax,%rax,1) //不懂
%rax=0x18(%rsp)
if(%rax^金丝雀(就是如果栈没有被破坏。这不重要)){
//通关
}else{
call __stack_chk_fail@plt
}
}
}
迷迷糊糊!还是一行一行调试。
从
40108b: movzbl (%rbx,%rax,1),%ecx
开始调试,运行到0x40108f(即下一行)停止。
从结果和指令来看,0x40108b获取到了我们输入的第一个字符"1"。可得循环会循环6次,也就是6个字符依次遍历
运行完
40108f: mov %cl,(%rsp)
到0x401092(即下一行)停止。从指令来看,获取%ecx的低8位(即31)放入栈中。
401092: mov (%rsp),%rdx
在从栈中取出来放到%rdx
401096: and $0xf,%edx
只取低4位的值,也就是0x31变成1
401099: movzbl 0x4024b0(%rdx),%edx
从0x4024b0内存中的数组取数据,索引是%edx,现在的索引是1
数组内容是
maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?
到这里就差不多知道这题是要做什么的了,从上面的数组中找到"flyers"对应的六个索引。
4010a0: 88 54 04 10 mov %dl,0x10(%rsp,%rax,1)
把取到的数据放入%rsp+%rax+0x10处。索引1对应的数据是"a"
此时栈如图
输入字符“123456”循环完的栈如图

到这里清楚多了
%rbx=%rdi //输入123456,%rdi=363534333231
%rax=金丝雀
0x18(%rsp)=%rax
%eax=0
if(%rdi的长度==6){
%eax=0
while(%rax!=6){
%ecx=(%rbx,%rax,1) //获取输入的第一个字符
(%rsp)=%cl //它的低字节,8位
%rdx=(%rsp)
%edx=%edx & 0xf //只取低4位的值,也就是字符转数字1的ascii码0x31变成1(丢高4位)
%edx=0x4024b0(%rdx) //从一个数组里面取值索引是%edx
0x10(%rsp,%rax,1)=%dl //把取出来的值放入栈中
%rax++ //%rax作为循环变量
}
0x16(%rsp)=0
%esi=$40245e= "flyers"
%rdi=0x10(%rsp)
if(%rsi==%rdi){
nopl 0x0(%rax,%rax,1) //不懂
%rax=0x18(%rsp)
if(%rax^金丝雀(就是如果栈没有被破坏。这不重要)){
//通关
}else{
call __stack_chk_fail@plt
}
}
}
好了,差不多了,找答案"flyers"
maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?
索引:9fe567。需要注意的是直接打上去会爆炸。
因为这个
所以我们去"man ascii"里找
得到"9?>567"
半途而立通关。
这个答案也是对的,去掉高四位符合"9fe567即可"

第六炸
听说这是最难的一关
反编译bomb找到phase_6的函数,有点长…
看到read_six_numbers,应该是输入六个字符
gdb调试在phase_6打上断点,输入"1 2 3 4 5 6"运行,看看
运行到这里,看看初始栈的情况

执行read_six_numbers前
read_six_numbers函数的行为有点像是第一个参数(%rdi)是以空格分割的6个整数,第二个参数(%rsi),需要放入的起始地点,把六个整数依次放入。

代码好长,看的眼花,就把代码写成语言表达。还是挺难看懂,没关系,经过了我几个小时一行一行调试已经搞的七七八八了。
%r13=%rsp
%rsi=%rsp
read_six_numbers(%rdi,%rsi..);
%r14=%rsp
%r12d=0
32:
%rbp=%r13
%eax=0x0(%r13)
%eax=%eax-1
if(%eax<=5){
%r12d=%r12d+1
if(%r12d==6){ //0x0000000000401130 <+60>: je 0x401153 <phase_6+95>
%rsi=0x18(%rsp)
%rax=%r14
%ecx=0x7
do{
%edx=%ecx
%edx=%edx-(%rax)
(%rax)=%edx
%rax=%rax+4
}while(%rax != %rsi) //0x000000000040116d <+121>: jne 0x401160 <phase_6+108>
%esi=0
//0x0000000000401174 <+128>: jmp 0x401197 <phase_6+163>
%ecx=(%rsp,%rsi,1)
if(%ecx<=1){ //0x000000000040119d <+169>: jle 0x401183 <phase_6+143>
143:
%edx=0x6032d0 //%edx=0x10000014c
148:
0x20(%rsp,%rsi,2)=%rdx
%rsi=%rsi+4
if(%rsi==0x18){ //0x0000000000401195 <+161>: je 0x4011ab <phase_6+183>
%rbx=0x20(%rsp)
%rax=lea(0x28(%rsp))
%rsi=lea(0x50(%rsp))
%rcx=%rbx
//<201>
201:
%rdx=(%rax)
0x8(%rcx)=%rdx
%rax=%rax+8
//<212>
if(%rsi!=%rax){
%rcx=%rdx
goto 201 //0x00000000004011d0 <+220>: jmp 0x4011bd <phase_6+201>
}else
if(%rsi==%rax){ //0x00000000004011cb <+215>: je 0x4011d2 <phase_6+222>
0x8(%rdx)=0
%ebp=5
do{
%rax=0x8(%rbx)
%eax=(%rax)
if((%rbx)>=%eax){ //0x00000000004011e7 <+243>: jge 0x4011ee <phase_6+250>
%rbx=0x8(%rbx)
%ebp=%ebp-1
}else{💥}
}while(%ebp!=0) //0x00000000004011f5 <+257>: jne 0x4011df <phase_6+235>
// 通关...
}
}else if(%rsi!=0x18){
%ecx=(%rsp,%rsi,1)
if(%ecx<=1){ //0x000000000040119d <+169>: jle 0x401183 <phase_6+143>
goto 143
}else ($ecx>1){
%eax=1
%edx=0x6032d0
//0x00000000004011a9 <+181>: jmp 0x401176 <phase_6+130>
do{
%rdx=0x8(%rdx)
%eax=%eax+1
}while(%eax!=%exc)
goto 148
}
}
}else if(%ecx>1){
%eax=1
%edx=0x6032d0
do{
%rdx=0x8(%rdx)
%eax=%eax+1
}while(%eax!=%exc)
goto 148
}
}else if(%r12d!=6){
%ebx=%r12d
%rax=%ebx
%eax=(%rsp,%rax,4)
if(0x0(%rbp)!=%eax){ //0x000000000040113e <+74>: jne 0x401145 <phase_6+81>
81:
%ebx=%ebx+1
if(%ebx<=5){ //0x000000000040114b <+87>: jle 0x401135 <phase_6+65>
%rax=%ebx
%eax=(%rsp,%rax,4)
if(%eax!=0x0(%rbp)){
goto 81
}else{💥}
}else{
%r13=%r13+4
goto 32
}
}else if(0x0(%rbp)==%eax){
💥
}
}
}
先看第一部分
前面是初始化。
且可以看出输入的数据不能大于6。
遍历的索引%r12d只有在这里才变动,且是1-2-3-4-5-6,所以刚开始是在%r12d!=6处运行
%r12d!=6处的代码
第一部分主要是:判断输入的六个数有没有相同的,有相同就会💥
循环完一趟后就又回到开始下一轮
当判断完六个数不相同时,开始进入第二部分
第二部分主要是:拿7减去我们输入的数
输入的数变化如下(注:它不是完成了倒序的操作,这里是因为我输入的数据是1-2-3-4-5-6所以看起来像倒序)
下面进行下一步,在进行下一步之前我们需要知道后面出现的"%edx=0x6032d0"中的0x6032d0表示的是什么?
查看一下
看其命名是个结点,实际上也确实是。后面循环就会发现。现在提前了解。
为什么隔了16个字节?因为它实际上是这样的,还有8位字节存放下一个结点的地址。有链表的感觉了。而且感觉低4字节放的是数据
记下来
0x6032d0 <node1>: 0x10000014c 0x014c=332
0x6032d8 <node1+8>: 0x6032e8 <node2>
0x6032e0 <node2>: 0x2000000a8 0x00a8=168
0x6032e8 <node2+8>: 0x6032f0 <node3>
0x6032f0 <node3>: 0x30000039c 0x039c=924
0x6032f8 <node3+8>: 0x603300 <node4>
0x603300 <node4>: 0x4000002b3 0x02b3=691
0x603308 <node4+8>: 0x603310 <node5>
0x603310 <node5>: 0x5000001dd 0x01dd=477
0x603318 <node5+8>: 0x603320 <node6>
0x603320 <node6>: 0x6000001bb 0x01bb=443
0x603328 <node6+8>: 0
知道了0x6032d0表示的是什么后就可以进行第三部分了。
第三部分主要是:按照我们上面那个减7之后的数据(6-5-4-3-2-1)的顺序(第6个结点先放然后第5个…)来把结点放入我们的数据的后16个字节里。
放入后的栈如下:
经过第三部分的循环,$rsi==0x18满足了,现在进行第四部分。
先来看看第四部分的初始化。
栈如下
第四部分主要是:重新设置结点的下一结点,
按照放入的顺序(6结点->5结点->4结点->3结点->2结点->1结点)
结点设置完毕,终于到了最后一部分,第五部分。
第五部分就是判断当前结点的值是不是比下一结点的值大。全部满足就通过。
我的输入1-2-3-4-5-6带来的结点6->5->4->3->2->1在这一步刚开始就💥了
总结一下,做了哪些事情
- 循环判断输入的六个数是否相同,有相同的就直接💥
- 拿7减去我们输入的数
- 按照我们栈中的数的顺序来存放结点(注意经过了第2步)
- 按照栈中的顺序来链接结点
- 判断当前结点的值是不是比下一结点的值大,全部满足就通过
根据我们上面列出的六个结点的值可得大小排序为
3(0x039c=924)->4(0x02b3=691)->5(0x01dd=477)->6(0x1bb=443)->1(0x014c=332)->2(0x00a8=168)
因为要7-。所以最后可得需要输入的数为"4 3 2 1 6 5"。
有趣通关!最后总结的半汇编半语言代码如下
%r13=%rsp
%rsi=%rsp
read_six_numbers(%rdi,%rsi..);
%r14=%rsp //初始化
%r12d=0
32:
%rbp=%r13
%eax=0x0(%r13) //获取输入的数据
%eax=%eax-1 //输入的数据减1
if(%eax<=5){ //从这里可以看出输入的数据不能大于6
%r12d=%r12d+1 //看起来是要遍历的索引,而且是从第2个数(索引从0开始)开始的。
//%r12d==6,说明遍历完了最后的一个
if(%r12d==6){ //0x0000000000401130 <+60>: je 0x401153 <phase_6+95>
//②第二部分这里主要操作是:拿7减去我们输入的数
%rsi=0x18(%rsp) //获取我们输入的六个数据的后地址,作为循环的终止条件
%rax=%r14 //获取我们数据的起始地址
%ecx=0x7
do{
%edx=%ecx
%edx=%edx-(%rax) //拿7减去遍历的数
(%rax)=%edx //在放回去。要记住从这里开始我们的数据已经变了
%rax=%rax+4 //遍历下一个
}while(%rax != %rsi) //0x000000000040116d <+121>: jne 0x401160 <phase_6+108>
%esi=0 //初始化
//0x0000000000401174 <+128>: jmp 0x401197 <phase_6+163>
%ecx=(%rsp,%rsi,1) //获取(7-我们输入的第1个数)(上面的7-输入的数)。此时获取数据顺序是6-5-4-3-2-1
//获取的是第1个结点才走这里。其他结点都要通过遍历获取,不能直接获取到。链表
if(%ecx<=1){ //0x000000000040119d <+169>: jle 0x401183 <phase_6+143>
143:
%edx=0x6032d0 //链表头结点
148:
0x20(%rsp,%rsi,2)=%rdx //③第三部分的主要操作是这个:
//按照我们栈中的数的顺序来存放结点。现在我们是6-5-4-3-2-1。表示第6个结点先放然后第5个...
//我们的数据后8字节是0。把结点放入0后面,即我们的数据的后16个字节
%rsi=%rsi+4 //获取我们的数据的指针
//我们的数据只占了24(4*6)个字节所以%rsi==0x18时说明遍历了所有数据
if(%rsi==0x18){ //0x0000000000401195 <+161>: je 0x4011ab <phase_6+183>
%rbx=0x20(%rsp) //%rbx=%rsp+0x20。这是等于值。等于放入的第1个结点
%rax=lea(0x28(%rsp)) //%rax指向0x28(%rsp)。指向第2个放入的结点
%rsi=lea(0x50(%rsp)) //%rsi指向0x50(%rsp)。此处等于循环终止条件
%rcx=%rbx //%rcx=%rbx。等于放入的第1个结点
//<201>
201: //④第四部分的主要操作是这个:
%rdx=(%rax) //%rdx等于第n(n的初始值是2)个放入的结点(%rax指向第n个放入的结点(%rax)就等于第n个放入的结点)
0x8(%rcx)=%rdx //让第n-1个放入的结点的下一个结点指向第n个放入的结点。(形成6->5->4->3->2->1)
%rax=%rax+8 //指向下一个结点
//<212>
if(%rsi!=%rax){ //%rsi==%rax说明所有结点的下一个结点都已经重新设置完毕。
%rcx=%rdx //到下一个结点设置
goto 201 //0x00000000004011d0 <+220>: jmp 0x4011bd <phase_6+201>
}else //还要等上面遍历完6个结点才到这里
if(%rsi==%rax){ //0x00000000004011cb <+215>: je 0x4011d2 <phase_6+222>
0x8(%rdx)=0 //让尾结点下一个结点为0
//⑤第五部分也是最后一步这里的主要操作是:判断当前结点的值是不是比下一结点的值大。全部满足就通关
%ebp=5 //循环次数
do{
%rax=0x8(%rbx) //让%rax等于放入的第n(n的初始值是2)个结点
%eax=(%rax) //让%eax等于放入的第n个结点的低4字节的值。也就是结点的值
//让第n个结点和第n-1的值比较,如果第n-1结点的值大于第n个结点的值就通过,否则就💥
if((%rbx)>=%eax){ //0x00000000004011e7 <+243>: jge 0x4011ee <phase_6+250>
%rbx=0x8(%rbx)
%ebp=%ebp-1
}else{💥}
}while(%ebp!=0) //0x00000000004011f5 <+257>: jne 0x4011df <phase_6+235>
// 通关...
}
}else if(%rsi!=0x18){
%ecx=(%rsp,%rsi,1)
//除了第1个结点可以直接获取。其他结点都要通过遍历获取。链表
if(%ecx<=1){ //0x000000000040119d <+169>: jle 0x401183 <phase_6+143>
goto 143
}else ($ecx>1){
%eax=1 //第一个结点
%edx=0x6032d0 //链表头结点
//0x00000000004011a9 <+181>: jmp 0x401176 <phase_6+130>
do{ //循环到第%exc个结点
%rdx=0x8(%rdx)
%eax=%eax+1
}while(%eax!=%exc)
goto 148
}
}
}else if(%ecx>1){ //除了第1个结点可以直接获取。其他结点都要通过遍历获取。链表
%eax=1 //第一个结点
%edx=0x6032d0 //链表头结点
do{ //循环到第%exc个结点
%rdx=0x8(%rdx)
%eax=%eax+1
}while(%eax!=%exc)
goto 148
}
}else if(%r12d!=6){ //①第一部分这里主要操作是:循环判断了输入的六个数是否相同,有相同的就直接💥
//它是这样的,先拿第1个数来作为基值,然后遍历2-3-4-5-6。
// 在拿第2个数来作为基值,然后遍历3-4-5-6。...
%ebx=%r12d //遍历从基值后一个数开始
%rax=%ebx //索引
%eax=(%rsp,%rax,4) //获取遍历到的数,如遍历到第1个数就获取第2个数
//0x0(%rbp)表示基值
if(0x0(%rbp)!=%eax){ //0x000000000040113e <+74>: jne 0x401145 <phase_6+81>
81:
%ebx=%ebx+1 //遍历下一个数
//遍历次数最多的是第1次,要遍历2-3-4-5-6的数
if(%ebx<=5){ //0x000000000040114b <+87>: jle 0x401135 <phase_6+65>
%rax=%ebx
%eax=(%rsp,%rax,4)
if(0x0(%rbp)!=%eax){ //不等于就继续遍历
goto 81
}else{💥} //等于就💥
}else{ //拿第N个数作为基值,且遍历完了,就拿第N+1个数作为基值来继续遍历。
%r13=%r13+4
goto 32 //0x0(%rbp)表示的基值32中改变的
}
}else if(0x0(%rbp)==%eax){ //如果六个数中有相等的数就会💥
💥
}
}
}
这是当时打的断点(没有第一部分检查相不相同和第五部分判断大小。红线表示这个地方已经打过断点了),仅供参考
最终答案为
隐藏炸
有个小彩蛋,不过给我拆掉了。
在bomb.c文件中有
反编译bomb文件,里面找到了
看名字就是这两货了。而且是先走secret_phase。在文件中找到有secret_phase的地方,发现在phase_defused中有跳转到secret_phase函数的地方。
而phase_defused是每次拆除炸弹都会走的地方
看来就是从phase_defused中进入隐藏炸弹。
调试观察phase_defused函数
这里是判断你是不是六个炸弹都已经拆除了,拆除了才可以进入隐藏炸
出现了数据
要两个整数,一个字符,现在只有"0 0"两个,后面判断如果不是3个就进入不了隐藏关,看来进入隐藏关的关键是这个
<input_strings>观其名猜其意,应该就是我们输入的文件的内容
一看果然如此。"0 0"就是第四题的答案。
所以需要在"0 0"后面添加一个字符串。
后面的代码有个判断字符的。
查看内存0x402622处的字符。
这就是进入隐藏关的字符。
即可进入隐藏关
主要看的地方。输入一行数,该数应该是个数字,其小于等于0x3e9(1001)(减1就是0x3e8),
需要查看0x6030f0的值,调用fun7,且fun7的返回值要是2。
看这名字是个结点啊。
在看看
看起来是个二叉树
0x3e9也出现了,看来输入的是二叉树中的值。看看调用的fun7函数
fun7函数是这样,标准的二叉树查找
要想返回2就要先走左边,然后右边,然后命中返回0,回到右边%rax=0,就返回1,回到左边1+1=2.返回2。所以要根结点左边在右边的值,即输入22(0x16)。
拆弹成功。
最终答案为
个人总结和感想
拆完第六炸还是感觉挺不错的,随着刚开始看见写出来的代码懵懵懂懂,后面随着一步一步的调试,代码逐渐清晰起来,到最后拆弹成功,真不错。不过我是先做了实验三然后在写实验二的,感觉这两个都是能加深对栈堆的了解,只要知道了数据是怎么存储的,画图一步一步来,还是可以解决的。在写实验三和实验二就想着不百度(还是有那么一丢丢丢丢)自己可不可以解决,还是可以的嘛。
更多推荐

所有评论(0)