Reversing.kr(립케알)에 있는 간단한 크랙미를 풀어보기로 했다.
6000명이 푼 간단한 문제로 아마 Abex 크랙미 정도의 난이도에 해당하지 않을까 싶다. 우선 프로그램을 실행시켜보면 다음과 같이 시리얼을 입력하는 창이 나타난다.
확인 버튼은 항상 활성화되어있으며 시리얼 키를 입력하는 필드에는 별다른 이벤트가 존재하지 않는 것 같다. 아무런 값을 입력하지 않아도 혹은 틀린 시리얼을 입력해도 동일하게 다음과 같은 메시지 박스를 호출하고 있다.
그렇다면 해당 문자열을 기반으로 먼저 찾아보자. 일단 디버거로 파일을 열어보면 다음과 같이 0x00401188에서 진입점을 확인할 수 있다.
초반 부분부터 여러가지 함수를 호출하고 있는데 F8 등 Step Over로 하나하나 실행시키다 보면 머지않은 곳에서 다이얼로그를 실행시키는 함수 호출 명령어를 찾아낼 수 있다.
더 진행하기 전에 일단 아까 메시지 박스에서 출력된 실패 문자열 "Incorrect Password"를 문자열 탐색 기능을 이용해 찾아보기로 했다.
어렵지 않게 찾을 수 있었으며 해당 위치로 이동해보니 특정 값의 비교 이후 성공, 실패 메시지 박스를 호출하는 로직을 찾을 수 있었다.
현재 실패 메시지 박스를 출력하는 명령어는 0x00401135에서 시작되며 이는 0x0040110B, 0x00401112에서 jne를 통해 조건에 따라 점프하는 방식으로 호출되고 있다. 하지만 실제로 중단점을 걸고 실패 메시지 박스를 호출하도록 해보면 저곳에는 중단점이 걸리지 않는다. 하지만 실제로 메시지 박스를 호출하는 0x00401142에 중단점을 걸어보면 실패 메시지 박스가 이 곳에서 호출되는 것을 확인할 수 있다. 그렇다면 다른 곳에서 이 0x00401135로 점프하는 로직이 있을 것이며 즉 실패 메시지 박스를 출력하는 조건이 여러가지가 있음을 추측해 볼 수 있다. 그래서 코드를 조금 더 위로 올려서 찾아본 결과 다음과 같이 0x00401135로 점프하는 여러 명령어를 찾을 수 있었다.
이 중 텍스트 필드에 아무런 값을 입력하지 않고 버튼을 누르든 어떤 문자열을 입력하고 누르든 0x004010B5의 jne 구문에서 걸리는 것을 확인할 수 있었다. 그 위에는 GetDlgItemTextA() 함수가 호출되었는데 이는 Win32 API 함수로써 다이얼로그의 박스 내 컨트롤(버튼, 텍스트 필드 등)의 제목이나 내용 텍스트를 얻어오는 함수다. 이 프로그램에서는 시리얼 키를 입력하는 텍스트 필드 말고는 텍스트를 입력할 만한 컨트롤이 없으므로 즉 사용자가 입력한 시리얼 키를 가져오는 용도로 사용되고 있다고 추측할 수 있다.
실제로 함수 호출 시 스택에 삽입된 매개변수(eax: 0x0019F7F0)를 확인해보니 사용자가 입력한 시리얼 키인 "asdf"가 저장되었고 함수 호출의 반환값으로 4가 EAX 레지스터에 저장되었는데 이는 문자열의 길이다. 그렇다면 바로 아래쪽의 cmp 명령어에서 이를 어떻게 활용하고 있을까?
cmp에서는 ESP+5에 위치한 값을 0x61과 비교하고 있다. 이는 아스키로 97이며 'a'에 해당한다. 0x0019F7F0부터 사용자가 입력한 문자열이 저장되어 있으며 ESP+5는 이 0x0019F7F1 ~ 0x0019F7F2에 저장된 값을 의미한다. 즉 사용자가 입력한 시리얼 키의 두 번째 글자(현재는 0x73, 즉 's')가 'a'와 비교되는 것이다. 현재는 'asdf'를 입력했기 때문에 당연히 두 번째 글자가 'a'가 아니라서 jne 조건을 만족, 0x00401135로 점프하게 된다. 위에서 확인했듯이 이는 실패 메시지 박스를 호출하게 된다. 그렇다면 두 번째 글자를 'a'로 바꾸고 다시 시도해보면 어떨까?
문제없이 다음 명령어로 진행되는 것을 볼 수 있었다. 이번에는 또 다른 검증 로직이 기다리고 있는 것 같은데 확인해보니 사용자가 입력한 문자열의 세 번째, 네 번째 글자(esp+A에서 dword)의 주소를 ecx 레지스터에 저장하고 2, 문자열 "5y", ecx 레지스터를 차례대로 스택에 삽입한 후 함수를 호출하는 것을 볼 수 있었다. 딱히 함수 이름이 나타나있지 않기 때문에 어떤 역할을 하는지는 알 수 없지만 지금 시리얼 키를 검증하는 로직을 수행하고 있는 만큼 2글자짜리 문자열 비교를 수행하는 것이라고 추측해볼 수 있다. 그럼 실제로 함수를 호출해보면 어떻게 될까?
함수는 eax 레지스터에 1을 반환했다. 이후 명령어에서는 스택을 정리해준 후 test eax, eax를 통해 eax가 0임을 검증하고 있으며 현재는 1이 저장되어 있기 때문에 ZF가 설정되지 않아 jne 분기를 타게 된다. 이 역시 0x00401135로 점프하게 되며 역시 실패 메시지 박스를 호출하게 된다. 그렇다면 세 번째, 네 번째 글자를 함수에서 비교하던 "5y"로 변경해주고 다시 실행해보자.
문제없이 다음 코드로 넘어갈 수 있었다. 이번에는 레지스터에 이런저런 값을 복사하면서 입력값을 검증하고 있는데 순서대로 해석해보면 다음과 같다.
-
push ebx
-
push esi
-
mov esi, easy_crackme.40606C: 0x0040606C를 esi 레지스터에 복사한다. 해당 위치에는 "R3versing"이라는 문자열이 저장되어 있다.
-
lea eax, dword ptr ss:[esp+10]: esp+10 위치에서 dword(4바이트)만큼 eax 레지스터로 복사한다.
-
현재 esp는 0x0019F7E4며 esp+10은 0x0019F7F4인데 스택을 살펴보면 빈 공간을 가리키고 있다.
-
그러나 바로 옆에 입력된 시리얼 키인 "aa5y" 문자열이 존재한다.
-
즉 사용자가 입력한 문자열 참조의 연장선으로 여기선 입력하지 않은 다섯번째 글자부터 문자열을 의미한다.
-
-
mov dl, byte ptr ds:[eax]: eax 레지스터가 가리키는 곳에서 byte만큼 dl에 복사한다.
-
edx 레지스터는 확장된 dx 레지스터이며 이는 다시 상위, 하위 바이트인 dh, dl 레지스터로 구분된다.
-
즉 dl 레지스터에 복사한다는 것은 4바이트짜리 edx 레지스터의 최하위 1바이트에 복사한다는 것이다.
-
eax 레지스터가 가리키는 곳은 위에서 복사된대로 문자열의 5번째다. 여기서 바이트(byte)만 dl 레지스터로 복사함으로써 결과적으로 문자열의 5번째 글자 하나만 dl 레지스터로 복사하게 되는 것이다.
-
-
mov bl, byte ptr ds:[esi]: 위의 명령어와 비슷하며 이번에는 esi 레지스터가 가리키는 곳에서 1바이트("R")만 bl 레지스터로 복사한다.
-
mov cl, dl: dl 레지스터에 저장한 5번째 글자를 cl 레지스터로 복사한다.
-
cmp dl, bl: dl 레지스터와 bl 레지스터에 저장된 값을 비교한다. 즉 사용자가 입력한 문자열의 5번째 글자와 "R3versing" 문자열의 첫번째 글자를 비교하는 것이다.
현재는 시리얼을 4글자밖에 입력하지 않았기 때문에 비교에 실패하여 분기를 타고 결과적으로 실패 메시지 박스로 넘어가게 된다. 5번째 글자를 R로 넣고 다시 시도해보면 어떻게 될까?
역시 잘 넘어가는 걸 볼 수 있었다. 그 다음에도 비슷하게 6번째 글자와 문자 "3"을 비교하고 있다. 시리얼 키의 6번째 글자에 3을 넣어주고 다시 시도해보자.
역시 잘 넘어갔는데 아래쪽 코드를 확인해보니 이상한 점을 발견할 수 있었다. eax, esi 레지스터에 각각 2씩 더해준후 cl 레지스터를 test하여(현재는 아까 비교한대로 6번째 문자 "3"이 들어 있다) 다시 0x004010DA로 되돌아가고 있다. 0x004010DA에서는 eax, esi 레지스터가 가리키는 곳에서 1바이트씩 가져와 dl, bl 레지스터에 넣고 비교하는 작업을 수행하고 있는데 0x004010F4, 0x004010F7에서 eax, esi 레지스터에 2씩 더해주고 있다. 이는 사용자가 입력한 시리얼 키의 일부분(정확히는 5번째 문자부터)과 "R3versing" 문자열을 한 글자씩 비교하는 과정을 수행하며 모든 글자를 비교할 때까지 계속 점프하여 반복하게 된다. 레지스터에 2씩 더해주지만 한 글자씩 비교할 수 있는 것은 위에서 eax+1, esi+1 처럼 참조하고 있기 때문이다.
하지만 이렇게 문자열을 모두 비교하고 끝이 아니다. 아래쪽에 마지막으로 비교하는 구문이 하나 더 남아있다.
이는 단순히 esp+4에 위치한 1바이트짜리 데이터와 0x45, 친절하게 디버거에서 알려주는 아스키 문자 'E'를 비교하고 있다. esp+4는 스택에서 확인해보면 입력된 시리얼 키의 맨 처음 글자를 가리키고 있다. 그러므로 맨 처음 글자를 'E'로 바꿔주고 다시 확인해보면 어떨까?
문제없이 성공 메시지 박스를 볼 수 있었다. 이렇게 얻어낸 시리얼 키는 Reversing.kr의 Auth 메뉴에서 등록해주면 된다.
사실 리버싱에 찍먹하던 옛날부터 Abex 크랙미 이후로는 진도를 나간 적이 없기 때문에 이렇게 워게임 사이트에서 문제를 다운받아 풀어보는 건 처음이다. 언젠가 꼭 한번 Reversing.kr에서도 문제를 풀어보고 싶었는데 첫번째 문제는 순조롭게 풀 수 있었기 때문에 기분이 좋다. 다른 문제들도 계속 도전해봐야겠다.
'챌린지 > Reversing.kr' 카테고리의 다른 글
Challenge - Replace (0) | 2020.12.05 |
---|---|
Challenge - Music Player (0) | 2020.12.03 |
Challenge - Easy Unpack (0) | 2020.11.23 |
Challenge - Easy Keygen (0) | 2020.11.11 |