오늘은 면접을 보았다. 아쉬운 부분이 있었지만 그래도 좋은 결과를 기대해봐도 좋을 것 같다. (아님 말고) 아무튼, 오늘부터는 그동안 못풀었던 문제를 풀어보고자 한다. 11일에는 +Ma’s Reversing 사이트의 문제를 풀었는데, 해당 사이트의 문제들이 내가 생각했던 것들과는 거리가 멀어서 잠시 제쳐두고, Reversing.Kr의 문제를 하나씩 풀어보고자 한다.
1. Reversing.Kr - Easy Crack (Reversing, 100pts)
우선 파일을 실행해보면 아래 [그림 1]과 같은 화면을 볼 수 있다.
보다시피 텍스트를 입력할 수 있는 창이 있는데, 대충 아무 값이나 넣고 확인을 누르면 아래 [그림 2]와 같이 ‘Incorrect Password’라는 메시지가 나온다.
이는 프로그램이 요구하는 입력값(password)이 별도로 존재할 것이고, 해당 값을 입력하면 플래그가 출력되는 형태일 것이다. 실행 흐름을 얼추 파악했으니 이제 기드라로 넘어갈 차례다. Defined Strings 중 ‘Incorrect password’를 찾고 해당 문자열이 참조된 함수를 찾는다. 해당 함수가 높은 확률로 메인 함수 또는 (적어도) 올바른 패스워드를 검증하는 함수일 가능성이 높기 때문이다.
이를 통해 FUN_00401080 함수에서 해당 문자열을 참조하는 것을 확인할 수 있었으며, 동일한 함수에서 ‘Congratulation !!’ 이라는 문자열도 참조함을 알 수 있었다. 이 함수를 조금 더 확인해보니, 확실히 입력값을 검증하는 함수임을 알 수 있었다. 이 함수의 주요 루틴(입력값 검증 및 메시지 출력)은 아래 [그림 3]과 같다.
우선 GetDlgItemTextA() 함수를 이용하여 local_64에 임의의 1바이트 데이터를 저장하는데, 입력값 중 일부일 가능성이 높다. 그 이후에는 local_63의 값이 ‘a’인지 확인 후, (local_63 + 1)과 (local_63 + 2)를 DAT_00406078, DAT_00406079와 strncmp() 함수를 통해 비교한다. 참고로 DAT_00406078과 DAT_00406079의 값은 ‘5’, ‘y’이다.
이후 (local_63 + 3)부터 ‘R3versing’ 문자열과의 일치 여부를 반복문으로 검증한다. 즉 local_63부터 시작되는 올바른 입력값은 ‘a5yR3versing’이다. 입력값이 해당 값과 모두 일치하면, 가장 마지막에 GetDlgItemTextA() 함수를 통해 어떤 문자가 저장된 local_64의 값이 ‘E’인지 확인하고 맞으면 ‘Congratulation !!’ 메시지를 출력한다.
대충 이 함수에서 비교했던 모든 문자들을 합치면 ‘Ea5yR3versing’이 된다. 해당 값을 프로그램에 입력해보면 분석을 맞게 한 것인지 알 수 있다.
위 [그림 4]를 통해 볼 수 있듯 정확한 패스워드를 맞추었음을 알 수 있다. 해당 입력값을 reversing.kr에 가서 입력하면 100점을 얻을 수 있다.
2. Reversing.Kr - Easy Keygen (Reversing, 100pts)
이번 문제는 압축파일로 받을 수 있는데, 압축을 풀면 Easy Keygen.exe와 RaedMe.txt를 얻을 수 있다. 이 중 ReadMe.txt를 열면 아래 [그림 1]과 같은 화면을 볼 수 있다.
ReadMe.txt에 따르면 시리얼이 ‘5B134977135E7D13’일 때의 이름을 찾아야 한다고 한다. 이게 정확히 무슨 뜻인지는 실행을 해보면 알 수 있다.
위 [그림 2]를 보면 알 수 있듯 이 프로그램은 Name과 Serial을 입력받는다. 이 때, 올바른 name과 serial이 입력되지 않으면 ‘Wrong’ 메시지를 출력한다. 다만 입력해야 하는 serial은 정해져 있으므로, 올바른 name을 찾아야 한다.
대충 볼 거 다 봤으니 기드라로 넘어가보자.
이번에도 역시 Defined Strings로 메인 함수를 찾았다. [그림 2]에서 출력된 ‘Wrong’ 메시지가 참조된 함수를 찾았는데, 그 함수는 FUN_00401000이었다. 해당 함수의 주요 구문은 아래 [그림 3]과 같다.
[그림 3]에 있는 부분을 살펴보기 전, 나는 위의 easy crack을 풀 때와 마찬가지로 디컴파일링된 코드를 먼저 분석하였다. 그러나.. 이번 문제에서 기드라 디컴파일러가 분석을 제대로 하지 못해 해당 코드로는 시리얼 생성 루틴을 제대로 볼 수 없었다. 결과적으로, 이 문제는 디버깅을 하면서 봐야하는 문제였다. 그 이유와 자세한 시리얼 생성 루틴은 이제부터 설명하겠다.
우선 디버거로 반복문의 시작점인 0x401077에 BP를 걸고 한 줄씩 실행해보면 name-serial 변환 루틴을 자세히 볼 수 있다. 다만 거창하게 볼 것 없이 그냥 name에서 한 글자씩 따오고, 이를 어떤 값과 XOR 연산하는 것이 전부다. 이 때, 입력값과 XOR하는 값은 0x10 -> 0x20 -> 0x30 -> 0x10 순으로 순환된다. 디버깅으로 분석을 해야 하는 이유는 바로 이 값들을 기드라로는 알기 힘들기 때문이다. 해당 값을 참조할 때의 주소값이 [ESP+ESI + @]로 표현되는데, 정적 분석을 하는 디컴파일러로는 당시의 ESP와 ESI를 정확히 알기는 어렵다. 따라서 이 문제는 디버거로 천천히 실행 흐름을 보면서 풀어나가야 쉽게 풀리는 문제였던 것이다. 요즘 디버거 멸시(?)가 좀 심했던 내게 제대로 카운터를 먹였다.
아무튼, XOR 연산은 역연산하기 매우 쉬운 연산이기 때문에 맨 처음 주어졌던 시리얼 ‘5B134977135E7D13’을 역연산하여 name을 구할 수 있다. 상당히 단순한 방식이라 손으로 연산해도 되지만, 굳이 그러기보다는 python으로 한 번 짜보았다.
serial = [0x5B, 0x13, 0x49, 0x77, 0x13, 0x5E, 0x7D, 0x13]
xor_arr = [0x10, 0x20, 0x30]
i = 0
name = ''
for s in serial:
name += chr(s ^ xor_arr[i])
i = (i + 1) % 3
print("[*] The input name : %s" % name)
이처럼 간단한 코드를 실행하면, 아래 [그림 4]와 같이 serial ‘5B134977135E7D13’과 매치되는 name을 구할 수 있으며, [그림 5]와 같이 Easy Keygen.exe에 해당 값을 입력하면 ‘Correct!’ 메시지가 출력됨을 확인할 수 있다.
해당 name이 곧 플래그이므로, 그것을 reversing.kr에 가서 입력하면 100점을 얻을 수 있다.