今天刚刚验收CSAPP实验3,趁着余温,记录一下这个实验,顺便回顾下CSAPP课程的相关知识。 1.使用gdb工具反汇编出汇编代码,结合c语言文件找到每个关卡的入口函数。然后分析汇编代码,分析得到每一关的通关密码。 个人电脑PC,32位ubuntu系统环境 “二进制炸弹”是Linux可执行C程序,包含六个“阶段”和一个秘密“阶段”。 每个阶段都要求输入特定的字符串在stdin上。 如果输入了预期的字符串,则该阶段通过,进入下一阶段。 否则,炸弹会通过打印“ BOOM !!!”而“爆炸”。 (1).进入Ubuntu系统,将自己的程序包复制到虚拟机中,并使用tar xvf bomb201808010515.tar进行解压。 PS:每个关卡汇编代码下面有相应的文字分析。 (1). 首先注意到这个函数,这里应该是读取6个数字。接下来看到esp+0x18中存放的值和1进行比较,不相等则爆炸,由此猜测第1个数为1。连续的两条lea语句是将esp+0x1c,esp+0x30的值存放入对应寄存器。从esp+0x18到esp+0x30刚好存放6个int 类型的数据,故esp+0x30是最后一个数据,esp+0x1c是第2个数据。 (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!”,第二关顺利通过。结果如下图所示: (1).看到上面这段汇编代码,注意到函数_isoc99_sscanf@plt,说明这部分存在输入信息,往前看,发现movl $804a3a3,0x4(%esp)。进入GDB调试,设置断点 b phase_3,尝试使用x/s查看内容,发现是“%d %d”(如下图),说明需要输入两个数据。结合前面传入两个参数,这两个参数就是输入的数据,设为x,y。 (2). 接下来看到cmpl $0x7,%eax,进行无符号数比较,如果大于7或者小于0就爆炸,故可以知道eax中存放的值(参数1)的取值范围为0-7的整数。往下发现很多跳转指令,结合jmp *0x804a1e0(,%eax,4)知道,这是一个跳转表结构。 (3).分析这个跳转表 假设x为1,则得到地址0x804a1e0+4*1=0x804a1e4,使用x/8bx 0x804a1e4查看信息,为0x804c3c,即case 1的入口地址。 (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组数据进行验证: (1).查看上面这一段代码,分析得出输入两个数字(和关卡3一样,用gdb调试看0x804a3a3中存放的内容)。结合前面传入两个参数,这两个参数就是输入的数据,设为x,y。 (2).分析这一部分代码,test判断x是否小于0,cmp判断x是否大于0xe,故要是炸弹不爆炸,则参数x的取值范围为0-0xe的整数。 (3). 在调用func4之前将参数压入栈帧. (4). 继续分析发现下面对func4的返回值和参数y分别对7进行比较,如果不为7,则引爆炸弹,分析到此,可以知道对于x属于[0,0xe]使得func4(x,0,0xe)返回值为0x7的x即为所求。 (5).现在重点就是分析func4函数,看到汇编代码可以发现这是一个递归函数,关键在于找到递归的条件和退出函数的条件,以及递归之前函数的参数压栈准备。根据这段汇编代码写出C语言代码如下: (6).编写测试函数如下 运行代码结果如下,可以发现返回值为0x7对应的x只有0x7一种值,故第4关通关输入为7 7。 (1).注意到<string_length>函数,结合后面eax和0x6比较,可知这一关输入的参数是一个长度为6的字符串,需要我们去匹配出这个字符串。 (2).接着edx,eax寄存器的值初始化为0,movsbl (%ebx,%eax,1),%ecx,这一句是表示将长度为6的字符串的第eax个字符存入ecx中。取ecx中的低4位,利用基址比例变址寻址的方式,到数组中找值,并累和在寄存器edx中。往下看,可以看到cmp $0x2b,%edx,如果不相等则爆炸,说明累和得到的值必须为0x2b。 (1).首先看到函数,根据之前的经验,猜测这里也是读取6个数字。 (2).这里的汇编代码包括两个循环,用来给出数字的取值范围是属于1-6,并且数字之间彼此两两不相等。 (3). 接下来对链表的结点进行了连接 (4).进入GDB调试,设置断点b phase_6,往下分析代码过程中看到一个立即数,查看其值,出现一个node1,猜测这是一个链表的数据结构。于是多查看几个值,发现了其中的规律,每三个数为一组,第1个是数据域中的值,第2个是下标,第3个比较大,类似于地址,是指针域,然后对猜想进行验证。调试后验证猜测正确。根据之前对链表进行连接,顺序为322->370->1c1->24b->0a1->192。 (5).ebx+0x8存储的是第2个数字的地址,取出第2个数字x2,和edx存储的第1个数字x1进行比较,需要满足x1<=x2,接下来ebx地址加0x8,对下一个进行约束。由此可以看出这个序列是一个递增序列。 做完前6关,看到提示信息的那一刻,我以为就这样结束,但是听说还有秘密关卡,仔细一看汇编代码,果然有serect_phase的汇编代码片段。起初没有思路,不知道该从何入手,在网上参考了一些资料,结合bomb.c的代码,发现每个阶段函数结束后都调用了phase_defused()函数,猜测秘密关卡可能隐藏在某一关中。 (1).结合前面关卡,发现这里有2个立即数,使用gdb调试查看其内容 (1).这段代码首先将三个参数放入栈帧中,接下来调用了strtol函数。mov %eax,%ebx;lea -0x1(%eax),%eax;cmp $0x3e8,%eax;jbe 8048ec9 <secret_phase+0x32>;这4句对参数x的范围进行了限定,x的取值是1-0x3e9。 (2).这段代码中主要调用了函数fun7,调用之前可以分析出参数,即fun7(地址,n),第1个参数为某个数组或者链表的首地址,第2个参数为1-1001之间的整数。通关的条件是fun7()的返回值为0。分析fun7中代码,并结合返回值为0,故可以知道保证传入fun7中两个参数相等即可。 (3).根据fun7的汇编代码写出对应的C语言代码 (4).根据前面的分析,要使炸弹不爆炸需要fun7返回值为0,从函数中可以看出,返回值为0,只需*x=y即可,于是查看传入fun7中x中的内容即可。 1.本次实验进一步熟悉了GDB调试工具,调试能力得到了一定的提高。实验目的
2.熟悉linux指令,学会如何使用gdb对程序进行调试实验环境
实验内容及操作步骤
实验内容
操作步骤
拆弹前准备
(2).输入objdump –d bomb >my_bomb.txt,将反汇编代码输入到my_bomb.txt的文件中,便于拆弹时的查看和注释。
(3).打开反汇编文件,发现phase_i系列的函数,结合实验题目,可以知道这是每一个关卡对应的函数。开始拆炸弹
phase_1:确定输入字符串
08048b50 <phase_1>: 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 <strings_not_equal> //输入字符串进入函数进行比较 8048b67: 85 c0 test %eax,%eax 8048b69: 74 05 je 8048b70 <phase_1+0x20> //返回值为0,结束,否则爆炸 8048b6b: e8 46 05 00 00 call 80490b6 <explode_bomb> 8048b70: 83 c4 1c add $0x1c,%esp 8048b73: c3 ret
(1).可以看到在%eax!=0的时候就会调用<explode_bomb>,所以在调用<strings_not_equal> 函数之后的返回值%eax必须为0。往前看,发现代码movl $0x804a184,0x4(%esp)有立即数,是将此处地址的值拿来用,输入gdb –q bomb进入调试状态,用x/s 0x804a184查看内容。
(2).下面一步 mov 0x20(%esp),%eax就是把我们输入的参数放进%eax中,然后放进(%esp) ,再调用函数<strings_not_equal>。所以猜测要输入的就是0x804a184中的字符。开始第一关的尝试,重新打开一个终端,用来测试。测试结果如下,输出”Phase 1 defused. How about the next one?”,成功通关。
phase_2: 确定一个有规律的数列
08048b74 <phase_2>: 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 <read_six_numbers> 8048b8d: 83 7c 24 18 01 cmpl $0x1,0x18(%esp) //esp+0x18-1 8048b92: 74 05 je 8048b99 <phase_2+0x25> //esp+0x18==1 8048b94: e8 1d 05 00 00 call 80490b6 <explode_bomb> //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
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 <phase_2+0x3b> //(ebx)==2 8048baa: e8 07 05 00 00 call 80490b6 <explode_bomb> //(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 <phase_2+0x2d> //不相等,继续循环 8048bb6: 83 c4 34 add $0x34,%esp //结束 8048bb9: 5b pop %ebx 8048bba: 5e pop %esi 8048bbb: c3 ret
phase_3 :switch语句
08048bbc <phase_3>: 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@plt>
8048be3: 83 f8 01 cmp $0x1,%eax //eax-1 8048be6: 7f 05 jg 8048bed <phase_3+0x31> //eax>1 8048be8: e8 c9 04 00 00 call 80490b6 <explode_bomb> //eax<1,爆炸 8048bed: 83 7c 24 18 07 cmpl $0x7,0x18(%esp) //x-7 8048bf2: 77 3c ja 8048c30 <phase_3+0x74> //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
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 <phase_3+0x85> 8048c06: b8 b6 03 00 00 mov $0x3b6,%eax //eax=0x3b6,x=2 对应查看*0x804a1e8 8048c0b: eb 34 jmp 8048c41 <phase_3+0x85> 8048c0d: b8 03 03 00 00 mov $0x303,%eax //eax=0x303,x=3 8048c12: eb 2d jmp 8048c41 <phase_3+0x85> 8048c14: b8 1d 01 00 00 mov $0x11d,%eax //eax=0x11d,x=4 8048c19: eb 26 jmp 8048c41 <phase_3+0x85> 8048c1b: b8 a0 01 00 00 mov $0x1a0,%eax //eax=0x1a0,x=5 8048c20: eb 1f jmp 8048c41 <phase_3+0x85> 8048c22: b8 60 03 00 00 mov $0x360,%eax //eax=0x360,x=6 8048c27: eb 18 jmp 8048c41 <phase_3+0x85> 8048c29: b8 6f 02 00 00 mov $0x26f,%eax //eax=0x26f,x=7 8048c2e: eb 11 jmp 8048c41 <phase_3+0x85> 8048c30: e8 81 04 00 00 call 80490b6 <explode_bomb> //爆炸 8048c35: b8 00 00 00 00 mov $0x0,%eax //eax=0 8048c3a: eb 05 jmp 8048c41 <phase_3+0x85> 8048c3c: b8 7a 00 00 00 mov $0x7a,%eax //eax=0x7a,x=1
假设x为0,则得到地址0x804a1e0+4*0=0x804a1e0,使用x/8bx 0x804a1e0查看信息,为0x8048bff,即case 0的入口地址。
根据case 0的入口地址0x8048bff找到执行执行mov $0x1cc,%eax。
根据case 1的入口地址0x8048c3c找到执行执行mov $0x7a,%eax。同理可以确定其他几组数据。8048c41: 3b 44 24 1c cmp 0x1c(%esp),%eax //eax-y 8048c45: 74 05 je 8048c4c <phase_3+0x90> //eax==y 8048c47: e8 6a 04 00 00 call 80490b6 <explode_bomb> //eax!=y,爆炸 8048c4c: 83 c4 2c add $0x2c,%esp //结束 8048c4f: c3 ret
phase_4 :函数过程调用
08048cb9 <phase_4>: 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
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@plt> //调用某个函数 8048ce0: 83 f8 02 cmp $0x2,%eax //eax-2 判断传入参数的个数是否满足条件 8048ce3: 75 0d jne 8048cf2 <phase_4+0x39> //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 <phase_4+0x39> //x<0 引爆 8048ced: 83 f8 0e cmp $0xe,%eax //x-0xe 8048cf0: 7e 05 jle 8048cf7 <phase_4+0x3e> //x<=0xe 8048cf2: e8 bf 03 00 00 call 80490b6 <explode_bomb>
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> //调用func4
8048d13: 83 f8 07 cmp $0x7,%eax //eax-7,eax里面存放的是func4的返回值 8048d16: 75 07 jne 8048d1f <phase_4+0x66> //eax!=7,引爆 8048d18: 83 7c 24 1c 07 cmpl $0x7,0x1c(%esp) //y-7 8048d1d: 74 05 je 8048d24 <phase_4+0x6b> //y==7,结束 8048d1f: e8 92 03 00 00 call 80490b6 <explode_bomb> 8048d24: 83 c4 2c add $0x2c,%esp 8048d27: c3 ret
08048c50 <func4>: 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 <func4+0x42> //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> //递归调用函数func4 8048c8e: 01 c3 add %eax,%ebx //ebx=ebx+eax 8048c90: eb 19 jmp 8048cab <func4+0x5b> 8048c92: 39 c3 cmp %eax,%ebx //ebx-eax 8048c94: 7d 15 jge 8048cab <func4+0x5b> //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> //递归调用函数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
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; } }
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; }
切换到之前测试用的终端,输入7 7,终端显示“So you got that one. Try this one.”,第四关顺利通过。
phase_5: 序列的构造和数组寻址
08048d28 <phase_5>: 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 <string_length> 8048d38: 83 f8 06 cmp $0x6,%eax //eax-6(猜测eax是字符串的长度) 8048d3b: 74 05 je 8048d42 <phase_5+0x1a> //eax==6 8048d3d: e8 74 03 00 00 call 80490b6 <explode_bomb> //eax不是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 <phase_5+0x24> //eax!=6 8048d62: 83 fa 2b cmp $0x2b,%edx //edx-0x2b 8048d65: 74 05 je 8048d6c <phase_5+0x44> //相等 8048d67: e8 4a 03 00 00 call 80490b6 <explode_bomb> //爆炸 8048d6c: 83 c4 18 add $0x18,%esp //结束 8048d6f: 5b pop %ebx 8048d70: c3 ret
(3).分析到此,关键在于找到数组中的值,由汇编我们知道数组首地址为0x804a200,进入GDB调试,设置断点b phase_5,运行后查看其内容,从里边找出一组和为0x2b的数字,这里取出一组 {2,a,6,1,c,c}。
对应下标为0,1,2,3,4,4,去ascii表中找低四位为0,1,2,3,4,4的字符,故得到其中一组解为pabcdd。
(4). 切换到之前测试用的终端,输入pabcdd,终端显示“Good work! On to the next…!”,第五关顺利通过。
phase_6: 链表
08048d71 <phase_6>: 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 <read_six_numbers>
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 <phase_6+0x2f> //无符号数<= 8048d9b: e8 16 03 00 00 call 80490b6 <explode_bomb> //引爆 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 <phase_6+0x6a> //等于跳转 //以上代码是确定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 <phase_6+0x48> //不相等 8048db4: e8 fd 02 00 00 call 80490b6 <explode_bomb> //相等,引爆 8048db9: 83 c3 01 add $0x1,%ebx //ebx++ 8048dbc: 83 fb 05 cmp $0x5,%ebx //ebx-5 8048dbf: 7e e9 jle 8048daa <phase_6+0x39> //小于等于跳转 8048dc1: eb cc jmp 8048d8f <phase_6+0x1e> 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 <phase_6+0x52> //不相等,继续执行 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 <phase_6+0x6f> //不相等 8048dd9: eb 1c jmp 8048df7 <phase_6+0x86> //以上代码是判断数字之间是否互不相等。
//链表初始化 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) //链表尾
//递增序列 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 <phase_6+0xc7> //x1<=x2 8048e33: e8 7e 02 00 00 call 80490b6 <explode_bomb> 8048e38: 8b 5b 08 mov 0x8(%ebx),%ebx //ebx=ebx+0x8,下一个数 8048e3b: 83 ee 01 sub $0x1,%esi //esi-- 8048e3e: 75 ea jne 8048e2a <phase_6+0xb9> 8048e40: 83 c4 44 add $0x44,%esp 8048e43: 5b pop %ebx 8048e44: 5e pop %esi 8048e45: c3 ret
(6).结合之前输入的6个数字在1-6之间,且互不相同,这里应该是排序后递增链表序列的下标。故得到下标为5 6 3 4 1 2。切换到之前测试用的终端,输入5 6 3 4 1 2,终端显示“Congratulations! You’ve defused the bomb!”,第六关顺利通过。
secret_phase:二叉树
查看汇编代码如下0804923b <phase_defused>: 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 <phase_defused+0x8d> 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@plt> 8049282: 83 f8 03 cmp $0x3,%eax 8049285: 75 35 jne 80492bc <phase_defused+0x81> 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 <strings_not_equal> 804929b: 85 c0 test %eax,%eax 804929d: 75 1d jne 80492bc <phase_defused+0x81> 804929f: c7 04 24 78 a2 04 08 movl $0x804a278,(%esp) 80492a6: e8 55 f5 ff ff call 8048800 <puts@plt> 80492ab: c7 04 24 a0 a2 04 08 movl $0x804a2a0,(%esp) 80492b2: e8 49 f5 ff ff call 8048800 <puts@plt> 80492b7: e8 db fb ff ff call 8048e97 <secret_phase> 80492bc: c7 04 24 d8 a2 04 08 movl $0x804a2d8,(%esp) 80492c3: e8 38 f5 ff ff call 8048800 <puts@plt> 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 <phase_defused+0x9f> 80492d5: e8 f6 f4 ff ff call 80487d0 <__stack_chk_fail@plt> 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
发现提示输入两个数字和一个字符串,以及第4关的输入。于是猜测秘密关卡藏在第4关,而这个字符串就是打开秘密关卡的“钥匙”。
(2). 可以看到这里将0x804a3b2中存放的内容传入函数strings_not_equal中进行比较,如果返回值不为0,就跳转到0x80492bc,输出0x804a2d8中的内容,否则就一次往下输出0x804a278,0x804a2a0的信息,最后输出0x804a2d8的信息。猜测:0x804a3b2就是进入秘密关卡的“钥匙”,当“钥匙”输入错误就直接结束。
添加断点,用gdb调试查看上述提到的4个地址中的内容,验证了猜测的正确性。
08048e97 <secret_phase>: 8048e97: 53 push %ebx 8048e98: 83 ec 18 sub $0x18,%esp //开辟栈帧 8048e9b: e8 3d 02 00 00 call 80490dd <read_line> 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 <strtol@plt> 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 <secret_phase+0x32> 无符号数比较,小于等于 8048ec4: e8 ed 01 00 00 call 80490b6 <explode_bomb>
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> //调用fun7 8048ed9: 85 c0 test %eax,%eax //eax and eax 8048edb: 74 05 je 8048ee2 <secret_phase+0x4b> //等于0,不是0就爆炸 8048edd: e8 d4 01 00 00 call 80490b6 <explode_bomb> 8048ee2: c7 04 24 b8 a1 04 08 movl $0x804a1b8,(%esp) 8048ee9: e8 12 f9 ff ff call 8048800 <puts@plt> 8048eee: e8 48 03 00 00 call 804923b <phase_defused> 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
08048e46 <fun7>: 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 <fun7+0x47> //等于0 8048e56: 8b 1a mov (%edx),%ebx //取出地址中的值,给ebx,即ebx=x 8048e58: 39 cb cmp %ecx,%ebx //x-y 8048e5a: 7e 13 jle 8048e6f <fun7+0x29> //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> //调用函数fun7 8048e6b: 01 c0 add %eax,%eax //eax=eax*2 8048e6d: eb 23 jmp 8048e92 <fun7+0x4c> 8048e6f: b8 00 00 00 00 mov $0x0,%eax //eax=0 8048e74: 39 cb cmp %ecx,%ebx //x-y 8048e76: 74 1a je 8048e92 <fun7+0x4c> //如果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> //调用函数fun7 8048e87: 8d 44 00 01 lea 0x1(%eax,%eax,1),%eax eax=2*eax+1 8048e8b: eb 05 jmp 8048e92 <fun7+0x4c> 8048e8d: b8 ff ff ff ff mov $0xffffffff,%eax 8048e92: 83 c4 18 add $0x18,%esp 8048e95: 5b pop %ebx 8048e96: c3 ret
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); } }
8048ecd: c7 04 24 88 c0 04 08 movl $0x804c088,(%esp) //fun7参数1 m=0x804c088 8048ed4: e8 6d ff ff ff call 8048e46 <fun7> //调用fun7
(5).切换到之前测试用的终端,运行./bomb,从第一关开始输入,在第四关时,加上字符串“DrEvil”,最终输入秘密关卡的36,终端显示“Wow! You’ve defused the secret stage!Congratulations! You’ve defused the bomb!”。
(6).秘密关卡到这里就结束了,而对于这个关卡涉及到的结构可以进行进一步探索。查看0x804c088中内容发现有3个值,第1个是数值,第2,3个值比较大,是两个地址,联系所学,这两个地址就是左,右子结点的地址。将整个结构用图表示如下,可以看出这是一个典型的二叉树结构。
实验结果及分析
收获与体会
2.熟悉了linux中一些之前没有见过的指令,如movsbl指令movsbl指令负责拷贝一个字节,并用源操作数的最高位填充其目的操作数中的其余各位,这种扩展方式叫符号扩展,对linux指令更加熟悉。
3.对过程调用和参数准备工作得到了进一步的理解。
4.学到了一些更为复杂的数据表示,比如链表,二叉树的在计算机中的具体存储,以及在底层的使用。
5.实验中每个人的实验的可执行文件都是不同,实验过程中,也帮其他同学看过一些代码,对于不同的反汇编代码更加熟悉,也间接复习了之前计算机系统关于汇编语言的相关知识。
6.结合之前缓冲区溢出攻击的学习,在实验做完后,尝试了用hexedit去直接修改可执行文件的某些执行,使得无论输入什么,都不会跳转到爆炸函数处,进一步理解了缓冲区溢出攻击的原理。
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算