Autor Beitrag
s!lenCe
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 24
Erhaltene Danke: 1



BeitragVerfasst: 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:

ausblenden 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:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
asm
  mov dword ptr ds:[$00555550], $051EBCE9  //
  mov dword ptr ds:[$00555554], $90909000  // > jmp to my function
  mov byte ptr ds:[$00555558], $90         //
end;


Und in meiner Funktion
ausblenden 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:
ausblenden 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 24
Erhaltene Danke: 1



BeitragVerfasst: Mi 16.12.09 09:24 
Keiner eine Idee? :?
Allesquarks
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 510

Win XP Prof
Delphi 7 E
BeitragVerfasst: Mi 16.12.09 11:04 
Eigentlich sollte das mit einem Typecast von der Prozedur gehen:
ausblenden 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.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
  //Deklaration (of object für methoden)
  TMouseEvent = procedure(Sender: TObject; Button: TMouseButton;
                          Shift: TShiftState; X, Y: Integer) of Object;

var OnMouseDown:TMouseEvent;
  //Zuweisen zur Variablen
  OnMouseDown:=@MouseDownHandler;

  //Ausführen
  OnMouseDown(bla);
  
  //Typecast
  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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 24
Erhaltene Danke: 1



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 24
Erhaltene Danke: 1



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: Mi 16.12.09 21:27 
wenn es reich am schluss ie Funktion normal aufzurufen dann mach einfach

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
procedure myCallBack;
begin
  // tu was

  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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 24
Erhaltene Danke: 1



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1207
Erhaltene Danke: 31

Win 10
Delphi 2009 Pro, C++ (Visual Studio)
BeitragVerfasst: Mi 16.12.09 21:50 
am besten geht es immer noch, alles selbst zu machen.

also z.b.:

ausblenden 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
//tu was
end;

function hookDispatch();stdcall;asm
POP ret
PUSHAD
MOV EAX,[ESP+$24//z.b. Musst du natürlich selber suchen, wie die Parameter an die Originalfunktion gingen
MOV EDX,ESI
PUSH EAX //b
PUSH EDX //a
CALL hooked
POPAD
push ebp
mov ebp, esp
sub esp, 2D4h
mov eax, $00555559
jmp eax;
end;

function DoHook();
var code:Array[0..4of Byte;
begin
code[0]:=$EB//CALL
PCardinal(@code[1])^:=Cardinal(@hookDispatch) - offset - 5//offset ist die zu patchende Stelle
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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 24
Erhaltene Danke: 1



BeitragVerfasst: Mi 16.12.09 22:00 
ausblenden 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
   // Der durch den jump überschrieben Code
   push ebp
   mov ebp, esp
   sub esp, 2D4h
   //

   // Zur Originalen Funktion springen (nächte Anweisung nach dem Jmp)
   push $00452ED9
   ret
  end;
end;

procedure TMainThread.InstallHook;
var
  Address : DWORD;
begin
  Address := $00452ED0;
  HookCode(Pointer(Address),@CallbackFunction,@NewFunction);
end;


Da ist er. ^^
Flamefire
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1207
Erhaltene Danke: 31

Win 10
Delphi 2009 Pro, C++ (Visual Studio)
BeitragVerfasst: 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:
ausblenden 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 24
Erhaltene Danke: 1



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 24
Erhaltene Danke: 1



BeitragVerfasst: 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 user profile iconNarses: Überflüssige Zeilenumbrüche/Leerzeilen entfernt.
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: 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.