2 minute read

결과 화면

Image





진행 과정

Image <+0> ~ <+35> sscanf로 입력 받기 전까지 열심히 초기화 작업을 거친다. 이때 입력값의 포맷도 확인할 수 있다. 또 두 개의 정수이다.

Image

<+40> ~ <+43> 입력값이 1개면(제대로 입력되지 않음) 폭탄으로 간다.

<+45> ~ <+51> 스택의 첫 번째 값을 가져와 %eax에 넣고 0xF와 AND연산을 수행한다. 0xF=1111이므로, 이는 16진수에서 하위 4비트만을 남길 수 있다(0~15 사이로 강제). 그리고 그 값을 다시 스택의 %rsp가 가리키는 곳에 넣어준다.

<+54> ~ <+57> %eax 값에 15가 들어있는 경우 폭탄으로 가므로, 첫 번째 값은 하위 16비트가 1111이면 안된다.

<+59> ~ <+69> %ecx와 %edx를 0으로 초기화하고 %rsi에 무슨 주소를 저장하는 것 같아서 유심히 봤다. # 0x555555402b80 이렇게 되어있길래 시험삼아 값을 출력해봤다.

Image

뭔가 의미있는 수인 것 같다…! 이후로 이 array를 어떻게 이용하게 될지 궁금하여 코드를 슥 훑어보니 점프 주소로 미루어 짐작건대 루프가 있는 것 같다는 생각이 들었다. 이정도 추측을 하고 다음 코드로 넘어가자.

<+76> ~ <+79> %edx값에 1을 더하고, cltq? 처음봐서 찾아보니 Convert Long to Quadword, 32bit register %eax의 값을 64bit register rax로 확장해주는 instruction이라고 한다. sign extension이 일어나 현재 최상위 비트를 반영하여 64비트로 확장함을 주의하자.

<+81> ~ <+89> 이제 본격적으로 배열을 사용할 것 같다. ‘%rsi + %rax * 4’로 주소를 계산하여 그 값을 %eax에 가져와 저장하고, 그 값을 %ecx에 더하고, %eax값을 0xf(15)와 비교하여 같지 않다면 다시 <+76>으로 분기(루프 발견), 같다면 다음 Instruction을 진행한다. 이제 %edx 값이 루프 카운터 역할을 하게 된다는 점을 인지할 수 있다.

<+91> ~ <+101> 루프를 돌다보면 언젠가는 탈출하여 도달할 것이다. 15를 현재 스택포인터가 가리키는 위치에 넣고 15와 %edx값을 비교하여 같다면 (15번 루프를 돌았다면) 다음 줄의 폭탄으로 가는 길을 피할 수 있다. 즉, 루프를 15번 돌았을 때 %eax값이 0xf가 되도록 머리를 잘 써야 한다.

<+103> 스택 포인터에서 4byte만큼 떨어진, 두 번째로 입력받은 값과 %ecx의 값을 비교해서 같다면 그게 정답이고, 함수가 종료된다.

0x555555402b80 주소에 저장된 배열을 다시 보자. 총 4*4 matrix로 생각한다면, 0부터 indexing하여 (row=1,col=2) 위치에서 0x0000000f를 발견할 수 있다. 반복 루프에서 ‘%rsi + %rax * 4’로 주소를 계산하여 가져와 저장한 %eax값이 중요하고, 언제 15(0x0000000f)가 되는지 아는게 문제 풀이의 핵심이다.

편의상 0x0000000f를 f라고 표시하면, ‘f <- 6 <- e <- 2 <- 1 <- a <- 0 <- 8 <- 4 <- 9 <- d <- b <- 7 <- 3 <- c <- 5 <- f <- …’ 형식으로 반복됨이 보인다. f가 array주소로부터 0x64번째에 있고, 0x6은 0xe4에 있고.. 역추적 하는 느낌으로 ordering해봤다. cltq가 왜 나왔는지 이해가 간다(반복 곱셈 시 bound 관련 에러 방지).

이제 위의 순서 관계를 통해 15번 루프를 돌았을 때 %eax값이 15가 되도록 값을 조정해주면, 이는 5가 된다(첫 번째 값!). 그리고 만나는 모든 값을 더한 값이 %ecx이므로 ‘f <- 6 <- e <- 2 <- 1 <- a <- 0 <- 8 <- 4 <- 9 <- d <- b <- 7 <- 3 <- c’ 해당 경로의 모든 값을 16진수로 더하면 0x73(115)가 나오고, 두 번째 입력값이 된다.





정답

5 115



Leave a comment