逆向工程中如何确定无源码类的实际大小及成员信息?
问题描述
我正在学习逆向工程,尝试用自定义的WrapperClass包装无源码的PrivateClass。目前我通过将WrapperClass的指针作为this参数调用PrivateClass的构造函数,以此填充WrapperClass中的m_string_address、m_somevalue1、m_somevalue2及missingBytes等成员。但过程中出现程序崩溃(添加m_u1和m_u2后解决)、文本无法渲染(添加mData[2900]后解决)的问题,其中m_u1和m_u2存储m_string_address中字符串的长度,但我未预料到后续还有其他成员变量,mData[2900]是随机设置的较大值。
请问如何确定无源码类的真实大小?是否存在工具可识别类中的变量、顺序(或至少正确的数据类型及大小)?我推测可通过处理指定地址范围的汇编代码生成半反编译状态来实现。
现有代码
class WrapperClass { public: WrapperClass(const wchar_t* original); private: uintptr_t m_string_address; int m_somevalue1; int m_somevalue2; char missingBytes[2900]; }; WrapperClass::WrapperClass(const wchar_t* original) { typedef void(__thiscall* PrivateClassCtor)(void* pThis, const wchar_t* original); PrivateClassCtor PrivateClassCtorFunc = PrivateClassCtor(DLLBase + 0x1c00); PrivateClassCtorFunc(this, original); }
解决方案
确定无源码类的真实大小
- 跟踪析构/内存释放逻辑:如果
PrivateClass有显式析构函数,或代码中存在delete该类实例的操作,汇编里通常会出现和内存大小相关的指令(比如sub esp, X),X就是类的总大小。另外,构造函数中对this指针的最高访问偏移+对应数据类型大小,就是类的最小尺寸。 - 内存断点跟踪:调用构造函数后,给
WrapperClass实例的内存区域下写入断点,触发后查看汇编访问的偏移,逐步确定所有成员的位置和大小,累加得到总大小。 - 虚函数表辅助计算:如果
PrivateClass带虚函数,虚表指针占4字节(32位)或8字节(64位),后续成员偏移从该指针后开始计算;多重继承的情况还要考虑多个虚表的占用。
识别类成员的工具与方法
- IDA Pro/Ghidra:这两款是逆向行业标准工具,反编译功能可自动识别类结构:
- IDA中找到
PrivateClass的构造/成员函数,按F5反编译后,查看对this指针的偏移访问(如[ecx+0x8]),右键选Structures -> Create struct from selection,工具会根据汇编操作推断成员类型和偏移。 - Ghidra的Data Type Manager可手动创建结构体,结合反编译窗口的内存操作填充成员信息,工具会自动处理内存对齐规则。
- IDA中找到
- 手动分析汇编:拆解构造/成员函数的汇编代码:
- 构造函数里的
mov [ecx+0x0], eax可能是初始化指针成员,mov [ecx+0x4], 0x10对应整数成员;通过操作码(如movsd对应8字节、movsw对应2字节)可推断数据类型。 - 成员函数中对
this指针的访问偏移,直接对应成员位置,结合函数功能可进一步确认类型。
- 构造函数里的
- 内存布局验证:构造
WrapperClass后,在调试器中查看内存布局,对比反编译得到的成员偏移,验证每个成员的取值是否合理(比如字符串指针是否指向正确内存),逐步调整结构体定义直到程序稳定运行。
额外提示
- 注意内存对齐:C++类成员会按编译器规则(通常4/8字节)对齐,即使成员间有空隙,也要在
WrapperClass中预留填充字节,否则会导致内存偏移错误。 - 优先分析带参数的成员函数:这类函数通常直接操作类成员,更容易推断成员的类型和位置。
内容的提问来源于stack exchange,提问作者Rick
相关产品推荐
相关产品推荐

