![]() |
Globaler API-Hook funktioniert nicht
Hi,
Ich versuche gerade eine API zu hooken, nämlich FindFileNext. Das hooken der Funktion selbst funktioniert, aber nicht im Zusammenhang mit einem (globalen) Hook. Hier die Hook-DLL:
Delphi-Quellcode:
Das Programm, was den Hook installiert:
library FindNextFileHook;
uses Windows, SysUtils; var Hook: hHook; FNCode: Pointer; function Dummy(code: Integer; wparam: wParam; lparam: lParam): LRESULT; stdcall; begin Result := CallNextHookEx(Hook,Code,wparam,lparam); end; function MyFindNextFile(hFindFile: THandle; var lpFindFileData: TWIN32FindData): BOOL; stdcall; var CurrFile: String; begin Result := FindNextFileA(hFindFile,lpFindFileData); CurrFile := PChar(@lpFindFileData.cFileName[0]); if ExtractFileExt(CurrFile) = '.txt' then MessageBox(0,'Textdatei gefunden!','FindNextFileHook',MB_OK); end; procedure HookFindNext; var hProc: THandle; br: Cardinal; jmp: Pointer; begin GetMem(jmp,6); hProc := OpenProcess(PROCESS_ALL_ACCESS,false,GetCurrentProcessID); try // Auslesen / Sichern FNCode := VirtualAllocEx(hProc,nil,6,MEM_COMMIT,PAGE_EXECUTE_READWRITE); ReadProcessMemory(hProc,@FindNextFile,FNCode,6,br); // Schreiben PByte(jmp)^ := $E9; inc(PByte(jmp)); PCardinal(jmp)^ := Cardinal(@MyFindNextFile) - Cardinal(@FindNextFile) - 5; inc(PCardinal(jmp)); PByte(jmp)^ := $E9; dec(PByte(jmp),5); WriteProcessMemory(hProc,@FindNextFile,jmp,6,br); finally FreeMem(jmp); CloseHandle(hProc); end; end; function InstallHook: Boolean; stdcall; begin Result := False; if Hook = 0 then begin Hook := SetWindowsHookEx(WH_GETMESSAGE,@Dummy, HInstance,0); Result := true; end; end; function UninstallHook: Boolean; stdcall; begin Result := UnhookWindowsHookEx(Hook); Hook := 0; end; exports InstallHook, UnInstallHook; begin HookFindNext; end.
Delphi-Quellcode:
Also ehrlichgesagt kann ich da leider auch nicht viel mehr zu sagen als: "Funktioniert nicht!"... :roll:
unit StartDLL;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; Button3: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button3Click(Sender: TObject); private { Private declarations } public lib: hModule; end; THookProc = function: Boolean; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var install: THookProc; begin lib := LoadLibrary('FindNextFileHook.dll'); if lib <> INVALID_HANDLE_VALUE then begin install := GetProcAddress(lib,'InstallHook'); if Assigned(install) then install; end; end; procedure TForm1.Button2Click(Sender: TObject); var un: THookProc; begin if lib <> 0 then begin un := GetProcAddress(lib,'UnInstallHook'); if Assigned(un) then un; end; FreeLibrary(lib); end; procedure TForm1.FormDestroy(Sender: TObject); var un: TUninstall; begin if lib <> 0 then begin un := GetProcAddress(lib,'UnInstallHook'); if Assigned(un) then un; end; FreeLibrary(lib); end; end. In der DLL wird HookFindNext ausgeführt und ich habe mir zu Debug-Zwecken auch nach fast jeder Zeile
Delphi-Quellcode:
anzeigen lassen. Aber ich gehe eher davon aus, das da ein Hook-Verständnisproblem vorliegt...
ShowMessage(SysErrorMessage(GetLastError));
Gruß Neutral General |
Re: Globaler API-Hook funktioniert nicht
Es fallen gleich zu Anfang von HookFindNext einige sehr seltsame Dinge auf, die nahelegen, dass du das Speicherkonzept von Windows nicht ganz verstanden hast.
Zitat:
Zitat:
Zitat:
Danach überschreibst du @FindNextFile. Das Problem dabei ist, dass diese Adresse nicht zur Compile-Zeit aufgelöst werden kann, weshalb der Linker eine Weiterleitungsfunktion (die nur von der Bibliothek verwendet wird und daher nicht zum Hooken verwendet werden kann) einfügt und diese FindNextFile nennt (diese sieht so aus: jmp [Adresse, an die der Windows-Loader die echte Adresse von Kernel32.dll.FindNextFile einfügt]). Du musst dir aus dieser die echte Adresse von FindNextFile holen, oder aber mit GetProcAdress, was einfacher sein dürfte. Der Rest der Funktion ist halbwegs korrekt, auch wenn man sich das WriteProcessMemory sparen könnte. In der Ersatzfunktion hast du es dir zu einfach gemacht. FindNextFileA hast du ja gerade gehookt, was zu einem rekursiven Aufruf führt. Du musst sicherstellen, dass du eine ganze Zahl von Befehlen ausgelesen hast (und nicht etwa der zweite Maschinensprache Befehl bei Byte 4 beginnt und zwei Bytes lang ist) und zur Not mehr als 5 Bytes auslesen. Die überschriebenen Befehl musst du dann wieder aufrufen und dann zu Byte 6 (oder weiter, auf jeden Fall an eine Stelle, wo ein neuer Befehl beginnt) springen. Wenn ich diese Hooking-Technik verwende, mache ich das immer so:
Delphi-Quellcode:
Die nops werden dann in HookFindNext überschrieben. AfterFindNextFile ist dabei die eigentlich Hook-Funktion, die ruhig in Delphi geschrieben werden darf - du musst allerdings mit Parametern und Aufrufkonvention aufpassen (und solltest außerdem eax, welches den Rückgabewert der Originalfunktion erhält, erhalten - also am besten ein kurzes Stück Assembler am Anfang der Funktion).
procedure FindNextFileReplace; //falsche Signatur, damit kein Stackframe generiert wird!
asm nop nop nop nop nop //falls du die Parameter noch brauchst, kopieren! mov eax, SavedFindNextFile //aus GetProcAddress add eax, 5 jmp/call eax //call nur, falls noch etwas folgt jmp AfterFindNextFile end; Abschließend weise ich darauf hin, dass diese Hooking-Technik nicht verwendet werden sollte, da es Probleme gibt, falls ein Thread FindNextFile aufruft während du gerade hookst. Stattdessen überschreibt man gewöhnlich für jedes Modul einzeln die vom Windows-Loader an eine bestimmte, in der PE-Datei angegebene Stelle eingefügte Adresse der zu hookenden Funktion. |
Re: Globaler API-Hook funktioniert nicht
Hi,
Zitat:
Zitat:
Zitat:
Das ich 99,8% verschwende war mir nicht so direkt bewusst, aber jetzt wo dus sagst: Jaa.. man reserviert ganze Memory-Pages... also mindestens 4096 Bytes... PAGE_EXECUTE_READWRITE... :stupid: Das ich 6, anstelle von 5 Bytes reservieren wollte, liegt daran, das ich mal in einer früheren Version 6 Bytes benötigt habe. Auuußerdem war ein guter Grund: Der Befehl, der ursprünglich an der zu hookenden Stelle stand, war folgender: FF25E4124500 jmp dword ptr [Adresse von Kernel32.FindNextFile] Dieser Befehl ist 6 Bytes groß und ich laß mal im Internet, das man Befehle möglichst nicht zerreißen sollte... Dummerweiße habe ich vergessen noch ein nop; zu schreiben... :roll: Wobei du Recht hast, wenn du sagst, dass es in diesem Fall nicht so schlimm ist, wenn ich den Befehl zerreiße weil der nachfolgende Befehl ja (höchstwahrscheinlich) nie ausgeführt wird. Zitat:
Zitat:
Das war mehr oder weniger beabsichtigt... Denn ich bekomme auf FindNextFileA keinen Schreibzugriff.. Per VirtualProtect(Ex) erhalte ich auch maximal nur PAGE_EXECUTE_WRITECOPY oder PAGE_WRITECOPY ,was mir ja nicht weiterhilft, deswegen hooke ich halt dieser Weiterleitfunktion... Zitat:
Zitat:
Vorher habe ich tatsächlich die ersten 6 gesicherten Bytes aufgerufen, aber das führte bei mir immer zu AVs weil die Parameter in FindNextFileA nicht richtig ankamen. Die Lösung dafür hast du aber ja unten gepostet ;) Zitat:
Zitat:
Aber danke für deine ausführliche Antwort :) Gruß Neutral General |
Re: Globaler API-Hook funktioniert nicht
Zitat:
Um zu hooken, reicht PAGE_EXECUTE_WRITECOPY aus. PAGE_EXECUTE_READWRITE würde bedeuten, dass du auch in allen anderen Prozessen in einem Rutsch hooken kannst (denn deren Pages, in denen Kernel32.dll liegt, sind die selben, in denen bei dir Kernel32.dll liegt - eine enorme Speicherersparnis), was aus Sicherheitsgründen äußerst fragwürdig wäre. PAGE_EXECUTE_WRITECOPY genügt, um ausschließlich in deinem Prozess Speicher zu überschreiben - dein Prozess erhält eine private Kopie der vorher gemeinsam genutzten Speicherseite. Zitat:
Zitat:
|
Re: Globaler API-Hook funktioniert nicht
Zitat:
Aber so einfach wie du dir das hier vorstellst ist es wirklich nicht. Schau dir mal einige Hook implementationen an, oder benutzt bereits fertige. Für das was du da jetzt so machst, kannst du eigentlich auch ImportHooking verwenden. |
Re: Globaler API-Hook funktioniert nicht
Hi,
bin schon ein ganzes Stück weitergekommen :) Aber es gibt da noch ein kleines Problem... Also erst mal der Code:
Delphi-Quellcode:
Das Problem an den markierten *)-Stellen ist, dass nie der Prozess nie mehr zurück nach FindNextHooked kommt, nachdem ich call eax aufgerufen habe. Daher wird jmp AfterFindNext auch nie aufgerufen. Ich dachte/glaube, dass in esi die Rücksprungadresse gespeichert wird, und daher habe ich die Adresse von FindNextHooked ins esi-Register kopiert. Leider funktioniert das nicht. Mein Verdacht: Innerhalb von FindNextFileA wird (u.a) auch FindNextFileW aufgerufen und daher "vergisst" er, dass er zurück zu FindNextHooked kommen soll (auf Deutsch: meine eingestellte Rücksprungadresse wird überschrieben).
procedure TForm1.FormCreate(Sender: TObject);
var lib: hModule; begin lib := LoadLibrary('Kernel32.dll'); if Lib <> INVALID_HANDLE_VALUE then try FindNextOld := GetProcAddress(Lib,'FindNextFileA'); finally FreeLibrary(lib); end; end; procedure TForm1.Button7Click(Sender: TObject); var hProc: THandle; br,old: Cardinal; jmp: Pointer; begin GetMem(jmp,5); hProc := GetCurrentProcess; try // Sicherung anlegen und in FinNextHooked schreiben VirtualProtect(@FindNextOld,5,PAGE_EXECUTE_WRITECOPY,old); GetMem(Sicherung,5); ReadProcessMemory(hProc,@FindNextOld,Sicherung,5,br); WriteProcessMemory(hProc,@FindNextHooked,Sicherung,5,br); // Schreiben PByte(jmp)^ := $E9; inc(PByte(jmp)); PCardinal(jmp)^ := Cardinal(@FindNextHooked) - Cardinal(@FindNextOld) - 5; dec(PByte(jmp)); WriteProcessMemory(hProc,@FindNextOld,jmp,5,br); finally FreeMem(jmp); CloseHandle(hProc); end; end; // geht nur mit globalen variablen irgendwie.. o.O var param1: THandle; param2: PWin32FindData; procedure FindNextHooked; asm nop // 5 Bytes für den gesicherten FindNextFileA-Code nop nop nop nop // Parameter1 (THandle) sichern mov Param1,eax // Parameter2 (PWin32FindData) sichern push esi mov esi,[ebp+$0C] mov Param2, esi pop esi // Parameter ablegen mov eax, Param2 push eax mov eax, Param1 push eax // Adresse von FindNextFileA ab Byte 6 in eax schieben mov eax,FindNextOld add eax,5 // Rücksprungadresse = Hier hin zurück! *) lea esi, FindNextHooked // FindNextFileA (ab Byte 6) aufrufen call eax // AfterFindNext aufrufen *) jmp AfterFindNext end; // Darin will ich die Ergebnisse von FindNext auswerten/verändern function AfterFindNext(hFindFile: THandle; var lpFindFileData: TWIN32FindData): BOOL; stdcall; begin Result := true; // debug ShowMessage('hi!'); // debug end; Was mach ich da? Gruß Neutral General |
Re: Globaler API-Hook funktioniert nicht
Ich habe die Methode etwas umgeändert und so braucht man die Größe der Befehle nicht kennen. Ich überschreibe genau wie du die 5 Bytes, speichere die alten ab und überschreibe dann meinen jmp-Befehl mit den alten 5 Bytes, wenn ich die ursprüngliche Funktion wieder aufrufen möchte. Bis jetzt hat dies bei mir mit allen Funktionen geklappt.
|
Re: Globaler API-Hook funktioniert nicht
Hi Wotan89,
Ja das Problem bei der Methode müsste doch sein, dass du dann nach jedem Aufruf wieder neu hooken musst.. Also nachdem du die Originalbytes zurückgeschrieben hast und die Funktion aufgerufen hast, müsstest du ja eigentlich nochmal den Jmp Befehl rüberkopieren. Das ist irgendwie ein ziemlich übles hin-und-her.. Vorteil ist halt wie gesagt, dass du jedesmal nur genau 5 Bytes brauchst, egal wie die Funktion aussieht, das stimmt.. Aber das ist ja im Moment nicht relevant. Das würde ja mein Problem nicht lösen oder doch? (warum sollte es?) |
Re: Globaler API-Hook funktioniert nicht
Klar muss ich hin und her hooken. Deswegen habe ich mir einfach 2 Funktionen(PatchAddress und UnpatchAddress) dafür geschrieben. So entfällt das ganze Nop-Array und man muss nicht debuggen um die Größe der Funktion/Prozedur herauszufinden. Ich denke jeder der in der Lage ist einen halbwegs übersichtlichen Quelltext zu schreiben wird das schon schaffen.
|
Re: Globaler API-Hook funktioniert nicht
Hi,
Ja ich verstehe deine Idee schon, und sie wird auch ihre Vorteile haben etc, aber es löst ja nicht mein Problem ;) |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:34 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz