目录
- CS APP的实验2,拆解bomb
- 进入gdb调试模式
- 查看主函数
- phase_1函数
- phase_2函数
- phase_3函数
- phase_4函数
- phase_5
- phase_6
CS APP的实验2,拆解bomb
这里是在linux环境下通过gdb来进行的调试
进入gdb调试模式
终端中输入$ gdb bomb
查看主函数
输入(gdb) list main
发现其中初始化函数initialize_bomb()
后,有两个print
输出提示,然后紧接着一个input = read_line()
,将我们的输入作为参数调用了phase_1
函数。
所以这个phase_1
应该就是对输入进行检查的函数,查看该函数的汇编。
phase_1函数
- 输入
(gdb) disassemble phase_1
输出如下
Dump of assembler code for function phase_1:
0x08048b80 <+0>: push %ebp
0x08048b81 <+1>: mov %esp,%ebp
0x08048b83 <+3>: sub $0x8,%esp
0x08048b86 <+6>: movl $0x8049928,0x4(%esp)
0x08048b8e <+14>: mov 0x8(%ebp),%eax
0x08048b91 <+17>: mov %eax,(%esp)
0x08048b94 <+20>: call 0x8049067
0x08048b99 <+25>: test %eax,%eax
0x08048b9b <+27>: je 0x8048ba2
0x08048b9d <+29>: call 0x804962e
0x08048ba2 <+34>: leave
0x08048ba3 <+35>: ret
End of assembler dump.
- 前两句其中对当前的ebp寄存器内容进行压栈,并将栈指针寄存器esp和基地址寄存器ebp修改为当前栈顶地址。
当前状态
栈底方向^
...
main
...
参数
旧ebp <- ebp, esp寄存器
- 第三行将栈顶指针移动了8个字节
栈底方向^
...
旧ebp <- ebp
空
空 <- esp寄存器
- 然后两条mov指令,分别将两个值移入了空出来的栈位置,其中一个是
0x08049928
,另一个是基于ebp向栈底方向偏移8个字节的栈中内容,也就是我们的参数input
栈底方向^
...
旧ebp <- ebp
0x8049928
0x8(%ebp) <- esp寄存器
- 接着调用了函数
,所以我们对里的内存0x08049928
内容进行查看,就是我们的拆炸弹需要的字符串。
(gdb) x/s 0x8049928
答案是:Why make trillions when we could make... billions?
phase_2函数
- 输入
(gdb) disassemble phase_1
0x08048ba4 <+0>: push %ebp
0x08048ba5 <+1>: mov %esp,%ebp
0x08048ba7 <+3>: sub $0x28,%esp
0x08048baa <+6>: movl $0x0,-0x4(%ebp)
0x08048bb1 <+13>: lea -0x20(%ebp),%eax
0x08048bb4 <+16>: mov %eax,0x4(%esp)
0x08048bb8 <+20>: mov 0x8(%ebp),%eax
0x08048bbb <+23>: mov %eax,(%esp)
0x08048bbe <+26>: call 0x8048fd4
0x08048bc3 <+31>: movl $0x0,-0x8(%ebp)
0x08048bca <+38>: jmp 0x8048bf3
0x08048bcc <+40>: mov -0x8(%ebp),%eax
0x08048bcf <+43>: mov -0x20(%ebp,%eax,4),%edx
0x08048bd3 <+47>: mov -0x8(%ebp),%eax
0x08048bd6 <+50>: add $0x3,%eax
0x08048bd9 <+53>: mov -0x20(%ebp,%eax,4),%eax
0x08048bdd <+57>: cmp %eax,%edx
0x08048bdf <+59>: je 0x8048be6
0x08048be1 <+61>: call 0x804962e
0x08048be6 <+66>: mov -0x8(%ebp),%eax
0x08048be9 <+69>: mov -0x20(%ebp,%eax,4),%eax
0x08048bed <+73>: add %eax,-0x4(%ebp)
0x08048bf0 <+76>: incl -0x8(%ebp)
0x08048bf3 <+79>: cmpl $0x2,-0x8(%ebp)
0x08048bf7 <+83>: jle 0x8048bcc
0x08048bf9 <+85>: cmpl $0x0,-0x4(%ebp)
0x08048bfd <+89>: jne 0x8048c04
0x08048bff <+91>: call 0x804962e
0x08048c04 <+96>: leave
0x08048c05 <+97>: ret
sub $0x28,%esp
首先sp指针向下空出了0x28也就是40B的空间,32位的系统下,共10个单元,为了方便称之为M0~M9
movl $0x0,-0x4(%ebp)
然后给第0h号单元置零,也就是
M0=0
lea -0x20(%ebp),%eax; mov %eax,0x4(%esp)
将第7号单元的地址赋值给第8号单元。也就是
M8=&M7
可以推断,M8可能是个数组指针,M7是数组的首地址。mov 0x8(%ebp),%eax;mov %eax,(%esp);call 0x8048fd4
将参数放入最后一个单元M9,然后调用函数,读取了6个数字。正好10个单元剩下了6个,组成了一个6个元素的数组,应该是放在了M6~M2中,这里我们成为A0~A5。
$movl 0x0,-0x8(%ebp)
将1号单元置零,M1=0
$cmpl 0x2,-0x8(%ebp);jle 0x8048bcc
将M1和2比较,循环条件是小于等于
mov -0x8(%ebp),%eax; mov -0x20(%ebp,%eax,4),%edx
取值ebp-20+4*M1放入edx,其实就是edx=A[M1]
-0x8(%ebp),%eax; add $0x3,%eax; mov -0x20(%ebp,%eax,4),%eax
eax=A[M1+3]
cmp %eax,%edx; je 0x8048be6
; call 0x804962e 比较eax和edx,如果不相等这调用exploed_bomb
mov -0x8(%ebp),%eax; mov -0x20(%ebp,%eax,4),%eax; add %eax,-0x4(%ebp); incl -0x8(%ebp)
将A[M1]写入M0,M1++
经推断,原函数大致如下
void phase_2(char* input){
int result = 0;
int a[6];
input >> a;
for(int i=0;i<=2;i++){
if (a[i]!=a[i+3])
bomb();
result += a[i];
}
if(reuslt == 0)
bomb()
}
所以答案是一个6个元素的数组,a[i]要和a[i+3]相同,且sum(a[0],a[1],a[2])!=0即可。如
1 2 3 1 2 3
或1 1 1 1 1 1
phase_3函数
Dump of assembler code for function phase_3:
0x08048c06 <+0>: push %ebp
0x08048c07 <+1>: mov %esp,%ebp
0x08048c09 <+3>: sub $0x28,%esp
0x08048c0c <+6>: movl $0x0,-0x8(%ebp)
0x08048c13 <+13>: movl $0x0,-0x4(%ebp)
0x08048c1a <+20>: lea -0x10(%ebp),%eax
0x08048c1d <+23>: mov %eax,0xc(%esp)
0x08048c21 <+27>: lea -0xc(%ebp),%eax
0x08048c24 <+30>: mov %eax,0x8(%esp)
0x08048c28 <+34>: movl $0x804995b,0x4(%esp)
0x08048c30 <+42>: mov 0x8(%ebp),%eax
0x08048c33 <+45>: mov %eax,(%esp)
0x08048c36 <+48>: call 0x8048868
0x08048c3b <+53>: mov %eax,-0x4(%ebp)
0x08048c3e <+56>: cmpl $0x1,-0x4(%ebp)
0x08048c42 <+60>: jg 0x8048c49
0x08048c44 <+62>: call 0x804962e
0x08048c49 <+67>: mov -0xc(%ebp),%eax
0x08048c4c <+70>: mov %eax,-0x14(%ebp)
0x08048c4f <+73>: cmpl $0x7,-0x14(%ebp)
0x08048c53 <+77>: ja 0x8048c98
0x08048c55 <+79>: mov -0x14(%ebp),%edx
0x08048c58 <+82>: mov 0x8049964(,%edx,4),%eax
0x08048c5f <+89>: jmp *%eax
0x08048c61 <+91>: addl $0x2cc,-0x8(%ebp)
0x08048c68 <+98>: subl $0x3a9,-0x8(%ebp)
0x08048c6f <+105>: addl $0x12f,-0x8(%ebp)
0x08048c76 <+112>: subl $0xcb,-0x8(%ebp)
0x08048c7d <+119>: addl $0x5d,-0x8(%ebp)
0x08048c81 <+123>: subl $0x2f4,-0x8(%ebp)
0x08048c88 <+130>: addl $0x2f4,-0x8(%ebp)
0x08048c8f <+137>: subl $0x207,-0x8(%ebp)
0x08048c96 <+144>: jmp 0x8048c9d
0x08048c98 <+146>: call 0x804962e
0x08048c9d <+151>: mov -0xc(%ebp),%eax
0x08048ca0 <+154>: cmp $0x5,%eax
0x08048ca3 <+157>: jg 0x8048cad
0x08048ca5 <+159>: mov -0x10(%ebp),%eax
0x08048ca8 <+162>: cmp %eax,-0x8(%ebp)
0x08048cab <+165>: je 0x8048cb2
0x08048cad <+167>: call 0x804962e
0x08048cb2 <+172>: leave
0x08048cb3 <+173>: ret
End of assembler dump.
0x08048c06 ~ 0x08048c36
进行了如下操作,申请10个字(4B/字)的空间,
M0=0, M1=0, M6=&M3, M7=&M2,M8=0x804995b, M9=%ebp+8
(依然是从ebp-0x4到ebp-0x28,每4B作为一个单位),然后调用函数sscanf
。惯例其中M9是我们的input,然后M8未知,M7,M2作为指针参数来传递值,放入M2,M3中,因为参数的逆序入栈的。
我们检查M8(0x804995b)的内容
可以发现,里面存放的是字符串"%d %d"
也就是告诉我们输入两个数字,两个数字正是通过两个指针来实现传递的。
0x08048c3b~0x08048c44
对
sscanf
的结果进行检查,查看是否大于1,否则调用bomb()0x08048c49~0x08048c53
对num1进行检查,要求不大于7。
0x08048c55~0x08048c5f
一条无条件跳转语句,跳转到(num1*4+0x8049964),注意外面的
*
,跳转的目标不是num1*4+0x8049964
,而是该内存中该地址里的值,因为目前num1的范围是不大于7,还有点大0x08048c61~0x08048c96
这部分应该是上一条跳转语句将要跳转的位置,分别是M1加上或减去某个数。
0x08048c9d~0x08048ca3
对num1再进行检查,要求不大于5,这里再次缩小了num1的范围
0x08048ca5~0x08048cab
对M1和num2进行比较,要求相等
所以num2就应该是M1计算的结果,而num1则是小于等于5的值,我们可以取5,计算得num2位-519
phase_4函数
Dump of assembler code for function phase_4:
0x08048ce3 <+0>: push %ebp
0x08048ce4 <+1>: mov %esp,%ebp
0x08048ce6 <+3>: sub $0x28,%esp
0x08048ce9 <+6>: lea -0xc(%ebp),%eax
0x08048cec <+9>: mov %eax,0x8(%esp)
0x08048cf0 <+13>: movl $0x8049984,0x4(%esp)
0x08048cf8 <+21>: mov 0x8(%ebp),%eax
0x08048cfb <+24>: mov %eax,(%esp)
0x08048cfe <+27>: call 0x8048868
0x08048d03 <+32>: mov %eax,-0x4(%ebp)
0x08048d06 <+35>: cmpl $0x1,-0x4(%ebp)
0x08048d0a <+39>: jne 0x8048d13
0x08048d0c <+41>: mov -0xc(%ebp),%eax
0x08048d0f <+44>: test %eax,%eax
0x08048d11 <+46>: jg 0x8048d18
0x08048d13 <+48>: call 0x804962e
0x08048d18 <+53>: mov -0xc(%ebp),%eax
0x08048d1b <+56>: mov %eax,(%esp)
0x08048d1e <+59>: call 0x8048cb4
0x08048d23 <+64>: mov %eax,-0x8(%ebp)
0x08048d26 <+67>: cmpl $0x78,-0x8(%ebp)
0x08048d2a <+71>: je 0x8048d31
0x08048d2c <+73>: call 0x804962e
0x08048d31 <+78>: leave
0x08048d32 <+79>: ret
End of assembler dump.
0x08048ce3~0x08048cfe
进行了如下操作:申请了4B*10的栈空间,将M2(
ebp-0xc
)的地址写入M7(esp+0x8
),将0x8049984
写入M8,将上层传递进来的参数写入M9,然后调用我们对
0x8049984
进行检查,里面是字符串%d
,可见是读取一个整型同指针M7写入M20x08048d03~0x08048d06
对
sscanf
的返回值进行检查,要求只输入了一个数值0x08048d0c~0x08048d11
对输入的数进行检查,要求大于0
0x08048d18~0x08048d1e
将输入作为参数,调用func4
检查func4
可以发现,func4是一个求阶乘的递归函数。
0x08048d23~0x08048d2a
将func4的返回值放入M1,对M1和0x78进行比较,要求相等,也就是要求M1==120
我们只需找出阶乘结果位120的数,即答案:5
phase_5
(gdb) disassemble phase_5
Dump of assembler code for function phase_5:
0x08048d33 <+0>: push %ebp
0x08048d34 <+1>: mov %esp,%ebp
0x08048d36 <+3>: sub $0x18,%esp
0x08048d39 <+6>: mov 0x8(%ebp),%eax
0x08048d3c <+9>: mov %eax,(%esp)
0x08048d3f <+12>: call 0x804903d
0x08048d44 <+17>: mov %eax,-0x4(%ebp)
0x08048d47 <+20>: cmpl $0x6,-0x4(%ebp)
0x08048d4b <+24>: je 0x8048d52
0x08048d4d <+26>: call 0x804962e
0x08048d52 <+31>: movl $0x0,-0x8(%ebp)
0x08048d59 <+38>: movl $0x0,-0xc(%ebp)
0x08048d60 <+45>: jmp 0x8048d7e
0x08048d62 <+47>: mov -0xc(%ebp),%eax
0x08048d65 <+50>: add 0x8(%ebp),%eax
0x08048d68 <+53>: movzbl (%eax),%eax
0x08048d6b <+56>: movsbl %al,%eax
0x08048d6e <+59>: and $0xf,%eax
0x08048d71 <+62>: mov 0x804a5c0(,%eax,4),%eax
0x08048d78 <+69>: add %eax,-0x8(%ebp)
0x08048d7b <+72>: incl -0xc(%ebp)
0x08048d7e <+75>: cmpl $0x5,-0xc(%ebp)
0x08048d82 <+79>: jle 0x8048d62
0x08048d84 <+81>: cmpl $0x49,-0x8(%ebp)
0x08048d88 <+85>: je 0x8048d8f
0x08048d8a <+87>: call 0x804962e
0x08048d8f <+92>: leave
0x08048d90 <+93>: ret
End of assembler dump.
0x08048d33~0x08048d44
申请4B*6的栈空间,将输入作为参数调用函数
,并将结果写入M0,即M0=length(input)
0x08048d47~0x08048d4d
对输入长度进行检查,要求长度为6。
0x08048d52~0x08048d60
将M1,M2赋值为0,跳转
0x08048d62~0x08048d6e
将(input+M2)0扩展后放入eax,再取出eax对应地址里的值,因为input其实是个字符串,也就是相当于
eax=input[M2]
,然后截取eax的后8位,符号位扩展写回,最后取后四位。这里因为输入是个ascii码,范围是0~127所以符号位扩展肯定是0扩展。最终这系列操作就相当于取输入每个字符的ascii码值的后四位。0x08048d71~0x08048d78
取基于
0x804a5c0
偏移的内存中的内容,累加到M1中0x08048d7b~0x08048d82
M2自增,判断是否小于等于5,继续循环
0x08048d84
要求累加结果位0x49
检查
0x804a5c0
内容对应的数值:2, 10, 6, 1, 12, 16, 9, 3, 4, 7, 14, 5, 11, 8, 15,
我们取其中6个,让和等于0x49
这里我取下标2,5,5,5,5,7的6个数,只要后四位满足条件即可,所以字符串可以取
255557
phase_6
(gdb) disassemble phase_6
Dump of assembler code for function phase_6:
0x08048e25 <+0>: push %ebp
0x08048e26 <+1>: mov %esp,%ebp
0x08048e28 <+3>: sub $0x18,%esp
0x08048e2b <+6>: movl $0x804a660,-0x8(%ebp)
0x08048e32 <+13>: mov 0x8(%ebp),%eax
0x08048e35 <+16>: mov %eax,(%esp)
0x08048e38 <+19>: call 0x8048858
0x08048e3d <+24>: mov %eax,-0xc(%ebp)
0x08048e40 <+27>: mov -0x8(%ebp),%eax
0x08048e43 <+30>: mov %eax,(%esp)
0x08048e46 <+33>: call 0x8048d91
0x08048e4b <+38>: mov %eax,-0x8(%ebp)
0x08048e4e <+41>: mov -0x8(%ebp),%eax
0x08048e51 <+44>: mov %eax,-0x4(%ebp)
0x08048e54 <+47>: movl $0x1,-0x10(%ebp)
0x08048e5b <+54>: jmp 0x8048e69
0x08048e5d <+56>: mov -0x4(%ebp),%eax
0x08048e60 <+59>: mov 0x8(%eax),%eax
0x08048e63 <+62>: mov %eax,-0x4(%ebp)
0x08048e66 <+65>: incl -0x10(%ebp)
0x08048e69 <+68>: cmpl $0x4,-0x10(%ebp)
0x08048e6d <+72>: jne 0x8048e5d
0x08048e6f <+74>: mov -0x4(%ebp),%eax
0x08048e72 <+77>: mov (%eax),%eax
0x08048e74 <+79>: cmp -0xc(%ebp),%eax
0x08048e77 <+82>: je 0x8048e7e
0x08048e79 <+84>: call 0x804962e
0x08048e7e <+89>: leave
0x08048e7f <+90>: ret
End of assembler dump.
0x08048e25~0x08048e3d
申请4B*6的栈空间,将
0x804a660
放入M1中,将input作为参数调用
,返回结果写入M20x08048e40~0x08048e4b
将M1作为参数,调用fun6,将返回结果写回M1
emmm,fun6还没细看
未完待续,哈哈哈