The previous program has 9 program headers, then, the segment mapping indicates in which program header (from 00 to 08) each section is located.
PHDR - Program HeaDeR
Contains the program header tables and metadata itself.
INTERP
Indicates the path of the loader to use to load the binary into memory.
LOAD
These headers are used to indicate how to load a binary into memory.
Each LOAD header indicates a region of memory (size, permissions and alignment) and indicates the bytes of the ELF binary to copy in there.
For example, the second one has a size of 0x1190, should be located at 0x1fc48 with permissions read and write and will be filled with 0x528 from the offset 0xfc48 (it doesn't fill all the reserved space). This memory will contain the sections .init_array .fini_array .dynamic .got .data .bss.
DYNAMIC
This header helps to link programs to their library dependencies and apply relocations. Check the .dynamic section.
NOTE
This stores vendor metadata information about the binary.
GNU_EH_FRAME
Defines the location of the stack unwind tables, used by debuggers and C++ exception handling-runtime functions.
GNU_STACK
Contains the configuration of the stack execution prevention defense. If enabled, the binary won't be able to execute code from the stack.
GNU_RELRO
Indicates the RELRO (Relocation Read-Only) configuration of the binary. This protection will mark as read-only certain sections of the memory (like the GOT or the init and fini tables) after the program has loaded and before it begins running.
In the previous example it's copying 0x3b8 bytes to 0x1fc48 as read-only affecting the sections .init_array .fini_array .dynamic .got .data .bss.
Note that RELRO can be partial or full, the partial version do not protect the section .plt.got, which is used for lazy binding and needs this memory space to have write permissions to write the address of the libraries the first time their location is searched.
TLS
Defines a table of TLS entries, which stores info about thread-local variables.
Section Headers
Section headers gives a more detailed view of the ELF binary
It also indicates the location, offset, permissions but also the type of data it section has.
Meta Sections
String table: Περιέχει όλα τα strings που χρειάζεται το αρχείο ELF (αλλά όχι αυτά που χρησιμοποιούνται πραγματικά από το πρόγραμμα). Για παράδειγμα, περιέχει ονόματα τμημάτων όπως .text ή .data. Και αν το .text είναι στο offset 45 στον πίνακα strings, θα χρησιμοποιήσει τον αριθμό 45 στο πεδίο name.
Για να βρείτε πού είναι ο πίνακας strings, το ELF περιέχει έναν δείκτη στον πίνακα strings.
Symbol table: Περιέχει πληροφορίες σχετικά με τα σύμβολα όπως το όνομα (offset στον πίνακα strings), διεύθυνση, μέγεθος και περισσότερα μεταδεδομένα σχετικά με το σύμβολο.
Main Sections
.text: Οι εντολές του προγράμματος που πρέπει να εκτελούνται.
.data: Παγκόσμιες μεταβλητές με καθορισμένη τιμή στο πρόγραμμα.
.bss: Παγκόσμιες μεταβλητές που έχουν μείνει μη αρχικοποιημένες (ή αρχικοποιημένες σε μηδέν). Οι μεταβλητές εδώ αρχικοποιούνται αυτόματα σε μηδέν, αποτρέποντας έτσι την προσθήκη άχρηστων μηδενικών στο δυαδικό.
.rodata: Σταθερές παγκόσμιες μεταβλητές (τμήμα μόνο για ανάγνωση).
.tdata και .tbss: Όπως οι .data και .bss όταν χρησιμοποιούνται μεταβλητές τοπικού νήματος (__thread_local στην C++ ή __thread στην C).
.dynamic: Δείτε παρακάτω.
Symbols
Symbols είναι μια ονομαστική τοποθεσία στο πρόγραμμα που μπορεί να είναι μια συνάρτηση, ένα παγκόσμιο αντικείμενο δεδομένων, μεταβλητές τοπικού νήματος...
readelf -s lnstat
Symbol table '.dynsym' contains 49 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000001088 0 SECTION LOCAL DEFAULT 12 .init
2: 0000000000020000 0 SECTION LOCAL DEFAULT 23 .data
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strtok@GLIBC_2.17 (2)
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND s[...]@GLIBC_2.17 (2)
5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strlen@GLIBC_2.17 (2)
6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fputs@GLIBC_2.17 (2)
7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND exit@GLIBC_2.17 (2)
8: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _[...]@GLIBC_2.34 (3)
9: 0000000000000000 0 FUNC GLOBAL DEFAULT UND perror@GLIBC_2.17 (2)
10: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterT[...]
11: 0000000000000000 0 FUNC WEAK DEFAULT UND _[...]@GLIBC_2.17 (2)
12: 0000000000000000 0 FUNC GLOBAL DEFAULT UND putc@GLIBC_2.17 (2)
[...]
Κάθε είσοδος συμβόλου περιέχει:
Όνομα
Δεσμευτικά χαρακτηριστικά (ασθενές, τοπικό ή παγκόσμιο): Ένα τοπικό σύμβολο μπορεί να προσπελαστεί μόνο από το ίδιο το πρόγραμμα, ενώ τα παγκόσμια σύμβολα μοιράζονται έξω από το πρόγραμμα. Ένα ασθενές αντικείμενο είναι για παράδειγμα μια συνάρτηση που μπορεί να παρακαμφθεί από μια διαφορετική.
Ο κατάλογος NEEDED υποδεικνύει ότι το πρόγραμμα χρειάζεται να φορτώσει τη συγκεκριμένη βιβλιοθήκη προκειμένου να συνεχίσει. Ο κατάλογος NEEDED ολοκληρώνεται μόλις η κοινή βιβλιοθήκη είναι πλήρως λειτουργική και έτοιμη προς χρήση.
Relocations
Ο φορτωτής πρέπει επίσης να μετατοπίσει τις εξαρτήσεις αφού τις έχει φορτώσει. Αυτές οι μετατοπίσεις υποδεικνύονται στον πίνακα μετατοπίσεων σε μορφές REL ή RELA και ο αριθμός των μετατοπίσεων δίνεται στις δυναμικές ενότητες RELSZ ή RELASZ.
Αν το πρόγραμμα φορτωθεί σε διαφορετική θέση από τη προτιμώμενη διεύθυνση (συνήθως 0x400000) επειδή η διεύθυνση είναι ήδη χρησιμοποιούμενη ή λόγω ASLR ή οποιουδήποτε άλλου λόγου, μια στατική μετάθεση διορθώνει τους δείκτες που είχαν τιμές περιμένοντας το δυαδικό να φορτωθεί στη προτιμώμενη διεύθυνση.
Για παράδειγμα, οποιαδήποτε ενότητα τύπου R_AARCH64_RELATIV θα πρέπει να έχει τροποποιήσει τη διεύθυνση με το μεταθετικό σφάλμα συν την τιμή προσθέτου.
Δυναμικές Μεταθέσεις και GOT
Η μετάθεση θα μπορούσε επίσης να αναφέρεται σε ένα εξωτερικό σύμβολο (όπως μια συνάρτηση από μια εξάρτηση). Όπως η συνάρτηση malloc από τη libC. Έτσι, ο φορτωτής όταν φορτώνει τη libC σε μια διεύθυνση ελέγχει πού είναι φορτωμένη η συνάρτηση malloc, θα γράψει αυτή τη διεύθυνση στον πίνακα GOT (Global Offset Table) (που υποδεικνύεται στον πίνακα μεταθέσεων) όπου θα πρέπει να καθοριστεί η διεύθυνση του malloc.
Πίνακας Σύνδεσης Διαδικασιών
Η ενότητα PLT επιτρέπει την εκτέλεση καθυστερημένης σύνδεσης, που σημαίνει ότι η επίλυση της τοποθεσίας μιας συνάρτησης θα πραγματοποιηθεί την πρώτη φορά που θα προσπελαστεί.
Έτσι, όταν ένα πρόγραμμα καλεί τη malloc, στην πραγματικότητα καλεί την αντίστοιχη τοποθεσία του malloc στον PLT (malloc@plt). Την πρώτη φορά που καλείται, επιλύει τη διεύθυνση του malloc και την αποθηκεύει, ώστε την επόμενη φορά που θα κληθεί η malloc, αυτή η διεύθυνση να χρησιμοποιείται αντί του κώδικα PLT.
Αρχικοποίηση Προγράμματος
Αφού το πρόγραμμα έχει φορτωθεί, ήρθε η ώρα να εκτελεστεί. Ωστόσο, ο πρώτος κώδικας που εκτελείται δεν είναι πάντα η συνάρτηση main. Αυτό συμβαίνει επειδή, για παράδειγμα, στην C++ αν μια παγκόσμια μεταβλητή είναι ένα αντικείμενο μιας κλάσης, αυτό το αντικείμενο πρέπει να αρχικοποιηθείπριν εκτελεστεί η main, όπως στο:
Σημειώστε ότι αυτές οι παγκόσμιες μεταβλητές βρίσκονται στο .data ή .bss, αλλά στις λίστες __CTOR_LIST__ και __DTOR_LIST__ τα αντικείμενα που πρέπει να αρχικοποιηθούν και να καταστραφούν αποθηκεύονται για να παρακολουθούνται.
Από τον κώδικα C είναι δυνατόν να αποκτηθεί το ίδιο αποτέλεσμα χρησιμοποιώντας τις επεκτάσεις GNU:
__attributte__((constructor)) //Add a constructor to execute before__attributte__((destructor)) //Add to the destructor list
Από την προοπτική ενός μεταγλωττιστή, για να εκτελούνται αυτές οι ενέργειες πριν και μετά την εκτέλεση της συνάρτησης main, είναι δυνατόν να δημιουργηθεί μια συνάρτηση init και μια συνάρτηση fini που θα αναφέρονται στην δυναμική ενότητα ως INIT και FIN. και τοποθετούνται στις ενότητες init και fini του ELF.
Η άλλη επιλογή, όπως αναφέρθηκε, είναι να αναφερθούν οι λίστες __CTOR_LIST__ και __DTOR_LIST__ στις εγγραφές INIT_ARRAY και FINI_ARRAY στη δυναμική ενότητα και το μήκος αυτών υποδεικνύεται από INIT_ARRAYSZ και FINI_ARRAYSZ. Κάθε εγγραφή είναι ένας δείκτης συνάρτησης που θα καλείται χωρίς παραμέτρους.
Επιπλέον, είναι επίσης δυνατό να υπάρχει μια PREINIT_ARRAY με δείκτες που θα εκτελούνται πριν από τους δείκτες INIT_ARRAY.
Σειρά Αρχικοποίησης
Το πρόγραμμα φορτώνεται στη μνήμη, οι στατικές παγκόσμιες μεταβλητές αρχικοποιούνται στο .data και οι μη αρχικοποιημένες μηδενίζονται στο .bss.
Όλες οι εξαρτήσεις για το πρόγραμμα ή τις βιβλιοθήκες αρχικοποιούνται και η δυναμική σύνδεση εκτελείται.
Οι συναρτήσεις PREINIT_ARRAY εκτελούνται.
Οι συναρτήσεις INIT_ARRAY εκτελούνται.
Αν υπάρχει μια εγγραφή INIT, καλείται.
Αν είναι μια βιβλιοθήκη, το dlopen τελειώνει εδώ, αν είναι πρόγραμμα, είναι ώρα να καλέσουμε το πραγματικό σημείο εισόδου (συνάρτηση main).
Αποθήκευση Τοπικών Νημάτων (TLS)
Ορίζονται χρησιμοποιώντας τη λέξη-κλειδί __thread_local στην C++ ή την επέκταση GNU __thread.
Κάθε νήμα θα διατηρεί μια μοναδική τοποθεσία για αυτή τη μεταβλητή, έτσι μόνο το νήμα μπορεί να έχει πρόσβαση στη μεταβλητή του.
Όταν αυτό χρησιμοποιείται, οι ενότητες .tdata και .tbss χρησιμοποιούνται στο ELF. Οι οποίες είναι όπως η .data (αρχικοποιημένη) και η .bss (μη αρχικοποιημένη) αλλά για TLS.
Κάθε μεταβλητή θα έχει μια εγγραφή στην κεφαλίδα TLS που θα καθορίζει το μέγεθος και την απόσταση TLS, η οποία είναι η απόσταση που θα χρησιμοποιήσει στην τοπική περιοχή δεδομένων του νήματος.
Ο __TLS_MODULE_BASE είναι ένα σύμβολο που χρησιμοποιείται για να αναφέρεται στη βασική διεύθυνση της αποθήκευσης τοπικών νημάτων και δείχνει στην περιοχή μνήμης που περιέχει όλα τα τοπικά δεδομένα νημάτων ενός module.