Register for your free account! | Forgot your password?

Go Back   elitepvpers > Coders Den > General Coding > Coding Tutorials
You last visited: Today at 01:13

  • Please register to post and access all features, it's quick, easy and FREE!

Advertisement



[Tut] Code-Cave-Injection für alle Sprachen mit Schwerpunkt AutoIt

Discussion on [Tut] Code-Cave-Injection für alle Sprachen mit Schwerpunkt AutoIt within the Coding Tutorials forum part of the General Coding category.

Reply
 
Old   #1
 
Shadow992's Avatar
 
elite*gold: 77
Join Date: May 2008
Posts: 5,430
Received Thanks: 5,876
[Tut] Code-Cave-Injection für alle Sprachen mit Schwerpunkt AutoIt

1. Allgemeines

2. Voraussetzungen

3. Code-Cave Injection Allgemein
3.1 Allgemeines zu Code-Caves
3.2 Verwendungszweck von Code-Caves


4. Code-Cave mit Speicher-Reservierung für ASM-Befehle
4.1 Grundlagen zu theoretisch unendlich großen Code-Caves
4.2 Weiterführendes zu generierten Code-Caves und Pseudo-Code auf Basis der WinApi
4.3 In der Praxis auftretende Probleme
4.4 Kommunikation zwischen dem Code-Cave und externen Prozessen


5. CCInject.au3 UDF
5.1 Grundlegendes zur CCInject.au3
5.2 _AllocateMemoryForVariable & _CreateASM_CopyRegisterToVariable & _ReadMemoryVariable
5.3 _InjectASMAtAddress


6. Beispiel einer Skriptgesteuerten Code-Cave-Injection an einem Programm
-------------------------------------------------------------------------------------------------
1. Allgemeines

Es stellen sich sicher einige Leute die Frage, warum die Welt noch ein derartiges Tutorial braucht, wo es doch schon 1000 verschiedene Tutorials zu diesem Thema gibt.
Nunja die Antwort ist relativ einfach:

Keines der bisher gelesenen Tutorials konnte meinen Anforderungen gerecht werden.
Entweder fehlte ein Überblick über das komplette Thema und es wurde sich auf ein kleiner
Teilaspekt konzentriert oder aber es wurde einem ein Copy&Paste-Code vor die Nase geknallt mit den Worten:
"Friss oder Stirb". So musste ich zum Einstieg in das Thema zuerst etliche Seiten durchwälzen und einige Tests machen bevor ich verstand,
was CodeCaves sind und wie man sie erstellt/benutzt.

Ich versuche deswegen in diesem Tutorial zwar alles ziemlich genau anzusprechen, so dass man in der Lage sein müsste selbst Funktionen dazu zu schreiben, werde aber auf der anderen Seite auch einen Copy&Paste-Code erklären, damit auch die faulen AutoItler etwas von dem Tutorial haben.
-------------------------------------------------------------------------------------------------
2. Voraussetzungen

Leider ist das Thema Injection und Memory-Editing, wenn man sich nicht auf Cheat-Engine
und ein paar Addressen beschränkt, etwas komplizierter als andere Themen, finde ich.
Aber ich versuche in diesem Tutorial von Anfang an alles zu erklären, die einzigen Voraussetzungen sind:

1. Grobes Assembler-Grundwissen (Wir werden mit der FASM-Syntax arbeiten)
2. Schon einmal mit Olly Dbg gearbeitet haben

Recht viel mehr braucht es eigentlich nicht, wir werden zwar später ab Kapitel 5 alle
Beispielskripte mit AutoIt realisieren, bis dahin wird das Tutorial aber für alle Programmiersprachen
verwendbar sein, da ich das ganze sehr allgemein und theoretisch halten werden.
Somit profitieren nicht nur AutoItler von diesem Tutorial.
-------------------------------------------------------------------------------------------------
3. Code-Cave Injection Allgemein
3.1 Allgemeines zu Code-Caves

Zuerst einmal sollte man klären was ein Code-Cave ist. Ein Code-Cave ist eine Stelle in einem Code, der leer ist.
Code = Skript/Code
Cave = Höhle
An der Stelle des Code-Caves passiert also gar nichts, diese Stellen sind bereits im Programm.
Strenggenommen ist ein Code-Cave eine Stelle im Code an der man Befehle wegnehmen kann, ohne dass sich an dem Programm etwas ändert.
So könnte ein Code-Cave so aussehen (von Code Caves spricht man ausschließlich bei Assembler, bei allen anderen Sprachen macht das wenig Sinn):

Code:
Sprungmarke1:
MOV eax,10
ADD eax,20
NOP
NOP
NOP
JMP Sprungmarke1
Der Code-Cave im oberen Beispiel wäre also nur der Code, den man weglassen kann ohne die Funktionsweise des Programmes zu ändern.
In unserem Beispiel also:

Code:
NOP
NOP
NOP
Denn unser Programm würde auch ohne diese NOPs funktionieren, weil NOPs machen, wie bereits bekannt sein sollte, nichts.
Also könnte das Programm auch so aussehen und hätte dieselben Funktionen:

Code:
Sprungmarke1:
MOV eax,10
ADD eax,20
JMP Sprungmarke1
Als nächstes stellt sich wohl die Frage woher diese "NOPs" kommen, wenn sie doch sowieso überflüssig sind.
Einige dienen zur Strukturierung des ASM-Codes (z.b. Abgrenzung von verschiedenen Klassen), andere hingegen sind vom Programmierer eingebaut worden (meistens ohne es zu wissen).
So wäre die einfachste Möglichkeit in C/C++ ein NOP zu realisieren folgende Zeile Code:

Code:
;
Der Befehl wird also, sollte der Compiler es nicht wegoptimieren, direkt in ein NOP übersetzt.
Neben dem NOP gibt es noch viele Stellen an denen Code-Caves zu finden sind. Wir werden uns aber auf die NOPs beschränken, da diese für einen Anfänger die einfachste und sicherste Methode ist Code-Caves zu finden/nutzen.

Die Möglichkeiten, die man hat, wenn man ein Code-Cave gefunden hat sind theoretisch unbegrenzt, praktisch jedoch sind die Möglichkeiten so gering, dass es nichts bringt ein Code-Cave gefunden zu haben.
Denn jeder ASM-Befehl braucht eine unterschiedliche Anzahl an Bytes im Programm an Speicherplatz.
So braucht NOP immer genau 1 Byte, aber schon alleine ein einfaches

Code:
MOV eax, 1
braucht 5 Bytes.

Wie man sieht muss man also extrem viele NOPs hintereinander haben um ein sinnvolles Programm schreiben zu können, so viele NOPs wird man jedoch nur selten direkt hintereinander finden.
Jetzt könnte man sich denken, dass Code-Caves in einem Programm dadurch nutzlos geworden sind und das sind sie auch in den meisten Fällen.
Diesem Problem werden wir uns jedoch erst später widmen.
-------------------------------------------------------------------------------------------------
3.2 Verwendungszweck von Code-Caves

So jetzt wo wir theoretisch wissen wie ein Code-Cave funktioniert und wie man ihn findet, stellt sich die Frage was man damit anstellen kann.
Wir gehen bei den folgenden Annahmen davon aus, dass wir ein unendlich großes Code-Cave gefunden haben, wir können also beliebig viele Befehle in diesen Code-Cave reinpacken.

Wenn wir also von einem unendlich großen Code-Cave ausgehen, dann kann man damit einiges anstellen:

1. Man kann in Echtzeit die Werte der Register (z.B. EAX, EDX, usw.) auslesen und auch verändern.
2. Man kann ASM-Funktionen im Code-Cave aufrufen, die von außen nur schwer oder gar nicht aufrufbar sind.
3. Man kann direkt ASM-Funktionen an bestimmte Stellen im Code einschleußen und damit Werte, Addressen, Schleifen, usw. verändern.
4. Code-Caves kann man anstatt von Addressen verweden für Werte, die sich praktisch zufällig verändern (und dadurch schwer lokalisierbar per Cheat-Engine sind).
5. Es ist auch möglich werte auszulesen, die nur sehr kurz im Speicher liegen und anschließend sofort wieder gelöscht/überschrieben werden.

Ein einfaches Beispiel hierfür ist ein Zufallszahlgenerator.
Gehen wir davon aus, dass wir ein sehr kleines Programm haben, dass eine Zufallszahl erstellt, diese Zahl per Cheat-Engine zu finden wäre die Hölle und würde lange dauern.
Per OllyDbg hat man jedoch schnell die Funktion gefunden, in welcher die Zufallszahl erstellt wird. Jetzt nimmt man einfach ein Code-cave, das an dieser Stelle ist und fügt dort sein Code ein, der den Wert der Zufallszahl ausliest und an unser externes Programm schickt, somit verlieren wir z.b. nie wieder bei Schere/Stein/Papier.
Die ganzen Ansätze oben werden weiterhin unter der Annahme gemacht, dass man an einer beliebigen Stelle im Code ein unendlich großen Code-Cave einfügen kann, das muss man im Hinterkopf behalten.
-------------------------------------------------------------------------------------------------
4. Code-Cave mit Speicher-Reservierung für ASM-Befehle
4.1 Grundlagen zu theoretisch unendlich großen Code-Caves

Wie wir bereits in den bisherigen Kapiteln festgestellt haben, sind Code-Caves im Programm an sich eigentlich nutzlos, da sie zu wenig Platz für ASM-Befehle Bereit halten.
Jetzt gibt es aber tatsächlich eine Method an fast jeder beliebigen Stelle ein theoretisch unendlich großen Code-Cave zu erzeugen.

Das ganze beruht auf einem relativ einfachen Prinzip:
Man lässt sich einfach ein bisschen Platz im Arbeitsspeicher reservieren und führt diese Befehle vom eigentlichen Programm aus aus.
Einige werden jetzt denken, dass es dazu sicherlich eine Funktion gibt, um ASM-Code im Arbeitsspeicher ausführen zu lassen.
Diese Funktion gibt es tatsächlich, sie ist nur leider für unsere Zwecke nicht benutzbar, da sie den Code im Arbeitsspeicher als eigenständiges Programm sieht und nicht als Teile eines großen Programmes.

Also werden wir wohl auf guten alten ASM-Code zurückgreifen müssen.

Das ganze ist vom der Idee her relativ einfach.
Wir lassen von unserem Programm aus ein JMP auf das generierte Code-Cave in unserem Arbeitsspeicher machen
und springen am Ende des Code-Caves wieder zurück zur alten Stelle und das Programm wird dann weiter ausgeführt als wäre nichts gewesen.

Da wir aber einen Befehl im normalen Programm überschreiben, müssen wir diesen auch irgendwo wieder ausführen lassen.
Diesen Befehl kopiert man einfach an den Anfang des Code-Caves, so dass er ausgeführt wird als wäre nichts gewesen.
Anschließend kann man seinen eigenen ASM-Code ausführen lassen und muss dann aber am Ende des Code-Caves wieder zurück springen, wo man herkam, um den normalen Programmfluss garantieren zu können.

Das ganz kann man sich wohl besser an einem Beispiel klar machen.
Schauen wir uns einmal folgenden Code an:

Code:
MOV EAX, 10
MOV EDX, 22
XOR EDX, EAX
INC EDX
MOV EAX, EDX
XOR EDX, EDX
Gehen wir davon aus, dass wir an folgender Stelle unseren Code-Cave setzen wollen:
"MOV EAX, EDX"

Wir ersetzen also den Code "MOV EAX, EDX" durch ein JMP auf unsere Befehle im Ram und den alten Code im Ram.
Anschließend springen wir wieder zurück zu der ursprünglichen Addresse.
Das ganze sieht veranschaulicht so aus (Rot ist der verschobene original Code, schwarz ist der unveränderte Code und grün ist unser Code):


So mittlerweile wissen wir wie das ganze abläuft, nur 2 Sachen fehlen uns noch zum endgültigen Glück:

1. Wie reserviert man Speicher im Arbeitsplatz für ASM-Befehle?
2. Woher kennen wir die Addresse, an der unser Code im Arbeistspeicher stehen soll?

Die Frage 1 ist relativ einfach beantwortet:

Windows bietet uns zum Glück hier eine sehr schöne Funktion um in einem Programm Arbeitsspeicher zu reservieren.
Wir können durch eine bestimmte Funktion also einem fremden Prozess mehr Arbeitsspeicher zukommen lassen und in diesen Arbeitsspeicher schreiben wir dann unseren ASM-Code.
Spätestens jetzt sehen wir auch warum die Code-Caves theoretisch nur unendlich groß sind, denn der Arbeitsspeicher ist nicht unendlich groß, sondern hat nur eine bestimmte Größe.

Der Befehl, den wir brauchen heißt "VirtualAllocEx".

Werfen wir hierzu einen Blick in die WinApi-Dokumentation:



Wir sehen folgenden Aufbau:

Code:
LPVOID WINAPI VirtualAllocEx(
  _In_      HANDLE hProcess,
  _In_opt_  LPVOID lpAddress,
  _In_      SIZE_T dwSize,
  _In_      DWORD flAllocationType,
  _In_      DWORD flProtect
);
hProcess ist der erste Parameter und dieser Parameter bestimmt auch in welchem Prozess der Speicher reserviert werden soll.
Hier kommt also ein Handle von einem Prozess rein.

Der 2. Parameter (lpAddress) entscheidet darüber wo wir den Speicher reservieren lassen wollen, also an welcher Stelle,
da das uns aber egal ist als Code-Caver, können wir hier NULL einfügen.

Der 3. Parameter (dwSize) entscheidet über die Größe des zu reservierenden Speichers in bytes.
Hier geben wir also an wie viel Bytes wir für unseren ASM-Code reservieren lassen wollen im Arbeitsspeicher.

Der nächste Parameter (flAllocationType) definiert die Art und Weise wie der Speicher reserviert werden soll, wer sich für diesen Parameter genauer interessiert, soll einen Blick in die WinApi-Dokumentation werfen, wir werden standardmäßig 0x00001000 verwenden.

Der letzte Parameter (flProtect) entscheidet über die Rechte, die wir über den reservierten Speicher bekommen, wir werden hier immer 0x40 benutzen,
was uns im Grunde alle Rechte (Code ausführen, Code lesen, Code schreiben), die wir jemals brauchen könnten, gewährt.

Die Parameter sollten ziemlich selbsterklärend sein, außer eventuell der 3. Parameter.
Ich denke nicht jedem ist bewusst wie man auf die benötigte Speicherplatz Größe des ASM-Codes kommt.
Und in diesem Punkt muss ich euch leider enttäuschen, man selbst kommt da nicht einfach durch einfache Rechnungen drauf.

Man muss den ASM-Code erst in Maschinen-Code umwandeln lassen und zählt da dann die Bytes, die der Maschinen-Code einnimmt.
Achtung: Ein Byte ist 2 Zeichen in Maschinen-Code lang.

Beispiel:
Code:
01203938
Das obere Beispiel ist zwar 8 Zeichen lang, aber ein byte besteht immer aus 2 Hexadezimalzahlen.
Also brauchen wir für den oberen Code nicht 8 Byte Platz, sondern nur 4.

Und nun zur 2. Frage:
2. Woher kennen wir die Addresse, an der unser Code im Arbeistspeicher stehen soll?

Die Addresse, an der unser Speicher jetzt reserviert wurde, ist relativ leicht rauszufinden, denn VirtualAllocEx liefert uns automatisch, wenn die Speicher-Reservierung erfolgreich war, die Addresse zurück an der unser Speicher reserviert wurde.
-------------------------------------------------------------------------------------------------
4.2 Weiterführendes zu generierten Code-Caves und Pseudo-Code auf Basis der WinApi

Nun wissen wir schon einiges aber es gibt noch ein paar Kleinigkeiten zu beachten,
so sind CALLs, die durch Code-Caves ersetzt werden schlecht, genau so wie JMPs.
Man sollte also meiden Calls und JMPs in seine Code-Caves, die aus den Code-Cave heruasgehen, zu verwenden, sonst könnte es schnell einmal passieren, dass das Programm sich verabschiedet.

Soweit so gut, jetzt muss man nur noch folgendes wissen:
ASM-Code an sich kann nie direkt ohne Umwandlung in Maschinen-Code ausgeführt werden.
Das heißt in Wirklichkeit können wir folgendes um einen JMP zu realisieren nicht schreiben:

Code:
Sprungmarke1:
Mov EAX
.
.
.
JMP Sprungmarke1
Würden wir diesen Code kompilieren lassen, dann käme ausführbarer Maschinencode bei raus.
Dieser Maschinencode für den oberen Abschnitt unterscheidet sich aber je nach der Größe des vorherigen ASM-Codes.

Denn der JMP-Befehl in Maschinen-Code arbeitet nicht absolut, also springt immer zu derselben angegebenen Stelle, sondern springt relativ zu seiner momentanen Position.

Lassen wir also den JMP Befehl "JMP Sprungmarke1" in Maschinencode umwandeln und schauen uns dann den generierten Code an,
sehen wir, dass dort keine feste Addresse eingespeichert ist, sondern eine Relative, die Abhängig ist von der momentanen Position des JMP-Befehls.
Verschieben wir also den JMP-Befehl wird er nie wieder an seine Alte stelle hinspringen, sondern immer an eine andere Stelle.

Das Ganze macht das ein klein wenig komplizierter, aber prinzipiell ist das auch nicht über aus schwierig.

Also widmen wir uns doch einmal einer konkreten Funktion dazu (in Pseudo-Code auf Basis der WinApi).
Wir lassen ja an der Stelle, wo der Code-Cave hin soll ein JMP einfügen.
Dieser JMP ist immer 5 Bytes groß (wichtig um den Pseudo-Code zu verstehen),
(// Sind als einzeilige Kommentare zu sehen):

PHP Code:
$insert_at_address=0x02124334 // Addresse an der wir unseren Code einfügen wollen
$opcode_to_insert="03D0" // Entspricht folgendem ASM-Code: ADD EDX, EAX
$variable_to_safe_read_bytes=""

$process_handle=GetProcessHandleByName("test.exe")
ReadProcessMemory($process_handle,$insert_at_address,$variable_to_safe_read_bytes,5// Wir brauchen den Maschinen-Code, der davor hier stand um ihn später in unser Code-Cave einfügen zu können.

$code_cave_address=VirtualAllocEx($process_handle,NULL,StringLength($opcode_to_insert)/2+5+5,0x00001000,0x40// Wir müssen zusätzlich noch 5 Bytes für den Befehl, den wir später in den Code-Cave schreiben, reservieren und auch für das JMP, das am Ende des Code-Caves steht, also nochmals +5

WriteProcessMemory($process_handle,$insert_at_address,"E9"+NumberToBytes($code_cave_address-$insert_at_address-5),5
// E9 = JMP
// Wir sind bereits, wenn wir den JMP-Befehl ausführen, an der Stelle $insert_at_address, deswegen müssen wir nur noch $code_cave_address-$insert_at_address Bytes weiterspringen.
// Da der JMP-Befehl bereits 5 Bytes Platz wegnimmt, müssen wir wieder 5 Bytes weniger weit springen und dann kommen wir auf:
// $bytes_to_jump=$code_cave_address-$insert_at_address-5

//Anschließend müssen wir unseren Code-Cave füllen:
WriteProcessMemory($process_handle,$code_cave_address,$variable_to_safe_read_bytes,StringLength($variable_to_safe_read_bytes)/2)

WriteProcessMemory($process_handle,$code_cave_address+5,$opcode_to_insert,StringLength($opcode_to_insert)/2)
// +5 weil in unserem Code-Cave bereits der Befehl aus der original Exe steht.

// Und nun wird unser JMP zurück eingefügt:

WriteProcessMemory($process_handle,$code_cave_address+StringLength($opcode_to_insert)/2,"E9"+NumberToBytes(($insert_at_address+5)-($code_cave_address+StringLength($opcode_to_insert)/2+5+5)),5)
// Das JMP muss ans Ende unseres Code-Caves, ohne den bereits bestehenden Code zu überschreiben:
// $code_cave_address+StringLength($opcode_to_insert)/2
// Da wir jetzt rückwärts springen müssen, müssen wir die Addresse, wo wir hin wollen von der abziehen wo wir momentan sind:
// $insert_at_address-$code_cave_address
// Da wir aber nicht wieder beim JMP landen wollen, addieren wir 5 zu unserer Addresse, wo wir hinwollen:
// ($insert_at_address+5)-$code_cave_address
// Wir sind bei unserem JMP im Code-Cave bereits an der Stelle StringLength($opcode_to_insert)/2+5
// Das +5 kommt dadurch Zustande, dass wir ja bereits am Anfang unseres Code-Caves einen ASM-Befehl kopiert haben, der schon 5 Bytes wegnimmt.
// Danach kommt noch einmal +5, da der JMP-Befehl am Ende des Code-Caves ja auch 5 Bytes groß ist.
// und damit sind wir dann bei: ($insert_at_address+5)-($code_cave_address+StringLength($opcode_to_insert)/2+5+5) 
Unser obiger Code würde jetzt prinzipiell funktionieren, wir werden aber im nächsten Kapitel sehen, dass es noch ein paar mehr Sachen gibt, auf die geachtet werden muss, sonst kann es zu bösen Überraschungen kommen.
Der Code wie er also hier steht, soll nur die Grundlagen klären, in der Praxis würde man den oberen Code wohl nur in Ausnahmefällen einsetzen können, warum klären wir im nächsten Kapitel.
-------------------------------------------------------------------------------------------------
4.3 In der Praxis auftretende Probleme

Schauen wir uns einmal ein Beispiel an wie es in der "Natur" vorkommt:

Ausgangslage (2 zeichen = 1 Byte, die Leerzeichen müssen sich weggedacht werden):
Code:
3B F3 0F 8C 99 00 00 00 38 9D 03 FF FF FF 0F 85 F2 0E 00 00 8D 85 EC FE FF FF 50 8D 85 E0 FE FF
FF 50 8D B5 F4 FE FF FF E8 F1 FD FF FF 8B F0 3B F3 7C 6E F6 45 08 01 74 24 64 A1 18 00 00 00 8B
40 30 39 58 10 74 16
Normalerweise finden wir unser Programm in der oberen Ausgangslage, der obere Code ist ein Teil eines echten Programms.
Das erste was wir machen müssen ist sich die ganzen Bytes in ASM-Code umwandeln zu lassen.
Das ganze kann man ziemlich einfach und improvisiert mit OllyDbg machen.
Man öffnet einfach einen beliebigen Prozess, welcher keine Auswirkung auf die Prozessstabilität hat, markiert dort einfach einen beliebigen Teil des Programmes (lieber zu viel als zu wenig), macht einen Rechtsklick und klickt dann auf "Binary-->Binary paste"


wenn man das ganze dann anschaut, sieht das in etwa so aus (die Addressen unterscheiden sich natürlich immer, sind aber auch unwichtig)
Code:
004308B8      3BF3           CMP ESI,EBX
004308BA      0F8C 99000000  JL 00430959
004308C0      389D 03FFFFFF  CMP BYTE PTR SS:[EBP-FD],BL
004308C6      0F85 F20E0000  JNZ 004317BE
004308CC      8D85 ECFEFFFF  LEA EAX,DWORD PTR SS:[EBP-114]
004308D2      50             PUSH EAX
004308D3      8D85 E0FEFFFF  LEA EAX,DWORD PTR SS:[EBP-120]
004308D9      50             PUSH EAX
004308DA      8DB5 F4FEFFFF  LEA ESI,DWORD PTR SS:[EBP-10C]
004308E0      E8 F1FDFFFF    CALL 004306D6
004308E5      8BF0           MOV ESI,EAX
004308E7      3BF3           CMP ESI,EBX
004308E9      7C 6E          JL SHORT 00430959
004308EB      F645 08 01     TEST BYTE PTR SS:[EBP+8],1
004308EF      74 24          JE SHORT 00430915
004308F1      64:A1 18000000 MOV EAX,DWORD PTR FS:[18]
004308F7      8B40 30        MOV EAX,DWORD PTR DS:[EAX+30]
004308FA      3958 10        CMP DWORD PTR DS:[EAX+10],EBX
004308FD      74 16          JE SHORT 00430915
Wir wollen jetzt unseren Code-Cave irgendwo in diesem Code einfügen lassen, wo ist erst einmal egal.
Das ganze klingt auf den ersten Blick nach einer sehr billigen Aufgabe, aber in Wirklichkeit haben wir nur ein paar wenige Stellen in unserem Code, wo das Code-Cave-Einfügen wirklich Sinn macht.

Gehen wir einfach einmal alle möglichen Stellen von oben nach unten durch:

1. CMP ESI,EBX

Das ganze sieht auf den ersten Blick sofort nach einem Volltreffer aus.
Aber so ist es leider nicht. Wir erinnern uns, dass unser JMP immer 5 Bytes größe braucht.
Wir haben aber nur 2 Bytes, nämlich "3B F3".
Das reicht uns nicht, da fehlen 3 Bytes, aber wir sind ja nicht auf den Kopf gefallen, lassen wir einfach 2 Befehle überschreiben:
Code:
3BF3 CMP ESI,EBX
0F8C 99000000 JL 00430959
Jetzt haben wir zwar zu viele Bytes überschrieben, nämlich genau 8 Bytes.
Aber auch das ist kein Problem, füllen wir einfach die überschüssigen 3 Bytes mit Nops auf,
so dass unser Code so aussieht:
Code:
004308B8		     JMP ARBEITSSPEICHER
			     NOP
			     NOP
			     NOP
004308C0      389D 03FFFFFF  CMP BYTE PTR SS:[EBP-FD],BL
004308C6      0F85 F20E0000  JNZ 004317BE
004308CC      8D85 ECFEFFFF  LEA EAX,DWORD PTR SS:[EBP-114]
004308D2      50             PUSH EAX
004308D3      8D85 E0FEFFFF  LEA EAX,DWORD PTR SS:[EBP-120]
004308D9      50             PUSH EAX
004308DA      8DB5 F4FEFFFF  LEA ESI,DWORD PTR SS:[EBP-10C]
004308E0      E8 F1FDFFFF    CALL 004306D6
004308E5      8BF0           MOV ESI,EAX
004308E7      3BF3           CMP ESI,EBX
004308E9      7C 6E          JL SHORT 00430959
004308EB      F645 08 01     TEST BYTE PTR SS:[EBP+8],1
004308EF      74 24          JE SHORT 00430915
004308F1      64:A1 18000000 MOV EAX,DWORD PTR FS:[18]
004308F7      8B40 30        MOV EAX,DWORD PTR DS:[EAX+30]
004308FA      3958 10        CMP DWORD PTR DS:[EAX+10],EBX
004308FD      74 16          JE SHORT 00430915
und der Code in unserem Arbeitsspeicher müsste dann einfach so aussehen:

Code:
3BF3 CMP ESI,EBX
0F8C 99000000 JL 00430959
JMP JUMP_ZURÜCK
Nur wir sehen, dass jetzt der Jump "JL 00430959" jetzt eventuell irgendwohin springt, wo gar kein Code ist.
Das Offset des Jump-Befehls (Also die Befehle, die der Jump-Befehl überspringt) liegt aber bei "99000000".
Das bedeutet jetzt jedoch nicht, dass der Jump-Befehl 0x99000000 Bytes überspringt, das wären auch viel zu viele.
Man muss die Bytes noch in eine entsprechende Hexadezimalzahl umrechnen und dies geht wie folgt:

Zahl in Bytes:
Code:
99000000
Zuerst werden die Zeichen in 2er Gruppen zerlegt:
Code:
99 00 00 00
Anschließend werden die Bytes wie folgt geordnet:


Und dann haben wir unsere Zahl:
Code:
0x00000099
Wir überspringen also 0x99 Bytes.
Da wir aber nicht wissen ob in unserem Arbeitsspeicher bei der Addresse JMP_ADDRESSE+0x99 ein Befehl steht, sollten
wir es vermeiden allgemein Jump-Befehle in unserem Code-Cave aufzunehmen.

Also schauen wir unseren Code weiter an und sehendanach eine weitere mögliche Stelle, wo unser Code-Cave-Jump rein kann:

2. "CMP BYTE PTR SS:[EBP-FD],BL"

Diese Stelle wäre prinzipiell möglich zu benutzen, sie bietet genug Platz für unseren Code-Cave-Jump und sie springt nicht irgendwo wild in der Gegend herum.
Jedoch hat es einen großen Nachteil hier den Jump zu setzen (was aber theoretisch möglich wäre), da der Befehl CMP immer Flags für die nachfolgenden Jumps setzt, dürften wir keinerlei Befehle verwenden, die auch Flags setzen,
sonst könnte der nachfolgende Jump falsch ausgeführt werden.

Die nächste mögliche Stelle wäre dann:

3. "LEA EAX,DWORD PTR SS:[EBP-114]"

Und diesen Befehl können wir endlich ersetzen lassen, den Meisten wird der Befehl wohl nichts sagen, aber wofür gibt es eine Dokumentation:



Der Befehl bietet jedoch 6 Bytes an und wir brauchen nur 5.
Das heißt wir müssen ein NOP setzen.
Einige fragen sich jetzt eventuell wieso man nicht einfach den einen Byte in Ruhe lässt,
naja gehen wir das ganze einfach einmal theoretisch durch:

Das ist der Maschinen-Code:
"8D85 ECFEFFFF"

Wir überschreiben jetzt nur die ersten 5 Bytes mit unserem JMP:
"XX XX XX XX XX FF"

Dann sehen wir es bleibt ein "FF" übrig.
Dadurch kann es passieren, dass unser JMP nicht mehr als JMP angesehen wird, sondern z.B. als MOV oder als ADD oder noch schlimmer als CALL, was die Stabilität unseres Programmes extrem gefährden würde.
Deswegen müssen wir dieses eine Byte durch ein NOP ersetzen lassen.

Jetzt wo wir wissen, dass wir unseren JMP-Befehl nicht einfach immer 5 Bytes überschreiben lassen können ohne auf die übrigen Bytes des Befehls zu schauen, sollten wir auch unseren Pseudocode anpassen, sonst können wir diesen ja gar nicht benutzen:

PHP Code:
$insert_at_address=0x02124334 // Addresse an der wir unseren Code einfügen wollen
$opcode_to_insert="03D0" // Entspricht folgendem ASM-Code: ADD EDX, EAX
$variable_to_safe_read_bytes=""
$bytes_to_overwrite=6;

$process_handle=GetProcessHandleByName("test.exe")
ReadProcessMemory($process_handle,$insert_at_address,$variable_to_safe_read_bytes,$bytes_to_overwrite

$code_cave_address=VirtualAllocEx($process_handle,NULL,StringLength($opcode_to_insert)/2+$bytes_to_overwrite+5,0x00001000,0x40

WriteProcessMemory($process_handle,$insert_at_address,"E9"+NumberToBytes($code_cave_address-$insert_at_address-5),5
WriteProcessMemory($process_handle,$code_cave_address,$variable_to_safe_read_bytes,StringLength($variable_to_safe_read_bytes)/2)
WriteProcessMemory($process_handle,$code_cave_address+$bytes_to_overwrite,$opcode_to_insert,StringLength($opcode_to_insert)/2)
WriteProcessMemory($process_handle,$code_cave_address+StringLength($opcode_to_insert)/2,"E9"+NumberToBytes(($insert_at_address+5)-($code_cave_address+StringLength($opcode_to_insert)/2+$bytes_to_overwrite+5)),5
Ich werde den Code jetzt nicht weiter erörtern, ich denke er sollte jetzt auch mit der kleinen Änderung klar sein.
-------------------------------------------------------------------------------------------------
4.4 Kommunikation zwischen dem Code-Cave und externen Prozessen
Shadow992 is offline  
Thanks
22 Users
Old 11/14/2012, 00:46   #2
 
elite*gold: 0
Join Date: Jun 2012
Posts: 80
Received Thanks: 532
good tutorial, but it would be nice a little translation to english ^^
-Cesc- is offline  
Thanks
1 User
Old 11/14/2012, 12:23   #3

 
elite*gold: 190
The Black Market: 143/0/0
Join Date: Aug 2011
Posts: 8,447
Received Thanks: 1,124
Kapiere zwar nicht wirklich viel, aber ein Thanks bekommst du trotzdem für die Mühe.
Carlos Lehder is offline  
Thanks
1 User
Old 11/14/2012, 15:05   #4
 
elite*gold: 0
Join Date: Jul 2011
Posts: 5
Received Thanks: 2
Du bist meine rettung xD warte gespannt auf Nr. 4.4
Soulsbook is offline  
Thanks
1 User
Old 11/16/2012, 14:41   #5
 
elite*gold: 10
Join Date: Aug 2012
Posts: 813
Received Thanks: 106
Wirklich gut gemacht! Es ist sehr hilfreich und gut erklärt. Ich habe selten ein so gut aufgebautes Tutorial gesehen.
GodHacker is offline  
Thanks
1 User
Old 11/16/2012, 23:46   #6
 
tolio's Avatar
 
elite*gold: 2932
The Black Market: 169/1/0
Join Date: Oct 2009
Posts: 6,966
Received Thanks: 1,097
geile scheiße, zwar für 98% der epvp besucher (nicht coders den) zu hoch, trotzdem sehr schön ge-und beschrieben, so sollte ein tutorial aussehen!
tolio is offline  
Thanks
1 User
Old 10/26/2013, 12:51   #7
 
elite*gold: 0
Join Date: Oct 2013
Posts: 3
Received Thanks: 1
Oh ja, vielen Dank. Bin grad soderso dabei mich mit Assembler zu beschäftigen und da kommt so etwas natürlich Perfekt. Also sehr gut aufgebaut, die Einleitung ist nicht zu kurz oder zu lang und ich hab hier wirlich viel gelernt. Nochmal Danke, wirklich gut.
PORNO|BO$$ is offline  
Thanks
1 User
Old 10/29/2013, 23:59   #8
 
elite*gold: 0
Join Date: Sep 2010
Posts: 9
Received Thanks: 2
Schön ausführlich erklärt und nachvollziehbar..
Wenn du das noch fortsetzt, wäre das toll

Danke
VOLLCOOL is offline  
Thanks
1 User
Reply


Similar Threads Similar Threads
[Release] UDF for Easy Code-Cave injection and Memory Manipulation
02/06/2013 - AutoIt - 9 Replies
English: This project was started because I was bored to do all these useless things (like allocate memory, create opcode for injection etc.) again and again. So i wrote this UDF for Code-Cave-Injection. Just to sum up in a few words what a Code-Cave can do: A Code-Cave enables you to insert some ASM-Code into an running process, which then gets executed by the process. On that way you are able to read the value of every register at any time, which is not always possible with Pointer...
[Anleitung] Cataclysm 4.0.6a Privatserver Client (Alle Sprachen)
01/03/2013 - WoW Private Server - 14 Replies
→→→ Nicht mehr Aktuell ←←← Cataclysm installation von Grundauf. Mit dieser kleinen Anleitung möchte ich euch in 3. Schritten erklären wie ihr einen Cataclysm Client 4.0.6a leicht und unkompliziert in allen verfügbaren Sprachen installieren könnt. 1. Ihr ladet den World of Warcraft Client von der originalen Blizzard Seite runter. Klick Diesen lasst ihr nun bis zum vollen Patch durchlaufen (Aktuell 4.3.4.15595).
[B] Programm Erstellung (Alle Sprachen) [S]Geld,egold
10/03/2012 - elite*gold Trading - 8 Replies
Hallo :) In diesem Thread möchte ich euch nun meine Programmierkünste zeigen :) Ich werde für euch für ein paar Euros oder egold ein perfektes Programm erstellen. Ich kann alle Sprachen. Einige sehr gut und einige nicht so gut. Gehandelt wird über Skype, da es so einfacher und übersichtlicher ist :) Ich nehme: PSC, PayPal, egold(ab 400) Und nun: Aufträge bitte :D
AutoIt code cave no DLLs xD ToxicFog leveler example
05/06/2009 - CO2 Programming - 18 Replies
Hey guys, This one is inspired by IAmHawkings w/ his code inject in VB. Inside the .zip i provide source code as well as executable to show you all how code injection can be done in AutoIT. Note: Not all assembly instructions are implemented, i got lazy lol. Steps to using it:



All times are GMT +2. The time now is 01:13.


Powered by vBulletin®
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
SEO by vBSEO ©2011, Crawlability, Inc.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Support | Contact Us | FAQ | Advertising | Privacy Policy | Terms of Service | Abuse
Copyright ©2024 elitepvpers All Rights Reserved.