Wargame/abex` crackme

abex` 2nd crackme Write-Up

0xe82de_ 2019. 7. 17. 02:27
728x90
728x90
728x90

abex` 2nd crackme.exe
0.02MB

사용 도구: 올리디버거 2.01

 

abex` 2nd crackme(이하 원본파일)를 풀어보자.

 

먼저 원본파일을 실행시키면 다음과 같은 메세지 박스가 뜬다.

[사진 1]

 

Quit 버튼을 클릭하면 프로그램이 종료되고 이외의 버튼을 클릭했을 때는 다음과 같이 메세지 박스가 뜬다.

 

 1. Check 버튼 클릭

[사진 2]

 

 2. About 버튼 클릭

[사진 3]

 

 3. Name에 'test' 문자열을 입력하고 Check 버튼을 클릭

[사진 4]

 

 4. Name에 'test' 문자열 입력, Serial에 '1' 1개 입력하고 Check 버튼을 클릭

[사진 5]

 

 5. Name에 'test' 문자열 입력, Serial에 '1' 20개 입력하고 Check 버튼을 클릭

[[사진 6]

 

다양한 방법으로 프로그램을 실행해봤지만 Name과 Serial에 어떤 값을 입력해야 하는지는 모르겠다.

Name 또는 Serial의 값이 고정되어 있는 건지 Name의 값에 따라 Serial이 달라지는 건지 분석해보자.

 

 

[사진 7]

올리디버거로 원본파일을 열어보면 위 같은 코드를 확인할 수 있다.

 

 

[사진 8]

첫 번째 문제와 달리 두 번째 문제의 코드에서 main 함수를 바로 찾기란 쉽지 않다.

따라서 올리디버거의 기능인 문자열 검색을 이용해보자.

코드 섹션에서 우클릭 - Search for - All referenced strings 기능을 클릭하면 위 같은 창이 뜬다.

위 창에서 앞서 살펴봤던 실행결과들의 내용과 관련된 문자열들을 찾을 수 있는데 두 번 반복되는 것을 확인할 수 있다.

사진 내에서 윗부분은 문자열들의 정의되는 부분이고 아랫부분이 실제로 사용되는 부분이다.

 

 

[사진 9]

[사진 7]에서 아랫 부분의 문자열을 따라가서 살펴보면 위와 같이 함수 프롤로그 부분을 찾을 수 있다.

함수 프롤로그에 브레이크 포인트(이하 BP)를 설정하고 실행하면 [사진 1]처럼 Name과 Serial을 입력할 수 있는 창이 뜬다. Name과 Serial을 입력(Name: TEST, Serial: abcd)하면 창은 사라지고 디버깅을 할 수 있다. 따라서 이 곳은 메인함수는 아니고 메인함수에서 호출되는 함수라고 추측할 수 있다.

 

 

[사진 10]
[사진 11]

먼저 이 함수에서 사용하는 스택의 크기가 매우 넓었다. 402ED3 주소에서 스택을 0x0C 크기만큼 확보하고 402EE9 주소에서 0x144크기 만큼 추가로 확보한다. 그래서 내가 입력한 Name(TEST)과 Serial(abcd)이 스택에서 찾기가 쉽지 않았지만 디버깅을 한줄씩 해보면 위 같이 [EBP-88] 주소를 어떤 함수에 인자로 전달한 후에 함수를 실행하면 [EBP-88] 주소에 내가 입력한 Name이 저장된다.

 

 

[사진 12]
[사진 13]

그 다음 [사진 12]의 명령어들을 요약하자면 [EBP-88] 주소에 저장된 "TEST" 문자열을 [EDB-74] 주소에 저장한다.

*[참고: 이 파일은 Visual Basic(이하 VB)으로 만들어졌는데, VB에서는 유니코드 기반의 가변 길이 문자열 객체를 사용한다. 따라서 가변 길이 문자열 객체는 필요에 따라 내부에서 수시로 동적 메모리 할당/해제 작업이 발생한다.]

 

 

[사진 14]
[사진 15]
[사진 16]

그 다음, 많은 명령어 및 함수들이 있지만 각각 어떤 기능을 하는지는 파악하지 못했다. 그래서 함수를 실행하고 리턴되는 값을 중심으로 찾아봤다. 그 결과 [사진 14]의 명령어들을 찾았는데, 4031F0 주소의 __vbaStrVarVal 함수를 실행하면 어떤 주소가 리턴되었다. 이 주소를 참조해보면 [사진 15]처럼 'T' 문자가 저장되어 있었다. 그 다음 4031F7 주소의 rtcAnsiValueBstr 함수를 실행하면 [사진 16]처럼 값 0x54가 리턴되었다. 이 값은 내가 Name에 입력한 "TEST" 문자열의 첫 번째 문자의 아스키 코드 값이다.

 

*[메모: 내가 입력한 "TEST" 문자열은 [EBP-74] 주소에 저장되어 있다. 하지만 위 함수들의 전달인자로 [EBP-54], [EBP-88] 주소를 전달하고 있는데, 어떻게 [EBP-74] 주소가 아닌 다른 주소들을 전달하여 값을 리턴하는지는 정확히 이해하지 못하였다.]

 

 

[사진 17]
[사진 18]

[사진 17]의 다음 명령어들을 분석해보면, 리턴된 아스키 코드 값을 스택에 저장하고 403243 __vbaVarAdd 함수의 인자로 전달한다. 그리고 리턴되는 주소를 참조하면 [사진 18]을 확인할 수 있는데, 이는 위에서 확인했던 [사진 13]의 VB string 포맷과 비슷하다. 즉 문자열은 18F37C 주소의 0xB8인 셈이다. __vbaVaradd 함수의 기능이 무엇인지 정확히 모르지만 이 함수에 값 0x64와 내가 입력했던 첫 번째 문자의 아스키코드 값 0x54를 인자로 전달한다. 그리고 이 두 값을 더하면 0xB8이다. 일단 확실하지 않으므로 가정만 해두자.

 

 

[사진 19]

다음으로 루프문을 끝까지 실행하고 스택을 보면 위에서 확인했던 0xB8이 유니코드로 변환되어 스택에 여러번 저장된 것을 확인할 수 있다.

 

 

[사진 20]
[사진 21]

그리고 루프문의 끝에 BP를 설정하고 실행하면 [사진 21]과 같이 스택에 문자열 "B8A9"이 저장된 것을 확인할 수 있다. "A9" 문자열도 "B8" 문자열과 같은 루프문에서 생성되었으므로 위에서 가정한 계산을 거쳤을 것이다. 내가 Name에 입력한 두 번째 문자는 'E'이고 아스키 코드 값은 0x45이다. 이 값에 0x64를 더하면 0xA9가 된다.

 

 

[사진 22]
[사진 23]

루프문을 탈출하고 스택을 확인하면 [사진 22]처럼 길이 8의 문자열 "B8A9B7B8"이 저장되어 있다. 이 값들은 Name에 입력한 "TEST" 문자열들이 변환된 것이다. 그리고 디버깅을 진행하면 [사진 23]처럼 내가 입력한 Serial과 변환된 "B8A9B7B8"을 비교하고 다를 경우엔 실패했다는 내용의 메시지 박스 함수를 실행하는 코드로 분기한다.

 

 

[사진 24]

위 처럼 Name: Test, Serial: B8A9B7B8을 입력 시 성공했다는 내용의 메시지 박스가 실행된다.

 

 

[사진 25]

그리고 다른 문자열 "test"로 테스트해보았다. Serial 계산과정에 따라서 각 문자들의 아스키 코드 값에 0x64를 더하였다.

문자 't'의 아스키 코드 값 0x74 + 0x64 = 0xD8

문자 'e'의 아스키 코드 값 0x65 + 0x64 = 0xC9

문자 's'의 아스키 코드 값 0x73 + 0x64 = 0xD7

문자 't'의 아스키 코드 값 0x74 + 0x64 = 0xD8

Serial: D8C9D7D8

 

*후기

VB 함수들의 지식이 없어서 이해하는데 힘들었다. 그리고 명령어 코드가 너무 길어서 각 코드들이 정확히 어떤 기능을 하는지도 이해하지 못했다. 어렵다.

728x90
728x90