visual-c++reverse-engineeringdllimportclass-constructors

calling a __thiscall DLL function from VC++ code


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

Solution

  • 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.