Ist das Projekt einen „Rahmen“ zu entwickeln, die (?) wird anderen erlauben, verschiedene Funktionen in verschiedenen Binärdateien zu haken? Oder ist es nur das Sie müssen dieses spezifische Programm, das Sie haben, Haken?
Zuerst nehmen wir an, Sie wollen die zweite Sache, Sie haben nur eine Funktion in einer Binärdatei, die Sie wollen, programmgesteuert und zuverlässig. Das Hauptproblem bei dieser universellen Vorgehensweise ist, dass es ein sehr hartes Spiel ist, dies zuverlässig zu tun, aber wenn Sie bereit sind, Kompromisse einzugehen, dann ist es definitiv machbar. Nehmen wir an, das ist x86-Sache.
Wenn Sie eine Funktion haken möchten, gibt es mehrere Möglichkeiten, wie Sie es tun können. Was Detours tut, ist Inline-Patching. Sie haben einen schönen Überblick darüber, wie es in einem Research PDF document funktioniert. Die Grundidee ist, dass Sie eine Funktion haben, z.B.
00E32BCE /$ 8BFF MOV EDI,EDI
00E32BD0 |. 55 PUSH EBP
00E32BD1 |. 8BEC MOV EBP,ESP
00E32BD3 |. 83EC 10 SUB ESP,10
00E32BD6 |. A1 9849E300 MOV EAX,DWORD PTR DS:[E34998]
...
...
Jetzt haben Sie den Beginn der Funktion mit einem CALL oder JMP an Ihre Funktion ersetzen und die ursprünglichen Bytes speichern, die Sie mit dem Patch irgendwo überschrieben:
00E32BCE /$ E9 XXXXXXXX JMP MyHook
00E32BD3 |. 83EC 10 SUB ESP,10
00E32BD6 |. A1 9849E300 MOV EAX,DWORD PTR DS:[E34998]
(Beachten Sie, dass ich überschrieb 5 Bytes .) Nun wird Ihre Funktion mit denselben Parametern und derselben Aufrufkonvention wie die ursprüngliche Funktion aufgerufen.Wenn Ihre Funktion will das Original nennen (aber es muss nicht), erstellen Sie ein „Trampolin“, dass 1) die ursprünglichen Anweisungen ausgeführt wird, die 2) jmps der ursprünglichen Funktion der restlichen überschrieben wurden:
Trampoline:
MOV EDI,EDI
PUSH EBP
MOV EBP,ESP
JMP 00E32BD3
Und das ist es, Sie müssen nur die Trampolin-Funktion in der Laufzeit durch Ausgeben von Prozessoranweisungen erstellen. Der schwierige Teil dieses Prozesses besteht darin, dass er zuverlässig funktioniert, für jede Funktion, für jede Aufrufkonvention und für verschiedene Betriebssysteme/Plattformen. Eines der Probleme besteht darin, dass die 5 Byte, die Sie überschreiben möchten, in der Mitte einer Anweisung enden. Um "Ende der Anweisungen" zu erkennen, müssten Sie grundsätzlich einen Disassembler einbauen, da zu Beginn der Funktion eine Anweisung vorhanden sein kann. Oder wenn die Funktion selbst kürzer als 5 Bytes ist (eine Funktion, die immer 0 zurückgibt, kann als XOR EAX,EAX; RETN
geschrieben werden, was nur 3 Bytes ist).
Die meisten aktuellen Compiler/Assembler produzieren einen 5 Byte langen Funktionsprolog, genau zu diesem Zweck, Hooking. Siehst du das MOV EDI, EDI
? Wenn du dich fragst: "Warum zum Teufel bewegen sie Edi nach Edi? Das macht nichts!" Sie sind absolut richtig, aber das ist der Zweck des Prologs, genau 5 Bytes lang zu sein (nicht in der Mitte einer Anweisung zu enden). Beachten Sie, dass das Disassembly-Beispiel nicht etwas ist, das ich erfunden habe, es ist calc.exe unter Windows Vista.
Der Rest der Haken Implementierung ist nur technische Details, aber sie können Ihnen viele Stunden Schmerzen bringen, denn das ist der schwierigste Teil ist. Auch das Verhalten beschrieben Sie in Ihrer Frage:
void MyInstallRules(void)
{
if(PreHook() == block) // <-- First a 'pre' hook which can block the function
return;
int * val = InstallRules(); // <-- Call original function
PostHook(val); // <-- Call post hook, if interest of original functions return value
}
scheint schlimmer als das, was ich beschrieben (und was Detours der Fall ist), zum Beispiel möchten Sie vielleicht „das Original nicht nennen“, sondern etwas anderen Wert zurück. Oder rufen Sie die ursprüngliche Funktion zweimal auf. Lassen Sie stattdessen Ihren Hookhandler entscheiden, ob und wo er die ursprüngliche Funktion aufrufen wird. Auch dann brauchen Sie nicht zwei Handler-Funktionen für einen Hook.
Wenn Sie nicht über genügend Wissen über die Technologien haben Sie sich für diese (meist Montage) benötigen, oder nicht wissen, wie das Einhaken zu tun, ich schlage vor, Sie untersuchen, was Detours tut. Haken Sie Ihre eigene Binärdatei und nehmen Sie einen Debugger (zum Beispiel OllyDbg), um auf Assemblerebene zu sehen, was genau gemacht wurde, welche Anweisungen platziert wurden und wo. Auch this tutorial könnte sich als nützlich erweisen.
Wie auch immer, wenn Sie Ihre Aufgabe, einige Funktionen in einem bestimmten Programm Haken ist, dann ist dies machbar und wenn Sie Probleme haben, fragen Sie einfach hier wieder. Im Grunde können Sie eine Menge Annahmen treffen (wie die Funktion Prolog oder verwendete Konventionen), die Ihre Aufgabe viel einfacher machen.
Wenn Sie einige zuverlässige Hakenrahmen schaffen wollen, dann ist noch eine ganz andere Geschichte, und Sie sollten zunächst durch die Erstellung von einfachen Haken für einige einfache Anwendungen beginnen.
Beachten Sie auch, dass diese Technik nicht OS spezifisch ist, ist es das gleiche auf allen x86-Plattformen, wird es auf Linux und Windows. Was ist Betriebssystemspezifisch ist, dass Sie wahrscheinlich Speicherschutz des Codes ändern ("entsperren" es, so dass Sie darauf schreiben können), die mit unter Linux und mit VirtualProtect
unter Windows erfolgt ist. Auch die Aufrufkonventionen sind anders, das können Sie mit der richtigen Syntax in Ihrem Compiler lösen.
Ein weiteres Problem ist "DLL-Injektion" (unter Linux wird es wahrscheinlich "Shared Library-Injektion" genannt, aber der Begriff DLL-Injektion ist weithin bekannt). Sie müssen Ihren Code (der den Haken ausführt) in das Programm einfügen. Mein Vorschlag ist, dass, wenn es möglich ist, einfach die Umgebungsvariable LD_PRELOAD verwendet wird, in der Sie eine Bibliothek angeben können, die direkt vor der Ausführung in das Programm geladen wird.Dies wurde in SO viele Male beschrieben, wie hier: What is the LD_PRELOAD trick?. Wenn Sie müssen tun dies in der Laufzeit, ich fürchte, Sie müssen mit gdb oder ptrace, die meiner Meinung nach ziemlich schwer ist (zumindest die ptrace Sache) zu tun. Sie können jedoch zum Beispiel this article on codeproject oder this ptrace tutorial lesen.
Ich fand auch ein paar netten Ressourcen:
Noch ein weiterer Punkt: Dieses "Inline-Patching" ist nicht die einzige Möglichkeit, dies zu tun. Es gibt sogar einfachere Wege, z.B. Wenn die Funktion virtuell ist oder wenn es eine Bibliothek exportierte Funktion ist, können Sie die gesamte Assembly/Disassembly/JMP-Sache überspringen und einfach den Zeiger auf diese Funktion (entweder in der Tabelle der virtuellen Funktionen oder in der exportierten Symboltabelle) ersetzen.
Nur um es raus zu werfen, haben Sie die nicht-technische Lösung in Betracht gezogen, d. H. * Bitten * den Autor, eine API zur Verfügung zu stellen? –
@KarlBielefeldt Das wäre schwer ... GoldSrc (für Half-Life) ist jetzt ungefähr 12 Jahre alt, denke ich? –