我有一个 DLL 文件需要进行逆向工程,我想直接在我的 x86 C++ 代码中调用它的函数之一(即类构造函数)。原因是我不知道类的结构是什么,而且我也没有头文件。该函数的
calling convention
是 __thiscall
。我有兴趣从我的代码和我在 MSDN页面、here 中阅读的内容访问
this
指针,this
指针位于 ecx
寄存器中。
Microsoft 特定的 __thiscall 调用约定用于 x86 架构上的 C++ 类成员函数。
...
this 指针通过寄存器 ECX 传递,而不是在堆栈上。
如果我想从五次调用加载的函数中获得五个不同的对象,我期望获得五个不同的
this
指针,但从我下面的代码来看,它们都是相同的值。
#include <iostream>
#include <Windows.h>
int main()
{
typedef void (__thiscall* CTOR)(void);
HMODULE dll = LoadLibraryA(R"(MYDLL.dll)");
if (!dll)
std::cout << "can not load dll error: " << GetLastError() << std::endl;
else
{
void* default_ctor_this_pointer = ::operator new(4);
if (default_ctor_this_pointer)
{
for (std::size_t i = 0; i < 5; i++)
{
std::memset(default_ctor_this_pointer, 0, 4);
CTOR default_ctor = (CTOR)(GetProcAddress(dll, "??0TestClass@@QAE@XZ"));
std::cout << "\t[+]TestClass::TestClass() = 0x" << default_ctor << " --> error: " << GetLastError() << std::endl;
std::cout << "\t\t[-]this_pointer before call: 0x" << std::hex << *(unsigned*)default_ctor_this_pointer << std::dec << std::endl;
_asm {mov ecx, default_ctor_this_pointer};
default_ctor();
_asm {mov default_ctor_this_pointer, ecx};
std::cout << "\t\t[-]this_pointer after call: 0x" << std::hex << *(unsigned*)default_ctor_this_pointer << std::dec << std::endl;
}
}
}
return 0;
}
这就是我得到的输出
[+]TestClass::TestClass() = 0x62995440 --> error: 0
[-]this_pointer before call: 0x0
[-]this_pointer after call: 0x62a7d8d8
[+]TestClass::TestClass() = 0x62995440 --> error: 0
[-]this_pointer before call: 0x0
[-]this_pointer after call: 0x62a7d8d8
[+]TestClass::TestClass() = 0x62995440 --> error: 0
[-]this_pointer before call: 0x0
[-]this_pointer after call: 0x62a7d8d8
[+]TestClass::TestClass() = 0x62995440 --> error: 0
[-]this_pointer before call: 0x0
[-]this_pointer after call: 0x62a7d8d8
[+]TestClass::TestClass() = 0x62995440 --> error: 0
[-]this_pointer before call: 0x0
[-]this_pointer after call: 0x62a7d8d8
我期望检索五个不同的
this
值,但正如您所看到的,它们是相同的值。我是不是操作方法不对?
您不能仅使用 4 字节分配来调用构造函数 - 您需要首先在
ecx
指向的地址处提供足够的存储空间。据您所知,构造函数可能因任何原因失败。
如果构造成功,你应该在寄存器
eax
中找到构造对象的地址。 ecx
的内容未定义,它是一个易失性寄存器(未保存被调用者!)。
仔细看看
__thiscall
构造函数调用的最小示例 (https://godbolt.org/z/zhfEexcEE):
class Foo {
public:
__thiscall Foo() : x(nullptr) {
}
void* x;
};
组装大部分是不言自明的:
_this$ = -4 ; size = 4
Foo::Foo(void) PROC ; Foo::Foo, COMDAT
// `ebp` and `esp` are non-volatile, and have to be saved by the callee.
push ebp
mov ebp, esp
// Compiler quirk - MSVC writes `this` to the stack ... twice.
push ecx
mov DWORD PTR _this$[ebp], ecx
// Member initialization
mov eax, DWORD PTR _this$[ebp]
mov DWORD PTR [eax], 0
// Here comes the ret val
mov eax, DWORD PTR _this$[ebp]
// Restore non-volatile registers
mov esp, ebp
pop ebp
ret 0
Foo::Foo(void) ENDP ; Foo::Foo