기타

Bomb Lab(밤랩) Phase 1 ~ Phase 6 + Secret Phase 풀이 (시스템프로그래밍)

restudy 2022. 7. 9. 19:08
반응형

이 포스트는 시스템 프로그래밍(System programming)에서 다루는 과제인 bomb lab(밤랩)의 풀이를 다루고 있다.

대표적인 원서인 컴퓨터 시스템(Computer Systems)에서도 다루고 있으며, 카네기멜론 대학(CMU)에서 만들어진 과제이다.

 

만약 이 글을 검색을 통해 들어왔다면 과제를 도움받기 위해 들어왔을 것이지만, 혹여나 자발적으로 풀어보고 싶은 사람이 있다면 아래의 링크에서 다운해볼 수 있다.

 

http://csapp.cs.cmu.edu/3e/labs.html

 

그 중에서도 Bomb Lab(밤랩)은 어셈블리 코드 내에 있는 폭탄에 해당하는 코드를 찾아서 해체시키는 컨셉의 과제이다.

쉽게 말해서 어셈블리 코드를 해석하는 것을 연습해볼 수 있는 과제이다.

 

그럼 바로 풀이와 해설을 정리하겠다.

기본 Phase에는 Phase 1부터 Phase 6이 있으며, 추가적으로 숨겨진 Secret Phase에 대한 풀이도 소개한다.

직접 풀이하고 작성한 것이니 다른 포스트보다 상세하게 나와있을 것이다. (다만, 그렇기 때문에 일부 부정확한 내용이 있을 수도 있다.)


Phase 1

우선 gdb bomb 해준 뒤 disas main을 통해 main 함수의 어셈블리코드를 확인해 보면 다음과 같다.

 

 

 

여기서 main+91번째 줄에서 첫 번째 문구를 검사하는 phase_1 함수가 실행됨을 알 수 있다. 

그래서 우선 b *main+91을 통해 main+91번째 줄에서 breakpoint를 걸고 프로그램을 실행해본다. 

이 때 입력값은 asdf 등의 아무 문구나 입력해준다.

 

 

 

그럼 여기서 si를 입력해 함수 phase_1 함수 안으로 들어가준다.

 

 

 

phase_1 함수에서 알 수 있는 점은 우선 phase_1+4 행에서 %rsi (2번째 매개변수 레지스터)가 사용되었으로 phase_1 함수에는 인자가 %rdi를 통해 한 개 전달되었고 strings_not_equal 함수에 %rdi, %rsi와 관련하여 두 개의 input이 들어간다는 것이다.

strings_not_equal 함수는 disas를 통해 직접 확인해보아도 좋고 두 string이 같은지 확인하는 함수임을 추측할 수도 있다.

rdi에 들어있는 값은 사용자의 input일 것이므로 rsi 레지스터에 들어있는 string이 무엇인지 확인하면 된다.

따라서 phase_1+4번 행 뒤로 넘어간 시점에서 rsi 레지스터에 어떤 값이 들어있는지 확인해주면 된다.

 

 

 

그럼 답은 When a problem comes along, you must zip it! 임을 확인할 수 있고, 이 문구를 solution.txt를 만들어 첫 번째 행에 입력하고 ./bomb solutiont.txt를 통해 프로그램을 돌려주면 다음과 같이 첫 번째 phase는 통과한 것을 확인할 수 있다.

 

 

 

 

Phase 2

이번에는 disas phase_2를 통해 phase_2 함수의 어셈블리코드를 확인한다.

 

 

 

disas read_six_numbers를 통해 어셈블리코드를 확인해도 되고 (disas read_six_numbers 한 뒤 x/s를 통해 입력 형식을 확인해주면 “%d %d %d %d %d %d"로 6개의 정수를 입력받음 을 확인할 수 있다.), 함수의 이름 통해 6개의 정수를 input으로 받는 것을 추측할 수 있다. 따라서 solution.txt의 2번째 줄에 "1 2 3 4 5 6"을 입력한 뒤 gdb를 실행해본다.

 

main의 phase_2를 호출하는 행인 main+121행에 breakpoint를 걸고 r solution.txt로 코드 를 실행하고 si를 통해 phase_2 함수로 진입한 뒤 ni와 disas를 반복하며 진행해본다.

 

 

 

코드를 확인하면 위와 같이 phase_2+53행부터 phase_2+73행 내에서 rbx 레지스터의 값이 1부터 1씩 증가하면서 6이 될 때까지 5번 반복된 뒤 phase_2+82행으로 탈출하는 것을 알 수 있다.

또한 rsp에 메모리가 길게 할당되어있고 정수 6개가 들어가 있음을 추측할 수 있 다.

반복문에서는 rbp에 rsp를 넣고 rbp+(4*rbx)-4의 연산을 통해 rbp 기준으로 0번째 index에 있는 숫자부터 차례대로 다음 숫자를 가져온다는 것을 알 수 있다.

 

loop를 첫 번째 돌 때 eax에는 1이 들어간다. 65번행에서 수행하는 연산을 해석하면 eax = 1 + (0번째 index 의 숫자)가 된다.

69번행에서는 (0번째 index의 숫자) + 1 = (1번째 index의 숫자)가 된다. loop를 두 번째 돌 때에는 eax에 2가 들어간다. 마찬가지로 cmp문을 해석하면 (1번째 index의 숫자) + 2 = (2번째 index의 숫자)가 된다.

이것은 계속 반복되면서 마지막에 (4번 째 index의 숫자) + 5 = (5번째 index의 숫자)가 된다. 즉, 6개의 수는 차례대로 n, n+1, n+1+2, n+1+2+3, n+1+2+3+4, n+1+2+3+4+5를 만족해야 한다. (이 때 n은 정수)

따라서 간단하게 n에 1을 넣어서 만든 “1 2 4 7 11 16”이 여러 답 중 하나가 된다.

 

마찬가지로 solution.txt의 두 번째 행에 입력하고 확인해보면 phase2도 통과한 것을 확인할 수 있다.

 

 

 

 

Phase 3

 

phase_3에 breakpoint를 걸어준 뒤 phase_3에서 sscanf를 호출하기 전에 입력 양식이 어떻게 넘어가는지 확인해주면 “%d %c %d"로 정수 1개, 문자 1개, 정수 1개를 순서대로 입력 받음을 알 수 있다.

phase_3+20~30행을 보면 rsp+0xf에 rcx의 주소를, rsp+0x10에 rdx의 주소를, rsp+0x14에 r8의 주소를 저장함을 알 수 있다.

argument의 순서는 rdx, rcx, r8 순서이 므로 rsp+0x10에 첫 번째 정수, rsp+0xf에 두 번째 문자, rsp+0x14에 마지막 정수에 해당하 는 주소가 저장됨을 알 수 있고, 또는 x/d $rsp+0x10, x/c $rsp+0xf, x/d $rsp+0x14로 확인 해봐도 알 수 있다. phase_3+47행은 이전 행에서 sscanf가 호출된 것으로 보아 eax에는 입력 변수의 개수임을 알 수 있고 이 input 변수가 2개 이하이면 explode_bomb가 실행되게 함을 추측할 수 있다.

phase_3+52행을 보면 cmpl문이 있다. 이것을 해석하면 첫 번째에 입력한 정수에서 0x7을 뺀 연산을 한다. 그 다음 줄인 phase_3+57행과 같이 보면 앞서 한 연산에 대해 ja를 확인하는데 ja는 unsigned 기준 더 클 때 성립한다. (~CF & ~ZF)

즉, 첫 번째 정수가 unsigned로 보았을 때 7보다 크면 phase_3+334행(explode_bomb)로 이동한다. 즉 첫 번째 정수는 7 이하의 정수여야 한다.

 

phase_3+63행에서는 eax에 0x10(%rsp) 즉 첫 번째 입력 정수를 저장한다. phase_3+67행에서는 rdx에 0x1604(%rip) 즉 0x5555555568e0을 저장한다. phase_3+74행에서는 rax에 (rdx + rax*4)에 해당하는 주소에 들어있는 값을 저장한다.

 

rax는 첫 번째 input인 7 이하의 정수이므로 조건에 맞도록 적당히 1을 넣고 진행해보자.

 

 

 

phase_3+78행에서는 위의 rax에 rdx의 값을 더한다. 그럼 0x5555555568e0 - 5586(decimal 인데 hex로 바꿔서 연산한다) = 0x55555555530e가 된다. phase_3+81행에서는 rax에 들어있는 주소로 jump한다.

이것은 위에도 계산했듯 0x55555555530e에 해당하는 phase_3+124행이 된다.

phase_3+124행에서는 eax에 0x67을 저장한다. phase_3+129행에서는 0x14(%rsp) 즉 2번째 정수와 0x18a (394 in decimal)을 비교하고 다음 행을 보면 이 둘이 같지 않을 시 explode_bomb를 호출하게 된다.

즉 첫 번째 정수가 1일 경우에는 두 번째 정수가 394가 되어야한다.

 

solution.txt를 수정하고 다시 실행해주면...

 

 

 

위와 같은 어셈블리코드를 확인할 수 있다. 0xf(%rsp) 즉 두 번째 input인 char에 해당하는 입력이 %al에 저장된 값과 같아야 explode_bomb가 호출되지 않고 성공적으로 함수를 종료시킬 수 있다.

 

i r %al을 통해 al 레지스터에 저장된 값을 확인해주면...

 

 

 

103이 들어있는데 이것은 아스키코드로 알파벳 'g'에 해당한다.

따라서 두 번째 input은 g가 되어야함을 알 수 있다. 따라서 “1 g 394"가 phase_3의 하나의 해답이 된다.

solution.txt 의 세 번째 행에 이를 입력하고 실행해주면 다음과 같이 phase_3도 성공적으로 통과한 것 을 확인할 수 있다.

 

 

 

 

Phase 4

 

disas phase_4를 통해 어셈블리코드를 확인해주면 위와 같다.

마찬가지로 phase_4에 breakpoint를 걸어주고 코드를 실행해본다.

 

phase_4+13행에서는 rsp+0x8에 rax의 값을 저장함을 알 수 있다.

phase_4+20행에서는 rcx에 rsp의 값을 저장함을 알 수 있다.

phase_4+23행에서는 rdx에 rsp+0x4의 주소를 저장한다.

phase_4+28행에서는 sscanf가 실행되기 이전 행이므로 입력 양식을 확인해주면 “%d %d"로 정수 2개를 입력받음을 알 수 있다.

phase_4+40행과 phase_4+43행을 확인해보면 입력이 2개가 아닐 경우 explode_bomb가 실행됨을 알 수 있다. phase_4+45행에서는 eax에 rsp의 값을 저장해주는데 가장 최근에 sscanf가 호출되었기 때 문에 eax에는 두 번째 정수가 저장된다. (이것은 breakpoint를 걸고 레지스터에 저장된 값을 확인해보아도 된다.)

이후 연산을 해주는데, eax에서 2를 뺀 값이 2보다 작거나 같은지 확인 하는 것이므로 두 번째 정수는 4보다 작거나 같아야 한다.

만약 그렇지 않는다면 explode_bomb가 호출된다.

 

따라서 solution.txt의 4번째 줄에 조건에 맞는 적당한 정수를 넣고 다시 run 해준다. (여기에서는 예를 들어 우선 3 4를 입력했다.)

만약 조건에 맞는다면 phase_4+61행에서 rsp의 값을 다시 esi에 저장한다.

또 edi에는 0x9 를 저장한 뒤 func4 함수를 호출한다.

 

그럼 일단 func4 함수를 확인해보자.

 

 

 

어셈블리코드와 레지스터의 정보를 통해 우선 위와 같은 2개의 input이 들어갔음을 확인할 수 있다.

 

 

 

우선 이 함수의 특징만 살펴보면 재귀함수라는 것을 알 수 있다.

왜냐하면 func4+30행이 나 func4+45행을 살펴보면 자기 자신을 호출하고 있기 때문이다.

재귀함수는 코드를 따라 갈 수는 있지만 호출이 많아지면 규칙을 발견하지 못하면 굉장히 복잡해지기 때문에 어떤 변수가 사용되는지 그리고 어떤 연산이 이루어지는지만 확인하고 c언어를 이용해 코드를 그대로 다시 구현하도록 하였다.

 

우선 함수의 인자는 di와 si가 있고, 반환값은 ax이다.

그리고 di가 0 또는 1일 때 예외 처리가 되어있고, 그 외에 (r)12, bp, bx가 중간에 사용됨을 확인할 수 있다.

그 외에는 간단한 덧셈 뺄셈, 그리고 함수 호출 외에는 특이한 연산이 사용되지 않 기 때문에 간단하게 구현할 수 있음을 확인할 수 있다.

 

 

 

따라서 c언어를 이용해 그대로 다시 구현한 코드는 위와 같고, 위의 함수 처음 호출 부분 의 레지스터에서 확인했듯 (두 번째 정수에 4를 입력했다는 가정 하에) di에 9 si에 4를 넣 어 그 결과값을 출력하도록 하면...

 

 

 

352가 나옴을 확인할 수 있다. 다시 phase_4의 어셈블리코드로 돌아가서 나머지를 확인해보면...

 

 

 

반환값 eax에 저장된 정수와 0x4(%rsp) 즉 첫 번째 입력값이 같지 않으면 explode_bomb가 호출된다.

따라서 첫 번째 정수는 352가 되어야 함을 확인할 수 있다.

따라서 (여러 답 중 하나인) “352 4”를 solution.txt의 4행에 입력해주면 다음과 같은 결과를 확인할 수 있다.

 

 

 

 

Phase 5

 

phase_5는 phase_5+4행에서 string_length가 호출됨을 보아 문자열의 길이를 검사함을 확 인할 수 있다.

그리고 phase_5+9행에서 그 리턴값 eax와 6이 같은지 검사하여 다를 경우 explode_bomb가 호출됨으로 보아 길이가 6인 string이 입력 양식임을 알 수 있다.

그렇다면 phase_5에 들어가는 input(rdi)은 문자열의 주소일 것이다.

이제 solution.txt의 5행에 적 당한 길이 6의 문자열, 예를 들어 "abcdef"를 작성하고 breakpoint를 걸어준 뒤 코드를 돌려보자.

 

phase_5+2행에서 입력 문자열의 주소가 rbx로 이동하고 phase_5+14행에서 다시 rax로 이동한다.

phase_5+17행에서 rbx+0x6은 (char의 크기가 1byte이므로) 입력 문자열이 끝난 바로 다음 주소이고 이 위치를 rdi에 저장한다.

phase_5+26행에서는 %rip+0x1432, 즉 주소 0x2900을 rsi에 저장해준다.

phase_5+33행에서의 (movzbl은 레지스터 크기 변환을 해주는 명령어이고) 중요한 점은 rax 에는 원래 입력 문자열의 주소가 들어있었으므로 edx에는 (rax)에 해당하는, 즉 문자열 맨 앞자리의 주소에 들어있는 값인 가장 첫 문자가 저장된다는 것이다.

phase_5+36행에서 edx에 저장된 문자와 0xf의 and 연산을 해준다. 예를 들어 a는 아스키코 드로 97이고 이것은 16진수로 나타내면 0x61이며 따라서 0xf와 and 연산을 수행하면 0x1이 된다.

phase_5+39행에서 ecx에 (ecx는 phase_5+21행에서 0으로 초기화 되었었다.) rsi + rdx*4에 해당하는 값을 저장해준다. rsi에는 0x2900이 들어가 있었으므로 그보다 약간 뒤의 메모리 주소가 될 것이다. phase_5+42행에서 rax에 1을 더해준다. 즉, 문자열의 다음 문자 주소로 이동한다.

phase_5+46행과 phase_5+49행은 rax를 1씩 증가시켜 모든 문자열에 대해 같은 수행이 끝날 때까지 위의 과정을 반복함을 알 수 있다.

phase_5+51행을 보면 이렇게 6번 반복한 뒤의 ecx 값들의 합이 0x21이어야 통과가 됨을 알 수 있다.

 

그렇다면 우리는 rsi와 그 뒤쪽으로 저장되어 있는 정수들이 어떤 값을 가지고 있는지 확인해보아야 한다. 어차피 0xf와의 and 연산을 해보았자 최대 15까지밖에 안나오기 때문에 적당히 확인해주면...

 

 

 

위와 같은 값들이 나온다. 6개 수의 합으로 0x21(=33)을 만들어야 하는데 우리는 아직 어떤 알파벳이 어떤 rsi+4*rdx 주소에 있는 정수를 가리키는지 잘 모른다.

그러니 적당히 알파벳 몇 개 정도만 가지고 반복문에 있는 비트 연산을 해보자.

 

예를 들어 알파벳 ‘b'는 아스키코드 98에 해당하고 hex로 0x62이며 0xf와 and 연산을 해주 면 2이다.

위 스크린샷을 참고하면 rsi+4*2에 들어있는 수는 6이다.

알파벳 ’c'는 아스키코드 99에 해당하고 hex로 0x63이며 0xf와 and 연산을 해주면 3이다.

따라서 rsi+4*3에 들어있는 수는 1이다.

 

그러면 우리는 a랑 c만 가지고 적당히 10+10+10+1+1+1=33을 만들 수 있기 때문에 "aaaccc"가 하나의 정답임을 알 수 있다.

따라서 solution.txt의 5행에 "aaaccc"를 입력하고 확인하면 다음과 같은 결과를 얻을 수 있다.

 

 

 

 

Phase 6

phase_6은 어셈블리코드가 너무 길다.

그래서 설명만 하자면 phase_6+34행에서 read_six_numbers를 호출함을 확인할 수 있다.

따라서 입력은 정수 6개가 될 것이다.

따라서 대충 solution.txt의 6행에 정수 6개를 입력하고 실행해본다.

여기서는 “4 5 6 7 8 9”를 입력 해보았다.

그 다음 phase_6의 어셈블리코드에서 순서를 쭉 따라서 확인해보자.

phase_6+34행에서 read_six_numbers를 호출하여 6개의 정수를 읽는다.

phase_6+87행으로 jump가 발생한 뒤 phase_6+87행과 phase_6+90행에서 rbp와 eax에도 첫 번째 정수를 저장해준다.

phase_6+94행부터 phase_6+100행에서 eax에서 1을 빼서 5보다 크면 explode_bomb가 호 출된다.

계산해보면 4-1<=5이므로 실행되지 않고 그 다음으로 넘어간다.

phase_6+102행부터 보면 r14d를 0에서 시작(phase_6+42행에서 0을 넣어줌)해 1씩 더하고 6이 되면 phase_6+117행으로 jump한다.

아니라면 r14d의 값을 ebx에 저장하고, phase_6+65행으로 jump한다.

처음 수행될 때는 ebx에 1이 들어가고, phase_6+65행부터 eax에 1이 옮겨진다.

그 다음 rsp+4*eax 주소에 있는 값이 eax에 들어간다.

 

당연하겠지만 rsp에는 solution.txt 6행에 입력 숫자들이 차례로 저장되어 있을 것이다.

다음과 같이 확인 해보아도 된다.

 

 

 

그러면 첫 번째로 반복문을 돌 때 rax는 1이므로 rsp+1*4에는 두 번째 정수인 5가 들어있을 것이다.

phase_6+71행에서 이 eax의 값을 rbp의 값과 비교한다.

eax에는 5가 들어있고 지금 rbp에 는 4가 들어있으니 phase_6+74행에 의해 phase_6+57행으로 jump가 발생한다.

그 다음부터는 다시 반복문의 내용이 반복된다. ebx에 1을 더해 rax로 옮기고 rsp+rax*4 주소에 들어있는 값을 비교하므로 그 다음 정수인 6과 비교하게 될 것이다.

지금 5, 6, 7, 8, 9 는 모두 4와 다르므로 일단 반복문이 정상적으로 탈출이 될 것이다.

 

그 다음에 phase_6+83행으로 진입한다.

코드를 보면, 지금 이중 반복문이 돌고, 방금 안쪽 반복문의 첫 번째 반복이 끝났음을 알 수 있다.

그 다음 r13에 4를 더한 뒤 그 주소의 값을 eax에 옮기는데, rsp 주소에 들어있던 값은 첫 번째 정수인 4이므로 그 뒤 주소의 값인 5가 들어갈 것이다.

 

그 뒤로 똑같이 반복문이 도는데, 요약해보면 이것은 이중 반복문이고 모든 정수의 값이 6보다 작거나 같아야하며 각각이 모두 다른 값을 가져야한다.

따라서 위의 조건에 맞도록 적당히 6 5 4 3 2 1을 solution.txt의 6행에 입력한 뒤 프로그램 을 다시 돌려본다.

 

 

 

이중 반복문을 성공적으로 탈출했음을 알 수 있다.

 

phase_6+117행에서는 r12+0x18의 주소를 rcx에 저장하는데, r12로부터 6칸 뒤의 값이므로 비어있는 주소일 것이다.

확인해보니 0이 들어있었다.

phase_6+122행부터는 edx에 7을 저장하고, 그것을 eax에도 저장하고, 7에서 r12 주소의 값, 즉 첫 번째 정수를 뺀다.

그러면 여기서는 7-6=1이 될 것이다.

그 다음 그 수를 r12 주소에 다시 저장하므로 6이 1로 치환된다.

그 다음 phase_6+137행에서 다음 칸(두 번째 정수가 들어있는 주소)으로 이동하고, 이것이 6을 뺀 값이 0과 다르면 다시 127행으로 돌아가 다시 수행한다.

이런 식으로 반복하다가 6이 들어있는 주소에서 6을 빼서 0이 되면 반복이 끝난 다.

그 다음 phase_6+146행에서 esi에 0을 저장하고 phase_6+179행으로 jump한다.

이 부분 뒤쪽에 breakpoint를 건 뒤 solution.txt의 6행에 3 5 2 6 4 1을 쓴 뒤 실행해보면 다음과 같다.

 

 

 

각각이 7에서 뺀 값으로 바뀐 것을 알 수 있다.

 

 

 

phase_6+179행에서 ecx에 rsp+rsi*4 주소에 들어있는 값을 저장한다.

(확인해보아도) rsi는 0 이므로 ecx에는 우선 첫 번째 칸에 있는 정수가 들어간다.

그 다음 eax에 1을 저장하고 rdx 에 특정 주소가 저장된다. 확인해보면 0x555555758210에는 826이 저장되어 있다.

그 다음 ecx가 1보다 큰지 검사하여 1보다 크면 153행으로 돌아가서 rdx에 rdx+8 주소에 있는 값을 저장하고, eax에 1을 더한 뒤 (아까 5가 될 때까지 반복문을 돌렸으므로 6이 된다.) ecx와 비교하여 같아질 때까지 153행으로 돌아간다.

이런 식으로 계속 생각하다보면 리스트에서 정렬이 이루어진다는 것을 알 수 있다.

그 다음 직접 주소에 들어있는 값들을 확인해보니...

 

 

 

이처럼 연결 리스트와 같이 +8에 있는 주소가 다음 노드의 값과 이어져 있는 것을 확인할 수 있었다.

따라서 rdx+8의 연산을 해준다는 것은 그 다음 노드로 이동한다는 것과 같다.

노드6은 그 뒤에 참조할 주소가 없기 때문에 아마도 마지막에 배치될 것이다.

 

 

 

 

phase_6의 마지막 부분을 봐주면 주소에 있는 값들끼리 대소비교를 해주고 283행에서 조건 에 맞지 않을 시 phase_6+285행에서 explode_bomb가 실행됨을 알 수 있다. 따라서 위에 위에 있는 스크린샷에서 노드들이 가진 값들을 내림차순으로 배열을 해주면 노드 번호대로 3 2 4 1 5 6이다. 그런데 아까 그 숫자들이 7에서 뺀 값으로 바뀌었기 때문에 이것을 역으로 다시 계산해주어야 한다. 그러면 답은 4 5 3 6 2 1임을 알 수 있다. 이것을 solution.txt 의 6행에 입력하고 확인해주면 다음과 같이 성공적으로 bomb를 해체하였음을 확인할 수 있다.

 

 

 

 

Secret Phase

Secret Phase가 있다고 해서 disas 파일을 뒤져보았는데, 다음과 같이 phase_defused에 secret_phase를 호출하는 부분이 있다.

 

 

 

위쪽 코드를 다시 보면 num_input_strings가 6개가 아니면 그냥 진행하는데 6개이면 50행으 로 jump한다.

50행부터 보면 무엇인가 다시 읽는다.

다음과 같이 브레이크포인트를 걸고 적당히 메모리들을 확인해주면...

 

 

 

352 4가 메모리에 저장되어있다.

그래서 어셈블리코드를 이것을 참고하여 다시 보면 352 4 뒤에 어떤 문자열을 써주면 조건문을 만족함을 추측할 수 있다.

그래서 브레이크포인트를 다시 걸고 그 문자열을 찾으면 (비교하는 부분에서) ...

 

 

DrEvil이고 이것을 4행의 뒤에 써준 뒤 다시 돌린다.

 

 

 

분석해보면 rsi에 입력한 정수가 들어간 다음, fun7이 호출된다. 그리고 이 값이 2로 리턴되어야 한다.

그래서 fun7 함수를 봐주면 rdi의 값은 36, rsi에는 우리가 입력한 정수이다.

이 함수를 분석해보면 포인터로 이진 트리 내에서 위치를 찾는 함수이다.

원하는 숫자보다 작으면 반환값의 2배를, 더 크면 반환값의 2배에 1을 더한 것을 반환한다.

그리고 이 트리 노드는 왼쪽 아래로 내려가는 포인터, 오른쪽 아래로 내려가는 포인터를 가지고 있다.

값을 직접 맨위 n1의 주소를 따라가 확인해보면...

 

 

 

아래처럼 트리 구조를 가짐을 알 수 있다.

밑을 더 확인해볼 수도 있지만 fun7의 반환값이 2이면 되기 때문에 0에서 왼쪽, 오른쪽, 왼쪽으로 내려가면 0 * 2에 1을 더하고 다시 2를 곱해 2가 된다.

그러므로 22가 답이 되고 solution.txt의 7번째 줄에 22를 추가해주면 답을 얻을 수 있다.

 

 

 

이로써 모든 Phase를 해결하였다.

 

 

 

반응형