I have a DLL file to get reverse-engineered and I would like to call one of its functions (i.e. a class constructor) directly in my x86 C++ code. The reason is that I have no clue what the structure of the class would be and I have no header file as well. The calling convention
of this function is __thiscall
. I am interested to get access to the this
pointer from my code and from what I have read in MSDN page, here , this
pointer is located in ecx
register.
The Microsoft-specific __thiscall calling convention is used on C++ class member functions on the x86 architecture.
...
The this pointer is passed via register ECX, and not on the stack.
If I want to get five different objects from five times calling the loaded function, I expect to get five different this
pointer, but from my code below, they are all the same value.
#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;
}
and this is what I get as output
[+]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
I expected to retrieve five different this
values, but as you can see they are the same value. Did I do the procedure the wrong way?
Edited-> after what Ext3h suggested, I changed the code as follows:
#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(4096);
if (default_ctor_this_pointer)
{
for (std::size_t i = 0; i < 5; i++)
{
std::memset(default_ctor_this_pointer, 0, 4096);
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, eax};
std::cout << "\t\t[-]this_pointer after call: 0x" << std::hex << *(unsigned*)default_ctor_this_pointer << std::dec << std::endl;
std::cout << "\t\t\t[-]";
for(std::size_t j = 0; j < 10: j++)
{
std::cout << "0x" <, std::hex << ((unsigned)(default_ctor_this_pointer))[j] << " " << std::dec;
} std::cout << "\n";
}
}
}
return 0;
}
The output:
[+]TestClass::TestClass = 0x62955C40 --> error: 0
[-]this_pointer before call: 0x0
[-]this_pointer after call: 0x62ab2380
[-]0x62ab2380 0x62ab0160 0x0 0x62ab23ac 0x940c70 0x0 0x0 0x0 0x0 0x0
[+]TestClass::TestClass = 0x62955C40 --> error: 0
[-]this_pointer before call: 0x0
[-]this_pointer after call: 0x62ab2380
[-]0x62ab2380 0x62ab0160 0x0 0x62ab23ac 0x940798 0x0 0x0 0x0 0x0 0x0
[+]TestClass::TestClass = 0x62955C40 --> error: 0
[-]this_pointer before call: 0x0
[-]this_pointer after call: 0x62ab2380
[-]0x62ab2380 0x62ab0160 0x0 0x62ab23ac 0x940950 0x0 0x0 0x0 0x0 0x0
[+]TestClass::TestClass = 0x62955C40 --> error: 0
[-]this_pointer before call: 0x0
[-]this_pointer after call: 0x62ab2380
[-]0x62ab2380 0x62ab0160 0x0 0x62ab23ac 0x940900 0x0 0x0 0x0 0x0 0x0
[+]TestClass::TestClass = 0x62955C40 --> error: 0
[-]this_pointer before call: 0x0
[-]this_pointer after call: 0x62ab2380
[-]0x62ab2380 0x62ab0160 0x0 0x62ab23ac 0x940978 0x0 0x0 0x0 0x0 0x0
You can't call the constructor with a mere 4 byte allocation - you will need to have provided sufficient storage space at the address pointed to by ecx
to begin with. For all you know, the constructor may have failed for any reason.
If the construction was successful, you should find the address of the constructed object in register eax
. The contents of ecx
are undefined, it's a volatile register (not callee saved!).
Take a close look at a minimal example of a __thiscall
constructor invocation (https://godbolt.org/z/zhfEexcEE):
class Foo {
public:
__thiscall Foo() : x(nullptr) {
}
void* x;
};
The assembly is mostly self-explanatory:
_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
_asm {mov default_ctor_this_pointer, eax};
std::cout << "\t\t[-]this_pointer after call: 0x" << std::hex << *(unsigned*)default_ctor_this_pointer << std::dec << std::endl;
Mind that this is not this
you are printing, but the content at the address of this
.
[+]TestClass::TestClass() = 0x62995440 --> error: 0
[-]*this_pointer after call: 0x62a7d8d8
Judging by the value it is a pointer after-all, but some static data from the same module as the constructor.
Most likely a vtable pointer as you've constructed a class with virtual methods.