Μετάβαση στο περιεχόμενο
  • ChatBox

    ChatBox

    Chatroom Rules

    • Το GreekHacking.Gr είναι ένα ελληνικό forum και επιτρέπεται μόνο η χρήση της Ελληνική Γλώσσας (ούτε greeklish).
    • Δεν επιτρέπονται οι βρισιές και γενικά η χρήση χυδαίας γλώσσας.
    • Απαγορεύεται αυστηρά το πορνογραφικό, προσβλητικό και βίαιο περιεχόμενο.
    • Μην χρησιμοποιείτε κεφαλαία γράμματα η σύμβολα.
Clipper

Τα πρώτα βήματα πριν την κατανόηση του Buffer Overflow

Recommended Posts

Clipper

Πως λειτουργεί ένα πρόγραμμα ή τα πρώτα βήματα πριν την κατανόηση του Buffer Overflow.

Το συγκεκριμένο post είναι αναδημοσίευση στα Ελληνικά ενός άρθρου που είχα γράψει παλιά στο Hellbound Hackers καθώς και σε άλλα forums (αλλά στα αγγλικά).

Δεν πρόκειται για ένα buffer overflow exploit, αλλά για το απαιτούμενο υπόβαθρο που θα βοηθήσει κάποιον να καταλάβει κάποια σύνθετα ζητήματα, όπως το buffer overflow. Θα προυσιαστεί, με παραδείγματα, πως επικοινωνούν η CPU, η μνήμη κατά την εκτέλεση ενός προγράμματος.

Έχω διαβάσει πολλά άρθρα για το περίφημο 'buffer overflow'. Τα περισσότερα ξεκινούν από ένα σημείο το οποίο απαιτεί γνώσεις που δεν τις έχουν όλοι. Αυτό το tutorial προσπαθεί να καλύψει το έλλειμα αυτό και να οδηγήσει κάποιον (ελπίζω) πιο εύκολα στην κατανόηση της βασικής αυτής αδυναμίας.

Αν στο τέλος του άρθρου αυτού θα αισθάνεστε πιο... «άνετα» με μερικές έννοιες όπως CALL, RETN καθώς και πως καλείται μια function() στην μνήμη (buffer, stack, κλπ) θα αισθάνομαι οτι το αρθράκι πέτυχε το στόχο του.

Πρώτα απ’ όλα θα ήθελα να σημειώσω πως οτιδήποτε θα πω θα αναφέρομαι για την οικογένεια επεξεργαστών xx86. Επίσης οι διευθύνεις μνήμεις θα αναφέρονται με το δεκαδικό σύστημα για λόγους ευκολίας αυτών που δεν έχουν συνηθίσει το δεκαεξαδικό σύστημα που συνήθως χρησιμοποιείται.

Απαιτήσεις για να διαβάσει κάποιος το άρθρο:
1.Στοιχειώδεις γνώσεις assembly.
2.Στοιχειώδεις γνώσεις C.
3.Να ξέρει τι σημαίνει υπολογιστής.
4.Να γνωρίζει να διαβάζει Ελληνικά.
5.Μα τίποτα από τα παραπάνω!! Απλά ανοιχτό μυαλό, φαντασία και... όρεξη... και Google Translate! :)

 

Πάμε λοιπόν:
Για κάθε πρόγραμμα που καλείται στη μνήμη για να εκτελεστεί, το λειτουργικό σύστημα του δημιουργεί 3 βασικά μέρη (Segments):
-Code Segment
-Data Segment (το γνωστό BSS)
-Stack Segment

CODE SEGMENT
Σ’ αυτό το κομμάτι μνήμης υπάρχουν όλες οι εντολές του προγράμματος. Κανένας (κανένας?... τεσπα σχεδόν κανένας) δεν μπορεί να γράψει σ’ αυτό το κομμάτι της μνήμης. Ονομάζεται μνήμη μόνο για διάβασμα (ReadOnlyΜνήμη Segment) – Μην την μπερδεύετε με τη ROM (ReadOnlyΜνήμη) του υπολογιστή που είναι τελείως διαφορετικό πράγμα.

Για παράδειγμα
Όλες οι εντολές assembly (εδω σε κώδικα C) θα τοποθετηθούν στο Code Segment.


 

/* Ολες οι τιμές της 1ης διαγωνίου του πίνακα να γινουν 1, και οι υπόλοιπες άλλα 0 */
  for (i = 0; i < 100; i++)
      for (j = 0; j < 100; j++)
           if (i<>j)
               a[i][j] = 0
           else
               a[i][j] = 1;

Τα σχόλια φυσικά δεν πρόκειται να μεταφερθουν στο Code Segment γιατί απλά κανένας compiler που γνωρίζω δεν μεταφράζει και τα... σχόλια.

DATASEGMENT
Εδώ τοποθετούνται όλες οι αρχικοποιημένενες γενικές μεταβλητές (globalvariables). Πρόκειται για ένα κομμάτι μνήμης που επιτρέπεται το γράψιμο πάνω σ’ αυτό.
Για παράδειγμα ο παρακάτω C κώδικας θα τοποθετηθεί στο DataSegment:

 

int i=1;
char cp=65;
int a[10]={1,2,3,4,5,6,7,8,9,10};
char p[10]={“Heloooo!”}


STACK SEGMENT
Όλες οι μεταβλητές των συναρτήσεων (functions), οι διευθύνεις των συναρτήσεων τοποθετούνται σε αυτό το κομμάτι μνήμης.

Αυτό το κομμάτι είναι μια δομή στοίβας (Stack - γνωστή σ’ αυτούς που έχουν παρακολουθήσει κάποιο μάθημα βασικών αρχών δομών δεδομένων). Δηλαδή οι μεταβλητές τοποθετοτούνται στην μνήμη, η μία «επάνω» στην άλλη φτιάχνοντας μια στοίβα. Έτσι, η μεταβλητή που μπήκε τελευταία θα είναι και αυτή που θα βγεί πρώτη από τη στοίβα (περίπτωση LIFO – Last In First Out).

Στον επεξεργαστή μας υπάρχουν μεταβλητές που χρησιμοποιούνται για να «φιλοξενούν» τιμές σχετικές με την τρέχουσα εργασία που κάνει ο επεξεργαστής. Οι μεταβλητές αυτές ονομάζονται καταχωρητές (registers). Υπάρχουν αρκετοί καταχωρητές που περιέχουν πληροφορίες για αρκετά πράγματα, δεν θα τα αναφέρω ένα προς ένα γιατί θα χάσουμε την.. μπάλ... εεε sorry, τονστόχο!

Ένας καταχωρητής ονομάζεται ESP (Extended Stack Pointer – Δείκτης της Στοιβας) περιέχει κάθε φορά την διεύθυνση που βρίσκεται το τελευταίο στοιχείο που μπήκε στη Στοίβα. Δηλαδή, είναι αυτό που θα βγεί πρώτο από αυτή.

Στη στοίβα, έχουμε την δυνατότητα να εισάγουμε τιμές (PUSH) ή να εξάγουμε (POP).
Οι εντολές PUSH και POP είναι εντολές της γλώσσας assembly που κάνουν αυτή τη δουλειά.

Εδώ πρέπει να αναφέρω 2 μικρά μυστικά:
[1] Οι εντολές PUSH και POP πραγματοποιούν πράξεις στην μνήμη σε «κομμάτια» των τεσσάρων bytes, εξ’ αιτίας της συγκεκριμένης αρχιτεκτονικής των επεξεργαστών της οικογένειας xx86.
[2] Η στοίβα μεγαλώνει «προς τα κάτω», δηλαδή αν SP=256, μετά από μιά εντολή “PUSH”, ο SP θα είναι 252.

Παράδειγμα:


 

 STACK
Διευ.      Μνήμη
  ---- ------------------
  256  |   xy          |
  252  |               |
  248  |               |
  244  |               |
  ...  .................
  (ESP=256)
  
  Εντολή > PUSH EAX  ; πχ. EAX = 34
  
  STACK
  256  |   xy          |
  252  |   34          |
  248  |               |
  244  |               |
  ...  .................
  (ESP=252)
  
  Εντολή > POP EAX  
  STACK
  256  |   xy          |
  252  |   34          |
  248  |               |
  244  |               |
  ...  .................
  (ESP=256)
  
  
  Εντολή > PUSH 15   ; remark: suppose EAX = 15
  Εντολή > PUSH 16   ; remark: suppose EBX = 16
  
  STACK
  256  |   xy          |
  252  |   15          |
  248  |   16          |
  244  |               |
  ...  .................
  (ESP=248)



Τι βρίσκεται πίσω από μια κλήση συνάρτησης
Πριν εξηγήσουμε τι γίνεται πρέπει να πουμε μερικά πράγματα για άλλον ένα καταχωρητή του επεξεργαστή: Το Δείκτη Εντολής EIP (Extended Instruction Pointer ή 'Instruction pointer'). Αυτός ο καταχωργητής περιέχει την διεύθυνση της εντολής που θα εκτελεστή από το πρόγραμμα μας. Δηλαδή αναφέρεται στο κομμάτι μνήμης Code Segment.

Κάθε φορά που ο επεξεργαστής εκτελεί μια εντολή καταχωρεί στον EIP την επόμενη προς εκτέλεση εντολή.
Πώς όμως η επεξεργαστης μας βρίκει ποια θα είναι η επόμενη εντολή;
Χμ... εδώ πρέπει να διακρίνουμε 2 περιπτώσεις:
1. Θα εκτελεστεί η επόμενη εντολή στη επόμενη θέση μνήμης στο code segment.
2. Θα εκτελεστεί μια απομακτυσμένη εντολή εξ’ αιτίας μια εντολής JUMP (πήδα... χε χε) σε μια άλλη διεύθυνση! Π.χ. αν ο επεξεργαστης συναντήσει την εντολή «JMP 345» θα βάλει στον EIP την τιμή 345. Άρα η επόμενη εντολή που θα εκτελεστεί θα είναι αυτή στην διεύθυνση 345.

Στην περίπτωση 1 η διεύθυνση υπολογίζεται απλά προσθέτοντας το μήκος της τρέχουσας εντολής στη τρέχουσα τιμή του EIP.
Παράδειγμα:

Έστω οτι έχουμε τις ακόλουθες 2 εντολές στις διευθύνσεις 100 κι 101:

 

100 push EDX
101 mov  ESP 0


Έστω οτι το παραπάνω μικρό μας πρόγραμμα ξεκινάει από την διεύθυνση 100. Δηλαδή EIP=100.
1.Ο επεξεργαστής εκτελεί την εντολή στην διεύθυνση 100.
2.Ο επεξεργαστής θέλει να [άει στην επόμενη εντολή στη σειρά, άρα ελέγχει:
Η τρέχουσα εντολή (pushEDX) είναι εντολή JMP? Όχι, άρα υπολόγισε το μήκος της: Ο επεξεργστής «γνωρίζει» οτι η εντολή push έχει μήκος 1 byte.
Οπότε:

   EIP = EIP + size(push EDX) =>
   EIP = 100 + 1 =>
   EIP = 101


Οπότε θα εκτελέσει την εντολή που βρισκεται στην διεύθυνση 101.

Στην περίπτωση 2, έχουμε JMP... εδώ τα πράγματα είναι λίγο πιο σύνθετα.
Για την ακρίβεια ακριβώς πριν το JMP σε μια άλλη διεύθυνση (δηλαδή μια κλήση συνάρτησης), ο επεξεργαστής πρέπει να κρατήσει την διεύθυνση της εντολής που θα εκτελεστεί όταν επιστρέψουμε από την συνάρτηση. Αυτή η διεύθυνση θα φυλαχθεί σε έναν προσωρινό καταχωρητή, ας πούμε τον EDX, οπότε μόλις εκτελεστούν όλες οι εντολές της συνάρτησης, θα θέσουμε

EIP = EDX

Στην γλώσσα assembly χρησιμοποιούνται οι εντολές CALL και RETN για τον υπολογισμό της παραπάνω διεύθυνσης.
Η CALL κάνει 2 πραγματάκια:
1.Κρατάει την εντολή που θα εκτελεστεί όταν τελειώσουμε με τις εντολές της συνάρτησης (βάζοντας τη διεύθυνση της εντολής αυτής στη στοίβα – στο STACK).
2.Καταχωρεί στον EIP την διεύθυνση της συνάρτησης έτσι ώστε να εκτελεστεί.

Η εντολή RETN θα κληθεί στο τέλος της συνάρτησης:
Θα κάνει POP (θα ανακτήσει) την διεύθυνση εκείνη που καταχώρησς η CALLστο βήματος 1 (λίγο πριν): Είναι η διεύθυνση της εντολής που θα εκτελεστεί μετά το τέλος της συνάρτησης.


Base pointer (EBP) (θα το δυσκολέψουμε λίγο τώρα....sorry)
Κάθε συνάρτηση σε κάθε πρόγραμμα (ακόμα και η main() σε ένα πρόγραμμα C) έχει το δικό της χώρο μεταβλητών στο stack. Αυτός ο χώρος ονομάζεται Stack Frame (θεωρώ οτι είναι άσκοπο να προσπαθήσω να μεταφράσω τέτοιες λέξεις εφόσων θεωρούνται πια ορισμοί). Το stack frame είναι μια λογική ομάδα από θέσεις μνήμης στο stack που κρατούνται όλες οι διευθύνσεις των μεταβλητών που ανήκουν στη συνάρτηση που εκτελείται εκείνη τη στιγμή.

Κάθε διεύθυση μέσα στο stack frame είναι σχετική διεύθυνση με βάση το τρέχων stack frame. Και που θα ξέρουμε πιο είναι το τρέχον stack frame? Αυτό καταχωρείται στον καταχωρητή EBP (στον base pointer).

Όποτε καλείται μια συνάρτηση ο επεξεργαστής κάνει PUSH τον παλιό ESP στο stack και βάζει στον EBP την διεύθνση της συνάρτηση έτσι ώστε να αναφέρεται στις μεταβλητές της με βάση τον EBP.
Θα προσπαθήσω να το ξεκαιαρίσω αυτό με ένα αληθινό παράδειγμα σε C.

Ακολουθεί αληθινό Παράδειγμα σε C



 

void function1(int , int , int );
  void main()
  {
      function1 (1, 2, 3);
  }
  
  void function1 (int a, int b, int c)
  {
          char z[4];
  }


Έκανα compile/link το παραπάνω πρόγραμμα από command line και μπήκα με olly debugger να δω τι... γίνεται:
Αδιαφορώντας για τις εντολές του λειτουργικού συστήματος (που καταλαμβάνουν το 90% του κώδικα!!) απομόνωσα το κομμάτι που αντιστοιχεί στο παραπάνω μικρό και βλαμμένο πρόγραμμα (χε χε χε,... το βρίζω γιατί δεν κάνει τίποτα σαν πρόγραμμα, απλά τρώει mμνήμη) :

Έχουμε λοιπόν:

  0040123C  /. 55             PUSH EBP
  0040123D  |. 8BEC           MOV EBP,ESP
  0040123F  |. 6A 03          PUSH 3                                   ; /Arg3 = 00000003
  00401241  |. 6A 02          PUSH 2                                   ; |Arg2 = 00000002
  00401243  |. 6A 01          PUSH 1                                   ; |Arg1 = 00000001
  00401245  |. E8 05000000    CALL bo1.0040124F                        ; \bo1.0040124F
  0040124A  |. 83C4 0C        ADD ESP,0C
  0040124D  |. 5D             POP EBP
  0040124E  \. C3             RETN
  
  0040124F  /$ 55             PUSH EBP
  00401250  |. 8BEC           MOV EBP,ESP
  00401252  |. 51             PUSH ECX
  00401253  |. 59             POP ECX
  00401254  |. 5D             POP EBP
  00401255  \. C3             RETN 


ΑΝΑΛΥΣΗ:
Αναγκαστικά εδώ θα μιλάμε πια με 16δικές διευθύσεις μνήμης εφόσων μιλαμε για ένα αληθινό παράδειγμα!

Στις διευνύσεις από 0040123Cέως 0040124Eείναι η συνάρτηση main().
Στις διευνύσεις από 0040124Fέως 00401255 είναι η συνάρτηση function1().

Η εντολή:

0040123C  /. 55             PUSH EBP

Φυλάει τον παλιό stack pointer στο STACK

Η εντολή:

 0040123D  |. 8BEC           MOV EBP,ESP

Αντιγράφει τον παλιό stack pointer στον καταχωρητή EBP.
Από εδώ και πέρα θα αναφερόμαστε στις μεταβλητές της συνάρτησης function1 με βάση τον EBP.
Οι 2 παραπάνω εντολές ονομάζονται "Procedure Prologue".

Τώρα το stack θα έχει:

 [ebp]
  STACK
  256  |   [ebp]       |
  ...  .................
  (ESP=256)


Η εντολές:

  0040123F  |. 6A 03          PUSH 3 ; /Arg3 = 00000003
  00401241  |. 6A 02          PUSH 2 ; |Arg2 = 00000002
  00401243  |. 6A 01          PUSH 1 ; |Arg1 = 00000001

Εδώ μπαίνουν στο stack οι παράμετροι της συνάρτησης function1.

Μετά τις παραπάνω εντολές το stack θα είναι:

  STACK
  256  |   [ebp]       |
  252  |     3         |
  248  |     2         |
  244  |     1         |
  ...  .................
  (ESP=244)


Η εντολή:
 

  00401245  |. E8 05000000 CALL bo1.0040124F ; \bo1.0040124F

Καλεί τη συνάρτηση στη διεύθυνση 0040124F. bo1 είναι το όνομα του προγράμματος μου.

Τώρα
το stack θα είναι:

  STACK
  256  |   [ebp]       |
  252  |     3         |
  248  |     2         |
  244  |     1         |
  240  |  0040124A     | <- the return address when the function1 ends.
  ...  .................
  (ESP=240)


Ας ακολουθήσουμε την εκτέλεση του προγράμματος,... δηλαδή ας πάμε στη διεύθυνση 0040124F (της function1):
 

0040124F  /$ 55             PUSH EBP
00401250  |. 8BEC           MOV EBP,ESP

Χμμ... εδώ έχουμε πάλι την "Procedure Prologue" της συνάρτησης (μην ξεχνάτε αυτή πρέπει να υπάρχει ηια κάθε function... ευτυχώς). Ο καταχωρητής EBP δείχνει στο stack frame της συνάρτησης main(). Αυτή η τιμή πρέπει να φυλαχθεί, οπότε ο EBP γίνεται PUSH στο stack. Μετά, το περιεσόμενα του του ESP μεταφέρετε στον EBP. Αυτό σημαίνει οτι οι παράμετροι της συνάρτησης θα καταχωρηθούν στο stack σαν offset (με σημείο αναφοράς) τον EBP. Επίσης «ελευθυερώνεται» και ο ESP να κάνει χρησιμοποιηθεί για τις επόμενες εντολές.


Τώρα το stack θα έχει γίνει:

  STACK
   256  |   [ebp]       |
   252  |     3         |
   248  |     2         |
   244  |     1         |
   240  |  0040124A     | <- Η return address όταν η function τελειώνει.
   236  |  <main’s EBP> | <- Ο ESP=EBP έχει την return address.
   ...  .................
   (ESP=236)



Η εντολές:

  00401253  |. 59             POP ECX
  00401254  |. 5D             POP EBP


Μετά τα 2 τελευταία POPs το STACK θα είναι:

 

  STACK
  256  |   [ebp]       |
  252  |     3         |
  248  |     2         |
  244  |     1         |
  ...  .................
  (ESP=244)


Τελειώνοντας έχουμε την:

  00401255  \. C3             RETN

Η συνάρτηση τελειώνει και επιστρέφει στη 0040124A (θυμάστε τον ορισμό της εντολής RET?)
 

  0040124A  |. 83C4 0C        ADD ESP,0C

Αφού επιστρέψει η συνάρτηση, γίνεται μια πράξη: Πρόσθεση.
 

Προσθέτουμε 12 (ή 0C δεκαεξαδικό) στο stack pointer (ESP). Εφόσων βάλαμε 3 παραμέτρους στο stack (4 bytes η καθέ μια). Αυξάνοντας τον ESP στην πραγματικότητα μειώνουμε το stack (αν θυμόσαστε από πριν γεμίζουμε το stack από τις υψηλές προς χαμηλές διευθύνσεις μνήμης) δηλαδή ESP = 244 + 12 = 256).

  STACK
  256  |   [ebp]       |
  ...  .................
  (ESP=256)

Έτσι ο ESP έχει πάλι την τιμή που είχε πριν την κλήση της συνάρτησης.

Ελπίζω να πήρατε μια αρχική γεύση για την χρήση του stack, των καταχωρητών και της γλώσσας assembly.

Αυτές οι γνώσεις μπορούν να χρησιμοποιηθούν (οπως είπαμε στην αρχή) και για την κατανόηση...πονηρών πραγμάτων όπως ενός buffer overflow. Για παράδειγμα, τι θα λέγατε εάν μπορούσαμε να γράψουμε επάνω στο stack (στη διεύθυνση 240 στο παράδειγμα μας) δηλαδή να γράφαμε επάνω στον EIP μια διεύθυνση... δική μας... και να ορίζαμε με αυτόν εμείς την διεύθυνση που θα εκτελούνταν η επόμενη εντολή του προγράμματος... :)

 

Σας προτέινω να δοκιμάστε το ηλίθιο προγραμματάκι μου σε C και να κάνετε τα δικά σας tests. Test, Check, Review, Test, Check, Review!!


Happy Programming Guys!!


Αναφορές:
[1] BUFFER OVERFLOWS DEMYSTIFIED by
murat@enderunix.org
[2] C Function Call Conventions and the Stack (UMBC CMSC 313, Computer Organization & Assembly Language, Spring 2002, Section 0101)
[3] The Assembly Language Book for IBM PC by Peter Norton (ISBN 960-209-028-6)
[4] Analysis of Buffer Overflow Attacks from
http://www.windowsecurity.com/articles/Analysis_of_Buffer_Overflow_Attacks.html

[5] 8088 8086 Programming and Applications for IBM PC/XT & Compatibles by Nikos Nasoufis
 

Edited by Clipper

Κατά τον δαίμονα εαυτού...

Μοιράσου αυτή την δημοσίευση


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Απάντηση στο θέμα...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



ChatBox

ChatBox

Chatroom Rules

  • Το GreekHacking.Gr είναι ένα ελληνικό forum και επιτρέπεται μόνο η χρήση της Ελληνική Γλώσσας (ούτε greeklish).
  • Δεν επιτρέπονται οι βρισιές και γενικά η χρήση χυδαίας γλώσσας.
  • Απαγορεύεται αυστηρά το πορνογραφικό, προσβλητικό και βίαιο περιεχόμενο.
  • Μην χρησιμοποιείτε κεφαλαία γράμματα η σύμβολα.
×
×
  • Create New...