002
10.09.2004, 16:26 Uhr
virtual
Sexiest Bit alive (Operator)
|
Ein knkretes Beispiel kann ich Dir nicht geben, lediglich einen Eindruck. Zu diesem Zweck mal folgendes Progrämmchen:
C++: |
#include <stdio.h>
void test() { char a[4] = { 0, 0, 0, 0 }; char b[4] = { 0, 0, 0, 0 }; char c[4] = { 0, 0, 0, 0 }; int i;
memcpy(b, "hello", 5); // Denk dir hier ein printf
printf("a (%p):", a); for(i=0; i<4; ++i) printf("%d ", a[i]); puts(""); printf("b (%p):", b); for(i=0; i<4; ++i) printf("%d ", b[i]); puts(""); printf("c (%p):", c); for(i=0; i<4; ++i) printf("%d ", c[i]); puts(""); }
int main() { test(); }
|
Zum Ablauf: test enthält drei 4 byte große Arrays, welche Anfangs alle auf 0 gesetzt werden. Dann gibt es eine memcpy Anweisung, die in den Speicher von b schreibt, leider aber auch ein wenig darüber. Das könnte auch ein printf, oder ein gets gewesen sein, also eben irgendwas, was den Speicher von b verändern wird.
Das Programm ist noch recht sicher, aber wenn Du es laufen läßt, solltest Du sehen, daß eben auch das letzte Byte von a in Mitleidenschaft gezogen wurde, weil man über die Puffergrenzen geschrieben hat. Bishier ist das so harmlos wie jede Art von Pufferüberlauf: es führt zum Absturz oder eben nicht (in diesem Fall nicht, weil ich es so wollte).
Was die Ausgabe aber auch zeigt ist, daß a eine größere Speicheradresse als b hat. Das ist von ziemlicher bedeutung, weil bei den meisten Archtikturen von großen nach kleinen Speicheraddressen wächst. Dh wenn Du in einer Funktion bist und der Stackframe bei X beginnt, dann wird dieser der nächste Stackframe auf einer kleineren Speicheraddresse abgelegt. So ein Stackframe enthät in der Regel auch sowas wie eine Rücksprungadresse, Sicherungskopien wichtiger Register usw. Rücksprungaddressen dienen dazu, daß sich der Computer merken kann, wo es weiter geht, wenn die Funktion verlassen wird.
Wenn ich nun im obigen Beispiel also eine gewisse anzahl von Bytes reinschreibe, habe ich ganz gute karten: 1. Jede lokale Variable zu verändern, die Stack eine höhere Addresse hat, daß der eigentliche Zielbuffer 2. Die sicherungkopien einiger wichtiger Register 3. Die Rücksprungadresse.
Da es den I/O Routinen ziemlich egal ist, was man so eingibt, kann man natürlich nun auf die Idee kommen, nicht nur druckbare Zeicheneinzugeben, sondern eben auch Bytefolgen, die eben irgendwie Sinn machen, aber letztlich mit dazu führen können, daß man og angesprochenen Dinge so überschreibt, daß das Programm nicht abstürzt, wohl aber etwas anderes tut, als man eigentlich wollte:
Ich mache sowas nicht, von daher kann ich Dir auch nicht sagen, in wie weit es Tools gibt, die diese Sache stark vereinfachen, aber ich denke, daß das Prinzip schon so ist. -- Gruß, virtual Quote of the Month Ich eß' nur was ein Gesicht hat (Creme 21) |