3

私は DynObj を見ていて、vftablesで自分の実験を行うことにしました。私は Visual Studio 2010 で作業しており、std::string を返す仮想関数を使用してオブジェクトをインスタンス化するコンソール メインを作成しました。

main のテスト コードは、オブジェクトの vftable から取得したポインターを使用して、オブジェクトのパブリック仮想関数を呼び出そうとします。プリミティブ型を返す関数には問題がないことがわかりました。ただし、関数が std::string を返すと、コンパイラは有効なスタック ポップを挿入します (esp,4 を追加します)。これにより、後続のスタック チェック コードで例外がスローされます。

これは、グローバル空間で宣言された関数の標準であることに気付きました。しかし、クラス内の関数は ESP 修飾子のポスト コールを生成しません。

アセンブリと一緒にコードの本質は次のとおりです...

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)  

  }

キャストされたポインターを介した仮想関数への最後の呼び出しの結果は次のようになります。

実行時チェックの失敗 #0 - ESP の値が関数呼び出しで適切に保存されませんでした。これは通常、ある呼び出し規約で宣言された関数を、別の呼び出し規約で宣言された関数ポインターで呼び出した結果です。

だから私の質問は次のとおりです。非プリミティブ型を返す仮想関数で適切な呼び出し規約を呼び出すにはどうすればよいですか?

ところで、00BE178B で中断し、次のステートメントを 00BE178E に設定すると、実行は問題なく完了します。

4

0 に答える 0