1. Guidelines to MFC reversing

    Tools - References
    Reversing Microsoft Visual C++ Part II: Classes, Methods and RTTI

    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.

    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.

    This is a standard C source code for windows:
    LRESULT CALLBACK DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
            case WM_COMMAND:
                    case IDC_ABOUT:
                            DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), NULL, (DLGPROC)MainDialogProc, 0);
                            // ...
    Instead this is source code that uses MFC:
    class CAboutDlg : public CDialog
    // Dialog Data
            enum { IDD = IDD_ABOUTBOX };
            virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
    // Implementation
    CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)  //CAboutDlg::IDD is dialog ID          
    void CAboutDlg::DoDataExchange(CDataExchange* pDX)
    BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) //Dialog Message Map: is like DialogProc
    // App command to run the dialog
    void CProvaRevApp::OnAppAbout()
            CAboutDlg aboutDlg;
    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                 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");
                    nReturnCode = pThread->ExitInstance();
                    goto InitFailure;
            nReturnCode = pThread->Run();
            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 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 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 loc_7831D334:
    .text:7831D334                 mov     eax, [esi]
    .text:7831D336                 mov     ecx, esi
    .text:7831D338                 call    dword ptr [eax+5Ch]
    .text:7831D33B loc_7831D33B:  
    .text:7831D33B                 mov     ebx, eax
    .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)