首先安装 gdb
sudo apt install gdb
在bomb.c所在文件夹下打开命令行
gdb bomb
在gdb环境下调试
先在phase_1处打断点,然后运行
break phase_1
run
Phase_1 Come on!
[-------------------------------------code-------------------------------------]
0x4013dd <main+311>: call 0x401120 <__printf_chk@plt>
0x4013e2 <main+316>: mov edi,0x8
0x4013e7 <main+321>: call 0x401150 <exit@plt>
=> 0x4013ec <phase_1>: sub rsp,0x8
0x4013f0 <phase_1+4>: mov esi,0x403150
0x4013f5 <phase_1+9>: call 0x4018b8 <strings_not_equal>
0x4013fa <phase_1+14>: test eax,eax
0x4013fc <phase_1+16>: jne 0x401403 <phase_1+23>
[------------------------------------stack-------------------------------------]
此时看到了
输入命令查看
x/s 0x403150
即phase_1的答案为I am not part of the problem. I am a Republican.
炸弹炸了,退出后重新开启即可
q
重新进入
gdb bomb
给 phase_2添加断点
break phase_2
run
中途输入phase_1的答案
disas phase_2
申请栈空间,读取六个数
Dump of assembler code for function phase_2:
=> 0x000000000040140a <+0>: push rbx
0x000000000040140b <+1>: sub rsp,0x20
0x000000000040140f <+5>: mov rsi,rsp
0x0000000000401412 <+8>: call 0x4019be <read_six_numbers>
第一个数必须为0,否则爆炸
0x0000000000401417 <+13>: cmp DWORD PTR [rsp],0x0
0x000000000040141b <+17>: jne 0x401424 <phase_2+26>
0x0000000000401424 <+26>: call 0x40199a <explode_bomb>
第二数必须为1,否则爆炸
0x000000000040141d <+19>: cmp DWORD PTR [rsp+0x4],0x1
0x0000000000401422 <+24>: je 0x401429 <phase_2+31>
0x0000000000401424 <+26>: call 0x40199a <explode_bomb>
接下来是一个循环,实现的功能是 f[n] = f[n-1] + f[n-2] (n从第三个数开始)
细节如下
rax = rbx-0x1
rcx = rbx-0x1
mov eax,DWORD PTR [rsp+rax4]
add eax,DWORD PTR [rsp+rcx4]
0x0000000000401429 <+31>: mov ebx,0x2
0x000000000040142e <+36>: jmp 0x401438 <phase_2+46>
0x0000000000401430 <+38>: call 0x40199a <explode_bomb>
0x0000000000401435 <+43>: add ebx,0x1
0x0000000000401438 <+46>: cmp ebx,0x5
0x000000000040143b <+49>: jg 0x401458 <phase_2+78>
0x000000000040143d <+51>: movsxd rdx,ebx
0x0000000000401440 <+54>: lea ecx,[rbx-0x2]
0x0000000000401443 <+57>: movsxd rcx,ecx
0x0000000000401446 <+60>: lea eax,[rbx-0x1]
0x0000000000401449 <+63>: cdqe
0x000000000040144b <+65>: mov eax,DWORD PTR [rsp+rax*4]
0x000000000040144e <+68>: add eax,DWORD PTR [rsp+rcx*4]
0x0000000000401451 <+71>: cmp DWORD PTR [rsp+rdx*4],eax
0x0000000000401454 <+74>: je 0x401435 <phase_2+43>
0x0000000000401456 <+76>: jmp 0x401430 <phase_2+38>
0x0000000000401458 <+78>: add rsp,0x20
0x000000000040145c <+82>: pop rbx
0x000000000040145d <+83>: ret
由上面的推理可知,答案为0 1 1 2 3 5
同上,重新运行gdb,打断点,运行
disas phase_3
Dump of assembler code for function phase_3:
=> 0x000000000040145e <+0>: sub rsp,0x18
0x0000000000401462 <+4>: lea r8,[rsp+0x8]
0x0000000000401467 <+9>: lea rcx,[rsp+0x7]
0x000000000040146c <+14>: lea rdx,[rsp+0xc]
0x0000000000401471 <+19>: mov esi,0x4031ae
0x0000000000401476 <+24>: mov eax,0x0
0x000000000040147b <+29>: call 0x401110 <__isoc99_sscanf@plt>
0x0000000000401480 <+34>: cmp eax,0x2
0x0000000000401483 <+37>: jle 0x40149b <phase_3+61>
0x0000000000401485 <+39>: mov eax,DWORD PTR [rsp+0xc]
0x0000000000401489 <+43>: cmp eax,0x7
0x000000000040148c <+46>: ja 0x40159f <phase_3+321>
0x0000000000401492 <+52>: mov eax,eax
0x0000000000401494 <+54>: jmp QWORD PTR [rax*8+0x4031c0]
0x000000000040149b <+61>: call 0x40199a <explode_bomb>
0x00000000004014a0 <+66>: jmp 0x401485 <phase_3+39>
0x00000000004014a2 <+68>: cmp DWORD PTR [rsp+0x8],0x201
0x00000000004014aa <+76>: jne 0x4014b6 <phase_3+88>
0x00000000004014ac <+78>: mov eax,0x67
0x00000000004014b1 <+83>: jmp 0x4015a9 <phase_3+331>
0x00000000004014b6 <+88>: call 0x40199a <explode_bomb>
0x00000000004014bb <+93>: mov eax,0x67
0x00000000004014c0 <+98>: jmp 0x4015a9 <phase_3+331>
0x00000000004014c5 <+103>: cmp DWORD PTR [rsp+0x8],0x1bd
0x00000000004014cd <+111>: jne 0x4014d9 <phase_3+123>
0x00000000004014cf <+113>: mov eax,0x6a
0x00000000004014d4 <+118>: jmp 0x4015a9 <phase_3+331>
0x00000000004014d9 <+123>: call 0x40199a <explode_bomb>
0x00000000004014de <+128>: mov eax,0x6a
0x00000000004014e3 <+133>: jmp 0x4015a9 <phase_3+331>
0x00000000004014e8 <+138>: cmp DWORD PTR [rsp+0x8],0x1e8
0x00000000004014f0 <+146>: jne 0x4014fc <phase_3+158>
0x00000000004014f2 <+148>: mov eax,0x74
0x00000000004014f7 <+153>: jmp 0x4015a9 <phase_3+331>
0x00000000004014fc <+158>: call 0x40199a <explode_bomb>
0x0000000000401501 <+163>: mov eax,0x74
0x0000000000401506 <+168>: jmp 0x4015a9 <phase_3+331>
0x000000000040150b <+173>: cmp DWORD PTR [rsp+0x8],0x167
0x0000000000401513 <+181>: jne 0x40151f <phase_3+193>
0x0000000000401515 <+183>: mov eax,0x76
0x000000000040151a <+188>: jmp 0x4015a9 <phase_3+331>
0x000000000040151f <+193>: call 0x40199a <explode_bomb>
0x0000000000401524 <+198>: mov eax,0x76
0x0000000000401529 <+203>: jmp 0x4015a9 <phase_3+331>
0x000000000040152b <+205>: cmp DWORD PTR [rsp+0x8],0x3ac
0x0000000000401533 <+213>: jne 0x40153c <phase_3+222>
0x0000000000401535 <+215>: mov eax,0x65
0x000000000040153a <+220>: jmp 0x4015a9 <phase_3+331>
0x000000000040153c <+222>: call 0x40199a <explode_bomb>
0x0000000000401541 <+227>: mov eax,0x65
0x0000000000401546 <+232>: jmp 0x4015a9 <phase_3+331>
0x0000000000401548 <+234>: cmp DWORD PTR [rsp+0x8],0x177
0x0000000000401550 <+242>: jne 0x401559 <phase_3+251>
0x0000000000401552 <+244>: mov eax,0x71
0x0000000000401557 <+249>: jmp 0x4015a9 <phase_3+331>
0x0000000000401559 <+251>: call 0x40199a <explode_bomb>
0x000000000040155e <+256>: mov eax,0x71
0x0000000000401563 <+261>: jmp 0x4015a9 <phase_3+331>
0x0000000000401565 <+263>: cmp DWORD PTR [rsp+0x8],0x3bc
0x000000000040156d <+271>: jne 0x401576 <phase_3+280>
0x000000000040156f <+273>: mov eax,0x72
0x0000000000401574 <+278>: jmp 0x4015a9 <phase_3+331>
0x0000000000401576 <+280>: call 0x40199a <explode_bomb>
0x000000000040157b <+285>: mov eax,0x72
0x0000000000401580 <+290>: jmp 0x4015a9 <phase_3+331>
0x0000000000401582 <+292>: cmp DWORD PTR [rsp+0x8],0x334
0x000000000040158a <+300>: jne 0x401593 <phase_3+309>
0x000000000040158c <+302>: mov eax,0x75
0x0000000000401591 <+307>: jmp 0x4015a9 <phase_3+331>
0x0000000000401593 <+309>: call 0x40199a <explode_bomb>
0x0000000000401598 <+314>: mov eax,0x75
0x000000000040159d <+319>: jmp 0x4015a9 <phase_3+331>
0x000000000040159f <+321>: call 0x40199a <explode_bomb>
0x00000000004015a4 <+326>: mov eax,0x68
0x00000000004015a9 <+331>: cmp BYTE PTR [rsp+0x7],al
0x00000000004015ad <+335>: jne 0x4015b4 <phase_3+342>
0x00000000004015af <+337>: add rsp,0x18
0x00000000004015b3 <+341>: ret
0x00000000004015b4 <+342>: call 0x40199a <explode_bomb>
0x00000000004015b9 <+347>: jmp 0x4015af <phase_3+337>
End of assembler dump.
有点长,没事,分解开来,一部分一部分看
第一部分,申请栈空间,数据读入
=> 0x000000000040145e <+0>: sub rsp,0x18
0x0000000000401462 <+4>: lea r8,[rsp+0x8]
0x0000000000401467 <+9>: lea rcx,[rsp+0x7]
0x000000000040146c <+14>: lea rdx,[rsp+0xc]
0x0000000000401471 <+19>: mov esi,0x4031ae
0x0000000000401476 <+24>: mov eax,0x0
0x000000000040147b <+29>: call 0x401110 <__isoc99_sscanf@plt>
注意到调用了一个函数名为__isoc99_sscanf@plt,实际上它是一个库函数,在RUNOOB.COM查阅相关文档得知,sscanf的声明如下:
C 库函数
int sscanf(const char *str, const char *format, ...)
从字符串读取格式化输入。
意思就是根据指定的格式(format)提取字符串s中的内容,返回读取成功的内容个数,其中…就是提取内容存储的地址。
参数的要求很复杂,
下面的实例演示了 sscanf() 函数的用法。
#include
#include
#include
int main()
{
int day, year;
char weekday[20], month[20], dtm[100];
strcpy( dtm, "Saturday March 25 1989" );
sscanf( dtm, "%s %s %d %d", weekday, month, &day, &year );
printf("%s %d, %d = %s\n", month, day, year, weekday );
return(0);
}
让我们编译并运行上面的程序,这将产生以下结果:
March 25, 1989 = Saturday
通过对sscanf的了解,可以知道,需要输入三部分参数
0x0000000000401462 <+4>: lea r8,[rsp+0x8]
0x0000000000401467 <+9>: lea rcx,[rsp+0x7]
0x000000000040146c <+14>: lea rdx,[rsp+0xc]
0x0000000000401471 <+19>: mov esi,0x4031ae
0x0000000000401476 <+24>: mov eax,0x0
0x000000000040147b <+29>: call 0x401110 <__isoc99_sscanf@plt>
第一部分参数 $rdi 我们输入的字符串的地址
第二部分参数 $esi 输入字符串的格式
第三部分参数 $rdx $rcx $r8 用来存放内容的地址,从占用栈空间大小可知,一个char两个int
查看输入格式mov esi,0x4031ae
x/s 0x4031ae
查看输入的字符串
x/s $rdi
下面这部分表示,输入小于等于2个时爆炸,输入的第一个(栈底)数大于7时,爆炸
0x0000000000401480 <+34>: cmp eax,0x2
0x0000000000401483 <+37>: jle 0x40149b <phase_3+61>
0x0000000000401485 <+39>: mov eax,DWORD PTR [rsp+0xc]
0x0000000000401489 <+43>: cmp eax,0x7
0x000000000040148c <+46>: ja 0x40159f <phase_3+321>
下面这段代码表示,输入的第一个整数的范围为
<7
,然后根据第一个整数的值依次进行跳转到不同的case
,后面可知case
只有8个,该整数的范围为0~7
0x0000000000401485 <+39>: mov eax,DWORD PTR [rsp+0xc]
0x0000000000401489 <+43>: cmp eax,0x7
0x000000000040148c <+46>: ja 0x40159f <phase_3+321>
0x0000000000401492 <+52>: mov eax,eax
0x0000000000401494 <+54>: jmp QWORD PTR [rax*8+0x4031c0]
接着是一个典型的switch case 循环分支,数
cmp
的个数可知,共8个case
0x00000000004014a2 <+68>: cmp DWORD PTR [rsp+0x8],0x201
0x00000000004014aa <+76>: jne 0x4014b6 <phase_3+88>
0x00000000004014ac <+78>: mov eax,0x67
0x00000000004014b1 <+83>: jmp 0x4015a9 <phase_3+331>
0x00000000004014b6 <+88>: call 0x40199a <explode_bomb>
0x00000000004014bb <+93>: mov eax,0x67
0x00000000004014c0 <+98>: jmp 0x4015a9 <phase_3+331>
0x00000000004014c5 <+103>: cmp DWORD PTR [rsp+0x8],0x1bd
0x00000000004014cd <+111>: jne 0x4014d9 <phase_3+123>
0x00000000004014cf <+113>: mov eax,0x6a
0x00000000004014d4 <+118>: jmp 0x4015a9 <phase_3+331>
0x00000000004014d9 <+123>: call 0x40199a <explode_bomb>
0x00000000004014de <+128>: mov eax,0x6a
0x00000000004014e3 <+133>: jmp 0x4015a9 <phase_3+331>
0x00000000004014e8 <+138>: cmp DWORD PTR [rsp+0x8],0x1e8
0x00000000004014f0 <+146>: jne 0x4014fc <phase_3+158>
0x00000000004014f2 <+148>: mov eax,0x74
0x00000000004014f7 <+153>: jmp 0x4015a9 <phase_3+331>
0x00000000004014fc <+158>: call 0x40199a <explode_bomb>
0x0000000000401501 <+163>: mov eax,0x74
0x0000000000401506 <+168>: jmp 0x4015a9 <phase_3+331>
0x000000000040150b <+173>: cmp DWORD PTR [rsp+0x8],0x167
0x0000000000401513 <+181>: jne 0x40151f <phase_3+193>
0x0000000000401515 <+183>: mov eax,0x76
0x000000000040151a <+188>: jmp 0x4015a9 <phase_3+331>
0x000000000040151f <+193>: call 0x40199a <explode_bomb>
0x0000000000401524 <+198>: mov eax,0x76
0x0000000000401529 <+203>: jmp 0x4015a9 <phase_3+331>
0x000000000040152b <+205>: cmp DWORD PTR [rsp+0x8],0x3ac
0x0000000000401533 <+213>: jne 0x40153c <phase_3+222>
0x0000000000401535 <+215>: mov eax,0x65
0x000000000040153a <+220>: jmp 0x4015a9 <phase_3+331>
0x000000000040153c <+222>: call 0x40199a <explode_bomb>
0x0000000000401541 <+227>: mov eax,0x65
0x0000000000401546 <+232>: jmp 0x4015a9 <phase_3+331>
0x0000000000401548 <+234>: cmp DWORD PTR [rsp+0x8],0x177
0x0000000000401550 <+242>: jne 0x401559 <phase_3+251>
0x0000000000401552 <+244>: mov eax,0x71
0x0000000000401557 <+249>: jmp 0x4015a9 <phase_3+331>
0x0000000000401559 <+251>: call 0x40199a <explode_bomb>
0x000000000040155e <+256>: mov eax,0x71
0x0000000000401563 <+261>: jmp 0x4015a9 <phase_3+331>
0x0000000000401565 <+263>: cmp DWORD PTR [rsp+0x8],0x3bc
0x000000000040156d <+271>: jne 0x401576 <phase_3+280>
0x000000000040156f <+273>: mov eax,0x72
0x0000000000401574 <+278>: jmp 0x4015a9 <phase_3+331>
0x0000000000401576 <+280>: call 0x40199a <explode_bomb>
0x000000000040157b <+285>: mov eax,0x72
0x0000000000401580 <+290>: jmp 0x4015a9 <phase_3+331>
0x0000000000401582 <+292>: cmp DWORD PTR [rsp+0x8],0x334
0x000000000040158a <+300>: jne 0x401593 <phase_3+309>
0x000000000040158c <+302>: mov eax,0x75
0x0000000000401591 <+307>: jmp 0x4015a9 <phase_3+331>
0x00000000004015a9 <+331>: cmp BYTE PTR [rsp+0x7],al
0x00000000004015ad <+335>: jne 0x4015b4 <phase_3+342>
0x00000000004015af <+337>: add rsp,0x18
0x00000000004015b3 <+341>: ret
0x00000000004015b4 <+342>: call 0x40199a <explode_bomb>
0x00000000004015b9 <+347>: jmp 0x4015af <phase_3+337>
End of assembler dump.
以其中一个为例
0x00000000004014a2 <+68>: cmp DWORD PTR [rsp+0x8],0x201
0x00000000004014aa <+76>: jne 0x4014b6 <phase_3+88>
0x00000000004014ac <+78>: mov eax,0x67
0x00000000004014b1 <+83>: jmp 0x4015a9 <phase_3+331>
0x00000000004014b6 <+88>: call 0x40199a <explode_bomb>
0x00000000004014bb <+93>: mov eax,0x67
0x00000000004014c0 <+98>: jmp 0x4015a9 <phase_3+331>
如果输入的%d %c %d
的第二个整数值不为0x201 (十进制下的 513)
则寻找下一个case
否则第二个整数是0x201
,$rax=0x67
,跳转到
,如果输入的字符不等于$al
($rax=0x67
),则爆炸,否则弹栈结束,该字符为g
综上所述,其中一个可行的答案为 0 g 513
共有8个可行的答案
disas phase_4
gdb-peda$ disas phase_4
Dump of assembler code for function phase_4:
=> 0x00000000004015f8 <+0>: sub rsp,0x18
0x00000000004015fc <+4>: lea rcx,[rsp+0x8]
0x0000000000401601 <+9>: lea rdx,[rsp+0xc]
0x0000000000401606 <+14>: mov esi,0x40331f
0x000000000040160b <+19>: mov eax,0x0
0x0000000000401610 <+24>: call 0x401110 <__isoc99_sscanf@plt>
0x0000000000401615 <+29>: cmp eax,0x2
0x0000000000401618 <+32>: jne 0x401627 <phase_4+47>
0x000000000040161a <+34>: mov eax,DWORD PTR [rsp+0xc]
0x000000000040161e <+38>: test eax,eax
0x0000000000401620 <+40>: js 0x401627 <phase_4+47>
0x0000000000401622 <+42>: cmp eax,0xe
0x0000000000401625 <+45>: jle 0x40162c <phase_4+52>
0x0000000000401627 <+47>: call 0x40199a <explode_bomb>
0x000000000040162c <+52>: mov edx,0xe
0x0000000000401631 <+57>: mov esi,0x0
0x0000000000401636 <+62>: mov edi,DWORD PTR [rsp+0xc]
0x000000000040163a <+66>: call 0x4015bb <func4>
0x000000000040163f <+71>: cmp eax,0x2
0x0000000000401642 <+74>: jne 0x40164b <phase_4+83>
0x0000000000401644 <+76>: cmp DWORD PTR [rsp+0x8],0x2
0x0000000000401649 <+81>: je 0x401650 <phase_4+88>
0x000000000040164b <+83>: call 0x40199a <explode_bomb>
0x0000000000401650 <+88>: add rsp,0x18
0x0000000000401654 <+92>: ret
End of assembler dump.
突然来个短的,松了一口气
还是分块看
申请空间,输入参数,输入参数个数要为
2
=> 0x00000000004015f8 <+0>: sub rsp,0x18
0x00000000004015fc <+4>: lea rcx,[rsp+0x8]
0x0000000000401601 <+9>: lea rdx,[rsp+0xc]
0x0000000000401606 <+14>: mov esi,0x40331f
0x000000000040160b <+19>: mov eax,0x0
0x0000000000401610 <+24>: call 0x401110 <__isoc99_sscanf@plt>
0x0000000000401615 <+29>: cmp eax,0x2
0x0000000000401618 <+32>: jne 0x401627 <phase_4+47>
这不是sscanf
嘛,老熟人了,直接查看输入参数的格式
x/s 0x40331f
输入的第一个数
a[0]
, 要求0<=a[0]<=14
0x000000000040161a <+34>: mov eax,DWORD PTR [rsp+0xc]
0x000000000040161e <+38>: test eax,eax
0x0000000000401620 <+40>: js 0x401627 <phase_4+47>
0x0000000000401622 <+42>: cmp eax,0xe
0x0000000000401625 <+45>: jle 0x40162c <phase_4+52>
0x0000000000401627 <+47>: call 0x40199a <explode_bomb>
下面这部分表示,调用了
func4($edi=第一个输入参数, $esi=0x0, $edx=0xe)
,且该函数返回值要为0x2
并且输入的第二个参数等于0x2
0x000000000040162c <+52>: mov edx,0xe
0x0000000000401631 <+57>: mov esi,0x0
0x0000000000401636 <+62>: mov edi,DWORD PTR [rsp+0xc]
0x000000000040163a <+66>: call 0x4015bb <func4>
0x000000000040163f <+71>: cmp eax,0x2
0x0000000000401642 <+74>: jne 0x40164b <phase_4+83>
0x0000000000401644 <+76>: cmp DWORD PTR [rsp+0x8],0x2
0x0000000000401649 <+81>: je 0x401650 <phase_4+88>
0x000000000040164b <+83>: call 0x40199a <explode_bomb>
0x0000000000401650 <+88>: add rsp,0x18
0x0000000000401654 <+92>: ret
下面我们来看
的细节
func4($edi=第一个输入参数, $esi=0x0, $edx=0xe)
disas func4
Dump of assembler code for function func4:
0x00000000004015bb <+0>: sub rsp,0x8
0x00000000004015bf <+4>: mov ecx,edx
0x00000000004015c1 <+6>: sub ecx,esi
0x00000000004015c3 <+8>: mov eax,ecx
0x00000000004015c5 <+10>: shr eax,0x1f
0x00000000004015c8 <+13>: add eax,ecx
0x00000000004015ca <+15>: sar eax,1
0x00000000004015cc <+17>: add eax,esi
0x00000000004015ce <+19>: cmp eax,edi
0x00000000004015d0 <+21>: jg 0x4015de <func4+35>
0x00000000004015d2 <+23>: jl 0x4015ea <func4+47>
0x00000000004015d4 <+25>: mov eax,0x0
0x00000000004015d9 <+30>: add rsp,0x8
0x00000000004015dd <+34>: ret
0x00000000004015de <+35>: lea edx,[rax-0x1]
0x00000000004015e1 <+38>: call 0x4015bb <func4>
0x00000000004015e6 <+43>: add eax,eax
0x00000000004015e8 <+45>: jmp 0x4015d9 <func4+30>
0x00000000004015ea <+47>: lea esi,[rax+0x1]
0x00000000004015ed <+50>: call 0x4015bb <func4>
0x00000000004015f2 <+55>: lea eax,[rax+rax*1+0x1]
0x00000000004015f6 <+59>: jmp 0x4015d9 <func4+30>
End of assembler dump.
拆开看,
func4
的第一部分是对输入的三个参数进行操作,不妨令三个参数为x, y, z
func4(int x, int y, int z)
0x00000000004015bb <+0>: sub rsp,0x8
0x00000000004015bf <+4>: mov ecx,edx
0x00000000004015c1 <+6>: sub ecx,esi
0x00000000004015c3 <+8>: mov eax,ecx
0x00000000004015c5 <+10>: shr eax,0x1f
0x00000000004015c8 <+13>: add eax,ecx
0x00000000004015ca <+15>: sar eax,1
0x00000000004015cc <+17>: add eax,esi
此部分对应的C语言代码为
int temp = z - y;
temp = (z - y) >> 31 + temp;
temp >>= 1;
temp += y;
上面的操作相当于求mid = (x + y) / 2
, 但是由于 x + y
可能会溢出,于是作出了一步优化,防止溢出
你可能会困惑为什么要(z - y) >> 31
,int
除过符号位一共31位有效数字,符号位不动,移动之后+0 or -0
,相当于啥也没加,嘿嘿,恶心你
上面的操作可写为如下形式
int mid = y + (z - y) / 2;
func4
的第二部分
0x00000000004015ce <+19>: cmp eax,edi
0x00000000004015d0 <+21>: jg 0x4015de <func4+35>
0x00000000004015d2 <+23>: jl 0x4015ea <func4+47>
0x00000000004015d4 <+25>: mov eax,0x0
0x00000000004015d9 <+30>: add rsp,0x8
0x00000000004015dd <+34>: ret
0x00000000004015de <+35>: lea edx,[rax-0x1]
0x00000000004015e1 <+38>: call 0x4015bb <func4>
0x00000000004015e6 <+43>: add eax,eax
0x00000000004015e8 <+45>: jmp 0x4015d9 <func4+30>
0x00000000004015ea <+47>: lea esi,[rax+0x1]
0x00000000004015ed <+50>: call 0x4015bb <func4>
0x00000000004015f2 <+55>: lea eax,[rax+rax*1+0x1]
0x00000000004015f6 <+59>: jmp 0x4015d9 <func4+30>
此部分用C语言表示为
if (x > mid) {
func4(x, mid + 1, z);
edx = edx * 2;
}
else if (x < mid) {
func4(x, y, mid - 1);
edx = edx * 2 + 1;
}
else {
edx = 0;
return edx;
}
好家伙,真是难识汇编真面目,这不就是可爱的二分查找嘛
此处注意找到x
时,edx=0
,从二叉树右枝返回时edx = edx * 2 + 1;
,从二叉树左子树返回时edx = edx * 2
再联系我们需要的返回的结果为
0x2
,那么就需要
edx: 0->1->2
,即找到后edx = 0
,再从右子树返回edx = edx*2 + 1 = 1
,再从左子树返回edx = edx*2 = 2
- 或者
edx: 0->0->1->2
,即找到后edx = 0
,先从左子树返回edx = 0 * 2 = 0
,再从右子树返回edx = edx*2 + 1 = 1
,再从左子树返回edx = edx*2 = 2
由于该题的查找范围是
0~14
,所以二叉树的高度只有4
,最多往上回溯3
次,即
第一次向左找,查找范围为0~7
第二次向右找,查找范围为3~7
这样回溯前两次查找,即可实现最终edx = 2
- 第三次找到了,即该参数为
5
- 第三次没找到,只能到左子树才能使回溯的结果正确,查找范围
3~5
第四次找到,该参数为4
附上一颗二叉树,及画流程图的方法
综上所述,phase_4
的答案为5 2或者4 2
继续打断点,查看phase_5
的汇编代码
gdb-peda$ disas phase_5
Dump of assembler code for function phase_5:
=> 0x0000000000401655 <+0>: push rbx
0x0000000000401656 <+1>: sub rsp,0x10
0x000000000040165a <+5>: mov rbx,rdi
0x000000000040165d <+8>: call 0x4018a4 <string_length>
0x0000000000401662 <+13>: cmp eax,0x6
0x0000000000401665 <+16>: jne 0x40168b <phase_5+54>
0x0000000000401667 <+18>: mov eax,0x0
0x000000000040166c <+23>: cmp eax,0x5
0x000000000040166f <+26>: jg 0x401692 <phase_5+61>
0x0000000000401671 <+28>: movsxd rcx,eax
0x0000000000401674 <+31>: movzx edx,BYTE PTR [rbx+rcx*1]
0x0000000000401678 <+35>: and edx,0xf
0x000000000040167b <+38>: movzx edx,BYTE PTR [rdx+0x403200]
0x0000000000401682 <+45>: mov BYTE PTR [rsp+rcx*1+0x9],dl
0x0000000000401686 <+49>: add eax,0x1
0x0000000000401689 <+52>: jmp 0x40166c <phase_5+23>
0x000000000040168b <+54>: call 0x40199a <explode_bomb>
0x0000000000401690 <+59>: jmp 0x401667 <phase_5+18>
0x0000000000401692 <+61>: mov BYTE PTR [rsp+0xf],0x0
0x0000000000401697 <+66>: mov esi,0x4031b7
0x000000000040169c <+71>: lea rdi,[rsp+0x9]
0x00000000004016a1 <+76>: call 0x4018b8 <strings_not_equal>
0x00000000004016a6 <+81>: test eax,eax
0x00000000004016a8 <+83>: jne 0x4016b0 <phase_5+91>
0x00000000004016aa <+85>: add rsp,0x10
0x00000000004016ae <+89>: pop rbx
0x00000000004016af <+90>: ret
0x00000000004016b0 <+91>: call 0x40199a <explode_bomb>
0x00000000004016b5 <+96>: jmp 0x4016aa <phase_5+85>
End of assembler dump.
第一部分,输入一个长度为
6
的字符串
=> 0x0000000000401655 <+0>: push rbx
0x0000000000401656 <+1>: sub rsp,0x10
0x000000000040165a <+5>: mov rbx,rdi
0x000000000040165d <+8>: call 0x4018a4 <string_length>
0x0000000000401662 <+13>: cmp eax,0x6
0x0000000000401665 <+16>: jne 0x40168b <phase_5+54>
第二部分,是一个典型的循环结构,遍历字符数组
str[0]~str[5]
,进行判断
0x0000000000401667 <+18>: mov eax,0x0
0x000000000040166c <+23>: cmp eax,0x5
0x000000000040166f <+26>: jg 0x401692 <phase_5+61>
0x0000000000401671 <+28>: movsxd rcx,eax
0x0000000000401674 <+31>: movzx edx,BYTE PTR [rbx+rcx*1]
0x0000000000401678 <+35>: and edx,0xf
0x000000000040167b <+38>: movzx edx,BYTE PTR [rdx+0x403200]
0x0000000000401682 <+45>: mov BYTE PTR [rsp+rcx*1+0x9],dl
0x0000000000401686 <+49>: add eax,0x1
0x0000000000401689 <+52>: jmp 0x40166c <phase_5+23>
0x000000000040168b <+54>: call 0x40199a <explode_bomb>
0x0000000000401690 <+59>: jmp 0x401667 <phase_5+18>
0x0000000000401692 <+61>: mov BYTE PTR [rsp+0xf],0x0
上面这段代码进行的操作是
遍历字符数组movzx edx,BYTE PTR [rbx+rcx*1]
取出其ASCII码的低四位and edx,0xf
,不妨设为m
利用m
作为索引,取出在地址0x403200
下存的对应字符movzx edx,BYTE PTR [rdx+0x403200]
设计个字符串还不忘嘲讽,非拆不可
将对应的6
个字符取出,后面要用mov BYTE PTR [rsp+rcx*1+0x9],dl
第三部分,将上面得到的字符串与
$esi
中的字符串进行比较,相同即可
0x0000000000401692 <+61>: mov BYTE PTR [rsp+0xf],0x0
0x0000000000401697 <+66>: mov esi,0x4031b7
0x000000000040169c <+71>: lea rdi,[rsp+0x9]
0x00000000004016a1 <+76>: call 0x4018b8 <strings_not_equal>
0x00000000004016a6 <+81>: test eax,eax
0x00000000004016a8 <+83>: jne 0x4016b0 <phase_5+91>
0x00000000004016aa <+85>: add rsp,0x10
0x00000000004016ae <+89>: pop rbx
0x00000000004016af <+90>: ret
首先查看$esi
中的字符串
x/s 0x4031b7
即在maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?中选出flyers即可
即输入字符低四位对应的十进制数m
应为9 15 14 5 6 7
即对应的ASCII码的低四位为1001
1111
1110
0101
0110
0111
查表可知
str[0]&0xf == 1001
可以是) y Y i I
str[1]&0xf == 1111
可以是/ ? _ o O
str[2]&0xf == 1110
可以是. > N ^ n ~
str[3]&0xf == 0101
可以是% 5 E e U u
str[4]&0xf == 0110
可以是& 6 F f V v
str[5]&0xf == 0111
可以是’ 7 G g W w
总上所述,phase_5的答案可为上述6个字符的任意组合,ionefg
是一个答案
最初存在一个链表,而且链表中有乱序的数值,按照我们输入的6
个数字对链表进行重新排序,要求排序之后的链表中的数值按降序(从大到小)排列
再次运行,查看汇编代码
gdb-peda$ disas phase_6
Dump of assembler code for function phase_6:
=> 0x00000000004016b7 <+0>: push r12
0x00000000004016b9 <+2>: push rbp
0x00000000004016ba <+3>: push rbx
0x00000000004016bb <+4>: sub rsp,0x50
0x00000000004016bf <+8>: lea rsi,[rsp+0x30]
0x00000000004016c4 <+13>: call 0x4019be <read_six_numbers>
0x00000000004016c9 <+18>: mov ebp,0x0
0x00000000004016ce <+23>: jmp 0x4016f9 <phase_6+66>
0x00000000004016d0 <+25>: call 0x40199a <explode_bomb>
0x00000000004016d5 <+30>: jmp 0x40170d <phase_6+86>
0x00000000004016d7 <+32>: add ebx,0x1
0x00000000004016da <+35>: cmp ebx,0x5
0x00000000004016dd <+38>: jg 0x4016f6 <phase_6+63>
0x00000000004016df <+40>: movsxd rax,ebp
0x00000000004016e2 <+43>: movsxd rdx,ebx
0x00000000004016e5 <+46>: mov edi,DWORD PTR [rsp+rdx*4+0x30]
0x00000000004016e9 <+50>: cmp DWORD PTR [rsp+rax*4+0x30],edi
0x00000000004016ed <+54>: jne 0x4016d7 <phase_6+32>
0x00000000004016ef <+56>: call 0x40199a <explode_bomb>
0x00000000004016f4 <+61>: jmp 0x4016d7 <phase_6+32>
0x00000000004016f6 <+63>: mov ebp,r12d
0x00000000004016f9 <+66>: cmp ebp,0x5
0x00000000004016fc <+69>: jg 0x401716 <phase_6+95>
0x00000000004016fe <+71>: movsxd rax,ebp
0x0000000000401701 <+74>: mov eax,DWORD PTR [rsp+rax*4+0x30]
0x0000000000401705 <+78>: sub eax,0x1
0x0000000000401708 <+81>: cmp eax,0x5
0x000000000040170b <+84>: ja 0x4016d0 <phase_6+25>
0x000000000040170d <+86>: lea r12d,[rbp+0x1]
0x0000000000401711 <+90>: mov ebx,r12d
0x0000000000401714 <+93>: jmp 0x4016da <phase_6+35>
0x0000000000401716 <+95>: mov esi,0x0
0x000000000040171b <+100>: jmp 0x401724 <phase_6+109>
0x000000000040171d <+102>: mov QWORD PTR [rsp+rcx*8],rdx
0x0000000000401721 <+106>: add esi,0x1
0x0000000000401724 <+109>: cmp esi,0x5
0x0000000000401727 <+112>: jg 0x401745 <phase_6+142>
0x0000000000401729 <+114>: mov eax,0x1
0x000000000040172e <+119>: mov edx,0x4052d0
0x0000000000401733 <+124>: movsxd rcx,esi
0x0000000000401736 <+127>: cmp DWORD PTR [rsp+rcx*4+0x30],eax
0x000000000040173a <+131>: jle 0x40171d <phase_6+102>
0x000000000040173c <+133>: mov rdx,QWORD PTR [rdx+0x8]
0x0000000000401740 <+137>: add eax,0x1
0x0000000000401743 <+140>: jmp 0x401733 <phase_6+124>
0x0000000000401745 <+142>: mov rbx,QWORD PTR [rsp]
0x0000000000401749 <+146>: mov rcx,rbx
0x000000000040174c <+149>: mov eax,0x1
0x0000000000401751 <+154>: jmp 0x401764 <phase_6+173>
0x0000000000401753 <+156>: movsxd rdx,eax
0x0000000000401756 <+159>: mov rdx,QWORD PTR [rsp+rdx*8]
0x000000000040175a <+163>: mov QWORD PTR [rcx+0x8],rdx
0x000000000040175e <+167>: add eax,0x1
0x0000000000401761 <+170>: mov rcx,rdx
0x0000000000401764 <+173>: cmp eax,0x5
0x0000000000401767 <+176>: jle 0x401753 <phase_6+156>
0x0000000000401769 <+178>: mov QWORD PTR [rcx+0x8],0x0
0x0000000000401771 <+186>: mov ebp,0x0
0x0000000000401776 <+191>: jmp 0x40177f <phase_6+200>
0x0000000000401778 <+193>: mov rbx,QWORD PTR [rbx+0x8]
0x000000000040177c <+197>: add ebp,0x1
0x000000000040177f <+200>: cmp ebp,0x4
0x0000000000401782 <+203>: jg 0x401795 <phase_6+222>
0x0000000000401784 <+205>: mov rax,QWORD PTR [rbx+0x8]
0x0000000000401788 <+209>: mov eax,DWORD PTR [rax]
0x000000000040178a <+211>: cmp DWORD PTR [rbx],eax
0x000000000040178c <+213>: jge 0x401778 <phase_6+193>
0x000000000040178e <+215>: call 0x40199a <explode_bomb>
0x0000000000401793 <+220>: jmp 0x401778 <phase_6+193>
0x0000000000401795 <+222>: add rsp,0x50
0x0000000000401799 <+226>: pop rbx
0x000000000040179a <+227>: pop rbp
0x000000000040179b <+228>: pop r12
0x000000000040179d <+230>: ret
End of assembler dump.
好家伙,令人窒息的长度,加油,逐个击破
第一部分,先读了
6
个数
=> 0x00000000004016b7 <+0>: push r12
0x00000000004016b9 <+2>: push rbp
0x00000000004016ba <+3>: push rbx
0x00000000004016bb <+4>: sub rsp,0x50
0x00000000004016bf <+8>: lea rsi,[rsp+0x30]
0x00000000004016c4 <+13>: call 0x4019be <read_six_numbers>
第二部分,一个二重循环
0x00000000004016c9 <+18>: mov ebp,0x0
0x00000000004016ce <+23>: jmp 0x4016f9 <phase_6+66>
0x00000000004016d0 <+25>: call 0x40199a <explode_bomb>
0x00000000004016d5 <+30>: jmp 0x40170d <phase_6+86>
0x00000000004016d7 <+32>: add ebx,0x1
0x00000000004016da <+35>: cmp ebx,0x5
0x00000000004016dd <+38>: jg 0x4016f6 <phase_6+63>
0x00000000004016df <+40>: movsxd rax,ebp
0x00000000004016e2 <+43>: movsxd rdx,ebx
0x00000000004016e5 <+46>: mov edi,DWORD PTR [rsp+rdx*4+0x30]
0x00000000004016e9 <+50>: cmp DWORD PTR [rsp+rax*4+0x30],edi
0x00000000004016ed <+54>: jne 0x4016d7 <phase_6+32>
0x00000000004016ef <+56>: call 0x40199a <explode_bomb>
0x00000000004016f4 <+61>: jmp 0x4016d7 <phase_6+32>
0x00000000004016f6 <+63>: mov ebp,r12d
0x00000000004016f9 <+66>: cmp ebp,0x5
0x00000000004016fc <+69>: jg 0x401716 <phase_6+95>
0x00000000004016fe <+71>: movsxd rax,ebp
0x0000000000401701 <+74>: mov eax,DWORD PTR [rsp+rax*4+0x30]
0x0000000000401705 <+78>: sub eax,0x1
0x0000000000401708 <+81>: cmp eax,0x5
0x000000000040170b <+84>: ja 0x4016d0 <phase_6+25>
0x000000000040170d <+86>: lea r12d,[rbp+0x1]
0x0000000000401711 <+90>: mov ebx,r12d
0x0000000000401714 <+93>: jmp 0x4016da <phase_6+35>
0x0000000000401716 <+95>: mov esi,0x0
该二重循环实现了两个功能,我们分开来看
1. 输入的6个数都不大于5
0x00000000004016fe <+71>: movsxd rax,ebp
0x0000000000401701 <+74>: mov eax,DWORD PTR [rsp+rax*4+0x30]
0x0000000000401705 <+78>: sub eax,0x1
0x0000000000401708 <+81>: cmp eax,0x5
0x000000000040170b <+84>: ja 0x4016d0 <phase_6+25>
2. 输入的6个数各不相同
0x00000000004016d7 <+32>: add ebx,0x1
0x00000000004016da <+35>: cmp ebx,0x5
0x00000000004016dd <+38>: jg 0x4016f6 <phase_6+63>
0x00000000004016df <+40>: movsxd rax,ebp
0x00000000004016e2 <+43>: movsxd rdx,ebx
0x00000000004016e5 <+46>: mov edi,DWORD PTR [rsp+rdx*4+0x30]
0x00000000004016e9 <+50>: cmp DWORD PTR [rsp+rax*4+0x30],edi
0x00000000004016ed <+54>: jne 0x4016d7 <phase_6+32>
0x00000000004016ef <+56>: call 0x40199a <explode_bomb>
上面的汇编相当于以下C代码
for (int i = 0; i <= 5; i++) {
for (int j = i + 1; j <= 5; j++) {
if (num[i] == num[j]) explode_bomb();
}
}
第三部分,将输入的
6
个数字放入链表节点中,按照降序重组链表
又一个二重循环,根据输入的6
个整数,将对应的链表节点的地址存储在新数组order[]
里
0x0000000000401716 <+95>: mov esi,0x0
0x000000000040171b <+100>: jmp 0x401724 <phase_6+109>
0x000000000040171d <+102>: mov QWORD PTR [rsp+rcx*8],rdx
0x0000000000401721 <+106>: add esi,0x1
0x0000000000401724 <+109>: cmp esi,0x5
0x0000000000401727 <+112>: jg 0x401745 <phase_6+142>
0x0000000000401729 <+114>: mov eax,0x1
0x000000000040172e <+119>: mov edx,0x4052d0
0x0000000000401733 <+124>: movsxd rcx,esi
0x0000000000401736 <+127>: cmp DWORD PTR [rsp+rcx*4+0x30],eax
0x000000000040173a <+131>: jle 0x40171d <phase_6+102>
0x000000000040173c <+133>: mov rdx,QWORD PTR [rdx+0x8]
0x0000000000401740 <+137>: add eax,0x1
0x0000000000401743 <+140>: jmp 0x401733 <phase_6+124>
0x0000000000401745 <+142>: mov rbx,QWORD PTR [rsp]
查看0x4052d0,可以发现这是一个由6
个节点链接而成的链表
x/12xg 0x4052d0
for (int i = 0; i <= 5; i++) {
node *p = head;
for (int count = 1; count <= 5; count++, p = p->next) {
if (count == arr[i]) { // arr[]为输入数组
order[i] = p;
}
}
}
一重循环,将链表按照order[]
重组,重组之后的链表中的值按照降序排列
0x0000000000401745 <+142>: mov rbx,QWORD PTR [rsp]
0x0000000000401749 <+146>: mov rcx,rbx
0x000000000040174c <+149>: mov eax,0x1
0x0000000000401751 <+154>: jmp 0x401764 <phase_6+173>
0x0000000000401753 <+156>: movsxd rdx,eax
0x0000000000401756 <+159>: mov rdx,QWORD PTR [rsp+rdx*8]
0x000000000040175a <+163>: mov QWORD PTR [rcx+0x8],rdx
0x000000000040175e <+167>: add eax,0x1
0x0000000000401761 <+170>: mov rcx,rdx
0x0000000000401764 <+173>: cmp eax,0x5
0x0000000000401767 <+176>: jle 0x401753 <phase_6+156>
第四部分,判断,如果链表中的数字不是降序的,则炸弹爆炸
0x0000000000401769 <+178>: mov QWORD PTR [rcx+0x8],0x0
0x0000000000401771 <+186>: mov ebp,0x0
0x0000000000401776 <+191>: jmp 0x40177f <phase_6+200>
0x0000000000401778 <+193>: mov rbx,QWORD PTR [rbx+0x8]
0x000000000040177c <+197>: add ebp,0x1
0x000000000040177f <+200>: cmp ebp,0x4
0x0000000000401782 <+203>: jg 0x401795 <phase_6+222>
0x0000000000401784 <+205>: mov rax,QWORD PTR [rbx+0x8]
0x0000000000401788 <+209>: mov eax,DWORD PTR [rax]
0x000000000040178a <+211>: cmp DWORD PTR [rbx],eax
0x000000000040178c <+213>: jge 0x401778 <phase_6+193>
0x000000000040178e <+215>: call 0x40199a <explode_bomb>
0x0000000000401793 <+220>: jmp 0x401778 <phase_6+193>
0x0000000000401795 <+222>: add rsp,0x50
0x0000000000401799 <+226>: pop rbx
0x000000000040179a <+227>: pop rbp
0x000000000040179b <+228>: pop r12
0x000000000040179d <+230>: ret
在看一下原来链表中数值的排列情况
gdb-peda$ x/12xg 0x4052d0
0x4052d0 <node1>: 0x0000000100000062 0x00000000004052e0
0x4052e0 <node2>: 0x0000000200000350 0x00000000004052f0
0x4052f0 <node3>: 0x0000000300000379 0x0000000000405300
0x405300 <node4>: 0x0000000400000230 0x0000000000405310
0x405310 <node5>: 0x00000005000001fd 0x0000000000405320
0x405320 <node6>: 0x00000006000000da 0x0000000000000000
<node>: val next
对其进行排序,从大到小
node1: 0x62 order:6
node2: 0x350 order:2
node3: 0x379 order:1
node4: 0x230 order:3
node5: 0x1fd order:4
node6: 0xda order:5
总上所述,phase_6的答案为3
2
4
5
6
1
在bomb.c的最后有一句提示,说明还有隐藏关卡
/* Wow, they got it! But isn't something... missing? Perhaps
* something they overlooked? Mua ha ha ha ha! */
是的,这个secret_phase
藏在函数phase_defused
里
查看phase_defused
disas phase_defused
gdb-peda$ disas phase_defused
Dump of assembler code for function phase_defused:
0x0000000000401b2b <+0>: cmp DWORD PTR [rip+0x3c3a],0x6 # 0x40576c
0x0000000000401b32 <+7>: je 0x401b35 <phase_defused+10>
0x0000000000401b34 <+9>: ret
0x0000000000401b35 <+10>: sub rsp,0x68
0x0000000000401b39 <+14>: lea r8,[rsp+0x10]
0x0000000000401b3e <+19>: lea rcx,[rsp+0x8]
0x0000000000401b43 <+24>: lea rdx,[rsp+0xc]
0x0000000000401b48 <+29>: mov esi,0x403369
0x0000000000401b4d <+34>: mov edi,0x405870
0x0000000000401b52 <+39>: mov eax,0x0
0x0000000000401b57 <+44>: call 0x401110 <__isoc99_sscanf@plt>
0x0000000000401b5c <+49>: cmp eax,0x3
0x0000000000401b5f <+52>: je 0x401b70 <phase_defused+69>
0x0000000000401b61 <+54>: mov edi,0x4032a8
0x0000000000401b66 <+59>: call 0x401060 <puts@plt>
0x0000000000401b6b <+64>: add rsp,0x68
0x0000000000401b6f <+68>: ret
0x0000000000401b70 <+69>: mov esi,0x403372
0x0000000000401b75 <+74>: lea rdi,[rsp+0x10]
0x0000000000401b7a <+79>: call 0x4018b8 <strings_not_equal>
0x0000000000401b7f <+84>: test eax,eax
0x0000000000401b81 <+86>: jne 0x401b61 <phase_defused+54>
0x0000000000401b83 <+88>: mov edi,0x403248
0x0000000000401b88 <+93>: call 0x401060 <puts@plt>
0x0000000000401b8d <+98>: mov edi,0x403270
0x0000000000401b92 <+103>: call 0x401060 <puts@plt>
0x0000000000401b97 <+108>: mov eax,0x0
0x0000000000401b9c <+113>: call 0x4017db <secret_phase>
0x0000000000401ba1 <+118>: jmp 0x401b61 <phase_defused+54>
End of assembler dump.
该部分汇编表示,需要用sscanf()读入,并利用函数
那么问题来了,在那里输入字符串呢?(入口呢?)
首先查看sscanf()的读入格式
x/s 0x403369
由此我们猜测,该字符串是在读入两个整数之后读取的
回忆之前几个关卡,只有phase_3
和phase_4
用到了sscanf()
,而且其读入格式分别为
phase_3 : %d %c %d
phase_4 : %d %d
那么,触发secret_phase
的字符串就应该是在phase_4
读入两个整数之后读入的
查看得知,需要读入的字符串是
0x0000000000401b70 <+69>: mov esi,0x403372
0x0000000000401b75 <+74>: lea rdi,[rsp+0x10]
0x0000000000401b7a <+79>: call 0x4018b8 <strings_not_equal>
gdb-peda$ x/s 0x403372
0x403372: "DrEvil"
于是我们在phase_4
读入两个整数之后加上DrEvil
,即可进入secret_phase
下面我们来看
secret_phase
disas secret_phase
gdb-peda$ disas secret_phase
Dump of assembler code for function secret_phase:
0x00000000004017db <+0>: push rbx
0x00000000004017dc <+1>: call 0x4019fd <read_line>
0x00000000004017e1 <+6>: mov rdi,rax
0x00000000004017e4 <+9>: call 0x401140 <atoi@plt>
0x00000000004017e9 <+14>: mov ebx,eax
0x00000000004017eb <+16>: lea eax,[rax-0x1]
0x00000000004017ee <+19>: cmp eax,0x3e8
0x00000000004017f3 <+24>: ja 0x401817 <secret_phase+60>
0x00000000004017f5 <+26>: mov esi,ebx
0x00000000004017f7 <+28>: mov edi,0x4050f0
0x00000000004017fc <+33>: call 0x40179e <fun7>
0x0000000000401801 <+38>: cmp eax,0x5
0x0000000000401804 <+41>: jne 0x40181e <secret_phase+67>
0x0000000000401806 <+43>: mov edi,0x403188
0x000000000040180b <+48>: call 0x401060 <puts@plt>
0x0000000000401810 <+53>: call 0x401b2b <phase_defused>
0x0000000000401815 <+58>: pop rbx
0x0000000000401816 <+59>: ret
0x0000000000401817 <+60>: call 0x40199a <explode_bomb>
0x000000000040181c <+65>: jmp 0x4017f5 <secret_phase+26>
0x000000000040181e <+67>: call 0x40199a <explode_bomb>
0x0000000000401823 <+72>: jmp 0x401806 <secret_phase+43>
End of assembler dump.
首先需要我们输入一个参数,该参数要小于等于0x3e8
下面代码说明,在调用fun7之前,准备了两个参数,$esi
是我们输入的一个参数,$edi
是一个地址0x4050f0
且fun7返回的结果要等于0x5
0x00000000004017f5 <+26>: mov esi,ebx
0x00000000004017f7 <+28>: mov edi,0x4050f0
0x00000000004017fc <+33>: call 0x40179e <fun7>
0x0000000000401801 <+38>: cmp eax,0x5
0x0000000000401804 <+41>: jne 0x40181e <secret_phase+67>
查看0x4050f0
地址下的内容
x/60xg 0x4050f0
gdb-peda$ x/60xg 0x4050f0
0x4050f0 <n1>: 0x0000000000000024 0x0000000000405110
0x405100 <n1+16>: 0x0000000000405130 0x0000000000000000
0x405110 <n21>: 0x0000000000000008 0x0000000000405190
0x405120 <n21+16>: 0x0000000000405150 0x0000000000000000
0x405130 <n22>: 0x0000000000000032 0x0000000000405170
0x405140 <n22+16>: 0x00000000004051b0 0x0000000000000000
0x405150 <n32>: 0x0000000000000016 0x0000000000405270
0x405160 <n32+16>: 0x0000000000405230 0x0000000000000000
0x405170 <n33>: 0x000000000000002d 0x00000000004051d0
0x405180 <n33+16>: 0x0000000000405290 0x0000000000000000
0x405190 <n31>: 0x0000000000000006 0x00000000004051f0
0x4051a0 <n31+16>: 0x0000000000405250 0x0000000000000000
0x4051b0 <n34>: 0x000000000000006b 0x0000000000405210
0x4051c0 <n34+16>: 0x00000000004052b0 0x0000000000000000
0x4051d0 <n45>: 0x0000000000000028 0x0000000000000000
0x4051e0 <n45+16>: 0x0000000000000000 0x0000000000000000
0x4051f0 <n41>: 0x0000000000000001 0x0000000000000000
0x405200 <n41+16>: 0x0000000000000000 0x0000000000000000
0x405210 <n47>: 0x0000000000000063 0x0000000000000000
0x405220 <n47+16>: 0x0000000000000000 0x0000000000000000
0x405230 <n44>: 0x0000000000000023 0x0000000000000000
0x405240 <n44+16>: 0x0000000000000000 0x0000000000000000
0x405250 <n42>: 0x0000000000000007 0x0000000000000000
0x405260 <n42+16>: 0x0000000000000000 0x0000000000000000
0x405270 <n43>: 0x0000000000000014 0x0000000000000000
0x405280 <n43+16>: 0x0000000000000000 0x0000000000000000
0x405290 <n46>: 0x000000000000002f 0x0000000000000000
0x4052a0 <n46+16>: 0x0000000000000000 0x0000000000000000
0x4052b0 <n48>: 0x00000000000003e9 0x0000000000000000
0x4052c0 <n48+16>: 0x0000000000000000 0x0000000000000000
有了phase_6
的经验,可以看出这是一棵二叉树
从二叉树可以看出,左儿子小于父节点,右儿子大于父节点,又是一棵二叉查找树,于是可以猜测fun7
的功能可能又是二分查找
下面查看
fun7()
disas fun7
gdb-peda$ disas fun7
Dump of assembler code for function fun7:
0x000000000040179e <+0>: test rdi,rdi
0x00000000004017a1 <+3>: je 0x4017d5 <fun7+55>
0x00000000004017a3 <+5>: sub rsp,0x8
0x00000000004017a7 <+9>: mov eax,DWORD PTR [rdi]
0x00000000004017a9 <+11>: cmp eax,esi
0x00000000004017ab <+13>: jg 0x4017b9 <fun7+27>
0x00000000004017ad <+15>: jne 0x4017c6 <fun7+40>
0x00000000004017af <+17>: mov eax,0x0
0x00000000004017b4 <+22>: add rsp,0x8
0x00000000004017b8 <+26>: ret
0x00000000004017b9 <+27>: mov rdi,QWORD PTR [rdi+0x8]
0x00000000004017bd <+31>: call 0x40179e <fun7>
0x00000000004017c2 <+36>: add eax,eax
0x00000000004017c4 <+38>: jmp 0x4017b4 <fun7+22>
0x00000000004017c6 <+40>: mov rdi,QWORD PTR [rdi+0x10]
0x00000000004017ca <+44>: call 0x40179e <fun7>
0x00000000004017cf <+49>: lea eax,[rax+rax*1+0x1]
0x00000000004017d3 <+53>: jmp 0x4017b4 <fun7+22>
0x00000000004017d5 <+55>: mov eax,0xffffffff
0x00000000004017da <+60>: ret
End of assembler dump.
果然是典型的二分查找,只不过是利用二叉查找树了而已
将fun7(int val, node* root)
用C语言表示
int fun7(int val, node* root) {
if (root == NULL) return -1;
if (val < root->val) {
return 2 * fun7(val, root->left);
} else if (val > root->val) {
return 2 * fun7(val, root->right) + 1;
} else {
return 0;
}
}
由此可见,只需要反推出返回值为0x5
时的输入val
即可
能够产生5
的过程有很多,但是综合考虑该二叉树高度为4
,只能作四次运算,所以产生5
的方式只能是0 -> 1 -> 2 -> 5,即
如图所示
所以secret_phase的答案为0x2f == 47
拆弹成功!请叫我不那么专业的拆弹专家
LInux 常见命令
C 汇编 Linux 手册
gedit便捷插件配置
以下语句导入插件库
sudo apt-get install gedit-plugins
生 成 s a m p l e . c 文 件 {\color{blue}{生成sample.c文件}} 生成sample.c文件
gedit sample.c
生 成 s a m p l e . o u t 可 执 行 文 件 {\color{blue}{生成sample.out可执行文件}} 生成sample.out可执行文件
直接生成 a.out
gcc sample.c
自命名 sample.out 文件
gcc sample.c -o sample.out
sample.c 如下
#include
#include
#include
typedef struct node *link;
struct node {
unsigned char item;
link next;
};
static link head = NULL; // 头指针
link make_node(unsigned char item) {
link p = malloc(sizeof *p);
p->item = item;
p->next = NULL;
return p;
}
void insert(link p) {
p->next = head;
head = p;
}
void free_node(link p) {
free(p);
}
link pop(void) {
if (head == NULL) {
return NULL;
} else {
link p = head;
head = head->next;
return p;
}
}
void print_item(link p) {
printf("%d->", p->item);
}
void fighting_function() {
printf("I can do it!\n");
}
int *swap(int *px, int *py) {
int temp;
temp = *px;
*px = *py;
*py = temp;
return px;
}
int main() {
/* 字符串比较 */
printf("请输入等待比较的字符串:\n");
char str1[50] = "Full hope full energy!";
char str2[50] = {0};
gets(str2);
printf("result = %d\n", strcmp(str1, str2));
/* 循环 */
// 一层
for (int i = 0; i < 5; i ++) {
printf("%c", 'h');
}
printf("\n");
// 两层
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
printf("%d %d\n", i, j);
}
}
/* 分支语句 */
// if else
int k = 1;
if (k == 1) {
printf("if\n");
} else if (k > 2) {
printf("else if\n");
} else {
printf("else\n");
}
// switch case
int love = 1;
printf("请输入 1~3");
scanf("%d", &love);
switch (love) {
case 1:
printf("I love you~\n");
break;
case 2:
printf("You love me~\n");
break;
default:
printf("Mixue ice cream and tea\n");
break;
}
/* 函数调用 */
fighting_function();
/* go to */
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
if (i >= 2) goto date;
}
}
date:
printf("Let's have a date~\n");
/* 指针 */
int sw_a = 10, sw_b = 20;
int *sp = swap(&sw_a, &sw_b);
printf("a = %d, b = %d, *sp = %d\n", sw_a, sw_b, *sp);
/******************\
* 解锁新的注释方式 *
* *
\******************/
/* 结构体 */
struct Map_Pos {
int x, y;
};
struct Map_Pos a = {1, 2};
printf("x = %d, y = %d\n", a.x, a.y);
/* 链表 */
link p = make_node(0);
insert(p);
p = make_node(2);
insert(p);
p = make_node(5);
insert(p);
while (p = pop()) {
print_item(p);
free_node(p);
}
return 0;
}
得到 sample.s
gcc sample.c -S
汇编代码 sample.s 如下,划分还是比较清晰的
/* 字符串比较 */
printf("请输入等待比较的字符串:\n");
char str1[50] = "Full hope full energy!";
char str2[50] = {0};
gets(str2);
printf("result = %d\n", strcmp(str1, str2));
/* 字符串比较 */
call puts@PLT
movabsq $8101808743130101062, %rax
movabsq $7286943410167357541, %rdx
movq %rax, -128(%rbp)
movq %rdx, -120(%rbp)
movabsq $36805310309742, %rax
movl $0, %edx
movq %rax, -112(%rbp)
movq %rdx, -104(%rbp)
movq $0, -96(%rbp)
movq $0, -88(%rbp)
movw $0, -80(%rbp)
movq $0, -64(%rbp)
movq $0, -56(%rbp)
movq $0, -48(%rbp)
movq $0, -40(%rbp)
movq $0, -32(%rbp)
movq $0, -24(%rbp)
movw $0, -16(%rbp)
leaq -64(%rbp), %rax
movq %rax, %rdi
movl $0, %eax
call gets@PLT
leaq -64(%rbp), %rdx
leaq -128(%rbp), %rax
movq %rdx, %rsi
movq %rax, %rdi
call strcmp@PLT
movl %eax, %esi
leaq .LC3(%rip), %rdi
movl $0, %eax
call printf@PLT
movl $0, -176(%rbp)
jmp .L13
/* 循环 */
// 一层
for (int i = 0; i < 5; i ++) {
printf("%c", 'h');
}
printf("\n");
/* 循环 */
// 一层
subq $16, %rsp
movl $0, -4(%rbp)
jmp .L13
.L14:
movl $104, %edi
call putchar@PLT
addl $1, -4(%rbp)
.L13:
cmpl $4, -4(%rbp)
jle .L14
movl $10, %edi
call putchar@PLT
movl $0, %eax
// 两层
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
printf("%d %d\n", i, j);
}
}
// 两层
subq $16, %rsp
movl $0, -8(%rbp)
jmp .L13
.L16:
movl $0, -4(%rbp)
jmp .L14
.L15:
movl -4(%rbp), %edx
movl -8(%rbp), %eax
movl %eax, %esi
leaq .LC2(%rip), %rdi
movl $0, %eax
call printf@PLT
addl $1, -4(%rbp)
.L14:
cmpl $4, -4(%rbp)
jle .L15
addl $1, -8(%rbp)
.L13:
cmpl $4, -8(%rbp)
jle .L16
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
/* 分支语句 */
// if else
int k = 1;
if (k == 1) {
printf("if\n");
} else if (k > 2) {
printf("else if\n");
} else {
printf("else\n");
}
subq $16, %rsp
movl $1, -4(%rbp)
cmpl $1, -4(%rbp)
jne .L13
leaq .LC2(%rip), %rdi
call puts@PLT
jmp .L14
.L13:
cmpl $2, -4(%rbp)
jle .L15
leaq .LC3(%rip), %rdi
call puts@PLT
jmp .L14
.L15:
leaq .LC4(%rip), %rdi
call puts@PLT
.L14:
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
// switch case
int love = 1;
printf("请输入 1~3");
scanf("%d", &love);
switch (love) {
case 1:
printf("I love you~\n");
break;
case 2:
printf("You love me~\n");
break;
default:
printf("Mixue ice cream and tea\n");
break;
}
subq $16, %rsp
movq %fs:40, %rax
movq %rax, -8(%rbp)
xorl %eax, %eax
movl $1, -12(%rbp)
leaq .LC2(%rip), %rdi
movl $0, %eax
call printf@PLT
leaq -12(%rbp), %rax
movq %rax, %rsi
leaq .LC3(%rip), %rdi
movl $0, %eax
call __isoc99_scanf@PLT
movl -12(%rbp), %eax
cmpl $1, %eax
je .L13
cmpl $2, %eax
je .L14
jmp .L19
.L13:
leaq .LC4(%rip), %rdi
call puts@PLT
jmp .L16
.L14:
leaq .LC5(%rip), %rdi
call puts@PLT
jmp .L16
.L19:
leaq .LC6(%rip), %rdi
call puts@PLT
nop
.L16:
movl $0, %eax
movq -8(%rbp), %rdx
xorq %fs:40, %rdx
je .L18
call __stack_chk_fail@PLT
.L18:
leave
.cfi_def_cfa 7, 8
ret
/* go to */
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
if (i >= 2) goto date;
}
}
date:
printf("Let's have a date~\n");
subq $16, %rsp
movl $0, -8(%rbp)
jmp .L13
.L18:
movl $0, -4(%rbp)
jmp .L14
.L17:
cmpl $1, -8(%rbp)
jg .L20
addl $1, -4(%rbp)
.L14:
cmpl $4, -4(%rbp)
jle .L17
addl $1, -8(%rbp)
.L13:
cmpl $4, -8(%rbp)
jle .L18
jmp .L16
.L20:
nop
.L16:
leaq .LC2(%rip), %rdi
call puts@PLT
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
void fighting_function() {
printf("I can do it!\n");
}
/* 函数调用 */
fighting_function();
fighting_function:
.LFB11:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
leaq .LC1(%rip), %rdi
call puts@PLT
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE11:
.size fighting_function, .-fighting_function
.globl swap
.type swap, @function
movl $0, %eax
call fighting_function
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
int *swap(int *px, int *py) {
int temp;
temp = *px;
*px = *py;
*py = temp;
return px;
}
/* 指针 */
int sw_a = 10, sw_b = 20;
int *sp = swap(&sw_a, &sw_b);
printf("a = %d, b = %d, *sp = %d\n", sw_a, sw_b, *sp);
swap:
.LFB12:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -24(%rbp)
movq %rsi, -32(%rbp)
movq -24(%rbp), %rax
movl (%rax), %eax
movl %eax, -4(%rbp)
movq -32(%rbp), %rax
movl (%rax), %edx
movq -24(%rbp), %rax
movl %edx, (%rax)
movq -32(%rbp), %rax
movl -4(%rbp), %edx
movl %edx, (%rax)
movq -24(%rbp), %rax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE12:
.size swap, .-swap
.section .rodata
.LC2:
.string "a = %d, b = %d, *sp = %d\n"
.text
.globl main
.type main, @function
subq $32, %rsp
movq %fs:40, %rax
movq %rax, -8(%rbp)
xorl %eax, %eax
movl $10, -24(%rbp)
movl $20, -20(%rbp)
leaq -20(%rbp), %rdx
leaq -24(%rbp), %rax
movq %rdx, %rsi
movq %rax, %rdi
call swap
movq %rax, -16(%rbp)
movq -16(%rbp), %rax
movl (%rax), %ecx
movl -20(%rbp), %edx
movl -24(%rbp), %eax
movl %eax, %esi
leaq .LC2(%rip), %rdi
movl $0, %eax
call printf@PLT
movl $0, %eax
movq -8(%rbp), %rsi
xorq %fs:40, %rsi
je .L14
call __stack_chk_fail@PLT
.L14:
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
这里可以看出结构体内部定义的数据是在申请的栈空间上连续排列的
/* 结构体 */
struct Map_Pos {
int x, y;
};
struct Map_Pos a = {1, 2};
printf("x = %d, y = %d\n", a.x, a.y);
subq $16, %rsp
movl $1, -8(%rbp)
movl $2, -4(%rbp)
movl -4(%rbp), %edx
movl -8(%rbp), %eax
movl %eax, %esi
leaq .LC2(%rip), %rdi
movl $0, %eax
call printf@PLT
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
/* 链表 */
link p = make_node(0);
insert(p);
p = make_node(2);
insert(p);
p = make_node(5);
insert(p);
while (p = pop()) {
print_item(p);
free_node(p);
}
subq $16, %rsp
movl $0, %edi
call make_node
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
movq %rax, %rdi
call insert
movl $2, %edi
call make_node
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
movq %rax, %rdi
call insert
movl $5, %edi
call make_node
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
movq %rax, %rdi
call insert
jmp .L13
.L14:
movq -8(%rbp), %rax
movq %rax, %rdi
call print_item
movq -8(%rbp), %rax
movq %rax, %rdi
call free_node
.L13:
call pop
movq %rax, -8(%rbp)
cmpq $0, -8(%rbp)
jne .L14
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc