Zum Inhalt wechseln

Als Gast hast du nur eingeschränkten Zugriff!


Anmelden 

Benutzerkonto erstellen

Du bist nicht angemeldet und hast somit nur einen sehr eingeschränkten Zugriff auf die Features unserer Community.
Um vollen Zugriff zu erlangen musst du dir einen Account erstellen. Der Vorgang sollte nicht länger als 1 Minute dauern.

  • Antworte auf Themen oder erstelle deine eigenen.
  • Schalte dir alle Downloads mit Highspeed & ohne Wartezeit frei.
  • Erhalte Zugriff auf alle Bereiche und entdecke interessante Inhalte.
  • Tausche dich mich anderen Usern in der Shoutbox oder via PN aus.
 

   

Foto

[ASM x86 | 01] Klassische Stack-Buffer-Overflows: Der Stack

- - - - -

  • Bitte melde dich an um zu Antworten
Eine Antwort in diesem Thema

#1
Phalanx

Phalanx

    Leecher

  • Members
  • PIP
  • Likes
    12
  • 1 Beiträge
  • 3 Bedankt
  • Android
  • Linux

*
BELIEBT


 

Im folgenden möchte ich den ersten Teil meines hoffentlich mehrteiligen Tutorials darstellen:
Den Stack.
Ich selber benutze primär Linux, habe jedoch zu Testzwecken immer mehrere Win-VMs. Ich benutze für dieses einfache
Tutorial gdb als Debugger, und würde bei komplizierteren Sachen zu Immunity wechseln, damit der gedoofte Win-User auch etwas praktisch ausprobieren kann.

 

ANFORDERUNGEN:
    Basics von x86 Assembly in Intel-Syntax verstehen
    Eigentlich enthält vorige Anforderung dies: Grundlagen der Prozess- und Speicherorganisation
    Syntax von C sollte bekannt sein

 

DER STACK:
Der Stack ist ein Segment innerhalb des Speichers, welcher zur Aufbewahrung lokaler, nicht-statischer Variablen (und Argumente) dient.
Er funktioniert nach dem LIFO-Prinzip(Last-In-First-Out):
    -Mithilfe der PUSH-Instruktion wird ein DWORD (= 4 bytes) auf dem Stack abgelegt (ESP - 4)
    -Mithilfe der POP-Instruktion wird ein DWORD aus dem Stack in ein Register, oder ein Speicherbereich kopiert (ESP + 4)
    -Es wird immer der zuletzt gepushte Wert mittels POP entnommen

Der Stack ist unterteilt in sogenannte Stack-Frames, wobei jeder Stack-Frame für eine Funktion innerhalb eines C(++)-Programms zuständig ist. Das EBP-Register (Extended Base Pointer) zeigt dabei auf den Anfang des momentanen Stack-Frames (wird deswegen auch Frame Pointer genannt), und das ESP-Register zeigt auf das Ende des Stack-Frames (Extended Stack Pointer). Eine Besonderheit des Stacks ist seine Ausrichtung:
Mittels eines PUSH wird ESP um ein DWORD verringert, nicht erhöht! Umgekehrt wird mittels einer POP-Instruktion, ESP um ein DWORD erhöht!
Dies liegt daran, dass der Anfang des Stacks (also der erste Stack-Frame, in C der Frame der main()-Funktion) auf einer hohen Speicheradresse im  Prozesspeicher beginnt, und alle nachfolgenden gepushten Werte bzw. Stack-Frames sich in niedrigeren Adressen befinden.
Der Stack wächst also von oben nach unten!
Warum der Stack so aufgebaut ist, wie er es ist, wird dir im Abschnitt Funktionsepilog klar.

Please Login HERE or Register HERE to see this link!

Folgender Beispielcode:

void funcA(int arg1, int arg2) {
    int local = 2;
}
int main() {
    funcA(1, 2);
    return 0;
}

Kompilat:

   # Function main()
   0x080483da <+0>:    push   ebp    # FUNKTIONS-
   0x080483db <+1>:    mov    ebp,esp# PROLOG
   0x080483dd <+3>:    push   0x2
   0x080483df <+5>:    push   0x1
   0x080483e1 <+7>:    call   0x80483cb <funcA>
   0x080483e6 <+12>:   add    esp,0x8
   0x080483e9 <+15>:   mov    eax,0x0
   0x080483ee <+20>:   leave  # FUNKTIONS-
   0x080483ef <+21>:   ret    # EPILOG
   # Function funcA(..)
   0x080483cb <+0>:    push   ebp    # FUNKTIONS-
   0x080483cc <+1>:    mov    ebp,esp# PROLOG
   0x080483ce <+3>:    sub    esp,0x10
   0x080483d1 <+6>:    mov    DWORD PTR [ebp-0x4],0x2
   0x080483d8 <+13>:   leave    # FUNKTIONS-
   0x080483d9 <+14>:   ret      # EPILOG

Der Funktionsprolog:

push ebp
mov ebp, esp

Das ist der sogenannte Funktionsprolog und der ist bei allen C-Funktionen gleich. Zuerst wird der alte Frame-Pointer mittels push ebp auf dem Stack abgelegt:

Please Login HERE or Register HERE to see this link!


und anschließend wird das EBP-Register mit dem ESP-Register überschrieben. Das ESP-Register zeigt momentan auf den zuvor gepushten Wert, den alten Frame-Pointer (Saved Frame Pointer, SFP). Der aktuelle Frame-Pointer (das EBP-Register) zeigt nun, genauso wie ESP auf diese Speicheradresse im RAM, die sich im Stack befindet und den SFP beinhaltet. Ein neuer Stack-Frame beginnt.

Please Login HERE or Register HERE to see this link!

 

Funktionsoperationen/Funktionsaufruf von funcA(...):

0x080483dd <+3>:    push   0x2
0x080483df <+5>:    push   0x1
0x080483e1 <+7>:    call   0x80483cb <funcA>

Anschließend werden die Hex-Werte 0x2 und 0x1 (dezimal: 2*16⁰=2*1=2, 1*16⁰=1*1=1) auf den Stack gepusht.
Und zufällig sind das genau die Werte, die wir der Function funcA(1,2) als Parameter übergeben haben, in umgekehrter Reihenfolge!

Please Login HERE or Register HERE to see this link!


Danach wird die Funktion funcA(...) mittels call <Addresse von funcA(...)> aufgerufen.
Die Call-Instruktion spielt beim Exploiting von Buffer-Oveflows eine große Rolle:
Es wird nämlich nicht *nur* die Adresse, an der sich die Assembly-Instruktionen der funcA(...) im .text-Segment befindet, angesprungen (EIP zeigt drauf). Nein, es wird zusätzlich die nächste Instruktion von main() auf dem Stack gepusht, quasi als Rücksprungadresse, wenn von funcA(...) zurück in die main()-Funktion verzweigt wird. Call ist folgendem äquivalent:


push 0x080483e6 # Adresse der Instruktion, die auf call folgt (push EIP+2, da eine Instruktion i.d.R. 2 bytes einnimmt)
JMP <Addresse von funcA(...)> # funcA(...) aufrufen

Please Login HERE or Register HERE to see this link!

 

Funktionsepilog von funcA(...):


0x080483ce <+3>:    sub    esp,0x10
0x080483d1 <+6>:    mov    DWORD PTR [ebp-0x4],0x2
0x080483d8 <+13>:   leave    # FUNKTIONS-
0x080483d9 <+14>:   ret      # EPILOG

Wir sind nun in funcA(...), der Prolog wurde schon durchgeführt und die Funktionsoperationen dieser Funktions sind relativ unspannend, denn der Stack wird bei <+3> um 16 bytes vergrößert, und anschließend wird bei <+6> der Wert 2, der lokalen Variable local auf dem Stack geschrieben (nicht direkt gepusht, wie man sieht). Das einzig nennenswerte hierbei ist die Tatsache, dass die ebp-relative Adressierung deutlich wird. Denn die 2 wird in eine Adresse, die als Offset zum ebp dargestellt wird geschrieben (in die Adresse 4 bytes "nach" dem EBP, da Variablen etc... von niederen zu hohen Speicher ausgedehnt sind).

Please Login HERE or Register HERE to see this link!


Die interessanten Instruktionen sind die leave- und die RET-Instruktion, die den sogenannten Funktionsepilog darstellen, also das Ende einer Funktion einleiten, und den Zustand der vorigen "Oberfunktion" einleiten (hier: main()).
Die leave-Instruktion ist generell äquivalent zu folgendem Ausdruck:

mov esp, ebp
pop ebp

Zuerst wird der Wert des EBP, die Adresse des SFP (dem Frame-Pointer von main()), in ESP kopiert. Der Stack-Frame ist also praktisch nicht mehr existent:

Please Login HERE or Register HERE to see this link!


Beide zeigen nun auf den SFP von main(), und mittels pop ebp wird genau dieser SFP nun zum aktuellen Frame-Pointer, da dieser in EBP geladen wurde.
Der Stack sieht momentan wiefolgt aus:

Please Login HERE or Register HERE to see this link!


Nun findet die RET-Instruktion statt, die folgendem Befehl gleicht:

pop eip

Da der SFP schon gepoppt wurde, und somit ESP um ein DWORD erhöht wurde, zeigt der ESP nun auf den RET, die Return-Address der Instruktion innerhalb von main(), die nach dem call <funcA(...)> ist. Dieser Wert wird nun in den EIP geladen, und die Funktion funcA(...) wurde vollständig abgearbeitet, und es wurde zur Funktion main() gewechselt, samt Frame-Pointer von main():

Please Login HERE or Register HERE to see this link!

 

 

Der entscheidene Punkt hierbei ist, dass durch die RET-Instruktion, der Inhalt, der sich an diesen Zeitpunkt am ESP befindet, in den EIP geladen wurde. Der EIP enthält ja bekannterweise die Adresse der auszuführenden Instruktion. So steuert man den Programmfluss, und so könnte man auch die Ausführung willkürlichen, schädlichen Codes herbeiführen. Dazu aber im nächsten Tutorial mehr.

Im hoffentlich nächsten Teil wird der Programmfluss eines einfachen Programms gesteuert. Bei Fehlern, konstruktiver Krititk/Tipps, nicht zögern, und per PN oder hier im Thread schreiben. Die Bilder musste ich leider als Thumbs einfügen, Vollbild ist anscheinend zu groß.


  • Ch!ller, Cube, Ar@m!s und 9 anderen gefällt das

Thanked by 3 Members:
freshman666 , Cube , lNobodyl

#2
pdr0

pdr0

    Pentester

  • Premium Member
  • Likes
    86
  • 148 Beiträge
  • 87 Bedankt

Ich weiß, es gibt den like button aber ich will trotzdem sagen, mach weiter so! Sowas bringt Leute weiter, nicht die 1000te Nachfrage was der beste Bot oder der beste Stealer ist.


  • Cube gefällt das

Thanked by 1 Member:
Phalanx


  Thema Forum Themenstarter Statistik Letzter Beitrag

Besucher die dieses Thema lesen:

Mitglieder: , Gäste: , unsichtbare Mitglieder:


This topic has been visited by 37 user(s)


    , Ar@m!s, Benassi, bumg2, byte, casamonica, Ch!ller, ChEeTaH182, Commentor, Crap, Cube, debug_23, dos, fAYe, Framerater, Goooofy, hacked, Huseijnx3, kiwitone, lNobodyl, lolwut, MDDD, muLTiii, pdr0, Phalanx, R3s1stanc3, Slixer, smc2014, sniffer, superuser123, Take1T, Toskom4n, vital, will, xVirtu, xxxsmackxxx, ZeroFreez
Die besten Hacking Tools zum downloaden : Released, Leaked, Cracked. Größte deutschsprachige Hacker Sammlung.