Reversing.kr 2번 Easy KeygenMe(100pt) 문제 풀이
- Easy KeygenMe
Easy Keygen 을 클릭하여 문제를 다운받자.
문제파일 폴더를 확인해보면 Easy_keygen.exe 파일과 함께 ReadMe.txt 텍스트 파일이 존재한다.
해석해보면 시리얼이 "5B134977135E7D13" 로 나오는 NAME을 찾으라고 되어있다.
프로그램을 실행시켜보면 Name과 Serial을 입력받는다.
Name에 임의의 값을 입력하고 Serial 역시 임의의 값을 입력해 보았는데 Wrong! 이라는 문자와 함께 프로그램이 종료가 된다.
이것을 통해 ReadMe.txt의 내용이 의미하는 것이 "5B134977135E7D13" 시리얼을 나타내는 NAME을 입력하라는 것이 아닌가 하고 유추해보았다.
본격적인 풀이로 들어가자.
우선 패킹의 유무와 어떤 언어로 프로그램이 제작되었는지를 확인해보자
[ Exeinfo PE 툴 결과 ]
패킹 : Not Packed
언어 : C++
ollydbg 로 easy_keygen.exe 파일을 디버깅 해보자.
우클릭 -> search for -> all referenced string 을 클릭하게되면 해당 프로그램에서 사용된 string을 출력시켜 준다.
해당 기능을 통해 우리는 처음에 확인한 Name과 Serial을 입력했을때 출력된 "wrong!" 문구를 확인 할 수 있었다.
그리고 string중에 눈에띄는 문구가
있다. 바로 "Correct!" 사용자가 입력한 Name과 해당 Name에 의해 도출된 Serial이 같을때 저 문구가 뜨지 않을까 하는 생각이 들었다.
해당 위치로 이동하니 00401118 주소의 분기문을 통해 조건의 참 거짓을 판단하는 코드가 보였다.
이것을 통해 우리는 해당 분기문을 지나기 전 코드의 실행 과정에 의해 프로그램의 결과가 결정된다는 것을 알 수 있다.
Input Name을 입력받는 곳을 확인해보자.
"0040105E" 주소 부분을 확인해 보면 사용자가 입력한 문자열이 존재하는 주소를 EDI에 넣는 것을 확인 할 수 있다.
stack address=0019FE18 주소를 시작으로 내가 입력한 "aaaaa" 문자가 저장되어 있다.
해당 어샘 코드는 레지스터들을 XOR 연산을 통해 초기화 하고 0040106E 부분에서 SCAS 명령어를 통해 문자의 길이를 구한다.
실제로 내가 입력한 값은 "aaaaa" 5글자이며 그에 해당하는 길이인 "5"가 ECX에 저장된것을 확인 할 수 있다.
해당 어샘코드 부터 본격적으로 사용자가 입력한 문자를 시리얼로 만들어 낸다.
위 의 어샘코드를 설명하기 전에 사용자 입력을 받기전에 프로그램이 시리얼을 만들어 낼때 사용하기 위한 특정한 키를 저장했는데 그 부분을 살펴보도록 하자
해당 부분은 Byte 단위로 0x10, 0x20, 0x30을 각각 스택에 넣어준다.
이 부분은 40107E 주소 코드에서 [ESP+ESI+0C] 부분으로서 ECX에 저장되어 루프 연산을 시도할때 모두 사용되게 된다.
401083의 [ESP+EBP+10] 이 가르키는 주소에는 사용자가 입력한 Name값이 저장되어 있다. 사용자가 입력한 문자열을 첫번째 글자부터 순서대로 Byte단위로 EDX레지스터에 넣는다.
현재 EDX에는 내가 입력한 "aaaaa"에서 첫번째 "a"가 들어가 있다.
EDX 에 들어있는 "a" 와 ECX에 첫번째로 저장되있는 0x10 를 XOR연산하여 "71" 이라는 결과값을 만들어낸다.
그리고 40109A 주소의 CALL 401150 부분에서 XOR 연산된 결과인 "71" 을 저장한다.
해당 부분을 자세히 살펴보자
step info(F7)을 이용해 CALL 401150 함수로 들어왔다.
00401156 주소 부분을 살펴보면 스택에 저장된 0019FE7C 주소를 EAX에 저장하는 코드를 확인할 수 있다.
0019FE7C에는 XOR 연산의 결과값인 "71"이 저장되어 있다.
그렇게 함수를 빠져나와서 루프를 돌게되면 EDX에 저장된 "aaaaa" 중 두번째 "a"와 ECX에 두번째로 저장된 0x20 를 XOR 연산한 결과 값인 "41"를 저장하게 된다.
0019FE7C 주소에 현재 까지 XOR 연산한 결과값이 차곡 차곡 쌓인다.
위 사진은 "7141517141" 은 내가 입력한 값인 "aaaaa"의 길이만큼 반복하여 XOR연산한 결과이다.
결론적으로 "aaaaa"의 Serial은 "7141517141" 이 된다.
[정리]
입력값 첫번째 "a" ^ "0x10" = "71"
입력값 두번째 "a" ^ "0x20" = "41"
입력값 세번째 "a" ^ "0x30" = "51"
입력값 네번쨰 "a" ^ "0x10" = "71" #0x10,0x20,0x30 까지 반복하고나면 다시 0x10와 입력값을 xor 연산한다.
입력값 다섯째 "a" ^ "0x20" = "41"
Serial = "7141517141"
input Serial 에 위에서 도출한 시리얼을 입력하고 실행하면 "aaaaa" 를 xor 연산한 결과가 저장된 버퍼인 EAX와 시리얼 키 값이 저장된 버퍼의 주소인 ESI의 키 값을 서로 비교한다.
그렇게 반복적으로 비교해 입력한 Serial과 저장되어있는 xor 연산한 Serial의 결과 값이 같으면 Correct! 문구가 있는주소로 JMZ 하고 한 글자라도 다르면 Worong! 문구가 있는 주소로 JMZ 하게된다
최종적으로 프로그램을 실행시키면 "Correct!" 가 출력된다.
그렇다면 ReadMe.txt 에 출력되있는 Serial 인 "5B134977135E7D13"을 반대로 0x10, 0x20, 0x30 를 반복하면서
5B ^ 0x10 , 13 ^ 0x20, 49 ^ 0x30 이런식으로 XOR 연산하면 NAME의 값을 도출해낼수 있을 것이다.
수동으로 계산하기에는 귀찮으면서 멋이 없기 때문에 코딩을 통해 NAME을 알아내보도록 하자.
[ 코드 ]
https://github.com/leeggoggal/Python/blob/master/reversing.kr_easy_keygenme_reverse_xor.py
해당 코드를 실행하면 Serial "5B134977135E7D13" 에 대한 Name이 출력된다.
EASY KeygenMe Clear!
'Reversing > Reversing.kr' 카테고리의 다른 글
Reversing.kr 6번 ImagePrc(120pt) 문제 풀이 (0) | 2017.11.11 |
---|---|
Reversing.kr 5번 Replace(150pt) 문제 풀이 (0) | 2017.11.11 |
Reversing.kr 3번 Easy Unpack(100pt) 문제 풀이 (0) | 2017.11.10 |
Reversing.kr 1번 Easy Crack(100pt) 문제 풀이 (1) | 2017.11.10 |