phase_3은 코드가 꽤 길다. 알아보기 편하게 jump 구조를 표시해주었다.
0000000000001679 <phase_3>:
1679: f3 0f 1e fa endbr64
167d: 48 83 ec 18 sub $0x18,%rsp
1681: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
1688: 00 00
168a: 48 89 44 24 08 mov %rax,0x8(%rsp)
168f: 31 c0 xor %eax,%eax
1691: 48 8d 4c 24 04 lea 0x4(%rsp),%rcx
1696: 48 89 e2 mov %rsp,%rdx
1699: 48 8d 35 6d 1d 00 00 lea 0x1d6d(%rip),%rsi # 340d <array.3473+0x28d>
16a0: e8 4b fc ff ff callq 12f0 <__isoc99_sscanf@plt>
16a5: 83 f8 01 cmp $0x1,%eax
16a8: 7e 1a /--- jle 16c4 <phase_3+0x4b>
16aa: 83 3c 24 07 /-----|--> cmpl $0x7,(%rsp)
16ae: 77 65 | /--|--- ja 1715 <phase_3+0x9c>
16b0: 8b 04 24 | | | mov (%rsp),%eax
16b3: 48 8d 15 a6 1a 00 00 | | | lea 0x1aa6(%rip),%rdx # 3160 <_IO_stdin_used+0x160>
16ba: 48 63 04 82 | | | movslq (%rdx,%rax,4),%rax
16be: 48 01 d0 | | | add %rdx,%rax
16c1: 3e ff e0 | | | notrack jmpq *%rax
16c4: e8 d7 06 00 00 | | \--> callq 1da0 <explode_bomb>
16c9: eb df \--|------ jmp 16aa <phase_3+0x31>
16cb: b8 5b 03 00 00 | mov $0x35b,%eax
16d0: 39 44 24 04 /-----|-----> cmp %eax,0x4(%rsp)
16d4: 75 52 | | /--- jne 1728 <phase_3+0xaf>
16d6: 48 8b 44 24 08 /--|-----|--|--> mov 0x8(%rsp),%rax
16db: 64 48 33 04 25 28 00 | | | | xor %fs:0x28,%rax
16e2: 00 00 | | | |
16e4: 75 49 | | /--|--|--- jne 172f <phase_3+0xb6>
16e6: 48 83 c4 18 | | | | | add $0x18,%rsp
16ea: c3 | | | | | retq
16eb: b8 f6 00 00 00 | | | | | mov $0xf6,%eax
16f0: eb de | +--|--|--|--- jmp 16d0 <phase_3+0x57>
16f2: b8 89 02 00 00 | | | | | mov $0x289,%eax
16f7: eb d7 | +--|--|--|--- jmp 16d0 <phase_3+0x57>
16f9: b8 48 00 00 00 | | | | | mov $0x48,%eax
16fe: eb d0 | +--|--|--|--- jmp 16d0 <phase_3+0x57>
1700: b8 25 01 00 00 | | | | | mov $0x125,%eax
1705: eb c9 | +--|--|--|--- jmp 16d0 <phase_3+0x57>
1707: b8 f5 02 00 00 | | | | | mov $0x2f5,%eax
170c: eb c2 | +--|--|--|--- jmp 16d0 <phase_3+0x57>
170e: b8 52 03 00 00 | | | | | mov $0x352,%eax
1713: eb bb | +--|--|--|--- jmp 16d0 <phase_3+0x57>
1715: e8 86 06 00 00 | | | \--|--> callq 1da0 <explode_bomb>
171a: b8 00 00 00 00 | | | | mov $0x0,%eax
171f: eb af | +--|-----|--- jmp 16d0 <phase_3+0x57>
1721: b8 d8 03 00 00 | | | | mov $0x3d8,%eax
1726: eb a8 | +--|-----|--- jmp 16d0 <phase_3+0x57>
1728: e8 73 06 00 00 | | \--> callq 1da0 <explode_bomb>
172d: eb a7 \-----|--------- jmp 16d6 <phase_3+0x5d>
172f: e8 1c fb ff ff \--------> callq 1250 <__stack_chk_fail@plt>
우선 앞부분을 보면, phase_2와 마찬가지로 sscanf 함수를 호출하는 것을 볼 수 있다.
0000000000001679 <phase_3>:
1679: f3 0f 1e fa endbr64
167d: 48 83 ec 18 sub $0x18,%rsp
1681: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
1688: 00 00
168a: 48 89 44 24 08 mov %rax,0x8(%rsp)
168f: 31 c0 xor %eax,%eax
1691: 48 8d 4c 24 04 lea 0x4(%rsp),%rcx
1696: 48 89 e2 mov %rsp,%rdx
1699: 48 8d 35 6d 1d 00 00 lea 0x1d6d(%rip),%rsi # 340d <array.3473+0x28d>
16a0: e8 4b fc ff ff callq 12f0 <__isoc99_sscanf@plt>
# ...
sscanf 함수는 성공적으로 읽어들인 항목의 개수를 반환한다는 사실을 알고 있으니 어떤 인자가 전달되는지 확인해본다. 우선 스택에 0x18만큼의 공간을 할당한다.
- 1st argument (%rdi): 사용자가 입력한 문자열.
- 2nd argument (%rsi): %rip + 0x1d6d. 주석을 참고하면 0x340d이다. .rodata 파일을 참고하면 %d %d로 두 개의 int형 data이다.
- 3rd argument (%rdx): %rsp로 포인터임을 알 수 있으므로 역시 어떤 배열 arr을 의미할 것이다.
- 4th argument (%rcx): %rsp + 0x4 -> arr[1]
0000000000001679 <phase_3>:
# ...
16a0: e8 4b fc ff ff callq 12f0 <__isoc99_sscanf@plt>
16a5: 83 f8 01 cmp $0x1,%eax
16a8: 7e 1a /--- jle 16c4 <phase_3+0x4b>
16aa: 83 3c 24 07 /-----|--> cmpl $0x7,(%rsp)
16ae: 77 65 | /--|--- ja 1715 <phase_3+0x9c>
16b0: 8b 04 24 | | | mov (%rsp),%eax
16b3: 48 8d 15 a6 1a 00 00 | | | lea 0x1aa6(%rip),%rdx # 3160 <_IO_stdin_used+0x160>
16ba: 48 63 04 82 | | | movslq (%rdx,%rax,4),%rax
16be: 48 01 d0 | | | add %rdx,%rax
16c1: 3e ff e0 | | | notrack jmpq *%rax
16c4: e8 d7 06 00 00 | | \--> callq 1da0 <explode_bomb>
# ...
sscanf 가 읽은 정수가 1 이하이면 16c4 줄로 jump해 폭탄이 터진다. 정상적으로 2개의 정수를 입력했다면 (%rsp), 즉 arr[0]을 의미하므로 첫 번째 정수가 7보다 크면 1715 줄로 이동하는데, 1715 줄은 explode_bomb을 호출한다. 따라서 첫 번째 정수는 7이하여야 한다. 이제 다음 부분을 확인해야는데, notrack jmpq라는 인스트럭션이 보인다 jmpq *%rax는 %rax 레지스터가 가리키는 주소로 간접 점프하는 명령어이고 notrack 프리픽스는 Return Address Stack의 추적을 방지한다고 하는데 결국 jmp 인스트럭션이다. %rax가 가리키는 주소는 다음과 같이 계산된다.
16b0: 8b 04 24 | | | mov (%rsp),%eax
16b3: 48 8d 15 a6 1a 00 00 | | | lea 0x1aa6(%rip),%rdx # 3160 <_IO_stdin_used+0x160>
16ba: 48 63 04 82 | | | movslq (%rdx,%rax,4),%rax
16be: 48 01 d0 | | | add %rdx,%rax
1. %eax : (%rsp). 즉 %eax는 arr[0]이다.
2. %rdx : %rip + 0x1aa6. 주석을 참고하면 0x3160이고 이는 .rodata에서 jump table의 시작 주소가 된다.
3. %rax : movslq 인스트럭션은 메모리에서 부호 있는 32비트 값을 읽어 64비트로 부호 확장하여 %rax에 저장한다. %rax는 (%rdx + %rax * 4)로 계산되며 앞서 %eax = arr[0]이므로, %rax도 arr[0] 이다. 이로써 %rax는 jump table에서 arr[0]에 해당하는 offset이 된다.
4. %rax에 %rdx를 더함으로써 최종 jump address가된다.
이로부터 arr[0] == 7까지 각 jump table에서 정답 %eax가 존재하고, 16d0에서 다음과 같이 정답을 비교함을 알 수 있다.
0000000000001679 <phase_3>:
# ...
16d0: 39 44 24 04 /-----|-----> cmp %eax,0x4(%rsp)
16d4: 75 52 | | /--- jne 1728 <phase_3+0xaf>
16d6: 48 8b 44 24 08 /--|-----|--|--> mov 0x8(%rsp),%rax
16db: 64 48 33 04 25 28 00 | | | | xor %fs:0x28,%rax
16e2: 00 00 | | | |
16e4: 75 49 | | /--|--|--- jne 172f <phase_3+0xb6>
16e6: 48 83 c4 18 | | | | | add $0x18,%rsp
16ea: c3 | | | | | retq
# ...
16d0에서는 정답 %eax와 arr[1]을 비교한다. 만약 일치하지 않는다면 1728 줄로 이동하고, 여기에는 explode_bomb 함수를 호출해 폭탄이 터지게 된다. 일치한다면 쭉 진행해서 phase_3을 통과한다.
즉 총 7개의 정답쌍이 존재한다.
0 984
1 859
2 246
3 649
4 72
5 293
6 757
7 850
phase_3의 전체 구조는 다음과 같다.
0000000000001679 <phase_3>:
1679: f3 0f 1e fa endbr64
167d: 48 83 ec 18 sub $0x18,%rsp
1681: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
1688: 00 00
168a: 48 89 44 24 08 mov %rax,0x8(%rsp)
168f: 31 c0 xor %eax,%eax
1691: 48 8d 4c 24 04 lea 0x4(%rsp),%rcx
1696: 48 89 e2 mov %rsp,%rdx
1699: 48 8d 35 6d 1d 00 00 lea 0x1d6d(%rip),%rsi # 340d <array.3473+0x28d>
16a0: e8 4b fc ff ff callq 12f0 <__isoc99_sscanf@plt>
16a5: 83 f8 01 cmp $0x1,%eax
16a8: 7e 1a /--- jle 16c4 <phase_3+0x4b>
16aa: 83 3c 24 07 /-----|--> cmpl $0x7,(%rsp)
16ae: 77 65 | /--|--- ja 1715 <phase_3+0x9c>
16b0: 8b 04 24 | | | mov (%rsp),%eax
16b3: 48 8d 15 a6 1a 00 00 | | | lea 0x1aa6(%rip),%rdx # 3160 <_IO_stdin_used+0x160>
16ba: 48 63 04 82 | | | movslq (%rdx,%rax,4),%rax
16be: 48 01 d0 | | | add %rdx,%rax
16c1: 3e ff e0 | | | notrack jmpq *%rax
16c4: e8 d7 06 00 00 | | \--> callq 1da0 <explode_bomb>
16c9: eb df \--|------ jmp 16aa <phase_3+0x31>
/* arr[0] == 7 */ |
16cb: b8 5b 03 00 00 | mov $0x35b,%eax # 859
16d0: 39 44 24 04 /-----|-----> cmp %eax,0x4(%rsp)
16d4: 75 52 | | /--- jne 1728 <phase_3+0xaf>
16d6: 48 8b 44 24 08 /--|-----|--|--> mov 0x8(%rsp),%rax
16db: 64 48 33 04 25 28 00 | | | | xor %fs:0x28,%rax
16e2: 00 00 | | | |
16e4: 75 49 | | /--|--|--- jne 172f <phase_3+0xb6>
16e6: 48 83 c4 18 | | | | | add $0x18,%rsp
16ea: c3 | | | | | retq
/* arr[0] == 6 */ | | | | |
16eb: b8 f6 00 00 00 | | | | | mov $0xf6,%eax # 246
16f0: eb de | +--|--|--|--- jmp 16d0 <phase_3+0x57>
/* arr[0] == 5 */ | | | | |
16f2: b8 89 02 00 00 | | | | | mov $0x289,%eax # 649
16f7: eb d7 | +--|--|--|--- jmp 16d0 <phase_3+0x57>
/* arr[0] == 4 */ | | | | |
16f9: b8 48 00 00 00 | | | | | mov $0x48,%eax # 72
16fe: eb d0 | +--|--|--|--- jmp 16d0 <phase_3+0x57>
/* arr[0] == 3 */ | | | | |
1700: b8 25 01 00 00 | | | | | mov $0x125,%eax # 293
1705: eb c9 | +--|--|--|--- jmp 16d0 <phase_3+0x57>
/* arr[0] == 2 */ | | | | |
1707: b8 f5 02 00 00 | | | | | mov $0x2f5,%eax # 757
170c: eb c2 | +--|--|--|--- jmp 16d0 <phase_3+0x57>
/* arr[0] == 1 */ | | | | |
170e: b8 52 03 00 00 | | | | | mov $0x352,%eax # 850
1713: eb bb | +--|--|--|--- jmp 16d0 <phase_3+0x57>
1715: e8 86 06 00 00 | | | \--|--> callq 1da0 <explode_bomb>
171a: b8 00 00 00 00 | | | | mov $0x0,%eax # 0
171f: eb af | +--|-----|--- jmp 16d0 <phase_3+0x57>
/* arr[0] == 0 */ | | | |
1721: b8 d8 03 00 00 | | | | mov $0x3d8,%eax # 984
1726: eb a8 | +--|-----|--- jmp 16d0 <phase_3+0x57>
/* arr[0] out of range */ | | |
1728: e8 73 06 00 00 | | \--> callq 1da0 <explode_bomb>
172d: eb a7 \-----|--------- jmp 16d6 <phase_3+0x5d>
172f: e8 1c fb ff ff \--------> callq 1250 <__stack_chk_fail@plt>
이제 7개 정답쌍 중 하나를 입력해본다.
(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?
That's number 2. Keep going!
0 984
Halfway there!
phase_3 완료. psol.txt를 업데이트하고 마무리한다.