|
|
@ -0,0 +1,971 @@ |
|
|
|
# All |
|
|
|
This is bomb 121. |
|
|
|
|
|
|
|
It belongs to 10225501432 (10225501432@stu.ecnu.edu.cn) |
|
|
|
|
|
|
|
|
|
|
|
# phase_1 |
|
|
|
|
|
|
|
## 查看phase_1汇编 |
|
|
|
|
|
|
|
~~~python |
|
|
|
=> 0x0000555555401204 <+0>: sub $0x8,%rsp |
|
|
|
0x0000555555401208 <+4>: lea 0x1801(%rip),%rsi # 0x555555402a10 |
|
|
|
0x000055555540120f <+11>: call 0x55555540172b <strings_not_equal> |
|
|
|
0x0000555555401214 <+16>: test %eax,%eax |
|
|
|
0x0000555555401216 <+18>: jne 0x55555540121d <phase_1+25> |
|
|
|
0x0000555555401218 <+20>: add $0x8,%rsp |
|
|
|
0x000055555540121c <+24>: ret |
|
|
|
0x000055555540121d <+25>: call 0x5555554019e6 <explode_bomb> |
|
|
|
0x0000555555401222 <+30>: jmp 0x555555401218 <phase_1+20> |
|
|
|
~~~ |
|
|
|
|
|
|
|
<+0>行进行了压栈操作 |
|
|
|
<+4>行将内存地址0x555555402a10赋予了寄存器%rsi |
|
|
|
<+11>行调用了函数<strings_not_equal>,从名称来看,是用于判断字符串是否相等,这串字符串就是密码 |
|
|
|
|
|
|
|
进入函数<strings_not_equal>查看,只看前几行 |
|
|
|
|
|
|
|
## 查看strings_not_equal汇编 |
|
|
|
|
|
|
|
~~~python |
|
|
|
=> 0x000055555540172b <+0>: push %r12 |
|
|
|
0x000055555540172d <+2>: push %rbp |
|
|
|
0x000055555540172e <+3>: push %rbx |
|
|
|
0x000055555540172f <+4>: mov %rdi,%rbx |
|
|
|
0x0000555555401732 <+7>: mov %rsi,%rbp |
|
|
|
0x0000555555401735 <+10>: call 0x55555540170e <string_length> |
|
|
|
~~~ |
|
|
|
|
|
|
|
发现调用了函数<string_length>,进入查看 |
|
|
|
|
|
|
|
## 查看string_length汇编 |
|
|
|
|
|
|
|
~~~python |
|
|
|
=> 0x000055555540170e <+0>: cmpb $0x0,(%rdi) |
|
|
|
0x0000555555401711 <+3>: je 0x555555401725 <string_length+23> |
|
|
|
0x0000555555401713 <+5>: mov %rdi,%rdx |
|
|
|
0x0000555555401716 <+8>: add $0x1,%rdx |
|
|
|
0x000055555540171a <+12>: mov %edx,%eax |
|
|
|
0x000055555540171c <+14>: sub %edi,%eax |
|
|
|
0x000055555540171e <+16>: cmpb $0x0,(%rdx) |
|
|
|
0x0000555555401721 <+19>: jne 0x555555401716 <string_length+8> |
|
|
|
0x0000555555401723 <+21>: repz ret |
|
|
|
0x0000555555401725 <+23>: mov $0x0,%eax |
|
|
|
0x000055555540172a <+28>: ret |
|
|
|
~~~ |
|
|
|
|
|
|
|
<0> <3>行用于判断是否是空地址,是的话返回0并退出 |
|
|
|
|
|
|
|
<5> 到 <19>行使用了循环的方式判断字符串有几位 |
|
|
|
首先将%rdi地址赋予%rdx,并且进入循环 |
|
|
|
随后进入循环,每次循环%rdx地址后后移一位 |
|
|
|
每完成一次循环,%eax就加一 |
|
|
|
跳出条件是 %rdx的地址指向的内存位置所储存的数据为0 |
|
|
|
|
|
|
|
故%rdi寄存器储存的是用户输入字符串的起始地址 |
|
|
|
|
|
|
|
## 继续查看strings_not_equal的后续部分 |
|
|
|
|
|
|
|
~~~python |
|
|
|
=> 0x000055555540172b <+0>: push %r12 |
|
|
|
0x000055555540172d <+2>: push %rbp |
|
|
|
0x000055555540172e <+3>: push %rbx |
|
|
|
0x000055555540172f <+4>: mov %rdi,%rbx |
|
|
|
0x0000555555401732 <+7>: mov %rsi,%rbp |
|
|
|
0x0000555555401735 <+10>: call 0x55555540170e <string_length> |
|
|
|
0x000055555540173a <+15>: mov %eax,%r12d |
|
|
|
0x000055555540173d <+18>: mov %rbp,%rdi |
|
|
|
0x0000555555401740 <+21>: call 0x55555540170e <string_length> |
|
|
|
0x0000555555401745 <+26>: mov $0x1,%edx |
|
|
|
0x000055555540174a <+31>: cmp %eax,%r12d |
|
|
|
0x000055555540174d <+34>: je 0x555555401756 <strings_not_equal+43> |
|
|
|
0x000055555540174f <+36>: mov %edx,%eax |
|
|
|
0x0000555555401751 <+38>: pop %rbx |
|
|
|
0x0000555555401752 <+39>: pop %rbp |
|
|
|
0x0000555555401753 <+40>: pop %r12 |
|
|
|
0x0000555555401755 <+42>: ret |
|
|
|
~~~ |
|
|
|
|
|
|
|
这一段实现了检查输入字符串和密码字符串的长度是否相等 |
|
|
|
可以锁定进入<strings_not_equal>函数前的%rsi寄存器就是密码部分 |
|
|
|
|
|
|
|
## 结论 |
|
|
|
|
|
|
|
使用命令'x/s 0x555555402a10'直接查看 |
|
|
|
发现密码为"I am not part of the problem. I am a Republican." |
|
|
|
|
|
|
|
# phase_2 |
|
|
|
|
|
|
|
## 查看phase_2汇编 |
|
|
|
|
|
|
|
首先查看部分汇编 |
|
|
|
|
|
|
|
~~~python |
|
|
|
=> 0x0000555555401224 <+0>: push %rbp |
|
|
|
0x0000555555401225 <+1>: push %rbx |
|
|
|
0x0000555555401226 <+2>: sub $0x28,%rsp |
|
|
|
0x000055555540122a <+6>: mov %fs:0x28,%rax |
|
|
|
0x0000555555401233 <+15>: mov %rax,0x18(%rsp) |
|
|
|
0x0000555555401238 <+20>: xor %eax,%eax |
|
|
|
0x000055555540123a <+22>: mov %rsp,%rsi |
|
|
|
0x000055555540123d <+25>: call 0x555555401a22 <read_six_numbers> |
|
|
|
~~~ |
|
|
|
|
|
|
|
可以发现其中途调用了函数<read_six_numbers>,其密码必然是六个数字 |
|
|
|
进入函数<read_six_numbers>查看 |
|
|
|
|
|
|
|
## 查看read_six_numbers汇编 |
|
|
|
|
|
|
|
~~~python |
|
|
|
=> 0x0000555555401a22 <+0>: sub $0x8,%rsp |
|
|
|
0x0000555555401a26 <+4>: mov %rsi,%rdx |
|
|
|
0x0000555555401a29 <+7>: lea 0x4(%rsi),%rcx |
|
|
|
0x0000555555401a2d <+11>: lea 0x14(%rsi),%rax |
|
|
|
0x0000555555401a31 <+15>: push %rax |
|
|
|
0x0000555555401a32 <+16>: lea 0x10(%rsi),%rax |
|
|
|
0x0000555555401a36 <+20>: push %rax |
|
|
|
0x0000555555401a37 <+21>: lea 0xc(%rsi),%r9 |
|
|
|
0x0000555555401a3b <+25>: lea 0x8(%rsi),%r8 |
|
|
|
0x0000555555401a3f <+29>: lea 0x12a3(%rip),%rsi # 0x555555402ce9 |
|
|
|
0x0000555555401a46 <+36>: mov $0x0,%eax |
|
|
|
0x0000555555401a4b <+41>: call 0x555555400ee0 <__isoc99_sscanf@plt> |
|
|
|
0x0000555555401a50 <+46>: add $0x10,%rsp |
|
|
|
0x0000555555401a54 <+50>: cmp $0x5,%eax |
|
|
|
0x0000555555401a57 <+53>: jle 0x555555401a5e <read_six_numbers+60> |
|
|
|
0x0000555555401a59 <+55>: add $0x8,%rsp |
|
|
|
0x0000555555401a5d <+59>: ret |
|
|
|
0x0000555555401a5e <+60>: call 0x5555554019e6 <explode_bomb> |
|
|
|
~~~ |
|
|
|
|
|
|
|
显然其判断起始地址储存在%rsi的数组中的数据 |
|
|
|
而由phase_2汇编可知,%rsi储存的地址由%rsp赋值而来 |
|
|
|
则%rsp含有用户输入数组的储存地址 |
|
|
|
|
|
|
|
## 继续查看phase_2汇编 |
|
|
|
|
|
|
|
### 进入循环 |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x0000555555401242 <+30>: cmpl $0x0,(%rsp) |
|
|
|
0x0000555555401246 <+34>: jne 0x55555540124f <phase_2+43> |
|
|
|
0x0000555555401248 <+36>: cmpl $0x1,0x4(%rsp) |
|
|
|
0x000055555540124d <+41>: je 0x555555401254 <phase_2+48> |
|
|
|
0x000055555540124f <+43>: call 0x5555554019e6 <explode_bomb> |
|
|
|
~~~ |
|
|
|
|
|
|
|
判断数组第一位是否为0,不是则引爆炸弹 |
|
|
|
判断数组第二位是否为1,是则跳转至<48>行 |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x0000555555401254 <+48>: mov %rsp,%rbx |
|
|
|
0x0000555555401257 <+51>: lea 0x10(%rbx),%rbp |
|
|
|
0x000055555540125b <+55>: jmp 0x555555401266 <phase_2+66> |
|
|
|
0x000055555540125d <+57>: add $0x4,%rbx |
|
|
|
0x0000555555401261 <+61>: cmp %rbp,%rbx |
|
|
|
0x0000555555401264 <+64>: je 0x555555401277 <phase_2+83> |
|
|
|
0x0000555555401266 <+66>: mov 0x4(%rbx),%eax |
|
|
|
0x0000555555401269 <+69>: add (%rbx),%eax |
|
|
|
0x000055555540126b <+71>: cmp %eax,0x8(%rbx) |
|
|
|
0x000055555540126e <+74>: je 0x55555540125d <phase_2+57> |
|
|
|
0x0000555555401270 <+76>: call 0x5555554019e6 <explode_bomb> |
|
|
|
0x0000555555401275 <+81>: jmp 0x55555540125d <phase_2+57> |
|
|
|
~~~ |
|
|
|
|
|
|
|
将%rsp的地址赋予%rbx,%rsp地址+16赋予%rbp |
|
|
|
随后跳转至<66>行 |
|
|
|
%eax储存%rbx+4地址所指向的值,并将其与%rbx所指向的值相加 |
|
|
|
随后%eax与%rbx+8地址所指向的值比较 |
|
|
|
相等则跳转至<57>行,%rbx的地址+4,如果%rsp+16与%rbx+4相等,则跳转至83行 |
|
|
|
|
|
|
|
从实际效果看,循环执行4次 |
|
|
|
每次将三元组,前两位相加与最后一位比较 |
|
|
|
相等则继续循环,不相等则爆炸 |
|
|
|
|
|
|
|
由上文可知,前两位分别为 0 1 |
|
|
|
|
|
|
|
~~~python |
|
|
|
第一次循环: 0 + 1 = 1 |
|
|
|
第二次循环: 1 + 1 = 2 |
|
|
|
第三次循环: 1 + 2 = 3 |
|
|
|
第四次循环: 2 + 3 = 5 |
|
|
|
~~~ |
|
|
|
|
|
|
|
故整个六位数字应该为 0 1 1 2 3 5 |
|
|
|
|
|
|
|
### 跳出循环&返回数值 |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x0000555555401277 <+83>: mov 0x18(%rsp),%rax |
|
|
|
0x000055555540127c <+88>: xor %fs:0x28,%rax |
|
|
|
0x0000555555401285 <+97>: jne 0x55555540128e <phase_2+106> |
|
|
|
0x0000555555401287 <+99>: add $0x28,%rsp |
|
|
|
0x000055555540128b <+103>: pop %rbx |
|
|
|
0x000055555540128c <+104>: pop %rbp |
|
|
|
0x000055555540128d <+105>: ret |
|
|
|
0x000055555540128e <+106>: call 0x555555400e40 <__stack_chk_fail@plt> |
|
|
|
~~~ |
|
|
|
|
|
|
|
实际效果来看,跳转到<83>行后,循环跳出,函数收尾并正常返回 |
|
|
|
|
|
|
|
### 结论 |
|
|
|
|
|
|
|
故整个六位数字应该为 0 1 1 2 3 5 |
|
|
|
|
|
|
|
# phase_3 |
|
|
|
|
|
|
|
## 查看部分汇编 |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x0000555555401293 <+0>: sub $0x18,%rsp |
|
|
|
0x0000555555401297 <+4>: mov %fs:0x28,%rax |
|
|
|
0x00005555554012a0 <+13>: mov %rax,0x8(%rsp) |
|
|
|
0x00005555554012a5 <+18>: xor %eax,%eax |
|
|
|
0x00005555554012a7 <+20>: lea 0x4(%rsp),%rcx |
|
|
|
0x00005555554012ac <+25>: mov %rsp,%rdx |
|
|
|
0x00005555554012af <+28>: lea 0x1a3f(%rip),%rsi # 0x555555402cf5 |
|
|
|
0x00005555554012b6 <+35>: call 0x555555400ee0 <__isoc99_sscanf@plt> |
|
|
|
0x00005555554012bb <+40>: cmp $0x1,%eax |
|
|
|
0x00005555554012be <+43>: jle 0x5555554012dd <phase_3+74> |
|
|
|
0x00005555554012c0 <+45>: cmpl $0x7,(%rsp) |
|
|
|
0x00005555554012c4 <+49>: ja 0x555555401363 <phase_3+208> |
|
|
|
~~~ |
|
|
|
|
|
|
|
可以看出phase_3调用了函数sscanf,其返回值为输入字符的个数 |
|
|
|
输入命令 `x/s 0x555555402cf5`显示"%d %d",可知sscanf输入两个字符,其返回值为2 |
|
|
|
<45><49>判断第一个数字是否小于0x7,故第一个数字应设置小于7 |
|
|
|
|
|
|
|
## 随意设置数字并带入 |
|
|
|
|
|
|
|
设置输入数据为5 32 |
|
|
|
随后断点设置在phase_3,使用nexti单步调试 |
|
|
|
查看汇编,并一路nexti |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x00005555554012ca <+55>: mov (%rsp),%eax |
|
|
|
0x00005555554012cd <+58>: lea 0x17ac(%rip),%rdx # 0x555555402a80 |
|
|
|
0x00005555554012d4 <+65>: movslq (%rdx,%rax,4),%rax |
|
|
|
0x00005555554012d8 <+69>: add %rdx,%rax |
|
|
|
0x00005555554012db <+72>: jmp *%rax |
|
|
|
0x00005555554012dd <+74>: call 0x5555554019e6 <explode_bomb> |
|
|
|
~~~ |
|
|
|
|
|
|
|
发现执行完`jmp *%rax`后,跳转至`0x000055555540134e` |
|
|
|
|
|
|
|
## 继续查看汇编 |
|
|
|
|
|
|
|
继续nexti |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x00005555554012e2 <+79>: jmp 0x5555554012c0 <phase_3+45> |
|
|
|
0x00005555554012e4 <+81>: mov $0x2ca,%eax |
|
|
|
0x00005555554012e9 <+86>: jmp 0x5555554012f0 <phase_3+93> |
|
|
|
0x00005555554012eb <+88>: mov $0x0,%eax |
|
|
|
0x00005555554012f0 <+93>: sub $0xf2,%eax |
|
|
|
0x00005555554012f5 <+98>: add $0x14a,%eax |
|
|
|
0x00005555554012fa <+103>: sub $0x162,%eax |
|
|
|
0x00005555554012ff <+108>: add $0x162,%eax |
|
|
|
0x0000555555401304 <+113>: sub $0x162,%eax |
|
|
|
0x0000555555401309 <+118>: add $0x162,%eax |
|
|
|
0x000055555540130e <+123>: sub $0x162,%eax |
|
|
|
0x0000555555401313 <+128>: cmpl $0x5,(%rsp) |
|
|
|
0x0000555555401317 <+132>: jg 0x55555540131f <phase_3+140> |
|
|
|
0x0000555555401319 <+134>: cmp %eax,0x4(%rsp) |
|
|
|
0x000055555540131d <+138>: je 0x555555401324 <phase_3+145> |
|
|
|
0x000055555540131f <+140>: call 0x5555554019e6 <explode_bomb> |
|
|
|
0x0000555555401324 <+145>: mov 0x8(%rsp),%rax |
|
|
|
0x0000555555401329 <+150>: xor %fs:0x28,%rax |
|
|
|
0x0000555555401332 <+159>: jne 0x55555540136f <phase_3+220> |
|
|
|
0x0000555555401334 <+161>: add $0x18,%rsp |
|
|
|
0x0000555555401338 <+165>: ret |
|
|
|
0x0000555555401339 <+166>: mov $0x0,%eax |
|
|
|
0x000055555540133e <+171>: jmp 0x5555554012f5 <phase_3+98> |
|
|
|
0x0000555555401340 <+173>: mov $0x0,%eax |
|
|
|
0x0000555555401345 <+178>: jmp 0x5555554012fa <phase_3+103> |
|
|
|
0x0000555555401347 <+180>: mov $0x0,%eax |
|
|
|
0x000055555540134c <+185>: jmp 0x5555554012ff <phase_3+108> |
|
|
|
0x000055555540134e <+187>: mov $0x0,%eax |
|
|
|
0x0000555555401353 <+192>: jmp 0x555555401304 <phase_3+113> |
|
|
|
0x0000555555401355 <+194>: mov $0x0,%eax |
|
|
|
0x000055555540135a <+199>: jmp 0x555555401309 <phase_3+118> |
|
|
|
0x000055555540135c <+201>: mov $0x0,%eax |
|
|
|
0x0000555555401361 <+206>: jmp 0x55555540130e <phase_3+123> |
|
|
|
0x0000555555401363 <+208>: call 0x5555554019e6 <explode_bomb> |
|
|
|
0x0000555555401368 <+213>: mov $0x0,%eax |
|
|
|
0x000055555540136d <+218>: jmp 0x555555401313 <phase_3+128> |
|
|
|
0x000055555540136f <+220>: call 0x555555400e40 <__stack_chk_fail@plt> |
|
|
|
~~~ |
|
|
|
|
|
|
|
发现其跳转至`0x0000555555401304` |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x0000555555401313 <+128>: cmpl $0x5,(%rsp) |
|
|
|
0x0000555555401317 <+132>: jg 0x55555540131f <phase_3+140> |
|
|
|
~~~ |
|
|
|
|
|
|
|
判断第一个数字是否小于等于5,不是则引爆 |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x0000555555401319 <+134>: cmp %eax,0x4(%rsp) |
|
|
|
0x000055555540131d <+138>: je 0x555555401324 <phase_3+145> |
|
|
|
0x000055555540131f <+140>: call 0x5555554019e6 <explode_bomb> |
|
|
|
~~~ |
|
|
|
|
|
|
|
判断第二个数字是否和eax寄存器中数据相等 |
|
|
|
此时查看寄存器eax中数值`p $eax`,返回数据为-354,故第二个数据为-354 |
|
|
|
|
|
|
|
## 结论 |
|
|
|
|
|
|
|
phase_3第一个数据要求小于等于5,第二个数据要求为-354 |
|
|
|
|
|
|
|
# phase_4 |
|
|
|
|
|
|
|
## 查看部分汇编代码 |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x00005555554013ad <+0>: sub $0x18,%rsp |
|
|
|
0x00005555554013b1 <+4>: mov %fs:0x28,%rax |
|
|
|
0x00005555554013ba <+13>: mov %rax,0x8(%rsp) |
|
|
|
0x00005555554013bf <+18>: xor %eax,%eax |
|
|
|
0x00005555554013c1 <+20>: mov %rsp,%rcx |
|
|
|
0x00005555554013c4 <+23>: lea 0x4(%rsp),%rdx |
|
|
|
0x00005555554013c9 <+28>: lea 0x1925(%rip),%rsi # 0x555555402cf5 |
|
|
|
0x00005555554013d0 <+35>: call 0x555555400ee0 <__isoc99_sscanf@plt> |
|
|
|
0x00005555554013d5 <+40>: cmp $0x2,%eax |
|
|
|
0x00005555554013d8 <+43>: jne 0x5555554013e5 <phase_4+56> |
|
|
|
~~~ |
|
|
|
|
|
|
|
可以看到`0x00005555554013c9 <+28>: lea 0x1925(%rip),%rsi # 0x555555402cf5` |
|
|
|
输入`x/s 0x555555402cf5`,显示为`%d %d`,因此输入数据为两个整形 |
|
|
|
|
|
|
|
随意输入两个整数,例如`114 514` |
|
|
|
|
|
|
|
~~~python |
|
|
|
p *(int*)$rsp |
|
|
|
p *(int*)($rsp+4) |
|
|
|
|
|
|
|
$1 = 514 |
|
|
|
$2 = 114 |
|
|
|
~~~ |
|
|
|
|
|
|
|
所以rsp存储第二个数字的地址,rsp+4存储第一个数字的地址 |
|
|
|
|
|
|
|
## 继续查看部分汇编代码 |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x00005555554013da <+45>: mov (%rsp),%eax |
|
|
|
0x00005555554013dd <+48>: sub $0x2,%eax |
|
|
|
0x00005555554013e0 <+51>: cmp $0x2,%eax |
|
|
|
0x00005555554013e3 <+54>: jbe 0x5555554013ea <phase_4+61> |
|
|
|
0x00005555554013e5 <+56>: call 0x5555554019e6 <explode_bomb> |
|
|
|
0x00005555554013ea <+61>: mov (%rsp),%esi |
|
|
|
0x00005555554013ed <+64>: mov $0x9,%edi |
|
|
|
0x00005555554013f2 <+69>: call 0x555555401374 <func4> |
|
|
|
0x00005555554013f7 <+74>: cmp %eax,0x4(%rsp) |
|
|
|
0x00005555554013fb <+78>: je 0x555555401402 <phase_4+85> |
|
|
|
0x00005555554013fd <+80>: call 0x5555554019e6 <explode_bomb> |
|
|
|
0x0000555555401402 <+85>: mov 0x8(%rsp),%rax |
|
|
|
0x0000555555401407 <+90>: xor %fs:0x28,%rax |
|
|
|
0x0000555555401410 <+99>: jne 0x555555401417 <phase_4+106> |
|
|
|
0x0000555555401412 <+101>: add $0x18,%rsp |
|
|
|
0x0000555555401416 <+105>: ret |
|
|
|
0x0000555555401417 <+106>: call 0x555555400e40 <__stack_chk_fail@plt> |
|
|
|
~~~ |
|
|
|
|
|
|
|
<+45> <+48> <+51> <+54> 要求输入的第二个数据小于等于4 |
|
|
|
|
|
|
|
不妨让输入数据变为`114 4`再次单步调试 |
|
|
|
一路nexti,直至,<+69>执行完毕 |
|
|
|
|
|
|
|
显然<+74> <+78>判断第一个数据是否和函数 func4 返回值是否相同 |
|
|
|
输入`p $eax`查看,显示为352 |
|
|
|
|
|
|
|
## 结论 |
|
|
|
|
|
|
|
因此,可行的一组数据为`352 4` |
|
|
|
|
|
|
|
# phase_5 |
|
|
|
|
|
|
|
## 查看部分汇编代码 |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x000055555540141c <+0>: sub $0x18,%rsp |
|
|
|
0x0000555555401420 <+4>: mov %fs:0x28,%rax |
|
|
|
0x0000555555401429 <+13>: mov %rax,0x8(%rsp) |
|
|
|
0x000055555540142e <+18>: xor %eax,%eax |
|
|
|
0x0000555555401430 <+20>: lea 0x4(%rsp),%rcx |
|
|
|
0x0000555555401435 <+25>: mov %rsp,%rdx |
|
|
|
0x0000555555401438 <+28>: lea 0x18b6(%rip),%rsi # 0x555555402cf5 |
|
|
|
0x000055555540143f <+35>: call 0x555555400ee0 <__isoc99_sscanf@plt> |
|
|
|
0x0000555555401444 <+40>: cmp $0x1,%eax |
|
|
|
0x0000555555401447 <+43>: jle 0x5555554014a3 <phase_5+135> |
|
|
|
~~~ |
|
|
|
|
|
|
|
<+28>行可以看到`0x555555402cf5` |
|
|
|
输入`x/s 0x555555402cf5`显示`%d %d` |
|
|
|
故输入两个十进制整数 |
|
|
|
|
|
|
|
## 进入循环 |
|
|
|
|
|
|
|
### 循环起点 |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x0000555555401449 <+45>: mov (%rsp),%eax |
|
|
|
0x000055555540144c <+48>: and $0xf,%eax |
|
|
|
0x000055555540144f <+51>: mov %eax,(%rsp) |
|
|
|
0x0000555555401452 <+54>: cmp $0xf,%eax |
|
|
|
0x0000555555401455 <+57>: je 0x555555401489 <phase_5+109> |
|
|
|
0x0000555555401457 <+59>: mov $0x0,%ecx |
|
|
|
0x000055555540145c <+64>: mov $0x0,%edx |
|
|
|
0x0000555555401461 <+69>: lea 0x1638(%rip),%rsi # 0x555555402aa0 <array.3415> |
|
|
|
~~~ |
|
|
|
|
|
|
|
发现有一个数组array.3415 |
|
|
|
查看后发现数组为{10, 2, 14, 7, 8, 12, 15, 11, 0, 4, 1, 13, 3, 9, 6, 5} |
|
|
|
|
|
|
|
### 进入循环 |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x0000555555401468 <+76>: add $0x1,%edx |
|
|
|
0x000055555540146b <+79>: cltq |
|
|
|
0x000055555540146d <+81>: mov (%rsi,%rax,4),%eax |
|
|
|
0x0000555555401470 <+84>: add %eax,%ecx |
|
|
|
0x0000555555401472 <+86>: cmp $0xf,%eax |
|
|
|
0x0000555555401475 <+89>: jne 0x555555401468 <phase_5+76> |
|
|
|
0x0000555555401477 <+91>: movl $0xf,(%rsp) |
|
|
|
0x000055555540147e <+98>: cmp $0xf,%edx |
|
|
|
=> 0x0000555555401481 <+101>: jne 0x555555401489 <phase_5+109> |
|
|
|
0x0000555555401483 <+103>: cmp %ecx,0x4(%rsp) |
|
|
|
0x0000555555401487 <+107>: je 0x55555540148e <phase_5+114> |
|
|
|
~~~ |
|
|
|
|
|
|
|
当寄存器%edx与%eax同时为0xf时,循环跳出 |
|
|
|
因为%edx初始值为0,进入循环加1,则循环必定15次 |
|
|
|
故可逆推最开始进入循环时%eax的值 |
|
|
|
逆推得%eax为5 |
|
|
|
此时输入%ecx计算得结果为115 |
|
|
|
|
|
|
|
### 退出循环 |
|
|
|
|
|
|
|
正常检查栈金丝雀值 |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x0000555555401489 <+109>: call 0x5555554019e6 <explode_bomb> |
|
|
|
0x000055555540148e <+114>: mov 0x8(%rsp),%rax |
|
|
|
0x0000555555401493 <+119>: xor %fs:0x28,%rax |
|
|
|
0x000055555540149c <+128>: jne 0x5555554014aa <phase_5+142> |
|
|
|
0x000055555540149e <+130>: add $0x18,%rsp |
|
|
|
0x00005555554014a2 <+134>: ret |
|
|
|
0x00005555554014a3 <+135>: call 0x5555554019e6 <explode_bomb> |
|
|
|
0x00005555554014a8 <+140>: jmp 0x555555401449 <phase_5+45> |
|
|
|
0x00005555554014aa <+142>: call 0x555555400e40 <__stack_chk_fail@plt> |
|
|
|
~~~ |
|
|
|
|
|
|
|
## 结论 |
|
|
|
|
|
|
|
密码为`5 115` |
|
|
|
|
|
|
|
# phase_6 |
|
|
|
|
|
|
|
## 读取数据 |
|
|
|
|
|
|
|
~~~python |
|
|
|
=> 0x00005555554014af <+0>: push %r14 |
|
|
|
0x00005555554014b1 <+2>: push %r13 |
|
|
|
0x00005555554014b3 <+4>: push %r12 |
|
|
|
0x00005555554014b5 <+6>: push %rbp |
|
|
|
0x00005555554014b6 <+7>: push %rbx |
|
|
|
0x00005555554014b7 <+8>: sub $0x60,%rsp |
|
|
|
0x00005555554014bb <+12>: mov %fs:0x28,%rax |
|
|
|
0x00005555554014c4 <+21>: mov %rax,0x58(%rsp) |
|
|
|
0x00005555554014c9 <+26>: xor %eax,%eax |
|
|
|
0x00005555554014cb <+28>: mov %rsp,%r13 |
|
|
|
0x00005555554014ce <+31>: mov %r13,%rsi |
|
|
|
0x00005555554014d1 <+34>: call 0x555555401a22 <read_six_numbers> |
|
|
|
0x00005555554014d6 <+39>: mov %r13,%r12 |
|
|
|
0x00005555554014d9 <+42>: mov $0x0,%r14d |
|
|
|
0x00005555554014df <+48>: jmp 0x555555401506 <phase_6+87> |
|
|
|
~~~ |
|
|
|
|
|
|
|
这段开辟了一个栈空间并且设置了一个金丝雀值 |
|
|
|
将%rsp赋予%r13,%rsi,%r12 |
|
|
|
将0x0赋予%r14d |
|
|
|
随后跳转到87行 |
|
|
|
|
|
|
|
## 第一个循环 |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x00005555554014e1 <+50>: call 0x5555554019e6 <explode_bomb> |
|
|
|
0x00005555554014e6 <+55>: jmp 0x555555401515 <phase_6+102> |
|
|
|
0x00005555554014e8 <+57>: add $0x1,%ebx |
|
|
|
0x00005555554014eb <+60>: cmp $0x5,%ebx |
|
|
|
0x00005555554014ee <+63>: jg 0x555555401502 <phase_6+83> |
|
|
|
0x00005555554014f0 <+65>: movslq %ebx,%rax |
|
|
|
0x00005555554014f3 <+68>: mov (%rsp,%rax,4),%eax |
|
|
|
0x00005555554014f6 <+71>: cmp %eax,0x0(%rbp) |
|
|
|
0x00005555554014f9 <+74>: jne 0x5555554014e8 <phase_6+57> |
|
|
|
0x00005555554014fb <+76>: call 0x5555554019e6 <explode_bomb> |
|
|
|
0x0000555555401500 <+81>: jmp 0x5555554014e8 <phase_6+57> |
|
|
|
0x0000555555401502 <+83>: add $0x4,%r13 |
|
|
|
0x0000555555401506 <+87>: mov %r13,%rbp |
|
|
|
0x0000555555401509 <+90>: mov 0x0(%r13),%eax |
|
|
|
0x000055555540150d <+94>: sub $0x1,%eax |
|
|
|
0x0000555555401510 <+97>: cmp $0x5,%eax |
|
|
|
0x0000555555401513 <+100>: ja 0x5555554014e1 <phase_6+50> |
|
|
|
0x0000555555401515 <+102>: add $0x1,%r14d |
|
|
|
0x0000555555401519 <+106>: cmp $0x6,%r14d |
|
|
|
0x000055555540151d <+110>: je 0x555555401524 <phase_6+117> |
|
|
|
0x000055555540151f <+112>: mov %r14d,%ebx |
|
|
|
0x0000555555401522 <+115>: jmp 0x5555554014f0 <phase_6+65> |
|
|
|
~~~ |
|
|
|
|
|
|
|
分析可知,进入循环得初始处理是将%r13存储的%rsp地址赋给%rbp |
|
|
|
随后比较(%rsp)即第一个数字,减去1后与0x5比较,如果小于等于5才不会引爆 |
|
|
|
相当于每个输入的数据都必须小于等于6 |
|
|
|
并且由于是ja,故数字还必须-1后大于等于0,故数字大于等于1 |
|
|
|
|
|
|
|
%r14d初始值为0x0,每完成一次循环都+1,等于6的时候跳出,则循环执行6次,恰好遍历六个数字 |
|
|
|
%r14d未达到0x6时,会将当前值给%ebx,随后跳转到65行 |
|
|
|
执行等价于%eax等于第n个数字的操作 |
|
|
|
|
|
|
|
随后%eax与%rbp指向的数字比较,不相等则%ebx+1,继续判断%eax与%rbp指向的数字,直到%ebx大于5 |
|
|
|
随后%r13后移一位,继续进入最开始的循环 |
|
|
|
|
|
|
|
### 第一个循环结论 |
|
|
|
|
|
|
|
等效于数组各数字不能相等,并且都需要大于等于1,小于等于6 |
|
|
|
|
|
|
|
## 第二个与第三个循环 |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x0000555555401524 <+117>: lea 0x18(%r12),%rcx |
|
|
|
0x0000555555401529 <+122>: mov $0x7,%edx |
|
|
|
0x000055555540152e <+127>: mov %edx,%eax |
|
|
|
0x0000555555401530 <+129>: sub (%r12),%eax |
|
|
|
0x0000555555401534 <+133>: mov %eax,(%r12) |
|
|
|
0x0000555555401538 <+137>: add $0x4,%r12 |
|
|
|
0x000055555540153c <+141>: cmp %r12,%rcx |
|
|
|
0x000055555540153f <+144>: jne 0x55555540152e <phase_6+127> |
|
|
|
0x0000555555401541 <+146>: mov $0x0,%esi |
|
|
|
0x0000555555401546 <+151>: jmp 0x555555401562 <phase_6+179> |
|
|
|
0x0000555555401548 <+153>: mov 0x8(%rdx),%rdx |
|
|
|
0x000055555540154c <+157>: add $0x1,%eax |
|
|
|
0x000055555540154f <+160>: cmp %ecx,%eax |
|
|
|
0x0000555555401551 <+162>: jne 0x555555401548 <phase_6+153> |
|
|
|
0x0000555555401553 <+164>: mov %rdx,0x20(%rsp,%rsi,8) |
|
|
|
0x0000555555401558 <+169>: add $0x1,%rsi |
|
|
|
0x000055555540155c <+173>: cmp $0x6,%rsi |
|
|
|
0x0000555555401560 <+177>: je 0x555555401578 <phase_6+201> |
|
|
|
0x0000555555401562 <+179>: mov (%rsp,%rsi,4),%ecx |
|
|
|
0x0000555555401565 <+182>: mov $0x1,%eax |
|
|
|
0x000055555540156a <+187>: lea 0x202cbf(%rip),%rdx # 0x555555604230 <node1> |
|
|
|
0x0000555555401571 <+194>: cmp $0x1,%ecx |
|
|
|
0x0000555555401574 <+197>: jg 0x555555401548 <phase_6+153> |
|
|
|
0x0000555555401576 <+199>: jmp 0x555555401553 <phase_6+164> |
|
|
|
~~~ |
|
|
|
|
|
|
|
跳出第一个循环后,进入第二个循环 |
|
|
|
%rcx被赋予等价为%rsp+0x18的地址,这个地址并不存放数组的数据 |
|
|
|
%edx被赋予0x7的值 |
|
|
|
令%eax = 7,随后7-(%rsp) |
|
|
|
%r12储存地址+4,等价%rsp+4 |
|
|
|
比较%r12与%rcx的地址,不一致则跳转到127行,127行继续进行上述操作 |
|
|
|
最终结果是%r12地址移动至%rsp+0x18,数组内每个数字都变为7-该数字,随后将%esi置0x0,跳转至179行 |
|
|
|
|
|
|
|
进入第三个循环 |
|
|
|
%ecx被赋予(%rsp)的值 |
|
|
|
%eax被置1 |
|
|
|
%rdx被赋予一个节点地址 |
|
|
|
并且判断%ecx的值是否大于1 |
|
|
|
|
|
|
|
如果是跳转到153行,(%rdx地址+0x8)赋给rdx,可以猜测,%rdx原先赋给的node1是个结构体,前八字节储存的是数据,后八字节储存的是地址 |
|
|
|
输入`x/128x 0x555555604230`即可查看 |
|
|
|
因此应该为一个链表,并且每次循环都向后移动一位 |
|
|
|
随后%eax+1,使得%ecx和%eax比较 |
|
|
|
循环,直至%ecx与%eax相等 |
|
|
|
|
|
|
|
如果不是大于1 |
|
|
|
跳转到164行 |
|
|
|
将节点地址赋给%rsp对应位置 |
|
|
|
|
|
|
|
最终效果为 |
|
|
|
数字为7-n对应第n个节点 |
|
|
|
节点地址在栈中存储顺序和数字输入顺序一致 |
|
|
|
|
|
|
|
完成后跳转到201行 |
|
|
|
|
|
|
|
## 第三个循环 |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x0000555555401578 <+201>: mov 0x20(%rsp),%rbx |
|
|
|
0x000055555540157d <+206>: mov 0x28(%rsp),%rax |
|
|
|
0x0000555555401582 <+211>: mov %rax,0x8(%rbx) |
|
|
|
0x0000555555401586 <+215>: mov 0x30(%rsp),%rdx |
|
|
|
0x000055555540158b <+220>: mov %rdx,0x8(%rax) |
|
|
|
0x000055555540158f <+224>: mov 0x38(%rsp),%rax |
|
|
|
0x0000555555401594 <+229>: mov %rax,0x8(%rdx) |
|
|
|
0x0000555555401598 <+233>: mov 0x40(%rsp),%rdx |
|
|
|
0x000055555540159d <+238>: mov %rdx,0x8(%rax) |
|
|
|
0x00005555554015a1 <+242>: mov 0x48(%rsp),%rax |
|
|
|
0x00005555554015a6 <+247>: mov %rax,0x8(%rdx) |
|
|
|
0x00005555554015aa <+251>: movq $0x0,0x8(%rax) |
|
|
|
0x00005555554015b2 <+259>: mov $0x5,%ebp |
|
|
|
0x00005555554015b7 <+264>: jmp 0x5555554015c2 <phase_6+275> |
|
|
|
0x00005555554015b9 <+266>: mov 0x8(%rbx),%rbx |
|
|
|
0x00005555554015bd <+270>: sub $0x1,%ebp |
|
|
|
0x00005555554015c0 <+273>: je 0x5555554015d3 <phase_6+292> |
|
|
|
0x00005555554015c2 <+275>: mov 0x8(%rbx),%rax |
|
|
|
0x00005555554015c6 <+279>: mov (%rax),%eax |
|
|
|
0x00005555554015c8 <+281>: cmp %eax,(%rbx) |
|
|
|
0x00005555554015ca <+283>: jge 0x5555554015b9 <phase_6+266> |
|
|
|
0x00005555554015cc <+285>: call 0x5555554019e6 <explode_bomb> |
|
|
|
0x00005555554015d1 <+290>: jmp 0x5555554015b9 <phase_6+266> |
|
|
|
~~~ |
|
|
|
|
|
|
|
显然%rbx存储第一个数字对应的节点地址 |
|
|
|
%rax存储第二个数字对应的节点地址 |
|
|
|
0x8(%rbx)取出第一个数字对应节点储存的下一个节点的地址,这个地址被%rax中的地址取代 |
|
|
|
%rdx储存第三个数字对应的节点地址 |
|
|
|
0x8(%rax)取出第二个数字对应节点储存的下一个节点的地址,这个地址被%rdx中的地址取代 |
|
|
|
%rax被第四个数字对应的节点地址覆盖 |
|
|
|
0x8(%rdx)取出第三个数字对应节点储存的下一个节点的地址,这个地址被%rax中的地址取代,即第四个数字对应的节点地址 |
|
|
|
%rdx被第五个数字对应的节点地址覆盖 |
|
|
|
0x8(%rax)取出第四个数字对应下一个节点的地址,这个地址被%rdx中的地址取代,即第五个数字对应的节点地址 |
|
|
|
%rax被第六个数字对应节点的地址覆盖 |
|
|
|
0x8(%rdx)取出第五个数字对应下一个节点的地址,这个地址被%rdx中的地址取代,即第六个数字对应的节点地址 |
|
|
|
随后给%rax储存地址处赋予Null指针 |
|
|
|
|
|
|
|
综合结果即将原有链表顺序重新排列 |
|
|
|
效果为输入顺序为i,数值为k的一组数据 |
|
|
|
第7-k个节点被移动至第i位 |
|
|
|
|
|
|
|
然后置%ebp为5,跳转至275行 |
|
|
|
%rbx储存第一个节点的地址,赋予%rax第二个节点的地址 |
|
|
|
将第二个节点的数据赋给%eax |
|
|
|
比较第二个节点的数据b和第一个节点的数据a |
|
|
|
如果a>=b |
|
|
|
则继续循环,跳转到266行,%rbx移动至下一个节点,同时%ebp-1,并且如果%ebp = 1时,就跳出循环到292行 |
|
|
|
故进行了5次循环,每次效果都是判断相邻节点是否上一个的数据大于下一个 |
|
|
|
|
|
|
|
## 收尾 |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x00005555554015d1 <+290>: jmp 0x5555554015b9 <phase_6+266> |
|
|
|
0x00005555554015d3 <+292>: mov 0x58(%rsp),%rax |
|
|
|
0x00005555554015d8 <+297>: xor %fs:0x28,%rax |
|
|
|
0x00005555554015e1 <+306>: jne 0x5555554015f0 <phase_6+321> |
|
|
|
0x00005555554015e3 <+308>: add $0x60,%rsp |
|
|
|
0x00005555554015e7 <+312>: pop %rbx |
|
|
|
0x00005555554015e8 <+313>: pop %rbp |
|
|
|
0x00005555554015e9 <+314>: pop %r12 |
|
|
|
0x00005555554015eb <+316>: pop %r13 |
|
|
|
0x00005555554015ed <+318>: pop %r14 |
|
|
|
0x00005555554015ef <+320>: ret |
|
|
|
0x00005555554015f0 <+321>: call 0x555555400e40 <__stack_chk_fail@plt> |
|
|
|
~~~ |
|
|
|
|
|
|
|
简单的探测金丝雀值变化并返回 |
|
|
|
|
|
|
|
## 安排密码 |
|
|
|
输入`x/128x 0x555555604230`查看各个节点 |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x555555604230 <node1>: 0x3b 0x03 0x00 0x00 0x01 0x00 0x00 0x00 |
|
|
|
0x555555604238 <node1+8>: 0x40 0x42 0x60 0x55 0x55 0x55 0x00 0x00 |
|
|
|
0x555555604240 <node2>: 0x1c 0x02 0x00 0x00 0x02 0x00 0x00 0x00 |
|
|
|
0x555555604248 <node2+8>: 0x50 0x42 0x60 0x55 0x55 0x55 0x00 0x00 |
|
|
|
0x555555604250 <node3>: 0x0f 0x02 0x00 0x00 0x03 0x00 0x00 0x00 |
|
|
|
0x555555604258 <node3+8>: 0x60 0x42 0x60 0x55 0x55 0x55 0x00 0x00 |
|
|
|
0x555555604260 <node4>: 0xea 0x00 0x00 0x00 0x04 0x00 0x00 0x00 |
|
|
|
0x555555604268 <node4+8>: 0x70 0x42 0x60 0x55 0x55 0x55 0x00 0x00 |
|
|
|
0x555555604270 <node5>: 0x83 0x00 0x00 0x00 0x05 0x00 0x00 0x00 |
|
|
|
0x555555604278 <node5+8>: 0x10 0x41 0x60 0x55 0x55 0x55 0x00 0x00 |
|
|
|
0x555555604280 <host_table>: 0x4f 0x2d 0x40 0x55 0x55 0x55 0x00 0x00 |
|
|
|
0x555555604288 <host_table+8>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
|
|
|
0x555555604290 <host_table+16>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
|
|
|
0x555555604298 <host_table+24>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
|
|
|
0x5555556042a0 <host_table+32>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
|
|
|
0x5555556042a8 <host_table+40>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
|
|
|
~~~ |
|
|
|
|
|
|
|
node1的数据是0x0000033b |
|
|
|
node2的数据是0x0000021c |
|
|
|
node3的数据是0x0000020f |
|
|
|
node4的数据时0x000000ea |
|
|
|
node5的数据是0x00000083 |
|
|
|
注意到node6储存在node5的地址处,和这五个node不是连续储存 |
|
|
|
输入`x/64x 0x555555604110` |
|
|
|
node6的数据是0x000001e2 |
|
|
|
|
|
|
|
按照需要的,相邻数据成降序 |
|
|
|
则是 node1 node2 node3 node6 node4 node5 |
|
|
|
对应为 1 2 3 6 4 5 |
|
|
|
但是是用7-n得到的 |
|
|
|
所以实际顺序是 6 5 4 1 3 2 |
|
|
|
|
|
|
|
# secret_phase |
|
|
|
|
|
|
|
查询资料得其在phase_defused中被调用 |
|
|
|
|
|
|
|
## 查看phase_defused汇编 |
|
|
|
|
|
|
|
~~~python |
|
|
|
=> 0x0000555555401ba7 <+0>: sub $0x78,%rsp |
|
|
|
0x0000555555401bab <+4>: mov %fs:0x28,%rax |
|
|
|
0x0000555555401bb4 <+13>: mov %rax,0x68(%rsp) |
|
|
|
0x0000555555401bb9 <+18>: xor %eax,%eax |
|
|
|
0x0000555555401bbb <+20>: mov $0x1,%edi |
|
|
|
0x0000555555401bc0 <+25>: call 0x5555554018c2 <send_msg> |
|
|
|
0x0000555555401bc5 <+30>: cmpl $0x6,0x202ae0(%rip) # 0x5555556046ac <num_input_strings> |
|
|
|
0x0000555555401bcc <+37>: je 0x555555401be7 <phase_defused+64> |
|
|
|
0x0000555555401bce <+39>: mov 0x68(%rsp),%rax |
|
|
|
0x0000555555401bd3 <+44>: xor %fs:0x28,%rax |
|
|
|
0x0000555555401bdc <+53>: jne 0x555555401c66 <phase_defused+191> |
|
|
|
0x0000555555401be2 <+59>: add $0x78,%rsp |
|
|
|
0x0000555555401be6 <+63>: ret |
|
|
|
0x0000555555401be7 <+64>: lea 0xc(%rsp),%rcx |
|
|
|
0x0000555555401bec <+69>: lea 0x8(%rsp),%rdx |
|
|
|
0x0000555555401bf1 <+74>: lea 0x10(%rsp),%r8 |
|
|
|
0x0000555555401bf6 <+79>: lea 0x1142(%rip),%rsi # 0x555555402d3f |
|
|
|
0x0000555555401bfd <+86>: lea 0x202bac(%rip),%rdi # 0x5555556047b0 <input_strings+240> |
|
|
|
0x0000555555401c04 <+93>: mov $0x0,%eax |
|
|
|
0x0000555555401c09 <+98>: call 0x555555400ee0 <__isoc99_sscanf@plt> |
|
|
|
0x0000555555401c0e <+103>: cmp $0x3,%eax |
|
|
|
0x0000555555401c11 <+106>: je 0x555555401c2d <phase_defused+134> |
|
|
|
0x0000555555401c13 <+108>: lea 0xfe6(%rip),%rdi # 0x555555402c00 |
|
|
|
0x0000555555401c1a <+115>: call 0x555555400e20 <puts@plt> |
|
|
|
0x0000555555401c1f <+120>: lea 0x100a(%rip),%rdi # 0x555555402c30 |
|
|
|
0x0000555555401c26 <+127>: call 0x555555400e20 <puts@plt> |
|
|
|
0x0000555555401c2b <+132>: jmp 0x555555401bce <phase_defused+39> |
|
|
|
0x0000555555401c2d <+134>: lea 0x10(%rsp),%rdi |
|
|
|
0x0000555555401c32 <+139>: lea 0x110f(%rip),%rsi # 0x555555402d48 |
|
|
|
0x0000555555401c39 <+146>: call 0x55555540172b <strings_not_equal> |
|
|
|
0x0000555555401c3e <+151>: test %eax,%eax |
|
|
|
0x0000555555401c40 <+153>: jne 0x555555401c13 <phase_defused+108> |
|
|
|
0x0000555555401c42 <+155>: lea 0xf57(%rip),%rdi # 0x555555402ba0 |
|
|
|
0x0000555555401c49 <+162>: call 0x555555400e20 <puts@plt> |
|
|
|
0x0000555555401c4e <+167>: lea 0xf73(%rip),%rdi # 0x555555402bc8 |
|
|
|
0x0000555555401c55 <+174>: call 0x555555400e20 <puts@plt> |
|
|
|
0x0000555555401c5a <+179>: mov $0x0,%eax |
|
|
|
0x0000555555401c5f <+184>: call 0x555555401634 <secret_phase> |
|
|
|
0x0000555555401c64 <+189>: jmp 0x555555401c13 <phase_defused+108> |
|
|
|
0x0000555555401c66 <+191>: call 0x555555400e40 <__stack_chk_fail@plt> |
|
|
|
~~~ |
|
|
|
|
|
|
|
直奔`secret_phase`,向上寻找跳转,发现只有当`test %eax %eax`设置`ZF=1`时才能执行到`secret_phase` |
|
|
|
而%eax的值由`strings_not_equal`决定,从phase_1中可知,`strings_not_equal`使用的用来当“标准”的字符串储存在寄存器%rsi的地址指向的地方 |
|
|
|
而上一步%rsi储存地址为`0x555555402d48` |
|
|
|
输入`x/s 0x555555402d48`显示为`DrEvil` |
|
|
|
|
|
|
|
下一步是寻找触发条件 |
|
|
|
|
|
|
|
继续向上看,发现<+93> <+98> <+103> <+106>行出现 |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x0000555555401c04 <+93>: mov $0x0,%eax |
|
|
|
0x0000555555401c09 <+98>: call 0x555555400ee0 <__isoc99_sscanf@plt> |
|
|
|
0x0000555555401c0e <+103>: cmp $0x3,%eax |
|
|
|
0x0000555555401c11 <+106>: je 0x555555401c2d <phase_defused+134> |
|
|
|
~~~ |
|
|
|
|
|
|
|
即判断sscanf函数接收的字符串是否为3个,是才能跳转到`strings_not_equal` |
|
|
|
|
|
|
|
继续向上看,能跳转到`__isoc99_sscanf@plt`部分,而非ret退出,需要一个jmp指令 |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x0000555555401bc5 <+30>: cmpl $0x6,0x202ae0(%rip) # 0x5555556046ac <num_input_strings> |
|
|
|
~~~ |
|
|
|
|
|
|
|
这段意思为完成六个phase,先决条件 |
|
|
|
只能在`__isoc99_sscanf@plt`上面和`ret`下面寻找,即 |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x0000555555401be7 <+64>: lea 0xc(%rsp),%rcx |
|
|
|
0x0000555555401bec <+69>: lea 0x8(%rsp),%rdx |
|
|
|
0x0000555555401bf1 <+74>: lea 0x10(%rsp),%r8 |
|
|
|
0x0000555555401bf6 <+79>: lea 0x1142(%rip),%rsi # 0x555555402d3f |
|
|
|
0x0000555555401bfd <+86>: lea 0x202bac(%rip),%rdi # 0x5555556047b0 <input_strings+240> |
|
|
|
~~~ |
|
|
|
|
|
|
|
输入`x/s 0x555555402d3f`得`"%d %d %s"`,结合之前拆弹经历,这是表示sscanf字符串的输入顺序 |
|
|
|
输入`x/s 0x5555556047b0`得`352 4` |
|
|
|
|
|
|
|
显然为phase_4触发 |
|
|
|
|
|
|
|
故在phase_4输入时改为`352 4 DrEvil` |
|
|
|
|
|
|
|
## 正式拆弹 |
|
|
|
|
|
|
|
### 查看secret_phase汇编 |
|
|
|
|
|
|
|
~~~python |
|
|
|
=> 0x0000555555401634 <+0>: push %rbx |
|
|
|
0x0000555555401635 <+1>: call 0x555555401a63 <read_line> |
|
|
|
0x000055555540163a <+6>: mov $0xa,%edx |
|
|
|
0x000055555540163f <+11>: mov $0x0,%esi |
|
|
|
0x0000555555401644 <+16>: mov %rax,%rdi |
|
|
|
0x0000555555401647 <+19>: call 0x555555400ec0 <strtol@plt> |
|
|
|
0x000055555540164c <+24>: mov %rax,%rbx |
|
|
|
0x000055555540164f <+27>: lea -0x1(%rax),%eax |
|
|
|
0x0000555555401652 <+30>: cmp $0x3e8,%eax |
|
|
|
0x0000555555401657 <+35>: ja 0x555555401684 <secret_phase+80> |
|
|
|
0x0000555555401659 <+37>: mov %ebx,%esi |
|
|
|
0x000055555540165b <+39>: lea 0x202aee(%rip),%rdi # 0x555555604150 <n1> |
|
|
|
0x0000555555401662 <+46>: call 0x5555554015f5 <fun7> |
|
|
|
0x0000555555401667 <+51>: cmp $0x2,%eax |
|
|
|
0x000055555540166a <+54>: je 0x555555401671 <secret_phase+61> |
|
|
|
0x000055555540166c <+56>: call 0x5555554019e6 <explode_bomb> |
|
|
|
0x0000555555401671 <+61>: lea 0x13d0(%rip),%rdi # 0x555555402a48 |
|
|
|
0x0000555555401678 <+68>: call 0x555555400e20 <puts@plt> |
|
|
|
0x000055555540167d <+73>: call 0x555555401ba7 <phase_defused> |
|
|
|
0x0000555555401682 <+78>: pop %rbx |
|
|
|
0x0000555555401683 <+79>: ret |
|
|
|
0x0000555555401684 <+80>: call 0x5555554019e6 <explode_bomb> |
|
|
|
0x0000555555401689 <+85>: jmp 0x555555401659 <secret_phase+37> |
|
|
|
~~~ |
|
|
|
|
|
|
|
strtol函数将字符串里的10进制整数提取出来 |
|
|
|
其返回值为对应的整数 |
|
|
|
|
|
|
|
将返回值减1后于0x3e8比较,如果小于等于就不引爆炸弹 |
|
|
|
随后令%esi也等于%ebx,即%rax最开始返回的整数 |
|
|
|
然后给%rdi赋给一个地址,从注释名称来看也为一个节点 |
|
|
|
随后进入`fun7`函数 |
|
|
|
|
|
|
|
查看`fun7`函数前先看看节点的结构 |
|
|
|
输入`x/256x 0x555555604150` |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x555555604150 <n1>: 0x24 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
|
|
|
0x555555604158 <n1+8>: 0x70 0x41 0x60 0x55 0x55 0x55 0x00 0x00 |
|
|
|
0x555555604160 <n1+16>: 0x90 0x41 0x60 0x55 0x55 0x55 0x00 0x00 |
|
|
|
0x555555604168: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
|
|
|
0x555555604170 <n21>: 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
|
|
|
0x555555604178 <n21+8>: 0xf0 0x41 0x60 0x55 0x55 0x55 0x00 0x00 |
|
|
|
0x555555604180 <n21+16>: 0xb0 0x41 0x60 0x55 0x55 0x55 0x00 0x00 |
|
|
|
0x555555604188: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
|
|
|
0x555555604190 <n22>: 0x32 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
|
|
|
0x555555604198 <n22+8>: 0xd0 0x41 0x60 0x55 0x55 0x55 0x00 0x00 |
|
|
|
0x5555556041a0 <n22+16>: 0x10 0x42 0x60 0x55 0x55 0x55 0x00 0x00 |
|
|
|
0x5555556041a8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
|
|
|
0x5555556041b0 <n32>: 0x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
|
|
|
0x5555556041b8 <n32+8>: 0xb0 0x40 0x60 0x55 0x55 0x55 0x00 0x00 |
|
|
|
0x5555556041c0 <n32+16>: 0x70 0x40 0x60 0x55 0x55 0x55 0x00 0x00 |
|
|
|
0x5555556041c8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
|
|
|
0x5555556041d0 <n33>: 0x2d 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
|
|
|
0x5555556041d8 <n33+8>: 0x10 0x40 0x60 0x55 0x55 0x55 0x00 0x00 |
|
|
|
0x5555556041e0 <n33+16>: 0xd0 0x40 0x60 0x55 0x55 0x55 0x00 0x00 |
|
|
|
0x5555556041e8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
|
|
|
0x5555556041f0 <n31>: 0x06 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
|
|
|
0x5555556041f8 <n31+8>: 0x30 0x40 0x60 0x55 0x55 0x55 0x00 0x00 |
|
|
|
0x555555604200 <n31+16>: 0x90 0x40 0x60 0x55 0x55 0x55 0x00 0x00 |
|
|
|
0x555555604208: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
|
|
|
0x555555604210 <n34>: 0x6b 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
|
|
|
0x555555604218 <n34+8>: 0x50 0x40 0x60 0x55 0x55 0x55 0x00 0x00 |
|
|
|
0x555555604220 <n34+16>: 0xf0 0x40 0x60 0x55 0x55 0x55 0x00 0x00 |
|
|
|
0x555555604228: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
|
|
|
~~~ |
|
|
|
|
|
|
|
显然这是一棵二叉树 |
|
|
|
前8字节储存数据 |
|
|
|
中8字节储存左子节点地址 |
|
|
|
后8字节储存右子节点地址 |
|
|
|
|
|
|
|
则%rdi的地址实际上是二叉树的根的地址 |
|
|
|
|
|
|
|
## 查看fun7函数汇编 |
|
|
|
|
|
|
|
~~~python |
|
|
|
=> 0x00005555554015f5 <+0>: test %rdi,%rdi |
|
|
|
0x00005555554015f8 <+3>: je 0x55555540162e <fun7+57> |
|
|
|
0x00005555554015fa <+5>: sub $0x8,%rsp |
|
|
|
0x00005555554015fe <+9>: mov (%rdi),%edx |
|
|
|
0x0000555555401600 <+11>: cmp %esi,%edx |
|
|
|
0x0000555555401602 <+13>: jg 0x555555401612 <fun7+29> |
|
|
|
0x0000555555401604 <+15>: mov $0x0,%eax |
|
|
|
0x0000555555401609 <+20>: cmp %esi,%edx |
|
|
|
0x000055555540160b <+22>: jne 0x55555540161f <fun7+42> |
|
|
|
0x000055555540160d <+24>: add $0x8,%rsp |
|
|
|
0x0000555555401611 <+28>: ret |
|
|
|
0x0000555555401612 <+29>: mov 0x8(%rdi),%rdi |
|
|
|
0x0000555555401616 <+33>: call 0x5555554015f5 <fun7> |
|
|
|
0x000055555540161b <+38>: add %eax,%eax |
|
|
|
0x000055555540161d <+40>: jmp 0x55555540160d <fun7+24> |
|
|
|
0x000055555540161f <+42>: mov 0x10(%rdi),%rdi |
|
|
|
0x0000555555401623 <+46>: call 0x5555554015f5 <fun7> |
|
|
|
0x0000555555401628 <+51>: lea 0x1(%rax,%rax,1),%eax |
|
|
|
0x000055555540162c <+55>: jmp 0x55555540160d <fun7+24> |
|
|
|
0x000055555540162e <+57>: mov $0xffffffff,%eax |
|
|
|
0x0000555555401633 <+62>: ret |
|
|
|
~~~ |
|
|
|
|
|
|
|
<+0> <+3>行判断%rdi是否等于0,是的话就跳转至<+57>行,让%eax等于-1并返回,不是则继续 |
|
|
|
<+5> 行压栈 |
|
|
|
<+9> 行读取根节点数据,赋给%edx |
|
|
|
<+11> <+13>行比较%esi和%edx,%esi即提取的整数,如果%edx大于%esi,则跳转至<+29>行 |
|
|
|
看看<+29>行结果 |
|
|
|
<+29> 行即读取左子节点数据并赋给%rdi自身 |
|
|
|
<+33> 行调用自身`fun7`函数 |
|
|
|
<+38> 行如果`fun7`函数执行完毕,令%eax*2 |
|
|
|
随后jmp至<+24>行,弹出栈并返回%eax |
|
|
|
|
|
|
|
如果%edx小于等于%esi |
|
|
|
<+15> 行令%eax为0 |
|
|
|
<+20> 行比较%esi %edx是否相等,相等则让%rsp+8,弹出栈,并返回,不相等则跳转到<+42行> |
|
|
|
顺着分支 |
|
|
|
<+42> 行即读取右子节点数据并赋给%rdi自身 |
|
|
|
<+46> 行调用自身`fun7`函数 |
|
|
|
<+51> 行如果`fun7`函数执行完毕,令%eax = 2*%eax + 1 |
|
|
|
随后jmp至<+24>行,弹出栈并返回%eax |
|
|
|
|
|
|
|
整体运行效果是提取出的整数%esi |
|
|
|
大于该节点数据,则向左子节点找 |
|
|
|
等于就弹出 |
|
|
|
小于该节点数据,就向右子节点找 |
|
|
|
并且如果节点数据有0立刻返回-1 |
|
|
|
|
|
|
|
## 回看secret_phase汇编 |
|
|
|
|
|
|
|
~~~python |
|
|
|
0x0000555555401662 <+46>: call 0x5555554015f5 <fun7> |
|
|
|
0x0000555555401667 <+51>: cmp $0x2,%eax |
|
|
|
0x000055555540166a <+54>: je 0x555555401671 <secret_phase+61> |
|
|
|
0x000055555540166c <+56>: call 0x5555554019e6 <explode_bomb> |
|
|
|
0x0000555555401671 <+61>: lea 0x13d0(%rip),%rdi # 0x555555402a48 |
|
|
|
0x0000555555401678 <+68>: call 0x555555400e20 <puts@plt> |
|
|
|
0x000055555540167d <+73>: call 0x555555401ba7 <phase_defused> |
|
|
|
0x0000555555401682 <+78>: pop %rbx |
|
|
|
0x0000555555401683 <+79>: ret |
|
|
|
0x0000555555401684 <+80>: call 0x5555554019e6 <explode_bomb> |
|
|
|
0x0000555555401689 <+85>: jmp 0x555555401659 <secret_phase+37> |
|
|
|
~~~ |
|
|
|
|
|
|
|
如果`fun7`最终返回数据为2就收尾并解除炸弹 |
|
|
|
如果不是就引爆炸弹 |
|
|
|
|
|
|
|
## 逆推数据 |
|
|
|
|
|
|
|
### 什么样才能返回2 |
|
|
|
|
|
|
|
显然,改变%rax只有四种方式 |
|
|
|
|
|
|
|
1. 节点储存数据为0,返回-1 |
|
|
|
2. 节点右子节点等于该输入数据,返回2*%eax+1 |
|
|
|
3. 节点左子节点等于该输入数据,返回2*%eax |
|
|
|
4. 当节点数据小于等于输入数据时,令%eax = 0 |
|
|
|
|
|
|
|
故组合一下,有 |
|
|
|
|
|
|
|
1. 最后一次搜索: 当节点数据小于等于输入数据时,令%eax = 0,此时恰好相等,返回,完成一次搜索 |
|
|
|
2. 倒数第二次搜索: 最后一次搜索由右子节点搜索进入,返回的是%eax = 0,随后返回2*%eax+1 = 1 |
|
|
|
3. 倒数第三次搜索: 倒数第二次搜索由左子节点搜索进入,返回的是%eax = 1,随后返回2*%eax = 2 |
|
|
|
4. 进入左子节点搜索是因为父节点数据大于用户输入的数据 |
|
|
|
|
|
|
|
推测进入方式为 n1->n21->n32 |
|
|
|
n32数据为0x16,转换成10进制为22 |
|
|
|
|
|
|
|
可以推测用户输入字段包括22即可 |
|
|
|
|
|
|
|
### 总结 |
|
|
|
|
|
|
|
输入22+string即可,如22nn |