Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Detours : Routinen hooken (https://www.delphipraxis.net/100567-detours-routinen-hooken.html)

sk0r 30. Sep 2007 17:12


Detours : Routinen hooken
 
Hi,

ich möchte gerne eine Routine durch einen Detour hooken.
Nur leider klappt es bei mir nicht. Ich möchte die ersten
fünf Bytes der zu hookenden Routine mit einem jmp + address
überschreieben, welche zu einem allozierten Speicher zeigt.
In diesem allozierten Speicherbereich habe ich mir zehn
Bytes reserviert: Die ersten fünf sind noch von der Routine,
denn sie müssen ja auch ausgeführt werden. Danach kommt ein
jmp zu einer neuen Adresse und zwar zu der neuen Routine.
Um dann in der gehookten Routine auch die alte Routine ausführen
zu können, rufe ich die originale einfach auf, aber überspringe
die ersten fünf Bytes, damit ich nicht in einer Endlosschleife lande.

So sieht die Theorie aus, aber in der Praxis funktioniert es nicht. :(

Im folgenden Beispiel möchte ich die API-Prozedur Sleep() hooken.

Delphi-Quellcode:
type T_Sleep = procedure(dwMilliseconds: Cardinal);stdcall;

var pSleep: T_Sleep;

//Neue Sleep Prozedur
procedure NewSleep(dwMilliseconds: Cardinal);stdcall;
begin
  MessageBox(0, 'Test', 'Q', 0);
  pSleep(dwMilliseconds);
end;

function DetourHook(lpModule, lpRoutine: PChar; pTargetAddr, pNewAddr: Pointer):Pointer;
var
  pBackupAddr: Pointer;
  dwFiveBytes: DWord;
  pResultAddr: Pointer;
  dwJmpCode: DWord;
  dwJmpGateway: DWord;
  hAlloc: Pointer;
  bVProtect: Boolean;
  dwvProtect: DWord;
const
  DETOUR_JMP = $E9;
  DETOUR_SIZE = $05;
begin
  result := nil;
  pBackupAddr := GetProcAddress(GetModuleHandle(lpModule), lpRoutine); //Adresse der Routine einholen, zur weiteren Verwendung
  pResultAddr := Pointer(Cardinal(pBackupAddr) + (DETOUR_SIZE)); //Originale Adresse + fünf Bytes speichern, damit er nicht mehr den Jump Code ausführt (wird dann der result Wert)
  CopyMemory(@dwFiveBytes, pTargetAddr, DETOUR_SIZE); //Die Bytes, welche gleich überschrieben werden, kopieren.
  dwJmpCode := dwFiveBytes + DWord(DETOUR_JMP) + DWord(pNewAddr^); //Die zu überschreibenden 5 Bytes + Jump opcode + die Adresse, auf die gesprungen werden soll
  hAlloc := VirtualAlloc(0, DETOUR_SIZE*2, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE); //Irgendwo im Bereich des Prozesses Speicher allozieren
  if hAlloc <> nil then
  begin
    bVProtect := VirtualProtect(hAlloc, DETOUR_SIZE*2, PAGE_READWRITE, dwvProtect); //Zugriffsrechte für Lesen und Schreiben einholen
    if bVProtect then
    begin
      CopyMemory(hAlloc, @dwJmpCode, DETOUR_SIZE*2); //Den Jump Code in den Bereich schreiben
      dwJmpGateway := DWord(DETOUR_JMP) + DWord(hAlloc); //Jump + Adresse des neuen allozierten Speicherbereichs
       if VirtualProtect(pTargetAddr, DETOUR_SIZE, PAGE_READWRITE, dwvProtect) then //Schreib - und Leserechte an der originalen Adresse einholen
      begin
        CopyMemory(pTargetAddr, @dwJmpGateway, DETOUR_SIZE); //Den Jump Code zum Gateway an die originale Adresse kopieren
        result := pResultAddr;
      end;
    end;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  lol, retaddr: Pointer;
begin
  lol := GetProcAddress(GetModuleHandle('kernel32.dll'), 'Sleep');
  if lol <> nil then
  begin
    retaddr := DetourHook('kernel32.dll', 'Sleep', lol, @NewSleep);
    if retaddr <> nil then
    begin
      ShowMessage('Detoured');
      pSleep := retaddr; //Backup Adresse zum Backup zuweisen
      //Sleep(2007);
    end
    else begin
      ShowMessage('Error');
    end;
  end;
end;
So weit bin ich nun gekommen. Ich bekomme aber immer eine Zugriffsverletzung,
wenn ich dann Sleep() aufrufen will. Deshalb nun meine Frage: Weiß jemand,
wo denn mein Fehler liegt? Ich weiß einfach nicht, was ich falsch mache...

Ich bedanke mich im Voraus für Hilfe.

MfG: sk0r

brechi 30. Sep 2007 17:38

Re: Detours : Routinen hooken
 
Jo du darst keine halben Befehle ausführen. Wer sagt denn, dass bei 5 Bytes die genaue größe ist? Du musst dir einen Disassembler schreiben der den Code analysiert und dann die entsprechende richtige Anzahl kopiert.

sk0r 5. Okt 2007 14:02

Re: Detours : Routinen hooken
 
Naja, es ist schon die richtige Größe.

Wenn ich z.B. Sleep aus der kernel32.dll hooken möchte, dann bestehen
die Bytes, die ich überschreibe aus dem stdcall. Dies rekonstruiere ich
bei der neuen Sleep wieder.

Falls es jemand anderen noch interessiert, hier der Code mit Beispiel.

Delphi-Quellcode:

var origSleep: Pointer;

procedure NewSleep(dwMilliseconds: Cardinal);stdcall;
begin
  MessageBox(0, 'Hallo aus der neuen Prozedur.', 'NewSleep', MB_ICONINFORMATION);
  asm
    jmp origSleep
  end;
end;

function DetourHook(lpModule, lpRoutine: PChar; pNewAddr: Pointer):Pointer;
type
  TDetourRec = packed record
    bJmpOpcode: Byte;
    dwAddress: DWord;
end;
var
  lpDetourCode: TDetourRec;
  lpGatewayCode: TDetourRec;
  pTargetAddr: Pointer;
  pJmpGateway: Pointer;
  dwTargetProtect: DWord;
const
  DETOUR_JMP = $E9;
  DETOUR_SIZE = $05;
begin
  result := nil;

  pTargetAddr := GetProcAddress(GetModuleHandle(lpModule), lpRoutine);
  if pTargetAddr = nil then exit;

  pJmpGateway := VirtualAlloc(0, DETOUR_SIZE, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  if pJmpGateway <> nil then
  begin

    lpDetourCode.bJmpOpcode := DETOUR_JMP;
    lpDetourCode.dwAddress := DWord(pNewAddr) - DWord(pJmpGateway) - DETOUR_SIZE;

    CopyMemory(pJmpGateway, @lpDetourCode, DETOUR_SIZE);

    lpGatewayCode.bJmpOpcode := DETOUR_JMP;
    lpGatewayCode.dwAddress := DWord(pJmpGateway) - DWord(pTargetAddr) - DETOUR_SIZE;

    if VirtualProtect(pTargetAddr, DETOUR_SIZE, PAGE_EXECUTE_READWRITE, dwTargetProtect) then
    begin
      CopyMemory(pTargetAddr, @lpGatewayCode, DETOUR_SIZE);
      result := Pointer(DWord(pTargetAddr) + DETOUR_SIZE);
    end;
  end;
end;

begin
  origSleep := DetourHook('kernel32.dll', 'Sleep', @NewSleep);
  if origSleep = nil then ExitProcess(0);
  Sleep(2007);
  MessageBox(0, 'I slept 2007 milliseconds', 'EntryPoint', MB_ICONINFORMATION);
end.
Ja, ich weiß, ich brauch gar keinen Speicherbereich allozieren, es geht auch
mit weniger Code, bzw. einfacher, aber das kann ja jeder selbst ändern.

Flocke 5. Okt 2007 15:15

Re: Detours : Routinen hooken
 
Also bei mir fängt kernel32.Sleep so an:
Code:
8BFF   mov edi, edi
55      push ebp
8BEC   mov ebp, esp
6A00    push 0
Dein Detour überschreibt die ersten 5 Bytes - diese sind aber wichtig, da sie den Stackframe erzeugen.

Wie Brechi schon schrieb: du kannst nicht einfach Maschinencode überschreiben, ohne ihn analysiert zu haben. Dazu brauchst du einen Disassembler oder zumindest ein vereinfachte Abwandlung davon (z.B. hier).

Du musst also mehr oder minder wissen, was der Code macht, den du überschreibst - dann kannst du den überschriebenen Bereich sichern (und ggf. relozieren) und ab einer gewissen Stelle im Originalcode weitermachen.

sk0r 5. Okt 2007 15:54

Re: Detours : Routinen hooken
 
Das denke ich ja auch, mir wurde aber gesagt, dass in diesem Fall, also obigen Beispiel, man
das nicht beachten muss, da die Bytes für stdcall benutzt werden. Man überschreibt also quasi
das stdcall, welches aber in der neuen Prozedur rekonstruiert wird, somit also kein Verlust entsteht.
So habe ich das verstanden, was mit von jemanden aus einem anderen Forum erklärt wurde.

Ich zitiere ihn mal:

Zitat:

In diesem Fall braucht man keinen Backup der Original-bytes, da diese nur aus dem stdcall Function-Prolog bestehen, welches wir dadurch rekonstruieren, dass wir unsere Callback-Funktion NewSleep als solche Kennzeichnen.
Ihr könnt ja mal den Code testen, er sollte funktionieren. Bei mir funktioniert er.


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:50 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz