본문 바로가기

RVS

Reversing.kr 2번 문제(Easy Keygen)

Easy Keygen 문제는 아래 그림과 같이 하나의 실행 파일(Easy Keygen.exe)와 텍스트 파일(ReadMe.txt) 파일이 존재한다. 

언제나 처럼 ReadME.txt 파일을 살펴보자.

 

아래 그림의 ReadME.txt 파일의 내용은 다음과 같다.

Serial이 5B134977135E7D13S 일 때, Name이 무엇인지를 찾으라는 것이다.

그렇다면 프로그램을 실행시켜 해당 문제를 이해해보자.

 

Easy Keygen.exe 파일을 실행해보면 command 창이 실행된다.

그리고 Input Name과 Input Serial을 입력하여야 한다.

그러나, 각각 입력을 완료한 후 엔터를 수행하게 되면 Wrong 이라는 단어가 뜬다.

 

 

그럼 대충 알겠다.. Input Name에는 Input Serial 키가 이미 정해져 있다는 것이다. 즉, Input Serial 키를 생성하는 알고리즘이 존재한다.

디버깅을 수행해보자.

 

 

 

먼저 디버깅을 수행하며서 Wrong 문자열이 출력되는 곳의 위치를 찾아보자.

[F8] 단축키를 통해 차례로 디버깅을 수행하다보면 Wrong이 출력되는 위치는 (그림 1)의 0040129E 주소이다.

그리고 바로 위 주소인 00401299에서 Input Name과 Input Serial 키를 입력 받는것을 알 수 있다.

00401299 주소에서는 call easy keygen.401000 명령을 통해 401000 주소에 위치한 함수를 호출한다.

호출하는 함수를 자세히 살펴보자.

 

그림 1

 

호출하는 함수 내부를 살펴보기 위해서 먼저 해당 함수를 호출하는 위치인 00401299 주소에 [F2] 단축키를 통해 Breakpoint를 걸어두자.

그리고 [Ctrl] + [F2] 단축키로 프로그램을 재 실행한 후, [F9] 단축키를 통해 Breakpoint를 걸어둔 00401299 주소로 위치시킨다.

 

이제, 단축키 [F7]을 통해 해당 함수 내부로 들어가서 함수 내에서 어떤 일을 수행하는지를 살펴보자.

아래 (그림 2)는 call easy keygen.401000 함수를 호출하는 00401299 주소에서 [F7] 단축키를 통해 해당 keygen.401000함수 내부로 진입한 것이다. 

해당 함수 내부에서 단축키 [F8]로 디버깅을 수행하다보면 00401059 주소에서 디버깅이 멈추고, Input Name 값을 입력 받는다.

 

우리는 여기서 예상해볼 수 있다!

Input Name 값을 입력받은 후 입력 값을 가지고서 Serial 값을 생성할 것이다.

그렇다면 Input Name 값을 입력하는 위치인 00401059 주소 뒤를 자세하게 분석할 필요가 있겠다.

그림 2

 

아래 (그림 3)은 00401059 이후의 코드를 나타낸다.

먼저, 가장 눈에 뛰는 코드는 0040106E 주소의 repne scasb 명령이다.

두 번째는 00401073 주소 아래부터의 비교문과 점프문이다.

차례대로 보자.

 

그림 3

 

첫 번째로 repne scasb 명령이 존재하는 구간에 대해서 살펴보았다.

아래 (그림 4)의 코드 구간을 디버깅하면서 확인해보니 00401072 주소의 명령이 실행된 후 ecx 레지스터에는 입력한 Input Serial 문자열의 길이가 세팅되어있었다.

 

그림 4

 

더 자세히 살펴보자.

repne scasb 명령이 실행되기 전 0040106C 주소까지 디버깅을 하게 되면 다음과 같이 레지스터가 세팅된다.

1. 0040105E lea edi, dword ptr ss:[esp+18]  =>  EDI 레지스터가 Input Name 값으로 세팅

2. 00401062 or ecx, FFFFFFFF  => ECX 레지스터가 FFFFFFFF 값으로 세팅

3. 00401065 xor eax, eax  => eax가 00000000 값으로 세팅 (레지스터를 초기화할 때 xor을 자주 사용함)

4. 00401067 add esp, 8  => esp 가 8 증가

5. 0040106A xor ebp, ebp => ebp가 00000000 값으로 세팅 (레지스터를 초기화할 때 xor을 자주 사용함)

6. 0040106C xor esi, esi  => esi가 00000000 값으로 세팅 (레지스터를 초기화할 때 xor을 자주 사용함)

아래 (그림 5)는 위 1~6까지의 명령이 수행된 후에 세팅된 레지스터를 나타낸다.

그림 5

위 (그림 5)와 같이 레지스터가 세팅된 후 0040106E repne scasb 명령을 수행하는데,

repne scasb 명령어는 반복 작업을 나타내는 연산자로 보통 문자열을 다룰 때 사용된다.

이는 ECX 레지스터에 반복 횟수를 셋팅한 후 그 수만큼 반복시키는 동작을 수행한다.

 

이 때, repne의 rep는 반복 (Repeat)을 의미하고, 뒤에 이어진 n은 NOT (0)을 e는 Equal (=)을 의미한다.

즉, repne는 ecx 레지스터가 0이 아닐 때까지 반복한다. 그러므로 ecx는 반복 시 1씩 감소하게된다.

반면, edi 레지스터는 ecx 레지스터가 감소할 때 동일한 횟수로 증가한다.

 

그리고 scasb 명령은 Scan String Byte의 줄임말이다.

eax 레지스터의 1바이트 데이터와 edi 레지스터의 데이터를 비교하여 값이 같으면 Zero Flag가 1이 된다.

 

아래 (그림 6)은 repne scasb 명령을 수행 후에 세팅된 레지스터를 나타낸다.

ecx 레지스터가 FFFFFFFF에서 FFFFFFF6으로 세팅되었다.

우리는 "1a2b3c4d" 문자열을 입력하였고, ecx는 입력한 문자열만큼 감소하게된다.

그러면.. FFFFFFF6이 아닌 FFFFFFF7이 되어야 한다. 

아무래도 문자열 맨 뒤에 보이지는 않지만 존재하는 공백을 포함해서 그런듯하다.

 

그림 6

 

repne scasb 명령이 실행된 후, 아래 (그림 7)에서의 0041070 주소 not ecx 명령이 수행된다.

어셈블리 not 명령은 neg 명령어로 사용하는데, 부호를 변경할 때 사용된다.

 

그림 7

이전에 세팅된 ecx의 FFFFFFF6 값은 NOT 명령으로 다음 (그림 8)에서와 같이 값 9로 세팅된다. 즉, 입력한 문자열 수인 값 8보다 하나 더 큰 수가 세팅된다. 

위 (그림 7)의 00401072 주소의 dec ecx 명령이 수행된 이유이다. dec 명령은 세팅된 값에서 1을 뺀다.

따라서, 아래 (그림 8)처럼 ecx 레지스터가 9로 세팅되었다가 (그림 9)에서처럼 ecx 레지스터가 입력한 문자열 길이인 8로 세팅된다.

 

그림 8
그림 9

 

 

(그림 10)처럼 그 이후의 코드는 ecx에 세팅된 문자열 길이만큼 반복하는 것처럼 보인다. 좀 더 자세히 살펴보자.

 

그림 10

 

아래 (그림 11)에서와 같이 0040107E 주소의 movsx ecx, byte ptr ss:[esp+esi+C] 명령과 00401083 주소의 movsx edx, byte ptr ss:[esp+ebp+10] 명령을 수행하게 되면 다음 (그림 12)와 같이 ecx 레지스터에는 10이 세팅되고, edx에는 31, 즉 입력한 Input Name 값의 첫번째 문자열이 세팅된다.

 

(그림 11)의 00401088 주소 xor ecx, edx 명령을 통해 (그림 12)와 같이 ecx 레지스터에는 10이 edx 레지스터에는 31(입력한 input name 값의 첫번째 문자열)이 입력된 것을 확인할 수 있다.

계산된 값은 (그림 13)에서 볼 수 있듯이 ecx 레지스터에 세팅된다. 즉, xor ecx, edx는 21로 ecx 레지스터에 세팅된 것을 확인할 수 있다.

 

그림 11
그림 12
그림 13

그리고, (그림 11)에서 0040108A 주소가 실행되면 (그림 13)의 EAX 레지스터가 0019FE7C로 세팅된다.

(그림 11)에서 0040108E 주소와 0040108F 주소는 각각 ecx와 eax 레지스터를 push한다.

ecx는 xor 연산이 완료된 값이 세팅되어 있고, eax에는 특정 주소값이 세팅되어 있다.

그리고 0040109A 주소에서 easy keygen.401150이 호출되어 실행된다.

뭔가, ecx의 값을 eax 레지스터에 셋팅된 주소에 저장할 것 같은 느낌이 들어서 0019FE7C 메모리 덤프를 확인해보았다.

 

그림 14

위 (그림 14)는 0019FE7C 메모리 덤프를 확인한 것이다. 아스키코드를 보면 연산이 완료된 값인 21이 저장된 것을 확인하였다.

 

해당 코드는 입력한 문자열 만큼 계속 반복된다. 다만 디버깅을 하면서 레지스터를 살펴보면 다음과 같은 과정이 반복된다. 만약, 네 번째, 다섯 번째 문자열이 존재하면 네 번째 문자열은 1, 다섯 번째 문자열은 2를 수행한다.

 

1. 입력한 Input Name의 첫 번째 문자열과 0x10을 xor

2. 입력한 Input Name의 두 번째 문자열과 0x20을 xor

3. 입력한 Input Name의 세 번째 문자열과 0x30을 xor

 

결론적으로 우리는 Serial이 5B134977135E7D13S 일 때, Name이 무엇인지를 찾아야하므로 거꾸로 계산하면된다.

즉, 똑같이 XOR 연산을 수행하면 된다. 예를 들어, 0x5B XOR 0x10, 0x13 XOR 0x20, 0x49 XOR 0x30, 0x77 XOR 0x10....을 반복적으로 계산한다. 

 

아래의 Python 코드로 작성해서 계산해 보았다. ㅠㅠ다만, 내 코드는 마지막 문자는 무시해주어야 한다..

코딩을 너무 못했다. 다른 분들은 아주 간단하게 짰던데, 다음 번에 다시 시도해봐야지 ㅎㅎ

import binascii 
 
def count(a,b,c):
    a_bin_result=bin(int(a, 16) ^ int(fir, 16))
    b_bin_result=bin(int(b, 16) ^ int(sed, 16))
    c_bin_result=bin(int(c, 16) ^ int(thd, 16))
    a_bin_result=hex(int(a_bin_result, 2))
    b_bin_result=hex(int(b_bin_result, 2))
    c_bin_result=hex(int(c_bin_result, 2))

    print(f"{a_bin_result} {b_bin_result} {c_bin_result}")
    

def change_bin(B):
    bin_list=[]
    #리스트 초기화
    for i in range(8):
        bin_list.append(0)

    #리스트 3개씩 초기화 
    i=0
    for x in range(0,len(B),2):
        num=''.join(B[x:x+2])
        num = hex(int(num,16)) #헥스 값으로 인코딩
        bin_list.pop(i)
        bin_list.insert(i,num)
        i=i+1

    bin_list.append(hex(0))

    #각 리스트 값 계산을 위해 초기화
    for y in range(0, len(bin_list), 3):
        b_list=bin_list[y:y+3]

        a=b_list[0]
        b=b_list[1]
        c=b_list[2]        
        count(a,b,c)

if __name__=='__main__':
    fir=hex(0x10)
    sed=hex(0x20)
    thd=hex(0x30)

    A='5B134977135E7D13'
    B=list(A)
    change_bin(B)

    

어쨌든 위 코드를 실행해보면 아래 (그림 15)와 같은 결과가 나타난다. 마지막 0x30은 무시해주자 ^^;

인증 플레그는 각 헥스 값을 아스키 문자로 변경해주면 된다. ㅎㅎ (그림 16)은 아스키 코드표이다.

 

그림 15
그림 16

'RVS' 카테고리의 다른 글

Reversing.kr 11번 문제(Easy ELF)  (0) 2020.03.19
Reversing.kr 7번 문제 (Position)  (0) 2020.03.19
Reversing.kr 9번 문제(Ransomware)  (0) 2020.03.16
UPX Manual Unpacking  (0) 2020.03.12
Reversing.kr 4번 문제(Music Player)  (1) 2020.03.04