마지막 phase답게 매우 길다. 앞부분을 먼저 확인해본다.
000000000000183a <phase_6>:
183a: f3 0f 1e fa endbr64
183e: 41 56 push %r14
1840: 41 55 push %r13
1842: 41 54 push %r12
1844: 55 push %rbp
1845: 53 push %rbx
1846: 48 83 ec 60 sub $0x60,%rsp
184a: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
1851: 00 00
1853: 48 89 44 24 58 mov %rax,0x58(%rsp)
1858: 31 c0 xor %eax,%eax
185a: 49 89 e5 mov %rsp,%r13
185d: 4c 89 ee mov %r13,%rsi
1860: e8 7d 05 00 00 callq 1de2 <read_six_numbers>
1865: 41 be 01 00 00 00 mov $0x1,%r14d
186b: 49 89 e4 mov %rsp,%r12
# ...
인자를 저장하기 위한 레지스터들을 push하고있다. 그리고 phase_2처럼 read_six_numbers 함수를 호출하고 있다. 6개의 정수를 입력받는다. 이때 %eax = 0, %r13 = %rsp(입력한 정수 배열 arr의 주소), %rsi = %r13(arr)이다. 그리고 %r14d에 1을 저장하고 %r12에 %rsp(arr)를 저장한다.
000000000000183a <phase_6>:
# ...
186e: eb 28 /--- jmp 1898 <phase_6+0x5e>
1870: e8 2b 05 00 00 /-----------|--> callq 1da0 <explode_bomb>
1875: eb 30 | /--|--- jmp 18a7 <phase_6+0x6d>
1877: 48 83 c3 01 | /-----|--|--> add $0x1,%rbx
187b: 83 fb 05 | | | | cmp $0x5,%ebx
187e: 7f 10 | | /--|--|--- jg 1890 <phase_6+0x56>
1880: 41 8b 04 9c /--|--|--|--|--|--> mov (%r12,%rbx,4),%eax
1884: 39 45 00 | | | | | | cmp %eax,0x0(%rbp)
1887: 75 ee | | +--|--|--|--- jne 1877 <phase_6+0x3d>
1889: e8 12 05 00 00 | | | | | | callq 1da0 <explode_bomb>
188e: eb e7 | | \--|--|--|--- jmp 1877 <phase_6+0x3d>
1890: 49 83 c6 01 | | \--|--|--> add $0x1,%r14
1894: 49 83 c5 04 | | | | add $0x4,%r13
1898: 4c 89 ed | | | \--> mov %r13,%rbp
189b: 41 8b 45 00 | | | mov 0x0(%r13),%eax
189f: 83 e8 01 | | | sub $0x1,%eax
18a2: 83 f8 05 | | | cmp $0x5,%eax
18a5: 77 c9 | \--------|------ ja 1870 <phase_6+0x36>
# ...
1898 줄로 jump한다. %rbp에 %r13(arr)을 저장하고, %eax에 arr[0]을 저장한다. 그리고 arr[0] - 1 > 5이면 1870 줄로 jump하는 연산을 하고 있다. 1870 줄에는 explode_bomb 함수가 있으므로 첫 번째 정수 arr[0]은 6 이하여야 한다.
000000000000183a <phase_6>:
# ...
18a7: 41 83 fe 05 | \-----> cmp $0x5,%r14d
18ab: 7f 05 | /--- jg 18b2 <phase_6+0x78>
18ad: 4c 89 f3 | | mov %r14,%rbx
18b0: eb ce \--------------|--- jmp 1880 <phase_6+0x46>
18b2: be 00 00 00 00 \--> mov $0x0,%esi
# ...
arr[0]이 6 이하이면 %r14d(현재 1)과 5를 비교한다. %r14d > 5이면 18b2로 jump하고, 아니면 %rbx에 %r14를 저장하고 1880 줄로 jump한다. 다시 1880 줄을 보자.
000000000000183a <phase_6>:
# ...
1880: 41 8b 04 9c /--|--|--|--|--|--> mov (%r12,%rbx,4),%eax
1884: 39 45 00 | | | | | | cmp %eax,0x0(%rbp)
1887: 75 ee | | +--|--|--|--- jne 1877 <phase_6+0x3d>
1889: e8 12 05 00 00 | | | | | | callq 1da0 <explode_bomb>
188e: eb e7 | | \--|--|--|--- jmp 1877 <phase_6+0x3d>
1890: 49 83 c6 01 | | \--|--|--> add $0x1,%r14
1894: 49 83 c5 04 | | | | add $0x4,%r13
1898: 4c 89 ed | | | \--> mov %r13,%rbp
189b: 41 8b 45 00 | | | mov 0x0(%r13),%eax
189f: 83 e8 01 | | | sub $0x1,%eax
18a2: 83 f8 05 | | | cmp $0x5,%eax
18a5: 77 c9 | \--------|------ ja 1870 <phase_6+0x36>
# ...
%eax에 %r12 + 4*%rbx 주소의 값을 저장한다. %r12는 arr이고, %rbx는 %r14이다. 즉, 최초 arr[0]의 값을 검사하고 arr[1]을 검사한다. 그리고 %rbp(arr)의 값과 검사해 arr[1] == arr[0]이 아니면 1877 줄로 jump하고 있다. 두 값이 동일하면 폭발한다. %rbx를 인덱스로 하여 6개의 숫자를 순회하는 것으로 보인다.
000000000000183a <phase_6>:
# ...
1877: 48 83 c3 01 | /-----|--|--> add $0x1,%rbx
187b: 83 fb 05 | | | | cmp $0x5,%ebx
187e: 7f 10 | | /--|--|--- jg 1890 <phase_6+0x56>
1880: 41 8b 04 9c /--|--|--|--|--|--> mov (%r12,%rbx,4),%eax
1884: 39 45 00 | | | | | | cmp %eax,0x0(%rbp)
1887: 75 ee | | +--|--|--|--- jne 1877 <phase_6+0x3d>
1889: e8 12 05 00 00 | | | | | | callq 1da0 <explode_bomb>
188e: eb e7 | | \--|--|--|--- jmp 1877 <phase_6+0x3d>
1890: 49 83 c6 01 | | \--|--|--> add $0x1,%r14
1894: 49 83 c5 04 | | | | add $0x4,%r13
1898: 4c 89 ed | | | \--> mov %r13,%rbp
189b: 41 8b 45 00 | | | mov 0x0(%r13),%eax
189f: 83 e8 01 | | | sub $0x1,%eax
18a2: 83 f8 05 | | | cmp $0x5,%eax
18a5: 77 c9 | \--------|------ ja 1870 <phase_6+0x36>
# ...
%rbx를 1 증가시킨다. 그리고 다시 %rbx(index) > 5이면 1890 줄로 jump한다. %r13(arr+4)를 %rbp에 저장한다. 그리고 다시 현재 값(이 단계에서는 arr[1])이 6보다 큰지 검사하는 루프가 반복된다. 현재까지 살펴본 코드를 정리하면 다음과 같다.
000000000000183a <phase_6>:
# ...
1860: e8 7d 05 00 00 callq 1de2 <read_six_numbers>
1865: 41 be 01 00 00 00 mov $0x1,%r14d
186b: 49 89 e4 mov %rsp,%r12
186e: eb 28 /--- jmp 1898 <phase_6+0x5e>
1870: e8 2b 05 00 00 /-----------|--> callq 1da0 <explode_bomb>
1875: eb 30 | /--|--- jmp 18a7 <phase_6+0x6d>
1877: 48 83 c3 01 | /-----|--|--> add $0x1,%rbx
187b: 83 fb 05 | | | | cmp $0x5,%ebx
187e: 7f 10 | | /--|--|--- jg 1890 <phase_6+0x56>
1880: 41 8b 04 9c /--|--|--|--|--|--> mov (%r12,%rbx,4),%eax
1884: 39 45 00 | | | | | | cmp %eax,0x0(%rbp)
1887: 75 ee | | +--|--|--|--- jne 1877 <phase_6+0x3d>
1889: e8 12 05 00 00 | | | | | | callq 1da0 <explode_bomb>
188e: eb e7 | | \--|--|--|--- jmp 1877 <phase_6+0x3d>
1890: 49 83 c6 01 | | \--|--|--> add $0x1,%r14
1894: 49 83 c5 04 | | | | add $0x4,%r13
1898: 4c 89 ed | | | \--> mov %r13,%rbp
189b: 41 8b 45 00 | | | mov 0x0(%r13),%eax
189f: 83 e8 01 | | | sub $0x1,%eax
18a2: 83 f8 05 | | | cmp $0x5,%eax
18a5: 77 c9 | \--------|------ ja 1870 <phase_6+0x36>
18a7: 41 83 fe 05 | \-----> cmp $0x5,%r14d
18ab: 7f 05 | /--- jg 18b2 <phase_6+0x78>
18ad: 4c 89 f3 | | mov %r14,%rbx
18b0: eb ce \--------------|--- jmp 1880 <phase_6+0x46>
18b2: be 00 00 00 00 \--> mov $0x0,%esi
# ...
6개의 정수 배열 arr을 입력받는다. 배열의 각 원소 arr[i]는 6이하여야 하며 서로 달라야 한다. 그렇다면 6개의 숫자가 0~6사이에서 매핑되어야 하는데, 18a5 줄의 ja 인스트럭션에서 힌트가 보인다. ja 인스트럭션은 unsigned 값을 비교하므로, 0은 제외된다. 따라서 배열 arr은 1~6의 6개의 정수로 구성된다. 이 조건을 만족해 루프를 탈출하면 %rsi에 0을 저장한다. 다음 코드를 보자.
000000000000183a <phase_6>:
# ...
18b7: 8b 0c b4 /--------> mov (%rsp,%rsi,4),%ecx
18ba: b8 01 00 00 00 | mov $0x1,%eax
18bf: 48 8d 15 6a 39 00 00 | lea 0x396a(%rip),%rdx # 5230 <node1>
18c6: 83 f9 01 | cmp $0x1,%ecx
18c9: 7e 0b | /--- jle 18d6 <phase_6+0x9c>
18cb: 48 8b 52 08 | /--|--> mov 0x8(%rdx),%rdx
18cf: 83 c0 01 | | | add $0x1,%eax
18d2: 39 c8 | | | cmp %ecx,%eax
18d4: 75 f5 | \--|--- jne 18cb <phase_6+0x91>
18d6: 48 89 54 f4 20 | \--> mov %rdx,0x20(%rsp,%rsi,8)
18db: 48 83 c6 01 | add $0x1,%rsi
18df: 48 83 fe 06 | cmp $0x6,%rsi
18e3: 75 d2 \--------- jne 18b7 <phase_6+0x7d>
18e5: 48 8b 5c 24 20 mov 0x20(%rsp),%rbx
# ...
%ecx에 arr[%rsi](arr[i])을 저장한다. 이번에도 배열을 순회하는 것 같다. %eax = 1이고, %rdx에는 .data파일의 0x5230을 저장하고 있다. <node1>이라는 주석을 보아 리스트로 추정할 수 있고, 이는 노드의 주소이다. 현재 배열 요소 arr[i] <= 1이면 18d6 줄로 jump해 arr + 8*i + 0x20(32byte)의 주소에 %rdx를 저장한다. index를 1 증가시키고, index가 6이 아니면 187b 줄로 jump한다. 첫 번째 노드를 할당하는 듯 하다.
18cb - 18d4 루프를 보면 %rdx를 8만큼 증가시키고, %eax를 1 증가시킨다. %rdx가 node임을 고려하면 다음 노드임을 알 수 있다. 그리고 %eax를 1증가시키고 %ecx(arr[0])와 비교한다. 루프가 종료되면 %rdx는 arr[i]값에 해당하는 node의 주소가 된다. 그리고 arr + 8*i + 0x20(32byte)로 배열에 해당 node의 주소를 저장함을 알 수 있다.
이제 %rsi를 증가시켜 다음 arr[i]값에 해당하는 node를 할당한다. 즉, arr[i]의 값이 n이라면, node n이 매핑된다고 생각할 수 있다. 6개의 노드의 주소를 8byte씩 할당되는 것으로 보인다.
.data 파일을 확인해본다.
5230 cb010000 01000000 40520000 00000000 ........@R......
5240 82010000 02000000 50520000 00000000 ........PR......
5250 8d030000 03000000 60520000 00000000 ........`R......
5260 41010000 04000000 70520000 00000000 A.......pR......
5270 48020000 05000000 10510000 00000000 H........Q......
5280 67340000 00000000 72340000 00000000 g4......r4......
이를 토대로 각 노드에 대한 값을 확인해보면 다음과 같다.
address | node | value | adress of next node |
5230 | node 1 | cb 01 00 00 -> 0x01cb -> 459 | 40 52 00 00 -> 0x5240 |
5240 | node 2 | 82 01 00 00 -> 0x0182 -> 386 | 50 52 00 00 -> 0x5250 |
5250 | node 3 | 8d 03 00 00 -> 0x038d -> 909 | 60 52 00 00 -> 0x5260 |
5260 | node 4 | 41 01 00 00 -> 0x0141 -> 321 | 70 52 00 00 -> 0x5270 |
5270 | node 5 | 48 02 00 00 -> 0x0248 -> 584 | 10 51 00 00 -> 0x5110 |
5110 | node 6 | 53 02 00 00 -> 0x0253 -> 595 | 00 00 00 00 -> 0x0000 |
이제 다음 코드를 확인해보자.
000000000000183a <phase_6>:
# ...
18e5: 48 8b 5c 24 20 mov 0x20(%rsp),%rbx
18ea: 48 8b 44 24 28 mov 0x28(%rsp),%rax
18ef: 48 89 43 08 mov %rax,0x8(%rbx)
18f3: 48 8b 54 24 30 mov 0x30(%rsp),%rdx
18f8: 48 89 50 08 mov %rdx,0x8(%rax)
18fc: 48 8b 44 24 38 mov 0x38(%rsp),%rax
1901: 48 89 42 08 mov %rax,0x8(%rdx)
1905: 48 8b 54 24 40 mov 0x40(%rsp),%rdx
190a: 48 89 50 08 mov %rdx,0x8(%rax)
190e: 48 8b 44 24 48 mov 0x48(%rsp),%rax
1913: 48 89 42 08 mov %rax,0x8(%rdx)
1917: 48 c7 40 08 00 00 00 movq $0x0,0x8(%rax)
191e: 00
191f: bd 05 00 00 00 mov $0x5,%ebp
1924: eb 09 /--- jmp 192f <phase_6+0xf5>
# ...
mov 인스트럭션의 동작을 정리하면 다음과 같다. %rsp는 여전히 arr을 의미한다.
%rbx = *(arr + 32) = node 1의 주소
%rax = *(arr + 40) = node 2의 주소
(node 1의 주소) + 8 위치에 %rax(node 2의 주소) 저장.
%rdx = *(arr + 48) = node 3의 주소
(node 2의 주소) + 8 위치에 %rdx(node 3의 주소) 저장
%rax = *(arr + 56) = node 4의 주소
(node 3의 주소) + 8 위치에 %rax(node 3의 주소) 저장
%rdx = *(arr + 64) = node 5의 주소
(node 4의 주소) + 8 위치에 %rdx(node 4의 주소) 저장
%rax = *(arr + 72) = node 6의 주소
(node 5의 주소) + 8 위치에 %rax(node 5의 주소) 저장
(node 6의 주소) + 8 위치에 0 저장
node 1부터 node 6까지 연결되는 linked list 형태이며 마지막에는 0을 저장해 linked list의 끝을 의미하고 있다. 이제 이것이 linked list임을 알았으니 다음 코드를 확인해보자.
000000000000183a <phase_6>:
# ...
191f: bd 05 00 00 00 mov $0x5,%ebp
1924: eb 09 /--- jmp 192f <phase_6+0xf5>
1926: 48 8b 5b 08 /-----|--> mov 0x8(%rbx),%rbx
192a: 83 ed 01 | | sub $0x1,%ebp
192d: 74 11 | /--|--- je 1940 <phase_6+0x106>
192f: 48 8b 43 08 | | \--> mov 0x8(%rbx),%rax
1933: 8b 00 | | mov (%rax),%eax
1935: 39 03 | | cmp %eax,(%rbx)
1937: 7e ed +--|------ jle 1926 <phase_6+0xec>
1939: e8 62 04 00 00 | | callq 1da0 <explode_bomb>
193e: eb e6 \--|------ jmp 1926 <phase_6+0xec>
1940: 48 8b 44 24 58 \-----> mov 0x58(%rsp),%rax
# ...
%ebp에 5를 저장하고 192f 줄로 이동한다. 현재 %rbx는 node 1의 주소이다. %rax에 %rbx + 8의 값을 저장하고 있다. 즉, %rax는 다음 node의 주소이다.
%eax에 다음 node의 값을 저장하고 있다. 그리고 다음 node의 값과 비교한다. 현재 node 값 <= 다음 node값 이면 1926 줄로 jump하고 아니면 폭발한다.
1926 줄을 보면, %rbx를 8 증가시킨다. 즉, 다음 노드를 검사한다. 그리고 %ebp를 1 감소시키고 0이면 1940 줄로 jump하여 루프를 종료하는 것을 볼 수 있다. 그리고 %rax에 arr + 88을 저장한다. node의 값이 오름차순이 되도록 해야 한다.
그 아래 나머지는 스택 에러 처리 부분이다.
000000000000183a <phase_6>:
# ...
1945: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
194c: 00 00
194e: 75 0d /--- jne 195d <phase_6+0x123>
1950: 48 83 c4 60 | add $0x60,%rsp
1954: 5b | pop %rbx
1955: 5d | pop %rbp
1956: 41 5c | pop %r12
1958: 41 5d | pop %r13
195a: 41 5e | pop %r14
195c: c3 | retq
195d: e8 ee f8 ff ff \--> callq 1250 <__stack_chk_fail@plt>
이제 각 node의 값이 오름차순이 되도록 하려면, 4 2 1 5 6 3 순으로 정렬되어야 한다.
(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?
That's number 2. Keep going!
Halfway there!
So you got that one. Try this one.
Good work! On to the next...
4 2 1 5 6 3
Congratulations! You've defused the bomb!
Your instructor has been notified and will verify your solution.
[Inferior 1 (process 8248) exited normally]
(gdb) Quit
phase_6 클리어!
psol.txt
Wow! Brazil is big.
1 2 4 8 16 32
0 984
10 5
aaaaab
4 2 1 5 6 3
이제 hidden phase만 남았다.
'ComputerSystemProgramming' 카테고리의 다른 글
[컴퓨터시스템프로그래밍] CS:APP Cache Lab csim.c (2) solution (0) | 2025.01.05 |
---|---|
[컴퓨터시스템프로그래밍] CS:APP Bomb lab: Defusing a Binary Bomb (8) sercret_phase (0) | 2024.11.12 |
[컴퓨터시스템프로그래밍] CS:APP Bomb lab: Defusing a Binary Bomb (6) phase_5 (0) | 2024.11.11 |
[컴퓨터시스템프로그래밍] 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 |