实验题目
bomblab
实验目的
- 使用gdb工具反汇编出汇编代码,结合c语言文件找到每个关卡的入口函数。然后分析汇编代码,分析得到每一关的通关密码。
- 进一步加深对linux指令的理解,对gdb调试的一些基本操作以及高级操作有所了解。
- 熟悉汇编程序,懂得如何利用汇编程序写出C语言程序伪代码,熟悉并掌握函数调用过程中的栈帧结构的变化,熟悉汇编程序及其调试方法。
实验环境
个人PC、Linux 32位操作系统、Ubuntu16.04
实验内容
准备阶段
- 将实验压缩包解压并找到本人所用到的实验文件夹bomb7,复制到linux系统中,打开文件夹得到bomb、bomb.c、README文件;
- 阅读README等实验相关材料,通过各种方式了解实验的相关内容和过程;
- 检查bomb实验中的两个文件,发现bomb.c文件残缺,无法运行;而bomb文件可以正常运行,实验主要部分与bomb.c文件关系不大;
- 了解实验的要求,该实验共有7个关卡,包括6个普通关卡和1个隐藏关卡。
- 将bomb文件反汇编并生成 .txt 文件(
objdump bomb -d > my_bomb.txt
),这样可以在my_bomb.txt中分析汇编代码,而bomb文件则可结合gdb进行调试分析;
分析阶段
<phase_1>
- 反汇编代码
08048b90 <phase_1>:
8048b90: 83 ec 1c sub $0x1c,%esp //esp-28 -> esp,申请栈空间
8048b93: c7 44 24 04 44 a1 04 movl $0x804a144,0x4(%esp) //0x0804a144 -> M(esp+4),将内存空间放入esp寄存器
8048b9a: 08
8048b9b: 8b 44 24 20 mov 0x20(%esp),%eax //M(esp+32) -> eax
8048b9f: 89 04 24 mov %eax,(%esp) //eax -> M(esp)
8048ba2: e8 63 04 00 00 call 804900a <strings_not_equal> //调用函数判断string是否相等
8048ba7: 85 c0 test %eax,%eax //test if eax is empty,if empty,ZF = 1.
8048ba9: 74 05 je 8048bb0 <phase_1+0x20> //jump if ZF = 1
8048bab: e8 65 05 00 00 call 8049115 <explode_bomb> //call <explode_bomb>
8048bb0: 83 c4 1c add $0x1c,%esp //esp+12 -> esp
8048bb3: c3 ret //return
- 解题思路
- 先把重要的反汇编代码意义标注出来(注释如上)
- 采用逆向思维法,先找出炸弹什么时候会爆炸。
8048bab: e8 65 05 00 00 call 8049115 <explode_bomb> //call <explode_bomb>
执行这一步就会爆炸。
- 找到跳过爆炸这一步的条件。
8048ba7: 85 c0 test %eax,%eax //test if eax is empty,if empty,ZF = 1.
8048ba9: 74 05 je 8048bb0 <phase_1+0x20> //jump if ZF = 1
只要eax为0,则执行je指令,跳过爆炸。
- 顺藤摸瓜,找出eax为0的条件。
8048b9b: 8b 44 24 20 mov 0x20(%esp),%eax //M(esp+32) -> eax
8048b9f: 89 04 24 mov %eax,(%esp) //eax -> M(esp)
8048ba2: e8 63 04 00 00 call 804900a <strings_not_equal> //调用函数判断string是否相等
追溯到 mov 0x20(%esp),%eax指令,把我们输入的参数放进%eax中,然后放进(%esp) ,再调用函数<strings_not_equal>,如果输入的内容与传入的字符串相等,则返回0,这个就是eax为0的条件。
- 找出传入<strings_not_equal>的参数。
8048b93: c7 44 24 04 44 a1 04 movl $0x804a144,0x4(%esp) //0x0804a144 -> M(esp+4),将内存空间放入esp寄存器
所求的字符串即是0x804a144里面的值。
- 进入gdb调试模式,找到0x804a144地址格子里面的东西
gates@ubuntu:~/bomb7$ gdb -q bomb
Reading symbols from bomb...
(gdb) x/s 0x804a144
0x804a144: "Brownie, you are doing a heck of a job."
- 输入找出来的字符串,验证答案。
gates@ubuntu:~/bomb7$ ./bomb
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
Brownie, you are doing a heck of a job.
Phase 1 defused. How about the next one?
- 炸弹一拆除!
<phase_2>
- 反汇编代码
08048bb4 <phase_2>:
8048bb4: 53 push %ebx //save old edx
8048bb5: 83 ec 38 sub $0x38,%esp //open new stack
8048bb8: 8d 44 24 18 lea 0x18(%esp),%eax //esp+24->eax
8048bbc: 89 44 24 04 mov %eax,0x4(%esp) //eax -> M(esp+4)
8048bc0: 8b 44 24 40 mov 0x40(%esp),%eax //M(esp+64) -> eax
8048bc4: 89 04 24 mov %eax,(%esp) //eax -> M(esp)
8048bc7: e8 70 05 00 00 call 804913c <read_six_numbers>
8048bcc: 83 7c 24 18 00 cmpl $0x0,0x18(%esp) //cmp
8048bd1: 79 22 jns 8048bf5 <phase_2+0x41> //M(esp+24-0)>=0
8048bd3: e8 3d 05 00 00 call 8049115 <explode_bomb> //Bomb!
8048bd8: eb 1b jmp 8048bf5 <phase_2+0x41> //
8048bda: 89 d8 mov %ebx,%eax //ebx -> eax
8048bdc: 03 44 9c 14 add 0x14(%esp,%ebx,4),%eax //eax+M(20+esp+4*ebx) -> eax
8048be0: 39 44 9c 18 cmp %eax,0x18(%esp,%ebx,4) //cmp
8048be4: 74 05 je 8048beb <phase_2+0x37> //jump if eax==M(24+esp+4*ebx)
8048be6: e8 2a 05 00 00 call 8049115 <explode_bomb> //Bomb!
8048beb: 83 c3 01 add $0x1,%ebx //ebx+1 -> ebx
8048bee: 83 fb 06 cmp $0x6,%ebx //cmp
8048bf1: 75 e7 jne 8048bda <phase_2+0x26> //jump if ebx!=6
8048bf3: eb 07 jmp 8048bfc <phase_2+0x48> //jump
8048bf5: bb 01 00 00 00 mov $0x1,%ebx //1 -> ebx
8048bfa: eb de jmp 8048bda <phase_2+0x26> //jump
8048bfc: 83 c4 38 add $0x38,%esp //esp+56 -> esp
8048bff: 5b pop %ebx //pop
8048c00: c3 ret
- 解题思路
- 先把重要的反汇编代码意义标注出来(注释如上)
- 找到炸弹爆炸的汇编代码。
8048bd3: e8 3d 05 00 00 call 8049115 <explode_bomb> //Bomb!
8048be6: e8 2a 05 00 00 call 8049115 <explode_bomb> //Bomb!
- 找到跳过第一个炸弹的方法。
804913c <read_six_numbers>
8048bcc: 83 7c 24 18 00 cmpl $0x0,0x18(%esp) //cmp
8048bd1: 79 22 jns 8048bf5 <phase_2+0x41> //M(esp+24-0)>=0
<read_six_numbers>是读取输入的六个数字,cmpl、jns语句结合,判断M(esp+24)与0x0的大小,满足M(esp+24)>=0x0则可跳过第一个炸弹,由此可知,输入的第一个数应该要大于等于0。
- 顺着跳转指令寻找重要寄存器的值。
8048bf5: bb 01 00 00 00 mov $0x1,%ebx //1 -> ebx
8048bfa: eb de jmp 8048bda <phase_2+0x26> //jump
8048bda: 89 d8 mov %ebx,%eax //ebx -> eax
ebx=1,eax=ebx=1。
- 找到跳过第二个炸弹的方法。
8048bdc: 03 44 9c 14 add 0x14(%esp,%ebx,4),%eax //eax+M(20+esp+4*ebx) -> eax
8048be0: 39 44 9c 18 cmp %eax,0x18(%esp,%ebx,4) //cmp
8048be4: 74 05 je 8048beb <phase_2+0x37> //jump if eax==M(24+esp+4*ebx)
分析这一段代码可以知道,这是把第一个数与eax(此处是1)相加,得到的结果与第二个数相比较,如果等于第二个数方可跳过炸弹。所以第二个数就是第一个数加1。
- 顺藤摸瓜,找到跳出循环的条件。
8048beb: 83 c3 01 add $0x1,%ebx //ebx+1 -> ebx
8048bee: 83 fb 06 cmp $0x6,%ebx //cmp
8048bf1: 75 e7 jne 8048bda <phase_2+0x26> //jump if ebx!=6
8048bf3: eb 07 jmp 8048bfc <phase_2+0x48> //jump
由此段代码可以知道,ebx每次加1,然后跳到上面的一步,直到等于6跳出循环。
- 总体描述输入内容。
输入第一个数只要满足大于等于0即可,第二个数等于第一个数加1,第三个数等于第二个数加2,第四个数等于第三个数加3……,依此类推,最后输进去6个数即可。
- 输进去满足上述关系的6个数,发现答案确实如此。
Phase 1 defused. How about the next one?
1 2 4 7 11 16
That's number 2. Keep going!
- 炸弹二拆除!
<phase_3>
- 反汇编代码
08048c01 <phase_3>:
8048c01: 83 ec 2c sub $0x2c,%esp //
8048c04: 8d 44 24 1c lea 0x1c(%esp),%eax //
8048c08: 89 44 24 0c mov %eax,0xc(%esp) //为输入内容做准备
8048c0c: 8d 44 24 18 lea 0x18(%esp),%eax //
8048c10: 89 44 24 08 mov %eax,0x8(%esp) //为输入内容做准备
8048c14: c7 44 24 04 0f a3 04 movl $0x804a30f,0x4(%esp) //%d%d
8048c1b: 08
8048c1c: 8b 44 24 30 mov 0x30(%esp),%eax //
8048c20: 89 04 24 mov %eax,(%esp) //传递参数
8048c23: e8 38 fc ff ff call 8048860 <__isoc99_sscanf@plt> //
8048c28: 83 f8 01 cmp $0x1,%eax //eax与1比较
8048c2b: 7f 05 jg 8048c32 <phase_3+0x31> //eax>1则跳过爆炸
8048c2d: e8 e3 04 00 00 call 8049115 <explode_bomb> //Bomb!
8048c32: 83 7c 24 18 07 cmpl $0x7,0x18(%esp) //
8048c37: 77 66 ja 8048c9f <phase_3+0x9e> //M(0x18+exp)>0x7则跳转爆炸
8048c39: 8b 44 24 18 mov 0x18(%esp),%eax //M(esp+0x18) -> eax
8048c3d: ff 24 85 a0 a1 04 08 jmp *0x804a1a0(,%eax,4) //jump to *(0x804a1a0+4*eax)
8048c44: b8 00 00 00 00 mov $0x0,%eax //0 -> eax
8048c49: eb 05 jmp 8048c50 <phase_3+0x4f> //jump
8048c4b: b8 4c 02 00 00 mov $0x24c,%eax //0x24c -> eax
8048c50: 2d 31 03 00 00 sub $0x331,%eax //eax-0x331 -> eax
8048c55: eb 05 jmp 8048c5c <phase_3+0x5b> //jump
8048c57: b8 00 00 00 00 mov $0x0,%eax //0x0 -> eax
8048c5c: 05 05 03 00 00 add $0x305,%eax //eax+0x305 -> eax
8048c61: eb 05 jmp 8048c68 <phase_3+0x67> //jump
8048c63: b8 00 00 00 00 mov $0x0,%eax //0x0 -> eax
8048c68: 2d 9e 00 00 00 sub $0x9e,%eax //eax-0x9e -> eax
8048c6d: eb 05 jmp 8048c74 <phase_3+0x73> //jump
8048c6f: b8 00 00 00 00 mov $0x0,%eax //0x0 -> eax
8048c74: 05 9e 00 00 00 add $0x9e,%eax //eax+0x9e -> eax
8048c79: eb 05 jmp 8048c80 <phase_3+0x7f> //jump
8048c7b: b8 00 00 00 00 mov $0x0,%eax //0x0 -> eax
8048c80: 2d 9e 00 00 00 sub $0x9e,%eax //eax-0x9e -> eax
8048c85: eb 05 jmp 8048c8c <phase_3+0x8b> //jump
8048c87: b8 00 00 00 00 mov $0x0,%eax //0x0 --> eax
8048c8c: 05 9e 00 00 00 add $0x9e,%eax //eax+0x9e -> eax
8048c91: eb 05 jmp 8048c98 <phase_3+0x97> //jump
8048c93: b8 00 00 00 00 mov $0x0,%eax //0x0 -> eax
8048c98: 2d 9e 00 00 00 sub $0x9e,%eax //eax-0x9e -> eax
8048c9d: eb 0a jmp 8048ca9 <phase_3+0xa8> //jump
8048c9f: e8 71 04 00 00 call 8049115 <explode_bomb> //Bomb!
8048ca4: b8 00 00 00 00 mov $0x0,%eax //0x0 -> eax
8048ca9: 83 7c 24 18 05 cmpl $0x5,0x18(%esp) //cmp 0x5,M(0x18+esp)
8048cae: 7f 06 jg 8048cb6 <phase_3+0xb5> //if greater, jump to Bomb
8048cb0: 3b 44 24 1c cmp 0x1c(%esp),%eax //cmp eax,M(0x1c+esp)