Tools - References
Reversing Microsoft Visual C++ Part II: Classes, Methods and RTTI http://www.openrce.org/articles/full_view/23
IDA http://www.hex-rays.com/idapro
Crackme http://quequero.org/uicwiki/images/Lezione6.zip
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.
Introduction
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.
Essay
This is a standard C source code for windows:
Code:
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:
Code:
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:
Code:
.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.
Code:
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:
Code:
.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:
Code:
.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)
...