Autor |
Beitrag |
s!lenCe
      
Beiträge: 24
Erhaltene Danke: 1
|
Verfasst: Di 15.12.09 23:16
Hallo allerseits,
folgendes ich möchte einen Hook in einer externen Anwendung per DLL-Injection setzen.
Hintergrund: Ich will eine Erweiterung für einen Game-Server schreiben.
Da ich noch nicht so ganz in der Materie mit Assembler und Hooks stecke habe ich ein paar Fragen.
Ich habe die .exe des Servers disassembled und mir die Adresse der Zielfunktion geholt.
Diese ist sagen wir mal "00555550". Dort ist also praktisch der Anfang der Funktion, die erste Anweisung.
Die ersten drei Anweisungen sehen wie folgt aus:
Delphi-Quelltext 1: 2: 3:
| push ebp mov ebp, esp sub esp, 2D4h |
Ich möchte diese 9 Bytes jetzt überschreiben mit einem Sprung zu meiner Funktion. In der Funktion werden zuerst dann die überschriebenen
Anweisungen ausgeführt und anschließend zum nächsten Anweisung gesprungen. Das mache ich momentan so:
Delphi-Quelltext 1: 2: 3: 4: 5:
| asm mov dword ptr ds:[$00555550], $051EBCE9 mov dword ptr ds:[$00555554], $90909000 mov byte ptr ds:[$00555558], $90 end; |
Und in meiner Funktion
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| procedure Test; begin asm push ebp mov ebp, esp sub esp, 2D4h mov eax, $00555559 jmp eax; end; end; |
Ich kopiere also die einzelnen Opcodes in die entsprechenden Stellen.
Die Opcodes bedeuten folgendes:
Delphi-Quelltext 1: 2: 3: 4: 5:
| jmp 004a4d91 nop nop nop nop |
Ich habe die Adresse zu der gesprungen wird (004a4d91) frei erfunden, denn da müsste die Adresse meiner eigenen (Delphi-)Funktion
hin. Nur weiß ich nicht wie ich die Adresse meiner Funktion ermitteln kann und sie in die einzelnen Opcodes aufteilen, sodass ich sie
in den Anweisungen oben anwenden kann.
Vielleicht gibt es auch einen viel einfacheren Weg. Ich bin für alles offen ^^.
greetz silenCe
|
|
s!lenCe 
      
Beiträge: 24
Erhaltene Danke: 1
|
Verfasst: Mi 16.12.09 09:24
Keiner eine Idee? 
|
|
Allesquarks
      
Beiträge: 510
Win XP Prof
Delphi 7 E
|
Verfasst: Mi 16.12.09 11:04
Eigentlich sollte das mit einem Typecast von der Prozedur gehen:
Delphi-Quelltext 1:
| myaddress:=integer(@myfunction); |
Schöner sind Methoden- oder Funktionenzeiger. In Delphi definiert man einen Funktions- oder ProzedurTyp!! Einer Variable dieses Typs kann man dann eine Funktion zuweisen. Das ist natürlich nichts anderes als die Adresse. Den Typ braucht nur der Compiler, damit er weiß, was in die Register und auf den Stack gehört. mit einem Typecast dieser Variablen solltest du deine Sprungadresse haben.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| TMouseEvent = procedure(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer) of Object;
var OnMouseDown:TMouseEvent; OnMouseDown:=@MouseDownHandler;
OnMouseDown(bla); myaddress:=integer(OnMouseDown); |
Ersteres geht wohl eher Hardgecoded, mit Letzterem kannst Dir ne Funktion schreiben der du die zu injizierende Funktion übergeben kannst.
Opcodes solltest du dir dann mit ein paar logischen Operatoren und Schieben bauen können.
|
|
s!lenCe 
      
Beiträge: 24
Erhaltene Danke: 1
|
Verfasst: Mi 16.12.09 20:11
So habe es jetzt mit der HookCode aus der uallCollection gemacht.
Er springt in die Callback-Funktion sobald die Funktion vom Server aufgerufen wird
(mittles Showmessage getestet). Nur wenn ich dann in der Callback-Funktion zur Original-Funktion
zurückspringe, dann crasht der Server. An sich sollte das aber richtig sein. Liegt es eventuell daran,
dass die Funktion eigentlich (wenn Sie im Server aufgerufen wird) per __fastcall aufgerufen wird, ich sie
aber normal aufrufe? Würde das einen Crash verursachen?
greetz silenCe
|
|
uall@ogc
      
Beiträge: 1826
Erhaltene Danke: 11
Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
|
Verfasst: Mi 16.12.09 20:31
__fastcall ist nicht wirklich definiert, d.h. es kann daran liegen. In Delphi liegen die Parameter in EAX,EDX,ECX,Stack ...
Dies muss bei Fastcall nicht sein, werden z.b. nur 2 Register verwendtet crashedesnatürlich(und die Parameter liegen and der falschen Stelle)
_________________ wer andern eine grube gräbt hat ein grubengrabgerät
- oder einfach zu viel zeit
|
|
s!lenCe 
      
Beiträge: 24
Erhaltene Danke: 1
|
Verfasst: Mi 16.12.09 21:18
Ich müsste das doch umgehen können indem ich in meiner Callback
per ASM Routine selber wieder zum Original-Code springe (d.h. nach dem eingefügten JMP bzw. push/ret)
oder? Denn dann muss ich ja selber die Funktion eigentlich nicht aufrufen.
btw.: Finde deine uallCollection echt super. Gute Arbeit
greetz silenCe
|
|
uall@ogc
      
Beiträge: 1826
Erhaltene Danke: 11
Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
|
Verfasst: Mi 16.12.09 21:27
wenn es reich am schluss ie Funktion normal aufzurufen dann mach einfach
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| procedure myCallBack; begin asm jmp[nextHook] end; end; |
kommst halt nur net so leicht an die parameter
_________________ wer andern eine grube gräbt hat ein grubengrabgerät
- oder einfach zu viel zeit
|
|
s!lenCe 
      
Beiträge: 24
Erhaltene Danke: 1
|
Verfasst: Mi 16.12.09 21:44
Hmm habe es gemacht wie du geschrieben hast am Ende der Callback-Funktion den Jmp zur originalen
Funktion, habe aber halt noch vor dem jmp die ASM-Anweisungen, die durch den Hook überschrieben worden
sind reingeschrieben. Der Server crasht aber immernoch an der Stelle.
Wenn der Hook platziert wird macht er ja am Anfang der Funktion eine Push/Ret Anweisung, um zur Callback zu
springen. Beeinflusst das auch nachwirkend den Stack (wegen dem Push) oder wird er durch das Ret wieder in den Urzustand
versetzt?
greetz silenCe
|
|
Flamefire
      
Beiträge: 1207
Erhaltene Danke: 31
Win 10
Delphi 2009 Pro, C++ (Visual Studio)
|
Verfasst: Mi 16.12.09 21:50
am besten geht es immer noch, alles selbst zu machen.
also z.b.:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29:
| var ret:Pointer; function hooked(a,b:Cardinal); stdcall; begin end;
function hookDispatch();stdcall;asm POP ret PUSHAD MOV EAX,[ESP+$24] MOV EDX,ESI PUSH EAX PUSH EDX CALL hooked POPAD push ebp mov ebp, esp sub esp, 2D4h mov eax, $00555559 jmp eax; end;
function DoHook(); var code:Array[0..4] of Byte; begin code[0]:=$EB; PCardinal(@code[1])^:=Cardinal(@hookDispatch) - offset - 5; Move(Ptr(offset)^,code[0],5); end; |
Ich habe als Umleitung einen CALL genommen, hat den Vorteil, dass du in deiner Dispatch funktion prüfen kannst, woher das Ding kam. Auf die NOPs habe ich verzichtet. sind eigendlich nicht nötig, da der Code nie ausgeführt wird.
Ansonsten: Zeige mal den Code, den du jetzt hast
|
|
s!lenCe 
      
Beiträge: 24
Erhaltene Danke: 1
|
Verfasst: Mi 16.12.09 22:00
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
| var NewFunction : procedure();
procedure CallbackFunction(); begin asm push ebp mov ebp, esp sub esp, 2D4h push $00452ED9 ret end; end;
procedure TMainThread.InstallHook; var Address : DWORD; begin Address := $00452ED0; HookCode(Pointer(Address),@CallbackFunction,@NewFunction); end; |
Da ist er. ^^
|
|
Flamefire
      
Beiträge: 1207
Erhaltene Danke: 31
Win 10
Delphi 2009 Pro, C++ (Visual Studio)
|
Verfasst: Mi 16.12.09 22:58
So weit ich weiß, übernimmt der uallhook schon das zurückersetzen der überschriebenen bytes.
wenn du das so lösen willst reicht ein: Delphi-Quelltext 1: 2: 3: 4:
| procedure CallbackFunction() asm JMP[NewFunction] end; |
Bin mir aber nicht sicher. Probiers aus und guck dir mit nem Debugger an, was aus den Anweisung wird. Geht auch mit dem Delphi-Debugger, wenn du eine eigene funktion hookst.
Dann kennst du zumindest das Prinzip.
|
|
s!lenCe 
      
Beiträge: 24
Erhaltene Danke: 1
|
Verfasst: Mi 16.12.09 23:09
So wie es oben steht ist es eigentlich von der Reihenfolge richtig, wenn ichs mir im Debugger anschaue.
Er springt am Anfang der Original-Funktion zu meiner Funktion und in meiner Funktion stehen dann die ASM-Anweisungen, die überschrieben
worden sind und dann springt er wieder zum Anfang der Original-Funktion + 9 Bytes (9, da ich die überschriebenen 9 Bytes ja schon in meiner Funktion stehen habe),
da wo praktisch der Code weitergeht.
Der Server hängt sich aber trotzdem weg. Mein Ziel ist es eigentlich nur, wenn der Hook sitzt die Parameter mit der die Funktion aufgerufen wird auszuwerten.
Also ich selbst will diese Funktion niemals mit eigenen Parametern aufrufen. Muss ich vielleicht zusätzlich irgendwas beachten, da die Funktion eigentlich eine
Methode einer Klasse ist?
greetz silenCe
|
|
uall@ogc
      
Beiträge: 1826
Erhaltene Danke: 11
Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
|
Verfasst: Do 17.12.09 08:29
uallHook übernimmt den aufruf der überschriebenen Bytes.
Das Push/Ret ändert nichts am Stack und ist somit nichts anderes als ein Jump.
Wie gesagt: einfach ein JMP [nextHook] (dieser hat die überschriebenen Bytes am Anfang und übernimmt somit alles).
Step doch mal durch und schau was genau passiert.
_________________ wer andern eine grube gräbt hat ein grubengrabgerät
- oder einfach zu viel zeit
|
|
s!lenCe 
      
Beiträge: 24
Erhaltene Danke: 1
|
Verfasst: So 20.12.09 19:13
Hallo,
da bin ich wieder ^^.
Habe nun ein anderes Problem. Ich versuche eine andere Funktion zu hooken und benutze dafür wieder HookCode. Das Problem ist aber, dass er an der Adresse, an der er den Hook setzen soll einfach nichts macht. Er lässt den Originalen Code einfach stehen, anstatt das push / ret reinzuschreiben. Ich dachte, dass der Bereich evtl. schreibgeschützt ist und habe ihn mit VirtualProtect schreibbar gemacht, was aber keinen Unterschied macht.
Woran kann es liegen, dass er die Bytes nicht überschreibt?
Edit: Ok habs gelöst in dem ich in der uallHook.pas das Exit beim unknown assembler instruction Fehler in der Funktion HookCodeNt weggenommen habe.
greetz silenCe
Moderiert von Narses: Überflüssige Zeilenumbrüche/Leerzeilen entfernt.
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mo 21.12.09 02:05
Könntest Du bitte zu diesem Problem ein wenig mehr Details schreiben?
So z.B. Start der zu hookenden Funktion usw.? ggf. kann man da in der uallHookCode insgesamt einen Patch einbauen, damit es korrekt geht.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|