MFCS42.PDB
加载到IDA 7.0(不具有MFC 4.0源)中,以使其创建代表许多MFC类和虚函数表的适当结构。 > 但是,IDA似乎只为非常基本的类(例如
*Vtbl
)创建CObject
结构,而不是为子类(例如CCmdTarget
)创建CCmdTarget
结构: > 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;
};
实际上,这导致缺少
CObjectVtbl
的新虚拟函数,因为它仅通过继承CObject
来引用CCmdTarget
,但是q4312079q还有7种方法。以前是手工制作的这些结构(您可能会觉得乏味),实际上它应该看起来像这样:// 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;
};
只有这样,才能在子类很有意义,因为它们的vftable众所周知。样本hexrays反编译显示仅使用基本vftables并没有多大意义:
使用子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);
}
[...]
反编译没有子vftable:
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
}
[...]
有没有办法让IDA从PDB加载子类的vftable并创建所有必需的结构?还是在IDA 7.0中尚无法实现?
据我所知,PDB应该具有该信息。是否有工具可以查看PDB文件以查看是否确实具有此信息?
#1 楼
据我所知,您所说的一切都是100%正确的。我也遇到过同样的问题。实际上,当一个朋友给我发送指向此帖子的链接时,我正在后台处理这个确切的问题。这里实际上存在三个相关的问题。首先,在IDA 7.2之前,IDA的类型系统本身没有虚拟功能表的概念。意思是,尽管您可以创建带有一堆函数指针的结构,但是IDA并没有一种机制可以更改派生对象中VTable指针的类型。 “派生”的意思是简单地将基类中的所有内容都包含在偏移量0处。幸运的是,IDA 7.2确实理解了继承中的VTables概念。 Hex-Rays网站上有一些文档。该页面的末尾总结了规则:
VFT指针必须具有“ __vftable”名称
VFT类型必须遵循“ CLASSNAME_vtbl”模式
对于多重继承,请使用“ CLASSNAME_%04X_vtbl”作为VFT类型名称
,其中,如果偏移量不为零,则%04X对应于CLASSNAME中vft指针的偏移量
第二个问题是PDB插件当前不利用类型系统的这些最新更改。即,它不会根据上述规则生成VTable类型/类VTable成员名称。
第三个问题是,坦率地说,PDB文件格式是一场噩梦。您可以使用Visual Studio随附的“ dia2dump”示例查看PDB的内容(在“ DIA SDK”目录中),但是请注意,输出通常是错误的,误导性的或缺少重要信息。虚拟功能信息的处理特别糟糕。我尚未完成调查,但到目前为止,我了解到的一件事是,只有基类才具有
SymTagVTable
符号。即,对于从另一个带有VTable的类B
派生的类A
,只有A
会带有SymTagVTable
符号,而不是B
。如果B
定义了A
中未定义的其他虚拟功能,则甚至是这样。当前,如果类具有SymTagVTable
符号,则PDB插件仅创建VTable,因此会出现您所看到的行为。相反,您必须遍历B
的成员函数,并使用get_virtual
方法检查它们是否是虚拟的-顺便说一句,dia2dump不会执行这些操作。我仍在研究所有这些对于多重继承如何工作。总而言之,解决您的问题(也是我的问题)的方法是修改IDA 7.2 PDB插件( SDK):
为派生类恢复VTable信息,包括多重继承方案
根据类名(和位移偏移量)以合适的方式命名VTable结构类型在多重继承的情况下)
使用
typeinf.hpp
中的新功能标记具有属性TAFLD_VFTABLE
的VTable指针和具有属性TAUDT_VFTABLE
的VTable结构本身。再次,这就是我的意思目前也正在研究。
评论
这是IDA 7.2中有关CPP / OOP内容的新增功能的重要内容。我已经快速阅读了有关它的内容,但没有意识到它已经从这里开始。目前我没有资格更新到7.2,但我希望将来能这样做,并且至少现在可以通过某种方式来命名我的结构和字段以使其符合其新模式,脚本。
–雷
18/12/6在2:01