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

Αναζήτηση στην κοινότητα

Εμφάνιση αποτελεσμάτων για τις ετικέτες 'how a program runs'.

  • Αναζήτηση με βάση τις ετικέτες

    Πληκτρολογήστε τις ετικέτες και χωρίστε τες με κόμμα.
  • Αναζήτηση με βάση τον συγγραφέα

Τύπος περιεχομένου


Ενότητες

  • GreekHacking
    • Κανόνες & Ανακοινώσεις
    • Καλωσόρισμα νέων μελών
    • Προτάσεις / Βελτιώσεις
    • Νέα σχετικά με το Hacking/Security
    • Ασφάλεια & Προστασία Δεδομένων
    • Εκτός Θέματος
  • Leaks
    • Hacking Tutorials
    • Anonymity & Privacy / Ανωνυμία και Προστασία Προσωπικών Δεδομένων
    • Προγράμματα για Hacking / Hacking Programs
    • Cracking Οδηγοί & Πληροφορίες
    • Βιβλία σχετικά με το Hacking
    • Προγράμματα - High Quality downloads
    • E-Books
    • XXX Movies

Categories

  • Ορολογίες
  • Hacking για αρχάριους
  • Hacking για προχωρημένους
  • Mobile Hacking
  • Cracking Tutorials
  • Hacking Computers
  • Hacking Tutorials στα Αγγλικά
  • Tutorials for hacks
  • Διάφοροι Οδηγοί
    • Οδηγοί σχετικά με τα Linux
    • Οδηγοί για υπολογιστές
    • Οδηγοί σχετικά με τα Mac
    • Οδηγοί για κινητά
  • Hacking Tutorials στα Αγγλικά Copy

Categories

  • Hacking
  • Ιστοσελίδες/Server
  • Hardware
  • Windows

Βρείτε αποτελέσματα...

Βρείτε αποτελέσματα που...


Ημερομηνία δημιουργίας

  • Start

    End


Τελευταία ενημέρωση

  • Start

    End


Φιλτράρισμα με βάση τον αριθμό των...

  1. Πως λειτουργεί ένα πρόγραμμα ή τα πρώτα βήματα πριν την κατανόηση του 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

ChatBox

ChatBox

Chatroom Rules

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