# Suse 9.0 + Speicherreservierung für char(stack)



## Nebuchadnezar (14. August 2006)

HI ich hoffe ich bin hier im richtigen Forum da die Frage etwas Themenübergreifend ist.
Ich beschäftige mich gerade mit Pufferüberläufen (keine Sorge habe nichts böses damit vor) und in Linux ist mir bezüglich der Speicherreservierung für variablen eine Kuriosität aufgefallen. 
Normalerweise wird ein Pufferüberlauf bei char Variablen bzw. die Funktionen dafür ausgenutzt, wenn keine Grenzen überprüft werden. Chars werden immer um ein vielfaches von 32bit reserviert (leichter zum adressieren für die CPU).
also bsp:

```
char buf[18];
```
intern wird dieser Puffer auf 20 erweitert (5x4 = 20)
-> wenn ich in diesen Puffer 28 mal a hinschreibe, wird die Rücksprungadresse überschrieben (eip = 0x61616161)
(4 byte ebp + 4 byte eip)
So funktioniert das bei der X86 Architektur - steht in jedem Tutorial und ich konnte das in Windows einwandfrei beobachten.
Jedoch wird bei mir mit Suse 9.0 viel mehr Speicher reserviert
(im Gegensatz zu den Linux Tutorials bezüglich Pufferüberläufen)
selbes beispiel wie oben:
18 -> 0x12
0x12 wird auf 0x20 erweitert + 8 byte
-> 18 = 0x12 -> 0x20 -> 0x28 = 40 byte
oder:
33 = 0x21 -> 0x30 -> 0x38 = 56 byte

Daraus schloss ich: wenn ich in ein char buf[18] 48 mal
"a" reinschreibe wird der eip mit 0x61616161 überschrieben. Diese Schlussfolgerung war richtig, jedoch möchte ich gern wissen wieso das bei Suse 9.0/ 9.2 so ist. (Diese Beobachtung konnte ich im Internet mit keinem Tutorial bestätigen.)
Hat jemand ähnliche Erfahrungen gemacht?
Bye.


----------



## RedWing (15. August 2006)

Hallo,

gleich vorweg ich kenn mich mit der Thematik Pufferüberläufe nicht wirklich aus.
Aber das mit der Erweiterung zu einer 4 Byte Grenze erscheint mir irgendwie 
komisch:

Das Programm:


```
#include <stdio.h>

int main(){
    int a = 0;
    char test[18];
    int b = 0;
    printf("stack address of b\t=%p\n", &b);
    printf("stack address of test\t=%p\n", test);
    printf("stack address of a\t=%p\n", &a);
    return 0;
}
```

Ergibt folgende Ausgabe bei mir auf einem Debian System:

```
stack address of b      =0xbf8545ac
stack address of test   =0xbf8545b2
stack address of a      =0xbf8545c4
```

Sprich die Differenz zwischen der Addresse von a und der Addresse von test sind
genau 18 bytes und keine 20...

btw. meinst du mit dem Wort "ebp" den Framepointer?

//nocheinedit: Ein X86 Prozessor sollte doch sowohl Byte, Wort und Langwort- Adressierbarkeit 
beherrschen? Also auch aus dem Gesichtspunkt macht das mit der Auffüllung eines char- Arrays auf 
eine 4 Byte Grenze wenig Sinn, aber ich lass mich gerne eines besseren belehren 

Gruß,
RedWing


----------



## Nebuchadnezar (15. August 2006)

Seltsam ^^
Hab dein Programm probiert und ich komm auf genau die 20 Byte nur um meine Aussage noch weiter zu untermauern. 
Welchen Compiler/Os hast du benutzt? 
Das sollte Aufklärung geben
Puffer Überlauf
besonders dieser Absatz:


> bar()
> {
> char buf[10];
> 
> ...



ebp ist der Basepointer. Wenn mit Framepointer der Beginn des Stacks gemeint ist ja.

```
______________________
|_______%esp _________|
|_________int__________|
|________.....__________|
|________char_________|
|_______%ebp_________|
|_______%eip__________|
```
das wär da Stackframe bei deinem programm...so in etwa ohne die 2te integer

wenn die variabeln in einer unterfunktion reserviert werden würden(^^^)
zb 
Aufruf: func();
würde bei %eip die Adresse nach dem Aufruf von func() gespeichert werden.
wenn func() wieder verlassen wird erfolgt wenn das ganze disassembliert wird ein
ret Befehl. Dabei wird in den Eip die gespeicherte adresse zurückgeschrieben und die ausführung geht wieder im Hauptprogramm weiter.
Da würd dann der ganze Spass anfangen.
Du schreibst in nen 20byte Puffer ein paar instruktionen rein (Shellcode einschläußen) den rest füllst auf 24 byte mit "a" auf und in die nächsten 4 schreibst den beginn der Adresse deiner char Variable. Wenn das programm func() verlassen will springt der IP in die char Variable rein und führt den eingeschläußten Code aus
-> du bringst dem Programm Dinge bei die es gar nicht wusste :-D.


----------



## Dennis Wronka (15. August 2006)

RedWing hat gesagt.:
			
		

> Ergibt folgende Ausgabe bei mir auf einem Debian System:


Beim Compiler geh ich einfach mal vom gemeinhin ueblichen GCC aus.

Evtl. interessant koennten aber die Versionen von GlibC und GCC sein, und evtl. auch die Kernel-Version, aber ich denk der Kernel wird da eher weniger Einfluss haben.


----------



## Nebuchadnezar (15. August 2006)

Hmmm dann haben wir schon mal 3 Versionen :-(.
Bei Windows 2k/XP usw ist es immer gleich (auf 4 Byte auffüllen).
Bei meiner Suse 9.0 am Laptop und Suse 9.2 am Desktop pc (x86_64 long modus)
ist es, wie ich es oben erwähnt habe.
char buf[20] ->0x14 ->0x20 -> 0x28 = 40 byte reserviert.
Am Anfang dachte ich das liegt an meinem athlon64 (abgesehen davon dass ich das ganze mit einem 64 bit und einem 32 bit kernel getestet habe). Verwirrt war ich erst, als ich die selben Ergebnisse mit meinem Laptop (Intel Centrino 1.8Ghz) erziehlt habe.
Ich vermute mal du hast nicht zufällig noch eine andere Distro zum testen installiert.
Btw. probier mal ein Programm nur mit einem char buf[18] und näher dich ab 18 Byte byteweise an den eip ran, bis du einen coredump bekommst und im gdb steht dass der Eip (wenn du nur mit a auffüllst) mit 0x61616161 überschrieben wird mit einem Aufruf àla:

```
perl -e 'print "a" x 26' | ./test
```


----------



## Dennis Wronka (15. August 2006)

Ich hab LFS laufen, da koennte ich das ganze mal testen.

Nachtrag: Hab grad mal das Programm von RedWing kompiliert und getestet.
Das kommt dabei raus:


> stack address of b      =0xbfe9a918
> stack address of test   =0xbfe9a91e
> stack address of a      =0xbfe9a930


Bei mir sind es demnach auch 18 Bytes.
Kernel 2.6.17.7 (das System baut aber auf 2.6.16 auf), GlibC 2.3.6, GCC 4.1.0


----------



## Nebuchadnezar (15. August 2006)

Seltsam. Liegt entweder an einer alten Compiler version die ich habe.
Oder....Shice Suse 9.x.
Wie gesagt, mich würd bei euch auch der abstand zwischen char buf[18] und ebp interessieren.

Wenn das wirklich so ist, müsste bei dir der Speicherbereich zwischen buf und ebp
in etwa so aussehen.

```
0x61616161 0x61616161 0x61616161 0x61616161 0x61618980 (ebp) 0x00000408
```
und das würd mir eigentlich komisch vorkommen 

im Windows ist das immer so in etwa


```
0x61616161 0x61616161 0x61616161 0x61616161 0x00006161    0x89800408(ebp)
```


----------



## Dennis Wronka (15. August 2006)

Wie genau komm ich da ran? Kann mir valgrind vielleicht dabei helfen? Oder cachegrind?


----------



## RedWing (15. August 2006)

Hallo,



			
				Nebuchadnezar hat gesagt.:
			
		

> Wenn mit Framepointer der Beginn des Stacks gemeint ist ja.



Ja mit dem Framepointer mein ich den Zeiger auf den Anfang des aktuellen 
Stackframe:
Ich kann leider nur von der M68K Architektur sprechen, denke aber das das bei 
einem x86 der selbe Mechansimus sein sollte:
Beim M86K steht der Stackpointer immer im Adressregister A7 und der 
Framepointer im Adressregister (man kann für den Framepointer auch ein
beliebiges andres Register wählen, aber Konvention is da glaube A6) A6:

Stack:

```
niedere Adressen    A7 ->   funktionslokale Parameter
                    A6 ->   A6.alt
                            Rücksprungadresse
                            aktuelle Parameter
                            ...
obere Adressen              A6.alt
```

Wenn der Stack dann wieder abgebaut wird, geschieht im westl. folgendes:

1.) Der Stackpointer A7 wird mit dem Wert des Framepointers A6 geladen (lokaler 
Speicherplatz wird freigegeben)
2.) Der Framepointer A6 wird mit dem Wert des alten Framepointers der sich nun 
hinter dem Wert der Adresse A7 versteckt  restauriert und A7 wird um 4 erhöht
4.) Der Programcounter wird mit dem Wert der sich jetzt hinter A7 versteckt 
geladen (Rücksprung)

Ich denke aber das dieser Mechanismus vom Compiler abhängig sein kann.



			
				Dennis hat gesagt.:
			
		

> Evtl. interessant koennten aber die Versionen von GlibC und GCC sein,




```
redwing@euklid:~ $ gcc --version
gcc (GCC) 4.0.4 20060730 (prerelease) (Debian 4.0.3-6)
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
```

Ich denke die Version der glibc ist hier unintressant.
Und wie gesagt das Programm läuft bei mir auf einem x86...

Ich werds gleich mal noch unter Windows ausprobieren....
@Nebuchadnezar Was hast du denn für einen Compiler verwendet?
Eventuell liegts aber auch an der 64 Bit Architektur... 

Gruß,
RedWing


----------



## Nebuchadnezar (15. August 2006)

Das müsst eigentlich stimmen was du sagst.
Wenn du ein C Programm oder was auch immer disassemblierst schaut das so aus:
mov esp, ebp (glaub intel syntax - von rechts nach links)

```
push ebp
mov ebp,esp (intel syntax glaub ich ... von rechts nach links)
...

pop ebp
mov esp,ebp
```


----------



## RedWing (15. August 2006)

Hallo,

also unter Windows ist die Differenz bei mir für einen 18 Byte großen Puffer
44 Byte  Ich weiß nicht wieso:

```
stack address of b  =0x22eedc
stack address of test   =0x22eee0
stack address of a  =0x22ef0c

gcc (GCC) 3.4.4 (cygming special) (gdc 0.12, using dmd 0.125)
Copyright (C) 2004 Free Software Foundation, Inc.
Dies ist freie Software; die Kopierbedingungen stehen in den Quellen. Es
gibt KEINE Garantie; auch nicht f"ur VERKAUFBARKEIT oder F"UR SPEZIELLE ZWECKE.

CYGWIN_NT-5.1 euklid 1.5.18(0.132/4/2) 2005-07-02 20:30 i686 unknown unknown Cygwin
```

Weiterhin hab ich mal folgendes Programm unter Linux gedebugt:

```
#include <stdio.h>
#include <string.h>

int foo(){
    char test[18];
    strcpy(test, "aaaaaaaaaaaaaaaaaaaaaaaaaa");
    return 0;

}
int main(){
    foo();
    return 0;
}
```
Also das sollten 26 a's sein. Sprich 18 a's für den Puffer. 4 a's für den 
Framepointer, und mit den letzten 4a's wird der eip überschrieben.

Folgendes kam dabei dann raus:

```
(gdb) r
Starting program: /home/redwing/a.out 

Program received signal SIGSEGV, Segmentation fault.
0x61616161 in  ()
(gdb) info frame 
Stack level 0, frame at 0xbfad770c:
 eip = 0x61616161; saved eip 0x61616161
 called by frame at 0xbfad7710
 Arglist at 0xbfad7704, args: 
 Locals at 0xbfad7704, Previous frame's sp is 0xbfad770c
 Saved registers:
  eip at 0xbfad7708
(gdb) q
The program is running.  Exit anyway? (y or n) y
```
Eventuell versuchst du das auch mal bei dir, würd mich intressieren was bei rumkommt.

Das mit diesen 44 Bytes unter Windows hab ich keine Erklärung dafür 

Gruß,
RedWing


----------



## RedWing (15. August 2006)

Nebuchadnezar hat gesagt.:
			
		

> Das müsst eigentlich stimmen was du sagst.
> Wenn du ein C Programm oder was auch immer disassemblierst schaut das so aus:
> mov esp, ebp (glaub intel syntax - von rechts nach links)
> 
> ...



ja ich denk auch.
Unter dem M68K gabs dafür die Instruktion "link A6, n" (wobei n = Anzahl der Bytes,
welche die funktionslokalen Parameter belegen, ist) beim Eintritt in ein 
Unterprogramm.
Sprich so werden die Register dann gesichert:

```
SP <- SP - 4 (um den Stackpointer eins weiterzusetzen)
(SP) <- A6 (A6 wird in den Inhalt von SP(StackPointer) gerettet)
A6 <- SP (In A6 wird der aktuelle Stackframe gemerkt)
SP <- SP + n (Der StackPointer wird um die Anzahl der Bytes der funktionslokalen 
Variablen nach oben gesetzt)
```

und immer wenn in einer Hochsprache, wie C ein 
return kommt wird das dann durch ein

```
unlnk A6
rts
```

ersetzt, wobei unlnk genau die oben beschriebenen Schritte wieder "rückgängig"
macht.

Gruß,
RedWing


----------



## RedWing (15. August 2006)

Dennis hat gesagt.:
			
		

> Wie genau komm ich da ran? Kann mir valgrind vielleicht dabei helfen? Oder cachegrind?


Also valgrind nicht aber du kannst dir den Frame im gdb anschauen, siehe 
vorvorletzer Post...

Gruß,
RedWing


----------



## Nebuchadnezar (15. August 2006)

@Redwing 
Danke, du hast endlich in Linux das probiert was ich die ganze Zeit wissen wollt.
Komisch dass bei dir ned auf 20 erweitert wird. Zumindest reserviert dir das prog bei 18 wenigstens ned 40 (18 -> 0x12 -> 0x20 ->0x28) = 40 byte, was mir völlig unerklärlich ist.
Das bei dir 44 reserviert werden ist komisch aber das liegt vielleicht an der windows version vom gcc die ich übrigens nirgends gefunden hab oder hast cygwin verwendet?.
...ups das is mingw
naja wie ma sieht schaut die theorie anders als die Praxis aus..
gut zugegeben es steht auch in manchen quellen bei shellcode sollte man vorher ein paar nops einschläußen dann hat ma ned so das problem den puffer zu erwischen. Aber dass in Linux nirgends so wie es logisch ist auf 20 erweitert wird wundert mich(naja ist anscheinend compilersache und jeder compiler optimiert anders bezüglich adressierung)
Für den Fall dass du Visual Studio/Borland CBuilder/borland c++ installiert/griffbereit hast,
könntest es damit testen.


----------



## RedWing (15. August 2006)

> Das bei dir 44 reserviert werden ist komisch aber das liegt vielleicht an der windows version vom gcc die ich übrigens nirgends gefunden hab oder hast cygwin verwendet?.
> ...ups das is mingw



Naja ich hab bei mir cygwin installiert und AFAIK hat cygwin den mingw 
Compiler mit an Board...



> naja wie ma sieht schaut die theorie anders als die Praxis aus..



Traurig aber wahr 



> Für den Fall dass du Visual Studio/Borland CBuilder/borland c++ installiert/griffbereit hast,könntest es damit testen.



Da muss ich leider passen...

Gruß,
RedWing


----------

