mgchem's log
article thumbnail
Published 2023. 10. 8. 01:46
Lab2: Bomblab (phase 2) Study/CS

Overview

 

(gdb) disas phase_2

phase_2 함수의 구성을 살펴보자.

 

Disassembled code를 보고 대략적인 흐름을 파악한다.

 

1. 0x132d에서 read_six_numbers 함수를 call한다. 숫자 6개를 입력하면 될 것이고, 실질적으로 우리가 집중해서 봐야 할 부분은 입력값을 받은 이 이후가 될 것이다.

 

2. stack의 가장 앞에 저장된 값이 1 이하면 폭탄이 터진다.

 

3. 0x1349 ~ 0x1361은 loop이다.

 

4. %rbx는 index로 초기값은 0x1338에서 1로 주어졌다. stack 내에 저장된 값(아마 우리가 입력한 숫자 6개?)과 loop 내 과정을 통해 만들어진 %eax에 저장된 값을 비교하여 다를 경우 0x1363으로 넘어가 폭탄이 터진다.

 

5. %rbx, 즉 index가 6이 될 때까지 무사히 loop를 돌면 phase_2 함수를 call했던 위치로 return된다. 아마 폭탄 해체 성공 메시지가 출력될 것이다.

 

Loop 안에서 무슨 일이 일어나는지 code만 보고 파악할 수도 있겠지만, 가장 쉽고 머리가 편한 방법은 임의의 값을 입력하고 한 줄씩 실행시켜 각 register에서 무슨 일이 일어나는지 살펴보는 것이다. 2번에서 봤듯이 가장 앞의 값이 1 이하면 폭탄이 터지므로, 임의로 2 3 4 5 6 7를 입력해보고 더 자세히 살펴보자.

 


gdb TUI mode

 

TUI를 활용하면 code와 register 변화를 확인하며 디버깅할 수 있다.

(gdb) layout reg
(gdb) layout asm

register 현황과 가장 최근 변경된 register을 함께 띄울 수 있다. assembly code만 보고 싶으면 layout asm을 입력하면 되며, TUI mode를 끄고 싶으면 tui disable을 입력하면 된다. 관련 명령어 목록은 링크 참고.

 


Defusing phase 2

 

read_six_numbers 직후 stack을 확인한다.

입력한 2 3 4 5 6 7이 (%rsp), 0x4(%rsp), 0x8(%rsp), 0xc(%rsp), 0x10(%rsp), 0x14(%rsp)에 순서대로 저장되어있다.

int는 32bit, 하나의 주소값에 할당된 메모리는 1Byte == 8bit. 따라서 각 숫자는 4Byte, 즉 주소값 4씩을 차지한다.

 

loop에 처음 들어온 상태이다. %ebx는 1인 상태로 시작하며, %rbp는 %rsp로 업데이트된 상태이다.

한 줄씩 실행하는 명령인 si를 입력하며 %rax에 저장된 값의 변화를 확인하면;

> mov %ebx, %eax     //%eax에 %ebx == 1을 저장한다.

> imul (%rsp), %eax     //stack의 가장 앞에 위치한 2를 %eax에 곱한다. %eax == 2

> add -0x4(%rbp, %rbx, 4), %eax     //현재 %rbx는 1이므로 해당 값은 stack 가장 앞에 위치한 2를 %eax에 더한다. %eax == 4

> cmp %eax, 0x0(%rbp, %rbx, 4) 및 je     //이 다음 줄이 explode_bomb이다. stack에서 그 다음 위치한 3과 %eax는 같아야 한다.

%eax == 4이므로 폭탄은 터지게 될 것이다. Breakpoint가 작동하는 것까지는 확인해보고 실행 종료해도 괜찮다. quit를 입력하여 실행을 멈출 수 있다.

 

만약 %eax와 stack 두 번째 자리한 값이 같아 je에서 <phase_2+53>으로 jump한다면 어떻게 될까?

%rbx에 1을 더하고 해당 값이 6이 되었는지 확인한다음 위의 시행을 반복한다. 단, 이번엔 %rbx의 값이 1이 늘어난 상태이다.

%rbx는 한 바퀴 돌 때마다 1씩 늘어나는 값이며, 특정 값에 도달할 경우 반복이 멈춘다. 이 코드에서 %rbx는 index 역할을 함을 알 수 있다.

 

앞서 얻은 정보를 정리해보자.

>mov %ebx, %eax     //%eax에 현재 index값을 저장한다.

> imul (%rsp), %eax     //stack의 가장 앞에 위치한 값을 %eax에 곱한다.

> add -0x4(%rbp, %rbx, 4), %eax     //stack에서 가장 앞에 있는 값부터 시작하여, 한 바퀴 돌 때마다 그 다음 값을 %eax에 더한다.

> cmp %eax, 0x0(%rbp, %rbx, 4) 및 je     //다음 줄이 explode_bomb이다. stack에서 두 번째 위치한 값부터 시작하여, 한 바퀴 돌 때마다 그 다음값과 %eax를 비교한다. 둘은 같아야 한다.

 

만약 우리가 첫번째 숫자로 2를 준다면 stack의 가장 앞에 위치한 값은 2이다.

첫 번째 loop에서는 index 1이므로 %eax는 1, 2를 곱하면 2, stack 가장 앞의 값을 더하면 4. 이 값이 stack 두 번째 값과 같아야 하므로 우리가 입력해야하는 두 번째 숫자는 4이다.

그럼 다음 loop, 그 다음 loop도 동일하게 생각할 수 있다.

 

다시 말하면, 첫 번째 숫자로 2를 입력할 경우,

첫 번째 숫자와 두 번째 숫자는 1*2 == 2 차이

두 번째 숫자와 세 번째 숫자는 2*2 == 4 차이

세 번째 숫자와 네 번째 숫자는 3*2 == 6 차이.

이와 같이 이어진다.

따라서 답은 2 4 8 14 22 32 가 될 것이라 예상할 수 있다.

 

$ gdb bomb
(gdb) b explode_bomb
(gdb) run

gdb를 다시 실행하여 breakpoint를 걸고 bomb을 실행한다. phase_1의 답은 복사해두었던 값을 입력하여 넘어가고,

2 4 8 14 22 32를 입력하면?

 

Phase 2를 해결하였다.

'Study > CS' 카테고리의 다른 글

Lab2: Bomblab (phase 5)  (0) 2023.10.08
Lab2: Bomblab (phase 4)  (0) 2023.10.08
Lab2: Bomblab (phase 3)  (1) 2023.10.08
Lab2: Bomblab (phase 1)  (1) 2023.10.07
Lab2: Bomblab (bomb-quiet)  (0) 2023.10.07
profile

mgchem's log

@mgchem

뱁새의 다리찣기