No "add esp, 4" for virtual functions returning std :: string

I watched DynObj and decided to do my own experimenting with vftables. I work with Visual Studio 2010 and created a main console that creates an object with a virtual function that returns std :: string.

The test code basically tries to call the public virtual function of the object using a pointer obtained from the vftable object. I found that there is no problem for a function returning a primitive type. But when the function returns std :: string, the compiler inserts the effective pop stack (add esp, 4). This causes a stack validation code, which is followed by an exception.

I noticed that this is the norm for functions declared in the global space. But the function inside the class does not generate an ESP modifier mail call.

Here's the gist of the code along with the assembly ...

class VClass
  {
  public:
    VClass() {}
    ~VClass() {}

    virtual std::string GetString() {return "vstring";}
  };

std::string StrFunc()
  {
  return "string";
  }

void main(int argc, char* argv[])
  {
  VClass vClass;
  __int32 ** vftabletable;
    // 00BE1730  lea         ecx,[ebp-18h]  
    // 00BE1733  call        VClass::VClass (0BE1181h)  
    // 00BE1738  mov         dword ptr [ebp-4],0  

  vftabletable = (__int32**)&vClass;
    // 00BE173F  lea         eax,[ebp-18h]  
    // 00BE1742  mov         dword ptr [ebp-24h],eax  

  StrFunc();
    // 00BE1745  lea         eax,[ebp-15Ch]  
    // 00BE174B  push        eax  
    // 00BE174C  call        StrFunc (0BE11EFh)  
    // 00BE1751  add         esp,4  
    // 00BE1754  lea         ecx,[ebp-15Ch]  
    // 00BE175A  call        std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> > (0BE1221h)

  vClass.GetString();
    // 00BE175F  lea         eax,[ebp-134h]  
    // 00BE1765  push        eax  
    // 00BE1766  lea         ecx,[ebp-18h]  
    // 00BE1769  call        VClass::GetString (0BE1195h)  
    // 00BE176E  lea         ecx,[ebp-134h]  
    // 00BE1774  call        std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> > (0BE1221h)  

  ((std::string (*)())vftabletable[0][0])();
    // 00BE1779  mov         esi,esp  
    // 00BE177B  lea         eax,[ebp-10Ch]  
    // 00BE1781  push        eax  
    // 00BE1782  mov         ecx,dword ptr [ebp-24h]  
    // 00BE1785  mov         edx,dword ptr [ecx]  
    // 00BE1787  mov         eax,dword ptr [edx]  
    // 00BE1789  call        eax  
    // 00BE178B  add         esp,4  
    // 00BE178E  cmp         esi,esp  
    // 00BE1790  call        @ILT+570(__RTC_CheckEsp) (0BE123Fh)  
    // 00BE1795  lea         ecx,[ebp-10Ch]  
    // 00BE179B  call        std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> > (0BE1221h)  

  }

The last call to the virtual function through the cast pointer results in:

Runtime Check Error # 0 - The ESP value was not properly stored on a function call. Usually this is the result of calling a function declared using one calling convention with a function pointer declared with another conditional call.

So my question is: how do I invoke the correct calling convention for a virtual function that returns a non-primitive type?

Btw, when I split into 00BE178B and set the next statement to 00BE178E, execution completes without problems.

+3

Source: https://habr.com/ru/post/1769151/


All Articles