* 출처 : http://www.simples.co.kr/RCEZone
abstract
abex’s crackme #2 파일을 디버깅합니다.
Goal
- 아주 간단한 crackme 파일을 분석하여 디버거와 디스어셈 코드에 익숙해집니다.
- Visual Basic 의 파일 구조를 간단히 살펴보고 분석 방법을 배워봅니다.
Prologue
지난 번에 이어서 abex' crackme 두번째 파일을 분석해보도록 하겠습니다.
두번째 파일은 Visual Basic 으로 제작되었으며,
Visual C++ 혹은 Assembly 로 작성된 파일과는 또 다른 파일 형태를 경험 하실 수 있습니다.
* 설명에 나온 메모리(스택) 주소들은 사용자의 PC 환경에 따라 변경됩니다.
디버깅 하실 때 이점을 감안하시기 바랍니다.
* 디버깅이 어렵게 느껴진다면 그건 자연스러운 현상입니다.
꾸준히 노력과 시간을 투자한다면 결국 익숙해지실 것입니다.
VirusTotal 검사
디버깅 할 파일의 안전성을 확인하기 위해 www.virustotal.com 을 이용하시기 바랍니다.
<Fig. 1>
abex' crackme #2
일단 실행시켜서 어떤 프로그램인지 알아봐야겠죠?
<Fig. 2>
전형적인 crackme 의 형태인 serial key 알아내기 프로그램입니다.
Name 을 따로 입력받는 걸로 봐서 Serial 값을 생성할 때 Name 문자열이 사용될 것 같습니다.
Name 과 Serial 을 적절히 입력하고 [Check] 버튼을 눌러봅니다.
<Fig. 3>
"Wrong serial!" 메시지 박스가 출력됩니다.
다른 값으로 몇 번을 시도해도 같은 메시지 박스가 나타납니다.
디버깅을 시작하기 전에 Visual Basic 파일의 특징을 살펴보겠습니다.
Visual Basic 파일 특징
먼저 Visual Basic (VB) 파일 특징에 대해 간단히 설명드리겠습니다.
1) VB 전용 엔진
VB 파일은 MSVBVM60.dll (MicroSoft Visual Basic Virtual Machine 6.0) 이라는 VB 전용 엔진을 사용합니다.
(The Thunder Runtime Engine 이라는 이름으로도 불리고 있습니다.)
VB 엔진의 사용 예를 들어보면 메시지 박스를 출력하고 싶을때 VB 소스 코드에서 MsgBox() 함수를 사용합니다.
VB 컴파일러는 실제로 MSVBVM60.dll!rtcMsgBox() 함수가 호출되도록 만들고,
이 함수 내부에서 Win32 API 인 user32.dll!MessageBoxA() 함수를 호출 해주는 방식으로 동작합니다.
(VB 소스 코드내에서 user32.dll!MessageBoxA() 함수를 직접 호출 하는 것도 가능합니다.)
2) N(Native) code, P(Pseudo) code
VB 파일은 컴파일 옵션에 따라서 N code 와 P code 로 컴파일이 가능합니다.
간단히 설명드리면 N code 는 일반적인 디버거에서 해석 가능한 IA-32 명령어를 사용하는 반면에,
P code 는 인터프리터(Interpreter) 언어 개념으로서 VB 엔진으로 가상 머신을 구현하여
자체적으로 해석가능한 명령어(바이트 코드)를 사용하는 것입니다.
따라서 VB 의 P code 를 정확히 해석하려면 VB 엔진을 분석하여 에뮬레이터를 구현하여야 합니다.
* P code 의 사용 예로 JAVA (JAVA Virtual Machine), Python (Python 전용 엔진)등이 있습니다.
* P code 를 사용했을때의 장점은 이식성이 좋아진다는 것입니다.
(플랫폼 별로 엔진을 제작/배포하면 다른 플랫폼 에서 기존의 사용자 코드를 거의 수정없이 그대로 사용 가능합니다.)
3) Event Handler
VB 는 주로 GUI 프로그래밍을 할 때 사용되며, IDE 인터페이스 자체도 GUI 프로그래밍에 최적화 되어 있습니다.
즉, VB 프로그램은 Windows 운영체제의 event driven 방식으로 동작하기 때문에
main() 혹은 WinMain() 에 사용자 코드(우리가 디버깅을 원하는 코드)가 존재하는 것이 아니라,
각 event handler 에 사용자 코드가 존재합니다.
위의 abex' crackme #2 에서는 [Check] 버튼 handler 에 사용자 코드가 있겠지요.
4) undocumented 구조체
VB 에서 사용되는 각종 정보들(Dialog, Control, Form, Module, Function, etc)은
내부적으로 구조체 형식으로 파일에 저장됩니다.
MicroSoft 에서는 이러한 구조체 정보를 공개하지 않았기 때문에 VB 파일의 디버깅에 어려움이 있습니다.
* Visual Basic 파일 포멧은 그 자체로 흥미로운 부분이 많이 있기 때문에,
향후 VB 파일 포멧에 대한 상세한 분석을 해보도록 하겠습니다.
Start debugging
OllyDbg 를 실행시켜 abex' crackme #2 파일의 디스어셈 코드를 살펴보겠습니다.
<Fig. 4>
프로그램이 시작되면 처음 하는 일은 VB 엔진의 메인 함수(ThunRTMain)를 호출하는 것입니다.
00401232 FF25 A0104000 JMP DWORD PTR DS:[4010A0] ; MSVBVM60.ThunRTMain
00401238 68 141E4000 PUSH 401E14 ; => EP
0040123D E8 F0FFFFFF CALL 00401232 ; <JMP.&MSVBVM60.#100>
RT_MainStruct 구조체 주소(401E14)를 파라미터로 넘기고 VB 엔진의 메인 함수인 ThunRTMain() 를 호출합니다.
위 3 줄의 코드가 VB 파일의 startup 코드의 전부입니다.
간단하긴 하지만 아래 3 가지 항목은 눈여겨 볼 필요가 있습니다.
1) 간접호출 (Indirect Call)
40123D 주소의 CALL 명령은 ThunRTMain() 함수 호출 인데요, 조금 특이한 기법을 사용합니다.
MSVBVM60.dll!ThunRTMain() 으로 직접 가는 것이 아니라, 중간의 401232 주소의 JMP 명령을 통해서 갑니다.
이 기법은 VC++, VB 컴파일러에서 많이 사용하는 간접호출(Indirect Call) 기법입니다.
* 참고로 4010A0 주소는 IAT(Import Address Table) 영역이며 MSVBVM60.ThunRTMain() 함수의 실제 주소가 담겨있습니다.
향후 PE header 설명할 때 IAT 에 대해 자세히 설명드리겠습니다.
2) RT_MainStruct 구조체
우리가 주목할 부분은 ThunRTMain() 함수의 파라미터인 RT_MainStruct 구조체 입니다.
여기서는 401E14 주소에 RT_MainStruct 가 존재합니다.
<Fig. 5>
MS 에서 RT_MainStruct 를 공개하지 않았지만, 이미 실력있는 해외 리버서들이 분석을 완료하고 인터넷에 공개하였습니다.
RT_MainStruct 구조체의 멤버는 또 다른 구조체의 주소들입니다.
즉, VB 엔진은 파라미터로 넘어온 RT_MainStruct 구조체를 가지고
프로그램의 실행에 필요한 모든 정보를 얻는다는 걸 알 수 있습니다.
* 나중에 VB 파일 포멧 분석할 때 멤버 하나씩 살펴보도록 하겠습니다.
3) ThunRTMain() 함수
참고로 ThunRTMain() 함수가 나온김에 어떻게 생겼는지 살펴 보겠습니다.
<Fig. 6>
<Fig. 6> 는 ThunRTMain() 코드의 시작부분입니다.
메모리 주소가 완전히 틀려진 것이 보이시죠?
이 주소는 MSVBVM60.dll 이 로딩된 주소입니다.
VB 파일의 대한 설명은 이정도로 마치도록 하고 abex' crackme #2 로 돌아가겠습니다.
단서를 찾아라!
도대체 우리가 보고 싶은 "패치 해야 할 코드" 는 어디 있을까요?
지금 수준에서 RT_MainStruct 구조체를 분석하는 것은 쉽지 않을테니, 좀 더 간단한 방법을 생각해 봅니다.
역시 간단히 떠오르는 생각은 <Fig. 3> 의 에러 메시지 박스와 그 문자열을 단서로 해서 시작하면 될 것 같습니다.
문자열 검색
OllyDbg 의 문자열 검색 기능(All referenced text strings) 을 사용하면 아래와 같은 윈도우가 나타납니다.
<Fig. 7>
아까 본 메시지 박스의 문자열을 확인 하였습니다.
해당 문자열을 더블 클릭하여 이 주소로 가봅니다.
<Fig. 8>
메시지 박스의 타이틀, 내용 그리고 실제 메시지 박스 함수 호출 코드(4034A6)까지 나타났습니다.
프로그래밍 관점에서 생각해보면, 어떤 알고리즘으로 시리얼 키를 생성하고
사용자가 입력한 키와 문자열 비교를 통해서 각각 TRUE(키가 같음) 와 FALSE(키가 틀림) 로 코드가 갈라질 것입니다.
즉, 위 코드 전후에 문자열 비교 코드, 그리고 키가 맞았을 때 출력될 성공 메시지 박스 호출 코드가 존재할 것입니다.
(키가 맞았을때 메시지 박스가 호출될 꺼라는 것은 <Fig. 7> 의 문자열을 보고 유추한 내용입니다.)
<Fig. 8> 에서 스크롤을 조금 올리다 보면 과연 (추측한대로) 분기문을 포함한 코드가 나타납니다.
<Fig. 9>
403329 주소의 __vbaVarTstEq 함수를 호출해서 리턴값(AX) 를 비교(TEST 명령) 한 후
403332 주소의 조건 분기(JE 명령)에 의해서 참, 거짓 코드로 분기하게 됩니다.
* 위 코드에서 사용된 어셈블리 명령어 설명
TEST : 논리 비교 (Logical Compare)
bit-wise logical ‘AND’연산과 동일 (operand 값이 변경되지 않고 EFLAGS 레지스터만 변경됨)
두 operand 중에 하나가 0 이면 AND 연산 결과는 0 → ZF = 1 로 세팅됨
JE : 조건 분기 (Jump if equal)
ZF = 1 이면 점프
문자열 찾기
<Fig. 9> 에서 403329 주소의 __vbaVarTstEq 함수가 문자열 비교 함수라면,
그 위에 있는 두 개의 PUSH 명령어는 비교 함수의 파라미터 - 즉, 비교 문자열이 될 것입니다.
(C 언어의 strcmp 함수를 떠올려서 추측한 것입니다.)
403329 주소까지 가보겠습니다. 403329 주소에 BP 를 설치하시고 실행[F9]해 주세요. 메인 다이알로그가 나타나고 <Fig. 3> 과 같이 입력하신 후 [Check] 버튼을 누르시면 403329 주소에 멈추게 됩니다.
00403321 LEA EDX,DWORD PTR SS:[EBP-44]
00403324 LEA EAX,DWORD PTR SS:[EBP-34]
00403327 PUSH EDX ; 0012FBDC
00403328 PUSH EAX ; 0012FBEC
00403329 CALL DWORD PTR DS:[<&MSVBVM60.__> ; MSVBVM60.__vbaVarTstEq
이 상태에서 스택을 보면 아래와 같습니다. (스택의 주소는 디버깅 환경에 따라 달라집니다.)
<Fig. 10>
00403321 주소의 SS:[EBP-44] 는 무엇을 나타내는 걸까요?
IA-32 Register 기본 설명 에서 SS 는 스택 세그먼트(Stack Segment) 이고, EBP 는 베이스 포인터(Base Pointer) 레지스터라고 소개했었습니다.
즉, SS:[EBP-44] 주소가 의미하는 것은 스택내의 주소를 말하는데 ,
이것이 바로 함수내에서 선언된 로컬 객체의 주소 입니다 .
(로컬 객체는 스택에 저장되는걸 아시죠~)
해당 메모리 주소([EBP-44] = [12FBDC]) 를 보면 아래 그림과 같습니다.
<Fig. 11>
* 참고
OllyDbg 에서 주소를 직접 입력하지 않고 편하게 찾아가는 명령이 있습니다.
<Fig. 9>의 Code 윈도우에서 마우스로 403321 주소를 선택하신 후 마우스 우측 메뉴의 'Follow in dump → Memory address' 명령을 쓰시면 됩니다. 또는 <Fig. 10> 에서 마우스로 12FAB8 (12FBDC) 주소를 선택하신 후 마우스 우측 메뉴의 'Follow in dump' 명령을 사용하시면 됩니다.
VB 의 문자열은 C++ 의 string 클래스 객체와 마찬가지로 가변 길이 문자열 타입을 사용합니다.
따라서 <Fig. 11> 에서 보는 바와 같이 바로 문자열이 나타나지 않고 16 byte 크기의 데이타가 나타납니다.
(이것이 바로 VB 에서 사용하는 문자열 객체일 것입니다.)
다른 값들은 동일하고 노란색으로 표시된 부분의 값이 틀린데 메모리 주소처럼 보이네요.
(가변 문자열 타입은 내부에 -동적으로 할당한- 실제 문자열 버퍼 주소를 가지고 있습니다.)
OllyDbg 의 메모리(덤프) 윈도우에서 마우스 우측 메뉴의 Long – Address with ASCII dump 명령를 선택합니다.
이 명령은 메모리 윈도우의 보기 형식을 마치 스택 윈도우처럼 변경하고,
특히 문자열 주소인 경우 해당 문자열을 표시해줍니다.
(원래대로 보고 싶을때는 마우스 우측 메뉴의 Hex – Hex/ASCII (16 bytes) 명령)
<Fig. 12>
위 그림에서 알 수 있듯이 결국 EDX(0012FBDC) 는 실제 serial 값이고,
EAX(0012FBEC) 는 사용자가 입력한 serial 값입니다.
(참고로 VB 는 UNICODE 문자열을 사용합니다.)
이 노란 주소 부분을 찾아가서 보면 실제 문자열을 확인할 수 있습니다.
<Fig. 13>
Crackme 프로그램을 실행해서 Name = "ReverseCore", Serial = "B6C9DAC9" 로 입력해보면
아래와 같이 성공 메시지 박스가 출력됩니다.
<Fig. 14>
Serial 을 찾았으니 크랙은 성공했다고 할 수 있습니다.
그런데 Name 이 Serial 과 어떤 관계가 있을까요?
Name 에 다른 값을 주고 동일한 Serial 을 입력하면 틀렸다고 나옵니다.
역시나 Name 문자열을 기반으로 Serial 을 그때 그때 생성하는 알고리즘입니다.
(continue)
'analysis' 카테고리의 다른 글
| 키로거(KeyLogger) 분석 (3) (4) | 2009/05/03 |
|---|---|
| 키로거(KeyLogger) 분석 (2) (50) | 2009/05/03 |
| 키로거(KeyLogger) 분석 (1) (14) | 2009/04/24 |
| Lena's Reversing for Newbies #10 (2) (7) | 2009/03/18 |
| Lena's Reversing for Newbies #10 (1) (16) | 2009/03/17 |
| abex' crackme #2 (2) (27) | 2009/03/01 |
| abex' crackme #2 (1) (32) | 2009/02/28 |
| abex' crackme #1 (34) | 2009/02/28 |
| "Hello World!" - 내 생애 첫 디버깅 (3) (40) | 2009/02/28 |
| "Hello World!" - 내 생애 첫 디버깅 (2) (6) | 2009/02/28 |
| "Hello World!" - 내 생애 첫 디버깅 (1) (50) | 2009/02/26 |
abex,
crack,
crackme,
debugger,
debugging,
it,
MSVBVM60.dll,
N code,
OllyDbg,
P code,
patch,
Reverse Engineering,
Reversing,
RT_MainStruct,
serial key,
ThunRTMain,
VB,
VB 파일 포멧,
Visual Basic,
디버거,
디버깅,
리버스 엔지니어링,
리버싱,
크랙미
abexcm2-voiees.exex
