ustc-caspp-lab2-bomb
准备
环境配置
推荐使用linux
这里用使用ubuntu,vm中创建即可,如何创建此处不记录
我们需要安装一些工具
sudo apt install gcc
sudo apt install make
sudo apt install gcc-multilib
sudo apt install gdb
资源
csapp官方
http://csapp.cs.cmu.edu/3e/labs.html
ustc 给出的实验代码是修改过的,你的和本文章的可能不一样,这里提供思路
直接看我贴出的汇编代码即可
前置知识
首先是x86寄存器的介绍,下面将会频繁使用

需要注意的是函数调用参数的传递寄存器
esp,ebp,eax
实验提供好编译好的程序是 32位的,我们只需要关心e开头,同时如果如果linux没有32位库,会无法运行,如果没有需要下载
需要注意的汇编指令
跳转指令

leal -28(%ebp), %eax 代表将ebp的内容-28,送到eax中;
mov -0x1c(%ebp,%eax,4),%edx 其中 base(offset, index, i),等于 base+offset+index*i
gdb的使用
gdb bomb 对bomb进行调试
set args xxxx 设置命令参数 等于执行 ./bomb xxx
b point 设置断点,可以是地址,函数名,例如b phase_1,b *0x44ff
run运行,直到断点
continue 继续运行到下一个断点
si 单条指令执行
layout asm 显示汇编
x/参数 <地址> 访问地址的内存,s是输出为字符串,d为输出为十进制,x为输出为十六进制,b、w、l、q控制输出字节,默认是w,四字节,s字符串不受这个控制除外。
delete 删除所有断点
通过简单的读bomb.c的内容可以看到,有6和函数phase_1 到phase_6,每个都需要一输入,然后来判断是否会“爆炸”,也就是能否通过测试。但是我们不知道这6个函数的源码,没发直接判断。我们需要通过反编译bomb,通过汇编层面破解这个程序。
phase1
08048b80 <phase_1>:
8048b80: 55 push %ebp
8048b81: 89 e5 mov %esp,%ebp
8048b83: 83 ec 08 sub $0x8,%esp
8048b86: c7 44 24 04 58 99 04 movl $0x8049958,0x4(%esp)
8048b8d: 08
8048b8e: 8b 45 08 mov 0x8(%ebp),%eax
8048b91: 89 04 24 mov %eax,(%esp)
8048b94: e8 fa 04 00 00 call 8049093 <strings_not_equal>
8048b99: 85 c0 test %eax,%eax
8048b9b: 74 05 je 8048ba2 <phase_1+0x22>
8048b9d: e8 b8 0a 00 00 call 804965a <explode_bomb>
8048ba2: c9 leave
8048ba3: c3 ret
我们输入的是一个字符串,所以寄存器中存的基本都是地址。
我们首先能看到 <strings_not_equal>,其返回结果在eax(默认)中,然后我们通过对eax的判断来决定是否“爆炸”
基本逻辑也就清楚了,用我们的输入和另一个字符串做对比,通过gdb可以看到eax是我们输入的内容,那么$0x8049958就是另一个字符串的地址
查看其值即可

phase2
//测试输入1 2 3 4 5 6
08048ba4 <phase_2>:
8048ba4: 55 push %ebp
8048ba5: 89 e5 mov %esp,%ebp
8048ba7: 83 ec 28 sub $0x28,%esp
8048baa: 8d 45 e4 lea -0x1c(%ebp),%eax
8048bad: 89 44 24 04 mov %eax,0x4(%esp)
8048bb1: 8b 45 08 mov 0x8(%ebp),%eax
8048bb4: 89 04 24 mov %eax,(%esp)
8048bb7: e8 44 04 00 00 call 8049000 <read_six_numbers>
8048bbc: c7 45 fc 01 00 00 00 movl $0x1,-0x4(%ebp) //初始循环变量i值为1
8048bc3: eb 1e jmp 8048be3 <phase_2+0x3f>
8048bc5: 8b 45 fc mov -0x4(%ebp),%eax // eax=-0x4(%ebp) eax=i
8048bc8: 8b 54 85 e4 mov -0x1c(%ebp,%eax,4),%edx //edx=ebp+eax*4-28, edx=arr[i]
8048bcc: 8b 45 fc mov -0x4(%ebp),%eax //eax=i
8048bcf: 48 dec %eax // eax=i--
8048bd0: 8b 44 85 e4 mov -0x1c(%ebp,%eax,4),%eax //eax=ebp+eax*4-28, eax=arr[i],前一个元素
8048bd4: 83 c0 05 add $0x5,%eax //eax=前一个元素+5
8048bd7: 39 c2 cmp %eax,%edx // 相当于判断arr[i]==arr[i-1]+5
8048bd9: 74 05 je 8048be0 <phase_2+0x3c> //相同,进入下一个循环
8048bdb: e8 7a 0a 00 00 call 804965a <explode_bomb> //不相同 bomb
8048be0: ff 45 fc incl -0x4(%ebp) //循环变量++
8048be3: 83 7d fc 05 cmpl $0x5,-0x4(%ebp) //循环退出条件 i<=5
8048be7: 7e dc jle 8048bc5<phase_2+0x21>
8048be9: c9 leave
8048bea: c3 ret
08049000 <read_six_numbers>:
8049000: 55 push %ebp
8049001: 89 e5 mov %esp,%ebp
8049003: 56 push %esi
8049004: 53 push %ebx
8049005: 83 ec 30 sub $0x30,%esp
8049008: 8b 45 0c mov 0xc(%ebp),%eax //eax 是str的地址 “1 2 3 4 5 6”
804900b: 83 c0 14 add $0x14,%eax // ->6 指向6所在的地址
804900e: 8b 55 0c mov 0xc(%ebp),%edx
8049011: 83 c2 10 add $0x10,%edx // ->5
8049014: 8b 4d 0c mov 0xc(%ebp),%ecx
8049017: 83 c1 0c add $0xc,%ecx // ->4
804901a: 8b 5d 0c mov 0xc(%ebp),%ebx
804901d: 83 c3 08 add $0x8,%ebx // ->3
8049020: 8b 75 0c mov 0xc(%ebp),%esi
8049023: 83 c6 04 add $0x4,%esi // ->2
8049026: 89 44 24 1c mov %eax,0x1c(%esp) // 在栈中保存
804902a: 89 54 24 18 mov %edx,0x18(%esp)
804902e: 89 4c 24 14 mov %ecx,0x14(%esp)
8049032: 89 5c 24 10 mov %ebx,0x10(%esp)
8049036: 89 74 24 0c mov %esi,0xc(%esp)
804903a: 8b 45 0c mov 0xc(%ebp),%eax
804903d: 89 44 24 08 mov %eax,0x8(%esp)
8049041: c7 44 24 04 85 9c 04 movl $0x8049c85,0x4(%esp)
8049048: 08
8049049: 8b 45 08 mov 0x8(%ebp),%eax
804904c: 89 04 24 mov %eax,(%esp)
804904f: e8 14 f8 ff ff call 8048868 <sscanf@plt> //格式化为int
8049054: 89 45 f4 mov %eax,-0xc(%ebp)
8049057: 83 7d f4 05 cmpl $0x5,-0xc(%ebp)
804905b: 7f 05 jg 8049062 <read_six_numbers+0x62>
804905d: e8 f8 05 00 00 call 804965a <explode_bomb>
8049062: 83 c4 30 add $0x30,%esp
8049065: 5b pop %ebx
8049066: 5e pop %esi
8049067: 5d pop %ebp
8049068: c3 ret
大部分逻辑都写到了代码注释上,突破点在<read_six_numbers>,也就是读6个数字,展开分析,其中并没有做太多操作,主要是sscanf
C 库函数 int sscanf(const char *str, const char *format, ...) 从字符串读取格式化输入。返回值为成功格式化的个数
接着向下分析,能看到movl $0x1,-0x4(%ebp) 在一个地址上放了0x1,然后进行跳转,然后进行了增加,和5进行判断,再决定跳转。如何符合条件,会执行中间那一段,其又回跳转到8048be0,形成了循环。我们大致能判断这是一个for,在分析for内部干了什么,具体看代码注释
phase3
08048beb <phase_3>:
8048beb: 55 push %ebp
8048bec: 89 e5 mov %esp,%ebp
8048bee: 83 ec 28 sub $0x28,%esp
8048bf1: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%ebp)
8048bf8: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp)
8048bff: 8d 45 f0 lea -0x10(%ebp),%eax
8048c02: 89 44 24 0c mov %eax,0xc(%esp)
8048c06: 8d 45 f4 lea -0xc(%ebp),%eax
8048c09: 89 44 24 08 mov %eax,0x8(%esp)
8048c0d: c7 44 24 04 89 99 04 movl $0x8049989,0x4(%esp) //"%d %d",我们需要两个数字
8048c14: 08
8048c15: 8b 45 08 mov 0x8(%ebp),%eax
8048c18: 89 04 24 mov %eax,(%esp)
8048c1b: e8 48 fc ff ff call 8048868 <sscanf@plt>
8048c20: 89 45 fc mov %eax,-0x4(%ebp)
8048c23: 83 7d fc 01 cmpl $0x1,-0x4(%ebp)
8048c27: 7f 05 jg 8048c2e <phase_3+0x43> //判断格式化的个数是否>1
8048c29: e8 2c 0a 00 00 call 804965a <explode_bomb>
8048c2e: 8b 45 f4 mov -0xc(%ebp),%eax //eax=第一个参数
8048c31: 89 45 ec mov %eax,-0x14(%ebp)
8048c34: 83 7d ec 07 cmpl $0x7,-0x14(%ebp)
8048c38: 77 40 ja 8048c7a <phase_3+0x8f> // >7 bomb
8048c3a: 8b 55 ec mov -0x14(%ebp),%edx //4
8048c3d: 8b 04 95 90 99 04 08 mov 0x8049990(,%edx,4),%eax //
8048c44: ff e0 jmp *%eax
8048c46: 83 45 f8 75 addl $0x75,-0x8(%ebp) //=0 ->-426 -1062
8048c4a: 81 6d f8 a0 03 00 00 subl $0x3a0,-0x8(%ebp) //=1 ->-49b -1179
8048c51: 81 45 f8 40 02 00 00 addl $0x240,-0x8(%ebp) //=2 ->-fb -251
8048c58: 81 6d f8 6e 01 00 00 subl $0x16e,-0x8(%ebp) //=3 ->-33b -827
8048c5f: 83 45 f8 6d addl $0x6d,-0x8(%ebp) //=4 ->-1cd -461
8048c63: 81 6d f8 94 01 00 00 subl $0x194,-0x8(%ebp) //=5 ->-23a -570
8048c6a: 81 45 f8 94 01 00 00 addl $0x194,-0x8(%ebp) //=6 ->-a6 -166
8048c71: 81 6d f8 3a 02 00 00 subl $0x23a,-0x8(%ebp) //=7 ->-23a -570
8048c78: eb 05 jmp 8048c7f <phase_3+0x94>
8048c7a: e8 db 09 00 00 call 804965a <explode_bomb>
8048c7f: 8b 45 f4 mov -0xc(%ebp),%eax //eax=第一个参数
8048c82: 83 f8 05 cmp $0x5,%eax //
8048c85: 7f 08 jg 8048c8f <phase_3+0xa4> // 大于5 bomb
8048c87: 8b 45 f0 mov -0x10(%ebp),%eax //第二个参数
8048c8a: 39 45 f8 cmp %eax,-0x8(%ebp) //对比另一个数时
8048c8d: 74 05 je 8048c94 <phase_3+0xa9>
8048c8f: e8 c6 09 00 00 call 804965a <explode_bomb>
8048c94: c9 leave
8048c95: c3 ret
首先看到sscanf@plt我们知道要格式化参数了,上方有一个地址,我们看一下地址的内容

这个是sscanf的参数,说明我们将会格式化两个数组。
然后我们对返回结果进行判断,是否大于>1,也就是我们必须输出2个以上数字,超出2的数字在格式化的时候会被忽略。
之后我们对第一个参数进行的判断,第一个限制条件 第一个参数 args_1<=7
8048c44处进行了无条件转移,我们看一下eax存的什么,当输入4的时候

转换16进制为0x8048c5f,跳转之后,我们对一个变量【-0x8(%ebp)】进行了一些了操作,最后结果为-461
接着我们我们又对参数进行了判断,第二个条件,args_1<=5
然后判断第二个参数args_2和【-0x8(%ebp)】是否相等,在args_1==4时,其为-461。
其他的可行解相同逻辑,不在展开。
phase_4
08048cca <phase_4>:
8048cca: 55 push %ebp
8048ccb: 89 e5 mov %esp,%ebp
8048ccd: 83 ec 28 sub $0x28,%esp
8048cd0: 8d 45 f4 lea -0xc(%ebp),%eax
8048cd3: 89 44 24 08 mov %eax,0x8(%esp)
8048cd7: c7 44 24 04 b0 99 04 movl $0x80499b0,0x4(%esp) //"%d"
8048cde: 08
8048cdf: 8b 45 08 mov 0x8(%ebp),%eax
8048ce2: 89 04 24 mov %eax,(%esp)
8048ce5: e8 7e fb ff ff call 8048868 <sscanf@plt>
8048cea: 89 45 fc mov %eax,-0x4(%ebp)
8048ced: 83 7d fc 01 cmpl $0x1,-0x4(%ebp)
8048cf1: 75 07 jne 8048cfa <phase_4+0x30> //判断是否格式化一个数字
8048cf3: 8b 45 f4 mov -0xc(%ebp),%eax //eax=输入参数
8048cf6: 85 c0 test %eax,%eax
8048cf8: 7f 05 jg 8048cff <phase_4+0x35> //是否大于0
8048cfa: e8 5b 09 00 00 call 804965a <explode_bomb>
8048cff: 8b 45 f4 mov -0xc(%ebp),%eax //参数
8048d02: 89 04 24 mov %eax,(%esp)
8048d05: e8 8c ff ff ff call 8048c96 <func4>
8048d0a: 89 45 f8 mov %eax,-0x8(%ebp)
8048d0d: 81 7d f8 c1 f6 57 00 cmpl $0x57f6c1,-0x8(%ebp)
8048d14: 74 05 je 8048d1b <phase_4+0x51>
8048d16: e8 3f 09 00 00 call 804965a <explode_bomb>
8048d1b: c9 leave
8048d1c: c3 ret
有了上面的经验,我们又看到了sscanf,看一下内存地址中的数据

这次我们只需要一个数字,然后判断格式化是否成功。
限制参数>0
接下来调用了一个函数,func4,然后用返回值和0x57f6c1进行比较。
接下来我们看func4
08048c96 <func4>:
8048c96: 55 push %ebp
8048c97: 89 e5 mov %esp,%ebp
8048c99: 83 ec 08 sub $0x8,%esp
8048c9c: 83 7d 08 00 cmpl $0x0,0x8(%ebp)
8048ca0: 7f 09 jg 8048cab <func4+0x15> //>0
8048ca2: c7 45 fc 01 00 00 00 movl $0x1,-0x4(%ebp) //
8048ca9: eb 1a jmp 8048cc5 <func4+0x2f>
//参数>0
8048cab: 8b 45 08 mov 0x8(%ebp),%eax //参数
8048cae: 48 dec %eax //参数--
8048caf: 89 04 24 mov %eax,(%esp)
8048cb2: e8 df ff ff ff call 8048c96 <func4> //递归
8048cb7: 89 c2 mov %eax,%edx //
8048cb9: 89 d0 mov %edx,%eax //
8048cbb: c1 e0 03 shl $0x3,%eax // 参数左移3位
8048cbe: 89 c1 mov %eax,%ecx //
8048cc0: 29 d1 sub %edx,%ecx //左移三位结果-原参数,等效x7
8048cc2: 89 4d fc mov %ecx,-0x4(%ebp)
//参数<=0
8048cc5: 8b 45 fc mov -0x4(%ebp),%eax //递归退出
8048cc8: c9 leave
8048cc9: c3 ret
这是一个递归函数
int fun4(int num){
if(num>0){
num--
res=fun4(num)
res*=7
return res
}
return 1
}
也就是func4(n)带条7*n,0x57f6c1=7^8。即,结果为8
phase5
08048d1d <phase_5>:
8048d1d: 55 push %ebp
8048d1e: 89 e5 mov %esp,%ebp
8048d20: 83 ec 18 sub $0x18,%esp
8048d23: 8b 45 08 mov 0x8(%ebp),%eax
8048d26: 89 04 24 mov %eax,(%esp)
8048d29: e8 3b 03 00 00 call 8049069 <string_length>
8048d2e: 89 45 fc mov %eax,-0x4(%ebp)
8048d31: 83 7d fc 06 cmpl $0x6,-0x4(%ebp)
8048d35: 74 05 je 8048d3c <phase_5+0x1f>
8048d37: e8 1e 09 00 00 call 804965a <explode_bomb>
8048d3c: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%ebp) //sum
8048d43: c7 45 f4 00 00 00 00 movl $0x0,-0xc(%ebp) // i
8048d4a: eb 1c jmp 8048d68 <phase_5+0x4b>
8048d4c: 8b 45 f4 mov -0xc(%ebp),%eax //eax=0
8048d4f: 03 45 08 add 0x8(%ebp),%eax //eax直线输入的字符串的char
8048d52: 0f b6 00 movzbl (%eax),%eax //0x00000061 //零扩展 > unsigned int
8048d55: 0f be c0 movsbl %al,%eax //0x00000061 //al是eax的低8位,符号扩展 > int
8048d58: 83 e0 0f and $0xf,%eax //低4位
8048d5b: 8b 04 85 c0 a5 04 08 mov 0x804a5c0(,%eax,4),%eax //arr[i]
8048d62: 01 45 f8 add %eax,-0x8(%ebp) //sum+arr[i]
8048d65: ff 45 f4 incl -0xc(%ebp) / i++
8048d68: 83 7d f4 05 cmpl $0x5,-0xc(%ebp)
8048d6c: 7e de jle 8048d4c <phase_5+0x2f>
8048d6e: 83 7d f8 32 cmpl $0x32,-0x8(%ebp) //sum=50
8048d72: 74 05 je 8048d79 <phase_5+0x5c>
8048d74: e8 e1 08 00 00 call 804965a <explode_bomb>
8048d79: c9 leave
8048d7a: c3 ret
从<string_length>和下面判断,得到我们需要传入6个字符,接下来便利6个字符,将字符的最低8位作符号扩展,然后再将最低4位作为数组的下标【index】,计算sum+=arr[index]。最后sum=50就能解除炸弹。
其中arr为长16数组(超出16也没有意义)

接下来我们就需要凑数了,10+10+10+10+1+9是合适的,对照ascii表找到合适的就可以。此处提供一个aaaacf
phase_6
这是最长的一段代码,也是最难的,一眼看过去众多跳转至零,也就是其中有可能有for和if
08048d7b <phase_6>:
8048d7b: 55 push %ebp
8048d7c: 89 e5 mov %esp,%ebp
8048d7e: 83 ec 48 sub $0x48,%esp
8048d81: c7 45 f0 3c a6 04 08 movl $0x804a63c,-0x10(%ebp)
8048d88: 8d 45 d8 lea -0x28(%ebp),%eax
8048d8b: 89 44 24 04 mov %eax,0x4(%esp)
8048d8f: 8b 45 08 mov 0x8(%ebp),%eax
8048d92: 89 04 24 mov %eax,(%esp) //设置参数
8048d95: e8 66 02 00 00 call 8049000 <read_six_numbers>
8048d9a: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%ebp) // i=0
8048da1: eb 48 jmp 8048deb <phase_6+0x70> -->l6
l1
8048da3: 8b 45 f8 mov -0x8(%ebp),%eax
8048da6: 8b 44 85 d8 mov -0x28(%ebp,%eax,4),%eax //eax=arr[i]
8048daa: 85 c0 test %eax,%eax
8048dac: 7e 0c jle 8048dba <phase_6+0x3f> // <=0 -->l2
8048dae: 8b 45 f8 mov -0x8(%ebp),%eax
8048db1: 8b 44 85 d8 mov -0x28(%ebp,%eax,4),%eax //eax=arr[i]
8048db5: 83 f8 06 cmp $0x6,%eax
8048db8: 7e 05 jle 8048dbf <phase_6+0x44> //<=6 -->l3
l2
8048dba: e8 9b 08 00 00 call 804965a <explode_bomb>
l3
8048dbf: 8b 45 f8 mov -0x8(%ebp),%eax
8048dc2: 40 inc %eax //i++
8048dc3: 89 45 fc mov %eax,-0x4(%ebp)
8048dc6: eb 1a jmp 8048de2 <phase_6+0x67> -->l5
l4 //
8048dc8: 8b 45 f8 mov -0x8(%ebp),%eax //0
8048dcb: 8b 54 85 d8 mov -0x28(%ebp,%eax,4),%edx //arr[i]
8048dcf: 8b 45 fc mov -0x4(%ebp),%eax //1
8048dd2: 8b 44 85 d8 mov -0x28(%ebp,%eax,4),%eax
8048dd6: 39 c2 cmp %eax,%edx
8048dd8: 75 05 jne 8048ddf <phase_6+0x64>
8048dda: e8 7b 08 00 00 call 804965a <explode_bomb>
8048ddf: ff 45 fc incl -0x4(%ebp)
l5
8048de2: 83 7d fc 05 cmpl $0x5,-0x4(%ebp)
8048de6: 7e e0 jle 8048dc8 <phase_6+0x4d> -->l4
8048de8: ff 45 f8 incl -0x8(%ebp)
l6
8048deb: 83 7d f8 05 cmpl $0x5,-0x8(%ebp) // 5次
8048def: 7e b2 jle 8048da3 <phase_6+0x28> -->l1
8048df1: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%ebp)
8048df8: eb 34 jmp 8048e2e <phase_6+0xb3> -->l10
l7
8048dfa: 8b 45 f0 mov -0x10(%ebp),%eax
8048dfd: 89 45 f4 mov %eax,-0xc(%ebp)
8048e00: c7 45 fc 01 00 00 00 movl $0x1,-0x4(%ebp)
8048e07: eb 0c jmp 8048e15 <phase_6+0x9a> -->l9
l8
8048e09: 8b 45 f4 mov -0xc(%ebp),%eax
8048e0c: 8b 40 08 mov 0x8(%eax),%eax
8048e0f: 89 45 f4 mov %eax,-0xc(%ebp)
8048e12: ff 45 fc incl -0x4(%ebp)
l9
8048e15: 8b 45 f8 mov -0x8(%ebp),%eax
8048e18: 8b 44 85 d8 mov -0x28(%ebp,%eax,4),%eax
8048e1c: 3b 45 fc cmp -0x4(%ebp),%eax
8048e1f: 7f e8 jg 8048e09 <phase_6+0x8e> -->l8
8048e21: 8b 55 f8 mov -0x8(%ebp),%edx
8048e24: 8b 45 f4 mov -0xc(%ebp),%eax
8048e27: 89 44 95 c0 mov %eax,-0x40(%ebp,%edx,4)
8048e2b: ff 45 f8 incl -0x8(%ebp)
l10
8048e2e: 83 7d f8 05 cmpl $0x5,-0x8(%ebp)
8048e32: 7e c6 jle 8048dfa <phase_6+0x7f> -->l7
8048e34: 8b 45 c0 mov -0x40(%ebp),%eax
8048e37: 89 45 f0 mov %eax,-0x10(%ebp)
8048e3a: 8b 45 f0 mov -0x10(%ebp),%eax
8048e3d: 89 45 f4 mov %eax,-0xc(%ebp)
8048e40: c7 45 f8 01 00 00 00 movl $0x1,-0x8(%ebp)
8048e47: eb 19 jmp 8048e62 <phase_6+0xe7> -->l12
l11
8048e49: 8b 45 f8 mov -0x8(%ebp),%eax
8048e4c: 8b 54 85 c0 mov -0x40(%ebp,%eax,4),%node2
8048e50: 8b 45 f4 mov -0xc(%ebp),%eax //nod1
8048e53: 89 50 08 mov %edx,0x8(%eax)
8048e56: 8b 45 f4 mov -0xc(%ebp),%eax
8048e59: 8b 40 08 mov 0x8(%eax),%eax
8048e5c: 89 45 f4 mov %eax,-0xc(%ebp)
8048e5f: ff 45 f8 incl -0x8(%ebp)
l12
8048e62: 83 7d f8 05 cmpl $0x5,-0x8(%ebp)
8048e66: 7e e1 jle 8048e49 <phase_6+0xce> -->l11
8048e68: 8b 45 f4 mov -0xc(%ebp),%eax
8048e6b: c7 40 08 00 00 00 00 movl $0x0,0x8(%eax)
8048e72: 8b 45 f0 mov -0x10(%ebp),%eax
8048e75: 89 45 f4 mov %eax,-0xc(%ebp)
8048e78: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%ebp)
8048e7f: eb 22 jmp 8048ea3 <phase_6+0x128> -->l15
l13
8048e81: 8b 45 f4 mov -0xc(%ebp),%eax
8048e84: 8b 10 mov (%eax),%edx
8048e86: 8b 45 f4 mov -0xc(%ebp),%eax
8048e89: 8b 40 08 mov 0x8(%eax),%eax
8048e8c: 8b 00 mov (%eax),%eax
8048e8e: 39 c2 cmp %eax,%edx
8048e90: 7d 05 jge 8048e97 <phase_6+0x11c> --l14
8048e92: e8 c3 07 00 00 call 804965a <explode_bomb>
l14
8048e97: 8b 45 f4 mov -0xc(%ebp),%eax
8048e9a: 8b 40 08 mov 0x8(%eax),%eax
8048e9d: 89 45 f4 mov %eax,-0xc(%ebp)
8048ea0: ff 45 f8 incl -0x8(%ebp)
l15
8048ea3: 83 7d f8 04 cmpl $0x4,-0x8(%ebp)
8048ea7: 7e d8 jle 8048e81 <phase_6+0x106> -->l13
8048ea9: c9 leave
8048eaa: c3 ret
首先是两个循环嵌套,对输入的数字最判断,1-6之间(包含),不能有重复元素
从8048e47开始我们进入了新的循环 l11为其循环体,参数倒过来倒过去看不清在干啥。先看看eax和edx中存了啥


eax地址指向一个node,那我们可以合理猜测这步在构建链表。
我们跳出这个循环,直接看他构建好的链表什么样子,记着 0x804a63c 是node1地址.在8048e68打上断点查看

明显能看出第三列是链接地址。前面是数据
接着就进入下一个循环,l13是其循环体
比较的是两个节点前4个字节的大小,并且要保证 前一个节点大于等于后一个
节点数据如下
0x804a63c <node1>: 0x00000234 0x00000001 0x0804a630 0x000003e9
0x804a630 <node2>: 0x00000180 0x00000002 0x0804a624 0x00000234
0x804a624 <node3>: 0x00000312 0x00000003 0x0804a618 0x00000180
0x804a618 <node4>: 0x00000189 0x00000004 0x0804a60c 0x00000312
0x804a60c <node5>: 0x0000011e 0x00000005 0x0804a600 0x00000189
0x804a600 <node6>: 0x00000189 0x00000006 0x00000000 0x0000011e
重新排列是其满足条件,3,1,4,6,2,5 或者3,1,6,4,2,5
secret_phase
这部分在bomb.c中做出来提示,我们在汇编中在phase_6下方有个func7。这个显然是有用的。
向上追溯发现在<secret_phase>调用,<secret_phase>在<phase_defused>中调用
先看<phase_defused>
08049684 <phase_defused>:
8049684: 55 push %ebp
8049685: 89 e5 mov %esp,%ebp
8049687: 83 ec 78 sub $0x78,%esp
804968a: a1 4c a8 04 08 mov 0x804a84c,%eax //
804968f: 83 f8 06 cmp $0x6,%eax // 输入的行数,也就是我们完成6个才能开启
8049692: 75 6e jne 8049702 <phase_defused+0x7e>
8049694: b8 50 a9 04 08 mov $0x804a950,%eax //0x804a950 对应phase_4输入
8049699: 89 c2 mov %eax,%edx
804969b: 8d 45 ac lea -0x54(%ebp),%eax
804969e: 89 44 24 0c mov %eax,0xc(%esp)
80496a2: 8d 45 a8 lea -0x58(%ebp),%eax
80496a5: 89 44 24 08 mov %eax,0x8(%esp)
80496a9: c7 44 24 04 50 9e 04 movl $0x8049e50,0x4(%esp) //"%d %s"
80496b0: 08
80496b1: 89 14 24 mov %edx,(%esp)
80496b4: e8 af f1 ff ff call 8048868 <sscanf@plt>
80496b9: 89 45 fc mov %eax,-0x4(%ebp)
80496bc: 83 7d fc 02 cmpl $0x2,-0x4(%ebp)
80496c0: 75 34 jne 80496f6 <phase_defused+0x72>
80496c2: c7 44 24 04 56 9e 04 movl $0x8049e56,0x4(%esp) //"austinpowers"
80496c9: 08
80496ca: 8d 45 ac lea -0x54(%ebp),%eax
80496cd: 89 04 24 mov %eax,(%esp)
80496d0: e8 be f9 ff ff call 8049093 <strings_not_equal>
80496d5: 85 c0 test %eax,%eax
80496d7: 75 1d jne 80496f6 <phase_defused+0x72>
80496d9: c7 04 24 64 9e 04 08 movl $0x8049e64,(%esp)
80496e0: e8 e3 f0 ff ff call 80487c8 <puts@plt>
80496e5: c7 04 24 8c 9e 04 08 movl $0x8049e8c,(%esp)
80496ec: e8 d7 f0 ff ff call 80487c8 <puts@plt>
80496f1: e8 23 f8 ff ff call 8048f19 <secret_phase>
80496f6: c7 04 24 c4 9e 04 08 movl $0x8049ec4,(%esp)
80496fd: e8 c6 f0 ff ff call 80487c8 <puts@plt>
8049702: c9 leave
8049703: c3 ret
其中有参数检查我们输入的行数,等于6,也就解决了6个问题才能触发这个secret。
然后我们打印0x804a950内容,会发现他和phase_4的输入是相同。
根据下面的格式化,我们确定pahse_4需要输入两个字符,一个数字一个字符串
且字符串等于austinpowers,保存在了0x8049e56中。
满足这个条件之后我们才能进入<secret_phase>
再看<secret_phase>
08048f19 <secret_phase>:
8048f19: 55 push %ebp
8048f1a: 89 e5 mov %esp,%ebp
8048f1c: 83 ec 18 sub $0x18,%esp
8048f1f: e8 aa 03 00 00 call 80492ce <read_line>
8048f24: 89 45 f4 mov %eax,-0xc(%ebp)
8048f27: 8b 45 f4 mov -0xc(%ebp),%eax
8048f2a: 89 04 24 mov %eax,(%esp)
8048f2d: e8 26 f9 ff ff call 8048858 <atoi@plt> //atoi 说明我们需要输入一个数
8048f32: 89 45 f8 mov %eax,-0x8(%ebp)
8048f35: 83 7d f8 00 cmpl $0x0,-0x8(%ebp)
8048f39: 7e 09 jle 8048f44 <secret_phase+0x2b> //输入参数要>0
8048f3b: 81 7d f8 e9 03 00 00 cmpl $0x3e9,-0x8(%ebp) //输入参数要<=1001
8048f42: 7e 05 jle 8048f49 <secret_phase+0x30>
8048f44: e8 11 07 00 00 call 804965a <explode_bomb>
8048f49: 8b 45 f8 mov -0x8(%ebp),%eax
8048f4c: 89 44 24 04 mov %eax,0x4(%esp) //输入参数
8048f50: c7 04 24 f0 a6 04 08 movl $0x804a6f0,(%esp) //<n1>类型,应该是一个结构体
8048f57: e8 4f ff ff ff call 8048eab <fun7>
8048f5c: 89 45 fc mov %eax,-0x4(%ebp)
8048f5f: 83 7d fc 07 cmpl $0x7,-0x4(%ebp) //fun7返回结果要等于7
8048f63: 74 05 je 8048f6a <secret_phase+0x51>
8048f65: e8 f0 06 00 00 call 804965a <explode_bomb>
8048f6a: c7 04 24 b4 99 04 08 movl $0x80499b4,(%esp)
8048f71: e8 52 f8 ff ff call 80487c8 <puts@plt>
8048f76: e8 09 07 00 00 call 8049684 <phase_defused>
8048f7b: c9 leave
8048f7c: c3 ret
解释写到注释中,也就是我们还需要输入一行,且是一个数,大于0小于等于1001。
将这个数和n1结构体传入fun7,判断返回结果是否是7。
而且这个结构体是个二叉树类型,能明显看出有两个地址

最后看fun7
08048eab <fun7>:
8048eab: 55 push %ebp
8048eac: 89 e5 mov %esp,%ebp
8048eae: 83 ec 0c sub $0xc,%esp
8048eb1: 83 7d 08 00 cmpl $0x0,0x8(%ebp)
8048eb5: 75 09 jne 8048ec0 <fun7+0x15>
8048eb7: c7 45 fc ff ff ff ff movl $0xffffffff,-0x4(%ebp) //return -1
8048ebe: eb 54 jmp 8048f14 <fun7+0x69>
8048ec0: 8b 45 08 mov 0x8(%ebp),%eax
8048ec3: 8b 00 mov (%eax),%eax
8048ec5: 3b 45 0c cmp 0xc(%ebp),%eax
8048ec8: 7e 1c jle 8048ee6 <fun7+0x3b>
8048eca: 8b 45 08 mov 0x8(%ebp),%eax
8048ecd: 8b 50 04 mov 0x4(%eax),%edx
8048ed0: 8b 45 0c mov 0xc(%ebp),%eax
8048ed3: 89 44 24 04 mov %eax,0x4(%esp)
8048ed7: 89 14 24 mov %edx,(%esp)
8048eda: e8 cc ff ff ff call 8048eab <fun7>
8048edf: 01 c0 add %eax,%eax // return res*2
8048ee1: 89 45 fc mov %eax,-0x4(%ebp)
8048ee4: eb 2e jmp 8048f14 <fun7+0x69>
8048ee6: 8b 45 08 mov 0x8(%ebp),%eax
8048ee9: 8b 00 mov (%eax),%eax
8048eeb: 3b 45 0c cmp 0xc(%ebp),%eax
8048eee: 75 09 jne 8048ef9 <fun7+0x4e>
8048ef0: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp)// return 0
8048ef7: eb 1b jmp 8048f14 <fun7+0x69>
8048ef9: 8b 45 08 mov 0x8(%ebp),%eax
8048efc: 8b 50 08 mov 0x8(%eax),%edx
8048eff: 8b 45 0c mov 0xc(%ebp),%eax
8048f02: 89 44 24 04 mov %eax,0x4(%esp)
8048f06: 89 14 24 mov %edx,(%esp)
8048f09: e8 9d ff ff ff call 8048eab <fun7>
8048f0e: 01 c0 add %eax,%eax
8048f10: 40 inc %eax // return res*2+1
8048f11: 89 45 fc mov %eax,-0x4(%ebp)
8048f14: 8b 45 fc mov -0x4(%ebp),%eax
8048f17: c9 leave
8048f18: c3 ret
这又是一个递归函数
int fun(node,c){
if node==0{
return -1
}else{
if node=c{
return 0
}
if node<c{
res=d(node.right,c)
return res*2+1
}
if node >c {
res=d(node.left,c)
return 2*res
}
}
}
然后我们需要画出上面n1为根的二叉树

可以看出1001是符合条件的
本文来自博客园,作者:北方Cc,转载请注明原文链接:https://www.cnblogs.com/beifangcc/p/17324819.html

浙公网安备 33010602011771号