Wiki Artikel "Windows API"

Heyho ich hab da etwas Feedback für den Autor des Artikels "Windows API" aus dem Wiki (Xalon?) loszuwerden.

Und zwar, zunächst dein Text hat mir sehr weiter geholfen, die Technik ist gut und nachvollziehbar erklärt und ich mag deinen Schreibstil ;)

ABER ich halte deine Implementierung für sehr umständlich, dieses ganze hin und her gespringe und das nicht zerreissen eines Befehls, das geht meiner Meinung nach besser.
Ich habe einfach die originalen ersten 5-Bytes der API gespeichert und mit dem Sprungbefehl überschrieben (egal ob dabei etwas zerrissen wurde), In der angesprungenen Funktion (die statt der API ausgeführt werden soll) schreibe ich dann temporär den originalen Anfang wieder zurück, so dass die API wieder den originalen Zustand hat und ich sie benutzen kann, und am Ende der angesprungen Funktion schreibe ich wieder den Sprungbefehl an den Anfang der API. Es macht so nichts, dass die Befehle zerrissen wurden weil sie ja sowieso nie ausgeführt werden. Tut mir leid, aber das halte ich für eleganter als den originalen Code irgenwo anders auszuführen, was ist zB wenn in den ersten Befehlen der API relative Adressen benutzt werden und du diesen Code jetzt an einer ganz anderen Stelle ausführst? Naja, das war der eine Punkt, ich hoffe du konntest nachvollziehen was ich meine.

Der zweite Punkt, ich bin mir nicht sicher ob das allgemein gültig ist, aber ich habe meinen C++ Code mit Borland 6 geschrieben und bei dem Code den der generiert muss man noch etwas mehr Handarbeit anlegen als du beschrieben hast, Hintergrund ist folgender, ein API Aufruf sieht im Asm Listing so aus:
Code:
push parameter1
push parameter2
[...]
call API_Adresse
bla
auf dem Stack liegen also die ganzen Parameter und danach wird beim callen noch die Rücksprungadresse auf den Stack geschrieben. Nach einem API Aufruf (Zeile 5, "bla") sind sowohl die Rücksprungadresse (das sollte klar sein) als auch die gepushten Parameter wieder vom Stack verschwunden. Das ist das allgemeine Verhalten von Windows APIs (zumindest haben meine Forschungen das ergeben),

Wenn ich jetzt den Hook gesetzt habe, die API Adresse gecallt wird, von dort in meine Funktion gesprungen wird, diese abgearbeitet und am Ende mit einem Return beendet wird, dann liegen die gepushten Parameter noch immer auf dem Stack. C++ Funktionen entfernen, anders als API Funktionen, diese nicht wieder automatisch. Zumindest war das bei dem von bcc generierten Code der Fall, wie gesagt ich weiß nicht ob das für alle Compiler/Programmiersprachen gilt.
Der Rücksprung funktioniert zwar wie er soll, aber die aufrufende Funktion erwartet, dass der Stack nach dem API Aufruf aufgeräumt wurde. Das führte dann bei mir zu komischen Fehlern, die nicht sooo leicht zu debuggen waren :D naja, ich mache das mal an einem Beispiel deutlich:

Wir denken uns eine main Funktion:

Code:
int main(){
MessageBox(0,"bla","bla",MB_OK);
return 0;
}

und weiterhin sei die API "MessageBoxA" gehookt, wie befinden uns am Anfang der Main Funktion, auf dem Stack (er wächst in meiner Darstellung nach unten) liegt die Rücksprungadresse von irgendeiner Windows Funktion die uns aufgerufen hat (wahrscheinlich CreateProcess oder sowas):
Code:
STACK:
&CreateProcess
Jetzt wird die (gehookte) API aufgerufen, die Parameter kommen auf den Stack, die Rücksprungadresse kommt auf den Stack, wir befinden uns in der "API" (unserer Funktion, die stattdessen aufgerufen wurde):
Code:
STACK:
&CreateProcess
0
&"bla"
&"bla"
MB_OK
&main
Diese Funktion wird abgearbeitet, beendet und springt bei Return zurück in die main Funktion:
Code:
STACK:
&CreateProcess
0
&"bla"
&"bla"
MB_OK
Jetzt ist die main Funktion auch fertig, beendet, holt die Rücksprungadresse vom Stack und springt sie an. Was passiert? Sie springt zu &MB_OK, wo höchstwahrscheinlich nichts sinnvolles liegt und es Exceptions hagelt.

Ich hoffe das Beispiel macht klar, was ich meine. Das war auch alles was ich zu sagen hatte.
Ich weiß nicht, inwiefern sowas überhaupt in den Artikel aufgenommen werden sollte, ich weiß nicht ob das eine Eigenart von bcc oder von C++ ist, und es mit anderen Compilern/Programmiersprachen wunderbar funzt, aber ich habe etwas gebraucht um das zu debuggen und hatte einfach Mitteilungsbedürfnis :D. Schließlich habe ich es über ein kleines Inline Asm Programm, das den Stack aufräumt, lösen können.

Hier kannst du bei Interesse meinen Code finden, die vorkompilierten Binarys in dem Archiv sind noch Debug Binarys, dh. sie sind nur lauffähig wenn auf dem System der Borland Debugger installiert ist. Egal, der Source ist eh interessanter :D


So das wär's von mir, schönen Tag noch...


//edit, Achja hätte ich ja fast vergessen, da der Abschnitt zum globalen Hooking noch in Arbeit ist, schau dir mal an wie ich das gemacht habe, Windows bietet da einige nette Hilfestellungen an...
 
Ein paar Anmerkungen:
Code:
prochandle = OpenProcess(PROCESS_ALL_ACCESS,FALSE,GetCurrentProcessId());
damit das wirklich zuverslässig auch mit fremden Prozessen (die nicht von eder Anwendung gestartet wurden) Funktioniert, sollte man vorher "SeDebugPriviledge" holen.
So wie ich das sehe, ist es allerdings eine DLL, die in den Prozessraum eingeschleust wird - dann reicht doch schon GetCurrentProce ;)ss ?

Allerdings ging es mir eher darum:
Code:
als auch die gepushten Parameter wieder vom Stack verschwunden. Das ist das allgemeine Verhalten von Windows APIs (zumindest haben meine Forschungen das ergeben),
das nennt man STDCALL bzw gehört zur Callingconvention:
http://en.wikipedia.org/wiki/X86_calling_conventions
oder (finde ich netter gestaltet ;) )
http://www.3rd-evolution.de/docs/windows/callconv/
Schau mal nach, welche Konvention Dein Compiler verwendet und welche Register gegebenfalls überschrieben werden. Bei STDCALL wird nämlich erwartet, dass ESP,EBP, ESI,EDI,EBX (Stichwort: "preserved registers") unverändert zurückgegeben werden.D.h die Anwendung (bzw der Compiler) geht davon aus, dass diese Register durch einen API Aufruf nicht verändert werden und sichert diese nicht.
Benutzt Dein Compiler eine andere Konvention (wovon ich ausgehe, da Du sonst diese Anpassungen nicht gebraucht hättest), musst Du gegebenfalls dafür sorgen, dass diese Register nach dem Hook immer noch dieselben Werte wie vorher haben. Sonst wird die Fehlersuche und Debugging noch eine Stufe interessanter als bei einem zerschossenen Stackframe ;).
 
Ha cool, du scheinst dich auszukennen. Dann sollte man die Geschichte mit den Calling Conventions aber in dem Wiki Artikel erwähnen, oder? Dachte mir schon sowas, naja, war ja eh erstmal nur ein PoC, dann werde ich mir wohl mal nen geeigneten Compiler suchen bzw. schauen ob man bcc irgendwie dazu bringen kann STDCALL zu verwenden.
 
Wenn Du den Funktionsaufruf auf eigene Funktionen umlenkst und diese so auslegst, daß sie die Zielfunktion in sich aufrufen und selbst einen normalen Return benutzen sollen, mußt Du zwingend den Callstack für die eigene Funktion so aufbauen lassen wie er für die umgelenkte Funktion aufgebaut ist.

Die Alternative könnte darin bestehen, nur etwas VOR Weiterleitung an die umzulenkende Funktion extra zu erledigen und dann jene Weiterleitung nicht mit einem eingebetteten Call, sondern einem JUMP zu erledigen. DANN kümmert sich die umgeleitete Funktion wieder selbst um das Aufräumen des fraglichen Stackabschnitts.

Die Aufrufkonventionen werden - logischerweise - von Microsoft festgelegt und sind in den Windows-Headern definiert. Also einfach den Funktionskopf aus dem Windows-Header zum Vorbild der eigenen Funktionsdeklaration machen und die Sache ist geritzt!

----

Ähmmmm... Ja: CDW war schneller...
 
Zurück
Oben