Member Funktion Hook lässt Programm crashen



  • Hallo liebe Programmierfreunde,

    ich möchte einer Memberfunktion einer Klasse in einem anderen Prozess durch Dll Injektion mit Microsofts Detours 3.0 hooken. Leider stürzt der Wirtprozess bei Funktionen mit mehreren Parametern ab und bei Funktionen mit keinem Parameter wird in der Konsole ein merkwürdiger Wert ausgegeben.
    Hier ist der Quellcode zum Wirtprogramm und unten der Quellcode für die Dll:

    // main.cpp
    
    #include <Windows.h>
    #include <iostream>
    #include <stdlib.h>
    #include <tchar.h>
    #include <time.h>
    
    #include "MyClass.h"
    
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[]) {
    	MyClass obj;
    
    	srand(time(NULL));
    	while (true) {
    		wcout << "Time: " << obj.Get() << endl;
    
    		int duration = rand() % 500 + 1000;
    		Sleep(duration);
    		obj.Add(duration / 1000.0);
    	}
    
    	return 0;
    }
    
    // MyClass.h
    #pragma once
    
    class MyClass {
    private:
    	float _value;
    
    public:
    	MyClass();
    	~MyClass();
    
    	__declspec(noinline) void Add(float value);
    	__declspec(noinline) float Get();
    };
    
    // MyClass.cpp
    #include "MyClass.h"
    
    MyClass::MyClass() {
    	_value = 1;
    }
    
    MyClass::~MyClass() { }
    
    void MyClass::Add(float value) {
    	_value += value;
    }
    
    float MyClass::Get() {
    	return _value;
    }
    
    // dllmain.cpp
    #include <windows.h>
    #include <Detours/detours.h>
    #include <string>
    
    #pragma comment(lib, "Detours/detours.lib")
    #pragma comment(lib, "ws2_32.lib")
    
    using namespace std;
    
    class MyClass;
    
    typedef float (__thiscall *T__MyClass_Get)(MyClass*);
    T__MyClass_Get MyClass_Get = reinterpret_cast<T__MyClass_Get>(0x00C01AB0);
    
    typedef void (__thiscall *T__MyClass_Add)(MyClass*, float);
    T__MyClass_Add MyClass_Add = reinterpret_cast<T__MyClass_Add>(0x00C01AA0);
    
    float __cdecl MyGet(MyClass* obj) {
    	float value = MyClass_Get(obj);
    	return 5;
    }
    
    void __cdecl MyAdd(MyClass* obj, float value) {
    	MyClass_Add(obj, 0);
    }
    
    BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved) {
    	if (DetourIsHelperProcess()) {
    		return TRUE;
    	}
    
    	if (dwReason == DLL_PROCESS_ATTACH) {
    		DetourTransactionBegin();
    		DetourUpdateThread(GetCurrentThread());
    		DetourAttach(&(PVOID&) MyClass_Add, MyAdd);
    		DetourTransactionCommit();
    	}
    	else if (dwReason == DLL_PROCESS_DETACH) {
    		DetourTransactionBegin();
    		DetourUpdateThread(GetCurrentThread());
    		DetourDetach(&(PVOID&) MyClass_Add, MyAdd);
    		DetourTransactionCommit();
    	}
    
    	return TRUE;
    }
    

    Ich gehe stark davon aus, dass mein Programm fehlerhaft ist, aber ich weiß nicht was genau.
    Obwohl, ich weiß, dass die Detour Funktion dieselbe Signatur haben muss, wie die zu hookende, hilft mir das nicht weiter, denn ich kann keine Nicht-Memberfunktionen nicht als __thiscall deklarieren.
    Gibt es dafür vielleicht ein work-around?

    Thx und LG



  • DerCoder schrieb:

    Obwohl, ich weiß, dass die Detour Funktion dieselbe Signatur haben muss, wie die zu hookende, hilft mir das nicht weiter, denn ich kann keine Nicht-Memberfunktionen nicht als __thiscall deklarieren.
    Gibt es dafür vielleicht ein work-around?

    Ist das eine Fangfrage? Die offensichtliche Lösung ist die Funktion als Memberfunktion derselben Klasse zu deklarieren.



  • nwp3 schrieb:

    Ist das eine Fangfrage? Die offensichtliche Lösung ist die Funktion als Memberfunktion derselben Klasse zu deklarieren.

    Nein, eigentlich nicht 😞
    Ich muss also in der Dll die Klasse nachbilden. Wie bekomme ich dann einen Funktionszeiger für die Memberfunktion?

    Edit: Danach habe ich schonmal auf google gesucht, aber nichts gefunden, was mir weiter hilft. Wie bekomme ich dann den Funktionszeiger für die Originalfunktion? Auch mit dem typedef Konstrukt und der darauf folgenden Zeile?



  • Mach in der DLL einfach ne Dummy-Klasse. Die muss keine Member o.Ä. haben, einfach nur dass du ne Klasse hast. Und dann definierst du deine Hook-Funktionen als Memberfunktionen dieser Klasse. Ohne den impliziten "this" Parameter natürlich. Und statt "obj" übergibst du dann natürlich "this" an die Original-Funktion.

    Sollte mMn. funktionieren. Ich hab' mit Detours allerdings noch nie 'was gemacht, von daher hau mich nicht wenn's nicht klappt 🙂



  • Du castest ja Funktionszeiger um, von __cdecl nach __thiscall... Das kann ja nichts werden.

    Versuch mal sowas in der DLL:

    class MyClass{
    private:
        float _value; //vorsichtig mit reservierten Bezeichnern
    public:
        MyClass();
        ~MyClass();
    
        __declspec(noinline) void MyAdd(float value);
        __declspec(noinline) float MyGet();
    };
    
    void MyClass::MyAdd(float value){
        _value = value;
    }
    
    ...
    
    DetourAttach(&(PVOID&)MyClass::MyAdd, MyAdd); //oder so ähnlich
    


  • @nwp3
    DetourAttach erwartet nen Zeiger auf nen Funktionszeiger.
    Du brauchst also irgend eine Funktionszeiger-Variable. Einfach so direkt die Adresse von MyClass::MyAdd nehmen, und davon dann nochmal die Adresse, das wird nicht gehen 😉



  • du musst überhaupt keine dummy klasse definieren.

    class MyClass {
    private:
        float _value;
    
    public:
        MyClass();
        ~MyClass();
    
        __declspec(noinline) void Add(float value);
        __declspec(noinline) float Get();
    };
    

    du verwendest wohl MS VC++. Bei dessen generiertem code wird in bezug auf oop der this pointer über das ECX register übergeben (bei x86 natürlich aber das trifft ja hier zu). Deshalb fällt ein Parameter schonmal weg. Ansonsten werden die argumente von rechts nach links übergeben und die aufgerufene funktion muss den stack speicher der funktionsargumenten selbst über 'RET N' freigeben.

    Aus

    typedef float (__thiscall *T__MyClass_Get)(MyClass*); 
    typedef void (__thiscall *T__MyClass_Add)(MyClass*, float);
    

    wird

    typedef float (__stdcall *T__MyClass_Get)(void); 
    typedef void (__stdcall *T__MyClass_Add)(float);
    

    Beim hooken selbst empfehle ich immer die Target Funktion noch mal mittels RE anzusehen und zur runtime zu debuggen.

    konklusion: der crash / die komischen output werte rührten wohl daher, dass du immer ein DWORD zu viel freigegeben hast und somit der stack korrumpiert worden ist.

    wie ms detours im detail funktionieren weiß ich nicht, ich schreibe detours selbst. ich mache das immer so, dass die funktion zu der umgeleitet worden ist, komplett in inline assembler schreibe und eine event funktion calle, die ich dann wieder in c++ code verfasse. so hat man einen viel höhen grad an kontrolle und das debuggen ist einfacher.



  • Danke erstmal für die Antworten!

    Leider helfen sie mir aber nicht weiter. Selbst der Vorschlag von thiscall, den Objektzeiger, der bisher als erster Parameter übergeben wurde, wegzulassen, lässt das Programm abstürzen 😞

    Gibt es denn keine Seiten, auf denen das Hooken von Klassenfunktionen erläutert wird?

    Wenn du deine Detourfunktion selbst schreibst, musst du dann nicht aufpassen, dass du in den ersten 5 Bytes der Targetfunktion keine opcodes "zerschneidest"?
    Ich wäre dir sehr dankbar, wenn du mir funktionierenden Beispielcode zeigen könntest, immerhin lerne ich davon ja auch.

    Thx unf LG



  • DerCoder schrieb:

    Danke erstmal für die Antworten!

    Leider helfen sie mir aber nicht weiter. Selbst der Vorschlag von thiscall, den Objektzeiger, der bisher als erster Parameter übergeben wurde, wegzulassen, lässt das Programm abstürzen 😞

    Gibt es denn keine Seiten, auf denen das Hooken von Klassenfunktionen erläutert wird?

    Wenn du deine Detourfunktion selbst schreibst, musst du dann nicht aufpassen, dass du in den ersten 5 Bytes der Targetfunktion keine opcodes "zerschneidest"?
    Ich wäre dir sehr dankbar, wenn du mir funktionierenden Beispielcode zeigen könntest, immerhin lerne ich davon ja auch.

    Thx unf LG

    Also in Bezug auf Klassenmethoden hooken ist dies wohl ganz hilfreich:
    http://www.openrce.org/articles/full_view/23

    Ja, da muss ich selbst aufpassen. MS Detours bekommt das wohl über disasm selbst heraus. finde das selbstherausfinden allerdings nicht schlimm. ein blick mit ollydbg genügt ja.

    beispielcode kann ich dir gerade nicht geben, da ich nicht daheim bin, generell sollte man halt beim hooken sich die funktion mal selbst mittels RE analysieren und mit einem debugger durchsteppen und zu schauen wo und warum es crasht.

    es ist halt so, nur hooks anzuwenden, also nur an der oberfläche zu kratzen, da wird man nicht weit kommen, vor allem wenns mal crasht und das reine ändern des typedefs nichts bringt. assembler und RE kenntnisse sind da beim debuggen ein eminentes werkzeug.

    hooken ansich ist aber recht simpel, achte halt u.a. auch darauf, dass du instructions nicht zerschießt, dass du beim patchen auch schreibrechte hast (alte access rights sichern, ändern, schreiben, wiederherstellen), dass du die originalen instructions ausführst und dann wieder zur funktion +overjumpen des patches springst.



  • noch ein hinweis: wenn 5 bytes z.b. instructionms zerschießt, dann nimmt man einfach so viele zusätzliche bytes mit, bis keine instruction mehr zerschossen ist. als patch kommt dann ein z.b. eine jmp instruction + nops in frage.



  • Hey, ich bin's der aus Skype, guck mal was ich in 2 Sekunden 🙄 google gefunden hab: http://tresp4sser.wordpress.com/2012/10/06/how-to-hook-thiscall-functions/ :p



  • Okay das leuchtet mir ein.
    Welchen declspec brauche ich denn für die Detour Funktion? __cdecl oder __stdcall wenn ich Memberfunktionen hooken möchte? Bisher hat keine Variante funktioniert.
    Ich probiere micht jetzt mal an inline Assembler. Meine Detour Funktion sieht bisher so aus. Sie soll, anstatt die Originalfunktion aufzurufen, 5 zurückgeben.

    float __stdcall MyClass_Get(MyClass* obj) {
    	_asm {
    		mov eax, 5
    		ret
    	}
    }
    

    EDIT: Lese mir gerade Gregsons Link durch.

    EDIT2: Wenn das hier meine Detour Funktion ist, zeigt die Konsole -1.#IND an 😞

    float __fastcall MyClass_Get(MyClass* obj) {
    	return 5;
    }
    


  • verwendest du jetzt immernoch ms detours? wenn ja, dann müsste doch in der doku dazu etwas stehen.

    ansonsten kommt es halt darauf an, wie ms detours im detail diesbezüglich funktioniert.

    falls es ein jmp-detour ist, dann werden z.b. aufrufe in der neuen funktion wie

    void my_func(...)
    {
        orig_func(...);
    }
    

    nach

    jmp dword ptr[orig_func]
    

    aufgelöst.

    wie gesagt, ohne selbst asm und re zu lernen, macht das nicht viel sinn. vllt gibt dir einer funktionierenden code der 1:1 auf einen konkreten fall adaptiert ist, bei anderen fällen funktionierts wiederum nicht mehr und selbst updaten fällt dann flach weil man nicht weiß wo's hängt. ist jetzt nicht gegen dich, aber glaub mir, wenn du selbst genau weißt was du machst dann hast du mehr spaß und nutzen in der ganzen sache.

    mal abgesehen vom konkreten fall: was möchtest du eigentlich genau erreichen? glaube nicht, dass du einfach mal zum spaß ne klassenmethode in deiner eigenen test app hooken willst, es sei denn natürlich du würdest richtig debuggen, aber davon schriebst du jetzt nichts.



  • thiscall schrieb:

    falls es ein jmp-detour ist, dann werden z.b. aufrufe in der neuen funktion wie

    void my_func(...)
    {
        orig_func(...);
    }
    

    nach

    jmp dword ptr[orig_func]
    

    aufgelöst.

    begrenzt sich aber nicht auf void-funktionen, es geht auch bei rückgabewert berechnenden funktionen, wie z.b.:

    int my_func(....)
    {
        return orig_func(...)
    }
    
    //wird zu:
    jmp dword ptr[orig_func]
    
    //wird NICHT zu:
    call orig_func
    ret
    

    je nachdem halt



  • Ja, ich verwende immer noch MS Detours.
    Ms Detours arbeitet mit JMP, nicht mit CALL (das habe ich mit CheatEngine herausgefunden).

    Gibt es ein gutes Buch, was mir in diesem Zusammenhang empfohlen werden kann?



  • DerCoder schrieb:

    Gibt es ein gutes Buch, was mir in diesem Zusammenhang empfohlen werden kann?

    Für einsteiger ist das buch "assembler - grundlagen der programmierung" ganz okay.

    da geht's zwar auch am anfang um DOS, aber das ist nicht unbedingt schlimm. ich selbst bin das buch damals nur theoretisch durchgegangen, das hat aber gereicht, da ich vorher schon erfahrung mit c++ und winapi gehabt hatte.

    reverse engineering wird dort aber nur kurz mal angerissen. bezüglich re habe ich mal das buch "hacking - die kunst des exploits" den anfang theoretisch durchgegangen (konzentriert sich aber auf linux). ansonsten gibts auch die lena reverse engineering tutorials. weiterhin hilft es auch test applikationen zu schreiben und diese dann zu reversen bis man dann zu einem echten target übergehen kann (z.b. ein spiel oder was auch immer).

    http://www.amazon.de/Assembler-Grundlagen-Programmierung-mitp-Professional/dp/3826614690
    http://www.amazon.de/Hacking-Die-Kunst-Exploits-mit/dp/3898645363/ref=sr_1_1?s=books&ie=UTF8&qid=1395349348&sr=1-1&keywords=hacking+die+kunst+des+exploits
    https://tuts4you.com/download.php?list.17

    ich hoffe, dass dir das weiterhilft.


Anmelden zum Antworten