I'm decompiling a MFC 4.0 application, and now loaded the MFCS42.PDB
from the MFC 4.2 source into IDA 7.0 (not having the MFC 4.0 source) to make it create the appropriate structs representing the many MFC classes and virtual function tables.
However, IDA seems to only create *Vtbl
structs for the very base classes, like CObject
, not for child classes like CCmdTarget
, which looks as follows:
struct CObjectVtbl // totally correct
{
CRuntimeClass *(__thiscall *GetRuntimeClass)(CObject *this);
void (__thiscall *~CObject)(CObject *this);
void (__thiscall *Serialize)(CObject *this, CArchive *);
void (__thiscall *AssertValid)(CObject *this);
void (__thiscall *Dump)(CObject *this, CDumpContext *);
};
struct __cppobj CObject // totally correct
{
CObjectVtbl *vfptr;
};
struct __cppobj CCmdTarget : CObject // wrong, makes it have only a CObject vftable
{
int m_dwRef;
IUnknown *m_pOuterUnknown;
unsigned int m_xInnerUnknown;
CCmdTarget::XDispatch m_xDispatch;
int m_bResultExpected;
CCmdTarget::XConnPtContainer m_xConnPtContainer;
AFX_MODULE_STATE *m_pModuleState;
};
In effect, this results in missing new virtual functions of CCmdTarget
, as it only references the CObjectVtbl
by inheriting from CObject
, but CCmdTarget
has 7 more methods.
I previously hand-crafted these structures (which is tedious as you can guess), and it should actually look more like this:
// CObject and CObjectVtbl same as above
struct CCmdTargetVtbl : CObjectVtbl // inherit to keep base methods
{
BOOL (__thiscall *OnCmdMsg)(CCmdTarget *this, UINT nID, int nCode, void *pExtra, void *pHandlerInfo);
void (__thiscall *OnFinalRelease)(CCmdTarget *this);
AFX_MSGMAP *(__thiscall *GetMessageMap)(CCmdTarget *this);
int field_20; // Don't know names yet
int field_24;
int field_28;
int field_2C;
};
struct CCmdTargetMembers // member struct to reuse it in child classes
{
int m_dwRef;
IUnknown *m_pOuterUnknown;
unsigned int m_xInnerUnknown;
CCmdTarget::XDispatch m_xDispatch;
int m_bResultExpected;
CCmdTarget::XConnPtContainer m_xConnPtContainer;
AFX_MODULE_STATE *m_pModuleState;
};
struct CCmdTarget
{
CCmdTargetVtbl *vfptr;
CCmdTargetMembers members;
};
Only this way, accessing virtual functions on child classes makes sense as their vftables are known. A sample hexrays decompilation shows that it does not make much sense with only the base vftables available:
Decompilation with child vftables:
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
int nReturnCode; // esi MAPDST
CWinApp *pWinApp; // edi
CWinThreadVtbl *pThread; // ebx
nReturnCode = -1;
pWinApp = (CWinApp *)CBumperApp::instance;
if ( AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nShowCmd) )
{
pThread = &pWinApp->vftable->CWinThread;
if ( pWinApp->vftable->InitApplication(pWinApp) )
{
if ( pThread->InitInstance((CWinThread *)pWinApp) )
{
nReturnCode = pThread->Run((CWinThread *)pWinApp);
}
[...]
Decompilation without child vftables:
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
int nReturnCode; // esi MAPDST
CWinApp *pWinApp; // edi
CObjectVtbl *pThread; // ebx
nReturnCode = -1;
pWinApp = CBumperApp::instance;
if ( AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nShowCmd) )
{
pThread = pWinApp->vfptr;
if ( pWinApp->vfptr[5].GetRuntimeClass(pWinApp) ) // nonsense
{
if ( (pThread[2].Serialize)(pWinApp) ) // nonsense
{
nReturnCode = (pThread[2].AssertValid)(pWinApp); // nonsense
}
[...]
Is there any way to get IDA to load the vftables for child classes from the PDB and create all required structs? Or is this not yet possible in IDA 7.0?
To my knowledge, the PDB should have that info. Is there a tool to look into the PDB file to see if it indeed has this info?