Wiki - Windows API

Hallo

ich arbeite mich gerade in den Wiki Beitrag http://wiki.hackerboard.de/index.php/Windows_API
ein. Ich bekomme beim ausführen des Beispiels zum Kapitel " Inline Hooking" beim Aufruf von new_adress(i); eine EExternalException. Ich verwende den Borland Builder 5.5 und musste auch die asm Anweisungen in der NOP Tabelle von "asm("NOP");" zu "asm nop" ändern. Wie kann new_adress(i) ausgeführt werden?

Edit 1 ---------------------

Die Exception gibt es nur in einem Win2000 VMWare Image. Das liegt wohl an der Länge eines Sprungbefehls. Die sind unter Win2000 ja nur 3 Bytes? Unter XP wird durch die new_adress Funktion eine Endlosschleife ausgeführt????

Edit 2 ---------------------

Mein Compiler scheint mit dem NOP-Array nicht zurecht zu kommen. In dem Screenshot im Anhang kann man erkennen das sich die Anweisungen geändert haben. Wie kann man das umgehen? Könnt ihr mir da einen Tipp geben?


mfg
Tobias
 
Hmm,
also als ich das Tutorial erstellt hab hab ich Dev-Cpp als IDE, Windows XP SP2 Home als OS und den Olly als Debugger benutzt, schonmal damit versucht?

mfg
 
Original von tubias
Hallo


Edit 1 ---------------------

Die Exception gibt es nur in einem Win2000 VMWare Image. Das liegt wohl an der Länge eines Sprungbefehls. Die sind unter Win2000 ja nur 3 Bytes? Unter XP wird durch die new_adress Funktion eine Endlosschleife ausgeführt????

Edit 2 ---------------------

Mein Compiler scheint mit dem NOP-Array nicht zurecht zu kommen. In dem Screenshot im Anhang kann man erkennen das sich die Anweisungen geändert haben. Wie kann man das umgehen? Könnt ihr mir da einen Tipp geben?
Das C Zeug außen vor (k.A von low-level C): Sprünge/Calls hängen nicht von OS ab ;). Es sollte für den Aufruf ein Call generiert werden, 5 bis 6 Bytes (je nach dem ob direkt oder indirekt). Eventuell auch ein "mov Register,Adresse; call register".
Allerdings reagiert windows 2000 meiner Erfahrung nach Empfindlicher auf Fehler, die den Stackframe durcheinander bringen. Es könnte auch damit zusammenhängen, dass (wie im Edit 2 erwähnt) statt NOPs etwas anderes generiert wurde.

zu 2:
versuche es direkt mit dem Opcode: 0x90
 
Hallo,
also viele Probleme kann z.B. auch der Compiler machen.

Wenn man das 1. Beispiel, also wo check() aufgerufen werden soll statt output(), mit gcc + Optimierung (gcc -O3) zum Beispiel kompiliert, funktioniert das hooking nicht.
Man kann zwar die Adresse zu output usw. auslesen und auch überschreiben, dennoch wird immer output() ausgeführt.
Hängt einfach damit zusammen, dass gcc dies dann entsprechend wegoptimiert hat (Sprünge sind übel für die Laufzeit).

Wie Xalon sagte, am besten mit gcc (ist in Dev-Cpp enthalten, und kostenlos dazu) kompilieren.
 
Hallo

Mit dem Dev-Cpp gehts bei mir auch einwandfrei. Ich habe in dem Borland Builder alle Code-Optimierungen deaktiviert. Ich würde schon gerne herausfinden wie das mit dem Borland Builder geht. Ich versuche mal das ganze irgendwie über ein BYTE Array zu machen.

@CDW: Ich meine mal irgendwo gelesen zu haben das sich die Länge eines Sprungbefehls mit XP geändert hat. Das gilt dann aber anscheinend nur für das Präambel der Win 2000 DLLs. Any way... Direkt 0x90 implementieren geht nicht. Es kommt die Fehlermeldung "[Tasm Fehler] Unit1.asm(922): Illegal instruction"

mfg
Tobias
 
Original von tubias
@CDW: Ich meine mal irgendwo gelesen zu haben das sich die Länge eines Sprungbefehls mit XP geändert hat. Das gilt dann aber anscheinend nur für das Präambel der Win 2000 DLLs. Any way... Direkt 0x90 implementieren geht nicht. Es kommt die Fehlermeldung "[Tasm Fehler] Unit1.asm(922): Illegal instruction"

mfg
Tobias
Dann weiß ich nicht,was du dann mit dem Sprungbefehl und Präambel meinst. I.R ändern Intel&Co ihre Opcodelängen ;). Vielleicht meinst Du die "optimierte" Nutzung von Systemcalls durch XP (SYSENTER vs. INT 2e)? Ein Call mit einer Adresse drin sollte 5-6 Bytes groß sein. Wenn man CALL [REGISTER] oder CALL REGISTER nutzt, sind es 2 Bytes (bei JMP ebenso). Aber für Dich sollte nur relevant sein, was Dein Compiler generiert - und das Resultat sollte sowohl XP wie win2k kompatibel sein ;)

Bei TASM sollte sowas funktionieren:

db 90h,90h,90h ...

Ansonsten in der TASM Doku nachschlagen (da TASM nicht frei verfügbar ist, habe ich mich damit auch nie wirklich befasst - allerdings sollte dieser die allgemeine Syntax schon schlucken).
Mit dem Byte Array hast Du das Problem,dass der Compiler das einfach mal in die Datasection verlagern kann (und wahrscheinlich wird) - und diese sehr wahrscheinlich keinen Execution-Flag gesetzt bekommt - sprich, es wird dann "unerklärliche" Fehler geben, die man erst entweder mit einem PE-Editor (Section-Flags anpassen), entsprechenden Linker-Optionen (mit MS-Linker z.B /section:NAME,RWE ) oder einem beherzten Aufruf von VirtualProtect beheben kann.

Edit: ok, ByteArray sollte einen Versuch Wert sein. So wie ich das jetzt lese, wird der Inhalt davon kopiert - dann ist es egal, ob es in der Code oder Data-Section liegt.
 
Normal sieht die Präambel einer Funktion so aus:
Code:
55         push ebp
8BEC     mov ebp, esp
ab XP SP2 hat MS das doch erweitert um ihr detouring besser zu unterstützen:
Code:
8BFF     mov edi, edi
55         push ebp
8BEC     mov ebp, esp
Aber ich weis nicht genau...

Habs mit "db 90h,90h,90h" versucht, ist aber genau das selbe Ergebnis.

Mit dem BYTE Array war so eine Idee von mir. Hast du dazu Informationen? (Weil du sagst du würdest lesen ;-) )
 
Original von tubias
Habs mit "db 90h,90h,90h" versucht, ist aber genau das selbe Ergebnis.
Mit der Präambel hast Du recht. Wobei die Begriffsdefinition nicht immer einheitlich ist (das kenne ich allerdings nur von Reverseing Artikeln). Es betrifft nur die Win-API Funkitonen an sich und dürfte bei den "Eigenversuchen" (wo eine Funktion innerhalb des Programms gehookt wird) nicht wichtig sein. Bezüglich Win2k sollte man allerdings im Hinterkopf behalten, dass es sehr empfindlich ist, was die "preserved register" EDI,ESI,EBX angeht. Wenn eine Funktion diese verändert zurückgibt (aus einem Callback oder Windows-Nachrichtenloop), gibt es Ärger.

Was das Bytearray angeht - wenn es um das Beispiel geht, so bin ich mir wieder nicht mehr so sicher (allerdings weiß ich jetzt wieder, warum ich kein C mag ;) ). Grundsätzlich: wenn man den Inhalt des Arrays direkt ausführen möchte, dann wird man sehr wahrscheinlich darauf VirtualProtect anwenden müssen - benutzt man das Array nur um es irgendwo reinzukopieren (kennt man vielleicht von Shellcode-Arrays), dann ist es egal, wo der Compiler das Array hinparkt.
Es geht allerdings auch schon darum, genau zu kontrollieren, was der Compiler ausgibt. D.h es macht nur wenig Sinn auf "Teufel komm raus" zu versuchen, es dem Borland Compiler beizubringen. Wenn ich mir z.B den im ersten Post verlinkten Screenshot anschaue - NOPs werden ja schon generiert, allerdings wird zuerst irgendeine Debug-Anweisung hineingebracht (erkennbar an INT 3). Ob diese im Release-Modus auch noch in der Funktion vorhanden ist?

Kannst Du mal eine Binary (wenn es geht, ohne Borland-DLL Abhängigkeiten) anhängen (Sourcecode wäre optional, allerdings könnte es für andere interessant sein) ? Dann könnte man schauen, was genau BC "falsch" macht.
 
Hallo,
Original von CDW
wenn man den Inhalt des Arrays direkt ausführen möchte, dann wird man sehr wahrscheinlich darauf VirtualProtect anwenden müssen - benutzt man das Array nur um es irgendwo reinzukopieren (kennt man vielleicht von Shellcode-Arrays), dann ist es egal, wo der Compiler das Array hinparkt.
Ne die kann man direkt ausführen, zumindest bei gcc:
Code:
char code[]= "\x90\x90\x90\x90";

int main() {
    int (*shell)();
    *(int*)&shell = (int)code;
    shell();
}
Dieser Code (oder so in der Art) würde dann hier vier mal NOP ausführen.

(allerdings weiß ich jetzt wieder, warum ich kein C mag
Da schließe ich mir dir an ;)
 
Hallo

Es kam immer die Compiler-Warnung:
"W8002 Compilierung mit Assembler-Sprachfähigkeiten neu gestartet"
Indem Fall kam es zu oben beschriebenem Fehler. Wenn man die Compiler-Option -B setzt geht es auch mit dem BC. Super!

Dieser Code (oder so in der Art) würde dann hier vier mal NOP ausführen.
Mit dem BC geht das auch.

(allerdings weiß ich jetzt wieder, warum ich kein C mag
Und ich merke das es langsam Zeit wird richtig Assembler zu lernen. Trotzdem ist C/C++ das Beste ;-)


Ich möchte jetzt eine Funktion aus einer DLL hooken die in einer anderen Anwendung dynamisch geladen wurde. Aber ich bekomme es noch nicht zum laufen. Ich weis auch noch nicht wo das Problem ist...

Edit 1----------------------------
Mit GetExitCodeThread(hThread, lpExitCode) kann man ja das Modulehandle des Thread bekommen. Wie kann man das den schon rausbekommen wenn ExitCode noch STILL_ACTIVE ist? Also im DLL_PROCESS_ATTACH?

Edit 2----------------------------
Also ich versuche jetzt das ganze in einer DLL zu machen die in einen anderen Prozess geladen wurde. Ich bekomme aber einfach noch nicht die richtigen Adressen der Funktionen. Habt ihr vielleicht noch eine Tipp?

Edit 3----------------------------
Aha, das Problem sind also die IAT und EAT. Mal sehen...
 
Ah gut, wo zu dem Wikieintrag schon mal ein Thema existiert, ich finde das Beispiel eher suboptimal veranschaulicht. Also er ist nicht schlecht, und es stimmt/funktioniert alles was dort gesagt wird, nur als ich in das Thema einsteigen wollte, hat mich damals der Artikel mehr verwirrt, als das er mir weitergeholfen hat.

Hier ein paar Verbesserungsvorschlaege:
1. Alles _nur_ am Beispiel einer WinAPI-Funktion veranschaulichen, da in der Praxis es aeußerst selten vorkommt, dass man seine Funktionen hooken moechte.
2. Tramp dynamisch erzeugen (nicht dieses komische nop-array)
3. die ganzen WriteProcessMem()-Aufrufe vermeiden, wenn man erstmal im Zielprozess ist, gehts auch einfacher (zb mov esi, adr; mov byte ptr[esi], 0xE9)
4. Die Kommentare unbedingt farblich vom Code unterscheiden, es wirkt irgendwie unuebersichtlich.

Ich hab mal etwas Code angehaengt, wie ich es machen wuerde, vllt hilfts ja dem ein oder anderem weiter. Er ist wg 5 Byte Praeambel fuer xp sp2 konzipiert und sollte sich unter VC++ problemlos kompilieren lassen. Die kompilierte DLL muss dann nur noch in einen Prozess geladen werden, aber das kriegt glaub ich auch jeder so hin.
 
Hallo

Na ja, die Vorschläge finde ich gut, aber dein Beispiel ist nicht besser ;-) Da wird auch die MessageBoxA verwendet...

So wie ich das jetzt auf die schnelle gesehen habe machst die eine IAT/EAT-Hook oder wie? In dem Wiki wird ein anderer Mechanismus erklärt bei dem man nicht einfach ein 5Byte Trampolin erzeugen kann. Oder? Außerdem ist es gut, zumindest erstmal, die API des UserMode zu verwenden und nicht mit asm.
 
Oehm, ja ich hooke MessageBoxA (WinAPI-Funktion), dazu:
Hier ein paar Verbesserungsvorschlaege:
1. Alles _nur_ am Beispiel einer WinAPI-Funktion veranschaulichen, da in der Praxis es aeußerst selten vorkommt, dass man seine Funktionen hooken moechte.


So wie ich das jetzt auf die schnelle gesehen habe machst die eine IAT/EAT-Hook oder wie?
Nope, das ist ein Detour-Hook, der ueberschreibt die ersten 5 Bytes der zu hookenden Funktion. Bei IAT/EAT parst du die zb die IAT von dem Modul, in dem du Funkionen hooken moechtest und ueberschreibst dann die Adresse. Bei EAT-Hooking laeufts genauso, nur das du die Adressen in dem Modul veraenderst, welches die Funktion exportiert.


In dem Wiki wird ein anderer Mechanismus erklärt bei dem man nicht einfach ein 5Byte Trampolin erzeugen kann. Oder?
Jo, genau das mache ich aber auch. Nur hab ich kein riesen nop-array, sondern reserviere mir im Speicher 10 Bytes und schreib dort die Originalbytes+ Jump zum Originalcode (Tramp) hinein. Dadurch erspar ich mir noch ein paar nop-arrays, wenn ich mal mehrere Funktionen hooken will. Ich haette zwar auch dynamische Hookfunktionen erzeugen koennen, aber das mach IMHO nicht so viel Sinn, wenn man diese in irgendeiner Form noch manipulieren moechte.


Außerdem ist es gut, zumindest erstmal, die API des UserMode zu verwenden und nicht mit asm.
Jetzt nicht Aepfel mit Birnen verwechseln ;) Also Asm kannst du auch im Usermode verwenden, sogar ganze Programme mit schreiben. Du haettest es auch in C machen koennen:
Code:
(unsigned char *)o_MessageBoxA=(unsigned char *)GetProcAdr(hUser32, "MessageBoxA");
o_MessageBoxA[0]=0xE9;
usw... Nur fand ich pers. asm angenehmer. Wie gesagt, WriteProcessMemory ist voellig ueberfluessig, da man im eigenen Prozessraum schreibt.
 
OK, Danke.
Kann man so auch Funktionen hooken die in dem Prozess in den die DLL geladen wird aus einer anderen DLL dynamisch geladen wurden (Keine API Funktionen)? Das bekomme ich irgendwie noch nicht hin. Wenn man mit GetProcAddress die Adresse der Funktion holen will, bekommt man die aus der IAT? Das habe ich noch nicht kappiert... aber ich bleib dran...
 
Also hier ein paar grundlegende Sachen:
Wenn eine Exe gestartet wird, dann wird diese zunaechst in den Speicher geladen (in der IAT steht noch nichts), anschließend, laed der Loader saemtliche Module, die von der Exe benoetigt werden in den Speicher. Anschließend parst er die EAT und schreibt die benoetigten Adressen dann in die IAT der Exe.

GetProcAdr() parst die EAT des angegebenen Modules und gibt bei Erfolg die Speicheradr der Funktion zurueck.


Und nochmal, Detour veraendert die ersten paar Bytes der Funktion. Dann ist es auch egal, ob die Funktion dynamisch geladen wird oder nicht. Sobald die Adresse aufgerufen wird, findet man immer nur die gepatchte Funktion.
 
Ja, das ist klar. Wenn man aber in diesem Fall zu der Adresse die GetProcAddress zurückgibt springt, befindet man sich nicht bei dem "Funktionskopf" bzw. bei der Präambel der Funktion, sondern man müßte nochmal springen. Dann schreibt man in diesem Fall doch nicht die 5 + n Byte der Funktion in das Trampolin sondern einfach die 5 Byte der Adresse. Das währe dann ein EAT-Hook. Oder? Also bei den Tests die ich gemacht habe hat das zumindest so ausgesehen.
 
Zurück
Oben