标签:++ 自动 number 总结 dac 排列 表示 amp string
这个实验曾经做过
这次又做了一遍,对之前的过程作了补充
binary bomb
一、实验目的:
增强对程序机器级表示、汇编语言、调试器和逆向工程等理解。
二、实验要求:
1熟练使用gdb调试器和objdump;
2单步跟踪调试每一阶段的机器代码;
3理解汇编语言代码的行为或作用;
4“推断”拆除炸弹所需的目标字符串。
5在各阶段的开始代码前和引爆炸弹函数前设置断点,便于调试。
三、实验内容:
准备:
使用objdump –d bomb > asm.txt命令,将bomb程序逆成汇编代码,分析汇编代码。
Phase_1:
汇编代码如图
代码分析:
这是一个字符串比较,所以应该是和内存中的已有内容比较。
有一个内存地址,我们去看一下内存0x804a124的内容
找到字符串,输入之后
通过。
phase_2:
截图有点小。。
代码分析:
push %esi
push %ebx
sub $0x34,%esp
lea 0x18(%esp),%eax
mov %eax,0x4(%esp)
mov 0x40(%esp),%eax
mov %eax,(%esp)
call 804911c <read_six_numbers> ;读入6个数
cmpl $0x0,0x18(%esp) ;第一个数和0比较
jne 8048bdb <phase_2+0x27> ;如果不等于0就爆炸,所以第一个为0
cmpl $0x1,0x1c(%esp) ;第二个数和1比较
je 8048bfa <phase_2+0x46> ;如果等于1就跳走了,不等于就爆炸了
call 80490f5 <explode_bomb> ;爆炸
jmp 8048bfa <phase_2+0x46>
mov -0x8(%ebx),%eax ;eax = a[i-2] 存上上个数
add -0x4(%ebx),%eax ;eax = a[i - 2] + a[i - 1] 存上上个数与
cmp %eax,(%ebx) ;相当于把eax赋给ebx,也就是把当前位置的相邻
je 8048bf1 <phase_2+0x3d> ;ebx必须和eax相等,否则就爆炸
call 80490f5 <explode_bomb>
add $0x4,%ebx ;ebx+4,ebx指向了新的位置
cmp %esi,%ebx ;ebx是否和ebx+30相等,不等就跳回到8048be2,循环
jne 8048be2 <phase_2+0x2e>
jmp 8048c04 <phase_2+0x50>;相等,结束
lea 0x20(%esp),%ebx ;ebx指向新的地址
lea 0x30(%esp),%esi ;esi始终存的是地址esp+30
jmp 8048be2 <phase_2+0x2e>
add $0x34,%esp
pop %ebx
pop %esi
ret
根据规律得出最后是a[i] = a[i - 2] + a[i - 1],而且第一个数是0,第二个数是1,递推出
0 1 1 2 3 5
输入
通过。
Phase_3:
截取部分代码:
代码分析:
sub $0x2c,%esp
lea 0x1c(%esp),%eax
mov %eax,0xc(%esp)
lea 0x18(%esp),%eax
mov %eax,0x8(%esp)
movl $0x804a2ef,0x4(%esp)
mov 0x30(%esp),%eax
mov %eax,(%esp)
call 8048860 <__isoc99_sscanf@plt> ; 输入
cmp $0x1,%eax ;至少输入2个数
jg 8048c3b <phase_3+0x31> ;否则就爆炸
call 80490f5 <explode_bomb>
cmpl $0x7,0x18(%esp); <=7
ja 8048c7e <phase_3+0x74> ;如果第一个操作数大于7 ,就爆炸
mov 0x18(%esp),%eax
jmp *0x804a180(,%eax,4) ;后面有截图,可以看到,在这个地址处存放着一些地址,根据输入的eax的值,跳到相应位置
;下面是8种情况 后面依次根据第一个数的内容跳到相应位置
mov $0x90,%eax ;第一个数是0,跳到这里,将0x90放到eax中
jmp 8048c8f <phase_3+0x85>
mov $0x166,%eax
jmp 8048c8f <phase_3+0x85>
mov $0x160,%eax
jmp 8048c8f <phase_3+0x85>
mov $0x1e2,%eax
jmp 8048c8f <phase_3+0x85>
mov $0x2c5,%eax
jmp 8048c8f <phase_3+0x85>
mov $0x2e0,%eax
jmp 8048c8f <phase_3+0x85>
mov $0x3d8,%eax
jmp 8048c8f <phase_3+0x85>
call 80490f5 <explode_bomb>
mov $0x0,%eax
jmp 8048c8f <phase_3+0x85>
mov $0x15d,%eax
;---------------------------------------------------------所有的条件最后都汇总到这里,判断第二个数
cmp 0x1c(%esp),%eax ;将输入的第二个数字和从对应地址赋值的数比较,不相等就爆炸
je 8048c9a <phase_3+0x90>
call 80490f5 <explode_bomb>
add $0x2c,%esp
ret
用gdb看看内存里位置0x804a180位置处的内容是:
里面存的都是代码的地址,也就是要跳的位置
这个是条件判断,根据输入的第一个数,看第二个数是不是和程序的赋给的内容一样。
所以一共有8种情况,任意输入一种即可
如果第一个数是0,跳到0x8048c4d位置,把0x90给了eax,所以第二个就是0x90,也就是十进制的144
输入 0 144
通过
Phase_4:
这个是一个递归
代码截图:
代码分析:
sub $0x2c,%esp
lea 0x1c(%esp),%eax
mov %eax,0xc(%esp)
lea 0x18(%esp),%eax
mov %eax,0x8(%esp)
movl $0x804a2ef,0x4(%esp)
mov 0x30(%esp),%eax
mov %eax,(%esp)
call 8048860 <__isoc99_sscanf@plt>;输入
cmp $0x2,%eax ;输入两个数,不是的话就爆炸
jne 8048d2e <phase_4+0x33>
cmpl $0xe,0x18(%esp) ;第一个数小于等于14,否则爆炸
jbe 8048d33 <phase_4+0x38>
call 80490f5 <explode_bomb>
movl $0xe,0x8(%esp) ;旧[esp+8] = e
movl $0x0,0x4(%esp) ;旧[esp+4] = 0
mov 0x18(%esp),%eax
mov %eax,(%esp)
call 8048c9e <func4> ;调用func4
cmp $0xb,%eax ;func4返回的一定是11,否则爆炸
jne 8048d5b <phase_4+0x60>
cmpl $0xb,0x1c(%esp) ;第二个数一定是11,否则就爆炸
je 8048d60 <phase_4+0x65>
call 80490f5 <explode_bomb>
add $0x2c,%esp
ret
phase_4函数其中还调用了一个函数func4(),func4()返回的值是11,我们进行推断可以得出输入时0 11或者1 11
分析着很麻烦,发现第二个数必须是0xb,一定是11,前面有第一个数必须小于15,一共没几个情况,一个个试一下呗!发现很幸运的输入 0 11就过了!
Phase5:
代码如下:
代码分析:
08048d64 <phase_5>:
8048d64: 53 push %ebx
8048d65: 83 ec 18 sub $0x18,%esp
8048d68: 8b 5c 24 20 mov 0x20(%esp),%ebx
8048d6c: 89 1c 24 mov %ebx,(%esp)
8048d6f: e8 57 02 00 00 call 8048fcb <string_length>
8048d74: 83 f8 06 cmp $0x6,%eax ;输入字符串长度一定是6,否则爆炸
8048d77: 74 05 je 8048d7e <phase_5+0x1a>
8048d79: e8 77 03 00 00 call 80490f5 <explode_bomb>
8048d7e: ba 00 00 00 00 mov $0x0,%edx ;赋初值
8048d83: b8 00 00 00 00 mov $0x0,%eax ;赋初值
8048d88: 0f b6 0c 03 movzbl (%ebx,%eax,1),%ecx ;0扩展,
; ecx = ebx + eax
8048d8c: 83 e1 0f and $0xf,%ecx
;ecx = ecx & 0xf ,和0xf按位与,控制在0~15之间
8048d8f: 03 14 8d a0 a1 04 08 add 0x804a1a0(,%ecx,4),%edx ;
;在内存中相应位置取出数字加到edx 中
8048d96: 83 c0 01 add $0x1,%eax
;eax自加
8048d99: 83 f8 06 cmp $0x6,%eax
;eax和6比较,一共循环6次
8048d9c: 75 ea jne 8048d88 <phase_5+0x24>
8048d9e: 83 fa 2d cmp $0x2d,%edx
;最后的edx中一定是0x2d,我们要找到这样的组合即可
8048da1: 74 05 je 8048da8 <phase_5+0x44>
8048da3: e8 4d 03 00 00 call 80490f5 <explode_bomb>
8048da8: 83 c4 18 add $0x18,%esp
8048dab: 5b pop %ebx
8048dac: c3 ret
经过分析得出,我们要输入6个字符,然后对每个字符对16取模之后得到[0,15]的数,推测内存里应该有16个数,代码里还有一个内存地址,把取模后的数作为偏移地址,在内存中找到相应的值,加到一起,和是45就可以。
看一下内存地址有啥
前16个看起来靠谱,和我们得到的结论一样。
现在只需要凑出6个数之和是45就行,凑个777888,然后在内存里这个数组中看一下,有7和8,下标分别是9和13,然后这6个数都是对16取模之后的下标,让他们几个都同时加32,得到41 41 41 45 45 45,对应的ascii表中的字符就是 )))--- ,输入后通过
同理,然后还可以加32 + 16,得到 57 57 57 61 61 61,对应的ascii码表中的字符就是===999,输入后:
通过
Phase_6:
代码分析:
输入6个数后,先进行了两个判断:1、判断是否都是在[1,6]上的数 2、判断有没有重复
;判断是否在[1,6]内
8048dcf: 83 e8 01 sub $0x1,%eax ;eax--
8048dd2: 83 f8 05 cmp $0x5,%eax ;如果eax是小于等于0的数,减一后和5无符号比较,是大于5的;
;如果eax是大于6的数,减一后还是大于5;
;所以 输入的数字一定要 在区间 [ 1 , 6 ] 内
8048dd5: 76 05 jbe 8048ddc <phase_6+0x2f> ;无符号比较<=
8048dd7: e8 19 03 00 00 call 80490f5 <explode_bomb>
8048ddc: 83 c6 01 add $0x1,%esi ;esi作为偏移
8048ddf: 83 fe 06 cmp $0x6,%esi ;一共循环6次
8048de2: 75 07 jne 8048deb <phase_6+0x3e>
8048de4: bb 00 00 00 00 mov $0x0,%ebx ;6次循环结束
8048de9: eb 38 jmp 8048e23 <phase_6+0x76> ;跳出循环
;判断是都有重复的
8048deb: 89 f3 mov %esi,%ebx
8048ded: 8b 44 9c 10 mov 0x10(%esp,%ebx,4),%eax ;根据ebx取出一个输入的数
8048df1: 39 44 b4 0c cmp %eax,0xc(%esp,%esi,4) ;取出的每个数都依次和其他的每个数字比较
8048df5: 75 05 jne 8048dfc <phase_6+0x4f>
8048df7: e8 f9 02 00 00 call 80490f5 <explode_bomb> ;每个数字不能重复,否则爆炸
8048dfc: 83 c3 01 add $0x1,%ebx
8048dff: 83 fb 05 cmp $0x5,%ebx
8048e02: 7e e9 jle 8048ded <phase_6+0x40>
8048e04: eb c5 jmp 8048dcb <phase_6+0x1e>
;判重结束;
接着根据输入构建一个链表
8048e12: ba 3c c1 04 08 mov $0x804c13c,%edx ;将第一个元素的下标放到edx里
8048e17: 89 54 b4 28 mov %edx,0x28(%esp,%esi,4) ;把下一个的地址放到栈里
8048e1b: 83 c3 01 add $0x1,%ebx ;ebx++
8048e1e: 83 fb 06 cmp $0x6,%ebx ;循环6次
8048e21: 74 17 je 8048e3a <phase_6+0x8d>
;两个判断结束后跳到这里
8048e23: 89 de mov %ebx,%esi ;esi = ebx
8048e25: 8b 4c 9c 10 mov 0x10(%esp,%ebx,4),%ecx ;依次取出输入的几个数
8048e29: 83 f9 01 cmp $0x1,%ecx
8048e2c: 7e e4 jle 8048e12 <phase_6+0x65> ;如果取出的数是1,跳到0x8048e12
8048e2e: b8 01 00 00 00 mov $0x1,%eax
8048e33: ba 3c c1 04 08 mov $0x804c13c,%edx ;将第一个元素的下标放到edx里
8048e38: eb cc jmp 8048e06 <phase_6+0x59>
;根据输入的6个数构建一个链表
8048e3a: 8b 5c 24 28 mov 0x28(%esp),%ebx ;取出下一个地址
8048e3e: 8d 44 24 2c lea 0x2c(%esp),%eax
8048e42: 8d 74 24 40 lea 0x40(%esp),%esi
8048e46: 89 d9 mov %ebx,%ecx
8048e48: 8b 10 mov (%eax),%edx ;将下一个地址放到edx中
8048e4a: 89 51 08 mov %edx,0x8(%ecx) ;将这个地址给ecx+8
8048e4d: 83 c0 04 add $0x4,%eax ;指向下一个元素
8048e50: 39 f0 cmp %esi,%eax ;走到最后一个跳出
8048e52: 74 04 je 8048e58 <phase_6+0xab>
8048e54: 89 d1 mov %edx,%ecx ;把下一个地址给ecx
8048e56: eb f0 jmp 8048e48 <phase_6+0x9b>
8048e58: c7 42 08 00 00 00 00 movl $0x0,0x8(%edx)
8048e5f: be 05 00 00 00 mov $0x5,%esi
要求链表的值是递减的
;这个链表中的值一定要是降序的
8048e64: 8b 43 08 mov 0x8(%ebx),%eax ;下个元素的地址给eax
8048e67: 8b 00 mov (%eax),%eax ;取出下个元素的地址的值
8048e69: 39 03 cmp %eax,(%ebx) ;ebx的值>=eax 成功跳转,否则爆炸;也就是一定要满足降序规则
8048e6b: 7d 05 jge 8048e72 <phase_6+0xc5>
8048e6d: e8 83 02 00 00 call 80490f5 <explode_bomb>
8048e72: 8b 5b 08 mov 0x8(%ebx),%ebx ;读入下一个地址
8048e75: 83 ee 01 sub $0x1,%esi
8048e78: 75 ea jne 8048e64 <phase_6+0xb7> ;zf=0跳,也就是esi不等于1时跳
;esi等于1时结束,循环5次,相继读入下一个元素的值
8048e7a: 83 c4 44 add $0x44,%esp
8048e7d: 5b pop %ebx
8048e7e: 5e pop %esi
8048e7f: c3 ret
用gdb看一下内存里这几个元素里的东西
每个node的结构应该是这样
struct{
int value;
int id;
int* next;
}
根据降序排列,顺序是0x3a9 0x39f 0x32f 0x2a0 0x22f 0x5c,对应的顺序是6 3 4 5 2 1
输入:
通过。
secret phase:
代码分析:
Defuse中:
movl $0x804c4d0,(%esp)
80492a8: e8 b3 f5 ff ff call 8048860 <__isoc99_sscanf@plt>
80492ad: 83 f8 03 cmp $0x3,%eax ;如果检测到输入的内容是3个,就会将内存中的内容取出来进行比较。有内存地址0x804a352
80492b0: 75 35 jne 80492e7 <phase_defused+0x81>
80492b2: c7 44 24 04 52 a3 04 movl $0x804a352,0x4(%esp)
80492b9: 08
80492ba: 8d 44 24 2c lea 0x2c(%esp),%eax
80492be: 89 04 24 mov %eax,(%esp)
80492c1: e8 24 fd ff ff call 8048fea <strings_not_equal>
80492c6: 85 c0 test %eax,%eax
80492c8: 75 1d jne 80492e7 <phase_defused+0x81>
80492ca: c7 04 24 18 a2 04 08 movl $0x804a218,(%esp)
80492d1: e8 1a f5 ff ff call 80487f0 <puts@plt>
80492d6: c7 04 24 40 a2 04 08 movl $0x804a240,(%esp)
80492dd: e8 0e f5 ff ff call 80487f0 <puts@plt>
80492e2: e8 ea fb ff ff call 8048ed1 <secret_phase>
Gdb查看0x804a352:
特殊字符串是 DrEvil
Secrer_phase:
在第四关加上该字符串,在解决6个之后就会出现提示破解秘密phase
调用phase
Fun4函数分析:
经过推测,输入1001
通过。
四、实验结果:
五、实验总结:
通过看汇编真正的能做一点东西,找到flag,这还是很有成就感的,一关一关的很有趣。
汇编中每个语句都是很简单的,但是综合起来,就是有一种感觉,跳上跳下的,到处跳!不过根据汇编读出来一个程序也很有趣,就是不如高级语言读起来容易。
学会使用gdb的基本操作,也找到了一个比较好的插件哈哈peda,可以每一步运行时,都自动打印出stack里的东西,寄存器的东西,代码运行位置等,很方便。还学习了一个新的函数strtol().
通过这个实验也把汇编又复习了一下,收获很多。
标签:++ 自动 number 总结 dac 排列 表示 amp string
原文地址:https://www.cnblogs.com/zhibin123/p/10358955.html