C++ member function von C aufrufen - wenn ich Funktionspointer und die Adresse des Objekts kenne



  • Edit:
    CodeSchnipsel vergessen...

    typedef void (QPainter::*drawTextPtr)(const QRectF& , const QString&  ,  const QTextOption& ); // funktions pointer definieren
    
    void drawTextHooked ( const QRectF & rectangle, const QString & text, const QTextOption & option = QTextOption() ) // meine funktion die statt der originalen aufgerufen wird
    {
    	drawTextPtr drawTextOri = (drawTextPtr) GetProcAddress(GetModuleHandle("QtGui4.dll"),"?drawText@QPainter@@QAEXABVQRectF@@HABVQString@@PAV2@@Z"); // originale adresse laden
    	return (this.*drawTextOri)(rectangle,text,option); // aufrufen
    }
    

    Mein Problem ist jetzt nur das ich ums verrecken GetProcAddress nicht gecasted bekomm, so dass mein Compiler das annimmt 😕

    Gibts da nen Trick den ich überseh??

    mfg


  • Mod

    Ich gebe dir mal das Grundprinzip. Dein konkreter Code sieht mir zu kompliziert aus als dass ich da jetzt durchsteigen möchte.

    C++ Klasse:

    #include"wrapper.h"
    
    class foo
    {
     public:
      void bar();
    };
    
    void call_bar(void *adress)
    {
     reinterpret_cast<foo*>(adress)->bar();
    }
    

    Der Wrapper:

    #ifdef __cplusplus
     extern "C" {
     #endif 
    
       void call_bar(void *);
    
     #ifdef __cplusplus
     }
     #endif
    

    Dein C-Code:

    #include"wrapper.h"
    
    // ...
     call_bar(pointer_to_object);
    

    Wenn's voll portabel sein soll, dann muss die main-Funktion mit einem C++-Compiler übersetzt werden. Bei manchen Implementierungen ist dies nicht nötig, aber sicher ist sicher. Außerdem sollte das gesamte Projekt mit Compilern des gleichen Herstellers übersetzt werden.

    Das ganze ist ungetestet, aber so ungefähr sollte das gehen.

    Eine einfache Alternative wäre natürlich das ganze Projekt mit einem C++-Compiler zu übersetzen.



  • Das hier:

    typedef void (QPainter::*drawTextPtr)(const QRectF& , const QString&  ,  const QTextOption& ); // funktions pointer definieren
    

    ist kein einfacher Funktionszeiger, sondern ein Methodenzeiger. Der kann auch eine virtuelle Methode bezeichnen, deswegen reicht ein einfacher Funktionszeiger dafür nicht aus, und deswegen wirst du den Rückgabewert von GetProcAddress auch ums Verrecken nicht da reingecastet kriegen.

    Was du da vorhast ist in keiner Weise standardisiert; in jedem Fall wird das plattform- und wahrscheinlich auch compilerabhängig bleiben. Frag mal im Visual C++-Forum, vielleicht hat sich da schon mal jemand mit der Problematik rumgeschlagen. Es sollte mich nicht wundern, wenn dabei am Ende etwas wie

    typedef void (*QPainterDrawTextPtr)(QPainter *self, QRectF const *, QString const *, QTextOption const *);
    

    herauskäme, aber sicher sagen kann ich das nicht.

    Vielleicht hilft dir auch die UnDecorateSymbolName-Funktion im WinAPI weiter.



  • Hallo,

    @SeppJ: Erstmal VIELEN Dank für deine Hilfe.
    Nur ist mein Problem nicht das die Adresse des Objekts Variabel ist sondern das es die Adresse der Funktion ist (welche ich eben mit GetProcAddress holen muss).

    Also 1. Problem: Wie kann ich eine Memberfunction aufrufen welche welche ich mit GetProcAddress lade? Quasi, wie bekomm ich einen "Funktionszeiger" in einen "Methodenzeiger".

    2. Problem: Wie komm ich an den this Pointer wenn die Funktion garnicht "weiß" das sie als Memberfunction aufgerufen wird? (Weil das erst später mit dem umbiegen der IAT möglich gemacht wird)

    Aber eins nach dem anderen, wär schon froh wenn Problem 1 Lösbar ist 🙂

    @seldon: Das wär nicht schlimm, muss nur auf meinem System laufen 😉
    Visual C++ nutz ich garnicht sondern Dev-C++, glaub also nicht das die mir helfen können *gg*

    mfg



  • Ich glaube, es sind noch ein paar Fragen offen:
    1. Wozu brauchst du C?
    2. Warum Dev-C++?
    3. Du willst also eine variable Memberfunktion aus deinem C-Code heraus aufrufen. Soweit ich dich verstanden habe, ist der Funktionspointer im C-Code bekannt. Ist das Objekt auch bekannt?



  • 1. Brauchen tu ichs nicht, aber wenn ichs als memberfunktion dekariere meckert er natürlich weils nicht in der class Definition ist
    2. weils klein und umsonst ist
    3. Jein, ich brauch den this Pointer und weiß nicht wie ich ran komm 😕

    mfg



  • PS: Mir würde es erstmal schon reichen wie ich das Problem löse wenn das Objekt bekannt ist. Ein Schritt nach dem anderen 😉



  • Hast du dir eigentlich mal SeppJ seine Lösung genauer angesehen? Dort sind deine Probleme eigentlich soweit gelöst. Die call_bar Funktion kannst du exportieren, somit brauchst du deine Klassen-Instanz nur noch als void-Zeiger in deinem C-Code herum reichen. Der Rest, wie auch das Auflösen von virtuellen Methoden, passiert ganz wie gewohnt in C++.



  • Du kannst jede nicht-statische Funktion

    foo::bar()

    zu bar(foo*)

    umschreiben. Wo ist das Problem?



  • HookingIsNoFun schrieb:

    1. Brauchen tu ichs nicht, aber wenn ichs als memberfunktion dekariere meckert er natürlich weils nicht in der class Definition ist
    2. weils klein und umsonst ist
    3. Jein, ich brauch den this Pointer und weiß nicht wie ich ran komm 😕

    mfg

    1. Verstehe ich nicht. Klingt nach einem Problem, weswegen du nicht extra nach C wechseln solltest. Das musst du nochmal erklären und darüber nachdenken.
    2. Nimm Code::Blocks. Dev-C++ ist veraltet.
    3. Vielleicht so etwas (ungetestet):

    ccode.h:

    #ifdef __cplusplus
    extern "C" {
    #endif
    
    void call_my_func(void *my_obj, void (*my_func_ptr)());
    
    #ifdef __cplusplus
    }
    #endif
    

    cxx_interface.h:

    #ifdef __cplusplus
    extern "C" {
    #endif
    
    void interface_callfunc(void *obj, void (*func_ptr)());
    
    #ifdef __cplusplus
    }
    #endif
    

    cxxcode.h:

    void do_sth();
    

    ccode.c:

    #include "ccode.h"
    #include "cxx_interface.h"
    void call_my_func(void *my_obj, void (*my_func_ptr)()))
    {
        interface_callfunc(my_obj, my_func_ptr);
    }
    

    cxxcode.cpp:

    #include "ccode.h"
    #include "cxx_interface.h"
    #include "cxxcode.h"
    #include <string>
    #include <iostream>
    
    void do_sth()
    {
        std::string str = "Hello World!";
        call_my_func(static_cast<void *>(&str), static_cast<void (*)()>(std::string::length));
    }
    
    void interface_callfunc(void *obj, void (*func_ptr)())
    {
        std::string *str_ptr = reinterpret_cast<std::string *>(obj);
        size_t (*std::string::length_func)() = static_cast<size_t (*)()>(func_ptr);
        std::cout << "string length: " << str->*length_func() << "\n";
    }
    


  • @Sk4terb0i: Wie schon gesagt, ich will keine Klassen-Instanz als Pointer rumgeben sondern eine Klassen-MemberFunktion 😉
    Oder versteh ich was falsch?

    @Ethon: Also einfach eine C Funktion schreiben und als ersten Parameter den Klassen-Instanz Pointer schreiben?
    Dann sollte aber folgendes funktionieren, oder?

    typedef void (*drawTextPtr_C)(QPainter*, const QRectF& , const QString&  ,  const QTextOption& );
    
    void drawTextHooked (QPainter* thisP, const QRectF & rectangle, const QString & text, const QTextOption & option = QTextOption() )
    { 
    	drawTextPtr_C drawTextOri = (drawTextPtr_C)(GetProcAddress(GetModuleHandleA("QtGui4.dll"),"?drawText@QPainter@@QAEXABVQRectF@@HABVQString@@PAV2@@Z"));
    	return drawTextOri(thisP, rectangle,text,option);
    }
    

    Compiliert zwar aber stürzt dann im Programm ab 😕
    Aber Danke 🙂 Vll mach ich ja nur ein wenig falsch.
    Hab auch schon __stdcall als calling convention probiert weil die Visual C++ Debug lib folgendes ausspuckt: http://www0.xup.in/exec/ximg.php?fid=54862306
    Aber ich glaube der Fehler liegt eher daran das der this Pointer auch im ECX Register übergeben werden kann

    @wxSkip

    Bin jetzt doch auf Visual C++ 2010 Express umgestiegen.
    Wenn ich das richtig sehe dann ist der Kern deines Codes diese Funktion hier:

    void interface_callfunc(void *obj, void (*func_ptr)())
    {
        std::string *str_ptr = reinterpret_cast<std::string *>(obj);
        size_t (*std::string::length_func)();
        std::cout << "string length: " << str->*length() << "\n";
    }
    

    Aber in der Funktion wird func_ptr doch garnicht mehr genutzt 😕
    Oder versteh ich da was falsch?

    Jedenfalls schonmal vielen Dank für die Hilfe.

    @All
    http://www.codeguru.com/cpp/w-p/dll/importexportissues/article.php/c123
    Das sollte die Lösung für mein Problem sein.

    Dort wird beschrieben das man den Pointer mit

    template
    Dest force_cast(Src src)
    {
     union
     {
      Dest d;
      Src s;
     } convertor;
    
     convertor.s = Src;
     return convertor.d;
    }
    

    casten kann..
    Dumm nur das VC++ leider folgendes ausspuckt:

    1>hookedfunctions.cpp(8): error C2146: Syntaxfehler: Fehlendes ';' vor Bezeichner 'force_cast'
    1>hookedfunctions.cpp(8): error C4430: Fehlender Typspezifizierer - int wird angenommen. Hinweis: "default-int" wird von C++ nicht unterstützt.
    1>hookedfunctions.cpp(8): error C2065: 'Src': nichtdeklarierter Bezeichner
    1>hookedfunctions.cpp(8): error C2146: Syntaxfehler: Fehlendes ')' vor Bezeichner 'src'
    1>hookedfunctions.cpp(8): error C4430: Fehlender Typspezifizierer - int wird angenommen. Hinweis: "default-int" wird von C++ nicht unterstützt.
    1>hookedfunctions.cpp(8): error C2059: Syntaxfehler: ')'
    1>hookedfunctions.cpp(9): error C2143: Syntaxfehler: Es fehlt ';' vor '{'
    1>hookedfunctions.cpp(9): error C2447: '{': Funktionsheader fehlt - Parameterliste im alten Stil?

    Jemand ne Idee? 🙂

    mfg und nochmal vielen Dank für die Hilfe!



  • Ups, hab's verbessert.
    Bei deinem force_cast sollte in der ersten Zeile eigentlich template<typename Src, typename Dest> stehen. Es sollte aber gar nicht nötig sein.

    UND: Du solltest endlich mal deinen Programmablauf erklären. Was passiert im C++-Code und im C-Code und in welcher Reihenfolge. Welche Variablen sind wo bekannt? Du kannst bei meinem Code natürlich auch nur den Funktionspointer herumgeben und das Objekt global speichern.



  • Vergiss mal dein GetProcessHandle-Zeugs. Was du willst ist eine Methode einer Klasse ausführen, richtig? C kennt aber weder Klassen, noch Methoden. Eine Methode ist aber nichts weiter, als eine Funktion mit zugehörigem Objekt. Diese Objekte kannst du in C weder erstellen, noch verwenden. Was du aber machen kannst ist dir die Adresse des Objektes zu merken und in C++ die gewünschte Methode aufrufen.



  • @wxSkip:
    Hab dein Beispiel angeschaut und, soweit ich das richtig verstanden hab, so umgesetzt:

    typedef void (QPainter::*drawTextPtr)(const QRectF& , const QString&  ,  const QTextOption& );
    
    ...
    
    drawTextPtr drawTextOri = static_cast<void (*)( QRectF& , QString& , QTextOption& )>(GetProcAddress(GetModuleHandleA("QtGui4.dll"),"?..."));
    

    Leider meckert er dann *error C2440: 'static_cast': 'FARPROC' kann nicht in 'void (__cdecl )(QRectF &,QString &,QTextOption &)' konvertiert werden

    Hab ich was falsch verstanden?

    und wenn ich force_cast mit der richtigen ersten Zeile ausstatte und dann so verwende

    drawTextPtr drawTextOri = force_cast<drawTextPtr>(GetProcAddress(GetModuleHandleA("QtGui4.dll"),"?drawText@QPainter@@QAEXABVQRectF@@HABVQString@@PAV2@@Z"));
    

    kommt

    1> hookedfunctions.cpp(39): error C2783: "Dest force_cast(Src)": template-Argument für "Dest" konnte nicht hergeleitet werden.
    1> hookedfunctions.cpp(9): Siehe Deklaration von 'force_cast'

    Fehlt da noch was? Hab noch nie mit templates gearbeitet 😕

    Ok ich versuchs nochmal meinen Programmablauf zu erklären 🙂

    Also, ich hab ein Spiel X.
    Ich will aus diesem Spiel Daten auslesen die per DrawText auf den Bildschirm gepackt werden.
    Ok, also hab ich mir überlegt mach ich das mit API Hooking (was ich schon öfter benutzt hab).

    Leider nutzt das Spiel nicht das normale WinAPI DrawText sondern die DrawText aus den Qt-Libs.

    Ich injecte also meine DLL ins Spiel und lass die IAT so patchen das jeder Aufruf von DrawText in meiner Funktion landet, das funtkioniert auch wunderbar.

    Jetzt muss ich aber in meiner Funktion 2 Dinge machen:
    1. Die richtige DrawText Funktion aufrufen
    2. Den QString in ein Char Array packen damit ich die Daten verarbeiten kann

    So weit, so gut 🙂 Problem 2 ist erstmal zweitrangig, bzw wenn ich Problem 1 gelöst hab sollte ich Problem 2 auch lösen können.

    Jetzt kommt das Problem.
    In MEINER Funktion kann ich nicht einfach DrawText() aufrufen weil das Spiel eine Qt Dll nutzt dies nirgends zum download gibt, ich kann also meinen Code nicht mit dieser Dll linken weil ich keine .lib dafür hab.

    Also will ich die Funktionsadresse der originalen Qt DrawText mit GetProcAddress() dynamisch laden um sie dann aufzurufen.

    Und naja, dafür muss ich irgendwie FARPROC so casten das ich damit die Memberfunktion DrawText auf "this" aufrufen kann 😕

    @Sk4terb0i: Ich hoffe meine Erklärung beantwortet deine Frage :))

    mfg

    PS: Danke für die Geduld, is echt wichtig das ich das hinbekomm und ohne Hilfe schaff ichs diesmal wohl nicht 😕



  • Deine Momentan angestrebte Lösung hat aber nicht mehr sehr viel mit C zu tun. QT ist soviel ich weiß Opensource. Damit solltest du auch an die Quellen kommen. Wenn dir nur die *.lib fehlt, die kannst du ganz einfach selber bauen. Schreibe einfach ein C++-File mit leeren Funktionsrümpfen. Die damit erstellte *.lib kannst du zum Linken verwenden. Dazu gibt es bei MS auch einen Beitrag.





  • Zum Cast: Das hier funktioniert bei mir:

    template<typename Dest, typename Src> Dest force_cast(Src in)
    {
        union {Dest d; Src s;} conv;
        conv.s = in;
        return conv.d;
    }
    
    void dummy_func(int){}
    
    int main()
    {
        cout << force_cast<void *>(dummy_func) << "\n";
    }
    


  • Vielen Dank, es funktioniert soweit 🙂
    Bei meinem force_cast war Dest und Src scheinbar vertauscht.

    Danke für eure Hilfe, wenn ich wieder Probleme hab weiß ich an welches Forum ich mich wende 😉


Anmelden zum Antworten