본문 바로가기

RVS

Reversing.kr 7번 문제 (Position)

Ready


Revsersing.kr 7번 Position 문제에는 (그림 1)과 같이 하나의 실행파일과 하나의 텍스트 파일이 존재한다.

먼저 ReadMe.txt 파일을 읽어보자.

 

그림 1

 

(그림 2)의 ReadMe.txt 파일의 내용이다.

Serial이 76876-77776 일 때의 Name을 찾고, 문제에는 여러 개의 답이 존재하며, 패스워드는 p로 끝난다는 글이 적혀있다. Position.exe 파일을 실행해보자.

 

 

그림 2

 

Position.exe는 (그림 3)과 같이 Name과 Serial을 입력 받고, 입력한 문자가 옳지 않은 경우 'Wrong' 문자를 보여준다.

우리는 Serial이 76876-77776 일 때의 Name을 구해야한다.

이를 통해, Name과 Serial 간에 특정 알고리즘을 통해 문자를 생성한다는 것을 알 수 있다.

Position.exe 프로그램을 디버깅하여 문자를 생성하는 방식을 살펴보자.

그림 3

 

 

 

Reversing


Position 프로그램을 디버거로 실행해보았다.

그리고 Input Name의 값을 입력하게 되면, 하나의 문자가 입력된 후에 프로그램이 멈춘다.

 

먼저, 프로그램의 String 을 살펴본 후, 문자를 입력받는 위치를 찾아보자

(그림 4)에와 같이 L"Input Name", L"Input Serial", L"Correct!", L"Wrong" 과 같은 문자를 볼 수 있다. 

L"Input Name"을 클릭하여 Name을 입력받는 곳을 살펴보자.

 

 

그림 4

 

아래 (그림 5)는' Serial 입력 값을 체크하는 루틴이다.

011917A2 주소에서는 먼저 [ecx-C] 주소의 값과 숫자 4를 비교한다.

[ecx-C]에는 'Input Name'에 입력한 문자열 길이가 저장된다.

즉, Input Name에 입력한 문자열 길이가 4인지를 체크하는 것으로 보인다.

 

그리고 아래 011917EA 주소와 011917FA 주소에서는 ax 레지스터와 'a' 그리고 'z' 문자를 비교한다.

(그림 6)은 ax 레지스터에 세팅된 값을 보여준다. ax 레지스터에는 Input Name으로 입력한 첫 번째 문자가 세팅되어 있다.

비교문 이후의 011917EE와 011917FE의 점프문을 보면 ax 레지스터에 세팅된 Input Name에 입력한 문자가 'a' 보다 크고 'z' 보다 작은지를 확인한다.

즉, Input Name에 입력된 문자가 소문자로 이뤄져있는지를 체크하는 것으로 보인다.

이를 4번 반복하여 입력한 네 개의 문자가 모두 소문자로 구성되어 있는지를 체크한다.

그림 5
그림 6

 

 

그리고 아래 (그림 7)에서 볼 수 있듯이 위의 루틴 이후에는 소문자로 이뤄진 네 문자의 입력 값들이 모두 서로 다른 값으로 이뤄져 있는지를 체크하는 루틴이다.

그림 7

 

이후 (그림 8)은 Input Serial 입력 값을 체크한다.

0119184A 주소에서 [eax-C] 주소 값과 B를 비교한다. B는 십진수로 숫자 11이다.

디버깅을 하다보면 [eax-C] 메모리 주소에는 Input Serial에 입력된 문자의 개수가 입력되는 것을 알 수 있다.

즉, Input Serial의 입력값이 11 글자인지를 체크하는 것이다.

 

이후, 0119185F 주소에서는 ax와 2D를 비교한다.

2D는 문자 '-'에 해당하고 ax 레지스터에는 디버깅을 통해 살펴보면 Input Serial에 입력된 여섯 번째 문자가 세팅되어 있다.

즉, Input Serial 입력 값의 여섯번째 문자가 '-' 문자인지를 체크한다.

 

그림 8

 

Input Serial 입력 값의 길이와 여섯 번째 문자를 확인한 이후에는 (그림 9)와 같이 cl, al, dl, bl 레지스터를 이용하여 shr, and, add와 같은 많은 연산을 수행한다. 

아래의 어셈블리 코드를 통해 연산이 어떻게 수행되는지를 살펴봐도 되겠지만, 설명하기에 복잡하기 때문에ㅎ IDA 도구를 통해 살펴보았다.

 

그림 9

 

 

(그림 10)은 IDA를 통해 (그림 9)와 같은 연산을 수행하는 어셈블리를 코드화 하여 살펴본 것이다.

먼저, v6과 v8은 Input Name에 입력된 문자의 0번째와 1번째가 저장되고 연산을 통해 각 변수들에 저장된다.

그리고 itow_s 함수를 통해 v7와 v9를 더한 값을 v10 변수에 저장한다.  v10 변수는 v51 주소를 가리킨다.

itow_s는 정수를 문자열로 변환하는 함수이다. 아래 (그림 10)처럼 네 개의 매개 변수를 입력받는다.
itow_s(value, buffer, size, radix)에서 각 매개 변수는 아래와 같은 의미를 가진다.
- value: 변환할 숫자.
- buffer: 변환 결과를 포함하는 출력 버퍼
- size: 문자 또는 와이드 문자 단위의 버퍼 크기
- radix: 값을 변환하는데 사용할 기 수 또는 숫자

그림 10

 

아래 (그림 11)은 itow_s 를 통해 v7과 v9가 더해진 문자가 저장된 v11과 input_key의 0번째 자리와 비교하여 검사하는 것을 살펴볼 수 있다.

이와 같은 방식으로 반복적으로 itow_s를 통해 계산된 값과 input_key의 0~4번째 자리까지를 검사한다.

(그림 11)에서 &input_key는 원래 v51로 IDA에서 표시한다. v51는 Input Key 입력 값을 가리킨다.
따라서, IDA의 Naming 기능을 통해 v51를 input_key로 변경해주었다.

 

그림 11

 

 

계속해서 살펴보면 아래 (그림 12) 구간에서는 input_name의 2번째 3번째 자리 문자를 통해 마찬가지로 연산을 수행하여 itow_s 함수를 통해 v21+v23 정수를 문자열로 변경한 후, v24 버퍼에 저장해준다.

 

(그림 12)에서 &input_name는 원래 v50으로 IDA에서 표시한다. v50는 Input Name 입력 값을 가리킨다.
따라서, IDA의 Naming 기능을 통해 v50를 input_name으로 변경해주었다.

 

그림 12

그리고 (그림 13)과 같이 itow_s를 통한 결과 값들과 input_key의 6~10번째 자리가 같은지를 검사한다.

그림 13

 

우리는 위의 디버깅을 통해서 입력한 Name으로 Key를 생성해내는 방법을 확인하였다.

본론으로 돌아가 해당 문제는 Key가 '76876-77776' 일 때 Name의 문자를 알아내야 한다.

 

IDA를 통해 살펴본 코드를 활용해서 파이썬을 통해 Name을 추측해보았다.

 

key='7687677776'
key=list(key)
buf=[]
buf2=[]
name_list=[]

for name1 in range(ord('a'), ord('z')+1):
	v7=(name1 & 1)+5
	v48 = ((name1 >> 4) & 1) + 5
	v42 = ((name1 >> 1) & 1) + 5
	v44 = ((name1 >> 2) & 1) + 5
	v46 = ((name1 >> 3) & 1) + 5
	for name2 in range(ord('a'), ord('z')+1):
		v34 = (name2 & 1) + 1
		v40 = ((name2 >> 4) & 1) + 1
		v36 = ((name2 >> 1) & 1) + 1
		v9 = ((name2 >> 2) & 1) + 1
		v38 = ((name2 >> 3) & 1) + 1
		
		if str(v7+v9)==key[0] and str(v46+v38)==key[1] and str(v42+v40)==key[2] and str(v44+v34)==key[3] and str(v48+v36)==key[4]:
			a=chr(int(name1))+chr(int(name2))
			buf.append(a)


for name3 in range(ord('a'), ord('z')+1):
	v21=(name3 & 1)+5
	v49 = ((name3 >> 4) & 1) + 5
	v43 = ((name3 >> 1) & 1) + 5
	v45 = ((name3 >> 2) & 1) + 5
	v47 = ((name3 >> 3) & 1) + 5
	for name4 in range(ord('a'), ord('z')+1):
		v35 = (name4 & 1) + 1
		v41 = ((name4 >> 4) & 1) + 1
		v37 = ((name4 >> 1) & 1) + 1
		v23= ((name4 >> 2) & 1) + 1
		v39 = ((name4 >> 3) & 1) + 1
		
		if str(v21+v23)==key[5] and str(v47+v39)==key[6] and str(v43+v41)==key[7] and str(v45+v35)==key[8] and str(v49+v37)==key[9]:
			b=chr(int(name3))+chr(int(name4))
			buf2.append(b)

for buf_1 in range(len(buf)):
	for buf_2 in range(len(buf2)):
		n=buf[buf_1]+buf2[buf_2]
		name_list.append(n)

for nn in name_list:
	if nn[3] =='p':
		print(nn)

위의 코드를 실행하여 추측된 단어는 (그림 14)와 같다. 네 개의 단어 중 정확한 단어는 'bump'이다.

bump를 통해 정답을 입력하면 인증 성공!

그림 14

'RVS' 카테고리의 다른 글

Reversing.kr 13번 문제 (AutoHotkey1)  (0) 2020.03.20
Reversing.kr 11번 문제(Easy ELF)  (0) 2020.03.19
Reversing.kr 9번 문제(Ransomware)  (0) 2020.03.16
UPX Manual Unpacking  (0) 2020.03.12
Reversing.kr 2번 문제(Easy Keygen)  (0) 2020.03.11