Einführung in das Ausnutzen und Umkehren mit IDA FREE und anderen kostenlosen Tools. Kapitel 2

Im ersten Teil haben wir verschiedene Tools installiert, die für diesen Kurs hilfreich sind. Ihr Merkmal ist, dass sie alle frei sind. Wir werden kein kostenpflichtiges Tool verwenden, und von denen, die eine kostenpflichtige Version haben, wie IDA oder PYCHARM, werden wir die KOSTENLOSE oder COMMUNITY-Version verwenden.

Schauen wir uns einige Konzepte an, bevor wir mit den Übungen beginnen.

Was ist eine TASCHE?

BAG ist das Ergebnis eines Fehlers oder Mangels bei der Erstellung von Computerprogrammen (Software) oder einem Computer. Der angegebene Fehler kann in jeder Phase des Software-Lebenszyklus auftreten, obwohl der offensichtlichste Fehler in der Entwicklungs- und Programmierphase auftritt.

Wie ich immer sage, kann ein Programmierer Fehler machen, und diese Fehler können Programmabstürze oder Fehler verursachen. Bisher habe ich nichts Neues gesagt.

Die Frage ist, den Unterschied zwischen BAG und VULNERABILITY zu kennen. Lassen Sie uns also sehen, was VULNERABILITY ist.

Was ist VULNERABILITÄT?

VULNERABILITY ist eine bestimmte Art von Fehler in einem Programm, mit dessen Hilfe die Sicherheit eines Computersystems verletzt werden kann.

Mit Schwachstellen können Sie daher Aktionen ausführen, für die das Programm nicht vorgesehen war, und diese missbrauchen.

Mit anderen Worten, Sicherheitslücke ist eine bestimmte Art von Fehler, eine Teilmenge zwischen ihnen.



Natürlich gibt es viele Arten von Schwachstellen. Wir werden uns auf die Untersuchung und Ausnutzung von Schwachstellen in WINDOWS konzentrieren.

Was ist EXPLOIT?


EXPLOIT ist ein Computerprogramm, das versucht, eine Sicherheitslücke eines anderen Programms auszunutzen. Das ultimative Ziel eines Exploits kann böswillig sein, beispielsweise das Zerstören oder Herunterfahren eines angegriffenen Systems, obwohl es normalerweise eine Verletzung von Sicherheitsmaßnahmen darstellt, um unbefugten Zugriff auf Informationen zu erhalten und diese in ihrem eigenen Interesse oder als Quelle für andere Angriffe auf Dritte zu verwenden.

Der Missbrauch der Sicherheitsanfälligkeit kann zum Ausfall der Anwendung oder des Systems selbst sowie zur Ausführung von nativem Code auf lokalen oder Remotecomputern führen. Der Betrieb und die Komplexität hängen von der Sicherheitsanfälligkeit selbst, der Umgebung und den Maßnahmen ab, die das Ziel während des Betriebs hat.

Die erste Art von Schwachstellen, die wir untersuchen werden, sind Pufferüberläufe. Wir werden mit den einfachsten Beispielen beginnen und dann die Komplexität schrittweise erhöhen.

Zuerst werden die Sicherheitsfunktionen des Systems nicht aktiviert, aber nach und nach werden wir sie aktivieren, um herauszufinden, wie wir mit ihnen umgehen können und in welchen Situationen.

Was ist ein Puffer?


BUFFER ist ein Speicherplatz einer bestimmten Größe, der für die Speicherung und Verwaltung von Daten reserviert ist.

Ein einfaches Beispiel ist ein 20-Liter-Glas, das ich zur Aufbewahrung von Inhalten habe. Es kann kleiner oder gleich 20 Liter sein, was der maximalen Größe entspricht. Wenn Sie mehr in einem Tank aufbewahren möchten, müssen Sie einen Weg finden, um den Puffer zu vergrößern. Wenn Sie beispielsweise versuchen, beispielsweise 40 Liter in einer 20-Liter-Dose einzusparen, läuft dieser über.

Was ist eine Pufferüberfüllung?


BUFFER OVERFLOW tritt auf, wenn ein Computerprogramm die für ihn reservierte Speichermenge überschreitet, indem Daten in einen zusammenhängenden Speicherblock geschrieben werden.

https://www.welivesecurity.com/la-es/tag/buffer-overflow-la-es

In Wahrheit tritt in einer Anwendung ein Pufferüberlauf auf, wenn der Programmcode nicht die erforderlichen Sicherheitsüberprüfungen enthält, z. B. das Messen der Datenmenge die in den Puffer kopiert werden und die Puffergröße nicht überschreiten.

Die häufigsten Arten von Pufferüberläufen sind Stapelpufferüberläufe und Heappufferüberläufe.

Hier sehen wir die Definition des Pufferüberlaufs. Wenn ich in unserem vorherigen Beispiel versuche, 40 Liter in einen 20-Liter-Tank zu füllen, läuft dieser nach unserem Verständnis über. Dies ist ein Überlauf, der einen Pufferüberlauf verursacht, d.h. Überlauf meines Tanks, wenn seine maximale Kapazität überschritten wird.

Erklären Sie nun den Unterschied zwischen dem Stapel und dem Heap.

Was ist ein Stapel?


STACK wird verwendet, um lokale Funktionsvariablen zu speichern, die nur benötigt werden, solange die Funktion ausgeführt wird. In den meisten Programmiersprachen ist es wichtig, dass wir zur Kompilierungszeit wissen, wie groß eine Variable ist, wenn wir sie auf dem Stapel behalten möchten.

Was ist eine Menge?


Heap wird verwendet, um dynamischen Speicher zu reservieren, dessen Nutzungsdauer nicht im Voraus bekannt ist, aber es wird erwartet, dass er einige Zeit dauern wird. Wenn wir seine Größe nicht kennen oder es zur Laufzeit bestimmt wird, muss die Größe berechnet und auf dem Heap reserviert werden.

Der Heap wird auch für Objekte verwendet, deren Größe variiert, da wir zur Kompilierungszeit nicht wissen, wie lange sie verwendet werden.

Ich arbeite seit mehr als 13 Jahren in unserem Unternehmen als Autor von Exploits, und das erste, was wir mit all den Angestellten machen, die mir bei meinem Beitritt angetan wurden, war zu versuchen, die Stapel und Haufen des berühmten GERARDO RICHART zu entwirren. Er ist einer der Gründer von CORE SECURITY und ein Guru für Exploit-Analysen.

Wir werden langsam mit den einfachsten Stapeln beginnen. Natürlich sind sie, wie gesagt, im Moment mit minimalem Schutz kompiliert und 32-Bit, um den Betrieb zu erleichtern.

Schauen wir uns den Quellcode für die STACK1-Task an.

https://drive.google.com/open?id=16btJAetpa1V5yHDZE2bnnFWTQNpsUR4H

Wir sehen einen Ordner mit Übungen, und darin befindet sich der Quellcode STACK1 mit dem Namen STACK1_VS_2017.CPP.

#define _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_DEPRECATE

#include <stdlib.h>
#include  <stdio.h> 
#include "Windows.h"


int main(int argc, char **argv) 
{


	MessageBoxA((HWND)-0, (LPCSTR) "Imprimir You win..\n", (LPCSTR)"Vamosss", (UINT)0);

	int cookie;
	char buf[80];

	printf("buf: %08x cookie: %08x\n", &buf, &cookie);
	gets(buf);

	if (cookie == 0x41424344) printf("you win!\n");

}


Wir werden versuchen, diesen Code zu verstehen und herauszufinden, wo der Pufferüberlauf auftreten kann und ob es sich um einen Pufferüberlauf auf dem Stapel oder auf dem Heap handelt.

Der MessageBoxA-Funktionsaufruf wurde dem STACK1-Quellcode hinzugefügt, um uns eine kleine Nachricht anzuzeigen, die uns auffordert, ihn zu lösen. Dies ist nur eine Ergänzung, die nichts beeinflusst. Dies ist ein Standardaufruf der angegebenen WINDOWS-Funktion, den wir hier nicht analysieren werden.

Wer Informationen zu dieser Funktion benötigt, kann diese hier abrufen.

Wir wissen, dass Sie innerhalb der Funktion, wenn lokale Variablen vorhanden sind, einen Platz für diese reservieren müssen.

Wir bleiben also bei diesem Quellcode, der von GERARDO RICHART erstellt wurde.

int main(int argc, char **argv) 
{

	int cookie;
	char buf[80];

	printf("buf: %08x cookie: %08x\n", &buf, &cookie);
	gets(buf);

	if (cookie == 0x41424344) printf("you win!\n");

}

Wir sehen in rot den ersten Teil des Programms, in dem Platz für lokale Variablen reserviert ist. In diesem Fall gibt es zwei lokale Variablen, COOKIE und BUF.

Sie können die Datentypen in der Tabelle sehen . Dort befinden sich auch andere Arten von Variablen.

Der Code wird in 32 Bit kompiliert.

Wir sehen, dass die COOKIE-Variable vom Typ INT ist, sodass 4 Byte Speicher für diese Variable reserviert werden.



Im Fall der BUF-Variablen sehen wir, dass es sich um ein Array oder eine Zeichenfolge handelt (Zeichengröße = 1 Byte).



Jene. es wird ein Array von 80 Zeichen sein, d.h. seine Länge beträgt 80x1 = 80 Bytes.

Jeder, der nicht weiß, was ein Array darüber liest, kann hier lesen:

https://www.programiz.com/c-programming/c-arrays

Somit kann ein Array viele Werte desselben Datentyps speichern. Sie müssen ihm nur sagen, welche Art von Daten und wie viel Daten vorhanden sein werden.



Im ersten Beispiel ist dies ein Array von ganzen Zahlen, d.h. Es sind 100 Bytes, und da jede Ganzzahl 4 Bytes benötigt, beträgt die Länge des Arrays 100 x 4 = 400 Bytes.

Im zweiten Beispiel benötigt FLOAT 4 Bytes, es handelt sich also um ein Array von 5 FLOAT, sodass seine Länge 5 x 4 = 20 Bytes beträgt.

Wenn wir das Array auf einer niedrigen Ebene analysieren, werden wir feststellen, dass es sich um einen reservierten Speicherplatz oder Puffer handelt. Dies ist nicht die einzige Möglichkeit, Speicherplatz zu reservieren. Es gibt andere Arten von variablen Daten, die ebenfalls Speicherplatz im Speicher benötigen, der als Puffer zum Speichern ihres Inhalts dient.

Zurück zu unserer Übung:

char buf[80];

Dies ist ein Array von Zeichen mit einer Länge von 80 x 1 = 80 Bytes, d.h. es sieht aus wie unser 20 Liter Glas. Wenn wir versuchen, mehr als 80 Bytes zu speichern, läuft die Bank über.

Nun wollen wir sehen, wo der BUF-Puffer verwendet wird.



Wir sehen, dass der Puffer an zwei Stellen verwendet wird, die mit roten Pfeilen markiert sind.

Die erste Anweisung verfügt über eine PRINTF-Funktion, mit der eine Nachricht in der Konsole angezeigt wird, bei der es sich um eine Zeichenfolge in Anführungszeichen handelt.

"buf: %08x cookie: %08x\n"

Die PRINTF-Funktion druckt die Zeichenfolge jedoch nicht nur in Anführungszeichen, sondern auch im angegebenen Format. Die darin enthaltenen Prozentsätze geben an, dass eine Ausgabezeile erstellt wird. Wir sehen, dass der String nur das erste Argument für die Funktion ist. Das Ausgabeformat und andere Argumente können mehrere sein (für jedes Argument% im Format gibt es eines). In unserem Fall gibt es zwei davon.



In diesem Fall haben wir zwei% X-Formate. Wenn ich mich also auf die PRINTF-Formattabelle beziehe:



Wir sehen, dass die Funktion diese ganzen Zahlen (INT) nimmt und sie in die Ausgabezeile mit der Basis des Zahlensystems 16 einfügt , d. H. im hexadezimalen Format. 08 zeigt an, dass wenn die Nummer weniger als 8 Ziffern enthält, die Funktion sie mit Leerzeichen füllt.

Die Ausgabe für "buf:% 31x" & buf wird so sein

buf:             19FED4 

Wir sehen, dass in diesem Beispiel Leerzeichen vor der Zahl stehen. Es gibt verschiedene Modifikatoren, um die Ausgabe anzuzeigen.

Alle möglichen Fälle sind hier aufgelistet:

http://www.cplusplus.com/reference/cstdio/printf/

Unser Fall ist folgender:





Wir sehen, dass das Ergebnis nicht abgeschnitten wird, sondern nur dann mit Leerzeichen gefüllt wird, wenn die Länge des einzufügenden Arguments kleiner als der Wert ist vor X.

Daher wissen wir, dass die Funktion zwei Hexadezimalzahlen druckt, die aus zwei Argumenten erhalten werden.

printf("buf: %08x cookie: %08x\n", &buf, &cookie);

Wir wissen, dass eine Variable eine Speicheradresse und einen Wert hat, der gespeichert werden kann. Es sieht aus wie unser 20-Liter-Glas. Es hat seinen Inhalt oder seine Bedeutung, d.h. Im Inneren gelagerte Liter, aber auch wenn ich eine Garage voller ähnlicher Dosen habe, muss ich feststellen, wo sich die gewünschte Dose unter all den befindet, die ich habe.

Das Symbol & zeigt dies an. Es gibt die Adresse oder den Ort des Glases zurück, nicht dessen Inhalt oder Wert.

Definition von AMPERSAND


Mit AMPERSAND wird die Speicheradresse der Variablen angegeben, in der Daten gespeichert werden.

Wenn ich die ausführbare Datei in der Konsole ausführe, wird beispielsweise angezeigt, dass beim Ausführen der PRINTF-Funktion Folgendes gedruckt wird:



Die Adressen können sich auf Ihrem PC ändern. Da jedoch die niedrigere Adresse beider Adressen mit der BUF-Adresse übereinstimmt, sehen wir, dass sie sich wie folgt befinden:



Die BUF-Adresse ist kleiner als die COOKIE-Adresse und erhöht sich daher.

Und was sagen uns diese variablen Adressen? (In meinem Fall & BUF = 0x19FED4 und & COOKIE = 0x19FF24)



Beide sind im hexadezimalen Format. Erinnerst du dich, dass dies das% X-Format war? Also habe ich 0x vorgesetzt, um Dezimalzahlen zu unterscheiden, die wir ohne Zusätze darstellen werden.

Wenn ich eine Subtraktion in der PYTHON-Konsole oder in PYCHARM durchführe:



Wir erhalten ein Ergebnis von 80 Bytes, da die COOKIE-Variable angeblich genau dort beginnt, wo der BUF-Puffer endet. Der Unterschied gibt uns also die Größe des Puffers.

Wenn wir diese Art von Variablen basierend auf dem Quellcode erstellen, kann es häufig vorkommen, dass der Compiler uns eine größere Größe als die im Quellcode reservierte gibt. Der Compiler garantiert, dass er mindestens 80 Bytes reserviert, d. H. er kann mehr reservieren, nicht weniger.

Tatsache ist, dass wir bereits etwas über den Code, die Größe der Variablen und ihre Position wissen, da er über die PRINTF-Funktion verfügt.

Schauen wir uns nun einen anderen Ort an, an dem der BUF-Puffer verwendet wird, da das Programm jetzt nur seine Adresse druckt, sie jedoch nicht zum Speichern von Daten verwendet.

int main(int argc, char **argv) 
{

	int cookie;
	char buf[80];

	printf("buf: %08x cookie: %08x\n", &buf, &cookie);
	gets(buf);

	if (cookie == 0x41424344) printf("you win!\n");

}

Hier in der roten Linie ist GET eine Funktion zur Eingabe von Daten über die Tastatur. Daten werden eingegeben, bis ich die Eingabetaste drücke.

Das Programm kann die vom Benutzer eingegebene Datenmenge nicht begrenzen, und es gibt auch keine Möglichkeit, diese Daten zu überprüfen. Alles, was vor dem Drücken der ENTER-Taste eingegeben wird, wird in den BUF-Puffer kopiert.

Das ist das Problem. Wir haben gesagt, dass BUF nur maximal 80 Bytes speichern kann. Wenn wir also mehr eingeben, erstellen wir einen Pufferüberlauf. Hier sind alle Bedingungen dafür, denn wenn der Benutzer mehr als 80 Bytes schreibt, läuft unser Tank über und die Flüssigkeit tropft nach unten .



Tatsache ist, dass sich unter dem BUF die COOKIE-Variable befindet, sodass der Überlauf diese überschreibt und mit einem Wert füllt, den Sie steuern können.

Wenn zum Beispiel jemand, der 80 * A und 4 * B druckt, 80 * A BUF und 4 * B COOKIE ausfüllt, und wie wir wissen, bleibt der Wert niedrig, wenn jemand ein Zeichen in der Konsole druckt. ASCII.



Da der Cookie mit vier Buchstaben B gefüllt wird, die dem Wert 0x42 entsprechen, können wir garantieren, dass der Cookie-Wert 0x42424242 ist, d. H. Auf meinem Computer hat die Cookie-Adresse 0x19FF24 0x42424242 als Inhalt.

0x19FF24 => 42424242



Tatsache ist, dass wir bereits gesehen haben, wie der COOKIE-Wert übergelaufen und gesteuert werden kann.

int main(int argc, char **argv) 
{

	int cookie;
	char buf[80];

	printf("buf: %08x cookie: %08x\n", &buf, &cookie);
	gets(buf);

	if (cookie == 0x41424344) printf("you win!\n");

}

Sie müssen "Sie gewinnen" ausdrucken, um die Übung abzuschließen. Dafür muss der COOKIE gleich dem Wert 0x41424344 sein, und wenn es keinen Überlauf gäbe, wäre dies unmöglich, da sich der COOKIE-Wert seit Beginn des Programms nie geändert hat. Wir können "Sie gewinnen" nicht drucken und verwenden dafür einen Pufferüberlauf, der besagt, dass das Programm möglicherweise eine andere als die programmierte Aktion ausführen kann.

In diesem Fall können Sie niemals "Sie gewinnen" eingeben. Nur ein Überlauf ermöglicht dies.

AAAAAAAA

Mit anderen Worten, anstatt beispielsweise 80 * A und 4 * B zu übergeben, müssen Sie zum Drucken von „Sie gewinnen“ 80 * A und dann die Buchstaben DCBA übergeben, da dadurch die Werte im COOKIE gespeichert werden ASCII.

44434241

https://www.arumeinformatica.es/blog/los-formatos-big-endian-y-little-endian/

Das Format, in dem Daten gespeichert werden, ist LITTLE ENDIAN. Mit anderen Worten, die Daten im Speicher werden in umgekehrter Reihenfolge gespeichert, um es einfach auszudrücken.



Und wenn die Sequenz 0x41424344 gespeichert wird, speichert das System sie im Speicher wie folgt:

44 43 42 41

Aus diesem Grund erfolgt beim Kopieren in den Speicher das Kopieren während der Eingabe. Daher müssen wir den Wert in umgekehrter Reihenfolge schreiben, damit er beim Lesen aus dem Speicher angezeigt wird war in der richtigen Form.

Wir können die ausführbare Datei in der Konsole ausführen.



Und der Cursor blinkt, während die GET-Funktion mich auffordert, die Eingabe einzugeben. Geben Sie vorsichtig 80 Zeichen A und dann DCBA ein.

In der PYTHON- oder PYCHARM-Konsole kann ich die Zeile drucken, ohne Anführungszeichen kopieren und in die Konsole einfügen, um sie nicht wie verrückt zu drucken, und dann die EINGABETASTE drücken, um sie einzugeben.





Wir sehen, dass wir "Sie gewinnen" haben.

Wir können dies im Debugger sehen. Dafür verwenden wir X64DBG.



Ich wähle die 32-Bit-Version.





Wenn wir bei der NTDLL.DLL-Bibliothek anhalten, drücken wir erneut RUN mit F9.

Wir sehen, dass der Debugger beim ersten Befehl des STACK1-Moduls stoppt, der als ENTRY POINT bezeichnet wird, oder beim ersten Befehl, der vom Modul ausgeführt wird.



Offensichtlich ist dies nicht wie unser Quellcode. Sie müssen verstehen, dass der Compiler viel Code hinzufügt, damit die ausführbare Datei funktioniert und ordnungsgemäß ausgeführt wird. Wir werden versuchen, unsere Hauptfunktion zu finden. Wir können uns an den Programmlinien orientieren.



Wir haben nur die Suche in der aktuellen Region ausgewählt. Wir wissen, dass sich die Zeilen im selben Abschnitt befinden werden.



Hier sehen wir, dass es Programmzeilen und andere gibt, die der Compiler hinzugefügt hat. Wir doppelklicken auf eine unserer Zeilen.



Jetzt können Sie viel mehr sehen. Wir sehen einen Aufruf der Funktion MessageBoxA, PRINTF, GETS und einen Vergleich mit dem Wert 0x41424344.

Zusätzlich fügen wir ein Plugin zum Dekompilieren von SNOWMAN hinzu. Wir können versuchen zu sehen, wie es den Code dekompiliert, d.h. wie er versucht, den Quellcode oder etwas so Ähnliches wie möglich aus der kompilierten Datei zu erhalten.





Wir sehen, dass dies nicht perfekt ist, aber es ist besser als das, was es war.

Ich werde BP an den Anfang der Funktion setzen und F9 drücken, bis der Debugger stoppt.



Für diejenigen, die nicht wissen, was ein Funktionsargument ist.



In unserem Fall hat die Hauptfunktion Argumente, die jedoch nicht innerhalb der Funktion verwendet werden.

int main(int argc, char **argv) 
{

	int cookie;
	char buf[80];

	printf("buf: %08x cookie: %08x\n", &buf, &cookie);
	gets(buf);

	if (cookie == 0x41424344) printf("you win!\n");

}

Hier sehen wir, dass es zwei Argumente gibt, die durch den Stapel geleitet werden, wenn die ausführbare Datei in 32 Bit kompiliert wird.

Kurz vor dem Funktionsaufruf werden die Argumente auf dem Stapel gespeichert.

Wenn wir am Anfang der Funktion anhalten, ist der erste Wert auf dem Stapel RETURN ADDRESS, d. H. Wo die Funktion nach Abschluss der Funktion zurückkehrt, und unter diesem Wert befinden sich die Argumente dieser Funktion.

Wenn ich mit der rechten Maustaste auf RETURN ADDRESS klicke und FOLLOW DWORD IN DISASSEMBLER auswähle, wird angezeigt, wohin der Debugger nach Abschluss der Funktion zurückkehren soll.



Er wird hierher zurückkommen. Dies bedeutet, dass die Hauptfunktion von einem höheren Aufruf aufgerufen wurde. Ich kann BP hierher bringen, die Übung neu starten und sicherstellen, dass es so ist.



Ich werde BP etwas früher setzen und das Programm neu starten.



Der Debugger stoppt hier.



Mit diesen PUSH-Anweisungen werden die Argumente in der Hauptfunktion gespeichert.

Unten finden Sie einen Link für diejenigen, die mehr über die Funktionsargumente erfahren möchten:

https://publications.gbdirect.co.uk/c_book/chapter10/arguments_to_main.html

Dies ist nicht sehr schwierig. Das erste Argument für ARGC ist INT, das die Anzahl der zur Ausführung des Programms verwendeten Konsolenparameter angibt, einschließlich des Pfads zur ausführbaren Datei. ARGV ist ein Array von Zeigern auf Zeichenfolgen.

Wir sehen, dass ich, wenn ich die Befehlszeile ändere, mehr Argumente übergeben und neu laden werde.





Hier stoppt der Debugger, wenn Argumente mit der Anweisung PUSH gespeichert werden sollen. Das erste gespeicherte Argument ist das am weitesten entfernte, und das letzte, das Sie speichern, ist das erste Argument für die Funktion.

Ich verfolge und jeder PUSH-Befehl speichert die Argumente.



Hier sehe ich die Argumente der Funktion. Oben ist das erste Argument für ARGC. Es ist 3, da es die Anzahl der Argumente angibt, die an die Konsole übergeben werden.



Hier sind 3 Argumente.

Jetzt drücken wir F7, um STEP INTO auszuführen und die Funktion aufzurufen.

Hier sehen wir, dass der Debugger beim Eingeben des CALL die RETURN ADDRESS im Stack speichert.



Wie bereits bei der Eingabe der Funktion erwähnt, wird als Erstes auf dem Stapel RETURN ADDRESS (in 32-Bit-Kompilierung) gespeichert. Nachfolgend sind die Argumente für die Funktion aufgeführt, zuerst das erste Argument und dann der Rest nacheinander.



Das zweite Argument ist, wie wir gesehen haben, eine Reihe von Zeigern. Hier sehen wir im Speicher, dass es drei Zeiger auf drei Zeilen gibt, die als Argumente übergeben werden.



Hier haben wir am Anfang der Funktion angehalten, etwas tiefer haben wir RÜCKGABEADRESSEN und ARGUMENTE.

Wir stellen klar, dass die Datei in 32-Bit kompiliert wird, da in der 64-Bit-Version die Argumente auf andere Weise übergeben werden. Wir werden es später sehen.

Dann beginnt die Funktion auszuführen. Das erste ist das sogenannte PROLOG, das den EBP-Wert der Funktion speichert, die unsere aufgerufen hat.



Dadurch bleibt der EBP-Wert knapp über der Absenderadresse.



Wenn ich den Anweisungen mit F7 folge.

Ich sehe, dass sich der EBP GUARDADO-Wert auf dem Stapel über der Rücksprungadresse befindet.



Die folgende Anweisung in PROLOGUE:

MOV EBP, ESP

Legt den EBP-Wert für die aktuell gespeicherte Funktion fest und war die übergeordnete Funktion, die unsere aufruft (In diesem Fall ist meine Hauptfunktion die EBP-basierte Funktion, in anderen Fällen kann sie anders sein, und wir siehe später)

Durch Platzieren des aktuellen ESP-Werts in EBP stellen wir sicher, dass wir einen Rahmen für unsere aktuelle Funktion erstellen.



Da diese Funktion nun auf EBP oder EBP-BASIERT basiert, wird der EBP-Wert in der Funktion gespeichert und als Referenz akzeptiert, und das ESP ändert sich.



Dieser EBP-Wert wird als Basis verwendet.

In EBP-basierten Funktionen können Variablen und Argumente nach ihrer Entfernung von dieser Adresse benannt werden, die bis zu ihrem Epilog im EBP-Wert gespeichert wird.

In der Liste sind mehrere Variablen aufgeführt, die als EBP-4 oder EBP-54 bezeichnet werden und sich auf den derzeit akzeptierten EBP-Wert beziehen.

Wir können sagen, dass sobald das EBP nach dem PROLOG seinen Wert annimmt, es wie ein Abfluss aussieht, so dass die Argumente immer in diese Richtung gehen, daher bezieht sich EBP + XXX auf die Argumente (das gespeicherte EBP und die RETURN-ADRESSE sind ebenfalls niedriger, haben aber keine Verknüpfungen im Code), während sich die Variablen, wie wir sehen werden, über dieser Adresse befinden, bezieht sich der Link zu EBP-XXX auf eine lokale Variable.

In EBP-basierten Funktionen also:

EBP + XXXX = an die Funktion
EBP übergebene Argumente - XXXX = lokale Funktionsvariablen

Nach PROLOGUE gibt es eine Möglichkeit, Platz für Variablen zu reservieren. In unserem Fall erfolgt dies durch Verschieben des ESP nach oben, sodass der verbleibende Platz darunter für die Summe aller variablen Längen und manchmal etwas mehr für den Fall reserviert ist, was vom Compiler abhängt.

00401043 | 83EC 54 | SUB ESP, 54

Wir sehen, dass sich das ESP über dem EBP befindet, das die Verbindung bleibt, und dass 0x54, konvertiert in eine Dezimalzahl, 84 ist, was die Summe der BUF- und COOKIE-Längen ist. Denken Sie daran, dass sie 80 bzw. 4 waren.







Bei der Ausführung wird ein Leerzeichen für die Variablen BUF und COOKIE mit einer Größe von 84 Byte erstellt. Sie können auf die erste Spalte in horizontaler Richtung klicken, den EBP-Wert anzeigen und diesen Wert auf dem Stapel finden. Offensichtlich wird es jetzt niedriger sein.



Ich doppelklicke hier.



Somit haben wir auch Werte bezüglich EBP auf dem Stapel.

Beispielsweise stimmt EBP-4 mit der Auflistung überein, -4 wird in der ersten Spalte des Stapels angezeigt und in der Erläuterung auch als EBP-4 angezeigt.



Wenn ich nachverfolge, sehen wir, dass von dem Ort, an dem sich das ESP befand, um die Variablen zu reservieren, es immer nach oben bewegt wird, da es den für die Variablen zugewiesenen Speicherplatz berücksichtigen muss. Wenn 4 PUSHs für MessageBoxA ausgeführt werden, platziert der Debugger die Variablen über dem reservierten Speicherplatz und erhöht den ESP.



Wenn ich mir den Stapel ansehe, sehe ich 4 grüne Argumente, die ich über dem reservierten, rot markierten Platz hinzufüge.



Wenn Sie die MessageBoxA-Funktion aufrufen, wird die RETURN-ADRESSE dieser Funktion auf dem Stapel gespeichert.



Hier ist die Absenderadresse von MessageBoxA. Wenn ich durch Verfolgen mit F8 zum RET dieser Funktion komme, führe ich MessageBoxA aus.



Wir sehen, dass der Debugger direkt unter dem MessageBoxA-Aufruf zurückgeht.



Die PUSH-Werte, die Sie für die MessageBoxA-Funktion und die RETURN ADDRESS für diese Funktion übergeben haben, wurden bereits verwendet, und das ESP befindet sich wieder direkt über dem reservierten Bereich, wie zuvor eine Funktion aufgerufen wurde. Das gleiche passiert mit dem Aufruf der PRINTF-Funktion.

Nachdem Sie die PRINTF-Funktion übergeben haben, werden die BUF- und COOKIE-Adressen gedruckt.



Die BUF-Adresse auf meinem Computer lautet 0x19FED4 und die COOKIE-Adresse lautet 0x19FF24.

Hier liest das Programm die BUF-Adresse, um sie an die GETS-Funktion zu übergeben und die BUF zu füllen. Wir können überprüfen, ob die Adresse mit der Anzeige der 0x19FED4-Konsole übereinstimmt.





Hier sehen wir, dass es EBP-54 ist. Wenn ich auf den Stapel doppelklicke, auf dem -54 angezeigt wird, wird angezeigt, dass die Adresse auf meinem Computer BUF = 0x19FED4 lautet.



Wenn ich nun die eingegebenen Daten unter dieser Adresse speichere, kann ich sie in einen Speicherauszug legen, um zu sehen, wie Bytes dort gespeichert werden.





Hier sind sie. Außerdem wird unten nichts angezeigt, da keine Daten vorhanden sind.

Wenn ich die GETS-Funktion mit F8 aufrufe, muss ich zur Konsole gehen, die EINGABETASTE eingeben und drücken, um den BUF-Puffer zu füllen und das COOKIE neu zu schreiben.



Wir sehen, dass die COOKIE-Variable auf meinem Computer bei 19FF24 war.

Hier vergleicht das Programm das Cookie mit 0x41424344.



Wir sehen, dass EBP-4 sagt, dass es sich zusätzlich zur Adresse um ein COOKIE handelt, wenn wir den HORIZON wie zuvor auf den EBP-Wert setzen.



Ich doppelklicke hier.

Wir sehen, dass EBP-4 ein COOKIE ist, da sich die Variable auf der Stapelebene -4 befindet und das HORIZON auf Null setzt.



Wir sehen, dass das Programm nicht springt und uns zeigt, dass Sie gewinnen!





Wir erreichen also manuell das Ziel, das Sie gewinnen! Sagt:

Wir haben STACK1 dynamisch mit X64DBG analysiert, einem Debugger, der es uns nicht ermöglicht, das Programm zu analysieren, ohne es zu starten. Dazu müssen wir andere Tools wie IDA PRO, GHIDRA oder RADARE verwenden.

Ich kann ein Skriptmodell für die Bedienung der Übung von PYTHON aus erstellen.

import sys
from subprocess import Popen, PIPE

payload = b"A" * 80 + b"\x44\x43\x42\x41"

p1 = Popen(r"C:\Users\ricardo\Desktop\abos y stack nuevos\STACK1_VS_2017.exe", stdin=PIPE)
print ("PID: %s" % hex(p1.pid))
print ("Enter para continuar")
p1.communicate(payload)
p1.wait()
input()

Im Fall von PYTHON 3 muss ich die PRINT-Funktion in Klammern setzen und vorsichtig sein, wenn ich Zeilen hinzufüge, die Bytes sein sollten (setze b vor die Zeilen in PYTHON 2).

Ich überprüfe, ob der Pfad korrekt ist und wann ich die Datei ausführe.



Gut. Wir haben bereits ein Skriptmodell für PYTHON 3 für den Betrieb von STACK1. Im nächsten Teil werden wir die statische Analyse in IDA, RADARE und GHIDRA fortsetzen.


Bitte beachten Sie, dass es neben der Aufgabe STACK1 auch die Versionen 2, 3 und 4 gibt. Sie können versuchen, diese zu lösen. Sie sind sehr einfach und STACK1 ähnlich, also langweilen Sie sich nicht.

Im nächsten Teil sehen wir IDA FREE, RADARE und GHIDRA.

Wir sehen uns im nächsten Teil 3.

Ricardo Narvaha
25/10/2019

PS # 1
Ein schönes PDF kann auf meiner Homepage heruntergeladen werden - yasha.su

PS # 2
Bald werde ich eine Fortsetzung des Artikels https://habr.com/en/post/464117/ darüber schreiben, wie ich Hilfe für Pater Chris Kaspersky gesammelt habe und was davon passiert.

Langweile dich nicht.

All Articles