본문 바로가기

System Programming/CSAPP Lab

[Bomb Lab] Phase 3, Assembly indirect jump

Assembly 코드 변환

phase_3을 assembly 코드로 변환해보면,

 

첫 번째 분기: 입력의 길이 확인

우선 첫 번째 분기가 일어나는 곳인 +32 줄에 breakpoint를 찍어보았다.

phase 1, 2는 알아낸 정답을 입력하고, 3은 임의로 4개의 숫자 1 2 3 4를 입력해보았다. 이 때 레지스터 eax 값이 2이고, 그러면 1보다 크기 때문에 34줄은 무사히 건너뛰게 된다. 여기서 스크린샷에 첨부하지는 않았으나 10 20 30의 3가지 숫자를 입력했을 때도 eax의 값이 2였는데, 아마도 최대 2개의 입력을 읽어오는 것 같다.

스택 포인터가 가리키는 지점의 메모리 값을 확인해보면, rsp+8, 12에 각각 1, 2번째 입력 값이 들어 있었다.

 

두 번째 분기: 1번째 입력 값의 크기 확인

이제 +39줄을 보면, 스택 포인터+8이 가리키는 곳, 즉 위에서 확인한 바에 따르면 사용자의 1번째 입력과 7을 비교하여 만약 7보다 크면 폭탄이 터진다. 실제로 10 20 ...을 입력했을 때는 터지고, 1 3을 입력했을 때는 다음까지 넘어갔다. (원래 의도는 폭탄을 터뜨리면 안 되겠지만, 스스로 공부하는 의도가 크기 때문에 가끔씩은 폭탄이 터지는 것을 확인하고 넘어가기도 하였다.)

 

세 번째 분기: indirect, unconditional jump

이제 다음으로 분기가 일어나는 +50줄에 breakpoint를 찍어본다. 이 명령어는 jmp이므로 unconditional jump이고, *이 붙어있는 indirect jump이므로 jump target을 메모리에서 불러온다. 그래서 여기서 주소 0x402470의 메모리 값을 확인해보면 그 다음 명령어의 PC 값이다. 이 주소를 base address로 하여 offset을 rax*8 만큼 더해서 실제로 jump하게 되는데, 여기서 rax는 사용자 입력의 1번째 값이다. (46줄에 의해) 7보다 작거나 같은 입력을 한 뒤, 이 주소의 값을 확인해보면 57~104줄 중 어느 하나의 PC 값을 담고 있어서 여기로 jump하게 되는 것이다.

 

nexti 또는 stepi

 

명령어는 assmebly code 레벨에서 명령어 하나씩 실행한다는 뜻이다. 위 사진은 입력을 4 3으로 하였을 때의 상황이다. 특정 offset만큼 jump를 하게 되는데, 57줄부터 104줄까지를 보면 eax에 뭔가 값을 담은 후에는 공통적으로 +123줄로 jump를 한다.

 

2번째 입력 값과의 비교, 그러나 비교 대상은 1번째 입력에 따라 달라진다

123줄에서는 레지스터 eax 값과 사용자 입력 2번째 값과 비교하여 같을 때만 함수가 무사히 종료된다. 이 명령어를 확인하면, 위에서 eax 레지스터에 뭘 담아두었는지를 이해할 수 있다. 2번째 입력 값과 비교할 정답을 저장하는데, 정답이 1번째 입력에 따라 달라지는 것이다.

 

통과하기 위해서는 여러 가지 정답 조합이 있겠지만, 1번째 입력은 0~7 사이의 값 중 하나로 하고, 2번째 입력은 일단 임의로 준다. 그리고 +50줄에 breakpoint를 걸어두고, 여기서부터 한 step씩 실행시키며 어디로 분기가 일어나는지를 확인한다. 그 때 레지스터 eax에 저장되는 값을 해석하면 이 값이 곧 정답이 된다.

분기 흐름은 step을 하나씩 따라가며 직접 PC 값을 통해 확인할 수도 있고, indirect jump 명령어 해석을 해보면 base address + 8*(1번째 입력) 주소에 저장된 값으로 jump하기 때문에, 위 사진의 마지막 줄처럼 메모리 값을 참조하여도 jump할 주소를 알 수 있다. 실제로 위에서도 0x400fa6이라는 같은 값을 얻었다. 이 때는 eax에 0x147(=327)을 저장한다. 

 

정답 확인

위에서 확인한 것처럼, 1번째 입력에 따라 정답 저장 코드로의 분기 흐름이 달라지기 때문에, 정답의 조합은 8가지가 나온다. 예를 들어, 또 다른 정답 중 하나는 1 311이다.

 

phase_3의 주요 point를 정리해보자면, unconditional & indirect jump 명령어를 해석하는 것이 중요하게 작용했다.