0x08048ae0 <;0>: push %ebp// 入栈
0x08048ae1 <;1>: mov %esp,%ebp// 形成帧底
0x08048ae3 <;3>: sub $0x18,%esp// esp - 24 生成栈帧
0x08048ae6 <;6>: movl $0x804a128,0x4(%esp)// 将立即数存放在 esp;4 的位置
0x08048aee <;14>: mov 0x8(%ebp),%eax
0x08048af1 <;17>: mov %eax,(%esp)// 准备入口参数
0x08048af4 <;20>: call 0x8048f22 er// 调用函数
0x08048af9 <;25>: test %eax,%eax// 设置标志位 eax与自己按位相与 判断是否等于0
0x08048afb <;27>: je 0x8048b02 <phase_1;34>// 根据标志位判断是否跳转 若ZF == 0 则跳过引爆炸弹的程序
0x08048afd <;29>: call 0x8049145 <explode_bomb>// 炸弹引爆
0x08048b02 <;34>: leave// 退栈
0x08048b03 <;35>: ret// 返回地址
画出调用函数strings_not_equal之前phase_1内部的栈帧情况;
;--------------------------;
| 其他 |(底部)
;--------------------------;
| 输入的参数 |通过eax转移到%ebp ; 0x8的地方
;--------------------------;
| 返回值(%eax) |
;--------------------------;
| |<———— ebp
;--------------------------;
| ... ... |
;--------------------------;
| $0x804a128 |
;--------------------------;
| %ebp ; 0x8 |<———— esp
;--------------------------;
而函数strings_not_equal的功能是判断两个参数是是否相等;所以只需要输入$0x804a128中对应的内容即可
0x08048b04 <;0>: push %ebp
0x08048b05 <;1>: mov %esp,%ebp
0x08048b07 <;3>: push %esi
0x08048b08 <;4>: push %ebx // esi和ebx为调用者保存寄存器;因为后面的循环用到了者两个寄存器;因此在这里需要压栈保存
0x08048b09 <;5>: sub $0x30,%esp///esp-48 生成栈帧
0x08048b0c <;8>: lea -0x20(%ebp),%eax// 加载有效地址
0x08048b0f <;11>: mov %eax,0x4(%esp)
0x08048b13 <;15>: mov 0x8(%ebp),%eax
0x08048b16 <;18>: mov %eax,(%esp)// 准备入口参数
0x08048b19 <;21>: call 0x8049187 <read_six_numbers>// 调用函数读入6个数字
0x08048b1e <;26>: cmpl $0x1,-0x20(%ebp)// 比较立即数0x1与ebp - 32的值 第一个数存放在ebp - 32的位置 应该是0x1
0x08048b22 <;30>: je 0x8048b42 <phase_2;62>// 如果上述比较结果相等;ZF==1;跳转
0x08048b24 <;32>: call 0x8049145 <explode_bomb>// 引爆炸弹
0x08048b29 <;37>: jmp 0x8048b42 <phase_2;62>// 无条件转移
0x08048b2b <;39>: mov -0x4(%ebx),%eax
0x08048b2e <;42>: add %eax,%eax
0x08048b30 <;44>: cmp %eax,(%ebx)
0x08048b32 <;46>: je 0x8048b39 <phase_2;53>// 如果上述比较结果相等;ZF==1;跳转
0x08048b34 <;48>: call 0x8049145 <explode_bomb>// 引爆炸弹
0x08048b39 <;53>: add $0x4,%ebx
0x08048b3c <;56>: cmp %esi,%ebx// 比较esi与ebx寄存器的值
0x08048b3e <;58>: jne 0x8048b2b <phase_2;39>// 如果上述比较结果不相等;ZF==0;跳转
0x08048b40 <;60>: jmp 0x8048b4a <phase_2;70>// 无条件转移
0x08048b42 <;62>: lea -0x1c(%ebp),%ebx// 赋ebx寄存器的初值
0x08048b45 <;65>: lea -0x8(%ebp),%esi// 赋esi寄存器的初值
0x08048b48 <;68>: jmp 0x8048b2b <phase_2;39>// 无条件转移
0x08048b4a <;70>: add $0x30,%esp
0x08048b4d <;73>: pop %ebx
0x08048b4e <;74>: pop %esi
0x08048b4f <;75>: pop %ebp
0x08048b50 <;76>: ret
将读入的6个数字设为数组num[6]中的数;则在调用函数read_six_numbers后的栈帧为;
;--------------------------;
ebp | |
;--------------------------;
ebp-4 | |
;--------------------------;
ebp-8 | |
;--------------------------;
ebp-12|num[6] |<———— esi的初值
;--------------------------;
ebp-16|num[5] |
;--------------------------;
ebp-20|num[4] |
;--------------------------;
ebp-20|num[3] |
;--------------------------;
ebp-24|num[2] |
;--------------------------;
ebp-28|num[1] |<———— ebx 初值
;--------------------------;
ebp-32|num[0] |<———— eax 初值
;--------------------------;
| ... .. |
;--------------------------;
esp | |
;--------------------------;
调用函数后
0x08048b1e <;26>: cmpl $0x1,-0x20(%ebp)// 比较立即数0x1与ebp - 32的值 第一个数存放在ebp - 32的位置 应该是0x1
0x08048b22 <;30>: je 0x8048b42 <phase_2;62>// 如果上述比较结果相等;ZF==1;跳转
判断出num[0] = 0x1
0x08048b42 <;62>: lea -0x1c(%ebp),%ebx// 赋ebx寄存器的初值
0x08048b45 <;65>: lea -0x8(%ebp),%esi// 赋esi寄存器的初值
将ebx与esi赋初值(esi的地址相当于num[6];为了确保只取了6个数;防止溢出)
0x08048b2b <;39>: mov -0x4(%ebx),%eax
0x08048b2e <;42>: add %eax,%eax
0x08048b30 <;44>: cmp %eax,(%ebx)
0x08048b32 <;46>: je 0x8048b39 <phase_2;53>// 如果上述比较结果相等;ZF==1;跳转
0x08048b34 <;48>: call 0x8049145 <explode_bomb>// 引爆炸弹
0x08048b39 <;53>: add $0x4,%ebx
0x08048b3c <;56>: cmp %esi,%ebx// 比较esi与ebx寄存器的值
0x08048b3e <;58>: jne 0x8048b2b <phase_2;39>// 如果上述比较结果不相等;ZF==0;跳转
当第一次循环的时候ebx=num[1],eax=num[0],为了满足46行的条件判断je 0x8048b39 <phase_2;53>必须有num[1] = 2num[0],然后ebx;4直到ebx == esi结束;所以6个数后一个是前一个的2倍;即;
for(int i = 0; i < 5; i;;)
num[i ; 1] = num[i] * 2;// num[0] == 1
最终结果是;
1 2 4 8 16 32
0x08048b51 <;0>: push %ebp
0x08048b52 <;1>: mov %esp,%ebp
0x08048b54 <;3>: sub $0x28,%esp
0x08048b57 <;6>: lea -0x10(%ebp),%eax
0x08048b5a <;9>: mov %eax,0xc(%esp)
0x08048b5e <;13>: lea -0xc(%ebp),%eax
0x08048b61 <;16>: mov %eax,0x8(%esp)
0x08048b65 <;20>: movl $0x804a3d1,0x4(%esp)
0x08048b6d <;28>: mov 0x8(%ebp),%eax
0x08048b70 <;31>: mov %eax,(%esp)
0x08048b73 <;34>: call 0x80487d0 <__isoc99_sscanf;plt>
0x08048b78 <;39>: cmp $0x1,%eax
0x08048b7b <;42>: jg 0x8048b82 <phase_3;49>
0x08048b7d <;44>: call 0x8049145 <explode_bomb>
0x08048b82 <;49>: cmpl $0x7,-0xc(%ebp)
0x08048b86 <;53>: ja 0x8048bc3 <phase_3;114>
0x08048b88 <;55>: mov -0xc(%ebp),%eax
0x08048b8b <;58>: jmp *0x804a1a0(,%eax,4)
0x08048b92 <;65>: mov $0x164,%eax
0x08048b97 <;70>: jmp 0x8048bd4 <phase_3;131>
0x08048b99 <;72>: mov $0x2a2,%eax
0x08048b9e <;77>: jmp 0x8048bd4 <phase_3;131>
0x08048ba0 <;79>: mov $0x255,%eax
0x08048ba5 <;84>: jmp 0x8048bd4 <phase_3;131>
0x08048ba7 <;86>: mov $0x77,%eax
0x08048bac <;91>: jmp 0x8048bd4 <phase_3;131>
0x08048bae <;93>: mov $0x30e,%eax
0x08048bb3 <;98>: jmp 0x8048bd4 <phase_3;131>
0x08048bb5 <;100>: mov $0x271,%eax
0x08048bba <;105>: jmp 0x8048bd4 <phase_3;131>
0x08048bbc <;107>: mov $0x389,%eax
0x08048bc1 <;112>: jmp 0x8048bd4 <phase_3;131>
0x08048bc3 <;114>: call 0x8049145 <explode_bomb>
0x08048bc8 <;119>: mov $0x0,%eax
0x08048bcd <;124>: jmp 0x8048bd4 <phase_3;131>
0x08048bcf <;126>: mov $0xa4,%eax
0x08048bd4 <;131>: cmp -0x10(%ebp),%eax
0x08048bd7 <;134>: je 0x8048bde <phase_3;141>
0x08048bd9 <;136>: call 0x8049145 <explode_bomb>
0x08048bde <;141>: leave
0x08048bdf <;142>: ret
在第20行有命令;movl $0x804a3d1,0x4(%esp),将一个立即数送入到esp寄存器中;所以查看立即数代表的值;
说明需要输入两个int型的字符num_1,num_2的分别存放在esp;0x8和esp;0xc处
0x08048b78 <;39>: cmp $0x1,%eax
0x08048b82 <;49>: cmpl $0x7,-0xc(%ebp)
0x08048b86 <;53>: ja 0x8048bc3 <phase_3;114>
说明输入数的个数为2个;而且需要小于等于0x7才行
0x08048b88 <;55>: mov -0xc(%ebp),%eax
0x08048b8b <;58>: jmp *0x804a1a0(,%eax,4)
这段代码是将esp-0xc的值存放到eax中;然后跳转到eax*4 ; *0x804a1a0的地址处;所以我查看了0x804a1a0的内容;
跳转对应地址的代码为;
0x08048b97 <;70>: jmp 0x8048bd4 <phase_3;131>
0x08048b99 <;72>: mov $0x2a2,%eax
0x08048b9e <;77>: jmp 0x8048bd4 <phase_3;131>
0x08048ba0 <;79>: mov $0x255,%eax
0x08048ba5 <;84>: jmp 0x8048bd4 <phase_3;131>
0x08048ba7 <;86>: mov $0x77,%eax
0x08048bac <;91>: jmp 0x8048bd4 <phase_3;131>
0x08048bae <;93>: mov $0x30e,%eax
0x08048bb3 <;98>: jmp 0x8048bd4 <phase_3;131>
0x08048bb5 <;100>: mov $0x271,%eax
0x08048bba <;105>: jmp 0x8048bd4 <phase_3;131>
0x08048bbc <;107>: mov $0x389,%eax
0x08048bc1 <;112>: jmp 0x8048bd4 <phase_3;131>
0x08048bc3 <;114>: call 0x8049145 <explode_bomb>
0x08048bc8 <;119>: mov $0x0,%eax
0x08048bcd <;124>: jmp 0x8048bd4 <phase_3;131>
0x08048bcf <;126>: mov $0xa4,%eax
所以这也是一个switch语句;参数就是eax里的值;整理下可以得到;
num_1 num_2 eax jmp
0 0x164 = 356 0 0x08048b92
1 0xa4 = 164 1 0x08048bcf
2 0x2a2 = 674 2 0x08048b99
3 0x255 = 597 3 0x08048ba0
4 0x77 = 119 4 0x08048ba7
5 0x30e = 782 5 0x08048bae
6 0x271 = 625 6 0x08048bb5
7 0x389 = 905 7 0x08048bbc
switch语句为 ;
switch(num_1)
{
case 0:
if(num_2 != 356) Boom();
break;
... ...
}
0x08048c42 <;0>: push %ebp
0x08048c43 <;1>: mov %esp,%ebp
0x08048c45 <;3>: sub $0x28,%esp
0x08048c48 <;6>: lea -0x10(%ebp),%eax
0x08048c4b <;9>: mov %eax,0xc(%esp)// 将ebp-16处的地址赋给esp;12 num_2
0x08048c4f <;13>: lea -0xc(%ebp),%eax
0x08048c52 <;16>: mov %eax,0x8(%esp)// 将ebp-12处的地址赋给esp;8 num_1
0x08048c56 <;20>: movl $0x804a3d1,0x4(%esp)
0x08048c5e <;28>: mov 0x8(%ebp),%eax
0x08048c61 <;31>: mov %eax,(%esp)
0x08048c64 <;34>: call 0x80487d0 <__isoc99_sscanf;plt>
0x08048c69 <;39>: cmp $0x2,%eax
0x08048c6c <;42>: jne 0x8048c74 <phase_4;50>// eax != 0x2
0x08048c6e <;44>: cmpl $0xe,-0xc(%ebp)//比较ebp-12处的值与0xe(14)的大小
0x08048c72 <;48>: jbe 0x8048c79 <phase_4;55>
0x08048c74 <;50>: call 0x8049145 <explode_bomb>
0x08048c79 <;55>: movl $0xe,0x8(%esp)// 0xe(14)赋值给esp;8
0x08048c81 <;63>: movl $0x0,0x4(%esp)// 0x0赋值给esp;4
0x08048c89 <;71>: mov -0xc(%ebp),%eax// eax = num_1
0x08048c8c <;74>: mov %eax,(%esp)// 准备参数
0x08048c8f <;77>: call 0x8048be0 <func4>// 调用递归函数
0x08048c94 <;82>: cmp $0x4,%eax// eax为函数func4的返回值
0x08048c97 <;85>: jne 0x8048c9f <phase_4;93>// eax要等于0x4;否则引爆炸弹
0x08048c99 <;87>: cmpl $0x4,-0x10(%ebp)
0x08048c9d <;91>: je 0x8048ca4 <phase_4;98>// -0x10(%ebp) == 0x4
0x08048c9f <;93>: call 0x8049145 <explode_bomb>
0x08048ca4 <;98>: leave
0x08048ca5 <;99>: ret
先查询第20行命令movl $0x804a3d1,0x4(%esp)中立即数;
0x08048c69 <;39>: cmp $0x2,%eax
0x08048c6c <;42>: jne 0x8048c74 <phase_4;50>// eax != 0x2
所以我需要输入两个int型的数num_1; num_2
0x08048c6e <;44>: cmpl $0xe,-0xc(%ebp)//比较ebp-12处的值与0xe(14)的大小
0x08048c72 <;48>: jbe 0x8048c79 <phase_4;55>
0x08048c99 <;87>: cmpl $0x4,-0x10(%ebp)
0x08048c9d <;91>: je 0x8048ca4 <phase_4;98>// -0x10(%ebp) == 0x4
这段代码确定了输入的第一个参数num_1需要小于14;第二个参数num_2为0x4
0x08048c79 <;55>: movl $0xe,0x8(%esp)// 0xe(14)赋值给esp;8
0x08048c81 <;63>: movl $0x0,0x4(%esp)// 0x0赋值给esp;4
0x08048c89 <;71>: mov -0xc(%ebp),%eax// eax = num_1
0x08048c8c <;74>: mov %eax,(%esp)// 准备参数
0x08048c8f <;77>: call 0x8048be0 <func4>// 调用递归函数
0x08048c94 <;82>: cmp $0x4,%eax// eax为函数func4的返回值
0x08048c97 <;85>: jne 0x8048c9f <phase_4;93>// eax要等于0x4;否则引爆炸弹
调用递归函数func4;传入的参数依次为; num_1; 0; 14;而且需要在调用函数后eax的值等于4;所以函数的返回值应为4;所以我需要查看func4函数;
0x8048be0 <func4>: push %ebp
0x8048be1 <func4;1>: mov %esp,%ebp
0x8048be3 <func4;3>: push %esi
0x8048be4 <func4;4>: push %ebx// ebx和esi是被调用保存寄存器
0x8048be5 <func4;5>: sub $0x10,%esp// 开栈
0x8048be8 <func4;8>: mov 0x8(%ebp),%edx// ebp;8 赋值给 edx edx = 14 保存参数f_1
0x8048beb <func4;11>: mov 0xc(%ebp),%eax// 保存f_2
0x8048bee <func4;14>: mov 0x10(%ebp),%ebx// 保存f_3
0x8048bf1 <func4;17>: mov %ebx,%ecx// ecx寄存器其到一个展示保存结果的作用
0x8048bf3 <func4;19>: sub %eax,%ecx// ecx -= eax
0x8048bf5 <func4;21>: mov %ecx,%esi
0x8048bf7 <func4;23>: shr $0x1f,%esi// 逻辑右移31位 esi = ecx >> 31
0x8048bfa <func4;26>: add %esi,%ecx//
0x8048bfc <func4;28>: sar %ecx//算数右移 没有参数默认移动一位
0x8048bfe <func4;30>: add %eax,%ecx// ecx ;= eax
0x8048c00 <func4;32>: cmp %edx,%ecx
0x8048c02 <func4;34>: jle 0x8048c1b <func4;59>// 如果edx >= ecx
0x8048c04 <func4;36>: sub $0x1,%ecx// ecx -= 0x1
0x8048c07 <func4;39>: mov %ecx,0x8(%esp)
0x8048c0b <func4;43>: mov %eax,0x4(%esp)
0x8048c0f <func4;47>: mov %edx,(%esp)
0x8048c12 <func4;50>: call 0x8048be0 <func4>// 递归调用自身
0x8048c17 <func4;55>: add %eax,%eax// eax *= 2;
0x8048c19 <func4;57>: jmp 0x8048c3b <func4;91>
0x8048c1b <func4;59>: mov $0x0,%eax// eax = 0
0x8048c20 <func4;64>: cmp %edx,%ecx
0x8048c22 <func4;66>: jge 0x8048c3b <func4;91>
0x8048c24 <func4;68>: mov %ebx,0x8(%esp)
0x8048c28 <func4;72>: add $0x1,%ecx
0x8048c2b <func4;75>: mov %ecx,0x4(%esp)
0x8048c2f <func4;79>: mov %edx,(%esp)
0x8048c32 <func4;82>: call 0x8048be0 <func4>
0x8048c37 <func4;87>: lea 0x1(%eax,%eax,1),%eax// eax = eax ; eax * 1 ; 1
0x8048c3b <func4;91>: add $0x10,%esp//增加栈
0x8048c3e <func4;94>: pop %ebx
0x8048c3f <func4;95>: pop %esi
0x8048c40 <func4;96>: pop %ebp
0x8048c41 <func4;97>: ret
设传入func4的参数为f_1,f_2,f_3, 则;
0x8048bf1 <func4;17>: mov %ebx,%ecx// ecx寄存器其到一个展示保存结果的作用
0x8048bf3 <func4;19>: sub %eax,%ecx// ecx -= eax
0x8048bf5 <func4;21>: mov %ecx,%esi
0x8048bf7 <func4;23>: shr $0x1f,%esi// 逻辑右移31位 esi = ecx >> 31
0x8048bfa <func4;26>: add %esi,%ecx//
0x8048bfc <func4;28>: sar %ecx//算数右移 没有参数默认移动一位
0x8048bfe <func4;30>: add %eax,%ecx// ecx ;= eax
上面的代码的作用是计算((((f_3 - f_2) >> 31) ; (f_3 - f_2))>> 1) ; f_2的结果保存在寄存器ecx;temp)中;下面是在不同的情况下不同的操作;
(1) 当edx>=ecx(f_1 >= temp)的情况下时;令eax=0;判断edx与ecx的值是否相等;相等直接返回eax的值,不能则递归调用自己;即;(eax = f_2, edx = f_1, ecx = temp)
0x8048c00 <func4;32>: cmp %edx,%ecx
0x8048c02 <func4;34>: jle 0x8048c1b <func4;59>// 如果edx >= ecx
0x8048c1b <func4;59>: mov $0x0,%eax// eax = 0
/* edx == ecx */
0x8048c20 <func4;64>: cmp %edx,%ecx
0x8048c22 <func4;66>: jge 0x8048c3b <func4;91>
0x8048c24 <func4;68>: mov %ebx,0x8(%esp)
0x8048c28 <func4;72>: add $0x1,%ecx
/* edx > ecx */
0x8048c2b <func4;75>: mov %ecx,0x4(%esp)
0x8048c2f <func4;79>: mov %edx,(%esp)
0x8048c32 <func4;82>: call 0x8048be0 <func4>
0x8048c37 <func4;87>: lea 0x1(%eax,%eax,1),%eax// eax = eax ; eax * 1 ; 1
用C语言表示为;
f_2 = 0;
if(temp == f_1)
return f_2;
else
{
temp ;;;
return (func4(f_1, temp, f_3) * 2 ; 1);
}
(2) 当edx>=ecx时;ecx的值减1;在调用自身;
0x8048c04 <func4;36>: sub $0x1,%ecx// ecx -= 0x1
0x8048c07 <func4;39>: mov %ecx,0x8(%esp)
0x8048c0b <func4;43>: mov %eax,0x4(%esp)
0x8048c0f <func4;47>: mov %edx,(%esp)
0x8048c12 <func4;50>: call 0x8048be0 <func4>// 递归调用自身
0x8048c17 <func4;55>: add %eax,%eax// eax *= 2;
用C语言表示为;
temp --;
return (func4(f_1, f_2, temp) * 2);
综上;func4的函数用C语言表示为;
int func4(int f_1, int f_2, int f_3)
{/* %edx %eax %ebx*/
int temp = f_3;
temp = ((((f_3 - f_2) >> 31) ; (f_3 - f_2))>> 1) ; f_2;
if(temp <= f_1)
{
f_2 = 0;
if(temp == f_1)
return f_2;
else
{
temp ;;;
return (func4(f_1, temp, f_3) * 2 ; 1);
}
}
else
{
temp --;
return (func4(f_1, f_2, temp) * 2);
}
}
为了求出第一个输入参数num_1的值;我写了一个程序运行求结果
int func4(int f_1, int f_2, int f_3)
{
int temp = f_3;
temp = ((((f_3 - f_2) >> 31) ; (f_3 - f_2))>> 1) ; f_2;
if(temp <= f_1)
{
f_2 = 0;
if(temp == f_1)
return f_2;
else
{
temp ;;;
return (func4(f_1, temp, f_3) * 2 ; 1);
}
}
else
{
temp --;
return (func4(f_1, f_2, temp) * 2);
}
}
int main()
{
int i = 0, r = 0;
for(i = 0; i < 14; i;;)
{
r = func4(i, 0, 14);
if(r == 0x4)
printf(%d
;,i);
}
return 0;
}
最终的结果是;2 4
0x08048ca6 <;0>: push %ebp
0x08048ca7 <;1>: mov %esp,%ebp
0x08048ca9 <;3>: push %ebx
0x08048caa <;4>: sub $0x14,%esp
0x08048cad <;7>: mov 0x8(%ebp),%ebx
0x08048cb0 <;10>: mov %ebx,(%esp)
0x08048cb3 <;13>: call 0x8048f00 <string_length>
0x08048cb8 <;18>: cmp $0x6,%eax
0x08048cbb <;21>: je 0x8048cc2 <phase_5;28>
0x08048cbd <;23>: call 0x8049145 <explode_bomb>
0x08048cc2 <;28>: mov $0x0,%edx
0x08048cc7 <;33>: mov $0x0,%eax
0x08048ccc <;38>: movzbl (%ebx,%eax,1),%ecx
0x08048cd0 <;42>: and $0xf,%ecx
0x08048cd3 <;45>: add 0x804a1c0(,%ecx,4),%edx
0x08048cda <;52>: add $0x1,%eax
0x08048cdd <;55>: cmp $0x6,%eax
0x08048ce0 <;58>: jne 0x8048ccc <phase_5;38>
0x08048ce2 <;60>: cmp $0x32,%edx
0x08048ce5 <;63>: je 0x8048cec <phase_5;70>
0x08048ce7 <;65>: call 0x8049145 <explode_bomb>
0x08048cec <;70>: add $0x14,%esp
0x08048cef <;73>: pop %ebx
0x08048cf0 <;74>: pop %ebp
0x08048cf1 <;75>: ret
在调用完string_length函数后让eax与6比较可得知需要输入6个字符;为了了解程序运行的过程;我在0x08048ccc<;38>处设置断点;运行程序后输入123456:
查看ecx寄存器的值;
当ecx的值为0x31是继续往下执行;
0x08048cd0 <;42>: and $0xf,%ecx
0x08048cd3 <;45>: add 0x804a1c0(,%ecx,4),%edx
0x08048cda <;52>: add $0x1,%eax
把ecx的值与0xf相与在把地址ecx * 4 ; 0x804a1c0的值赋值给edx寄存器;所以需要查看立即数0x804a1c0里的值;
整理后可得;
and $0xf,%ecx 0x804a1c0(,%ecx,4)
0 0x2 = 2
1 0xa = 10
2 0x6 = 6
3 0x1 = 1
4 0xc = 12
5 0x1 = 1
6 0x9 = 9
7 0x3 = 3
8 0x4 = 4
9 0x7 = 7
10 0xe = 14
11 0x5 = 5
12 0xb = 11
13 0x8 = 8
14 0xf = 15
15 0xd = 13
所以下面这个代码段是一个循环;一次取出我输入的字符存放到ecx中并与 0x相与;然后求出ecx*4;0x804a1c0对应的值;累加后存放到edx中
0x08048ccc <;38>: movzbl (%ebx,%eax,1),%ecx
0x08048cd0 <;42>: and $0xf,%ecx
0x08048cd3 <;45>: add 0x804a1c0(,%ecx,4),%edx
0x08048cda <;52>: add $0x1,%eax
0x08048cdd <;55>: cmp $0x6,%eax
0x08048ce0 <;58>: jne 0x8048ccc <phase_5;38>
当eax的值为6是代表已经运行了6次;此时跳出循环继续往下执行;
0x08048ce2 <;60>: cmp $0x32,%edx
0x08048ce5 <;63>: je 0x8048cec <phase_5;70>
0x08048ce7 <;65>: call 0x8049145 <explode_bomb>
把累加后的edx的值与0x32(50)比较;如果相等则不引爆炸弹。
所以整个phase_5首先依次取出输入的值;不同的值对应不同的数字;循环6次取出所有输入后把累加的结果与50比较;如果相等就不引爆炸弹。
为了简单;取50 = 8 ; 8 ; 8 ; 8 ; 8 ; 10
在循环中若要edx=8;则and $0xf,%ecx的值应该是0xD(13), 同理edx=10对应0x1
查找ASCII码表可知;
最低位为0xD的字符为;‘-’ ‘=’ ‘M’ ‘]’ ‘m’ ‘}’
最低位为0x1的字符为: ‘!’ ‘1’ ‘A’ ‘Q’ ‘a’ ‘q’
所以只要在0xD中任取5个字符;0x1中任取1个字符组成输入的密码即可;即输入MMMMMA
0x08048cf2 <;0>: push %ebp
0x08048cf3 <;1>: mov %esp,%ebp
0x08048cf5 <;3>: push %esi
0x08048cf6 <;4>: push %ebx
0x08048cf7 <;5>: sub $0x40,%esp
0x08048cfa <;8>: lea -0x20(%ebp),%eax
0x08048cfd <;11>: mov %eax,0x4(%esp)
0x08048d01 <;15>: mov 0x8(%ebp),%eax
0x08048d04 <;18>: mov %eax,(%esp)
0x08048d07 <;21>: call 0x8049187 <read_six_numbers>
0x08048d0c <;26>: mov $0x0,%esi// eis初始化
/*判断输入的数应该在1-6之间 而且各不相等*/
0x08048d11 <;31>: mov -0x20(%ebp,%esi,4),%eax
0x08048d15 <;35>: sub $0x1,%eax
0x08048d18 <;38>: cmp $0x5,%eax
0x08048d1b <;41>: jbe 0x8048d22 <phase_6;48>//如果eax减为负数时会大于0x5 这时会引爆
0x08048d1d <;43>: call 0x8049145 <explode_bomb>
0x08048d22 <;48>: add $0x1,%esi// 计数 esi ;;
0x08048d25 <;51>: cmp $0x6,%esi
0x08048d28 <;54>: jne 0x8048d31 <phase_6;63>// 不足6次
0x08048d2a <;56>: mov $0x0,%ebx// 如果完成了6次
0x08048d2f <;61>: jmp 0x8048d69 <phase_6;119>
0x08048d31 <;63>: mov %esi,%ebx// ebx = esi
0x08048d33 <;65>: mov -0x20(%ebp,%ebx,4),%eax
0x08048d37 <;69>: cmp %eax,-0x24(%ebp,%esi,4)
0x08048d3b <;73>: jne 0x8048d42 <phase_6;80>// 如果num[ebx] 等于 num[esi - 1] 引爆
0x08048d3d <;75>: call 0x8049145 <explode_bomb>
0x08048d42 <;80>: add $0x1,%ebx// 计数 ebx ;;
0x08048d45 <;83>: cmp $0x5,%ebx
0x08048d48 <;86>: jle 0x8048d33 <phase_6;65>// ebx <= 0x5
0x08048d4a <;88>: jmp 0x8048d11 <phase_6;31>//
/*小端方式压栈 如;$0x804c154 压入-0x38(%ebp,%esi,4)*/
0x08048d4c <;90>: mov 0x8(%edx),%edx
0x08048d4f <;93>: add $0x1,%eax// eax ;;
0x08048d52 <;96>: cmp %ecx,%eax
0x08048d54 <;98>: jne 0x8048d4c <phase_6;90>// 不相等
0x08048d56 <;100>: jmp 0x8048d5d <phase_6;107>// 相等则跳转
0x08048d58 <;102>: mov $0x804c154,%edx// *** ecx == 1
0x08048d5d <;107>: mov %edx,-0x38(%ebp,%esi,4)// 压栈
0x08048d61 <;111>: add $0x1,%ebx
0x08048d64 <;114>: cmp $0x6,%ebx
0x08048d67 <;117>: je 0x8048d80 <phase_6;142>// 相等
0x08048d69 <;119>: mov %ebx,%esi// ebx = esi
0x08048d6b <;121>: mov -0x20(%ebp,%ebx,4),%ecx// ecx = num[edx]
0x08048d6f <;125>: cmp $0x1,%ecx
0x08048d72 <;128>: jle 0x8048d58 <phase_6;102>// ecx <= 1
0x08048d74 <;130>: mov $0x1,%eax
0x08048d79 <;135>: mov $0x804c154,%edx//
0x08048d7e <;140>: jmp 0x8048d4c <phase_6;90>
/*node5->next = &node4*/
0x08048d80 <;142>: mov -0x38(%ebp),%ebx// num[0]
0x08048d83 <;145>: lea -0x34(%ebp),%eax// num[1]
0x08048d86 <;148>: lea -0x20(%ebp),%esi
0x08048d89 <;151>: mov %ebx,%ecx
0x08048d8b <;153>: mov (%eax),%edx
0x08048d8d <;155>: mov %edx,0x8(%ecx)// ecx;8是节点存储的第三个数据
0x08048d90 <;158>: add $0x4,%eax// 下一个地址
0x08048d93 <;161>: cmp %esi,%eax
0x08048d95 <;163>: je 0x8048d9b <phase_6;169>
0x08048d97 <;165>: mov %edx,%ecx
0x08048d99 <;167>: jmp 0x8048d8b <phase_6;153>
0x08048d9b <;169>: movl $0x0,0x8(%edx)//node6->next = NULL
0x08048da2 <;176>: mov $0x5,%esi
0x08048da7 <;181>: mov 0x8(%ebx),%eax
0x08048daa <;184>: mov (%eax),%eax
0x08048dac <;186>: cmp %eax,(%ebx)
0x08048dae <;188>: jle 0x8048db5 <phase_6;195>// node1.num <= node1.num
0x08048db0 <;190>: call 0x8049145 <explode_bomb>
0x08048db5 <;195>: mov 0x8(%ebx),%ebx
0x08048db8 <;198>: sub $0x1,%esi
0x08048dbb <;201>: jne 0x8048da7 <phase_6;181>// !=0
0x08048dbd <;203>: add $0x40,%esp
0x08048dc0 <;206>: pop %ebx
0x08048dc1 <;207>: pop %esi
0x08048dc2 <;208>: pop %ebp
分别来看 phase_6的汇编;
;1;检查输入的值是否满足要求;
0x08048d11 <;31>: mov -0x20(%ebp,%esi,4),%eax
0x08048d15 <;35>: sub $0x1,%eax
0x08048d18 <;38>: cmp $0x5,%eax
0x08048d1b <;41>: jbe 0x8048d22 <phase_6;48>//如果eax减为负数时会大于0x5 这时会引爆
在这里首先eax–,如果eax的初值小于等于1或大于5;在eax减1后会大于5;所以这里是对输入的值的大小进行判断
0x08048d25 <;51>: cmp $0x6,%esi
0x08048d28 <;54>: jne 0x8048d31 <phase_6;63>// 不足6次
0x08048d31 <;63>: mov %esi,%ebx// ebx = esi
0x08048d33 <;65>: mov -0x20(%ebp,%ebx,4),%eax
0x08048d37 <;69>: cmp %eax,-0x24(%ebp,%esi,4)
0x08048d3b <;73>: jne 0x8048d42 <phase_6;80>// 如果num[ebx] 等于 num[esi - 1]
这里是对相邻两个数之间进行判断;确保相邻两数不等
0x08048d2a <;56>: mov $0x0,%ebx// 如果完成了6次
0x08048d2f <;61>: jmp 0x8048d69 <phase_6;119>
如果所有的数都已经进行了判断则跳出第一个大循环;继续向下运行
;2;把对应数据压栈保存
0x08048d6b <;121>: mov -0x20(%ebp,%ebx,4),%ecx// ecx = num[edx]
0x08048d6f <;125>: cmp $0x1,%ecx
0x08048d72 <;128>: jle 0x8048d58 <phase_6;102>// ecx <= 1
0x08048d74 <;130>: mov $0x1,%eax
0x08048d79 <;135>: mov $0x804c154,%edx//
0x08048d7e <;140>: jmp 0x8048d4c <phase_6;90>
0x08048d4c <;90>: mov 0x8(%edx),%edx
将esi的值保存到edx中;如果ecx>= 1则执行下述操作;
0x08048d4c <;90>: mov 0x8(%edx),%edx
0x08048d4f <;93>: add $0x1,%eax// eax ;;
0x08048d52 <;96>: cmp %ecx,%eax
0x08048d54 <;98>: jne 0x8048d4c <phase_6;90>// 不相等
0x08048d56 <;100>: jmp 0x8048d5d <phase_6;107>// 相等则跳转
如果ecx与eax不等则继续循环;相等后就会跳出循环执行;
0x08048d5d <;107>: mov %edx,-0x38(%ebp,%esi,4)// 压栈
0x08048d61 <;111>: add $0x1,%ebx
0x08048d64 <;114>: cmp $0x6,%ebx
0x08048d67 <;117>: je 0x8048d80 <phase_6;142>// 相等
即把edx里面对应的值压栈保存到ebp ;esi*4 -0x38中;而edx的初值在第102行
0x08048d58 <;102>: mov $0x804c154,%edx// *** ecx == 1
0x08048d5d <;107>: mov %edx,-0x38(%ebp,%esi,4)// 压栈
0x08048d61 <;111>: add $0x1,%ebx
0x08048d64 <;114>: cmp $0x6,%ebx
0x08048d67 <;117>: je 0x8048d80 <phase_6;142>// 相等
当该循环刚开始运行时ecx==1执行上诉操作;所以我需要查看0x804c154:
整理可得;
0x804c154 <node1>: 0x0000010a 0x00000001 0x0804c160
0x804c160 <node2>: 0x00000303 0x00000002 0x0804c16c
0x804c16c <node3>: 0x0000038e 0x00000003 0x0804c178
0x804c154 <node4>: 0x0000036f 0x00000004 0x0804c184
0x804c178 <node5>: 0x00000054 0x00000005 0x0804c190
0x804c190 <node6>: 0x000002cf 0x00000006 0x00000000
// 0x00000000 0x38313631 --->esi
可以看出第二列为节点的编号;
即可设节点结构为;
struct node{
int num_1;
int num;// 序号
int num_2
}
;3;建立节点之间的连接
0x08048d80 <;142>: mov -0x38(%ebp),%ebx// num[0]
0x08048d83 <;145>: lea -0x34(%ebp),%eax// num[1]
0x08048d86 <;148>: lea -0x20(%ebp),%esi
0x08048d89 <;151>: mov %ebx,%ecx
0x08048d8b <;153>: mov (%eax),%edx
赋初值操作;esi的设置是为了防止循环到最后链表指向空
0x08048d8d <;155>: mov %edx,0x8(%ecx)// ecx;8是节点存储的第三个数据
0x08048d90 <;158>: add $0x4,%eax// 下一个地址
0x08048d93 <;161>: cmp %esi,%eax
0x08048d95 <;163>: je 0x8048d9b <phase_6;169>
0x08048d97 <;165>: mov %edx,%ecx
0x08048d99 <;167>: jmp 0x8048d8b <phase_6;153>
在赋初值和循环过程中eax指向的地址一直是ebx后一个的地址,所以在命令mov %edx,0x8(%ecx)中的操作是把前一个节点的0x8处的地址指向下一个节点;所以ecx;0x8应该是指针next,所以当循环结束后链表的结构是;
node1.next->node2.next->node3.next->node4.next->node5.next->node6
链表的第一行应该是节点的值node.num
(4) 排序
0x08048dac <;186>: cmp %eax,(%ebx)
0x08048dae <;188>: jle 0x8048db5 <phase_6;195>// node1.num <= node1.num
表明是升序排序
所以根据节点的值升序排序的节点序列为;5 1 6 2 4 3
0x08048e17 <;0>: push %ebp
0x08048e18 <;1>: mov %esp,%ebp
0x08048e1a <;3>: push %ebx
0x08048e1b <;4>: sub $0x14,%esp
0x08048e1e <;7>: call 0x80491d6 <read_line>
0x08048e23 <;12>: movl $0xa,0x8(%esp)
0x08048e2b <;20>: movl $0x0,0x4(%esp)
0x08048e33 <;28>: mov %eax,(%esp)
0x08048e36 <;31>: call 0x8048830 <strtol;plt>// Strtol有三个参数;第一个是要转化的字符串的地址;第二个是结束符标志;0表明是; ;;第三个是转化的数的进制;a说明最后是10进制。
0x08048e3b <;36>: mov %eax,%ebx
0x08048e3d <;38>: lea -0x1(%eax),%eax
0x08048e40 <;41>: cmp $0x3e8,%eax
0x08048e45 <;46>: jbe 0x8048e4c <secret_phase;53>// eax-1 <= 1000
0x08048e47 <;48>: call 0x8049145 <explode_bomb>
0x08048e4c <;53>: mov %ebx,0x4(%esp)
0x08048e50 <;57>: movl $0x804c0a0,(%esp)
0x08048e57 <;64>: call 0x8048dc4 <fun7>
0x08048e5c <;69>: cmp $0x5,%eax// eax 需要等于0x5
0x08048e5f <;72>: je 0x8048e66 <secret_phase;79>
0x08048e61 <;74>: call 0x8049145 <explode_bomb>
0x08048e66 <;79>: movl $0x804a15c,(%esp)
0x08048e6d <;86>: call 0x8048780 <puts;plt>
0x08048e72 <;91>: call 0x804930e <phase_defused>
0x08048e77 <;96>: add $0x14,%esp
0x08048e7a <;99>: pop %ebx
0x08048e7b <;100>: pop %ebp
0x08048e7c <;101>: ret
首先需要考虑如何在boomlab中调出隐藏关;因此我查看了phase_defused函数的反汇编;(节选)
0x08049341 <;51>: movl $0x804a42b,0x4(%esp)
0x08049349 <;59>: movl $0x804c8f0,(%esp)
0x08049350 <;66>: call 0x80487d0 <__isoc99_sscanf;plt>
0x08049355 <;71>: cmp $0x3,%eax
0x08049358 <;74>: jne 0x804938e <phase_defused;128>
0x0804935a <;76>: movl $0x804a434,0x4(%esp)
首先在71行把eax与0x3比较;我查看其他的命令发现没通过一关eax就会加1;而当eax与0x3相等时代表我正在通过第4关;而且讲义上说开启隐藏关需要在第 4 关输入特殊字符;所以我查看了0x804a42b与0x804a434
所以隐藏关的开启是在第四关写入2 4 DrEvil
0x08048e45 <;46>: jbe 0x8048e4c <secret_phase;53>// eax-1 <= 1000
0x08048e47 <;48>: call 0x8049145 <explode_bomb>
0x08048e4c <;53>: mov %ebx,0x4(%esp)
0x08048e50 <;57>: movl $c,(%esp)
0x08048e57 <;64>: call 0x8048dc4 <fun7>// 参数c 和 ebx
0x08048e5c <;69>: cmp $0x5,%eax// eax 需要等于0x5
0x08048e5f <;72>: je 0x8048e66 <secret_phase;79>
这里说明了需要输入一个数num,满足num <= 1001, 而且这个数需要传入func7函数里;返回值需要为5
于是查看func7的反汇编;
0x08048dc4 <;0>: push %ebp
0x08048dc5 <;1>: mov %esp,%ebp
0x08048dc7 <;3>: push %ebx
0x08048dc8 <;4>: sub $0x14,%esp
0x08048dcb <;7>: mov 0x8(%ebp),%edx// a
0x08048dce <;10>: mov 0xc(%ebp),%ecx// b
0x08048dd1 <;13>: test %edx,%edx
0x08048dd3 <;15>: je 0x8048e0c <fun7;72>// edx == 0
0x08048dd5 <;17>: mov (%edx),%ebx// ebx = a
0x08048dd7 <;19>: cmp %ecx,%ebx
0x08048dd9 <;21>: jle 0x8048dee <fun7;42>// *a <= b
0x08048ddb <;23>: mov %ecx,0x4(%esp)// b
0x08048ddf <;27>: mov 0x4(%edx),%eax
0x08048de2 <;30>: mov %eax,(%esp)// a
0x08048de5 <;33>: call 0x8048dc4 <fun7>// 递归调用
0x08048dea <;38>: add %eax,%eax// eax *= 2
0x08048dec <;40>: jmp 0x8048e11 <fun7;77>// 跳出
0x08048dee <;42>: mov $0x0,%eax
0x08048df3 <;47>: cmp %ecx,%ebx// *a == b
0x08048df5 <;49>: je 0x8048e11 <fun7;77>// 跳出 返回值eax = 0
0x08048df7 <;51>: mov %ecx,0x4(%esp)
0x08048dfb <;55>: mov 0x8(%edx),%eax
0x08048dfe <;58>: mov %eax,(%esp)
0x08048e01 <;61>: call 0x8048dc4 <fun7>
0x08048e06 <;66>: lea 0x1(%eax,%eax,1),%eax//eax = eax*2 ; 0x1
0x08048e0a <;70>: jmp 0x8048e11 <fun7;77>
0x08048e0c <;72>: mov $0xffffffff,%eax// eax = -1
0x08048e11 <;77>: add $0x14,%esp
0x08048e14 <;80>: pop %ebx
0x08048e15 <;81>: pop %ebp
0x08048e16 <;82>: ret
设func7的参数为a,b,则
0x08048dd1 <;13>: test %edx,%edx
0x08048dd3 <;15>: je 0x8048e0c <fun7;72>// edx == 0
0x08048e0c <;72>: mov $0xffffffff,%eax// eax = -1
0x08048e11 <;77>: add $0x14,%esp
如果指针a为空则返会-1
0x08048dd5 <;17>: mov (%edx),%ebx// ebx = a
0x08048dd7 <;19>: cmp %ecx,%ebx
0x08048dd9 <;21>: jle 0x8048dee <fun7;42>// *a <= b
先判断*a,b的大小关系;如果*a <= b则;
/* *a == b*/
0x08048dee <;42>: mov $0x0,%eax
0x08048df3 <;47>: cmp %ecx,%ebx// *a == b
0x08048df5 <;49>: je 0x8048e11 <fun7;77>// 跳出 返回值eax = 0
/* *a < b */
0x08048df7 <;51>: mov %ecx,0x4(%esp)
0x08048dfb <;55>: mov 0x8(%edx),%eax
0x08048dfe <;58>: mov %eax,(%esp)// *a ;0x8
0x08048e01 <;61>: call 0x8048dc4 <fun7>
0x08048e06 <;66>: lea 0x1(%eax,%eax,1),%eax//eax = eax*2 ; 0x1
0x08048e0a <;70>: jmp 0x8048e11 <fun7;77>
如果 *a > b,则;
0x08048ddb <;23>: mov %ecx,0x4(%esp)// b
0x08048ddf <;27>: mov 0x4(%edx),%eax
0x08048de2 <;30>: mov %eax,(%esp)// *a ; 0x4
0x08048de5 <;33>: call 0x8048dc4 <fun7>// 递归调用
0x08048dea <;38>: add %eax,%eax// eax *= 2
0x08048dec <;40>: jmp 0x8048e11 <fun7;77>// 跳出
综上,用C语言描述func7函数;
int fun7(int *a, int b)
{
if(a == NULL)
return -1;
else if(*a <= b)
{
if(*a == b)
return 0;
else
return (2 * fun7(*a ; 8, b) ; 1);
}
else
return (2 * fun7(*a ; 0x4));
}
因为返回值为5 ;则有;
5 = 2 * 2 ; 1
2 = 2 * 1
1 = 2 * 0 ; 1
0: a == b
/*********************/
0
;0x4 / ;0x8
/
1 = 2 * ;0; ; 1
;0x4 / ;0x8
/
2 = 2 * ;1;
/
/
5 = 2 * ;2; ; 1 注;;;标注表示上一级func7的返回值
而参数a地址为0x804c0a0,所以最初输入的值应该是0x804c0a0先加0x8的对应的地址;然后该地址再加0X4后对应的地址在加0x8,最后的这个地址对应的值即为所求;
最终结果为 ;47