MFC(Microsoft Foundation Classes)
- 마이크로 소프트에서 제공하는 클래스의 집합
- 통합 개발 환경을 위한 Visual C++에 부속되는 클래스 라이브러리
- 윈도우 어플리케이션을 개발에 적합한 환경으로 제공되는 프레임 워크
※ 리버스 엔지니어링에 필요한 MFC 구조
메시지 처리 방식
- MFC는 객체 지향언어로 설계돼 있고, C++ 방식을 그대로 따른다
- 이벤트 처리 방식을 이용해 각 프로시저가 만들어져 각 이벤트를 처리하는 함수에 대한 분석이 필요하다
- 프로시저에 해당하는 핸들러를 찾는 것이 가장 중요
초기화 루틴 찾기
- MFC 프로그램이 초기화 될 때 많은 액션을 취하는 경우가 많다
- MFC 초기화 루틴을 찾을 수 있어야한다
rdata + virtual function
- MFC는 클래스로 구성되어 있다
- 가상 함수와 PE Header에 대한 지식이 필요하다 (.rdata section)
MFC 기본 골격
- MFC는 각종 오브젝트가 모두 클래스로 구현되어 있다
- 개발자들은 MFC의 AFX 클래스에서 원하는 기능을 상속 받아 클래스를 생성하는 식으로 쉽게 코딩할 수 있다
- 이러한 부분은 어셈블리 코드로 쉽게 파악하기 힘들다
- API로 개발한 것 보다 세부적인 위치나 특성을 훨씬 파악하기 힘든 편이다
- MFC로 된 프로그램에서 원하는 기능을 포착하려면 MFC의 간단한 구조를 이해하고 메시지 핸들러나 가상 함수 위치 파악하는 작업이 필요하다
- 메시지 처리 방식, 초기화 루틴 같은 경우 흐름에 따라 봐야한
MFC로 개발 되었는지 여부 확인
MFC로 개발되었는지 확인 하는 방법으로 PE 도구 확인
- PE Header를 봤을 떄 MFC42.dll 등의 MFC 관련 DLL을 임포트하는지 확인할 수 있다
- 프로텍터나 패커등으로 패킹된 경우 IAT 정보를 알 수 없을 때
- 언패킹해서 확인
- 메모리에 올라갔을 때 안의 코드를 디스어셈블해 보면 MFC 관련 클래스가 .rdata 섹션에서 확인한다
MFC 라이브러리 등록
MFC 리버스 엔지니어링 할 때 가장 먼저 작업해 둬야할 내용
- MFC는 심볼에 해당하는 lib 파일이 모두 함께 배포된다
- 디버깅 시 심볼 파일이 있으면 작업 속도가 훨씬 빠르기 떄문에 심볼 파일을 등록해둔다
※ OllDBG -> dbug -> select Improt Libraries 메뉴에 있다 여기서 필요한 lib 파일 추가
MFC 초기화 루틴 찾기
기본으로 다이얼 로그 프로젝트 시 OnInitDialog()에서 이뤄진다
- IDA에서 ImportTable로 이동하는 MFC42.DLL의 OnInitDialog()함수를 분석해준다
- MFC 프로젝트를 생성시CMFC프로젝트명 이라는 클래스가 생성되고 CMFC프로젝트명Dlg라는 클래스안에서 작업하게 된다
- OnInitDialog()는 CDialog의 OnInitDialog()에서 출발하고 CDialog는 MFC 기본 클래스로 lib 파일에 의한 위치 파악할 수있다
- OnInitDialog() 호출 되는 위치를 파악해서 InitDialog()를 찾을 수 있다
버튼 핸들러 찾아내기
- MFC 핸들러로 생성된 함수는 대부분 가상 함수이다 -> .rdata 섹션에 테이블이 보관된다
- 상속의 개념에 따라 함수의 엔트리 포인트가 push ebp등으로 시작되지 않을 수 있다
- 바이너리로 변환됐을 때 컴파일러는 이 함수를 완전한 함수로 취급하지 않는다
- 엔트리 포인트 패턴을 생각하고 찾으면 영원히 못찾는다!!!
[EX]
[1] 일반적인 엔트리 | [2] 실제 핸들러 코드 |
push ebp mov ebp, esp MFC 핸들러는 이런식으로 코드를 만들지 않는다 |
sub ebp, 200 mov ecx, dword ptr ss:[esp+204] sub가 바로 나오거나 mov가 바로 나오거나 한다 |
- 메시지 맵의 구조체 모양이 바이너리에 그대로 생성되기 때문에 해당 구조체 규칙을 파악할 필요가있다
struct AFX_MSGMAP _ENTRY(
UINT nMessage; // 윈도우 메시지(WM_PAINT, WM_CREATE 등)
UINT nCode; // control code or WM_NOTIFY code (new in MFC 3.0)
UINT nID; // 메시지를 발생시킨 커트롤 ID(윈도우 메시지일 경우 : 0)
UINT nLastID; // 컨트롤 ID의 범위를 나타내는 entry(new in MFC 3.0)
UINT_PTR nSig; // nMessage에 해당되는 함수의 시그니처
AFX_PMSG pfn; // nMessage를 처리하는 함수의 포인터
);
핸들러를 하나 만들 때마다 위 구조체가 개수대로 생성된다 그리고 마지막 항목이 핸들러 번지가된다
하지만! 메시지 맵 구조체는 셀 수 없이 많이 생성된다 -> 힌트는 리소스 ID
- nID와 nLastID에는 이벤트를 생성했을 때 리소스 ID가 들어간다
- 따라서 리소스 해커 같은 리버싱 보조 툴을 이용해 리소스 ID를 알아낸 뒤 해당 리소스 ID를 지닌 구조체를 알아낸다
- AFX_PMSG pfn에 대한 값을 찾아내는 것을 궁극적인 목표로 삼으면된다!!
[참고 도서] 리버스 엔지니어링 바이블 - 코드 재창조의 미학
'개인 공부 > 리버싱' 카테고리의 다른 글
Reverse Engineering-12(FSC_Level2) (0) | 2023.05.18 |
---|---|
Reverse Engineering-11(FSC_Level1) (0) | 2023.05.11 |
Reverse Engineering-009(strcat ~ strlwr) (1) | 2023.04.27 |
Reverse Engineering-008(strcpy) (0) | 2023.04.23 |
Reverse Engineering-008(반복문) (0) | 2023.04.19 |