Phase3는 문제 유형이 2개인데, 이 포스트에서는 입력받는 값이 3개(%d %s %d)인 경우를 다룬다.
그러면 한 번 phase_3의 어셈블리 코드를 해석해보자.



Phase 3는 어셈블리 코드를 분석하여 C언어의 switch 구문과 유사한 구조를 찾아내는 것이 핵심이다.
함수 초반부에서 scanf 함수를 호출하여 사용자 입력을 받는다.
<+35>: `mov 0x184f(%rip),%rsi명령어를 통해 포맷 문자열의 주소를%rsi 레지스터에 로드한다. GDB로 해당 주소(0x555555402be6)의 문자열을 확인하면 **%d %c %d`** 임을 알 수 있다.
%rsi 는 함수에 들어가는 인자이므로 이 값이 <+42> 의 scanf 함수(<__isoc99_scanf@plt>)로 들어감을 확인할 수 있다. 입력된 세 값(정수, 문자, 정수)은 스택 포인터(%rsp)를 기준으로 한 특정 위치에 저장된다. 이 값들은 <+20>에서 <+30> 사이의 명령어들을 통해 파악할 수 있다. %rdx: 첫 번째 정수(%d)가 저장될 주소 (%rsp + 16)
%rcx: 문자(%c)가 저장될 주소 (%rsp + 15)
%r8: 두 번째 정수(%d)가 저장될 주소 (%rsp + 20)
각 데이터 타입의 크기(정수 4바이트, 문자 1바이트)를 고려하면, 이 명령어들은 스택에 세 개의 변수를 위한 공간을 마련하고 각 변수의 시작 주소를 scanf의 인자로 넘겨주는 것과 동일하다.
예를 들어 사용자가 2 n 4를 입력하면, scanf 함수가 실행된 후 스택에는 다음과 같이 값이 저장된다.

<+47>, <+50>: scanf의 반환 값(%rax)이 2보다 큰지 비교한다 (cmp $0x2,%eax, jle ). scanf는 성공적으로 입력받은 인자의 개수를 반환하므로, 입력값은 반드시 3개여야 한다.코드의 다음 부분은 첫 번째로 입력된 정수에 대한 조건을 검사한다.


<+52>, <+57>: cmp $0x7,0x10(%rsp) 명령어는 스택에 저장된 첫 번째 정수가 7보다 큰지 비교하고 크면은 폭탄이 터진다(ja <phase_3+331> ,ja는 unsigned 비교).따라서 첫 번째 정수는 0에서 7 사이의 값이어야 한다.
현재까지의 답: (0-7) (char) (정수)

<+63>: %eax에 %rsp+10, 첫 번째 정수 x의 주소값을 넘겨준다.<+67>: lea 0x1849(%rip),%rdx 명령어로 특정 주소를 %rdx에 로드한다. 이 주소는 <+74> ~<+81>을 보면은 분기할 주소들의 목록, 즉 점프 테이블의 시작 주소임을 알 수 있다. 해당 주소에 16진수, 워드 단위로의 값출력을 하면은 다음과 같은 값이 나온다. 

<+74>: movslq (%rdx,%rax,4),%rax 명령어가 실행된다. 여기서 봐야할 점은 다음과 같다.%rax에는 <+63>을 통해 첫 번째 입력 정수 값이 들어있다.
(%rdx, %rax, 4)는 rdx + rax * 4 주소를 의미한다. 즉, 시작 주소(<+67>)에서 첫 번째 정수 값만큼의 인덱스를 이용해 이동한다. 이로 $0x555555402c00 은 배열로 유추할 수 있다.
결과적으로, 이 명령어는 첫 번째 정수 값에 해당하는 분기 주소를 테이블에서 읽어와 %rax에 저장한다.
만약 첫 번째 정수 x가 2 이면은 %rax에는 *(0x555555402c00 + 4 x 2)의 값이 sign extension되어 0xffffffffe80b 이 저장된다.

<+78>: add %rdx, %rax 명령어는 이 오프셋 값에 점프 테이블의 시작 주소(%rdx)를 더하여 실제 실행할 코드의 주소를 계산한다.x가 2일 경우에는 0x555555402c00 + 0xffffffffe80b = 0x55555540140b 이 된다. 이 값이 %rax에 저장된다.<+81>: jmpq *%rax 명령어는 %rax에 저장된 주소로 프로그램 카운터를 이동시킨다.이러한 구조는 C언어의 switch문이 컴파일된 전형적인 형태이다. 첫 번째 정수 x의 값(0~7)에 따라 8개의 다른 코드 블록으로 분기하게 된다.
첫 번째 정수 x가 2일 경우일 때의 분기를 보도록 하자. 0x55555540140b은 <Phase_3 +158>에 해당한다.

<+158>:%eax에 0x69의 값을 넣는다.<+163>: *(rsp+20)(두 번째 정수 y)과 0x2f2가 동일한지 판단한다. 동일하면은 <+341>로 분기하고, 그렇지 않으면은 폭탄이 터진다.따라서 x가 2 일 때, 두 번째로 입력하는 정수 y는 0x2f2(754)이다.

이 명령어에선 %al(rax 레지스터에서 하위 8비트 부분)과 스택에 저장된 문자(*(%rsp+15))를 비교한다. 이전 단계에서 switch문의 각 case 블록은 %eax에 고유한 값을 저장했다 (<+158>).
따라서 입력된 문자는 해당 case에서 지정한 값의 ASCII 코드와 일치해야 한다. 예를 들어, x=2의 경우 문자는 반드시 ASCII 코드 0x69, 즉 'i'여야 한다.
앞선 분석 과정을 종합하면, Phase 3의 핵심 로직은 C언어의 switch문과 동일한 구조임을 알 수 있다.
switch(x)문의 x처럼 분기할 case를 선택하는 역할을 한다. 이 값은 0에서 7 사이로 제한된다.0x555555402c00 주소는 점프 테이블, 즉 각 case로 점프하기 위한 주소 오프셋을 담은 배열을 가리킨다.case의 로직으로 분기한다.이러한 메커니즘을 통해, 첫 번째 정수 x의 값에 따라 분기되는 8개의 주소는 다음과 같이 정리할 수 있다.
<+90><+124><+158><+192><+223><+250><+277><+304>그리고 이 분기에는 두 번째 정수 입력과 Char 입력 조건이 작성되어 있다. 정리해보면 답은 다음과 같다.
답:
0 t 1351 j 923
2 i 754
3 d 178
4 n 307
5 k 432
6 f 794
7 b 343
