| Code Kitchen Allgemeines Coder-Forum rund um das Programmieren eigenständiger, ausführbarer Programme. |
Diskussion: C++/ASM DLL Funktion modifizieren im Forum Code Kitchen, in der Kategorie Software Home; Anzeige Hey Leute, ich versuche schon längere Zeit (immer mal wieder) eine Funktion (z.B. SetCursorPos/PostMessage) aus einer DLL (in diesem ...
![]() |
| | #1 (permalink) |
| Registriert seit: 21.12.10 ![]() Likes: 0 | Anzeige Hey Leute, ich versuche schon längere Zeit (immer mal wieder) eine Funktion (z.B. SetCursorPos/PostMessage) aus einer DLL (in diesem Fall user32.dll) zu importieren (während der Laufzeit) und diese dann mit eigenen Assemblerbefehlen zu verändern. Schema: DllAufruf Funktion aus DLL importieren Assembler Code ausführen (z.B. 10 Bytes lang) Funktionsstartpunkt + 10 Bytes ausführen (die ersten 10 Bytes der Originalfunktion ignorieren) Dazu habe ich mir C++/ASM Gewisse Stelle anspringen angeschaut, aber es funktioniert bei mir trotzdem nicht. Soweit bin ich bis jetzt: Code: typedef BOOL (WINAPI *ggSCP) (int x,int y) ;
void SetCurs(int x, int y)
{
hInst = LoadLibrary(TEXT("C:\\Windows\\System32\\user32.dll"));
ggSCP DLLFuncSCP = ggSCP(GetProcAddress(hInst, "SetCursorPos"));
FreeLibrary(hInst);
hInst = NULL;
__asm
{
mov edi, edi
push ebp
mov ebp, esp
add DLLFuncSCP, 5
jmp [DLLFuncSCP]
}
} Wäre nett, wenn jemand helfen könnte ![]() Mfg Raisem |
| | |
| | #2 (permalink) |
| Registriert seit: 15.03.08 ![]() Likes: 0 | Ich vermute, dass es daran liegt, dass Du die Dll zu früh freigibst. Den SetCursorPos-Function Pointer erhälst Du durch GetProcAddress(...), gibst die Dll aber sofort wieder frei (FreeLibrary(...)). Ich schätze, dass Du die Funktion deshalb nicht mehr aufrufen kannst (--> versuchen). s. auch http://msdn.microsoft.com/en-us/libr...v=vs.80).aspx: "Processes explicitly linking to a DLL call the FreeLibrary function when the DLL module is no longer needed." Aber meine Zeit unter C++ und Dlls sind schon länger vorbei, da ist C# halt Luxus... |
| | |
| HaBOT | - Anzeige - |
| |
| | #3 (permalink) |
| Themenstarter Registriert seit: 21.12.10 ![]() Likes: 0 | Ich kann sie aber gar nicht später freigeben :/ Im Assemblercode springe ich ja zur Funktion -> ich erreiche nichtmehr die Aufrufe nach dem asm code .. EDIT: Bzw. soll das in der MSDN heißen, dass ich FreeLibrary gar nicht brauche, da das Programm das sowieso von selbst macht? Geändert von Raisem (21.12.10 um 19:43 Uhr) |
| | |
| | #4 (permalink) |
| Moderator ![]() Registriert seit: 20.07.05 ![]() ![]() ![]() ![]() ![]() ![]() Likes: 202 | Du musst die auch nicht freigeben Zum wesentlichen: Debugging ist bei Hooks und anderen "low-level" Späßen eigentlich eine Pflicht. 10 Minuten OllyDbg sparen 1 Stunde "Fehler durch Anschauen im Source finden" suche Mögliche Fehlerursachen aus dem Source: 1: unbedingt Callingconventions beachten, sonst bringt man den Stack durcheinander: http://code.entersources.com/f/Insid..._2_4776_0.aspx http://www.3rd-evolution.de/docs/windows/callconv/ (keine Ahnung, welchen Compiler Du verwendest und welche Konvention dieser per Default einsetzt) 2. Stackframe nicht kaputt machen zuerst wird bei der Funktionsausführung der Stackframe Deiner "SetCur" Funktion eingerichtet. Also üblicherweise Code: push ebp mov ebp,esp sub esp, X Code: ... lokale_variable2 lokale_variable1 alter_EBP_Wert Rücksprungadresse_des_Aufrufers_von_SetCur ParamX ParamY Das sollte der Compiler von alleine generieren. Erst jetzt kommt der von Dir eingegebener Code zur geltung: Es wird die Adresse ermittelt und ein wenig Code (_ASM Abschnitt) ausgeführt. Nun schaut der Stack so aus: Code: jungster_EBP_Wert ... lokale_variable2 lokale_variable1 alter_EBP_Wert Rücksprungadresse_des_Aufrufers_von_SetCur ParamX ParamY Code: POP EBP RETN 8 Und dann wird versucht zu RETurnen - dazu wird die nächste Wert auf dem Stack als Adresse benutzt (in dem Fall ist es der Wert einer lokalen Variable aus Deiner Funktion SetCur). D.h entweder benutzt Du einen CALL und lässt die API erstmal in Deine SetCur Funktion zurückkehren oder Du fummelst ein wenig rum und richtest den Stackframe vor dem JMP so her, wie er sein sollte (Pi mal Daumen): zuerst: Code: mov esp, ebp ; ESP Wert vor Ausführung Deines Codes pop ebp ; EBP Wert vor Ausführung Deines Codes und erst jetzt Dein eigentlicher ASM-Inlinecode: Code: push ebp bla PS: mir ist klar, dass ich den Code dupliziere, da der Stackframewiederherstellungsabschnitt größtenteils durch nachfolgenden Code wieder zückgängig gemacht wird, aber so sollte es imho ersichtlicher sein. Für den konkreten Fall dürfte es ausreichen, wenn Du nur Deinen Asm Code modifizierst: Code:
mov esp, ebp ;nur ESP wiederherstellen
add DLLFuncSCP, 5
jmp [DLLFuncSCP]
__________________ Noch mal, für alle Pseudo-Geeks: 1+1=0. -> 10 wäre Überlauf! Selig, wer nichts zu sagen hat und trotzdem schweigt. |
| | |
| | #5 (permalink) |
| Themenstarter Registriert seit: 21.12.10 ![]() Likes: 0 | Geupdateter Code... Funktioniert aber immer noch nicht so super (Access Violation..) : (Sehr stark am verlinkten Post orientiert) typedef BOOL (WINAPI *ggSCP) (int x,int y) ; void SetCurs(int x, int y) { hInst = LoadLibrary(TEXT("user32.dll")); int blubb = (int)GetProcAddress(hInst, "SetCursorPos"); blubb+=7; __asm { mov EDI, EDI push EBP mov EBP, ESP push DWORD PTR SS:[EBP+8] push DWORD PTR SS:[EBP+12] push blubb add ESP,4 jmp DWORD PTR SS:[ESP-4] } Wäre nett, falls noch wer Vorschläge bringen könnte |
| | |
| | #6 (permalink) |
| Moderator ![]() Registriert seit: 20.07.05 ![]() ![]() ![]() ![]() ![]() ![]() Likes: 202 | kleine Anmerkung: habe den obigen Post editiert und den Vorschlag mit dem Call erstmal entfernt, da ein Call ja die Rücksprungadresse ablegt und der Stackframe damit ebenfalls zersört wäre. Die Frage ist nun, ob Du unbedingt in die SetCur zurückkehren möchtest oder die Rückkehr zum Aufrufer der SetCur auch akzeptabel ist. Im ersten Fall müsste man vor Deinem _asm Part dann noch die Rücksprungadresse bestimmen und manuell auf dem Stack ablegen. Und das dürfte nicht ganz so einfach gehen (hier müsste man entweder mit Labels arbeiten und deren Adresse bestimmen können oder über inline ASM die aktuelle Adresse bestimmen und dann ausrechnen, wieviele Bytes zwischen dieser Adresse und der gewünschten liegen). Bsp: Code: _asm: call delta delta: pop eax <--- hier ist nun die Adresse von delta add eax, XYZ <--- Anzahl an Bytes zwischen Delta und gewünschter Rückkehradresse
__________________ Noch mal, für alle Pseudo-Geeks: 1+1=0. -> 10 wäre Überlauf! Selig, wer nichts zu sagen hat und trotzdem schweigt. |
| | |
| | #7 (permalink) |
| Themenstarter Registriert seit: 21.12.10 ![]() Likes: 0 | Oh, den letzten Post von mir hab ich abgeschickt, bevor ich deinen gelesen hab Vielen dank schonmal für die ausführlichen Erklärungen ! ![]() EDIT: Also das hab ich zwar verstanden, was du geschrieben hast, aber die direkte Umsetzung funktioniert trotzdem nicht so :/ Code: void SetCurs(int x, int y)
{
hInst = LoadLibrary(TEXT("C:\\Windows\\System32\\user32.dll"));
//ggSCP DLLFuncSCP = ggSCP(GetProcAddress(hInst, "SetCursorPos"));
int DLLFuncSCP = (int)(GetProcAddress(hInst, "SetCursorPos"));
__asm
{
mov edi, edi // <-- die ersten 5 Bytes der Funktion
push ebp // und gleichzeitig auch die von dir vorgeschlagene Änderung
mov esp, ebp //<-----
jmp [DLLFuncSCP]
}
} Hier noch ein paar Zusatzinformationen:Mein eigentliches Ziel ist es, den Schutz von Gameguard zu umgehen (versuche das schon länger, bräuchte es zwar eigentlich nichtmehr, aber ist gut für mein ego, wenn ich das schaffe ). Es gibt ein Tutorial diesen Gameguard zu umgehen, das mehrfach gepostet wird, aber ich schätze mal man darf nicht auf andere Foren verweisen... Dort wird dieser Code verwendet: (Der Ersteller hat das Tutorial auf das Spiel Flyff von Gpotato bezogen, es geht aber mit allen Spielen, die Gameguard verwenden). Dieser Code wird erzeugt und als DLL verpackt: Code: #include <windows.h>
HINSTANCE hInst;
DWORD DLLFunc;
HWND hFlyff;
HWND hWnd;
__declspec(naked) BOOL WINAPI __stdcall myPostMessageA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
__asm
{
mov edi, edi
push ebp
mov ebp, esp
jmp [DLLFunc]
}
}
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpvReason*/)
{
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
{
if (DLLFunc == NULL) {
hInst = LoadLibrary("user32.dll");
DLLFunc = (DWORD)GetProcAddress(hInst, "PostMessageA") + 5;
}
if (hFlyff == NULL) {
hFlyff = ::FindWindow(NULL, "FLYFF");
}
}
break;
case DLL_THREAD_ATTACH:
{
if (DLLFunc == NULL) {
hInst = LoadLibrary("user32.dll");
DLLFunc = (DWORD)GetProcAddress(hInst, "PostMessageA") + 5;
}
if (hFlyff == NULL) {
hFlyff = ::FindWindow(NULL, "FLYFF");
}
}
break;
case DLL_THREAD_DETACH:
{
if (hInst != NULL) {
// Un-Load DLL
::FreeLibrary(hInst);
hInst = NULL;
}
}
break;
case DLL_PROCESS_DETACH:
{
if (hInst != NULL) {
// Un-Load DLL
::FreeLibrary(hInst);
hInst = NULL;
}
}
break;
}
return TRUE;
} Also habe ich nur die Grundidee übernommen (und Codeschnipsel) um das neu nachzubauen (was ja leider nicht funktioniert ) Sorry falls ich irgendwie nicht verstanden hab, was ihr versucht habt zu erklären, aber bin auf dem Zweig der Assemblerprogrammierung und des Debuggings absoluter Neuling ![]() Mfg Rai Geändert von Raisem (22.12.10 um 17:43 Uhr) |
| | |
| | #8 (permalink) | |
| Moderator ![]() Registriert seit: 20.07.05 ![]() ![]() ![]() ![]() ![]() ![]() Likes: 202 | Sorry, hätte ich dazuschreiben sollen: meine Vorschläge beziehen sich immer auf die Abarbeitung vor deinem eigentlichen Code. Also so: Code:
__asm
{
;Stackherstellung:
mov esp, ebp ; ESP Wert vor Ausführung Deines Codes
pop ebp ; EBP Wert vor Ausführung Deines Codes
;Dein eigentlicher Code
mov edi, edi
push ebp
mov ebp, esp
add DLLFuncSCP, 5
jmp [DLLFuncSCP]
} dein Code komplett modifiziert: Code: mov edi, edi
mov esp, ebp ;nur ESP wiederherstellen
add DLLFuncSCP, 5
jmp [DLLFuncSCP] Zudem schau dir den Link mit calling Conventions nochmal an. Der wesentliche Unterschied zwischen deinem Code und dem aus Tutorial: im Tutorial tut die Funktion erstmal nichts und wird zudem direkt als STDCALL deklariert. PS: Zitat:
Also wirst du nicht um DLLs herumkommen (ohne gehts zwar auch, aber nicht mit reinem C/C++ und schon gar nicht ohne viel inline ASM bzw viel rumprobieren und Debuggen Und wie gesagt, das A und O bei solchen Sachen ist der Umgang mit dem Debugger. Solange man nicht weiß, an welcher Stelle es genau scheitert (Stimmt die Rücksprungadresse nicht oder hat man bloß irgendwo die Variablenwerte vertauscht?) wird das Herumprobieren im Quellcode nicht wirklich etwas bringen
__________________ Noch mal, für alle Pseudo-Geeks: 1+1=0. -> 10 wäre Überlauf! Selig, wer nichts zu sagen hat und trotzdem schweigt. | |
| | |
| | #9 (permalink) |
| Themenstarter Registriert seit: 21.12.10 ![]() Likes: 0 | Funktioniert soweit (so wie du's geschrieben hast). Vielen Dank bis hierhin schonmal Wieso sollte es mit pur c++ /asm nur sehr schwierig gehen? (funktioniert ja ) Hab die Funktion Code: void SetCurs(int x, int y) Code:
BOOL WINAPI SetCurs(int x, int y) Mal ein anderer Ansatz, zu dem ich gerne wüsste, was ihr davon haltet: {[(Gameguard überschreibt/blockiert die ersten 5 Bytes von bestimmten Funktionen (was ich ja zu umgehen versuche, indem ich den inline ASM verwende und einfach später einsteige). Was wäre denn, wenn ich die user32.dll einfach kopieren würde und dann in den Ordner meines Projekts packen würde? )]} <- Probier ich mal aus ![]() Mfg Raisem |
| | |
| | #10 (permalink) | ||
| Moderator ![]() Registriert seit: 20.07.05 ![]() ![]() ![]() ![]() ![]() ![]() Likes: 202 | Zitat:
. Das funktioniert (grob gesagt) folgendermaßen: du schreibst den Code, der bestimmte WinAPI Aufrufe (also die kernel32/user32 DLL Funktionen) modifiziert. Nun haben moderne Betriebssysteme schon lange eine Speichertrennung im Userspace(Stichworte Ring0 und Ring3). Heißt im Klartext: solange es sich nicht um Treiber handelt, hat jedes Programm 4GB RAM[0] für sich und kann durch modifizieren einer Systemfunktion nicht gleich die anderen Programme kaputtmachen[1]. D.h wenn du erstmal in deinem Programm den Code von SetCursorPos oder anderen Funktionen änderst, gilt das erstmal nur für dein Programm. D.h du musst deinen Code in den Speicherblock der anderen Anwendung bringen. Am einfachsten geht das mit einer DLL. Denn diese kann man vom anderen Programm einfach laden lassen und schon kann der Code ausgeführt werden. Beim Laden der DLL wird das Betriebssystem automatisch alle nötigen Initialisierungsschritte für die DLL (Imports, Exports, das richtige Laden in den Speicher, Relocations) durchführen. Ein anderer Weg ist es, den Code direkt in die andere Anwendung zu schreiben. Dafür gibt es mehrere Ansätze und der üblichste wäre "WriteProcessMemory" (kernel32) Funktion. Bloß bekommt man hier keinerlei Hilfe vom Betriebssystem und muss alles selber machen. Das fängt schon mal damit an, dass man die Größe des Codeblocks ermitteln muss (afaik nicht in sauberem C/C++ umsetzbar )Da der Code zudem Adressunabhängig sein sollte, alle Bibliothekfunktionen die man nutzt, erstmal tabu sind. Das Gleiche gilt für WinAPIs. Möchte man trotzdem STDLIBs oder WinAPIs nutzen, muss man dafür sorgen, dass die Anwendung (in die man Code injiziert) diese lädt und entsprechend Adressen der Funktionen in dieser Anwendung herausfinden und im Code anpassen. Ohne viel inline Asm + compilerspezifischen Code/Flags und größeren Debugsessions wird das nicht umsetzbar sein ![]() Bei einer DLL hat man, wie gesagt, großteil der Probleme gar nicht. [0] 2^32 Bytes = 4GB für 32 Bit Systeme. Für 64-bit entsprechend mehr [1] Es geht auch ohne Treiber, allerdings muss der Benutzer, der das Programm ausführt, entsprechende Rechte haben und das Programm dafür vorgesehene Funktionen (OpenProcess, WriteProcessMemory, SetThreadContext oder CreateRemoteThread) nutzen. Zitat:
![]() Soweit ich weiß wird eine DLL, die von irgendeiner Anwendung genutzt wird, normalerweise vom System nur einmal geladen. Jede weitere Anwendung bekommt quasi ein "Sichtfenster" darauf (somit ist die DLL im RAM tatsächlich nur 1 mal vorhanden). Erst wenn man anfängt, die DLL zu modifizieren, bekommt die modifizierende Anwendung eine Kopie der DLL im eigenen Speicher (das ganze wird von Windows transparent gemacht, so dass es "unsichtbar" abläuft). Kurzum: eine Systemdll im Ordner der Anwendung wird gar nicht geladen. Lösung: Umbenennen der DLL (z.B nach "us3r32.dll" oder einen anderen, gleich langen und einmaligen Namen wählen). Dann umbenennen der "user32.dll" Strings in der Exe selbst zu diesem Namen (man sollte sowohl ASCII wie UNICODE Strings abarbeiten). Problem: sofern die Exe geschützt ist, wird der Schutz rummeckern wegen Patch/Modifizierung des Codes. Zudem verschlüsseln die meisten Protectoren den Code sowie die Imports der Exe und laden diese später selbst nach (dynamisch). Somit wird Gameguard (wenn überhaupt funktionierend) die neue "us3r32.dll" auch "schützen". Alternativ bastelt man halt einen Loader, der die DLL in die Anwenung bringt und dabei die vorhande ersetzt. Dazu muss er zuerst herausfinden, an welcher Adresse sich die User32.DLL in der Anwendung befindet, die eigene DLL exakt an diese Stelle laden und zugleich alle Initialisierungsschritte durchführen. Da kann man gleich den richtigen DLL-Code patchen
__________________ Noch mal, für alle Pseudo-Geeks: 1+1=0. -> 10 wäre Überlauf! Selig, wer nichts zu sagen hat und trotzdem schweigt. | ||
| | |
![]() |
| - Anzeige - | |
| |
| Themen-Optionen | |
| Ansicht | |
| |