공부/리버싱핵심원리

8장 abex' crackme #2 (abexcm2) - Retry

이전에 못끝낸 8장을 마무리하려고 한다. 하지만 감을 잃어버렸기에 처음부터 다시 진행하도록 하자.

 


1. Visual Basic 파일 특징

PEID로 분석을 하면 Microsoft Visual Basic 으로 제작되었음을 알 수 있다.

Visual Basic은 초등학교 때 이후로 공부를 한 적이 없는 언어이다. 간단하게 VB의 특징을 짚고 넘어가보자.

 

VB 전용 엔진

VB 파일은 MSVBVM60.dll (MicroSoft Visual Basic Virtual Machine) 이라는 VB 전용 엔진을 사용한다. The Thunder Runtime Engine 이라고도 불리운다고 하니, 알아두자.

 

메시지 박스를 사용한다면 VB 소스코드에서 MsgBox() 함수를 사용한다.

이 때 VB컴파일러는 MSVBVB60.dll!rtcMsgBox()함수가 호출되도록 만든다.

이 함수 내부에서 Win32 API인 user32.dll!MessageBoxW() 함수를 호출한다.

물론 VB 소스코드에서 user32.dll!MessageBoxW() 함수를 직접 호출할 수도 있긴 하다.

 

N code, P code

N code : Native Code를 의미한다.

              일반적인 디버거에서 해석 가능한 IA-32 Instruction을 사용한다.

P code : Pseudo Code를 의미한다. 인터프리터 개념이라고 생각하면 편하다.

.             VB엔진으로 가상머신을 구현하여 자체적으로 해석 가능한 명령어를 사용한다.

              자체적으로 해석 가능한 명령어란 바이트 코드를 의미한다.

 

VB 파일은 컴파일 옵션에 따라 N code와 P code로 컴파일이 가능하다.

뭐 N code는 많이 보겠지만, P code를 완벽하게 해석하려면 직접 에뮬레이터를 구현해야 한다.

 

P code와 유사한 형태는 Java, Python 등이 있다.

 

Event Handler

우선 VB는 주로 GUI 프로그래밍을 할 때 사용이 된다. (콘솔 + VB = ?)

그래서 개발 IDE 인터페이스 자체도 GUI에 최적화되어있다.

 

즉 VB 프로그램은 Windows의 Event Driven 방식으로 동작하기 때문에 main()에 사용자 코드가 존재하는 것이 아니라, 각 Event Handler에 사용자 코드가 존재하게 된다.

 

abexcm2의 경우 Check 버튼에 사용자 코드가 존재할 것이라고 예측할 수 있겠다.

 

Undocumented Structure

Microsoft는 VB 구조체 정보를 공개하지 않았다. 그래서 어렵다.

 


2. Start Debugging

F9를 누르면 EntryPoint로 간다.

EP에서 부터 확인을 한다면, 먼저 401E14를 스택에 PUSH한다.

이후 ThunRTMain 함수로 JMP 한다. 이 함수는 VB엔진의 메인함수이다.

 

RT_MainStruct

여기서 PUSH 해준 401E14는 무엇일까

바로 RT_MainStruct 구조체이다. 

Undocumented 이지만, 실력있는 리버서들이 이미 분석을 마쳐놓은 상태라고 한다...!

 

RT_MainStruct 구조체의 멤버는 또 다른 구조체의 주소들이다. 즉 VB 엔진은 파라미터로 넘어온 RT_MainStruct 구조체를 가지고 프로그램의 실행에 필요한 모든 정보를 얻는다.

 

간접호출

40123D 주소를 보면 CALL 401232 (JMP.&ThunRTMain) 로 이동한다.

그래서 40123D -(Call)-> 401232 -(JMP)-> ThunRTMain() 의 방식을 거치게 된다.

이런 방법은 VC++, VB 컴파일러에서 많이 사용한다고 한다. 왜 간접호출 방식을 사용하는걸까 ...

 

ThunRTMain 함수

이 함수는 어떻게 생겼을까

주소가 아예 변했음을 알 수 있다. 이 주소는 MSVBVM60.dll의 주소 영역이다.

분석해야하는 프로그램의 주소가 아닌, VB 엔진의 코드라는 것이다. 그러니 여기서는 현재는 분석을 할 것이 없다.

 

 

 

 


3. crackme 분석

현재 내가 패치를 할 코드는 어디있을까

프로그램을 실행하고 틀린다면 이렇게 Wrong Serial이 출력되는 것을 알 수 있다.

그래서 에러 메시지 박스와 그 문자열을 이용하여 문제를 해결할 수 있겠다.

 

문자열 검색

문자열 검색 기능을 이용하여 확인해보자.

이렇게 실제 작동될 때 보이는 문자열들이 확인된다. Wrong serial을 더블클릭하여 해당 주소로 들어가보자.

메시지박스의 타이틀로 들어가지는 문자열이어야 한다.

확인을 해보면 정말 "Wrong serial!", "Nope, this serial is wrong!", rtcMsgBox 함수 호출까지 보인다.

 

조금만 거꾸로 생각해보자. 여기서 Wrong serial 이란는 문자열과, rtcMsgBox가 보인다는 것은, 개발자가 일부러 코드를 꼬아서 짜지 않는 이상 좀 더 위의 코드에서 조건문이 나왔을 가능성이 크다. ( 입력한 키와 실제 키가 같냐 다르냐 )

그리고 실제로 위의 부분에서 Jump Equal 코드가 보이는 것을 확인할 수 있다.

즉 조건문을 만족시킨다면 403408로 점프를 하게 되는 것이다. 그 기준은 한 줄 위에서 실행된 TEST 명령으로 인해서 변한 ZF의 값이 된다. 만약 ZF가 1이라면 점프를 하게 된다.

 

TEST연산에 대해 좀 더 자세히 알아보자면,

논리비교 연산으로 AND연산과 동일하다. 오퍼랜드의 값은 변화시키지 않고 SF, ZF, PF의 값을 변화시킨다.

 

사실 여기까지 하면 문제는 풀었다고 생각할 수 있다. 왜냐하면 JE 명령을 그냥 NOP 명령으로 바꾸면 되기 때문이다. 아니면 JNE도 괜찮겠군

 

문자열 주소 찾기

하지만 좀 더 분석을 하기 위해 문자열 주소도 찾아보자.

먼저 문자열을 비교를 해야하는데 이걸 이용하면 문자열을 찾을 수 있겠다.

 

TEST 명령 위를 보자.

__vbaVarTstEq 함수를 호출하고 있다. 호추 코드 위를 보면

EBP-44와 EBP-34를 각각 EDX, EAX에 LEA 연산을 진행한다.

LEA 연산은 주소값을 집어넣게 된다.

SS:[EBP-44]는 결국 스택 세그먼트이므로, 스택 내의 주소를 의미하게 된다.

 

스택 내이다. 0019F288 = SS:[EBP-34], 0019F298 = SS:[EBP-44] 이다.

 레지스터에도 값이 이렇게 들어가져 있다.

 

그럼 덤프에서 실제로 값이 어떻게 들어가져있는 지를 확인해보자.

 

VB는 가변 길이 문자열 타입을 채택하기에 16바이트의 데이터 중 바로 문자열이 나타나지 않고, 세 번째 섹션처럼 값이 다른 부분이 존재한다. 가변 길이 문자열 타입은 내부에 동적으로 할당한 실제 문자열 버퍼 주소를 가지고 있다.

 

실제로 19F288의 저 값을 메모리 주소로 Follow DWORD를 하게 된다면

이런 값이 존재한다. 처음보는 문자열이다. 아마 암호화된 정답 serial이지 않을까 싶다.

그리고 19F298을 Follow DWORD를 한다면 위와 같이 asdf가 뜨게 된다. 처음에 입력했던 serial이다.

 

그래서 name이 qwer일 때의 답은 D5DBC9D6임을 알 수 있다.

무난히 재미있음을 알 수 있다.

 

 

 

Serial 생성 알고리즘

답은 찾았지만, Serial 문자열 생성 알고리즘도 분석해보자. 

프로그램에 대해 약간의 게싱을 해보자면, check 버튼을 눌렀을 때의 Event Handler가 Serial 문자열 생성 알고리즘 + 결과 출력이라고 생각할 수 있다.

 

그러니 함수 시작부분으로 거슬러 올라가보자. 

그 부분은 바로 이곳이다. 주소는 402ED0 이다.

 

VB에서 문자열을 읽어 들일 때 API를 사용할 거라고 생각하였다. 

그러니 CALL 명령을 중심으로, 파라미터와 리턴 값을 신경쓰며 디버깅을 진행해보자.