본문 바로가기

개인 공부/리버싱

Reverse Engineering-10 (MFC)

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에 대한 값을 찾아내는 것을 궁극적인 목표로 삼으면된다!!

 

 

[참고 도서]  리버스 엔지니어링 바이블 - 코드 재창조의 미학