Eingelocht

Seite 2: Hochstapler

Inhaltsverzeichnis

Folgendes Listing zeigt ein einfaches Beispielprogramm in C, Auszüge aus dem zugehörigen Assembler-Code und den Stack-Inhalt während der Ausführung der Funktion.

void function(int a, int b, int c) {
char buffer1[8];
char buffer2[16];
...
}


void main() {
function(1,2,3);
}

Assembler-Code
(Auszug aus "gcc -S -o example1.s example1.c")

function:
pushl %ebp # sichert EBP
movl %esp,%ebp # kopiert ESP nach EBP
subl $24,%esp # schafft Platz f. buffer1+2
movl %ebp,%esp # korrigiert EBP
...
popl %ebp
ret

main:
pushl %ebp
movl %esp,%ebp
pushl $3 # Parameter auf den Stack
pushl $2
pushl $1
call function # Funktionsaufruf
addl $12,%esp # Stack aufräumen

So liegen die Variablen auf dem Stack

Auf dem Stack befinden sich unter anderem die lokalen Puffer buffer1 und buffer2 und die gespeicherte Rücksprungadresse. Kopiert man in der Funktion mit strcpy (buffer1, buffer2) den Inhalt des größeren, zweiten Puffers in den ersten, überschreibt diese Operation auch diese Rücksprungadresse. Der abschließende Assemblerbefehl ret holt diesen quasi zufälligen Wert vom Stack und schreibt ihn in den Instruction Pointer. Im nächsten Arbeitsschritt versucht der Prozessor, von dieser Adresse den nächsten Befehl zu laden -- was in der Regel fehlschlägt und eine Speicherschutzverletzung erzeugt.
Dass das nicht immer so sein muss, demonstriert das folgendes Beispiellisting:

void function(int a, int b, int c) {
char buffer1[8];
char buffer2[16];
int *ret;
ret = buffer1 + 12;
(*ret) += 8;
}

void main() {
int x;

x = 0;
function(1,2,3);
x = 1;
printf("%d\n",x);
}

Hier erhöht das Programm den Wert der Rücksprungadresse um 8 -- mit dem Resultat, dass es direkt den printf-Aufruf anspringt. Der Befehl x=1 kommt nicht zur Ausführung. Probieren Sie es aus: Das Programm gibt "0" aus. (Anmerkung: Je nach verwendetem Compiler muss der Wert in (*ret) += 8 variiert werden. Nicht jeder Compiler erzeugt den gleichen Assemblercode!)