1

これについては何度か議論されていることは知っていますが、私の状況は少し異なります。

一部のクラスをエクスポートするサードパーティの dll があります。残念ながら、ヘッダー ファイルは使用できません。エクスポートされた関数を呼び出すことは引き続き可能です。しかし、正しい 'this' ポインター (RCX レジスターで渡される) を渡すことはできません。

まず、関数名を抽出するために dumpbin /exports を使用します (名前はサードパーティ ライブラリと関数名が機密であるため変更されます)。

       4873 1308 0018B380 ?GetId@ThirdPartyClass@ThirdPartyNamespace@@QEBAJXZ = ??GetId@ThirdPartyClass@ThirdPartyNamespace@@QEBAJXZ (public: long __cdecl ThirdPartyNamespace::ThirdPartyClass::GetId(void)const )

これで、API を使用して、ThirdPartyNamespace::ThirdPartyClass へのポインターを受け取るコールバックを登録できるようになりました (ThirdPartyClass の前方宣言のみがあります)。

ここで、ThirdPartyNamespace::ThirdPartyClass::GetId() を呼び出そうとしています:

long (ThirdPartyNamespace::ThirdPartyClass::*_pFnGetId)() const;
HMODULE hModule = GetModuleHandle("ThirdPartyDLL.dll");
*(FARPROC*)&_pFnGetId= GetProcAddress(hModule, "?GetId@ThirdPartyClass@ThirdPartyNamespace@@QEBAJXZ");

long id = (ptr->*_pFnGetId)();

すべて問題ないように見えます (つまり、ステップインすると - 実際に ThirdPartyClass::GetId メソッド内に入ります。しかし、このポインターは適切ではありません。ptr は適切ですが、デバッガーで手動で rcx を ptr に変更すると、正常に動作します。しかし、コンパイラは何らかの理由で ptr を渡しません。

long id = (ptr->*_pFnGetId)();
000000005C882362  movsxd      rax,dword ptr [rdi+30h]  
000000005C882366  test        eax,eax  
000000005C882368  jne         MyClass::MyCallback+223h (05C882373h)
000000005C88236A  movsxd      rcx,dword ptr [rdi+28h]  
000000005C88236E  add         rcx,rsi  
000000005C882371  jmp         MyClass::MyCallback+240h (05C882390h)  
000000005C882373  movsxd      r8,dword ptr [rdi+2Ch]  
000000005C882377  mov         rcx,rax  
000000005C88237A  mov         rax,qword ptr [r8+rsi]  
000000005C88237E  movsxd      rdx,dword ptr [rax+rcx]  
000000005C882382  movsxd      rcx,dword ptr [rdi+28h]  
000000005C882386  lea         rax,[r8+rdx]  
000000005C88238A  add         rcx,rax  
000000005C88238D  add         rcx,rsi  
000000005C882390  call        qword ptr [rdi+20h]  
000000005C882393  mov         ebp,eax  

これらのコマンドを実行する前に、rsi には ThirdPartyClass のオブジェクトへのポインター (つまり ptr) が含まれていますが、それを rcx で直接渡す代わりに、いくつかの演算が実行され、その結果、このポインターは完全に間違ったものになります。

非仮想関数 ThirdPartyClass::GetId() を呼び出すことになるため、コンパイラがなぜそれを行うのか理解できないいくつかのトレース:

000000005C88237A  mov         rax,qword ptr [r8+rsi] 
    R8  0000000000000000    
    RSI 000000004C691AA0    // good pointer to ThirdPartyClass object
    RAX 0000000008E87728    // this gets pointer to virtual functions table of ThirdPartyClass
000000005C88237E  movsxd      rdx,dword ptr [rax+rcx] 
    RAX 0000000008E87728    
    RCX FFFFFFFFFFFFFFFF    
    RDX FFFFFFFFC0F3C600
000000005C882382  movsxd      rcx,dword ptr [rdi+28h]  
    RCX 0000000000000000    
    RDI 000000005C9BE690    
000000005C882386  lea         rax,[r8+rdx]
    RAX FFFFFFFFC0F3C600    
    RDX FFFFFFFFC0F3C600    
    R8  0000000000000000    
000000005C88238A  add         rcx,rax 
    RAX FFFFFFFFC0F3C600    
    RCX FFFFFFFFC0F3C600    
000000005C88238D  add         rcx,rsi  
    RCX 000000000D5CE0A0    
    RSI 000000004C691AA0    
000000005C882390  call        qword ptr [rdi+20h]   

私の見解では、それは次のように単純でなければなりません

long id = (ptr->*_pFnGetId)();
mov         rcx,rsi  
call        qword ptr [rdi+20h] 
mov         ebp,eax

また、qword ptr [rdi+20h] を呼び出す前に rcx を rsi に設定すると、期待値が返されます。

私は何か完全に間違っていますか?前もって感謝します。

4

1 に答える 1