두 가지 함수를 분석하려고 합니다.

phase_2

read_six_numbers

__isoc99_sscanf@plt

역순으로 분석해보겠습니다.

 


__isoc99_sscanf@plt

 

C언어에서 sscanf는 문자열에서 다른 변수로 입력을 받아들이는 역할을 하는 함수이고, 여기서도 비슷할거라 추측합니다.


read_six_numbers

Dump of assembler code for function read_six_numbers:
   0x0000555555401acc <+0>:	sub    $0x8,%rsp
   0x0000555555401ad0 <+4>:	mov    %rsi,%rdx
   0x0000555555401ad3 <+7>:	lea    0x4(%rsi),%rcx
   0x0000555555401ad7 <+11>:	lea    0x14(%rsi),%rax
   0x0000555555401adb <+15>:	push   %rax
   0x0000555555401adc <+16>:	lea    0x10(%rsi),%rax
   0x0000555555401ae0 <+20>:	push   %rax
   0x0000555555401ae1 <+21>:	lea    0xc(%rsi),%r9
   0x0000555555401ae5 <+25>:	lea    0x8(%rsi),%r8
   0x0000555555401ae9 <+29>:	lea    0x12f1(%rip),%rsi        # 0x555555402de1
   0x0000555555401af0 <+36>:	mov    $0x0,%eax
   0x0000555555401af5 <+41>:	callq  0x555555400fc0 <__isoc99_sscanf@plt>
   0x0000555555401afa <+46>:	add    $0x10,%rsp
   0x0000555555401afe <+50>:	cmp    $0x5,%eax
   0x0000555555401b01 <+53>:	jle    0x555555401b08 <read_six_numbers+60>
   0x0000555555401b03 <+55>:	add    $0x8,%rsp
   0x0000555555401b07 <+59>:	retq
   0x0000555555401b08 <+60>:	callq  0x555555401a90 <explode_bomb>

<+29>

%rsi는 인자로 자주 쓰이는 레지스터입니다.

이 레지스터에 어떤 값이 전달되는지 관찰해보았습니다.

(gdb) x/s 0x555555402de1
0x555555402de1:	"%d %d %d %d %d %d"

"%d %d %d %d %d %d" 였습니다. 즉, sscanf에서 문자열에서 6개의 정수로 이루어진 정수 띄어쓰기 단위로 읽어들인다고 생각할 수 있습니다.

 

<+50 ~ 53>

포인트만 관찰해보면, 에서 %eax의 값이 5보다 작거나 같으면 폭탄으로 점프하기 때문에 6개 이상의 숫자를 입력해야합니다. 즉 read_six_number 함수에서 %eax를 통해 값의 개수를 반환한다고 추론할 수 있습니다.

 


<main>

 

   0x00005555554011f6 <+108>:	callq  0x555555400f00 <puts@plt>
   0x00005555554011fb <+113>:	callq  0x555555401b0d <read_line>
   0x0000555555401200 <+118>:	mov    %rax,%rdi
   0x0000555555401203 <+121>:	callq  0x555555401304 <phase_2>
   0x0000555555401208 <+126>:	callq  0x555555401c51 <phase_defused>

<+113> : read_line 함수를 호출합니다. 한 줄 입력받는 함수라고 추론할 수 있습니다.

<+118> : %rdi 에게 값을 넘기는것을 보아 %rdi에 입력받은 문자열이 저장된다고 추론해볼 수 있습니다.

테스트를 진행해보니, 예상이 맞았습니다.


<__isoc_sscanf> <read_six_numbers>

 

지금까지의 분석을 통해

테스트로 얻은 결론은 이렇습니다.

 

  1. %rsi에서 전달한 문자열을 통해 __isoc99_sscanf에서 어떤 형식으로 입력을 받을지 결정할 수 있습니다. 지금 경우에는 "%d %d %d %d %d %d" 가 전달되었으므로 6개의 정수를 읽는다고 판단할 수 있습니다.
  2. 입력한 문자열이 __isoc99_sscanf 를 진행하면서 %rdi에서 전달되어 sscanf 함수를 통해 %rsp에 정수 배열형태로 저장됩니다.
  3. read_six_numbers에서 정수 배열 개수가 5개 이하라면 폭탄을 터뜨립니다.
  4. read_six_numbers는 반환값으로 입력한 정수의 개수를 반환한다. 더 자세히 말하면 read_six_numbers는가 %rax에 저장하지 않으므로,  __isoc99_sscanf 함수가 %rax에 입력한 정수의 개수를 반환합니다.

phase_2

 

Dump of assembler code for function phase_2:
=> 0x0000555555401304 <+0>:	push   %rbp
   0x0000555555401305 <+1>:	push   %rbx
   0x0000555555401306 <+2>:	sub    $0x28,%rsp
   0x000055555540130a <+6>:	mov    %fs:0x28,%rax
   0x0000555555401313 <+15>:	mov    %rax,0x18(%rsp)
   0x0000555555401318 <+20>:	xor    %eax,%eax
   0x000055555540131a <+22>:	mov    %rsp,%rsi
   0x000055555540131d <+25>:	callq  0x555555401acc <read_six_numbers>
   0x0000555555401322 <+30>:	cmpl   $0x1,(%rsp)
   0x0000555555401326 <+34>:	jne    0x555555401331 <phase_2+45>
   0x0000555555401328 <+36>:	mov    %rsp,%rbx
   0x000055555540132b <+39>:	lea    0x14(%rbx),%rbp
   0x000055555540132f <+43>:	jmp    0x555555401341 <phase_2+61>
   0x0000555555401331 <+45>:	callq  0x555555401a90 <explode_bomb>
   0x0000555555401336 <+50>:	jmp    0x555555401328 <phase_2+36>
   0x0000555555401338 <+52>:	add    $0x4,%rbx
   0x000055555540133c <+56>:	cmp    %rbp,%rbx
   0x000055555540133f <+59>:	je     0x555555401351 <phase_2+77>
   0x0000555555401341 <+61>:	mov    (%rbx),%eax
   0x0000555555401343 <+63>:	add    %eax,%eax
   0x0000555555401345 <+65>:	cmp    %eax,0x4(%rbx)
   0x0000555555401348 <+68>:	je     0x555555401338 <phase_2+52>
   0x000055555540134a <+70>:	callq  0x555555401a90 <explode_bomb>
   0x000055555540134f <+75>:	jmp    0x555555401338 <phase_2+52>
   0x0000555555401351 <+77>:	mov    0x18(%rsp),%rax
   0x0000555555401356 <+82>:	xor    %fs:0x28,%rax
   0x000055555540135f <+91>:	jne    0x555555401368 <phase_2+100>
   0x0000555555401361 <+93>:	add    $0x28,%rsp
   0x0000555555401365 <+97>:	pop    %rbx
   0x0000555555401366 <+98>:	pop    %rbp
   0x0000555555401367 <+99>:	retq
   0x0000555555401368 <+100>:	callq  0x555555400f20 <__stack_chk_fail@plt>

 

 

 

위에서 read_six_number에서 분석한 결과에 따르면,

현재 우리가 입력한 값은 오른쪽과 같은 구조를 가지게 됩니다.

(주소값은 예시)

 

 


<+30 ~ 34>

 

(%rsp)는 우리가 입력한 첫번째 숫자입니다.

<+30> 에서 1과 비교 후 <+34>에서 첫번째 숫자가 1이 아니라면 <+45>로 넘어가게 됩니다.

   0x0000555555401322 <+30>:	cmpl   $0x1,(%rsp)
   0x0000555555401326 <+34>:	jne    0x555555401331 <phase_2+45>

<+45>로 넘어가면 폭탄이 터지므로 첫번째 숫자는 1이어야 합니다.

0x0000555555401331 <+45>:	callq  0x555555401a90 <explode_bomb>

<+30 ~ 34>

 

여기서부터는 입력한 첫번째 숫자가 1임을 보장된 상태로 코드를 진행합니다.

<+36>:	mov    %rsp,%rbx
<+39>:	lea    0x14(%rbx),%rbp
<+43>:  jmp    0x555555401341 <phase_2+61>

 

 

<+36>에서 %rbx도 첫번째 숫자를 가리키는 포인터를 가지도록합니다.

<+39>에서 0x14를 더한 주소값을 %rbp에 저장하므로,

%rbp는 우리가 입력한 6번째 숫자를 가리키게 됩니다.

<+43>에서 <+61>로 점프합니다.

 

 


<+61 ~ 68>

 

<+61>:	mov    (%rbx),%eax
<+63>:	add    %eax,%eax
<+65>:	cmp    %eax,0x4(%rbx)
<+68>:	je     0x555555401338 <phase_2+52>
<+70>:	callq  0x555555401a90 <explode_bomb>

 

<+61> : %rax에 첫번째 숫자를 복사합니다.

<+63> : %rax의 값에 2를 곱합니다.

<+65> : 0x4(%rbx)는 %rbx가 가리키는 숫자의 다음 숫자를 의미합니다. 즉, 두번째 숫자와 비교합니다.

<+68> : 두번째 숫자와 첫번째 숫자에 2를 곱한값이 같다면 <+52>로 되돌아갑니다. 다르다면 <+70>에서 폭탄이 터집니다.

 

 


<+52 ~ 61>

 

   0x0000555555401338 <+52>:	add    $0x4,%rbx
   0x000055555540133c <+56>:	cmp    %rbp,%rbx
   0x000055555540133f <+59>:	je     0x555555401351 <phase_2+77>
   0x0000555555401341 <+61>:	mov    (%rbx),%eax

<+52> : %rbx가 0x04를 더해서 다음 원소를 가리키도록 합니다.

<+56> : 마지막 원소를 가리키는 포인터와 두번째 원소를 가리키는 포인터를 비교합니다.

<+59> : 만약 마지막 원소와 주소값이 같다면 <+77>로 점프합니다.

<+61> : 아니라면 <+61>부터 %rbx가 %rbp와 같을 때까지, 즉 %rax에는 다시 %rbx에 2를 곱한값이 저장되어야하고, %rbx가 다음 원소를 가리킴이 마지막 원소에 도달할 때까지 반복합니다.

 


<+77 ~ 99>

 

   0x0000555555401351 <+77>:	mov    0x18(%rsp),%rax
   0x0000555555401356 <+82>:	xor    %fs:0x28,%rax
   0x000055555540135f <+91>:	jne    0x555555401368 <phase_2+100>
   0x0000555555401361 <+93>:	add    $0x28,%rsp
   0x0000555555401365 <+97>:	pop    %rbx
   0x0000555555401366 <+98>:	pop    %rbp
   0x0000555555401367 <+99>:	retq

여기까지 도달했다면, 입력해야하는 숫자가 다음과 같은 공식을 만족해야합니다.

 

a[i+1] = a[i] * 2 : 0 <= i <= 5, a[i] = 1

 

스택에서 phase_2를 pop합니다.

 

즉 답은 다음과 같습니다.

1 2 4 8 16 32

'CS:APP' 카테고리의 다른 글

[CS:APP] BombLab Phase6 해설  (0) 2023.10.20
[CS:APP] BombLab Phase5 해설  (0) 2023.10.20
[CS:APP] BombLab Phase4 해설  (0) 2023.10.20
[CS:APP] BombLab Phase3 해설  (0) 2023.10.20
[CS:APP] BombLab Phase1 해설  (0) 2023.10.19

+ Recent posts