it-swarm.com.de

Wenn der Stapel nach unten wächst, wie kann ein Pufferüberlauf den Inhalt über der Variablen überschreiben?

Mir ist klar, wie ein Pufferüberlauf funktioniert, aber ich habe ein Problem damit, die Richtung zu verstehen, in die der Überlauf geleitet wird. Wenn der Stapel also nach unten wächst , bedeutet dies, dass die Rücksprungadresse über liegt. ) den reservierten Speicherplatz der Variablen. Wenn diese Variable jetzt übergelaufen ist, sollte sie dann nicht den Speicher unter anstatt über überschreiben?

tl; dr: Wenn der Stapel nach unten wächst, wie kann ein Pufferüberlauf den Inhalt über der Variablen überschreiben?

(Address space layout

10
AdHominem

Wenn der Stapel nach unten wächst, erhalten Funktionen, die später aufgerufen werden, Stapelrahmen an niedrigeren Speicheradressen. Außerdem wird die Rücksprungadresse auf den Stapel verschoben, bevor Speicherplatz für lokale Variablen reserviert wird, sodass die Rücksprungadresse eine höhere Adresse als die lokalen Variablen erhält. Arrays und Puffer sind jedoch im Speicher immer noch nach oben indiziert, sodass beim Schreiben über das Ende des Arrays hinaus die nächste Absenderadresse auf dem Stapel angezeigt wird.

Beispiel mit obligatorischer ASCII art:

Betrachten Sie die triviale Funktion, die Eingaben von einer nicht vertrauenswürdigen Quelle entgegennimmt und in einen lokalen Puffer kopiert:

void foo(char *s)
{
    char buf[8];
    strcpy(buf, s);
    return;
}

Der Stapel sieht ein bisschen so aus:

   <---- stack grows to the left
    memory addresses increase to the right -->
  0x8000                        0x8010
  +--------+----------+---------++------------
  + buf[8] | ret addr | char *s ||   ....... 
  +--------+----------+---------++--------------
   <--------- foo() ----------->  <---- caller --

Der Stapel wird von rechts nach links gefüllt, beginnend mit den Funktionsargumenten, der Rücksprungadresse und den Einheimischen der Funktion. Es ist leicht zu erkennen, dass ein einfacher Überlauf von buf zu zunehmenden Adressen die Rücksprungadresse gut trifft.

Was ist, wenn der Stapel invertiert ist und nach oben wächst? Das Überlaufen eines Puffers läuft dann genauso wie der Stapel in Richtung des leeren Teils des Stapels.

Klingt gut, aber es hilft nicht, wenn foo() eine andere Funktion aufruft, um das Kopieren durchzuführen. Was nicht ungewöhnlich ist, das habe ich gerade mit dem strcpy gemacht. Jetzt sieht der Stapel folgendermaßen aus:

    stack grows to the right -->
    memory addresses increase to the right -->
            0x8000                          0x8010
------------++---------+----------+---------++-----------+-------------+
  ....      || char *s | ret addr | buf[8]  || ret addr  | locals  ... |
------------++---------+----------+---------++-----------+-------------+
 caller --->  <-------- foo() ------------->  <---- strcpy() ---------->

Wenn Sie den Puffer im Stapelrahmen von foo() (rechts) überlaufen, wird die Rücksprungadresse von strcpy() und nicht foo() ordnungsgemäß überschrieben . Es spielt jedoch keine Rolle, dass wir immer noch zu einem Ort springen, der durch die überfüllten, von Angreifern kontrollierten Daten festgelegt wurde.

8
ilkkachu

Der Stapel wächst nur nach unten, wenn ihm etwas zugewiesen ist. Andererseits bedeutet das Ablesen des Endes eines Arrays, im Speicher nach oben zu lesen.

Angenommen, der Stapelzeiger zeigt 0x1002 an. An dieser Adresse befindet sich ein Rückgabezeiger, den Sie interessieren. Wenn Sie beispielsweise ein 2-Byte-Array zuweisen, wächst der Stapel nach unten: Der Stapelzeiger wird auf 0x1000 geändert, und die Adresse des Arrays wird auf diesen neuen Wert gesetzt. Das Schreiben des ersten Bytes in das Array verwendet die Adresse 0x1000 und das Schreiben des zweiten Bytes die Adresse 0x1001. Das Abschreiben des Endes verwendet 0x1002 und überschreibt das Wesentliche.

Sie können dies in den von Ihnen geposteten Bildern sehen, da das graue Feld für Puffer B nach oben wächst, wenn es über seine Grenzen hinaus geschrieben wird.

4
Reid Rankin

Es gibt einen Unterschied zwischen Pufferüberlauf und Stapelüberlauf. Zugewiesene Puffer verwenden möglicherweise nicht den Stapel, sondern den Heap. Dies hängt davon ab, wie sie zugewiesen werden und was der Compiler besser/schneller/etc.

Ein Stapelüberlauf überschreibt tatsächlich den unten stehenden Speicher, der möglicherweise einem anderen (vorherigen) Aufruf oder letztendlich dem Heap zugewiesen wurde. Der Haufen wächst nach oben und irgendwann können sie kollidieren. Dies ist eine gute Zeit für das Betriebssystem, um einen Segmentierungsfehler auszulösen, das Programm anzuhalten oder Signale zu senden. Das Problem bei Stapelüberläufen besteht darin, dass sie mit anderen Threads aus demselben Prozess in Kontakt kommen können. Stellen Sie sich nun einen Thread vor, der über einen großen Puffer für das Senden/Empfangen von Netzwerken verfügt. Der Inhalt eines anderen Thread-Stapels kann diesen Speicherbereich überschreiben und Speicherlecks über das Netzwerk verursachen.

Pufferüberläufe sind häufiger und gehen über die Grenzen ihres eigenen Gedächtnisses hinaus. Solange sich der Speicher, auf den zugegriffen wird, noch im Heap-Bereich befindet, gibt es kein Problem (aus Sicht des Betriebssystems). Es wird gefährlicher, wenn der Heap zu klein ist und zusätzliche Daten zugewiesen werden müssen. Zu diesem Zeitpunkt ist nicht abzusehen, was passieren würde, wenn Grenzen erreicht werden. Wenn Sie versuchen, auf ein Byte jenseits der Heap-Grenze zuzugreifen, sollte das Betriebssystem den Prozess mit einem SIGSEGV beenden.

3
Yorick de Wid

Ein Stapel wächst durch Push-Anweisung nach unten, schreibt und liest jedoch nach oben. Wenn Ihr Stapel beispielsweise die Adresse 10 bis 5 belegt, was einer Länge von 6 verwendbaren Adressen entspricht, wird der Stapel bei Verwendung eines Push-Befehls heruntergefahren und ein zusätzlicher Speicher hinzugefügt, der dazu führt, dass Ihr Stapel die Adresse 10 bis 4 belegt 7 verwendbare Adressen. Wenn Sie jedoch in den Stapel schreiben und Ihr Anweisungszeiger auf 4 steht, schreibt er von 4 auf 10 und wenn Sie die Adresse 10 übergeben, tritt ein Pufferüberlauf auf

2
moski1122