CSAPP实验二——bomb lab实验

article/2025/8/28 20:39:15

CSAPP实验二—— bomb lab实验

    • 实验前准备
    • 第一部分(phase_1)
    • 第二部分(phase_2)
    • 第三部分(phase_3)
    • 第四部分(phase_4)
    • 第五部分(phase_5)
    • 第六部分(phase_6)
    • 总结

实验前准备

在linux操作系统上安装GDB调试器。由于我是在Ubuntu虚拟机上进行实验,所以只需要在终端输入如下安装指令即可快速安装。
sudo apt install gdb

在可执行程序所在的目录下进入终端,输入gdb bomb即可启动调试过程(bomb是可执行程序名称),可以输入quit结束GDB调试。

第一部分(phase_1)

使用disas phase_1反汇编phase_1函数

   0x0000000000400ee0 <+0>:		sub    $0x8,%rsp0x0000000000400ee4 <+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,%rsp0x0000000000400efb <+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   %rbp0x0000000000400efd <+1>:		push   %rbx0x0000000000400efe <+2>:		sub    $0x28,%rsp0x0000000000400f02 <+6>:		mov    %rsp,%rsi0x0000000000400f05 <+9>:		callq  0x40145c <read_six_numbers>0x0000000000400f0a <+14>:	cmpl   $0x1,(%rsp)  			//将第一个输入与1比较0x0000000000400f0e <+18>:	je     0x400f30 <phase_2+52>    //确定第一个数字必须为10x0000000000400f10 <+20>:	callq  0x40143a <explode_bomb>0x0000000000400f15 <+25>:	jmp    0x400f30 <phase_2+52>0x0000000000400f17 <+27>:	mov    -0x4(%rbx),%eax		  //eax寄存器存放上一个输入数字的值0x0000000000400f1a <+30>:	add    %eax,%eax			  //上一个输入数字*20x0000000000400f1c <+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,%rsp0x0000000000400f40 <+68>:	pop    %rbx0x0000000000400f41 <+69>:	pop    %rbp0x0000000000400f42 <+70>:	retq   

通过进一步查看read_six_numbers函数的汇编代码,可以知道其完成的是读取用户输入的六个整数,每个数字占用四个字节大小空间,所以最终循环的终点为栈指针偏移4*6=0x18=24的位置。

这部分代码实现的是一个循环,将输入的六个数字依次与其前一位的两倍进行比较。当且仅当第一个数字为1,后一位依次是前一位的两倍的情况下,才不会引起爆炸。所以得到第二问的答案为

1 2 4 8 16 32

第三部分(phase_3)

前两部分内容都算简单,下面来看第三块

   0x0000000000400f43 <+0>:		sub    $0x18,%rsp0x0000000000400f47 <+4>:		lea    0xc(%rsp),%rcx0x0000000000400f4c <+9>:		lea    0x8(%rsp),%rdx0x0000000000400f51 <+14>:	mov    $0x4025cf,%esi0x0000000000400f56 <+19>:	mov    $0x0,%eax0x0000000000400f5b <+24>:	callq  0x400bf0 <__isoc99_sscanf@plt>0x0000000000400f60 <+29>:	cmp    $0x1,%eax0x0000000000400f63 <+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),%eax0x0000000000400f75 <+50>:	jmpq   *0x402470(,%rax,8)		//根据第一个输入的值来进行jmp的间接跳转0x0000000000400f7c <+57>:	mov    $0xcf,%eax				//第一个输入为0时跳转到这里		0x0000000000400f81 <+62>:	jmp    0x400fbe <phase_3+123>0x0000000000400f83 <+64>:	mov    $0x2c3,%eax0x0000000000400f88 <+69>:	jmp    0x400fbe <phase_3+123>0x0000000000400f8a <+71>:	mov    $0x100,%eax0x0000000000400f8f <+76>:	jmp    0x400fbe <phase_3+123>0x0000000000400f91 <+78>:	mov    $0x185,%eax0x0000000000400f96 <+83>:	jmp    0x400fbe <phase_3+123>0x0000000000400f98 <+85>:	mov    $0xce,%eax0x0000000000400f9d <+90>:	jmp    0x400fbe <phase_3+123>0x0000000000400f9f <+92>:	mov    $0x2aa,%eax0x0000000000400fa4 <+97>:	jmp    0x400fbe <phase_3+123>0x0000000000400fa6 <+99>:	mov    $0x147,%eax0x0000000000400fab <+104>:	jmp    0x400fbe <phase_3+123>0x0000000000400fad <+106>:	callq  0x40143a <explode_bomb>0x0000000000400fb2 <+111>:	mov    $0x0,%eax0x0000000000400fb7 <+116>:	jmp    0x400fbe <phase_3+123>0x0000000000400fb9 <+118>:	mov    $0x137,%eax0x0000000000400fbe <+123>:	cmp    0xc(%rsp),%eax		//第二个输入与对应的%eax里的值比较大小,只有等于才不会触发爆炸程序0x0000000000400fc2 <+127>:	je     0x400fc9 <phase_3+134>0x0000000000400fc4 <+129>:	callq  0x40143a <explode_bomb>0x0000000000400fc9 <+134>:	add    $0x18,%rsp0x0000000000400fcd <+138>:	retq   

这一部分卡我时间最长的是不知道第7行的%eax里函数的返回值为多少。如果对__isoc99_sscanf@plt函数过程进行分析感觉有点困难。这里我用了一个稍微讨巧一点的办法,和第二问的read_six_numbers函数里进行类比。因为在read_six_numbers函数里也调用了__isoc99_sscanf@plt函数,看看那里的%eax里存放的返回值会不会对我们有一定的提示。

   0x000000000040148a <+46>:	callq  0x400bf0 <__isoc99_sscanf@plt>0x000000000040148f <+51>:	cmp    $0x5,%eax0x0000000000401492 <+54>:	jg     0x401499 <read_six_numbers+61>

上图展示的是read_six_numbers函数反汇编得到的部分代码。第二问是要求输入六个数字,而这里是%eax里存放的值>5则跳转,所以可以猜想%eax存放的是输入的参数个数。

感觉有点牵强的话,也可以动手验证一下,设置好断点后,一条指令一条指令单步调试。用i r指令来查看寄存器里的值。发现猜想应该是没问题的,并且输入的参数应该是整数类型。

jmpq *0x402470(,%rax,8)这条指令往下,都是和对应跳转的位置有关。跳转到哪一行与第一个输入的数值有关,所以这一部分考察的应该是switch语句的相关知识。第一个输入与7进行无符号数比较,则可以断定第一个输入只可以为0,1,2,3,4,5,6。再分别计算0x402470(,%rax,8)的地址,就可以得到对应的跳转位置。

第一个输入值跳转位置
00x400f7c
10x400fb9
20x400f83
30x400f8a
40x400f91
50x400f98
60x400f9f

第二个输入会根据第一个输入与相应的数值比较,只有相等才可以通过。最终可以计算第三部分的答案如下

0 207(或1 311,或2 707,或3 256,或4 389,或5 206,或6 682)

第四部分(phase_4)

phase_4反汇编得到的代码如下

   0x000000000040100c <+0>:		sub    $0x18,%rsp0x0000000000401010 <+4>:		lea    0xc(%rsp),%rcx0x0000000000401015 <+9>:		lea    0x8(%rsp),%rdx0x000000000040101a <+14>:	mov    $0x4025cf,%esi0x000000000040101f <+19>:	mov    $0x0,%eax0x0000000000401024 <+24>:	callq  0x400bf0 <__isoc99_sscanf@plt>0x0000000000401029 <+29>:	cmp    $0x2,%eax				//输入为两个数值0x000000000040102c <+32>:	jne    0x401035 <phase_4+41>0x000000000040102e <+34>:	cmpl   $0xe,0x8(%rsp)0x0000000000401033 <+39>:	jbe    0x40103a <phase_4+46>   //第一个输入数值要小于等于140x0000000000401035 <+41>:	callq  0x40143a <explode_bomb>0x000000000040103a <+46>:	mov    $0xe,%edx0x000000000040103f <+51>:	mov    $0x0,%esi0x0000000000401044 <+56>:	mov    0x8(%rsp),%edi0x0000000000401048 <+60>:	callq  0x400fce <func4>		//调用func4函数,也是这一块的核心函数0x000000000040104d <+65>:	test   %eax,%eax0x000000000040104f <+67>:	jne    0x401058 <phase_4+76>	//%eax必须为00x0000000000401051 <+69>:	cmpl   $0x0,0xc(%rsp)0x0000000000401056 <+74>:	je     0x40105d <phase_4+81>	//第二个输入要等于00x0000000000401058 <+76>:	callq  0x40143a <explode_bomb>0x000000000040105d <+81>:	add    $0x18,%rsp0x0000000000401061 <+85>:	retq   

通过分析发现,在phase_4函数中还调用了func4函数,为了进一步确定第一个输入的数值,需要我们来查看func4的汇编代码。如下:

   0x0000000000400fce <+0>:		sub    $0x8,%rsp0x0000000000400fd2 <+4>:		mov    %edx,%eax0x0000000000400fd4 <+6>:		sub    %esi,%eax		//%eax=%edx-%esi0x0000000000400fd6 <+8>:		mov    %eax,%ecx0x0000000000400fd8 <+10>:	shr    $0x1f,%ecx		//逻辑右移31位,所以相当于保留符号位0x0000000000400fdb <+13>:	add    %ecx,%eax		//负数需要加一个偏置1,正数没有变化0x0000000000400fdd <+15>:	sar    %eax				//%eax等于(%edx-%esi)/20x0000000000400fdf <+17>:	lea    (%rax,%rsi,1),%ecx		//%ecx为(%edx+%esi)/20x0000000000400fe2 <+20>:	cmp    %edi,%ecx		//与%edi也就是第一个输入比较0x0000000000400fe4 <+22>:	jle    0x400ff2 <func4+36>0x0000000000400fe6 <+24>:	lea    -0x1(%rcx),%edx	//如果大了则%edx(上界)变成均值-1,进行递归0x0000000000400fe9 <+27>:	callq  0x400fce <func4>		0x0000000000400fee <+32>:	add    %eax,%eax0x0000000000400ff0 <+34>:	jmp    0x401007 <func4+57>0x0000000000400ff2 <+36>:	mov    $0x0,%eax0x0000000000400ff7 <+41>:	cmp    %edi,%ecx		//与%edi也就是第一个输入比较0x0000000000400ff9 <+43>:	jge    0x401007 <func4+57>0x0000000000400ffb <+45>:	lea    0x1(%rcx),%esi	//如果小了则%esi(下界)变成均值+1,进行递归0x0000000000400ffe <+48>:	callq  0x400fce <func4>0x0000000000401003 <+53>:	lea    0x1(%rax,%rax,1),%eax	//注意这里会变化%eax的值0x0000000000401007 <+57>:	add    $0x8,%rsp0x000000000040100b <+61>:	retq   

第四部分其实想得到答案很容易,根据分析,是比较第一个输入和%edx与%esi两个寄存器的值的均值,类似二分法折半查找。最直接的答案是第一次进入func4就直接出来,也就是第一个输入正好等于均值。由于在调用之前%edx=14%esi=0,所以第一个输入为7时,可以递归一次直接返回到phase_4,这时%eax返回值也为0,不会引爆炸弹。

如果想进一步分析的话,可以看到无论函数内部实现如何,我们只希望返回值%eax必须为0就可以了。而%eax一般都是置为0的,第20行的lea 0x1(%rax,%rax,1),%eax会对其产生影响,所以让每次递归都是上界变小就会保证%eax一定为0。

用数据会更直观一点,这里只考虑非负数的情况,假设每次递归都是上界减小,也就是均值大于第一个输入的值,直到最终结束递归。

递归次数%edx%esi%ecx%eax%edi(第一个输入)
1140777(一次直接返回)
260333(两次后返回)
320111(三次后返回)
400000(四次后返回)

所以答案如下

7 0(或3 0,或1 0,或0 0…)

第五部分(phase_5)

第五部分还是蛮有意思的,里面的构思比较巧妙。首先看一下汇编代码:

   0x0000000000401062 <+0>:		push   %rbx0x0000000000401063 <+1>:		sub    $0x20,%rsp0x0000000000401067 <+5>:		mov    %rdi,%rbx		//%rdi和%rbx存放着输入内容的内存地址0x000000000040106a <+8>:		mov    %fs:0x28,%rax	//金丝雀,缓冲区的保护机制0x0000000000401073 <+17>:	mov    %rax,0x18(%rsp)	//检查潜在的缓冲区溢出风险0x0000000000401078 <+22>:	xor    %eax,%eax0x000000000040107a <+24>:	callq  0x40131b <string_length>0x000000000040107f <+29>:	cmp    $0x6,%eax0x0000000000401082 <+32>:	je     0x4010d2 <phase_5+112>	//输入字符串长度为60x0000000000401084 <+34>:	callq  0x40143a <explode_bomb>0x0000000000401089 <+39>:	jmp    0x4010d2 <phase_5+112>0x000000000040108b <+41>:	movzbl (%rbx,%rax,1),%ecx	//每一个字符的ascii码数值传递给%ecx0x000000000040108f <+45>:	mov    %cl,(%rsp)0x0000000000401092 <+48>:	mov    (%rsp),%rdx0x0000000000401096 <+52>:	and    $0xf,%edx			//仅保留%edx最后四位0x0000000000401099 <+55>:	movzbl 0x4024b0(%rdx),%edx	//将0x4024b0加上%edx对应的内存位置的内容给%edx0x00000000004010a0 <+62>:	mov    %dl,0x10(%rsp,%rax,1) 	//得到的值进行存放0x00000000004010a4 <+66>:	add    $0x1,%rax0x00000000004010a8 <+70>:	cmp    $0x6,%rax				//循环的终点0x00000000004010ac <+74>:	jne    0x40108b <phase_5+41>0x00000000004010ae <+76>:	movb   $0x0,0x16(%rsp)		//超过6的后续用0填充0x00000000004010b3 <+81>:	mov    $0x40245e,%esi0x00000000004010b8 <+86>:	lea    0x10(%rsp),%rdi0x00000000004010bd <+91>:	callq  0x401338 <strings_not_equal>		//前面循环新得到的字符串和0x40245e存放的字符串进行比对0x00000000004010c2 <+96>:	test   %eax,%eax0x00000000004010c4 <+98>:	je     0x4010d9 <phase_5+119>0x00000000004010c6 <+100>:	callq  0x40143a <explode_bomb>0x00000000004010cb <+105>:	nopl   0x0(%rax,%rax,1)0x00000000004010d0 <+110>:	jmp    0x4010d9 <phase_5+119>0x00000000004010d2 <+112>:	mov    $0x0,%eax				//循环的起点0x00000000004010d7 <+117>:	jmp    0x40108b <phase_5+41>0x00000000004010d9 <+119>:	mov    0x18(%rsp),%rax0x00000000004010de <+124>:	xor    %fs:0x28,%rax			//检查缓冲区是否溢出0x00000000004010e7 <+133>:	je     0x4010ee <phase_5+140>0x00000000004010e9 <+135>:	callq  0x400b30 <__stack_chk_fail@plt>0x00000000004010ee <+140>:	add    $0x20,%rsp0x00000000004010f2 <+144>:	pop    %rbx0x00000000004010f3 <+145>:	retq   

一开始拿到这一题直接看到了callq 0x401338 <strings_not_equal>,所以想当然地直接先找到0x40245e对应内存位置存放的字符串“flyers"试试看,结果发现爆炸了。

于是就静下心来看了看,发现比较的两个字符串其中一个是内存地址为0x40245e的字符串,但另一个并不是输入,而是输入映射后得到的新的六个字符。也就是当你输入"flyers",结果并不会是”flyers"。具体来说,在第一次循环时,%rbx存放的是"f"的ascii码0x66,%edx中保存的是该ascii码的最后4位,也就是0x6。再根据movzbl 0x4024b0(%rdx),%edx这句话,此时%edx得到的值为"r",不是一开始的"f"。自然也就爆炸了。所以该问题还是得找到映射关系。

有了这个思路,就可以尝试查看内存0x4024b0存放了什么映射关系,使用x/s 0x4024b0查看得到结果如下:

“maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?”

后面的那句话简直了,顿时觉得实验特别好玩。前面的那些小写字母得顺序就是对应的映射关系。依次找到“f", “l”, “y”, “e”, “r”, "s"对应的位置是“9”,“15”,“14”,“5”,“6”,“7”。所以只需要ascii码最后四位值等于这些的字符就可以解开。我依次选取了“0x69”,“0x6f”,“0x6e”,“0x65”,“0x66”,“0x67”。也就是如下字符,第五问就解决了。

ionefg

第六部分(phase_6)

第六部分确实挺有难度,花的时间也是前五问的总和。其实现的功能可以进行拆分,一个部分一个部分去看会让自己的思路更清晰一点。

   0x00000000004010f4 <+0>:		push   %r14   //保存被调用者保存寄存器的内容0x00000000004010f6 <+2>:		push   %r130x00000000004010f8 <+4>:		push   %r120x00000000004010fa <+6>:		push   %rbp0x00000000004010fb <+7>:		push   %rbx0x00000000004010fc <+8>:		sub    $0x50,%rsp0x0000000000401100 <+12>:	mov    %rsp,%r130x0000000000401103 <+15>:	mov    %rsp,%rsi0x0000000000401106 <+18>:	callq  0x40145c <read_six_numbers>		//输入为6个数字0x000000000040110b <+23>:	mov    %rsp,%r140x000000000040110e <+26>:	mov    $0x0,%r12d		//%r12d用来计数,外部循环初始化起点i0x0000000000401114 <+32>:	mov    %r13,%rbp		//%rbp更新0x0000000000401117 <+35>:	mov    0x0(%r13),%eax	//当前输入数字0x000000000040111b <+39>:	sub    $0x1,%eax		//当前输入数字-10x000000000040111e <+42>:	cmp    $0x5,%eax0x0000000000401121 <+45>:	jbe    0x401128 <phase_6+52>	//需要输入的每个数字小于等于6(减1小于等于5)0x0000000000401123 <+47>:	callq  0x40143a <explode_bomb>0x0000000000401128 <+52>:	add    $0x1,%r12d0x000000000040112c <+56>:	cmp    $0x6,%r12d		//外部循环终点0x0000000000401130 <+60>:	je     0x401153 <phase_6+95>0x0000000000401132 <+62>:	mov    %r12d,%ebx		//内部循环起点j为外部循环的当前循环i0x0000000000401135 <+65>:	movslq %ebx,%rax0x0000000000401138 <+68>:	mov    (%rsp,%rax,4),%eax		//下一个输入的数字0x000000000040113b <+71>:	cmp    %eax,0x0(%rbp)		//也就是输入的六个数字两两不相等0x000000000040113e <+74>:	jne    0x401145 <phase_6+81>0x0000000000401140 <+76>:	callq  0x40143a <explode_bomb>0x0000000000401145 <+81>:	add    $0x1,%ebx				//内部循环变化0x0000000000401148 <+84>:	cmp    $0x5,%ebx				//内部循环终点0x000000000040114b <+87>:	jle    0x401135 <phase_6+65>	0x000000000040114d <+89>:	add    $0x4,%r13		//%r13更新,后面再传递给%rbp0x0000000000401151 <+93>:	jmp    0x401114 <phase_6+32>/*前面做的任务是判断输入的六个数字都小于等于6,且两两不相同*/0x0000000000401153 <+95>:	lea    0x18(%rsp),%rsi  //%rsi对应于循环终点0x0000000000401158 <+100>:	mov    %r14,%rax	//这里%r14存放的值是%rsp,作为循环起点0x000000000040115b <+103>:	mov    $0x7,%ecx0x0000000000401160 <+108>:	mov    %ecx,%edx0x0000000000401162 <+110>:	sub    (%rax),%edx0x0000000000401164 <+112>:	mov    %edx,(%rax)	//输入的每个数字x[i]都变成了7-x[i]0x0000000000401166 <+114>:	add    $0x4,%rax	//指向下一个数字,判断是否结束循环0x000000000040116a <+118>:	cmp    %rsi,%rax0x000000000040116d <+121>:	jne    0x401160 <phase_6+108>/*前面做的任务是将输入的每个数字x[i]都变成了7-x[i]*/0x000000000040116f <+123>:	mov    $0x0,%esi0x0000000000401174 <+128>:	jmp    0x401197 <phase_6+163>0x0000000000401176 <+130>:	mov    0x8(%rdx),%rdx	//不停修改%rdx的值,修改次数与输入数字的值有关0x000000000040117a <+134>:	add    $0x1,%eax0x000000000040117d <+137>:	cmp    %ecx,%eax0x000000000040117f <+139>:	jne    0x401176 <phase_6+130>0x0000000000401181 <+141>:	jmp    0x401188 <phase_6+148>0x0000000000401183 <+143>:	mov    $0x6032d0,%edx0x0000000000401188 <+148>:	mov    %rdx,0x20(%rsp,%rsi,2)	//每个占8个字节0x000000000040118d <+153>:	add    $0x4,%rsi0x0000000000401191 <+157>:	cmp    $0x18,%rsi0x0000000000401195 <+161>:	je     0x4011ab <phase_6+183>0x0000000000401197 <+163>:	mov    (%rsp,%rsi,1),%ecx	//当前循环输入的数字0x000000000040119a <+166>:	cmp    $0x1,%ecx			0x000000000040119d <+169>:	jle    0x401183 <phase_6+143>0x000000000040119f <+171>:	mov    $0x1,%eax 0x00000000004011a4 <+176>:	mov    $0x6032d0,%edx0x00000000004011a9 <+181>:	jmp    0x401176 <phase_6+130>/*前面做的任务是修改(%rsp+32)到(%rsp+80)的内存值(8字节为单位),根据输入的六个数字来决定*/0x00000000004011ab <+183>:	mov    0x20(%rsp),%rbx0x00000000004011b0 <+188>:	lea    0x28(%rsp),%rax0x00000000004011b5 <+193>:	lea    0x50(%rsp),%rsi0x00000000004011ba <+198>:	mov    %rbx,%rcx0x00000000004011bd <+201>:	mov    (%rax),%rdx0x00000000004011c0 <+204>:	mov    %rdx,0x8(%rcx)0x00000000004011c4 <+208>:	add    $0x8,%rax0x00000000004011c8 <+212>:	cmp    %rsi,%rax0x00000000004011cb <+215>:	je     0x4011d2 <phase_6+222>0x00000000004011cd <+217>:	mov    %rdx,%rcx0x00000000004011d0 <+220>:	jmp    0x4011bd <phase_6+201>/*让之前(%rsp+32)到(%rsp+80)的六块内存区域,前一块内存区域后八字节存放指向下一块内存区域的指针,链表结构*/0x00000000004011d2 <+222>:	movq   $0x0,0x8(%rdx) 		//链表尾节点的指针指向00x00000000004011da <+230>:	mov    $0x5,%ebp0x00000000004011df <+235>:	mov    0x8(%rbx),%rax		//当前节点的指针(下一个节点地址)0x00000000004011e3 <+239>:	mov    (%rax),%eax			0x00000000004011e5 <+241>:	cmp    %eax,(%rbx)0x00000000004011e7 <+243>:	jge    0x4011ee <phase_6+250>	//链表按照从大到小排列0x00000000004011e9 <+245>:	callq  0x40143a <explode_bomb>0x00000000004011ee <+250>:	mov    0x8(%rbx),%rbx		//给下一个节点内容0x00000000004011f2 <+254>:	sub    $0x1,%ebp0x00000000004011f5 <+257>:	jne    0x4011df <phase_6+235>0x00000000004011f7 <+259>:	add    $0x50,%rsp0x00000000004011fb <+263>:	pop    %rbx0x00000000004011fc <+264>:	pop    %rbp0x00000000004011fd <+265>:	pop    %r120x00000000004011ff <+267>:	pop    %r130x0000000000401201 <+269>:	pop    %r140x0000000000401203 <+271>:	retq   

第一块是由两个循环嵌套组成的。外层循环依次遍历输入的第一个数字到第六个数字。外层循环首先判断输入的数字是否不超过6,紧接着内存循环遍历当前输入的数字到末尾剩下的数字,实现的功能是判断输入的六个数字两两不相等。
第二块比较简单,用一个寄存器%edx依次保存7与输入的数字的差值,再将差值覆盖原来输入的数字。
第三块是根据前两块实现后得到的新的6个数字进行操作。在内存0x6032d0到0x60332f中存放着6个大小为16字节的结构体变量。每个结构体变量的后8个字节都存放着一个指针,指向下一个结构体变量的内存位置。这部分代码是根据新的6个数字的数值来重新给这几个结构体排序,将这6个结构体变量的初始地址按照排好的顺序依次存放在栈指针偏移0x20~0x50的位置。
第四块是修改6个结构体变量中存放的指针值,按照重新排好的顺序让前一个指针指向后一个指针,所以这里基本可以确定第三第四块实现的是重新排列一个链表节点的功能。
第五块是验证新排列的六个结构体变量是否是按照从大到小的顺序来排列的,如果验证满足,则炸弹就拆除了。

分析后发现:node3.value>node4.value>node5.value>node6.value>node1.value>node2.value
所以第二块得到的新的六个数字为:3,4,5,6,1,2
所以一开始的输入的六个数字为:4 3 2 1 6 5

放一张图来纪念一下

总结

bomb lab实验总共花了将近两天的时间,做完实验感觉对课程的理解加深了。不过看汇编语言还是挺累的,后面看的有点烦(lol)。实验的设计很有意思,不愧为cmu,真希望国内一些大学的课程实验也改成类似的形式。


http://chatgpt.dhexx.cn/article/Wdk9Z4xg.shtml

相关文章

Attacklab实验

一、实验目的 1、深入了解缓冲区溢出的隐患&#xff0c;了解如何利用缓冲区溢出这个漏洞对现有程序进行控制流劫持、执行非法程序代码&#xff0c;从而造成对程序进行攻击以及破坏的过程&#xff1b; 2、增强对程序机器级表示、汇编语言、调试器和逆向工程等理解。 二、实验…

计算机系统基础实验-LinkLab实验

这是大三时的实验课&#xff0c;很久以前的东西&#xff0c;应要求放出来&#xff0c;做的不是很好。linux永远都是很重要的&#xff0c;希望我和大家都记得这个。实际上做到第五阶段我就不会了。 实验课程名称&#xff1a;计算机系统基础 实验项目名称&#xff1a;LinkLab实…

Lab5实验分享

文章目录 一.全局变量名词解释二.实验难点图示1.文件系统层次关系梳理2.多级目录与多级索引 三.代码梳理1.磁盘管理2.文件系统3.用户接口4.文件服务 一.全局变量名词解释 #define NBLOCK 1024 //一块磁盘里面的block数目 uint32_t nbitblock; // 用于存储bitmap的block数目 ui…

实验.......

第一步配置ip与环回地址 做hub&spoken的fullmesh结构以r2为例 其他路由同样操作 要求使用rip协议&#xff0c;以r2为例

实验吧——WEB-认真一点!

认真一点&#xff01; 打开链接&#xff0c;看到这个页面&#xff0c;第一反应又是sql注入 翻翻源码没有任何提示 打开burp开始抓包&#xff0c;包头与返回头又没有任何提示&#xff0c;试着开始修改ID 的值&#xff0c;观察页面变化。 提交1或用语句让框内为真&#xff0c;显…

实验....

实验要求 r1代表运营商 r1远程登录r2实际登陆r9 r3访问r7的换回地址走r5 全网可达 1.全网可达 配置ip 2配置rip协议&#xff0c;r8r9用v1其余用v2 3让r8r9接入内网&#xff0c;在r8对端接口写入v2 r3访问r7的换回地址走r5 r1代表运营商 r1远程登录r2实际登陆r9 先打开r…

实验吧 认真一点!

真的很久很久没碰过这个玩意了&#xff0c;真是不会了&#xff0c;而且这个题目本身就不是很容易&#xff0c;搞得我也是满肚子邪火&#xff0c;很气。这个题目主要难在它吧过滤和ban结合到了一起了。我们还是先看题目,首先拿到题目看到输入id0和输入id1结果不一样&#xff0c;…

让我进去-实验吧

这题做的真的是久&#xff0c;莓办法&#xff0c;太菜了 进去看见cookie有个source值很奇怪&#xff0c;把他改成1试一下就收到这段代码 $flag "XXXXXXXXXXXXXXXXXXXXXXX"; $secret "XXXXXXXXXXXXXXX"; // This secret is 15 characters long for secu…

实验吧

1.变异凯撒 把flag转化为ascii码&#xff0c;加密密文转化为ascii码&#xff0c; 因为是凯撒加密&#xff0c;所以思考移动的位数&#xff0c;分别是加了5&#xff0c;6&#xff0c;7&#xff0c;8.。。。 所以移动的位次是从5递加 得到结果 ascii码值转化&#xff0c;得到flag…

[大学物理实验-5]波尔共振实验

[大学物理实验-5]波尔共振实验 前言实验预习&#xff08;Preview&#xff09;实验报告&#xff08;Report&#xff09;实验报告&#xff08;Report&#xff09;总结&#xff08;Sum up&#xff09; 前言 最近在整理一些学过的课程的资料&#xff0c;有一些课虽然和我的专业毫无…

实验一-Bomblab(炸弹实验)

实验概述&#xff1a;该实验就是通过分析汇编代码&#xff0c;并使用GDB调试&#xff0c;找到每一题的通关钥匙 第一关&#xff1a;&#xff08;key“He is evil and fits easily into most overhead storage bins.”&#xff09; 简单解析&#xff1a;比…

实验.........

首先配置ip地址和环回地址 以r1为例 先用ospf联通r2r3r4r5r6r7 以r2为例 r1启bgp与r2建邻 r2r3r4r5r6r7同理建邻居 这里以r2为例 r7r8建邻方法同r1r2 优化在r2上

实验........

实验....... 先配置ip地址以r2为例 然后配置rip同样以r2为例 启用bgp建立对等体&#xff0c;需要书写as号 以r1与r3为例 建立ebgp 以r3r4为例 r3 r4 建立换回&#xff0c;以环回为对等提

shlab实验

计算机系统原理实验报告 一、实验目的及内容 1. 实验目的&#xff1a;通过编写完善一个支持作业控制的简单 Unix 外壳 Shell 程序&#xff08;tsh&#xff09;, 掌握 shell 的基本功能原理和实现方法;掌握 Unix 进程控制、特别是信号的应用&#xff0c; 包括信号产生和信号…

Lab2实验分享

文章目录 一.TLB访存机制二.二级页表三.Page存储结构四.自映射机制图示五.页面与地址的转化六.部分实验代码详解 一.TLB访存机制 当一个虚拟地址被送到MMU中进行翻译的时候&#xff0c;硬件首先在TLB中寻找包含这个地址的页面&#xff0c;如果它的虚页号在TLB中&#xff0c;并…

实验吧(1)

实验吧真是一个神奇的地方&#xff0c;里面的东西表示全部都看不懂&#xff0c;emmmmm &#xff0c;加油&#xff01;好好学习&#xff01; 案例&#xff1a;忘记密码了&#xff01; 在实验吧里面打开解题链接&#xff0c;然后就会跳转到一个输入密码的页面&#xff0c;当然你…

实验吧 认真一点

本实验是输入id获取结果&#xff0c;查看页面源码&#xff0c;没有什么注释信息。随便输入个1&#xff0c;结果为You are in ...............&#xff0c;其他的id显示为You are not in ............... 使用Burp suite进行模糊测试&#xff0c;结果为or部分sql命令都可以使用。…

实验吧_网站综合渗透_Discuz!

前言 这个一个实验吧中的环境&#xff0c;分值为50分&#xff0c;下面是题目说明。诶&#xff0c;发现自己很菜。 实验吧_网站综合渗透_dedecms解析地址 需要的学会或者已经掌握的知识 已知cms版本在线搜索通用漏洞掌握菜刀或者类似的工具的使用其他 题目说明 你是国内一流安…

实验吧-简单的登录题——WriteUp再研究

前言 这个题目的难点就是在于对于CBC加密方式尤其是解密这部分要琢磨一番&#xff0c;让我想起当年大学的时候信安三勇中的两勇的课&#xff0c;一门密码学&#xff0c;一门数学基础&#xff0c;可怕之极。这个题网上writeup一大堆&#xff0c;但是在一些方面解释的不是很详细…

实验吧-简单的登录题

前言 这是在实验吧上面的一道web题。主要考察cbc字节反转攻击。 分析 题目开始就是输入id去登录 首先想到的就是sql注入了,输入1后页面显示Hello,重新载入的话页面返回报错信息 确实存在注入,看那后面的逗号,猜测注入点在limit后面。然后试了很多,发现题目把union,#…