리버싱 엔지니어링/리버싱 핵심 원리
8. abex’ crackme #2
흰싸라기
2022. 3. 13. 23:53
8.1 abex’ crackme #2 실행

- 시리얼 키(serial key)를 알아내느 프로그램
- 전형적인 crackme의 형태
- Name과 Serial을 적절히 입력 후 [Check] 버튼

- “Wrong serial!” 메시지 박스 출력
8.2 Visual Basic 파일 특징
8.2.1 VB 전용 엔진
- VB 전용 엔진 사용 : MSVBVM60.dll (Microsoft Visual Basic Virtual Machine 6.0)
- 사용 예 : 메시지 박스 호출 시 소스코드에서 MsgBox() 함수 사용, 함수 내부에서 Win32 API인 user32.dll!MessageBoxW() 함수를 호출하는 방식으로 동작
8.2.2 N(Native) code, P(Pseudo) code
- 컴파일 옵션에 따라 N code와 P code로 컴파일이 가능
- N code : 일반적인 디버거에서 해석 가능한 IA-32 Instruction 사용
- P code : 인터프리터(interpreter) 언어 개념으로 VB 엔진으로 가상머신을 구현하여 자체적으로 해석 가느안 명령어(바이트 코드) 사용
- P code의 정확한 해석은 VB 엔진을 분석해야함
- Java, Python과 유사한 형태
8.2.3 Event Handler
- VB는 주로 GUI 프로그래밍을 할 때 사용됨
- Windows 운영체제의 Event Driven 방식으로 작동
- 따라서, main()이 아닌 event handler에 사용자 코드가 존재
8.2.4 undocumented 구조체
- VB에서 사용되는 각종 정보(Dialog, Module, Function 등)은 내부적으로 구조체 형식으로 파일에 저장
- 이러한 구조를 정식 공개하지 않아 VB 파일의 디버깅에 어려움이 존재
8.3 디버깅 시작
OllyDbg를 실행시켜 abex’ crackme #2 디스어셈 코드 살펴보기

- PUSH 401E14 : RT_MainStruct 구조체 주소(401E14)를 스택에 입력
- RT_MainStruct 구조체
- MS에서 공개하지 않았지만, 리버서들이 분석하여 공개함
- 해당 구조체 멤버 : 또 다른 구조체의 주소들
- RT_MainStruct 구조체

- EP 코드에서 VB 엔진의 메인함수(ThunRTMain)를 호출함을 알 수 있음
- 40123D 주소의 명령어(CALL)에 이해 401232 주소의 명령어(JMP)가 실행
- JMP 명령어에 의해 메인 함수로 이동
- 스택에 입력한 401E14 값은 함수의 파라미터
- 구조체를 통해 프로그램 실행에 필요한 모든 정보를 얻어옴
- 간접 호출 : CALL 401232
- 메인 함수로 직접 가는 것이 아는 중간의 JMP 명령을 통해서 가능 방식
8.4 crackme 분석
8.4.1 문자열 검색
: 패치해야 할 코드를 찾기 위해 에러 메시지 박스와 그 문자열을 이용하기
1) 문자열 검색 기능(All referenced text strings) 사용
- 실행했을 때 본 메시지 박스의 문자열 찾기

2) 찾은 문자열 더블클릭하여 주소 이동

- 시리얼 키와 사용자가 입력한 키를 비교하는 코드를 예상할 수 있음
- 즉, 위 코드 전후에 문자열 비교 코드 및 키가 맞았을 때 성공 메시지 박스 호출 코드가 존재할 것임
3) 분기문 찾기
- 주소 403458에서 스크롤을 올리다보면 분기문을 발견할 수 있음

- TEST AX, AX : AX를 비교 후 조건 분기(JE)에 이해 참, 거짓으로 분기
- AX : 함수 리턴값
- TEST : AND 연산과 동일, 두 오퍼레이터 중 하나가 0이면 ZF = 1로 세팅됨
- JF(조건 분기) : ZF =1이면 점프
- AX는 함수 리턴 값, 403329 주소에서 호출한 함수(__vbaVarTstEq) 리턴 값을 비교한 것
8.4.2 문자열 주소 찾기
- 403329 주소 함수가 문자열 비교 함수라면 그 위의 PUSH 명령어는 비교 함수의 파라미터, 즉 비교 문자열이 될 것

1) 403329 주소까지 디버깅 진행

- 403321 주소의 SS:[EBP-44]
- SS는 스택 세그먼트, EBP는 베이스 포인터
- 즉, SS:[EBP-44] 주소는 스택 내의 주소 → 함수에서 선언된 로컬 객체의 주소
- 로컬 객체는 스택 영역에 저장됨
2) 스택 확인
- EDX : [EBP-44], EAX : [EBP-34] 확인

3) 스택에 저장된 메모리 주소로 이동

4) 문자열 표시 (마우스 우측 - Long - Address with ASCII dump)

- EDX는 실제 serial 값이고, EAX는 사용자가 입력한 serial 값임을 알 수 있음
5) 실행
- Name = “hello”, Serial = “CCC9D0D0”
