오늘은 BombLab을 풀어볼 것입니다.

 

조건

- bomblab (순정 gdb만을 사용, 풀이 이후 패치 진행)
- 순정 gdb가 조건이기에 at&t 문법으로 진행

 

문제 파일 확인

┌──(kali㉿kali)-[~/Desktop]
└─$ file bomb              
bomb: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, 
interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, 
BuildID[sha1]=11c83ac9c51d3036cf2669235060f17e2cd0400b, with debug_info, not stripped

 

64bit ELF 파일인 것을 확인한다.

순정 gdb를 사용하는 것이 조건이니 gdb 내에서 at&t 문법으로 변환을 해주어야 한다.

(gdb) set disassembly-flavor att

 

해당 명령어로 at&t 문법으로 확인할 수 있으며 익숙한 intel 문법과 달리 add %rax, %rbx면 rbx에 rax 값을 더해야한다.

즉, intel과 순서가 반대로 되어 있으며 못 보던 문법들이 나오니 서칭하면서 진행했다.


main 분석

0x0000000000400da0 <+0>:	push   %rbx
   0x0000000000400da1 <+1>:	cmp    $0x1,%edi
   0x0000000000400da4 <+4>:	jne    0x400db6 <main+22>
   0x0000000000400da6 <+6>:	mov    0x20299b(%rip),%rax        # 0x603748 <stdin@@GLIBC_2.2.5>
   0x0000000000400dad <+13>:	mov    %rax,0x2029b4(%rip)        # 0x603768 <infile>
   0x0000000000400db4 <+20>:	jmp    0x400e19 <main+121>
   0x0000000000400db6 <+22>:	mov    %rsi,%rbx
   0x0000000000400db9 <+25>:	cmp    $0x2,%edi
   0x0000000000400dbc <+28>:	jne    0x400df8 <main+88>
   0x0000000000400dbe <+30>:	mov    0x8(%rsi),%rdi
   0x0000000000400dc2 <+34>:	mov    $0x4022b4,%esi
   0x0000000000400dc7 <+39>:	call   0x400c10 <fopen@plt>
   0x0000000000400dcc <+44>:	mov    %rax,0x202995(%rip)        # 0x603768 <infile>
   0x0000000000400dd3 <+51>:	test   %rax,%rax
   0x0000000000400dd6 <+54>:	jne    0x400e19 <main+121>
   0x0000000000400dd8 <+56>:	mov    0x8(%rbx),%rcx
   0x0000000000400ddc <+60>:	mov    (%rbx),%rdx
   0x0000000000400ddf <+63>:	mov    $0x4022b6,%esi
   0x0000000000400de4 <+68>:	mov    $0x1,%edi
   0x0000000000400de9 <+73>:	call   0x400c00 <__printf_chk@plt>
   0x0000000000400dee <+78>:	mov    $0x8,%edi
   0x0000000000400df3 <+83>:	call   0x400c20 <exit@plt>
   0x0000000000400df8 <+88>:	mov    (%rsi),%rdx
   0x0000000000400dfb <+91>:	mov    $0x4022d3,%esi
   0x0000000000400e00 <+96>:	mov    $0x1,%edi
   0x0000000000400e05 <+101>:	mov    $0x0,%eax
   0x0000000000400e0a <+106>:	call   0x400c00 <__printf_chk@plt>
   0x0000000000400e0f <+111>:	mov    $0x8,%edi
   0x0000000000400e14 <+116>:	call   0x400c20 <exit@plt>
   0x0000000000400e19 <+121>:	call   0x4013a2 <initialize_bomb>
   0x0000000000400e1e <+126>:	mov    $0x402338,%edi
   0x0000000000400e23 <+131>:	call   0x400b10 <puts@plt>
   0x0000000000400e28 <+136>:	mov    $0x402378,%edi
   0x0000000000400e2d <+141>:	call   0x400b10 <puts@plt>
   0x0000000000400e32 <+146>:	call   0x40149e <read_line>
   0x0000000000400e37 <+151>:	mov    %rax,%rdi
   0x0000000000400e3a <+154>:	call   0x400ee0 <phase_1>
   0x0000000000400e3f <+159>:	call   0x4015c4 <phase_defused>
   0x0000000000400e44 <+164>:	mov    $0x4023a8,%edi
   0x0000000000400e49 <+169>:	call   0x400b10 <puts@plt>
   0x0000000000400e4e <+174>:	call   0x40149e <read_line>
   0x0000000000400e53 <+179>:	mov    %rax,%rdi
   0x0000000000400e56 <+182>:	call   0x400efc <phase_2>
   0x0000000000400e5b <+187>:	call   0x4015c4 <phase_defused>
   0x0000000000400e60 <+192>:	mov    $0x4022ed,%edi
   0x0000000000400e65 <+197>:	call   0x400b10 <puts@plt>
   0x0000000000400e6a <+202>:	call   0x40149e <read_line>
   0x0000000000400e6f <+207>:	mov    %rax,%rdi
   0x0000000000400e72 <+210>:	call   0x400f43 <phase_3>
   0x0000000000400e77 <+215>:	call   0x4015c4 <phase_defused>
   0x0000000000400e7c <+220>:	mov    $0x40230b,%edi
   0x0000000000400e81 <+225>:	call   0x400b10 <puts@plt>
   0x0000000000400e86 <+230>:	call   0x40149e <read_line>
   0x0000000000400e8b <+235>:	mov    %rax,%rdi
   0x0000000000400e8e <+238>:	call   0x40100c <phase_4>
   0x0000000000400e93 <+243>:	call   0x4015c4 <phase_defused>
   0x0000000000400e98 <+248>:	mov    $0x4023d8,%edi
   0x0000000000400e9d <+253>:	call   0x400b10 <puts@plt>
   0x0000000000400ea2 <+258>:	call   0x40149e <read_line>
   0x0000000000400ea7 <+263>:	mov    %rax,%rdi
   0x0000000000400eaa <+266>:	call   0x401062 <phase_5>
   0x0000000000400eaf <+271>:	call   0x4015c4 <phase_defused>
   0x0000000000400eb4 <+276>:	mov    $0x40231a,%edi
   0x0000000000400eb9 <+281>:	call   0x400b10 <puts@plt>
   0x0000000000400ebe <+286>:	call   0x40149e <read_line>
   0x0000000000400ec3 <+291>:	mov    %rax,%rdi
   0x0000000000400ec6 <+294>:	call   0x4010f4 <phase_6>
   0x0000000000400ecb <+299>:	call   0x4015c4 <phase_defused>
   0x0000000000400ed0 <+304>:	mov    $0x0,%eax
   0x0000000000400ed5 <+309>:	pop    %rbx
   0x0000000000400ed6 <+310>:	ret

 

디스어셈블된 main 함수를 보면 phase_1부터 phase_6까지 6개의 phase가 존재한다.

각 phase에서 폭탄을 터트리지 않고 종료되는 것이 목표이다.


phase_1

(gdb) disas phase_1
Dump of assembler code for function phase_1:
   0x0000000000400ee0 <+0>:	sub    $0x8,%rsp
   0x0000000000400ee4 <+4>:	mov    $0x402400,%esi
   0x0000000000400ee9 <+9>:	call   0x401338 <strings_not_equal>
   0x0000000000400eee <+14>:	test   %eax,%eax
   0x0000000000400ef0 <+16>:	je     0x400ef7 <phase_1+23>
   0x0000000000400ef2 <+18>:	call   0x40143a <explode_bomb>
   0x0000000000400ef7 <+23>:	add    $0x8,%rsp
   0x0000000000400efb <+27>:	ret
End of assembler dump.

 

phase_1 함수를 보면 strings_not_equal 함수를 호출하고 그 결과가 eax 레지스터에 담긴다.

그 후 test %eax, %eax 명령어를 진행하는데 test는 두 값을 and 연산한다.

즉, 다음 실행되는 je 명령어를 통해 폭탄을 피해 점프하기 위해서는 strings_not_equal 함수의 리턴 값은 0이 되어야 한다.

먼저 strings_not_equal 함수 실행 전 0x402400의 값을 %esi에 넣으니 0x402400을 확인해봤다.

 

 

0x402400은 다음과 같은 문자열을 가지고 있다.

phase_1에 진입하기 전 read_line 함수를 통해 입력값을 받기에 입력값과 0x402400의 문자열이 동일한지 체크한다고 생각했다.

 

 

문자열을 넣으니 Phase 1이 defused 되었다고 한다.


phase_2

(gdb) disas phase_2
Dump of assembler code for function phase_2:
   0x0000000000400efc <+0>:	push   %rbp
   0x0000000000400efd <+1>:	push   %rbx
   0x0000000000400efe <+2>:	sub    $0x28,%rsp
   0x0000000000400f02 <+6>:	mov    %rsp,%rsi
   0x0000000000400f05 <+9>:	call   0x40145c <read_six_numbers>
   0x0000000000400f0a <+14>:	cmpl   $0x1,(%rsp)
   0x0000000000400f0e <+18>:	je     0x400f30 <phase_2+52>
   0x0000000000400f10 <+20>:	call   0x40143a <explode_bomb>
   0x0000000000400f15 <+25>:	jmp    0x400f30 <phase_2+52>
   0x0000000000400f17 <+27>:	mov    -0x4(%rbx),%eax
   0x0000000000400f1a <+30>:	add    %eax,%eax
   0x0000000000400f1c <+32>:	cmp    %eax,(%rbx)
   0x0000000000400f1e <+34>:	je     0x400f25 <phase_2+41>
   0x0000000000400f20 <+36>:	call   0x40143a <explode_bomb>
   0x0000000000400f25 <+41>:	add    $0x4,%rbx
   0x0000000000400f29 <+45>:	cmp    %rbp,%rbx
   0x0000000000400f2c <+48>:	jne    0x400f17 <phase_2+27>
   0x0000000000400f2e <+50>:	jmp    0x400f3c <phase_2+64>
   0x0000000000400f30 <+52>:	lea    0x4(%rsp),%rbx
   0x0000000000400f35 <+57>:	lea    0x18(%rsp),%rbp
   0x0000000000400f3a <+62>:	jmp    0x400f17 <phase_2+27>
   0x0000000000400f3c <+64>:	add    $0x28,%rsp
   0x0000000000400f40 <+68>:	pop    %rbx
   0x0000000000400f41 <+69>:	pop    %rbp
   0x0000000000400f42 <+70>:	ret
End of assembler dump.

(gdb) disas read_six_numbers
Dump of assembler code for function read_six_numbers:
   0x000000000040145c <+0>:	sub    $0x18,%rsp
   0x0000000000401460 <+4>:	mov    %rsi,%rdx
   0x0000000000401463 <+7>:	lea    0x4(%rsi),%rcx
   0x0000000000401467 <+11>:	lea    0x14(%rsi),%rax
   0x000000000040146b <+15>:	mov    %rax,0x8(%rsp)
   0x0000000000401470 <+20>:	lea    0x10(%rsi),%rax
   0x0000000000401474 <+24>:	mov    %rax,(%rsp)
   0x0000000000401478 <+28>:	lea    0xc(%rsi),%r9
   0x000000000040147c <+32>:	lea    0x8(%rsi),%r8
   0x0000000000401480 <+36>:	mov    $0x4025c3,%esi
   0x0000000000401485 <+41>:	mov    $0x0,%eax
   0x000000000040148a <+46>:	call   0x400bf0 <__isoc99_sscanf@plt>
   0x000000000040148f <+51>:	cmp    $0x5,%eax
   0x0000000000401492 <+54>:	jg     0x401499 <read_six_numbers+61>
   0x0000000000401494 <+56>:	call   0x40143a <explode_bomb>
   0x0000000000401499 <+61>:	add    $0x18,%rsp
   0x000000000040149d <+65>:	ret
End of assembler dump.

 

phase_2는 처음에 read_six_numbers 함수를 호출한다.

해당 함수 내에서는 sscanf를 호출하는데 해당 함수의 리턴 값은 읽은 값의 수이며 이것이 0x5보다 커야 explode_bomb을 피할 수 있다.

 

 

0x4025c3의 문자열을 인자로 넣는 것으로 보아 숫자 6개를 입력하면 되는 것 같다.

이후 phase_2 함수로 돌아와 %rsp의 값(첫번째 입력값)이 0x1과 같은지 체크하며, 같지 않으면 폭탄이 터진다.

 

   0x0000000000400f17 <+27>:	mov    -0x4(%rbx),%eax
   0x0000000000400f1a <+30>:	add    %eax,%eax
   0x0000000000400f1c <+32>:	cmp    %eax,(%rbx)
   0x0000000000400f1e <+34>:	je     0x400f25 <phase_2+41>
   0x0000000000400f20 <+36>:	call   0x40143a <explode_bomb>
   0x0000000000400f25 <+41>:	add    $0x4,%rbx
   0x0000000000400f29 <+45>:	cmp    %rbp,%rbx
   0x0000000000400f2c <+48>:	jne    0x400f17 <phase_2+27>
   0x0000000000400f2e <+50>:	jmp    0x400f3c <phase_2+64>
   0x0000000000400f30 <+52>:	lea    0x4(%rsp),%rbx
   0x0000000000400f35 <+57>:	lea    0x18(%rsp),%rbp
   0x0000000000400f3a <+62>:	jmp    0x400f17 <phase_2+27>

 

첫번째 입력값으로 1을 주었다면 <phase_2+52>로 점프한 뒤 해당 부분을 반복한다.

반복문을 보면 rsp + 0x4의 값을 rbx에 넣고 <phase_2+27>로 이동해 rbx - 0x4의 값을 eax에 저장한다.

이후 eax + eax와 rbx의 값이 같은지 체크하고 같다면 넘어갈 수 있다.

 

대충 식을 정리해보면 다음과 같다.

if (rax + rax == rbx):
	jmp next
else:
	bomb

 

반복문에서는 n-1번 입력값을 가져와 2배 한뒤, n번 입력값과 같은지 비교한다.

또한 rbx는 반복마다 0x4씩 증가하며, int형 배열의 다음 원소로 이동한다는 의미이므로 입력값들은 전부 이전 값의 2배가 되는 구조를 가져야 한다.

 

정리하자면 입력값은 숫자 6개이며, 첫 값은 1이어야 한다.

그 다음 숫자들은 이전 수의 2배 값을 가져야 하므로 1 2 4 8 16 32를 넣으면 된다.

 

 

1 2 4 8 16 32를 넣으니 넘어갈 수 있었다.


phase_3

(gdb) disas phase_3
Dump of assembler code for function phase_3:
   0x0000000000400f43 <+0>:	sub    $0x18,%rsp
   0x0000000000400f47 <+4>:	lea    0xc(%rsp),%rcx
   0x0000000000400f4c <+9>:	lea    0x8(%rsp),%rdx
   0x0000000000400f51 <+14>:	mov    $0x4025cf,%esi
   0x0000000000400f56 <+19>:	mov    $0x0,%eax
   0x0000000000400f5b <+24>:	call   0x400bf0 <__isoc99_sscanf@plt>
   0x0000000000400f60 <+29>:	cmp    $0x1,%eax
   0x0000000000400f63 <+32>:	jg     0x400f6a <phase_3+39>
   0x0000000000400f65 <+34>:	call   0x40143a <explode_bomb>
   0x0000000000400f6a <+39>:	cmpl   $0x7,0x8(%rsp)
   0x0000000000400f6f <+44>:	ja     0x400fad <phase_3+106>
   0x0000000000400f71 <+46>:	mov    0x8(%rsp),%eax
   0x0000000000400f75 <+50>:	jmp    *0x402470(,%rax,8)
   0x0000000000400f7c <+57>:	mov    $0xcf,%eax
   0x0000000000400f81 <+62>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f83 <+64>:	mov    $0x2c3,%eax
   0x0000000000400f88 <+69>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f8a <+71>:	mov    $0x100,%eax
   0x0000000000400f8f <+76>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f91 <+78>:	mov    $0x185,%eax
   0x0000000000400f96 <+83>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f98 <+85>:	mov    $0xce,%eax
   0x0000000000400f9d <+90>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f9f <+92>:	mov    $0x2aa,%eax
   0x0000000000400fa4 <+97>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400fa6 <+99>:	mov    $0x147,%eax
   0x0000000000400fab <+104>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400fad <+106>:	call   0x40143a <explode_bomb>
   0x0000000000400fb2 <+111>:	mov    $0x0,%eax
   0x0000000000400fb7 <+116>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400fb9 <+118>:	mov    $0x137,%eax
   0x0000000000400fbe <+123>:	cmp    0xc(%rsp),%eax
   0x0000000000400fc2 <+127>:	je     0x400fc9 <phase_3+134>
   0x0000000000400fc4 <+129>:	call   0x40143a <explode_bomb>
   0x0000000000400fc9 <+134>:	add    $0x18,%rsp
   0x0000000000400fcd <+138>:	ret
End of assembler dump.

 

phase_3도 시작하자마자 sscanf 함수로 입력값의 개수를 체크한다.

입력값은 2개이며 입력값의 첫번째 값은 0~7 범위여야 한다.

 

   0x0000000000400f71 <+46>:	mov    0x8(%rsp),%eax
   0x0000000000400f75 <+50>:	jmp    *0x402470(,%rax,8)
   0x0000000000400f7c <+57>:	mov    $0xcf,%eax
   0x0000000000400f81 <+62>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f83 <+64>:	mov    $0x2c3,%eax
   0x0000000000400f88 <+69>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f8a <+71>:	mov    $0x100,%eax
   0x0000000000400f8f <+76>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f91 <+78>:	mov    $0x185,%eax
   0x0000000000400f96 <+83>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f98 <+85>:	mov    $0xce,%eax
   0x0000000000400f9d <+90>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f9f <+92>:	mov    $0x2aa,%eax
   0x0000000000400fa4 <+97>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400fa6 <+99>:	mov    $0x147,%eax
   0x0000000000400fab <+104>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400fad <+106>:	call   0x40143a <explode_bomb>

 

위 조건을 충족하면 다음 부분으로 넘어온다.

eax에 rsp+0x8의 값(첫번째 입력값)을 저장하고 jmp *0x402470(, %rax,8) 명령어를 실행한다.

 

검색을 하다보니 at&t에 있는 오프셋 계산 방법이라고 한다.

Offset(base, index,scale) = base + (index * scale) + Offset 식으라고 한다.

그대로 바꿔보면 0 + (첫번째 입력값 * 8) + 0x402470이다.

 

0x402470은 점프 테이블이며 다음과 같이 점프한다.

   0x0000000000400f7c <+57>:	mov    $0xcf,%eax
   0x0000000000400f81 <+62>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f83 <+64>:	mov    $0x2c3,%eax
   0x0000000000400f88 <+69>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f8a <+71>:	mov    $0x100,%eax
   0x0000000000400f8f <+76>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f91 <+78>:	mov    $0x185,%eax
   0x0000000000400f96 <+83>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f98 <+85>:	mov    $0xce,%eax
   0x0000000000400f9d <+90>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f9f <+92>:	mov    $0x2aa,%eax
   0x0000000000400fa4 <+97>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400fa6 <+99>:	mov    $0x147,%eax
   0x0000000000400fab <+104>:	jmp    0x400fbe <phase_3+123>

 

즉, 해당 부분에 순서대로 들어가는 것이다.

만약 3을 넣었다고 가정하면 0x400f8a로 이동되며 eax에 0x100을 넣고 <phase_3+123>으로 점프한다.

 

   0x0000000000400fbe <+123>:	cmp    0xc(%rsp),%eax
   0x0000000000400fc2 <+127>:	je     0x400fc9 <phase_3+134>
   0x0000000000400fc4 <+129>:	call   0x40143a <explode_bomb>
   0x0000000000400fc9 <+134>:	add    $0x18,%rsp
   0x0000000000400fcd <+138>:	ret

 

<phase_3+123>에서는 rsp+0xc(2번째 입력값)과 eax를 비교해 같으면 넘어갈 수 있다.

즉, 3을 넣었을 때 2번째 입력 값이 256(0x100)면 통과할 수 있다.

 

3 256을 입력해서 넘어갈 수 있었으며 이외에도 여러 정답이 존재한다.


phase_4

(gdb) disas phase_4
Dump of assembler code for function phase_4:
   0x000000000040100c <+0>:	sub    $0x18,%rsp
   0x0000000000401010 <+4>:	lea    0xc(%rsp),%rcx
   0x0000000000401015 <+9>:	lea    0x8(%rsp),%rdx
   0x000000000040101a <+14>:	mov    $0x4025cf,%esi
   0x000000000040101f <+19>:	mov    $0x0,%eax
   0x0000000000401024 <+24>:	call   0x400bf0 <__isoc99_sscanf@plt>
   0x0000000000401029 <+29>:	cmp    $0x2,%eax
   0x000000000040102c <+32>:	jne    0x401035 <phase_4+41>
   0x000000000040102e <+34>:	cmpl   $0xe,0x8(%rsp)
   0x0000000000401033 <+39>:	jbe    0x40103a <phase_4+46>
   0x0000000000401035 <+41>:	call   0x40143a <explode_bomb>
   0x000000000040103a <+46>:	mov    $0xe,%edx
   0x000000000040103f <+51>:	mov    $0x0,%esi
   0x0000000000401044 <+56>:	mov    0x8(%rsp),%edi
   0x0000000000401048 <+60>:	call   0x400fce <func4>
   0x000000000040104d <+65>:	test   %eax,%eax
   0x000000000040104f <+67>:	jne    0x401058 <phase_4+76>
   0x0000000000401051 <+69>:	cmpl   $0x0,0xc(%rsp)
   0x0000000000401056 <+74>:	je     0x40105d <phase_4+81>
   0x0000000000401058 <+76>:	call   0x40143a <explode_bomb>
   0x000000000040105d <+81>:	add    $0x18,%rsp
   0x0000000000401061 <+85>:	ret
End of assembler dump.

(gdb) disas func4
Dump of assembler code for function func4:
   0x0000000000400fce <+0>:	sub    $0x8,%rsp
   0x0000000000400fd2 <+4>:	mov    %edx,%eax
   0x0000000000400fd4 <+6>:	sub    %esi,%eax
   0x0000000000400fd6 <+8>:	mov    %eax,%ecx
   0x0000000000400fd8 <+10>:	shr    $0x1f,%ecx
   0x0000000000400fdb <+13>:	add    %ecx,%eax
   0x0000000000400fdd <+15>:	sar    $1,%eax
   0x0000000000400fdf <+17>:	lea    (%rax,%rsi,1),%ecx
   0x0000000000400fe2 <+20>:	cmp    %edi,%ecx
   0x0000000000400fe4 <+22>:	jle    0x400ff2 <func4+36>
   0x0000000000400fe6 <+24>:	lea    -0x1(%rcx),%edx
   0x0000000000400fe9 <+27>:	call   0x400fce <func4>
   0x0000000000400fee <+32>:	add    %eax,%eax
   0x0000000000400ff0 <+34>:	jmp    0x401007 <func4+57>
   0x0000000000400ff2 <+36>:	mov    $0x0,%eax
   0x0000000000400ff7 <+41>:	cmp    %edi,%ecx
   0x0000000000400ff9 <+43>:	jge    0x401007 <func4+57>
   0x0000000000400ffb <+45>:	lea    0x1(%rcx),%esi
   0x0000000000400ffe <+48>:	call   0x400fce <func4>
   0x0000000000401003 <+53>:	lea    0x1(%rax,%rax,1),%eax
   0x0000000000401007 <+57>:	add    $0x8,%rsp
   0x000000000040100b <+61>:	ret
End of assembler dump.

 

phase_4도 시작하자마자 sscanf 함수를 통해 입력값의 개수를 체크한다.

입력 개수는 2개여야 하며 입력값의 첫번째가 0xe보다 크면 폭탄이 터진다.

 

그 후 func4 함수로 진입한다.

해당 함수에서는 0xe를 eax에 넣고 esi(0)을 빼고 ecx에 저장한다.

그 후 ecx를 0x1f(31)번 오른쪽 시프트하는데 결국 0이 된다.

그 후 eax에 ecx(0)을 더하고 1번 오른쪽 시프트한다.

 

 

0xe를 1번 오른쪽 시프트하면 0x7이 된다.

그 후 (%rax, %rsi, 1) 계산 결과를 ecx에 저장한다.

아까처럼 식으로 바꾸자면 rax + (rsi * 1) + 0인데 rsi는 0이었으므로 그냥 rax(7)의 값을 ecx에 저장한다

그러고 ecx와 edi(첫번째 입력값)을 비교해서 ecx가 더 작거나 같다면 점프하고 크다면 func4를 재귀적으로 호출한다.

점프한 이후 ecx와 edi를 다시 비교하는데 이땐 ecx가 더 크거나 같다면 점프하고 작다면 func4를 재귀적으로 호출한다.

그렇다면 ecx의 값이 7이므로 edi(첫번째 입력값)도 7이면 두 조건다 만족할 수 있다.

나중에 보니 func4는 재귀적으로 탐색하는 이진 탐색 구조였다..

   0x000000000040104d <+65>:	test   %eax,%eax
   0x000000000040104f <+67>:	jne    0x401058 <phase_4+76>
   0x0000000000401051 <+69>:	cmpl   $0x0,0xc(%rsp)
   0x0000000000401056 <+74>:	je     0x40105d <phase_4+81>
   0x0000000000401058 <+76>:	call   0x40143a <explode_bomb>
   0x000000000040105d <+81>:	add    $0x18,%rsp
   0x0000000000401061 <+85>:	ret

 

func4 함수가 종료된 뒤 phase_4의 해당 부분으로 돌아온다.

test eax, eax를 하여 eax가 0이 아니면 폭탄이 터지는데 정상적으로 func4를 종료했다면 0을 반환하기에 넘어간다.

마지막으로 cmpl 0x0, 0xc(%rsp) 명령어를 진행하는데 여기서 rsp+0xc에는 2번째 입력값이 들어있다.

즉, 2번째 입력값은 0을 주면 넘어갈 수 있다.

 

 

7 0을 입력하여 넘어갈 수 있었으며 해당 phase도 재귀 과정에서 여러 답이 나올 것 같다.


phase_5

(gdb) disas phase_5
Dump of assembler code for function phase_5:
   0x0000000000401062 <+0>:	push   %rbx
   0x0000000000401063 <+1>:	sub    $0x20,%rsp
   0x0000000000401067 <+5>:	mov    %rdi,%rbx
   0x000000000040106a <+8>:	mov    %fs:0x28,%rax
   0x0000000000401073 <+17>:	mov    %rax,0x18(%rsp)
   0x0000000000401078 <+22>:	xor    %eax,%eax
   0x000000000040107a <+24>:	call   0x40131b <string_length>
   0x000000000040107f <+29>:	cmp    $0x6,%eax
   0x0000000000401082 <+32>:	je     0x4010d2 <phase_5+112>
   0x0000000000401084 <+34>:	call   0x40143a <explode_bomb>
   0x0000000000401089 <+39>:	jmp    0x4010d2 <phase_5+112>
   0x000000000040108b <+41>:	movzbl (%rbx,%rax,1),%ecx
   0x000000000040108f <+45>:	mov    %cl,(%rsp)
   0x0000000000401092 <+48>:	mov    (%rsp),%rdx
   0x0000000000401096 <+52>:	and    $0xf,%edx
   0x0000000000401099 <+55>:	movzbl 0x4024b0(%rdx),%edx
   0x00000000004010a0 <+62>:	mov    %dl,0x10(%rsp,%rax,1)
   0x00000000004010a4 <+66>:	add    $0x1,%rax
   0x00000000004010a8 <+70>:	cmp    $0x6,%rax
   0x00000000004010ac <+74>:	jne    0x40108b <phase_5+41>
   0x00000000004010ae <+76>:	movb   $0x0,0x16(%rsp)
   0x00000000004010b3 <+81>:	mov    $0x40245e,%esi
   0x00000000004010b8 <+86>:	lea    0x10(%rsp),%rdi
   0x00000000004010bd <+91>:	call   0x401338 <strings_not_equal>
   0x00000000004010c2 <+96>:	test   %eax,%eax
   0x00000000004010c4 <+98>:	je     0x4010d9 <phase_5+119>
   0x00000000004010c6 <+100>:	call   0x40143a <explode_bomb>
   0x00000000004010cb <+105>:	nopl   0x0(%rax,%rax,1)
   0x00000000004010d0 <+110>:	jmp    0x4010d9 <phase_5+119>
   0x00000000004010d2 <+112>:	mov    $0x0,%eax
   0x00000000004010d7 <+117>:	jmp    0x40108b <phase_5+41>
   0x00000000004010d9 <+119>:	mov    0x18(%rsp),%rax
   0x00000000004010de <+124>:	xor    %fs:0x28,%rax
   0x00000000004010e7 <+133>:	je     0x4010ee <phase_5+140>
   0x00000000004010e9 <+135>:	call   0x400b30 <__stack_chk_fail@plt>
   0x00000000004010ee <+140>:	add    $0x20,%rsp
   0x00000000004010f2 <+144>:	pop    %rbx
   0x00000000004010f3 <+145>:	ret
End of assembler dump.

 

phase_5는 string_length 함수를 통해 입력값의 길이를 체크하며 6글자여야 한다.

그 후 eax에 0 넣고 <phase_5+41>로 점프하고 입력값을 한 글자씩 가져와 0xf와 and 연산을 한다.

그리고 0x4024b0 배열에서 and 연산 결과값을 인덱스로 사용하여 값을 가져온다.

 

 

0x4024b0 배열은 다음과 같으며 하위 4비트만 남긴 값을 인덱스로 사용한다.

즉, and 연산 결과값이 1이면 a, 5면 e 이런 식이다.

이걸 반복해서 6자리 문자열을 만든다.

 

 

그후 반복하여 만든 문자열과 0x40245e 문자열을 비교해서 같으면 넘어갈 수 있다.

 

 

yon567을 넣어 넘어갈 수 있었으며 이것도 여러 정답이 있다.


phase_6

(gdb) disas phase_6
Dump of assembler code for function phase_6:
   0x00000000004010f4 <+0>:	push   %r14
   0x00000000004010f6 <+2>:	push   %r13
   0x00000000004010f8 <+4>:	push   %r12
   0x00000000004010fa <+6>:	push   %rbp
   0x00000000004010fb <+7>:	push   %rbx
   0x00000000004010fc <+8>:	sub    $0x50,%rsp
   0x0000000000401100 <+12>:	mov    %rsp,%r13
   0x0000000000401103 <+15>:	mov    %rsp,%rsi
   0x0000000000401106 <+18>:	call   0x40145c <read_six_numbers>
   0x000000000040110b <+23>:	mov    %rsp,%r14
   0x000000000040110e <+26>:	mov    $0x0,%r12d
   0x0000000000401114 <+32>:	mov    %r13,%rbp
   0x0000000000401117 <+35>:	mov    0x0(%r13),%eax
   0x000000000040111b <+39>:	sub    $0x1,%eax
   0x000000000040111e <+42>:	cmp    $0x5,%eax
   0x0000000000401121 <+45>:	jbe    0x401128 <phase_6+52>
   0x0000000000401123 <+47>:	call   0x40143a <explode_bomb>
   0x0000000000401128 <+52>:	add    $0x1,%r12d
   0x000000000040112c <+56>:	cmp    $0x6,%r12d
   0x0000000000401130 <+60>:	je     0x401153 <phase_6+95>
   0x0000000000401132 <+62>:	mov    %r12d,%ebx
   0x0000000000401135 <+65>:	movslq %ebx,%rax
   0x0000000000401138 <+68>:	mov    (%rsp,%rax,4),%eax
   0x000000000040113b <+71>:	cmp    %eax,0x0(%rbp)
   0x000000000040113e <+74>:	jne    0x401145 <phase_6+81>
   0x0000000000401140 <+76>:	call   0x40143a <explode_bomb>
   0x0000000000401145 <+81>:	add    $0x1,%ebx
   0x0000000000401148 <+84>:	cmp    $0x5,%ebx
   0x000000000040114b <+87>:	jle    0x401135 <phase_6+65>
   0x000000000040114d <+89>:	add    $0x4,%r13
   0x0000000000401151 <+93>:	jmp    0x401114 <phase_6+32>
   0x0000000000401153 <+95>:	lea    0x18(%rsp),%rsi
   0x0000000000401158 <+100>:	mov    %r14,%rax
   0x000000000040115b <+103>:	mov    $0x7,%ecx
   0x0000000000401160 <+108>:	mov    %ecx,%edx
   0x0000000000401162 <+110>:	sub    (%rax),%edx
   0x0000000000401164 <+112>:	mov    %edx,(%rax)
   0x0000000000401166 <+114>:	add    $0x4,%rax
   0x000000000040116a <+118>:	cmp    %rsi,%rax
   0x000000000040116d <+121>:	jne    0x401160 <phase_6+108>
   0x000000000040116f <+123>:	mov    $0x0,%esi
   0x0000000000401174 <+128>:	jmp    0x401197 <phase_6+163>
   0x0000000000401176 <+130>:	mov    0x8(%rdx),%rdx
   0x000000000040117a <+134>:	add    $0x1,%eax
   0x000000000040117d <+137>:	cmp    %ecx,%eax
   0x000000000040117f <+139>:	jne    0x401176 <phase_6+130>
   0x0000000000401181 <+141>:	jmp    0x401188 <phase_6+148>
   0x0000000000401183 <+143>:	mov    $0x6032d0,%edx
   0x0000000000401188 <+148>:	mov    %rdx,0x20(%rsp,%rsi,2)
   0x000000000040118d <+153>:	add    $0x4,%rsi
   0x0000000000401191 <+157>:	cmp    $0x18,%rsi
   0x0000000000401195 <+161>:	je     0x4011ab <phase_6+183>
   0x0000000000401197 <+163>:	mov    (%rsp,%rsi,1),%ecx
   0x000000000040119a <+166>:	cmp    $0x1,%ecx
   0x000000000040119d <+169>:	jle    0x401183 <phase_6+143>
   0x000000000040119f <+171>:	mov    $0x1,%eax
   0x00000000004011a4 <+176>:	mov    $0x6032d0,%edx
   0x00000000004011a9 <+181>:	jmp    0x401176 <phase_6+130>
   0x00000000004011ab <+183>:	mov    0x20(%rsp),%rbx
   0x00000000004011b0 <+188>:	lea    0x28(%rsp),%rax
   0x00000000004011b5 <+193>:	lea    0x50(%rsp),%rsi
   0x00000000004011ba <+198>:	mov    %rbx,%rcx
   0x00000000004011bd <+201>:	mov    (%rax),%rdx
   0x00000000004011c0 <+204>:	mov    %rdx,0x8(%rcx)
   0x00000000004011c4 <+208>:	add    $0x8,%rax
   0x00000000004011c8 <+212>:	cmp    %rsi,%rax
   0x00000000004011cb <+215>:	je     0x4011d2 <phase_6+222>
   0x00000000004011cd <+217>:	mov    %rdx,%rcx
   0x00000000004011d0 <+220>:	jmp    0x4011bd <phase_6+201>
   0x00000000004011d2 <+222>:	movq   $0x0,0x8(%rdx)
   0x00000000004011da <+230>:	mov    $0x5,%ebp
   0x00000000004011df <+235>:	mov    0x8(%rbx),%rax
   0x00000000004011e3 <+239>:	mov    (%rax),%eax
   0x00000000004011e5 <+241>:	cmp    %eax,(%rbx)
   0x00000000004011e7 <+243>:	jge    0x4011ee <phase_6+250>
   0x00000000004011e9 <+245>:	call   0x40143a <explode_bomb>
   0x00000000004011ee <+250>:	mov    0x8(%rbx),%rbx
   0x00000000004011f2 <+254>:	sub    $0x1,%ebp
   0x00000000004011f5 <+257>:	jne    0x4011df <phase_6+235>
   0x00000000004011f7 <+259>:	add    $0x50,%rsp
   0x00000000004011fb <+263>:	pop    %rbx
   0x00000000004011fc <+264>:	pop    %rbp
   0x00000000004011fd <+265>:	pop    %r12
   0x00000000004011ff <+267>:	pop    %r13
   0x0000000000401201 <+269>:	pop    %r14
   0x0000000000401203 <+271>:	ret
End of assembler dump.

 

phase_6도 처음에 read_six_number 함수를 통해 입력값을 체크하며 6개의 입력이 들어왔는지 검사한다. (phase_2 참고)

 

   0x000000000040110b <+23>:	mov    %rsp,%r14
   0x000000000040110e <+26>:	mov    $0x0,%r12d
   0x0000000000401114 <+32>:	mov    %r13,%rbp
   0x0000000000401117 <+35>:	mov    0x0(%r13),%eax
   0x000000000040111b <+39>:	sub    $0x1,%eax
   0x000000000040111e <+42>:	cmp    $0x5,%eax
   0x0000000000401121 <+45>:	jbe    0x401128 <phase_6+52>
   0x0000000000401123 <+47>:	call   0x40143a <explode_bomb>
   0x0000000000401128 <+52>:	add    $0x1,%r12d
   0x000000000040112c <+56>:	cmp    $0x6,%r12d
   0x0000000000401130 <+60>:	je     0x401153 <phase_6+95>
   0x0000000000401132 <+62>:	mov    %r12d,%ebx
   0x0000000000401135 <+65>:	movslq %ebx,%rax
   0x0000000000401138 <+68>:	mov    (%rsp,%rax,4),%eax
   0x000000000040113b <+71>:	cmp    %eax,0x0(%rbp)
   0x000000000040113e <+74>:	jne    0x401145 <phase_6+81>
   0x0000000000401140 <+76>:	call   0x40143a <explode_bomb>
   0x0000000000401145 <+81>:	add    $0x1,%ebx
   0x0000000000401148 <+84>:	cmp    $0x5,%ebx
   0x000000000040114b <+87>:	jle    0x401135 <phase_6+65>
   0x000000000040114d <+89>:	add    $0x4,%r13
   0x0000000000401151 <+93>:	jmp    0x401114 <phase_6+32>

 

read_six_numbers 함수 이후 입력값에 1을 뺀 뒤, 0x5와 비교해 eax(입력값)이 0x5보다 작거나 같다면 점프하며 이를 6번 반복한다.

즉, 입력한 6개의 숫자가 1~6 범위인지 체크하고, 중복된 값이 있는지 체크한다.

 

   0x0000000000401153 <+95>:	lea    0x18(%rsp),%rsi
   0x0000000000401158 <+100>:	mov    %r14,%rax
   0x000000000040115b <+103>:	mov    $0x7,%ecx
   0x0000000000401160 <+108>:	mov    %ecx,%edx
   0x0000000000401162 <+110>:	sub    (%rax),%edx
   0x0000000000401164 <+112>:	mov    %edx,(%rax)
   0x0000000000401166 <+114>:	add    $0x4,%rax
   0x000000000040116a <+118>:	cmp    %rsi,%rax
   0x000000000040116d <+121>:	jne    0x401160 <phase_6+108>
   0x000000000040116f <+123>:	mov    $0x0,%esi
   0x0000000000401174 <+128>:	jmp    0x401197 <phase_6+163>

 

그 후 <phase_6+95>로 넘어오면 edx에 0x7을 넣고 edx에 rax(입력값)을 뺀 뒤 다시 rax 메모리에 값을 저장한다.

이를 반복하며 모든 입력값에 적용하므로 결국 1 2 3 4 5 6을 넣으면 6 5 4 3 2 1로 바뀌게 된다.

 

   0x0000000000401176 <+130>:	mov    0x8(%rdx),%rdx
   0x000000000040117a <+134>:	add    $0x1,%eax
   0x000000000040117d <+137>:	cmp    %ecx,%eax
   0x000000000040117f <+139>:	jne    0x401176 <phase_6+130>
   0x0000000000401181 <+141>:	jmp    0x401188 <phase_6+148>
   0x0000000000401183 <+143>:	mov    $0x6032d0,%edx
   0x0000000000401188 <+148>:	mov    %rdx,0x20(%rsp,%rsi,2)
   0x000000000040118d <+153>:	add    $0x4,%rsi
   0x0000000000401191 <+157>:	cmp    $0x18,%rsi
   0x0000000000401195 <+161>:	je     0x4011ab <phase_6+183>
   0x0000000000401197 <+163>:	mov    (%rsp,%rsi,1),%ecx
   0x000000000040119a <+166>:	cmp    $0x1,%ecx
   0x000000000040119d <+169>:	jle    0x401183 <phase_6+143>
   0x000000000040119f <+171>:	mov    $0x1,%eax
   0x00000000004011a4 <+176>:	mov    $0x6032d0,%edx
   0x00000000004011a9 <+181>:	jmp    0x401176 <phase_6+130>

 

해당 부분은 만약 7에 입력값을 뺀 값이 1보다 크면 edx에 0x6032d0(node1)을 저장하고 <phase_6+130>으로 점프하여 edx의 다음 node로 이동하며 ecx번째 node를 찾아 스택에 저장한다.

만약 1과 같거나 작으면 <phase_6+143>으로 점프하여 0x6032d0을 rsp + (rsi * 2) + 0x20에 저장한다.

 

그 후 rsi에 0x4를 더하고 이를 총 6번 반복한다.

 

 

해당 node들이 7에서 입력값을 뺀 결과에 해당하는 순서대로 스택에 저장된다.

 

   0x00000000004011d2 <+222>:	movq   $0x0,0x8(%rdx)
   0x00000000004011da <+230>:	mov    $0x5,%ebp
   0x00000000004011df <+235>:	mov    0x8(%rbx),%rax
   0x00000000004011e3 <+239>:	mov    (%rax),%eax
   0x00000000004011e5 <+241>:	cmp    %eax,(%rbx)
   0x00000000004011e7 <+243>:	jge    0x4011ee <phase_6+250>
   0x00000000004011e9 <+245>:	call   0x40143a <explode_bomb>
   0x00000000004011ee <+250>:	mov    0x8(%rbx),%rbx
   0x00000000004011f2 <+254>:	sub    $0x1,%ebp
   0x00000000004011f5 <+257>:	jne    0x4011df <phase_6+235>
   0x00000000004011f7 <+259>:	add    $0x50,%rsp
   0x00000000004011fb <+263>:	pop    %rbx
   0x00000000004011fc <+264>:	pop    %rbp
   0x00000000004011fd <+265>:	pop    %r12
   0x00000000004011ff <+267>:	pop    %r13
   0x0000000000401201 <+269>:	pop    %r14
   0x0000000000401203 <+271>:	ret

 

그후 해당 부분에서 마지막 검증을 진행한다.

<phase+241> 부분에서 rbx의 값과 eax를 비교하여 rbx의 값이 더 크거나 같아야 되는데 이는 아까 스택에 저장하여 연결리스트로 만든 node 순서이다.

결국 n번째 node와 n+1번째 node를 비교해서 n번째 노드가 커야하며 이를 반복하기에 내림차순으로 node가 연결되어 있어야 한다.

그러기 위해선 node3 -> node4 -> node5 -> node6 -> node1 -> node2 순서로 연결되어 있어야 한다.

7에서 입력값을 뺀 것이 node 번호가 되므로 입력값은 4 3 2 1 6 5이다.

 

4 3 2 1 6 5를 입력하니 넘어갈 수 있었다.


secret_phase

(gdb) disas phase_defused
Dump of assembler code for function phase_defused:
   0x00000000004015c4 <+0>:	sub    $0x78,%rsp
   0x00000000004015c8 <+4>:	mov    %fs:0x28,%rax
   0x00000000004015d1 <+13>:	mov    %rax,0x68(%rsp)
   0x00000000004015d6 <+18>:	xor    %eax,%eax
   0x00000000004015d8 <+20>:	cmpl   $0x6,0x202181(%rip)        # 0x603760 <num_input_strings>
   0x00000000004015df <+27>:	jne    0x40163f <phase_defused+123>
   0x00000000004015e1 <+29>:	lea    0x10(%rsp),%r8
   0x00000000004015e6 <+34>:	lea    0xc(%rsp),%rcx
   0x00000000004015eb <+39>:	lea    0x8(%rsp),%rdx
   0x00000000004015f0 <+44>:	mov    $0x402619,%esi
   0x00000000004015f5 <+49>:	mov    $0x603870,%edi
   0x00000000004015fa <+54>:	call   0x400bf0 <__isoc99_sscanf@plt>
   0x00000000004015ff <+59>:	cmp    $0x3,%eax
   0x0000000000401602 <+62>:	jne    0x401635 <phase_defused+113>
   0x0000000000401604 <+64>:	mov    $0x402622,%esi
   0x0000000000401609 <+69>:	lea    0x10(%rsp),%rdi
   0x000000000040160e <+74>:	call   0x401338 <strings_not_equal>
   0x0000000000401613 <+79>:	test   %eax,%eax
   0x0000000000401615 <+81>:	jne    0x401635 <phase_defused+113>
   0x0000000000401617 <+83>:	mov    $0x4024f8,%edi
   0x000000000040161c <+88>:	call   0x400b10 <puts@plt>
   0x0000000000401621 <+93>:	mov    $0x402520,%edi
   0x0000000000401626 <+98>:	call   0x400b10 <puts@plt>
   0x000000000040162b <+103>:	mov    $0x0,%eax
   0x0000000000401630 <+108>:	call   0x401242 <secret_phase>
   0x0000000000401635 <+113>:	mov    $0x402558,%edi
   0x000000000040163a <+118>:	call   0x400b10 <puts@plt>
   0x000000000040163f <+123>:	mov    0x68(%rsp),%rax
   0x0000000000401644 <+128>:	xor    %fs:0x28,%rax
   0x000000000040164d <+137>:	je     0x401654 <phase_defused+144>
   0x000000000040164f <+139>:	call   0x400b30 <__stack_chk_fail@plt>
   0x0000000000401654 <+144>:	add    $0x78,%rsp
   0x0000000000401658 <+148>:	ret
End of assembler dump.

 

phase_defused 함수를 보니 secret_phase가 숨어있었다.

해당 함수로 들어가는 방법은 phase_4에서 특정 문자열을 입력하는 것이었다.

 

 

0x603870에 phase_4 때 입력한 값이 들어오며 이 때 DrEvil을 추가로 넣으면 secret_phase로 들어와진다.

 

 

phase_4에서 7 0 DrEvil을 넣자 phase_6 종료 후 phase_defused 함수에서 secret_phase에 들어와졌다.

 

(gdb) disas secret_phase
Dump of assembler code for function secret_phase:
   0x0000000000401242 <+0>:	push   %rbx
   0x0000000000401243 <+1>:	call   0x40149e <read_line>
   0x0000000000401248 <+6>:	mov    $0xa,%edx
   0x000000000040124d <+11>:	mov    $0x0,%esi
   0x0000000000401252 <+16>:	mov    %rax,%rdi
   0x0000000000401255 <+19>:	call   0x400bd0 <strtol@plt>
   0x000000000040125a <+24>:	mov    %rax,%rbx
   0x000000000040125d <+27>:	lea    -0x1(%rax),%eax
   0x0000000000401260 <+30>:	cmp    $0x3e8,%eax
   0x0000000000401265 <+35>:	jbe    0x40126c <secret_phase+42>
   0x0000000000401267 <+37>:	call   0x40143a <explode_bomb>
   0x000000000040126c <+42>:	mov    %ebx,%esi
   0x000000000040126e <+44>:	mov    $0x6030f0,%edi
   0x0000000000401273 <+49>:	call   0x401204 <fun7>
   0x0000000000401278 <+54>:	cmp    $0x2,%eax
   0x000000000040127b <+57>:	je     0x401282 <secret_phase+64>
   0x000000000040127d <+59>:	call   0x40143a <explode_bomb>
   0x0000000000401282 <+64>:	mov    $0x402438,%edi
   0x0000000000401287 <+69>:	call   0x400b10 <puts@plt>
   0x000000000040128c <+74>:	call   0x4015c4 <phase_defused>
   0x0000000000401291 <+79>:	pop    %rbx
   0x0000000000401292 <+80>:	ret
End of assembler dump.

 

secret_phase 내에서 read_line 함수를 통해 문자열을 받는다.

그 후 입력값을 strtol 함수를 통해 숫자로 바꾼다.

즉 1000을 넣으면 0x3e8로 변환되며, 입력값에 1을 뺀 값이 0x3e8보다 작아야 폭탄이 터지지 않는다.

그 후 0x6030f0을 edi에 넣은 상태로 fun7 함수를 호출한다.

fun7 함수의 리턴 값이 0x2여야 폭탄이 터지지 않고 통과한다.

(gdb) disas fun7
Dump of assembler code for function fun7:
   0x0000000000401204 <+0>:	sub    $0x8,%rsp
   0x0000000000401208 <+4>:	test   %rdi,%rdi
   0x000000000040120b <+7>:	je     0x401238 <fun7+52>
   0x000000000040120d <+9>:	mov    (%rdi),%edx
   0x000000000040120f <+11>:	cmp    %esi,%edx
   0x0000000000401211 <+13>:	jle    0x401220 <fun7+28>
   0x0000000000401213 <+15>:	mov    0x8(%rdi),%rdi
   0x0000000000401217 <+19>:	call   0x401204 <fun7>
   0x000000000040121c <+24>:	add    %eax,%eax
   0x000000000040121e <+26>:	jmp    0x40123d <fun7+57>
   0x0000000000401220 <+28>:	mov    $0x0,%eax
   0x0000000000401225 <+33>:	cmp    %esi,%edx
   0x0000000000401227 <+35>:	je     0x40123d <fun7+57>
   0x0000000000401229 <+37>:	mov    0x10(%rdi),%rdi
   0x000000000040122d <+41>:	call   0x401204 <fun7>
   0x0000000000401232 <+46>:	lea    0x1(%rax,%rax,1),%eax
   0x0000000000401236 <+50>:	jmp    0x40123d <fun7+57>
   0x0000000000401238 <+52>:	mov    $0xffffffff,%eax
   0x000000000040123d <+57>:	add    $0x8,%rsp
   0x0000000000401241 <+61>:	ret
End of assembler dump.

 

fun7 함수를 보니 재귀적으로 이진 탐색을 하는 것 같다.

우리 목표는 리턴 값이 0x2인 것이므로 그러기 위해선 <fun7+52>에는 가면 안된다.

반환값이 0x2가 되려면, <fun7+46>에서 rax에 0x1을 더하고 <fun7+24>에서 rax + rax를 한 뒤, <fun7+57>로 바로 점프해야 한다.

즉, root -> left -> right 경로에서 값을 찾고 리턴해야한다.

 

 

먼저 처음 fun7에 들어갈 때 기준값은 0x24이며 left로 이동해야 하기 때문에 입력값은 0x24보다 작아야 한다.

 

 

그 후 left로 이동할 때 기준값은 0x08이고, right로 이동해야 하기 때문에 0x08보단 커야 한다.

 

 

마지막으로 right로 이동할 때 기준값은 0x16이며, 해당 fun7 내에서 rax를 0x0 상태로 나와야 한다.

그러기 위해서는 입력값이 0x16과 같아야 하므로 입력값은 22가 된다.

 

 

22를 입력하니 모든 폭탄을 해체할 수 있었다.


phase_n 패치

패치해서 아무 입력값이나 넣어도 폭탄이 터지지 않게 해야되는데.. 생각해보면 phase_n 자체에 안들어가면 되지 않을까란 생각을 했다.

explode_bomb 함수는 phase_n 내에만 있으니,, 그렇게 진행해보자.

 

ida에서만 패치를 해봤지 gdb 내에서 하는 것은 처음이기에 좀 찾아보았다.

write 모드로 gdb 실행
 - gdb --write bomb
opcode 보기
 - x/xb 주소
패치하는 법
 - set {char}주소 = 바꿀 값

 

   0x0000000000400e3a <+154>:	call   0x400ee0 <phase_1>
   0x0000000000400e56 <+182>:	call   0x400efc <phase_2>
   0x0000000000400e72 <+210>:	call   0x400f43 <phase_3>
   0x0000000000400e8e <+238>:	call   0x40100c <phase_4>
   0x0000000000400eaa <+266>:	call   0x401062 <phase_5>
   0x0000000000400ec6 <+294>:	call   0x4010f4 <phase_6>

 

패치해야 될 부분은 다음과 같다.

main 함수에서 phase_n을 호출하는 부분이며 해당 부분을 전부 nop로 덮으면 될 것이다.

 

상대 주소 지정(call rel32) 방식은 일반적으로 5바이트 크기라고 하니 5바이트 전부 0x90으로 덮으면 된다.

 

다음과 같이 덮어써진 것을 확인할 수 있다.

나머지 phase들도 동일하게 nop로 덮어쓴다.

 

(gdb) disas main
Dump of assembler code for function main:
   0x0000000000400da0 <+0>:	push   %rbx
   0x0000000000400da1 <+1>:	cmp    $0x1,%edi
   0x0000000000400da4 <+4>:	jne    0x400db6 <main+22>
   0x0000000000400da6 <+6>:	mov    0x20299b(%rip),%rax        # 0x603748 <stdin@@GLIBC_2.2.5>
   0x0000000000400dad <+13>:	mov    %rax,0x2029b4(%rip)        # 0x603768 <infile>
   0x0000000000400db4 <+20>:	jmp    0x400e19 <main+121>
   0x0000000000400db6 <+22>:	mov    %rsi,%rbx
   0x0000000000400db9 <+25>:	cmp    $0x2,%edi
   0x0000000000400dbc <+28>:	jne    0x400df8 <main+88>
   0x0000000000400dbe <+30>:	mov    0x8(%rsi),%rdi
   0x0000000000400dc2 <+34>:	mov    $0x4022b4,%esi
   0x0000000000400dc7 <+39>:	call   0x400c10 <fopen@plt>
   0x0000000000400dcc <+44>:	mov    %rax,0x202995(%rip)        # 0x603768 <infile>
   0x0000000000400dd3 <+51>:	test   %rax,%rax
   0x0000000000400dd6 <+54>:	jne    0x400e19 <main+121>
   0x0000000000400dd8 <+56>:	mov    0x8(%rbx),%rcx
   0x0000000000400ddc <+60>:	mov    (%rbx),%rdx
   0x0000000000400ddf <+63>:	mov    $0x4022b6,%esi
   0x0000000000400de4 <+68>:	mov    $0x1,%edi
   0x0000000000400de9 <+73>:	call   0x400c00 <__printf_chk@plt>
   0x0000000000400dee <+78>:	mov    $0x8,%edi
   0x0000000000400df3 <+83>:	call   0x400c20 <exit@plt>
   0x0000000000400df8 <+88>:	mov    (%rsi),%rdx
   0x0000000000400dfb <+91>:	mov    $0x4022d3,%esi
   0x0000000000400e00 <+96>:	mov    $0x1,%edi
   0x0000000000400e05 <+101>:	mov    $0x0,%eax
   0x0000000000400e0a <+106>:	call   0x400c00 <__printf_chk@plt>
   0x0000000000400e0f <+111>:	mov    $0x8,%edi
   0x0000000000400e14 <+116>:	call   0x400c20 <exit@plt>
   0x0000000000400e19 <+121>:	call   0x4013a2 <initialize_bomb>
   0x0000000000400e1e <+126>:	mov    $0x402338,%edi
   0x0000000000400e23 <+131>:	call   0x400b10 <puts@plt>
   0x0000000000400e28 <+136>:	mov    $0x402378,%edi
   0x0000000000400e2d <+141>:	call   0x400b10 <puts@plt>
   0x0000000000400e32 <+146>:	call   0x40149e <read_line>
   0x0000000000400e37 <+151>:	mov    %rax,%rdi
   0x0000000000400e3a <+154>:	nop
   0x0000000000400e3b <+155>:	nop
   0x0000000000400e3c <+156>:	nop
   0x0000000000400e3d <+157>:	nop
   0x0000000000400e3e <+158>:	nop
   0x0000000000400e3f <+159>:	call   0x4015c4 <phase_defused>
   0x0000000000400e44 <+164>:	mov    $0x4023a8,%edi
   0x0000000000400e4e <+174>:	call   0x40149e <read_line>
   0x0000000000400e53 <+179>:	mov    %rax,%rdi
   0x0000000000400e56 <+182>:	nop
   0x0000000000400e57 <+183>:	nop
   0x0000000000400e58 <+184>:	nop
   0x0000000000400e59 <+185>:	nop
   0x0000000000400e5a <+186>:	nop
   0x0000000000400e5b <+187>:	call   0x4015c4 <phase_defused>
   0x0000000000400e60 <+192>:	mov    $0x4022ed,%edi
   0x0000000000400e65 <+197>:	call   0x400b10 <puts@plt>
   0x0000000000400e6a <+202>:	call   0x40149e <read_line>
   0x0000000000400e6f <+207>:	mov    %rax,%rdi
   0x0000000000400e72 <+210>:	nop
   0x0000000000400e73 <+211>:	nop
   0x0000000000400e74 <+212>:	nop
   0x0000000000400e75 <+213>:	nop
   0x0000000000400e76 <+214>:	nop
   0x0000000000400e77 <+215>:	call   0x4015c4 <phase_defused>
   0x0000000000400e7c <+220>:	mov    $0x40230b,%edi
   0x0000000000400e81 <+225>:	call   0x400b10 <puts@plt>
   0x0000000000400e86 <+230>:	call   0x40149e <read_line>
   0x0000000000400e8b <+235>:	mov    %rax,%rdi
   0x0000000000400e8e <+238>:	nop
   0x0000000000400e8f <+239>:	nop
   0x0000000000400e90 <+240>:	nop
   0x0000000000400e91 <+241>:	nop
   0x0000000000400e92 <+242>:	nop
   0x0000000000400e93 <+243>:	call   0x4015c4 <phase_defused>
   0x0000000000400e98 <+248>:	mov    $0x4023d8,%edi
   0x0000000000400e9d <+253>:	call   0x400b10 <puts@plt>
   0x0000000000400ea2 <+258>:	call   0x40149e <read_line>
   0x0000000000400ea7 <+263>:	mov    %rax,%rdi
   0x0000000000400eaa <+266>:	nop
   0x0000000000400eab <+267>:	nop
   0x0000000000400eac <+268>:	nop
   0x0000000000400ead <+269>:	nop
   0x0000000000400eae <+270>:	nop
   0x0000000000400eaf <+271>:	call   0x4015c4 <phase_defused>
   0x0000000000400eb4 <+276>:	mov    $0x40231a,%edi
   0x0000000000400eb9 <+281>:	call   0x400b10 <puts@plt>
   0x0000000000400ebe <+286>:	call   0x40149e <read_line>
   0x0000000000400ec3 <+291>:	mov    %rax,%rdi
   0x0000000000400ec6 <+294>:	nop
   0x0000000000400ec7 <+295>:	nop
   0x0000000000400ec8 <+296>:	nop
   0x0000000000400ec9 <+297>:	nop
   0x0000000000400eca <+298>:	nop
   0x0000000000400ecb <+299>:	call   0x4015c4 <phase_defused>
   0x0000000000400ed0 <+304>:	mov    $0x0,%eax
   0x0000000000400ed5 <+309>:	pop    %rbx
   0x0000000000400ed6 <+310>:	ret
End of assembler dump.

 

깔끔하게 덮어썼다.

 

 

패치한 bomb 파일에 아무 값이나 넣어도 정상적?으로 동작된다.