Ich habe mich auf einen Programmierwettbewerb vorbereitet und bin im Internet auf diese Frage gestoßen:
%Vor%Die Lösung besteht darin, Code zu schreiben, der "hi there" anstelle von "there hi" ausgibt (es dürfen keine zusätzlichen Druckfunktionen verwendet werden und Code wird nur in den Kommentarblock geschrieben).
Da ich eine grundlegende Assembler-Programmierung durchgeführt habe, wurde mir klar, dass dies mit der Stack-Manipulation unter Verwendung der Ganzzahl x
als Basis geschehen könnte.
Ich habe versucht, mit gdb die Rückgabeadresse der Funktionen zu finden und habe dann die Rückgabeadressen von a
und b
getauscht. Der Compiler löst einen Segmentierungsfehler aus, und daher nehme ich an, dass ich nicht die richtige Absenderadresse verwendet habe.
Wie berechne ich den Offset, um die Absenderadresse zu finden? Der Infoframe-Befehl auf gdb war nicht hilfreich, da die dort angegebene Stack-Adresse nicht funktionierte.
Ich führe dies auf Linux mit gcc.
Ich bin mir nicht sicher, ob das Folgende zählt. Es ist tragbar auf POSIX. Im Grunde genommen ändern Sie den Puffer von printf vor dem ersten Aufruf und manipulieren diesen, bevor er zum Terminal
geleert wird %Vor% Sie erhalten Warnungen, die Bibliotheksfunktionen memcpy
und exit
implizit zu deklarieren. Es ist legal auf C89, obwohl entmutigt. Aber in deinem Fall ist kein Trick zu schmutzig, denke ich. Sie können das Memcpy vermeiden, indem Sie die Zeichen manuell kopieren. Sie können exit
vermeiden, indem Sie stattdessen stdout
bis freopen
umleiten. Sie können BUFSIZ
in große Konstanten ändern, wenn das System eine merkwürdig kleine Puffergröße (kleiner als 9) aufweist. Es gibt Varianten dieser Lösung, bei denen Sie das \n
nicht manuell einfügen müssen und stattdessen das Programm normal von main beenden und das printf("\n")
für das Zeilenende verwenden muss
Dieses Problem kann nur gelöst werden, wenn Sie den Stack auf die gleiche Weise zerstören, wie ein Angreifer den Stapel eines Prozesses zerstört.
Und um den Stack zu versminen, kann man nur dann machen, wenn man jedes Detail der Implementierung des Compilers kennt, das Problem ist sonst unlösbar.
Wenn Sie die Details der Kompilierung (insbesondere die Stack-Struktur) kennen, können Sie die Adresse der lokalen x-Variablen verwenden, um die Adressen des aktuellen Frames aus dem Stack (von FRAME_C) zu erhalten; In jedem Frame ist der Basiszeiger des vorherigen Frames und ändern Sie es.
Der Stapel sieht so aus:
%Vor% Mit &x
können wir die Position von FRAME_C ermitteln.
Eine Lösung ist
return
Die knifflige Operation ist 2. Wenn jedoch jeder Frame eine bekannte Größe hat, können wir den Rückgabezeiger RET_A des Frames B modifizieren und RET_MAIN so etwas erkennen:
%Vor%Wie Sie sehen können, müssen Sie viele Details über die Implementierung des Compilers wissen, also ist dies keine tragbare Lösung.
Eine andere Lösung wäre, "hi, there" zu drucken und stdout
auf /dev/null
umzuleiten. Ich nehme an, dass exit () oder andere Compiler-abhängige Tricks nicht erlaubt sind, sonst hat das Problem keine Bedeutung für einen Wettbewerb.
meine Lösung ist für x86 / x64 und für CL
Compiler, aber denke für gcc gibt es auch.
question only - existieren für die Funktion äquivalent:
void ** _AddressOfReturnAddress();
und sind äquivalent für __declspec(noinline)
- damit der Compiler keine bestimmte Funktion inline ausführt
lassen
void* pb
- ist die Adresse in void b()
direkt nach c();
und
void* pa
ist die Adresse in void a()
direkt nach b();
weil a
und b
fast gleich sind - das können wir annehmen
(ULONG_PTR)pa - (ULONG_PTR)&a == (ULONG_PTR)pb - (ULONG_PTR)&b;
und natürlich das Stacklayout in a
und b
müssen identisch sein. darauf basierend und Lösung.
Nächster Code getestet / mit CL
Compiler gearbeitet - sowohl auf x86 / x64 (Windows) als auch mit /Ox (Full Optimization)
und mit /Od (Disable (Debug))
- alles hat funktioniert.
Tags und Links c