国外计算机课程lab,计算机系统实验之bomblab

今天刚刚验收CSAPP实验3,趁着余温,记录一下这个实验,顺便回顾下CSAPP课程的相关知识。

实验目的

1.使用gdb工具反汇编出汇编代码,结合c语言文件找到每个关卡的入口函数。然后分析汇编代码,分析得到每一关的通关密码。

2.熟悉linux指令,学会如何使用gdb对程序进行调试

实验环境

个人电脑PC,32位ubuntu系统环境

实验内容及操作步骤

实验内容

“二进制炸弹”是Linux可执行C程序,包含六个“阶段”和一个秘密“阶段”。 每个阶段都要求输入特定的字符串在stdin上。 如果输入了预期的字符串,则该阶段通过,进入下一阶段。 否则,炸弹会通过打印“ BOOM !!!”而“爆炸”。

操作步骤

拆弹前准备

(1).进入Ubuntu系统,将自己的程序包复制到虚拟机中,并使用tar xvf bomb201808010515.tar进行解压。

041a271c00460ec03368b30228a876ab.png

(2).输入objdump –d bomb >my_bomb.txt,将反汇编代码输入到my_bomb.txt的文件中,便于拆弹时的查看和注释。

(3).打开反汇编文件,发现phase_i系列的函数,结合实验题目,可以知道这是每一个关卡对应的函数。

开始拆炸弹

PS:每个关卡汇编代码下面有相应的文字分析。

phase_1:确定输入字符串

查看汇编代码

08048b50 :

8048b50:83 ec 1c sub $0x1c,%esp

8048b53:c7 44 24 04 84 a1 04 movl $0x804a184,0x4(%esp) //传入一个立即数

8048b5a:08

8048b5b:8b 44 24 20 mov 0x20(%esp),%eax

8048b5f:89 04 24 mov %eax,(%esp)

8048b62:e8 3d 04 00 00 call 8048fa4 //输入字符串进入函数进行比较

8048b67:85 c0 test %eax,%eax

8048b69:74 05 je 8048b70 //返回值为0,结束,否则爆炸

8048b6b:e8 46 05 00 00 call 80490b6

8048b70:83 c4 1c add $0x1c,%esp

8048b73:c3 ret

分析:

(1).可以看到在%eax!=0的时候就会调用,所以在调用 函数之后的返回值%eax必须为0。往前看,发现代码movl $0x804a184,0x4(%esp)有立即数,是将此处地址的值拿来用,输入gdb –q bomb进入调试状态,用x/s 0x804a184查看内容。

b0d12efa7f98bd26a35dc139d9cf4455.png

(2).下面一步 mov 0x20(%esp),%eax就是把我们输入的参数放进%eax中,然后放进(%esp) ,再调用函数。所以猜测要输入的就是0x804a184中的字符。开始第一关的尝试,重新打开一个终端,用来测试。测试结果如下,输出"Phase 1 defused. How about the next one?",成功通关。

367764e0411da828b419b9c0c2bbbc4f.png

phase_2: 确定一个有规律的数列

分析:

08048b74 :

8048b74:56 push %esi

8048b75:53 push %ebx

8048b76:83 ec 34 sub $0x34,%esp //开辟栈帧

8048b79:8d 44 24 18 lea 0x18(%esp),%eax

8048b7d:89 44 24 04 mov %eax,0x4(%esp)

8048b81:8b 44 24 40 mov 0x40(%esp),%eax

8048b85:89 04 24 mov %eax,(%esp)

8048b88:e8 5e 06 00 00 call 80491eb

8048b8d:83 7c 24 18 01 cmpl $0x1,0x18(%esp) //esp+0x18-1

8048b92:74 05 je 8048b99 //esp+0x18==1

8048b94:e8 1d 05 00 00 call 80490b6 //esp+0x18!=1,爆炸

8048b99:8d 5c 24 1c lea 0x1c(%esp),%ebx //ebx=esp+0x1c

8048b9d:8d 74 24 30 lea 0x30(%esp),%esi //esi=esp+0x30

(1). 首先注意到这个函数,这里应该是读取6个数字。接下来看到esp+0x18中存放的值和1进行比较,不相等则爆炸,由此猜测第1个数为1。连续的两条lea语句是将esp+0x1c,esp+0x30的值存放入对应寄存器。从esp+0x18到esp+0x30刚好存放6个int 类型的数据,故esp+0x30是最后一个数据,esp+0x1c是第2个数据。

8048ba1:8b 43 fc mov -0x4(%ebx),%eax //eax=esp+0x1c-0x4=esp+0x18

8048ba4:01 c0 add %eax,%eax //eax=eax*2=2*(esp+0x18)

8048ba6:39 03 cmp %eax,(%ebx) //(ebx)-eax

8048ba8:74 05 je 8048baf //(ebx)==2

8048baa:e8 07 05 00 00 call 80490b6 //(ebx)!=2,爆炸

8048baf:83 c3 04 add $0x4,%ebx //ebx=ebx+4=esp+0x20,地址+4,下一个数字

8048bb2:39 f3 cmp %esi,%ebx //ebx-esi=esp+0x20-(esp+0x30)

8048bb4:75 eb jne 8048ba1 //不相等,继续循环

8048bb6:83 c4 34 add $0x34,%esp //结束

8048bb9:5b pop %ebx

8048bba:5e pop %esi

8048bbb:c3 ret

(2). 接下来将ebx-0x4的中存放的值传给eax,执行add %eax,%eax操作,即eax中存放的数乘以2,再将这个值与第2个数据进行比较,如果不相等就爆炸,说明第2个数就是在第1个数的基础上乘以2。继续往下看,执行add $0x4,%ebx,将ebx中的地址加0x4,即下一个数字,接着的判断语句是和最后一个数字比较,不相等就跳转到对应位置继续循环。由此可知,这6个数字对应的递推关系为a[i+1]=2*a[i],其中a[0]=1,i=0,1,…,4。

(3). 切换到之前测试用的终端,输入1 2 4 8 16 32,终端显示“That’s number 2. Keep going!”,第二关顺利通过。结果如下图所示:

ef65c03a89d383a58e093924d83cdc6f.png

phase_3 :switch语句

分析

08048bbc :

8048bbc:83 ec 2c sub $0x2c,%esp //开辟栈帧

8048bbf:8d 44 24 1c lea 0x1c(%esp),%eax

8048bc3:89 44 24 0c mov %eax,0xc(%esp) //参数y esp+0x1c

8048bc7:8d 44 24 18 lea 0x18(%esp),%eax

8048bcb:89 44 24 08 mov %eax,0x8(%esp) //参数x esp+0x18

8048bcf:c7 44 24 04 a3 a3 04 movl $0x804a3a3,0x4(%esp) //查看汇编后知道需要输入两个数(%d %d)

8048bd6:08

8048bd7:8b 44 24 30 mov 0x30(%esp),%eax

8048bdb:89 04 24 mov %eax,(%esp)

8048bde:e8 8d fc ff ff call 8048870 <__isoc99_sscanf>

(1).看到上面这段汇编代码,注意到函数_isoc99_sscanf@plt,说明这部分存在输入信息,往前看,发现movl $804a3a3,0x4(%esp)。进入GDB调试,设置断点 b phase_3,尝试使用x/s查看内容,发现是“%d %d”(如下图),说明需要输入两个数据。结合前面传入两个参数,这两个参数就是输入的数据,设为x,y。

5edc652baa19f12c41926e5eafc67490.png

8048be3:83 f8 01 cmp $0x1,%eax //eax-1

8048be6:7f 05 jg 8048bed //eax>1

8048be8:e8 c9 04 00 00 call 80490b6 //eax<1,爆炸

8048bed:83 7c 24 18 07 cmpl $0x7,0x18(%esp) //x-7

8048bf2:77 3c ja 8048c30 //x>7或者x<0,引爆炸弹

8048bf4:8b 44 24 18 mov 0x18(%esp),%eax //eax=x

8048bf8:ff 24 85 e0 a1 04 08 jmp *0x804a1e0(,%eax,4) //switch语句,gdb查看*0x804a1e0

(2). 接下来看到cmpl $0x7,%eax,进行无符号数比较,如果大于7或者小于0就爆炸,故可以知道eax中存放的值(参数1)的取值范围为0-7的整数。往下发现很多跳转指令,结合jmp *0x804a1e0(,%eax,4)知道,这是一个跳转表结构。

8048bf8:ff 24 85 e0 a1 04 08 jmp *0x804a1e0(,%eax,4) //switch语句,gdb查看*0x804a1e0

8048bff:b8 cc 01 00 00 mov $0x1cc,%eax //eax=0x1cc,x=0 对应查看*0x804a1e0

8048c04:eb 3b jmp 8048c41

8048c06:b8 b6 03 00 00 mov $0x3b6,%eax //eax=0x3b6,x=2 对应查看*0x804a1e8

8048c0b:eb 34 jmp 8048c41

8048c0d:b8 03 03 00 00 mov $0x303,%eax //eax=0x303,x=3

8048c12:eb 2d jmp 8048c41

8048c14:b8 1d 01 00 00 mov $0x11d,%eax //eax=0x11d,x=4

8048c19:eb 26 jmp 8048c41

8048c1b:b8 a0 01 00 00 mov $0x1a0,%eax //eax=0x1a0,x=5

8048c20:eb 1f jmp 8048c41

8048c22:b8 60 03 00 00 mov $0x360,%eax //eax=0x360,x=6

8048c27:eb 18 jmp 8048c41

8048c29:b8 6f 02 00 00 mov $0x26f,%eax //eax=0x26f,x=7

8048c2e:eb 11 jmp 8048c41

8048c30:e8 81 04 00 00 call 80490b6 //爆炸

8048c35:b8 00 00 00 00 mov $0x0,%eax //eax=0

8048c3a:eb 05 jmp 8048c41

8048c3c:b8 7a 00 00 00 mov $0x7a,%eax //eax=0x7a,x=1

(3).分析这个跳转表

假设x为0,则得到地址0x804a1e0+4*0=0x804a1e0,使用x/8bx 0x804a1e0查看信息,为0x8048bff,即case 0的入口地址。

cbcee308f80fc14c316884db542d536c.png

根据case 0的入口地址0x8048bff找到执行执行mov $0x1cc,%eax。

假设x为1,则得到地址0x804a1e0+4*1=0x804a1e4,使用x/8bx 0x804a1e4查看信息,为0x804c3c,即case 1的入口地址。

1be4c555e3a16ea644b02f70f9712942.png

根据case 1的入口地址0x8048c3c找到执行执行mov $0x7a,%eax。同理可以确定其他几组数据。

8048c41:3b 44 24 1c cmp 0x1c(%esp),%eax //eax-y

8048c45:74 05 je 8048c4c //eax==y

8048c47:e8 6a 04 00 00 call 80490b6 //eax!=y,爆炸

8048c4c:83 c4 2c add $0x2c,%esp //结束

8048c4f:c3 ret

(4).最后一部分将eax的值和输入的参数y进行比较,不相等则爆炸,说明输入的第2个数字y为case语句中传给eax的值。由此可以确定这个关卡有8组不同的通关答案,分别是0 460,1 122,2 950, 3 771, 4 285, 5 416,6 864, 7 623。下面对2组数据进行验证:

98ed135d05532590498c138feb468a21.png

41aceb94436c9f8f48efd8c4391a73d1.png

phase_4 :函数过程调用

分析

08048cb9 :

8048cb9:83 ec 2c sub $0x2c,%esp //开辟栈帧

8048cbc:8d 44 24 1c lea 0x1c(%esp),%eax

8048cc0:89 44 24 0c mov %eax,0xc(%esp) //参数y

8048cc4:8d 44 24 18 lea 0x18(%esp),%eax

8048cc8:89 44 24 08 mov %eax,0x8(%esp) //参数x

8048ccc:c7 44 24 04 a3 a3 04 movl $0x804a3a3,0x4(%esp) 查看汇编后知道需要输入两个数(%d %d)

8048cd3:08

(1).查看上面这一段代码,分析得出输入两个数字(和关卡3一样,用gdb调试看0x804a3a3中存放的内容)。结合前面传入两个参数,这两个参数就是输入的数据,设为x,y。

8048cd4:8b 44 24 30 mov 0x30(%esp),%eax

8048cd8:89 04 24 mov %eax,(%esp)

8048cdb:e8 90 fb ff ff call 8048870 <__isoc99_sscanf> //调用某个函数

8048ce0:83 f8 02 cmp $0x2,%eax //eax-2 判断传入参数的个数是否满足条件

8048ce3:75 0d jne 8048cf2 //eax!=2 引爆

8048ce5:8b 44 24 18 mov 0x18(%esp),%eax //eax=x

8048ce9:85 c0 test %eax,%eax //判断x

8048ceb:78 05 js 8048cf2 //x<0 引爆

8048ced:83 f8 0e cmp $0xe,%eax //x-0xe

8048cf0:7e 05 jle 8048cf7 //x<=0xe

8048cf2:e8 bf 03 00 00 call 80490b6

(2).分析这一部分代码,test判断x是否小于0,cmp判断x是否大于0xe,故要是炸弹不爆炸,则参数x的取值范围为0-0xe的整数。

8048cf7:c7 44 24 08 0e 00 00 movl $0xe,0x8(%esp) //esp+0x8=0xe

8048cfe:00

8048cff:c7 44 24 04 00 00 00 movl $0x0,0x4(%esp) //esp+0x4=0

8048d06:00

8048d07:8b 44 24 18 mov 0x18(%esp),%eax //eax=x

8048d0b:89 04 24 mov %eax,(%esp) //esp=x

8048d0e:e8 3d ff ff ff call 8048c50 //调用func4

(3). 在调用func4之前将参数压入栈帧.

8048d13:83 f8 07 cmp $0x7,%eax //eax-7,eax里面存放的是func4的返回值

8048d16:75 07 jne 8048d1f //eax!=7,引爆

8048d18:83 7c 24 1c 07 cmpl $0x7,0x1c(%esp) //y-7

8048d1d:74 05 je 8048d24 //y==7,结束

8048d1f:e8 92 03 00 00 call 80490b6

8048d24:83 c4 2c add $0x2c,%esp

8048d27:c3 ret

(4). 继续分析发现下面对func4的返回值和参数y分别对7进行比较,如果不为7,则引爆炸弹,分析到此,可以知道对于x属于[0,0xe]使得func4(x,0,0xe)返回值为0x7的x即为所求。

08048c50 :

8048c50:83 ec 1c sub $0x1c,%esp //开辟栈帧

8048c53:89 5c 24 14 mov %ebx,0x14(%esp)

8048c57:89 74 24 18 mov %esi,0x18(%esp)

8048c5b:8b 44 24 20 mov 0x20(%esp),%eax //设esp+0x20存储x=>eax=x

8048c5f:8b 54 24 24 mov 0x24(%esp),%edx //设esp+0x24存储y=>edx=y

8048c63:8b 74 24 28 mov 0x28(%esp),%esi //设esp+0x28存储z=>esi=z

8048c67:89 f1 mov %esi,%ecx //ecx=z

8048c69:29 d1 sub %edx,%ecx //ecx=z-y

8048c6b:89 cb mov %ecx,%ebx //ebx=z-y

8048c6d:c1 eb 1f shr $0x1f,%ebx //逻辑右移,ebx=(z-y)>>31,得到符号位s

8048c70:01 d9 add %ebx,%ecx //ecx=ebx+ecx=s+z-y

8048c72:d1 f9 sar %ecx //算术右移,ecx=(s+z-y)>>1

8048c74:8d 1c 11 lea (%ecx,%edx,1),%ebx //ebx=edx*1+ecx=y+ecx=y+(s+z-y)>>1

8048c77:39 c3 cmp %eax,%ebx //ebx-x,比较计算结果y+(s+z-y)>>1和x

8048c79:7e 17 jle 8048c92 //ebx<=eax

8048c7b:8d 4b ff lea -0x1(%ebx),%ecx //ecx=ebx-1

8048c7e:89 4c 24 08 mov %ecx,0x8(%esp) //esp+0x8=ecx=ebx-1

8048c82:89 54 24 04 mov %edx,0x4(%esp) //esp+0x4=edx=y

8048c86:89 04 24 mov %eax,(%esp) //esp=x

8048c89:e8 c2 ff ff ff call 8048c50 //递归调用函数func4

8048c8e:01 c3 add %eax,%ebx //ebx=ebx+eax

8048c90:eb 19 jmp 8048cab

8048c92:39 c3 cmp %eax,%ebx //ebx-eax

8048c94:7d 15 jge 8048cab //ebx>=eax

8048c96:89 74 24 08 mov %esi,0x8(%esp) //esp+0x8=esi=z

8048c9a:8d 53 01 lea 0x1(%ebx),%edx //edx=ebx+1

8048c9d:89 54 24 04 mov %edx,0x4(%esp) //esp+0x4=ebx+1

8048ca1:89 04 24 mov %eax,(%esp) //esp=eax=x

8048ca4:e8 a7 ff ff ff call 8048c50 //递归调用函数func4

8048ca9:01 c3 add %eax,%ebx //ebx=eax+ebx

8048cab:89 d8 mov %ebx,%eax //eax=ebx

8048cad:8b 5c 24 14 mov 0x14(%esp),%ebx

8048cb1:8b 74 24 18 mov 0x18(%esp),%esi

8048cb5:83 c4 1c add $0x1c,%esp

8048cb8:c3 ret

(5).现在重点就是分析func4函数,看到汇编代码可以发现这是一个递归函数,关键在于找到递归的条件和退出函数的条件,以及递归之前函数的参数压栈准备。根据这段汇编代码写出C语言代码如下:

int func4(int x,int y,int z)

{

int t=(((z-y)+((z-y)>>31))>>1)+y;

if(t<=x)

{

if(t==x)

{

return t;

}

else return func4(x,t+1,z)+x;

}

else

{

return func4(x,y,t-1)+x;

}

}

(6).编写测试函数如下

int main()

{

int i=0;

for(i=0;i<=14;i++)//每一种取值都进行测试

{

printf("func4(%d,0,14)=%d ",i,func4(i,0,14));

if((i+1)%5==0) printf("\n");

}

return 0;

}

运行代码结果如下,可以发现返回值为0x7对应的x只有0x7一种值,故第4关通关输入为7 7。

990072141b853e5a067e05ab83a57d45.png

切换到之前测试用的终端,输入7 7,终端显示“So you got that one. Try this one.”,第四关顺利通过。

367b3467a4258c34a754fb51e3f4cb51.png

phase_5: 序列的构造和数组寻址

分析

08048d28 :

8048d28:53 push %ebx

8048d29:83 ec 18 sub $0x18,%esp //开辟栈帧

8048d2c:8b 5c 24 20 mov 0x20(%esp),%ebx //参数1 x存储在ebx

8048d30:89 1c 24 mov %ebx,(%esp)

8048d33:e8 53 02 00 00 call 8048f8b

8048d38:83 f8 06 cmp $0x6,%eax //eax-6(猜测eax是字符串的长度)

8048d3b:74 05 je 8048d42 //eax==6

8048d3d:e8 74 03 00 00 call 80490b6 //eax不是6,引爆

(1).注意到函数,结合后面eax和0x6比较,可知这一关输入的参数是一个长度为6的字符串,需要我们去匹配出这个字符串。

8048d42:ba 00 00 00 00 mov $0x0,%edx //edx=0

8048d47:b8 00 00 00 00 mov $0x0,%eax //eax=0

//movsbl指令负责拷贝一个字节,并用源操作数的最高位填充其目的操作数中的其余各位,这种扩展方式叫“符号扩展”

8048d4c:0f be 0c 03 movsbl (%ebx,%eax,1),%ecx //ecx=(eax+ebx) 长度为6的字符串的第eax个字符

8048d50:83 e1 0f and $0xf,%ecx //f=00001111 取ecx的低4位

8048d53:03 14 8d 00 a2 04 08 add 0x804a200(,%ecx,4),%edx //edx=edx+4*ecx+0x804a200

8048d5a:83 c0 01 add $0x1,%eax //eax+1

8048d5d:83 f8 06 cmp $0x6,%eax //eax-6

8048d60:75 ea jne 8048d4c //eax!=6

8048d62:83 fa 2b cmp $0x2b,%edx //edx-0x2b

8048d65:74 05 je 8048d6c //相等

8048d67:e8 4a 03 00 00 call 80490b6 //爆炸

8048d6c:83 c4 18 add $0x18,%esp //结束

8048d6f:5b pop %ebx

8048d70:c3 ret

(2).接着edx,eax寄存器的值初始化为0,movsbl (%ebx,%eax,1),%ecx,这一句是表示将长度为6的字符串的第eax个字符存入ecx中。取ecx中的低4位,利用基址比例变址寻址的方式,到数组中找值,并累和在寄存器edx中。往下看,可以看到cmp $0x2b,%edx,如果不相等则爆炸,说明累和得到的值必须为0x2b。

(3).分析到此,关键在于找到数组中的值,由汇编我们知道数组首地址为0x804a200,进入GDB调试,设置断点b phase_5,运行后查看其内容,从里边找出一组和为0x2b的数字,这里取出一组 {2,a,6,1,c,c}。

5a7eb2896b4a8e9806115b48bd01c136.png

对应下标为0,1,2,3,4,4,去ascii表中找低四位为0,1,2,3,4,4的字符,故得到其中一组解为pabcdd。

ce16723248d49f097cca0cf1f28ea62c.png

(4). 切换到之前测试用的终端,输入pabcdd,终端显示“Good work! On to the next…!”,第五关顺利通过。

706b4ee477d787c78f57daf8acfb2091.png

phase_6: 链表

分析

08048d71 :

8048d71:56 push %esi

8048d72:53 push %ebx

8048d73:83 ec 44 sub $0x44,%esp //开辟栈帧

8048d76:8d 44 24 10 lea 0x10(%esp),%eax

8048d7a:89 44 24 04 mov %eax,0x4(%esp) //参数2 y

8048d7e:8b 44 24 50 mov 0x50(%esp),%eax

8048d82:89 04 24 mov %eax,(%esp) //参数1 x

8048d85:e8 61 04 00 00 call 80491eb

(1).首先看到函数,根据之前的经验,猜测这里也是读取6个数字。

8048d8a:be 00 00 00 00 mov $0x0,%esi //esi=0

8048d8f:8b 44 b4 10 mov 0x10(%esp,%esi,4),%eax //eax=(4*esi+y)

8048d93:83 e8 01 sub $0x1,%eax //eax--

8048d96:83 f8 05 cmp $0x5,%eax //eax-5

8048d99:76 05 jbe 8048da0 //无符号数<=

8048d9b:e8 16 03 00 00 call 80490b6 //引爆

8048da0:83 c6 01 add $0x1,%esi //esi+=1,,esi=1

8048da3:83 fe 06 cmp $0x6,%esi //esi-6

8048da6:74 33 je 8048ddb //等于跳转

//以上代码是确定6个数字的范围。

8048da8:89 f3 mov %esi,%ebx //ebx=esi

8048daa:8b 44 9c 10 mov 0x10(%esp,%ebx,4),%eax //eax=(4*ebx+y)

8048dae:39 44 b4 0c cmp %eax,0xc(%esp,%esi,4) //(4*esi+esp+0xc)-eax

8048db2:75 05 jne 8048db9 //不相等

8048db4:e8 fd 02 00 00 call 80490b6 //相等,引爆

8048db9:83 c3 01 add $0x1,%ebx //ebx++

8048dbc:83 fb 05 cmp $0x5,%ebx //ebx-5

8048dbf:7e e9 jle 8048daa //小于等于跳转

8048dc1:eb cc jmp 8048d8f

8048dc3:8b 52 08 mov 0x8(%edx),%edx //edx=(edx+0x8)=(0x804c13c+0x8)

8048dc6:83 c0 01 add $0x1,%eax //eax++

8048dc9:39 c8 cmp %ecx,%eax //eax-ecx

8048dcb:75 f6 jne 8048dc3 //不相等,继续执行

8048dcd:89 54 b4 28 mov %edx,0x28(%esp,%esi,4) //4*esi+esp+0x28=edx

8048dd1:83 c3 01 add $0x1,%ebx //ebx++

8048dd4:83 fb 06 cmp $0x6,%ebx //ebx-6

8048dd7:75 07 jne 8048de0 //不相等

8048dd9:eb 1c jmp 8048df7

//以上代码是判断数字之间是否互不相等。

(2).这里的汇编代码包括两个循环,用来给出数字的取值范围是属于1-6,并且数字之间彼此两两不相等。

//链表初始化

8048df7:8b 5c 24 28 mov 0x28(%esp),%ebx /ebx=x1

8048dfb:8b 44 24 2c mov 0x2c(%esp),%eax //eax=x2

8048dff:89 43 08 mov %eax,0x8(%ebx) //x1->x2

8048e02:8b 54 24 30 mov 0x30(%esp),%edx //edx=x3

8048e06:89 50 08 mov %edx,0x8(%eax) //x1->x2->x3

8048e09:8b 44 24 34 mov 0x34(%esp),%eax //eax=x4

8048e0d:89 42 08 mov %eax,0x8(%edx) //x1->x2->x3->x4

8048e10:8b 54 24 38 mov 0x38(%esp),%edx //edx=x5

8048e14:89 50 08 mov %edx,0x8(%eax) //x1->x2->x3->x4->x5

8048e17:8b 44 24 3c mov 0x3c(%esp),%eax //eax=x6

8048e1b:89 42 08 mov %eax,0x8(%edx) //x1->x2->x3->x4->x5->x6

8048e1e:c7 40 08 00 00 00 00 movl $0x0,0x8(%eax) //链表尾

(3). 接下来对链表的结点进行了连接

(4).进入GDB调试,设置断点b phase_6,往下分析代码过程中看到一个立即数,查看其值,出现一个node1,猜测这是一个链表的数据结构。于是多查看几个值,发现了其中的规律,每三个数为一组,第1个是数据域中的值,第2个是下标,第3个比较大,类似于地址,是指针域,然后对猜想进行验证。调试后验证猜测正确。根据之前对链表进行连接,顺序为322->370->1c1->24b->0a1->192。

91410666e42dff86c3b286e3bbbd89ba.png

//递增序列

8048e25:be 05 00 00 00 mov $0x5,%esi //esi=5

8048e2a:8b 43 08 mov 0x8(%ebx),%eax //eax=x2的地址

8048e2d:8b 10 mov (%eax),%edx //edx=x2

8048e2f:39 13 cmp %edx,(%ebx) //x1-x2

8048e31:7e 05 jle 8048e38 //x1<=x2

8048e33:e8 7e 02 00 00 call 80490b6

8048e38:8b 5b 08 mov 0x8(%ebx),%ebx //ebx=ebx+0x8,下一个数

8048e3b:83 ee 01 sub $0x1,%esi //esi--

8048e3e:75 ea jne 8048e2a

8048e40:83 c4 44 add $0x44,%esp

8048e43:5b pop %ebx

8048e44:5e pop %esi

8048e45:c3 ret

(5).ebx+0x8存储的是第2个数字的地址,取出第2个数字x2,和edx存储的第1个数字x1进行比较,需要满足x1<=x2,接下来ebx地址加0x8,对下一个进行约束。由此可以看出这个序列是一个递增序列。

(6).结合之前输入的6个数字在1-6之间,且互不相同,这里应该是排序后递增链表序列的下标。故得到下标为5 6 3 4 1 2。切换到之前测试用的终端,输入5 6 3 4 1 2,终端显示“Congratulations! You’ve defused the bomb!”,第六关顺利通过。

6e78023f68004135033f4178b4f6b989.png

secret_phase:二叉树

做完前6关,看到提示信息的那一刻,我以为就这样结束,但是听说还有秘密关卡,仔细一看汇编代码,果然有serect_phase的汇编代码片段。起初没有思路,不知道该从何入手,在网上参考了一些资料,结合bomb.c的代码,发现每个阶段函数结束后都调用了phase_defused()函数,猜测秘密关卡可能隐藏在某一关中。

分析如何找到秘密关卡

查看汇编代码如下

0804923b :

804923b:81 ec 8c 00 00 00 sub $0x8c,%esp

8049241:65 a1 14 00 00 00 mov %gs:0x14,%eax

8049247:89 44 24 7c mov %eax,0x7c(%esp)

804924b:31 c0 xor %eax,%eax

804924d:83 3d cc c3 04 08 06 cmpl $0x6,0x804c3cc

8049254:75 72 jne 80492c8

8049256:8d 44 24 2c lea 0x2c(%esp),%eax

804925a:89 44 24 10 mov %eax,0x10(%esp)

804925e:8d 44 24 28 lea 0x28(%esp),%eax

8049262:89 44 24 0c mov %eax,0xc(%esp)

8049266:8d 44 24 24 lea 0x24(%esp),%eax

804926a:89 44 24 08 mov %eax,0x8(%esp)

804926e:c7 44 24 04 a9 a3 04 movl $0x804a3a9,0x4(%esp)

8049275:08

8049276:c7 04 24 d0 c4 04 08 movl $0x804c4d0,(%esp)

804927d:e8 ee f5 ff ff call 8048870 <__isoc99_sscanf>

8049282:83 f8 03 cmp $0x3,%eax

8049285:75 35 jne 80492bc

8049287:c7 44 24 04 b2 a3 04 movl $0x804a3b2,0x4(%esp)

804928e:08

804928f:8d 44 24 2c lea 0x2c(%esp),%eax

8049293:89 04 24 mov %eax,(%esp)

8049296:e8 09 fd ff ff call 8048fa4

804929b:85 c0 test %eax,%eax

804929d:75 1d jne 80492bc

804929f:c7 04 24 78 a2 04 08 movl $0x804a278,(%esp)

80492a6:e8 55 f5 ff ff call 8048800

80492ab:c7 04 24 a0 a2 04 08 movl $0x804a2a0,(%esp)

80492b2:e8 49 f5 ff ff call 8048800

80492b7:e8 db fb ff ff call 8048e97

80492bc:c7 04 24 d8 a2 04 08 movl $0x804a2d8,(%esp)

80492c3:e8 38 f5 ff ff call 8048800

80492c8:8b 44 24 7c mov 0x7c(%esp),%eax

80492cc:65 33 05 14 00 00 00 xor %gs:0x14,%eax

80492d3:74 05 je 80492da

80492d5:e8 f6 f4 ff ff call 80487d0 <__stack_chk_fail>

80492da:81 c4 8c 00 00 00 add $0x8c,%esp

80492e0:c3 ret

80492e1:90 nop

80492e2:90 nop

80492e3:90 nop

80492e4:90 nop

80492e5:90 nop

80492e6:90 nop

80492e7:90 nop

80492e8:90 nop

80492e9:90 nop

80492ea:90 nop

80492eb:90 nop

80492ec:90 nop

80492ed:90 nop

80492ee:90 nop

80492ef:90 nop

(1).结合前面关卡,发现这里有2个立即数,使用gdb调试查看其内容

c25bbfb4b2aa1b725dd3bc3382a317c3.png

发现提示输入两个数字和一个字符串,以及第4关的输入。于是猜测秘密关卡藏在第4关,而这个字符串就是打开秘密关卡的“钥匙”。

4507fbc72fec3a80dde1e1f87109c497.png

(2). 可以看到这里将0x804a3b2中存放的内容传入函数strings_not_equal中进行比较,如果返回值不为0,就跳转到0x80492bc,输出0x804a2d8中的内容,否则就一次往下输出0x804a278,0x804a2a0的信息,最后输出0x804a2d8的信息。猜测:0x804a3b2就是进入秘密关卡的“钥匙”,当“钥匙”输入错误就直接结束。

53e3c078e5e047c25af80de7d6b16533.png

daefde5a6a98fc651e7192134c94b6d5.png

添加断点,用gdb调试查看上述提到的4个地址中的内容,验证了猜测的正确性。

b17372310506645919ead927fce9f510.png

分析如何通关

08048e97 :

8048e97:53 push %ebx

8048e98:83 ec 18 sub $0x18,%esp //开辟栈帧

8048e9b:e8 3d 02 00 00 call 80490dd

8048ea0:c7 44 24 08 0a 00 00 movl $0xa,0x8(%esp) //参数3 z=a

8048ea7:00

8048ea8:c7 44 24 04 00 00 00 movl $0x0,0x4(%esp) //参数2 y=0

8048eaf:00

8048eb0:89 04 24 mov %eax,(%esp) //参数1 x

8048eb3:e8 28 fa ff ff call 80488e0

8048eb8:89 c3 mov %eax,%ebx //ebx=x

8048eba:8d 40 ff lea -0x1(%eax),%eax //eax=x-1

8048ebd:3d e8 03 00 00 cmp $0x3e8,%eax //eax-0x3e8

8048ec2:76 05 jbe 8048ec9 无符号数比较,小于等于

8048ec4:e8 ed 01 00 00 call 80490b6

(1).这段代码首先将三个参数放入栈帧中,接下来调用了strtol函数。mov %eax,%ebx;lea -0x1(%eax),%eax;cmp $0x3e8,%eax;jbe 8048ec9 ;这4句对参数x的范围进行了限定,x的取值是1-0x3e9。

8048ec9:89 5c 24 04 mov %ebx,0x4(%esp) //fun7参数2 n=x

8048ecd:c7 04 24 88 c0 04 08 movl $0x804c088,(%esp) //fun7参数1 m=0x804c088

8048ed4:e8 6d ff ff ff call 8048e46 //调用fun7

8048ed9:85 c0 test %eax,%eax //eax and eax

8048edb:74 05 je 8048ee2 //等于0,不是0就爆炸

8048edd:e8 d4 01 00 00 call 80490b6

8048ee2:c7 04 24 b8 a1 04 08 movl $0x804a1b8,(%esp)

8048ee9:e8 12 f9 ff ff call 8048800

8048eee:e8 48 03 00 00 call 804923b

8048ef3:83 c4 18 add $0x18,%esp

8048ef6:5b pop %ebx

8048ef7:c3 ret

8048ef8:90 nop

8048ef9:90 nop

8048efa:90 nop

8048efb:90 nop

8048efc:90 nop

8048efd:90 nop

8048efe:90 nop

8048eff:90 nop

(2).这段代码中主要调用了函数fun7,调用之前可以分析出参数,即fun7(地址,n),第1个参数为某个数组或者链表的首地址,第2个参数为1-1001之间的整数。通关的条件是fun7()的返回值为0。分析fun7中代码,并结合返回值为0,故可以知道保证传入fun7中两个参数相等即可。

08048e46 :

8048e46:53 push %ebx

8048e47:83 ec 18 sub $0x18,%esp //开辟栈帧

8048e4a:8b 54 24 20 mov 0x20(%esp),%edx //参数1,edx=&x

8048e4e:8b 4c 24 24 mov 0x24(%esp),%ecx //参数2,ecx=y

8048e52:85 d2 test %edx,%edx //判断edx是否为0

8048e54:74 37 je 8048e8d //等于0

8048e56:8b 1a mov (%edx),%ebx //取出地址中的值,给ebx,即ebx=x

8048e58:39 cb cmp %ecx,%ebx //x-y

8048e5a:7e 13 jle 8048e6f //x<=y

8048e5c:89 4c 24 04 mov %ecx,0x4(%esp) //esp+0x4=y

8048e60:8b 42 04 mov 0x4(%edx),%eax

8048e63:89 04 24 mov %eax,(%esp) //esp=edx+0x4

8048e66:e8 db ff ff ff call 8048e46 //调用函数fun7

8048e6b:01 c0 add %eax,%eax //eax=eax*2

8048e6d:eb 23 jmp 8048e92

8048e6f:b8 00 00 00 00 mov $0x0,%eax //eax=0

8048e74:39 cb cmp %ecx,%ebx //x-y

8048e76:74 1a je 8048e92 //如果x==y

8048e78:89 4c 24 04 mov %ecx,0x4(%esp) //esp+0x4=y

8048e7c:8b 42 08 mov 0x8(%edx),%eax

8048e7f:89 04 24 mov %eax,(%esp) //esp=edx+0x8

8048e82:e8 bf ff ff ff call 8048e46 //调用函数fun7

8048e87:8d 44 00 01 lea 0x1(%eax,%eax,1),%eax eax=2*eax+1

8048e8b:eb 05 jmp 8048e92

8048e8d:b8 ff ff ff ff mov $0xffffffff,%eax

8048e92:83 c4 18 add $0x18,%esp

8048e95:5b pop %ebx

8048e96:c3 ret

(3).根据fun7的汇编代码写出对应的C语言代码

int fun7(int *x,int y)

{

if(x==0)

return 0xffffffff;

if(*x<=y)

{

if(*x==y) return 0;

x+=8;

return 2*fun7(x,y)+1;

}

else

{

x+=4;

return 2*fun7(x,y);

}

}

(4).根据前面的分析,要使炸弹不爆炸需要fun7返回值为0,从函数中可以看出,返回值为0,只需*x=y即可,于是查看传入fun7中x中的内容即可。

8048ecd:c7 04 24 88 c0 04 08 movl $0x804c088,(%esp) //fun7参数1 m=0x804c088

8048ed4:e8 6d ff ff ff call 8048e46 //调用fun7

870d451630f4ae0c1f8f996440dfb54f.png

(5).切换到之前测试用的终端,运行./bomb,从第一关开始输入,在第四关时,加上字符串“DrEvil”,最终输入秘密关卡的36,终端显示“Wow! You’ve defused the secret stage!Congratulations! You’ve defused the bomb!”。

5ead728d480f82e8e413be43335dd5ad.png

(6).秘密关卡到这里就结束了,而对于这个关卡涉及到的结构可以进行进一步探索。查看0x804c088中内容发现有3个值,第1个是数值,第2,3个值比较大,是两个地址,联系所学,这两个地址就是左,右子结点的地址。将整个结构用图表示如下,可以看出这是一个典型的二叉树结构。

ba383e08763b9a894ed277e16a37b55b.png

实验结果及分析

8d90547ed31e72d533760c03671c90a2.png

所有关卡通关密码均已找出,执行结果如上。

对每一关进行简单分析:第1关是确定输入字符串;第2关是确定一个有规律的数列;第3关是switch语句的考察;第4关是函数过程调用的检测;第5关是序列的构造和数组寻址方式;第6关是考察链表的相关知识;秘密关卡的寻找考察了考查Linux Canary绕过技术及ROP(Return-Oriented-Programming)攻击负载的构造(如何找到秘密和关卡的入口),秘密关卡则考察了二叉树。

收获与体会

1.本次实验进一步熟悉了GDB调试工具,调试能力得到了一定的提高。

2.熟悉了linux中一些之前没有见过的指令,如movsbl指令movsbl指令负责拷贝一个字节,并用源操作数的最高位填充其目的操作数中的其余各位,这种扩展方式叫符号扩展,对linux指令更加熟悉。

3.对过程调用和参数准备工作得到了进一步的理解。

4.学到了一些更为复杂的数据表示,比如链表,二叉树的在计算机中的具体存储,以及在底层的使用。

5.实验中每个人的实验的可执行文件都是不同,实验过程中,也帮其他同学看过一些代码,对于不同的反汇编代码更加熟悉,也间接复习了之前计算机系统关于汇编语言的相关知识。

6.结合之前缓冲区溢出攻击的学习,在实验做完后,尝试了用hexedit去直接修改可执行文件的某些执行,使得无论输入什么,都不会跳转到爆炸函数处,进一步理解了缓冲区溢出攻击的原理。

标签:24,计算机系统,00,esp,bomblab,mov,eax,实验,ebx

来源: https://blog.csdn.net/weixin_44595362/article/details/106723509

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/259270.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SDM For Face Alignment 流程介绍及Matlab代码实现之预处理篇

SDM全称为 Supervised Descent Method&#xff0c;是一种机器学习的方法&#xff0c;可以被用来做Face Alignment. 下面我们将通过matlab代码来梳理整个实现的过程。 预处理阶段 Input&#xff1a; ../data/lfpw/trainset &#xff08;811张图片&#xff09; Output: mean_…

分享25个新鲜出炉的 Photoshop 高级教程

网络上众多优秀的 Photoshop 实例教程是提高 Photoshop 技能的最佳学习途径。今天&#xff0c;我向大家分享25个新鲜出炉的 Photoshop 高级教程&#xff0c;提高你的设计技巧&#xff0c;制作时尚的图片效果。这些教程可以帮助把你的想法变成现实&#xff0c;并创造新的东西。 …

SDM For Face Alignment 流程介绍及Matlab代码实现之训练篇

SDM 训练阶段的任务如下&#xff1a; 载入标准化的数据&#xff08;包括400*400的正脸及特征点&#xff09;对每一张标准化的图片&#xff0c;模拟人脸检测仪&#xff0c;产生10个扰动的人脸框及相应的初始特征点x0。求解Δx,Φ,其中Δxx∗−x0,x∗表示true shape,Φ表示每个特…

Hibernate5-多对1(n:1)-fetch=join

1.创建项目,项目名称hibernatedemo26,目录结构如图所示2.在项目中创建lib目录存储jar文件,目录结构如图所示3.在src目录中创建实体类Forum,包名(com.mycompany.demo.bean),如图所示4.实体类Forum的内容如下package com.mycompany.demo.bean;import java.util.Set;public class …

如何使用固定二级子域名公网访问多个本地Windows Web网站

文章目录 1. 下载windows版Nginx2. 配置Nginx3. 测试局域网访问4. cpolar内网穿透5. 测试公网访问6. 配置固定二级子域名7. 测试访问公网固定二级子域名 1. 下载windows版Nginx 进入官方网站(http://nginx.org/en/download.html)下载windows版的nginx 下载好后解压进入nginx目…

实验 6 数组1

//输入n个整数&#xff0c;将它们存入数组a中。输出最大值和它所对应的下标。 #include<stdio.h> int main(void) {int n,i,x;int a[10];x0;printf("enter n:");scanf("%d",&n);for(i0;i<n;i){printf("enter :");scanf("%d&qu…

初中计算机职称答辩,晋升中学语文高级教师职称答辩内容举例

晋升中学语文高级教师职称答辩内容举例 晋升中学语文高级教师职称答辩秘籍 最重要的一点&#xff1a;你要对课本上的重点篇目非常熟悉&#xff01;对于现代文来说作者、题材、课文重点、重点句子词语、中心思想等你都要明了。对于文言文来说&#xff0c;要求学生掌握的&#xf…

SDM For Face Alignment流程介绍及Matlab代码实现之测试篇

测试很简单了&#xff0c;只需要载入数据&#xff0c;然后做正则化处理&#xff0c;使用训练模型产生的{Rk},就可以预测特征点了。 face_alignment.m:用来预测特征点 function shape face_alignment( ShapeModel, DataVariation,...LearnedCascadedModel, Data, img, shape,…

计算机类公务员如何提升自己,大学毕业才发现:所学专业对考公务员如此重要,4类专业上岸率高...

导语&#xff1a;毕业季来临&#xff0c;同学们是想直接找工作积累工作经验&#xff0c;还是继续考取相关证书&#xff0c;来获得更稳定职业的入场券&#xff1f;毕业抉择很多毕业生面临的第一个问题就是未来职业规划&#xff0c;因为大学毕业之后&#xff0c;就意味着一段新的…

UVA 11401 - Triangle Counting

Problem G Triangle Counting Input: Standard Input Output: Standard Output You are given n rods of length 1, 2…, n. You have to pick any 3 of them & build a triangle. How many distinct triangles can you make? Note that, two triangles will be considere…

苏州软件测试11k工资要什么水平,3个月从机械转行软件测试,他的入职薪资是11K...

原标题&#xff1a;3个月从机械转行软件测试&#xff0c;他的入职薪资是11K只要找到适合自己的学习方式&#xff0c;成功转行只是早晚的问题&#xff01;今天汇智妹给大家介绍的这位小伙伴&#xff0c;是咱们汇学联盟平台上的一位线上学员——小周。97年的小哥哥&#xff0c;19…

TCP/IP 原理--链路层

链路层作用&#xff1a; &#xff08;1&#xff09;为IP模块发送和接收IP数据报&#xff1b; &#xff08;2&#xff09;为ARP发送ARP请求和接受ARP应答 &#xff08;3&#xff09;为RARP发送RARP请求和接受ARP应答 协议&#xff1a;以太网和SLIP协议 A.以太网协议数据封装格式…

拆解凹多边形

偶遇需要拆解凹多边形 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Media;namespace DrawPolygon {public static class Settings{public const float…

一个FORK的面试题

为什么80%的码农都做不了架构师&#xff1f;>>> #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(void) { int i; for(i0; i<2; i){ fork(); printf("-"); } wait(NULL); wait(NULL); return 0; }/c 如果…

C++11系列学习之二-----lambda表达式

C11添加了一项名为lambda表达式的新功能&#xff0c;通过这项功能可以编写内嵌的匿名函数&#xff0c;而不必编写独立函数和函数对象&#xff0c;使得代码更容易理解。lambda表达式的语法如下所示&#xff1a;[capture_block](parameters) exceptions_specification -> retu…

GCPC2014 C Bounty Hunter

题意&#xff1a;给你一个平面上的点集&#xff08;x值各不相等&#xff09;&#xff0c;问你从最左边走到最右边&#xff08;只能以x递增的顺序&#xff09;&#xff0c;再从最右边回到最左边&#xff08;以x递减的顺序&#xff09;问你最短距离是多少。 解题思路&#xff1a;…

计算机启动时运行ccleaner,Ccleaner的使用方法

ccleaner是一款非常好用的系统优化工具&#xff0c;它可以提升电脑速度&#xff0c;可以对上网历史记录、临时文件夹、回收站垃圾清理、注册表进行垃圾项扫描和清理、软件卸载等功能&#xff0c;保护用户的个人浏览隐私&#xff0c;为Windows系统腾出更多硬盘空间。下面小编就为…

PLSQL Developer软件使用大全

PLSQL Developer软件使用大全 第一章 PLSQL Developer特性 PL/SQL Developer是一个集成开发环境&#xff0c;专门面向Oracle数据库存储程序单元的开发。如今&#xff0c;有越来越多的商业逻辑和应用逻辑转向了Oracle Server&#xff0c;因此&#xff0c;PL/SQL编程也成了整个开…

电脑显示器变色_电脑维修(看完后就可以开一家自己的电脑维修店!)

第二部分 常见故障判断本部分将计算机从开机一直到关机期间的故障进行分类。每一类的判断、定位过程都是第一部分中维修判断一节的有机组成部分&#xff0c;即不论使用什么方法或不论去判断什么内容&#xff0c;这两部分总是相互结合使用的。以下各故障类型中所列的故障现象只是…

linux运维基础篇 unit7

unit 71.进程定义进程就是cpu未完成的工作2.ps命令psa ##关于当前环境的所有进程x ##与当前环境无关的所有进程f ##显示进程从属关系e ##显示进程调用环境工具的详细信息l ##长列表显示进程的详细信息u ##显…