이전 포스트에서 phase_1까지 해제하였고, phase_2를 풀어본다. 매번 앞 phase들의 정답을 입력하는 것은 실수할 가능성도 있고 귀찮으므로 psol.txt에 각 줄마다 phase들의 정답을 순서대로 적어두면, 이 파일을 실행시에 읽어서 바로 패스할 수 있다. phase_1의 정답은 "Wow! Brazil is big."이었으므로 psol.txt에 저장해준다.
origami0352@lagavulin:~/bomb4$ vim psol.txt
origami0352@lagavulin:~/bomb4$ ls
bomb bomb.c bomb_data.txt bomb_disassembled.txt bomb_rodata.txt psol.txt README
psol.txt
Wow! Brazil is big.
이제 phase_2 함수를 확인해보자.
000000000000160b <phase_2>:
160b: f3 0f 1e fa endbr64
160f: 55 push %rbp
1610: 53 push %rbx
1611: 48 83 ec 28 sub $0x28,%rsp
1615: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
161c: 00 00
161e: 48 89 44 24 18 mov %rax,0x18(%rsp)
1623: 31 c0 xor %eax,%eax
1625: 48 89 e6 mov %rsp,%rsi
1628: e8 b5 07 00 00 callq 1de2 <read_six_numbers>
162d: 83 3c 24 01 cmpl $0x1,(%rsp)
1631: 75 0a jne 163d <phase_2+0x32>
1633: 48 89 e3 mov %rsp,%rbx
1636: 48 8d 6c 24 14 lea 0x14(%rsp),%rbp
163b: eb 15 jmp 1652 <phase_2+0x47>
163d: e8 5e 07 00 00 callq 1da0 <explode_bomb>
1642: eb ef jmp 1633 <phase_2+0x28>
1644: e8 57 07 00 00 callq 1da0 <explode_bomb>
1649: 48 83 c3 04 add $0x4,%rbx
164d: 48 39 eb cmp %rbp,%rbx
1650: 74 0b je 165d <phase_2+0x52>
1652: 8b 03 mov (%rbx),%eax
1654: 01 c0 add %eax,%eax
1656: 39 43 04 cmp %eax,0x4(%rbx)
1659: 74 ee je 1649 <phase_2+0x3e>
165b: eb e7 jmp 1644 <phase_2+0x39>
165d: 48 8b 44 24 18 mov 0x18(%rsp),%rax
1662: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
1669: 00 00
166b: 75 07 jne 1674 <phase_2+0x69>
166d: 48 83 c4 28 add $0x28,%rsp
1671: 5b pop %rbx
1672: 5d pop %rbp
1673: c3 retq
1674: e8 d7 fb ff ff callq 1250 <__stack_chk_fail@plt>
phase_1에 비해 코드가 길기 때문에 나누어 확인해본다.
000000000000160b <phase_2>:
160b: f3 0f 1e fa endbr64
160f: 55 push %rbp
1610: 53 push %rbx
1611: 48 83 ec 28 sub $0x28,%rsp
1615: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
161c: 00 00
161e: 48 89 44 24 18 mov %rax,0x18(%rsp)
1623: 31 c0 xor %eax,%eax
1625: 48 89 e6 mov %rsp,%rsi
1628: e8 b5 07 00 00 callq 1de2 <read_six_numbers>
스택에 0x28만큼의 바이트를 할당했다. %rsi에 %rsp를 저장하고 %rdi와 함께 read_six_numbers 함수의 argument로 전달하여 호출한다.(argument1: %rdi, argument2: %rsi) 사용자의 입력 %rdi로부터 6개의 숫자를 읽어 %rsi에 저장하는 것 같다. read_six_numbers 함수를 확인해보자.
0000000000001de2 <read_six_numbers>:
1de2: f3 0f 1e fa endbr64
1de6: 48 83 ec 08 sub $0x8,%rsp
1dea: 48 89 f2 mov %rsi,%rdx
1ded: 48 8d 4e 04 lea 0x4(%rsi),%rcx
1df1: 48 8d 46 14 lea 0x14(%rsi),%rax
1df5: 50 push %rax
1df6: 48 8d 46 10 lea 0x10(%rsi),%rax
1dfa: 50 push %rax
1dfb: 4c 8d 4e 0c lea 0xc(%rsi),%r9
1dff: 4c 8d 46 08 lea 0x8(%rsi),%r8
1e03: 48 8d 35 f7 15 00 00 lea 0x15f7(%rip),%rsi # 3401 <array.3473+0x281>
1e0a: b8 00 00 00 00 mov $0x0,%eax
1e0f: e8 dc f4 ff ff callq 12f0 <__isoc99_sscanf@plt>
1e14: 48 83 c4 10 add $0x10,%rsp
1e18: 83 f8 05 cmp $0x5,%eax
1e1b: 7e 05 jle 1e22 <read_six_numbers+0x40>
1e1d: 48 83 c4 08 add $0x8,%rsp
1e21: c3 retq
1e22: e8 79 ff ff ff callq 1da0 <explode_bomb>
lea 인스트럭션을 이용한 주소 복사가 많고 sscanf 함수를 호출한다. 여러 인자가 sscanf 함수에 argument로 전달되고, sscanf 함수의 return 값 eax가 5보다 작거나 같으면 1e22로 jump해서 explode_bomb함수가 호출된다. sscanf 함수에 전달되는 인자를 살펴보면 다음과 같다.
- 1st argument (%rdi): 사용자가 입력한 문자열.
- 2st argument (%rsi): %rip + 0x15f7. 주석을 참고하면 0x3401이다. .rodata 파일을 참고하면 "%d %d %d %d %d %d"로 6개의 int형 data임을 알 수 있다.
- 3rd argument (%rdx): phase_2에서 전달받은 %rsi. %rsi는 %rsp의 값을 전달받았으므로 어떤 포인터를 의미한다. 여기서는 어떤 배열 arr[0]의 주소 arr이라 생각할 수 있다.
- 4th argument (%rcx): phase_2의 %rsi + 0x4 -> arr + 4(arr[1])
- 5th argument (%r8): phase_2의 %rsi + 0x8 -> arr + 8(arr[2])
- 6rh argument (%r9): phase_2의 %rsi + 0xc -> arr + 12(arr[3])
- 7th argument : phase_2의 %rsi + 0x10 -> arr + 16(arr[4])
- 8th argument : phase_2의 %rsi + 0x14 -> arr + 20(arr[5])
여기서 7th, 8th argument는 스택에 저장된다. (argument 6개 초과) sscanf 함수는 읽기에 성공한 항목의 개수를 반환한다. 즉, 6개의 정수가 모두 일치한 경우에만 jle 인스트럭션이 실행되지 않아 폭탄이 터지지 않는다. 이제 다시 phase_2 함수를 확인한다.
000000000000160b <phase_2>:
# ...
162d: 83 3c 24 01 cmpl $0x1,(%rsp)
1631: 75 0a jne 163d <phase_2+0x32>
1633: 48 89 e3 mov %rsp,%rbx
1636: 48 8d 6c 24 14 lea 0x14(%rsp),%rbp
163b: eb 15 jmp 1652 <phase_2+0x47>
163d: e8 5e 07 00 00 callq 1da0 <explode_bomb
# ...
(%rsp)와 0x1을 비교하여 (%rsp)의 값이 0x1이 아니면 163d로 jump해 explode_bomb함수를 호출한다. %rsp는 배열 arr의 주소이므로 (%rsp)는 arr[0]이 된다. arr[0]이 1이어야 하므로, 입력한 문자열의 첫번째 숫자가 1이어야 한다. arr[0]이 1이면 %rbx에 %rsp(arr)를, %rbp에 %rsp + 0x14(arr[5])를 저장하고 1652로 jump한다.
000000000000160b <phase_2>:
# ...
1652: 8b 03 mov (%rbx),%eax
1654: 01 c0 add %eax,%eax
1656: 39 43 04 cmp %eax,0x4(%rbx)
1659: 74 ee je 1649 <phase_2+0x3e>
165b: eb e7 jmp 1644 <phase_2+0x39>
165d: 48 8b 44 24 18 mov 0x18(%rsp),%rax
1662: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
1669: 00 00
166b: 75 07 jne 1674 <phase_2+0x69>
166d: 48 83 c4 28 add $0x28,%rsp
1671: 5b pop %rbx
1672: 5d pop %rbp
1673: c3 retq
1674: e8 d7 fb ff ff callq 1250 <__stack_chk_fail@plt>
%rbx는 arr이므로 %eax는 arr[0]이 된다. %eax를 2배 해주고 cmp 인스트럭션을 통해 %eax가 %rbx + 0x4의 값, 즉 arr[1]과 같다면 1649로 jump한다. arr[i] = 2*arr[i-1]의 구조가 예상된다. 다음과 같이 --visualize -jumps 옵션을 사용한 것처럼 jump구조를 표시해보았다.
000000000000160b <phase_2>:
# ...
162d: 83 3c 24 01 cmpl $0x1,(%rsp)
1631: 75 0a /--- jne 163d <phase_2+0x32>
1633: 48 89 e3 | mov %rsp,%rbx
1636: 48 8d 6c 24 14 | lea 0x14(%rsp),%rbp
163b: eb 15 /--|--- jmp 1652 <phase_2+0x47>
163d: e8 5e 07 00 00 | \--> callq 1da0 <explode_bomb>
1642: eb ef | jmp 1633 <phase_2+0x28>
1644: e8 57 07 00 00 | callq 1da0 <explode_bomb>
1649: 48 83 c3 04 /--|-----> add $0x4,%rbx
164d: 48 39 eb | | cmp %rbp,%rbx
1650: 74 0b | | /--- je 165d <phase_2+0x52>
1652: 8b 03 | \--|--> mov (%rbx),%eax
1654: 01 c0 | | add %eax,%eax
1656: 39 43 04 | | cmp %eax,0x4(%rbx)
1659: 74 ee \-----|--- je 1649 <phase_2+0x3e>
165b: eb e7 | jmp 1644 <phase_2+0x39>
165d: 48 8b 44 24 18 \--> mov 0x18(%rsp),%rax
1662: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
1669: 00 00
166b: 75 07 jne 1674 <phase_2+0x69>
166d: 48 83 c4 28 add $0x28,%rsp
1671: 5b pop %rbx
1672: 5d pop %rbp
1673: c3 retq
1674: e8 d7 fb ff ff callq 1250 <__stack_chk_fail@plt>
1649 ~ 1659 줄이 반복되다가 %rbx가 %rbp일 경우, 즉 arr[5]에 도달하면 165d로 jump하여 루프가 종료된다. 즉, 정답은 1부터 2배씩 커지는 6자리 배열로, 1 2 4 8 16 32가 된다. breakpoint를 걸고 실행해본다.
(gdb) set args psol.txt
(gdb) break explode_bomb
Breakpoint 1 at 0x1da0
(gdb) info breakpoint
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000000001da0 <explode_bomb>
(gdb) run
Starting program: /home/origami0352/bomb4/bomb psol.txt
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
Phase 1 defused. How about the next one?
1 2 4 8 16 32
That's number 2. Keep going!
phase_2 완료. psol.txt에 추가해준다.
Wow! Brazil is big.
1 2 4 8 16 32
'ComputerSystemProgramming' 카테고리의 다른 글
[컴퓨터시스템프로그래밍] CS:APP Bomb lab: Defusing a Binary Bomb (5) phase_4 (0) | 2024.11.11 |
---|---|
[컴퓨터시스템프로그래밍] CS:APP Bomb lab: Defusing a Binary Bomb (4) phase_3 (0) | 2024.11.09 |
[컴퓨터시스템프로그래밍] CS:APP Bomb lab: Defusing a Binary Bomb (2) phase_1 (0) | 2024.11.08 |
[컴퓨터시스템프로그래밍] CS:APP Bomb lab: Defusing a Binary Bomb (1) 준비 (0) | 2024.11.07 |
[컴퓨터시스템프로그래밍] CS:APP Data lab bits.c Solution (3) | 2024.10.12 |