MFC 리버싱하기
http://quequero.org/2008/08/guidelines-to-mfc-reversing/
Guidelines to MFC Reversing
Software developed with MFC may import MFC80U.dll (MFC80U is the name of the last version of the dll, as I’m writing), it depends on the type of compilation: as a static library or as a shared DLL.
I’ll analyze a software which imports the dll and has debug infos, just to make the job easier.
Once you understand MFC in this way, you can analyze MFC software compiled statically just adding to IDA the signatures of MFC and VisualC.
Prologue: What is MFC?
The Microsoft Foundation Classes Library (also Microsoft Foundation Classes or MFC) is a library that wraps portions of the Windows API in C++ classes, including functionality that allows to use a default application framework. Classes are defined for many of the handle-managed Windows objects and also for predefined windows and common controls.
Tools – References
IDA
Reversing Microsoft Visual C++ Part II: Classes, Methods and RTTI
Crackme
Essay
This is a standard C source code for windows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | LRESULT CALLBACK DialogProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_ABOUT: DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), NULL, (DLGPROC)MainDialogProc, 0); break ; // ... } } } |
Instead this is source code that uses MFC:
class CAboutDlg : public CDialog { public : CAboutDlg(); // Dialog Data enum { IDD = IDD_ABOUTBOX }; protected : virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support // Implementation protected : DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) //CAboutDlg::IDD is dialog ID { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) //Dialog Message Map: is like DialogProc END_MESSAGE_MAP() // App command to run the dialog void CProvaRevApp::OnAppAbout() { CAboutDlg aboutDlg; aboutDlg.DoModal(); } |
How you can imagine the disasm of MFC software is harder to understand.
MFC Main
This is the Main disasm of our target:
.text : 00401CBB public start .text : 00401CBB call ___security_init_cookie .text : 00401CC0 jmp ___tmainCRTStartup .text : 004019FB ___tmainCRTStartup proc near ; CODE XREF: start+5�j .text : 004019FB .text : 004019FB push 5Ch .text : 004019FD push offset unk_403DD8 .text : 00401A02 call __SEH_prolog4 ;... other initialization code .text : 00401B3E push ecx ; nShowCmd .text : 00401B3F push eax ; lpCmdLine .text : 00401B40 push ebx ; hPrevInstance .text : 00401B41 push 400000h ; hInstance .text : 00401B46 call _wWinMain@ 16 ; wWinMain(x,x,x,x) ; int __stdcall wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd) _wWinMain@ 16 proc near jmp ?AfxWinMain@ @YGHPAUHINSTANCE__@ @0PA_WH @Z ; AfxWinMain(HINSTANCE__ *,HINSTANCE__ *,wchar_t *, int ) _wWinMain@ 16 endp |
As you can see WinMain calls AfxWinMain.
If you have VisualStudio you can see MFC source code, in this article I’ll report only the functions we’ll need.
int AFXAPI AfxWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, int nCmdShow) { ASSERT(hPrevInstance == NULL); int nReturnCode = -1; CWinThread* pThread = AfxGetThread(); CWinApp* pApp = AfxGetApp(); // AFX internal initialization if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow)) goto InitFailure; // App global initializations (rare) if (pApp != NULL && !pApp->InitApplication()) goto InitFailure; // Perform specific initializations if (!pThread->InitInstance()) { if (pThread->m_pMainWnd != NULL) { TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n" ); pThread->m_pMainWnd->DestroyWindow(); } nReturnCode = pThread->ExitInstance(); goto InitFailure; } nReturnCode = pThread->Run(); InitFailure: AfxWinTerm(); return nReturnCode; } |
This is the disasm of AfxWinMain:
.text : 7831D2D2 public AfxWinMain .text : 7831D2D2 AfxWinMain proc near .text : 7831D2D2 push ebx .text : 7831D2D3 push esi .text : 7831D2D4 push edi .text : 7831D2D5 or ebx , 0FFFFFFFFh .text : 7831D2D8 call AfxGetModuleThreadState .text : 7831D2DD mov esi , [ eax + 4 ] ;pThread .text : 7831D2E0 call AfxGetModuleState .text : 7831D2E5 push [ esp +0Ch+arg_C] .text : 7831D2E9 mov edi , [ eax + 4 ] ;pApp .text : 7831D2EC push [ esp +10h+arg_8] .text : 7831D2F0 push [ esp +14h+arg_4] .text : 7831D2F4 push [ esp +18h+arg_0] .text : 7831D2F8 call AfxWinInit .text : 7831D2FD test eax , eax .text : 7831D2FF jz short loc_7831D33D .text : 7831D301 test edi , edi .text : 7831D303 jz short loc_7831D313 .text : 7831D305 mov eax , [ edi ] .text : 7831D307 mov ecx , edi .text : 7831D309 call dword ptr [ eax +98h] .text : 7831D30F test eax , eax .text : 7831D311 jz short loc_7831D33D .text : 7831D313 .text : 7831D313 loc_7831D313 : .text : 7831D313 mov eax , [ esi ] .text : 7831D315 mov ecx , esi .text : 7831D317 call dword ptr [ eax +58h] .text : 7831D31A test eax , eax .text : 7831D31C jnz short loc_7831D334 .text : 7831D31E cmp [ esi +20h], eax .text : 7831D321 jz short loc_7831D32B .text : 7831D323 mov ecx , [ esi +20h] .text : 7831D326 mov eax , [ ecx ] .text : 7831D328 call dword ptr [ eax +68h] .text : 7831D32B .text : 7831D32B loc_7831D32B : .text : 7831D32B mov eax , [ esi ] .text : 7831D32D mov ecx , esi .text : 7831D32F call dword ptr [ eax +70h] .text : 7831D332 jmp short loc_7831D33B .text : 7831D334 .text : 7831D334 loc_7831D334 : .text : 7831D334 mov eax , [ esi ] .text : 7831D336 mov ecx , esi .text : 7831D338 call dword ptr [ eax +5Ch] .text : 7831D33B .text : 7831D33B loc_7831D33B : .text : 7831D33B mov ebx , eax .text : 7831D33D .text : 7831D33D loc_7831D33D : .text : 7831D33D call AfxWinTerm .text : 7831D342 pop edi .text : 7831D343 pop esi .text : 7831D344 mov eax , ebx .text : 7831D346 pop ebx .text : 7831D347 retn 10h .text : 7831D347 AfxWinMain endp |
In the code there are calls as call [eax+XXh]: actually the call to AfxGetApp (and AfxGetThread) gives back a pointer to a structure that has offsets of all functions used by MFC framework.
In this case edi (pApp) holds the offset of 405498, which value is 40349C VA, where the virtual functions table of CWinApp is stored:
.rdata : 0040349C off_40349C dd offset ?GetRuntimeClass @CWinApp@ @UBEPAUCRuntimeClass@ @XZ ;CWinApp::GetRuntimeClass(void) .rdata : 004034A0 dd offset sub_401010 .rdata : 004034A4 dd offset nullsub_1 .rdata : 004034A8 dd offset nullsub_2 .rdata : 004034AC dd offset nullsub_1 .rdata : 004034B0 dd offset ?OnCmdMsg @CCmdTarget@ @UAEHIHPAXPAUAFX_CMDHANDLERINFO@ @ @Z ; CCmdTarget::OnCmdMsg(uint, int ,void *,AFX_CMDHANDLERINFO *) .rdata : 004034B4 dd offset ?OnFinalRelease @CCmdTarget@ @UAEXXZ ; CCmdTarget::OnFinalRelease(void) .rdata : 004034B8 dd offset ?IsInvokeAllowed @CCmdTarget@ @UAEHJ@ Z ; CCmdTarget::IsInvokeAllowed(long) .rdata : 004034BC dd offset ?GetDispatchIID @CCmdTarget@ @UAEHPAU_GUID@ @ @Z ; CCmdTarget::GetDispatchIID(_GUID *) .rdata : 004034C0 dd offset ?GetTypeInfoCount @CCmdTarget@ @UAEIXZ ; CCmdTarget::GetTypeInfoCount(void) .rdata : 004034C4 dd offset ?GetTypeLibCache @CCmdTarget@ @UAEPAVCTypeLibCache@ @XZ ; CCmdTarget::GetTypeLibCache(void) .rdata : 004034C8 dd offset ?GetTypeLib @CCmdTarget@ @UAEJKPAPAUITypeLib@ @ @Z ; CCmdTarget::GetTypeLib(ulong,ITypeLib * *) .rdata : 004034CC dd offset sub_401000 ;....................................................... |
Now a question should pop up in your mind: where does MFC get the address? A quick glance at the reference with IDA…
.text : 004023B0 sub_4023B0 proc near .text : 004023B0 push 0 .text : 004023B2 mov ecx , offset dword_405498 .text : 004023B7 call ??0CWinApp@ @QAE@ PB_W @Z ; CWinApp::CWinApp(wchar_t const *) .text : 004023BC push offset sub_4023F0 ; void (__cdecl *)() .text : 004023C1 mov dword_405498, offset off_40349C ;<-- this is our offset .text : 004023CB call _atexit .text : 004023D0 pop ecx .text : 004023D1 retn .text : 004023D1 sub_4023B0 endp |
This VA, 004023B0, is present in a structure
1 2 3 4 5 6 7 | .rdata : 00403304 unk_403304 db 0 .rdata : 00403305 db 0 .rdata : 00403306 db 0 .rdata : 00403307 db 0 .rdata : 00403308 dd offset _pre_cpp_init .rdata : 0040330C dd offset ??__E_afxInitAppState@ @YAXXZ ; `dynamic initializer for '_afxInitAppState' '(void) .rdata : 00403310 dd offset sub_4023B0 |
which is pushed to __initterm, called before WinMain
.text : 00401AAC push offset unk_403314 .text : 00401AB1 push offset unk_403304 .text : 00401AB6 call _initterm |
After this excursus, let’s go back to AfxWinMain:
call dword ptr [eax+98h] (40349C + 98 = 00403534) calls…
.text : 00403534 dd offset ?InitApplication @CWinApp@ @UAEHXZ ; CWinApp::InitApplication(void) |
…while call dword ptr [eax+58h], that is pThread->InitInstance, calls the function:
1 | .rdata : 004034F4 dd offset sub_401030 |
This function shows the dialog window, here is the main part of the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | .text : 00401030 sub_401030 proc near .text : 00401030 push ebp .text : 00401031 mov ebp , esp ;.......................................................................... .text : 0040109F call sub_401130 ;-------------------------------------------------------------------------- ;entrato nella call .text : 00401155 push 0 ; lpIconName .text : 00401157 push 66h ; Dialog ID .text : 00401159 mov ecx , esi .text : 0040115B call ??0CDialog@ @QAE@ IPAVCWnd@@ @Z ; CDialog::CDialog(uint,CWnd *) .text : 00401160 mov [ esp +14h+var_4], 0 .text : 00401168 mov dword ptr [ esi ], offset off_403744 ;virtual functions table offset which is store ; in CDialog.DoModal -> CDialog__PreModal -> AfxHookWindowCreate .text : 0040116E call ?AfxGetModuleState@ @YGPAVAFX_MODULE_STATE@ @XZ ; AfxGetModuleState(void) ;exit the call ;--------------------------------------------------------------------------- .text : 004010A4 lea edx , [ esp + 8 +arg_4] .text : 004010A8 mov [ esp + 8 +arg_88], 0 .text : 004010B3 mov ecx , edx .text : 004010B5 mov [ esi +20h], edx .text : 004010B8 call ?DoModal @CDialog@ @UAEHXZ ; CDialog::DoModal(void) .text : 004010BD lea ecx , [ esp + 8 +arg_4] .text : 004010C1 mov [ esp + 8 +arg_88], 0FFFFFFFFh .text : 004010CC call ??1CDialog@ @UAE@ XZ ; CDialog::~CDialog(void) ;.......................................................................... .text : 004010E3 mov esp , ebp .text : 004010E5 pop ebp .text : 004010E6 retn |
Get MESSAGE_MAP
But where is MESSAGE_MAP?: Message Map can be get from
BOOL CWnd::OnWndMsg( UINT message, WPARAM wParam, LPARAM lParam, LRESULT * pResult) { // .... const AFX_MSGMAP* pMessageMap; pMessageMap = GetMessageMap(); // .... if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, message, 0, 0)) != NULL) // ... } |
This is the disasm
.text : 78312E91 mov eax , [ edi ] ; eax = 403744 .text : 78312E93 mov ecx , edi .text : 78312E95 call dword ptr [ eax +30h] ; eax+30h = 00403774 = GetMessageMap() ;.rdata:00403774 dd offset sub_4011E0 ;................................................................... .text : 78312F1B push 0 .text : 78312F1D push 0 .text : 78312F1F jnb short loc_78312F67 .text : 78312F21 push [ ebp +arg_0] ;messagge .text : 78312F24 push dword ptr [ esi + 4 ] ; lpEntries (0040362C) .text : 78312F27 call AfxFindMessageEntry |
The call in 78312E95 leads us to:
;GetMessageMap() .text : 004011E0 mov eax , offset off_403628 ;eax = pMessageMap .text : 004011E5 retn ;---------------------------------------------------------------- ;pMessageMap .rdata : 00403628 off_403628 dd offset ?GetThisMessageMap @CDialog@ @KGPBUAFX_MSGMAP@ @XZ .rdata : 00403628 ; CDialog::GetThisMessageMap(void) .rdata : 0040362C dd offset unk_403580 ;pMessageMap->lpEntries |
At 403580 there’s the MESSAGE_MAP of this dialog.
So we can get the MessageMap quickly this way:
- Find before a call to CDialog:DoModal an istruction like this: mov dword ptr [esi], offset off_XXXXXX (it is used to load virtual functions table).
- Add 0×30 to that offset to get GetMessageMap function: into that function, look for the instruction mov eax, offset off_XXXXXX, where eax is pMessageMap
- Add 4 to pMessageMap to get Dialog MessageMap
Now an example. This is the software resource:
CONTROL "Register", 1006, BUTTON, //1006 = 0x3ee CONTROL "About", 1007, BUTTON, //1007 = 0x3ef CONTROL "Cancel", 1008, BUTTON, //1008 = 0x3f0 |
And this is its MESSAGE_MAP, which is an array of structures
struct AFX_MSGMAP_ENTRY { UINT nMessage; // windows message UINT nCode; // control code or WM_NOTIFY code UINT nID; // control ID (or 0 for windows messages) UINT nLastID; // used for entries specifying a range of control id's UINT_PTR nSig; // signature type (action) or pointer to message # AFX_PMSG pfn; // routine to call (or special value) }; |
.rdata : 00403580 MESSAGE_MAP dd 112h .rdata : 00403584 dd 0 .rdata : 00403588 dd 0 .rdata : 0040358C dd 0 .rdata : 00403590 dd 1Eh .rdata : 00403594 dd offset sub_4012D0 .rdata : 00403598 dd 0Fh .rdata : 0040359C dd 0 .rdata : 004035A0 dd 0 .rdata : 004035A4 dd 0 .rdata : 004035A8 dd 13h .rdata : 004035AC dd offset sub_401370 .rdata : 004035B0 dd 37h .rdata : 004035B4 dd 0 .rdata : 004035B8 dd 0 .rdata : 004035BC dd 0 .rdata : 004035C0 dd 28h .rdata : 004035C4 dd offset sub_401450 .rdata : 004035C8 dd 111h .rdata : 004035CC dd 0 .rdata : 004035D0 dd 3EFh .rdata : 004035D4 dd 3EFh .rdata : 004035D8 dd 38h .rdata : 004035DC dd offset sub_401460 .rdata : 004035E0 dd 111h .rdata : 004035E4 dd 0 .rdata : 004035E8 dd 3F0h .rdata : 004035EC dd 3F0h .rdata : 004035F0 dd 38h .rdata : 004035F4 dd offset sub_4014F0 .rdata : 004035F8 dd 111h .rdata : 004035FC dd 0 .rdata : 00403600 dd 3EEh .rdata : 00403604 dd 3EEh .rdata : 00403608 dd 38h .rdata : 0040360C dd offset sub_401510 .rdata : 00403610 dd 0 ... |
Every event has a structure where window ID and the function to use are stored.
IDC Script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | // mfc_message_map.idc version 0.2 by Pn 2008 #include <idc.idc> //Only some WM_ command are recognized static messageName(ptr, message) { if (message == 1) // WM_CREATE MakeComm(ptr, "WM_CREATE" ); else if (message == 2) // WM_DESTROY MakeComm(ptr, "WM_DESTROY" ); else if (message == 5) // WM_SIZE MakeComm(ptr, "WM_SIZE" ); else if (message == 0x10) // WM_CLOSE MakeComm(ptr, "WM_CLOSE" ); else if (message == 0x18) // WM_SHOWWINDOW MakeComm(ptr, "WM_SHOWWINDOW" ); else if (message == 0x0100) // WM_KEYDOWN MakeComm(ptr, "WM_KEYDOWN" ); else if (message == 0x0101) // WM_KEYUP MakeComm(ptr, "WM_KEYUP" ); else if (message == 0x0102) // WM_CHAR MakeComm(ptr, "WM_KEYCHAR" ); else if (message == 0x0110) // WM_INITDIALOG MakeComm(ptr, "WM_INITDIALOG" ); else if (message == 0x0111) // WM_COMMAND MakeComm(ptr, "WM_COMMAND" ); else if (message == 0x0112) // WM_SYSCOMMAND MakeComm(ptr, "WM_SYSCOMMAND" ); else if (message == 0x0113) // WM_TIMER MakeComm(ptr, "WM_TIMER" ); else if (message == 0x0116) // WM_INITMENU MakeComm(ptr, "WM_INITMENU" ); else if (message == 0x0117) // WM_INITMENUPOPUP MakeComm(ptr, "WM_INITMENUPOPUP" ); else if (message == 0x0126) // WM_MENUCOMMAND MakeComm(ptr, "WM_MENUCOMMAND" ); } static DefineStruct() { auto idStruct; idStruct = AddStrucEx(-1, "AFX_MSGMAP_ENTRY" ,0); if (idStruct == 0) return 0; if (AddStrucMember(idStruct, "nMessage" , 0, FF_DWRD|FF_DATA, -1, 4) != 0) { Warning( "\n1\n" ); DelStruc(idStruct); return 0; } if (AddStrucMember(idStruct, "nCode" , 4, FF_DWRD|FF_DATA, -1, 4) != 0) { Warning( "\n2\n" ); DelStruc(idStruct); return 0; } if (AddStrucMember(idStruct, "nID" , 8, FF_DWRD|FF_DATA, -1, 4) != 0) { Warning( "\n3\n" ); DelStruc(idStruct); return 0; } if (AddStrucMember(idStruct, "nLastID" , 12, FF_DWRD|FF_DATA, -1, 4) != 0) { Warning( "\n4\n" ); DelStruc(idStruct); return 0; } if (AddStrucMember(idStruct, "nSignature" , 16, FF_DWRD|FF_DATA, -1, 4) != 0) { Warning( "\n5\n" ); DelStruc(idStruct); return 0; } if (AddStrucMember(idStruct, "pFunction" , 20, FF_DWRD|FF_0OFF, -1, 4) != 0) { Warning( "\n6\n" ); DelStruc(idStruct); return 0; } return idStruct; } static GenerateMFCMap(addr) { auto idStruct, ptr, message, isOk; idStruct = GetStrucIdByName( "AFX_MSGMAP_ENTRY" ); if ( idStruct == -1) { idStruct = DefineStruct(); if (idStruct == 0) { Warning( "\nCannot declare the structure\n" ); return ; } } ptr = addr; isOk = 1; while ( Dword(ptr) != 0) { if (MakeStructEx(ptr, 24, "AFX_MSGMAP_ENTRY" ) == 0) { isOk = 0; break ; } messageName(ptr,Dword(ptr)); ptr = ptr + 24; } if (isOk == 0) { Warning( "\nCannot set the structure at %x\n" , addr); } else { Message( "Completed" ); } return ; } |
This is the disasm after I used the script on it:
.rdata : 00403580 stru_403580 AFX_MSGMAP_ENTRY <112h, 0 , 0 , 0 , 1Eh, offset sub_4012D0 > ; WM_SYSCOMMAND .rdata : 00403580 ; DATA XREF: .rdata:0040362C�o .rdata : 00403598 AFX_MSGMAP_ENTRY <0Fh, 0 , 0 , 0 , 13h, offset sub_401370 > .rdata : 004035B0 AFX_MSGMAP_ENTRY <37h, 0 , 0 , 0 , 28h, offset sub_401450 > .rdata : 004035C8 AFX_MSGMAP_ENTRY <111h, 0 , 3EFh, 3EFh, 38h, offset sub_401460 > ; WM_COMMAND .rdata : 004035E0 AFX_MSGMAP_ENTRY <111h, 0 , 3F0h, 3F0h, 38h, offset sub_4014F0 > ; WM_COMMAND .rdata : 004035F8 AFX_MSGMAP_ENTRY <111h, 0 , 3EEh, 3EEh, 38h, offset sub_401510 > ; WM_COMMAND .rdata : 00403610 db 0 |
Retrieve WM_COMMAND
The function BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo), precisely the function _AfxDispatchCmdMsg, handles WM_COMMAND event.
Actually if you set a bp on it you can see that after a button or a menu is clicked on, the debugger halts the execution. By stepping you can enter the function called for that event, without having to retrieve the MESSAGE_MAP.
'유용한 지식 자료들 > 기타' 카테고리의 다른 글
Indoor positioning system (0) | 2014.09.23 |
---|---|
HashSet HashMap 차이 정리 (0) | 2014.08.07 |
Decompilers (0) | 2013.08.12 |
virtualbox-host-only-network-cuckoo-sandbox-0-4-2/ (0) | 2013.07.29 |
리눅스 du 명령(디렉토리 용량 확인) 응용 (0) | 2013.07.16 |