Κατανόηση της μορφής αρχείου ELF

Understanding Elf File Format



Από τον πηγαίο κώδικα στον δυαδικό κώδικα

Ο προγραμματισμός ξεκινά με την έξυπνη ιδέα και τη σύνταξη πηγαίου κώδικα σε γλώσσα προγραμματισμού της επιλογής σας, για παράδειγμα C, και την αποθήκευση του πηγαίου κώδικα σε ένα αρχείο. Με τη βοήθεια ενός κατάλληλου μεταγλωττιστή, για παράδειγμα GCC, ο πηγαίος κώδικας μεταφράζεται πρώτα σε κώδικα αντικειμένου. Τελικά, ο σύνδεσμος μεταφράζει τον κώδικα αντικειμένου σε δυαδικό αρχείο που συνδέει τον κώδικα αντικειμένου με τις βιβλιοθήκες που αναφέρονται. Αυτό το αρχείο περιέχει τις μεμονωμένες οδηγίες ως κωδικό μηχανήματος που γίνονται κατανοητές από την CPU και εκτελούνται μόλις εκτελεστεί το μεταγλωττισμένο πρόγραμμα.

Το δυαδικό αρχείο που αναφέρθηκε παραπάνω ακολουθεί μια συγκεκριμένη δομή και ένα από τα πιο συνηθισμένα ονομάζεται ELF που συντομεύει το εκτελέσιμο και το σύνδεσμο μορφής. Χρησιμοποιείται ευρέως για εκτελέσιμα αρχεία, μετακινούμενα αρχεία αντικειμένων, κοινόχρηστες βιβλιοθήκες και κεντρικές αποθέσεις.







Πριν από είκοσι χρόνια-το 1999-το έργο 86open επέλεξε το ELF ως την τυπική μορφή δυαδικού αρχείου για συστήματα που μοιάζουν με Unix και Unix σε επεξεργαστές x86. Ευτυχώς, η μορφή ELF είχε τεκμηριωθεί στο παρελθόν τόσο στη δυαδική διεπαφή εφαρμογής V System, όσο και στο πρότυπο διεπαφής εργαλείου [4]. Αυτό το γεγονός απλοποίησε πάρα πολύ τη συμφωνία τυποποίησης μεταξύ των διαφόρων προμηθευτών και προγραμματιστών λειτουργικών συστημάτων που βασίζονται στο Unix.



Ο λόγος πίσω από αυτήν την απόφαση ήταν ο σχεδιασμός του ELF-ευελιξία, δυνατότητα επέκτασης και υποστήριξη μεταξύ πλατφορμών για διαφορετικές μορφές και μεγέθη διευθύνσεων. Ο σχεδιασμός του ELF δεν περιορίζεται σε έναν συγκεκριμένο επεξεργαστή, σύνολο εντολών ή αρχιτεκτονική υλικού. Για μια λεπτομερή σύγκριση των εκτελέσιμων μορφών αρχείων, ρίξτε μια ματιά εδώ [3].



Από τότε, η μορφή ELF χρησιμοποιείται από διάφορα λειτουργικά συστήματα. Μεταξύ άλλων, αυτό περιλαμβάνει Linux, Solaris/Illumos, Free-, Net- και OpenBSD, QNX, BeOS/Haiku και Fuchsia OS [2]. Επιπλέον, θα το βρείτε σε φορητές συσκευές με λειτουργικό σύστημα Android, Maemo ή Meego OS/Sailfish OS καθώς και σε κονσόλες παιχνιδιών όπως το PlayStation Portable, Dreamcast και Wii.





Οι προδιαγραφές δεν διευκρινίζουν την επέκταση ονόματος αρχείου για αρχεία ELF. Χρησιμοποιείται μια ποικιλία συνδυασμών γραμμάτων, όπως .axf, .bin, .elf, .o, .prx, .puff, .ko, .so, και .mod, ή κανένας.

Η δομή ενός αρχείου ELF

Σε ένα τερματικό Linux, το man man elf σας δίνει μια εύχρηστη περίληψη σχετικά με τη δομή ενός αρχείου ELF:



Λίστα 1: Η διαχείριση της δομής ELF

$ man έντεκα

Εγχειρίδιο προγραμματιστή ELF (5) Linux ELF (5)

ΟΝΟΜΑ
elf - μορφή αρχείων εκτελέσιμων και μορφών σύνδεσης (ELF)

ΣΥΝΟΨΗ
#περιλαμβάνω

ΠΕΡΙΓΡΑΦΗ
Το αρχείο κεφαλίδας ορίζει τη μορφή του εκτελέσιμου δυαδικού ELF
αρχεία. Μεταξύ αυτών των αρχείων είναι κανονικά εκτελέσιμα αρχεία, με δυνατότητα μεταφοράς
αρχεία αντικειμένων, βασικά αρχεία και κοινόχρηστες βιβλιοθήκες.

Ένα εκτελέσιμο αρχείο που χρησιμοποιεί τη μορφή αρχείου ELF αποτελείται από μια κεφαλίδα ELF,
ακολουθείται από έναν πίνακα κεφαλίδων προγράμματος ή έναν πίνακα κεφαλίδων ενότητας ή και τα δύο.
Η κεφαλίδα ELF βρίσκεται πάντα στο μηδέν μετατόπισης του αρχείου. Το πρόγραμμα
ο πίνακας κεφαλίδων και η μετατόπιση του πίνακα κεφαλίδων ενότητας στο αρχείο είναι
ορίζεται στην κεφαλίδα ELF. Οι δύο πίνακες περιγράφουν τα υπόλοιπα
ιδιαιτερότητες του αρχείου.

...

Όπως μπορείτε να δείτε από την παραπάνω περιγραφή, ένα αρχείο ELF αποτελείται από δύο ενότητες - μια κεφαλίδα ELF και δεδομένα αρχείου. Η ενότητα δεδομένων αρχείων μπορεί να αποτελείται από έναν πίνακα κεφαλίδας προγράμματος που περιγράφει μηδέν ή περισσότερα τμήματα, έναν πίνακα κεφαλίδας ενότητας που περιγράφει μηδέν ή περισσότερα τμήματα, που ακολουθείται από δεδομένα που αναφέρονται από καταχωρήσεις από τον πίνακα κεφαλίδων προγράμματος και πίνακα κεφαλίδας ενότητας. Κάθε τμήμα περιέχει πληροφορίες που είναι απαραίτητες για την εκτέλεση του αρχείου, ενώ οι ενότητες περιέχουν σημαντικά δεδομένα για σύνδεση και μετεγκατάσταση. Το σχήμα 1 το απεικονίζει σχηματικά.

Η κεφαλίδα του ELF

Η κεφαλίδα ELF έχει μήκος 32 byte και προσδιορίζει τη μορφή του αρχείου. Ξεκινά με μια ακολουθία τεσσάρων μοναδικών byte που είναι 0x7F ακολουθούμενη από 0x45, 0x4c και 0x46 που μεταφράζεται στα τρία γράμματα E, L και F. Μεταξύ άλλων τιμών, η κεφαλίδα υποδεικνύει επίσης εάν πρόκειται για αρχείο ELF για 32 ή Μορφή 64-bit, χρησιμοποιεί μικρή ή μεγάλη ενδυναμία, δείχνει την έκδοση ELF καθώς και για ποιο λειτουργικό σύστημα έχει συνταχθεί το αρχείο προκειμένου να λειτουργήσει με τη σωστή δυαδική διεπαφή εφαρμογής (ABI) και το σύνολο εντολών cpu.

Το hexdump του δυαδικού αρχείου αφής φαίνεται ως εξής:

. Κατάλογος 2: Το hexdump του δυαδικού αρχείου

$ hd/usr/bin/touch | κεφάλι -5
00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 00.. ΕΛΑ ........... |
00000010 02 00 3e 00 01 00 00 00 e3 25 40 00 00 00 00 00 | ..> ......% @ ..... |
00000020 40 00 00 00 00 00 00 00 00 28 e4 00 00 00 00 00 00 | @ ....... (....... |
00000030 00 00 00 00 40 40 00 38 00 09 00 40 40 00 1β 00 1α 00 | [email protected] @..... |
00000040 06 00 00 00 05 05 00 00 00 40 00 00 00 00 00 00 00 00 | [προστατευμένο με email] |

Το Debian GNU/Linux προσφέρει την εντολή readelf που παρέχεται στο πακέτο GNU ‘binutils’. Συνοδεύεται από το διακόπτη -h (σύντομη έκδοση για –file -header) εμφανίζει όμορφα την κεφαλίδα ενός αρχείου ELF. Η λίστα 3 απεικονίζει αυτό για το άγγιγμα εντολών.

. Λίστα 3: Εμφάνιση της κεφαλίδας ενός αρχείου ELF

$ readelf -h/usr/bin/touch
Κεφαλίδα ELF:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 00 00
Κατηγορία: ELF64
Δεδομένα: Συμπλήρωμα 2, μικρό ενδιάμεσο
Έκδοση: 1 (τρέχουσα)
OS / ABI: UNIX - System V
Έκδοση ABI: 0
Τύπος: EXEC (εκτελέσιμο αρχείο)
Μηχανή: Advanced Micro Devices X86-64
Έκδοση: 0x1
Διεύθυνση σημείου εισόδου: 0x4025e3
Έναρξη κεφαλίδων προγράμματος: 64 (byte σε αρχείο)
Έναρξη κεφαλίδων ενότητας: 58408 (byte σε αρχείο)
Σημαίες: 0x0
Μέγεθος αυτής της κεφαλίδας: 64 (byte)
Μέγεθος κεφαλίδων προγράμματος: 56 (byte)
Αριθμός κεφαλίδων προγράμματος: 9
Μέγεθος κεφαλίδων ενότητας: 64 (byte)
Αριθμός κεφαλίδων ενότητας: 27
Ευρετήριο πίνακα συμβολοσειρών κεφαλίδας ενότητας: 26

Η κεφαλίδα του προγράμματος

Η κεφαλίδα προγράμματος δείχνει τα τμήματα που χρησιμοποιούνται κατά την εκτέλεση και λέει στο σύστημα πώς να δημιουργήσει μια εικόνα διεργασίας. Η κεφαλίδα από τη λίστα 2 δείχνει ότι το αρχείο ELF αποτελείται από 9 κεφαλίδες προγράμματος που έχουν μέγεθος 56 bytes η κάθε μία και η πρώτη κεφαλίδα ξεκινά από το byte 64.

Και πάλι, η εντολή readelf βοηθά στην εξαγωγή των πληροφοριών από το αρχείο ELF. Ο διακόπτης -l (συντομογραφία -κεφαλίδες προγράμματος ή -τομέα) αποκαλύπτει περισσότερες λεπτομέρειες όπως φαίνεται στην Λίστα 4.

. Λίστα 4: Εμφάνιση πληροφοριών σχετικά με τις κεφαλίδες του προγράμματος

$ readelf -l/usr/bin/touch

Ο τύπος αρχείου Elf είναι EXEC (εκτελέσιμο αρχείο)
Σημείο εισόδου 0x4025e3
Υπάρχουν 9 κεφαλίδες προγράμματος, ξεκινώντας από την αντιστάθμιση 64

Επικεφαλίδες προγράμματος:
Πληκτρολογήστε Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Ευθυγράμμιση
PHDR 0x000000000000000040 0x0000000000000400040 0x0000000000400040
0x0000000000000000f8 0x00000000000001f8 R E 8
INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238
0x00000000000000001c 0x000000000000001c R 1
[Αίτημα διερμηνέα προγράμματος: /lib64/ld-linux-x86-64.so.2]
ΦΟΡΤΩΣΗ 0x0000000000000000 0x0000000000400000 0x000000000000000000
0x000000000000d494 0x000000000000d494 R E 200000
ΦΟΡΤΩΣΗ 0x0000000000001010 0x000000000060de10 0x000000000060de10
0x0000000000000524 0x0000000000000748 RW 200000
ΔΥΝΑΜΙΚΟ 0x000000000000dede28 0x000000000060de28 0x000000000060de28
0x0000000000000001d0 0x00000000000001d0 RW 8
ΣΗΜΕΙΩΣΗ 0x0000000000000254 0x0000000000400254 0x00000000004002525
0x000000000000000044 0x0000000000000044 R 4
GNU_EH_FRAME 0x000000000000bc40 0x00000000000040bc40 0x000000000040bc40
0x00000000000003a4 0x00000000000003a4 R 4
GNU_STACK 0x000000000000000000 0x000000000000000000x0000000000000000
0x000000000000000000 0x000000000000000000 RW 10
GNU_RELRO 0x00000000000000de10 0x000000000060de10 0x000000000060de10
0x0000000000000000f0 0x00000000000001f0 R 1

Χαρτογράφηση τμήματος προς τμήμα:
Τμηματικές ενότητες ...
00
01 .διασύνδεση
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
04 .δυναμική
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
08 .init_array .fini_array .jcr .δυναμικό .got

Η κεφαλίδα της ενότητας

Το τρίτο μέρος της δομής ELF είναι η κεφαλίδα τμήματος. Εννοείται ότι απαριθμεί τα μεμονωμένα τμήματα του δυαδικού. Ο διακόπτης -S (συντομογραφία -κεφαλίδες τμήματος ή –ενότητες) παραθέτει τις διαφορετικές κεφαλίδες. Όσον αφορά την εντολή αφής, υπάρχουν 27 κεφαλίδες ενοτήτων και η λίστα 5 δείχνει τις τέσσερις πρώτες από αυτές συν την τελευταία, μόνο. Κάθε γραμμή καλύπτει το μέγεθος της ενότητας, τον τύπο της ενότητας καθώς και τη μετατόπιση της διεύθυνσης και της μνήμης της.

. Λίστα 5: Λεπτομέρειες τμήματος που αποκαλύπτονται από το readelf

$ readelf -S/usr/bin/touch
Υπάρχουν 27 κεφαλίδες ενοτήτων, ξεκινώντας από την αντιστάθμιση 0xe428:

Κεφαλίδες τμημάτων:
[Nr] Όνομα Τύπος Διεύθυνση Offset
Μέγεθος EntSize Flags Πληροφορίες σύνδεσης Ευθυγράμμιση
[0] NULL 00000000000000000000000000
0000000000000000000000000000000000 0 0 0
[1] .interp PROGBITS 0000000000400238 00000238
00000000000000001γ 000000000000000000 Α 0 0 1
[2]. Σημείωση.Ετικέτα ABI ΣΗΜΕΙΩΣΗ 0000000000400254 00000254
000000000000002020 000000000000000000 A 0 0 4
[3] .note.gnu.build-i ΣΗΜΕΙΩΣΗ 0000000000400274 00000274
...
...
[26] .shstrtab STRTAB 0000000000000000000000e334
00000000000000εκ 000000000000000000 0 0 1
Κλειδί για σημαίες:
W (εγγραφή), A (κατανομή), X (εκτέλεση), M (συγχώνευση), S (συμβολοσειρές), l (μεγάλο)
I (πληροφορίες), L (σειρά σύνδεσης), G (ομάδα), T (TLS), E (εξαιρείται), x (άγνωστο)
O (απαιτείται επιπλέον επεξεργασία OS) o (συγκεκριμένο λειτουργικό σύστημα), p (συγκεκριμένο επεξεργαστή)

Εργαλεία για την ανάλυση ενός αρχείου ELF

Όπως ίσως έχετε παρατηρήσει από τα παραπάνω παραδείγματα, το GNU/Linux είναι εξοπλισμένο με μια σειρά από χρήσιμα εργαλεία που σας βοηθούν να αναλύσετε ένα αρχείο ELF. Ο πρώτος υποψήφιος που θα ρίξουμε μια ματιά είναι το βοηθητικό πρόγραμμα αρχείων.

Το αρχείο εμφανίζει βασικές πληροφορίες σχετικά με τα αρχεία ELF, συμπεριλαμβανομένης της αρχιτεκτονικής συνόλου εντολών για την οποία προορίζεται ο κώδικας σε μετακινούμενο, εκτελέσιμο ή κοινόχρηστο αρχείο αντικειμένου. Στην λίστα 6 σας λέει ότι το/bin/touch είναι ένα εκτελέσιμο αρχείο 64-bit μετά την Linux Standard Base (LSB), δυναμικά συνδεδεμένο και κατασκευασμένο για τον πυρήνα GNU/Linux έκδοση 2.6.32.

. Λίστα 6: Βασικές πληροφορίες χρησιμοποιώντας αρχείο

$ file /bin /touch
/bin/touch: ELF 64-bit LSB εκτελέσιμο, x86-64, έκδοση 1 (SYSV), δυναμικά συνδεδεμένο, διερμηνέας/lib64/l,
για GNU/Linux 2.6.32, BuildID [sha1] = ec08d609e9e8e73d4be6134541a472ad0ea34502, απογυμνωμένο
$

Ο δεύτερος υποψήφιος είναι αυτοδιάβαστος. Εμφανίζει λεπτομερείς πληροφορίες σχετικά με ένα αρχείο ELF. Η λίστα των διακοπτών είναι συγκριτικά μεγάλη και καλύπτει όλες τις πτυχές της μορφής ELF. Χρησιμοποιώντας το διακόπτη -n (συντομογραφία –notes) Η λίστα 7 εμφανίζει τις ενότητες σημείωσης, μόνο, που υπάρχουν στην αφή του αρχείου -την ετικέτα έκδοσης ABI και το κείμενο συμβολοσειράς bit ID.

. Λίστα 7: Εμφάνιση επιλεγμένων τμημάτων ενός αρχείου ELF

$ readelf -n/usr/bin/touch

Εμφάνιση σημειώσεων που βρέθηκαν στο αρχείο offset 0x00000254 με μήκος 0x00000020:
Μέγεθος δεδομένων κατόχου Περιγραφή
GNU 0x00000010 NT_GNU_ABI_TAG (ετικέτα έκδοσης ABI)
Λειτουργικό σύστημα: Linux, ABI: 2.6.32

Εμφάνιση σημειώσεων που βρέθηκαν στο αρχείο offset 0x00000274 με μήκος 0x00000024:
Μέγεθος δεδομένων κατόχου Περιγραφή
GNU 0x00000014 NT_GNU_BUILD_ID (μοναδικό κορδόνι αναγνωριστικού κατασκευής)
Αναγνωριστικό κατασκευής: ec08d609e9e8e73d4be6134541a472ad0ea34502

Σημειώστε ότι στο Solaris και το FreeBSD, το βοηθητικό πρόγραμμα elfdump [7] αντιστοιχεί στο readelf. Από το 2019, δεν υπήρξε νέα έκδοση ή ενημέρωση από το 2003.

Ο αριθμός τρία είναι το πακέτο με το όνομα elfutils [6] που είναι καθαρά διαθέσιμο για Linux. Παρέχει εναλλακτικά εργαλεία στο GNU Binutils και επιτρέπει επίσης την επικύρωση αρχείων ELF. Σημειώστε ότι όλα τα ονόματα των βοηθητικών προγραμμάτων που παρέχονται στο πακέτο ξεκινούν με eu για «elf utils».

Τελευταίο αλλά όχι λιγότερο σημαντικό θα αναφέρουμε το objdump. Αυτό το εργαλείο είναι παρόμοιο με το readelf αλλά εστιάζει σε αρχεία αντικειμένων. Παρέχει παρόμοιο εύρος πληροφοριών σχετικά με αρχεία ELF και άλλες μορφές αντικειμένων.

. Λίστα 8: Οι πληροφορίες αρχείου εξάγονται από το objdump

$ objdump -f /bin /touch

/bin/touch: μορφή αρχείου elf64-x86-64
αρχιτεκτονική: i386: x86-64, σημαίες 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
διεύθυνση έναρξης 0x00000000004025e3

$

Υπάρχει επίσης ένα πακέτο λογισμικού που ονομάζεται ‘elfkickers’ [9] το οποίο περιέχει εργαλεία για την ανάγνωση του περιεχομένου ενός αρχείου ELF καθώς και τον χειρισμό του. Δυστυχώς, ο αριθμός των κυκλοφοριών είναι μάλλον χαμηλός και γι 'αυτό το αναφέρουμε απλώς και δεν παρουσιάζουμε περαιτέρω παραδείγματα.

Ως προγραμματιστής, μπορείτε να ρίξετε μια ματιά στο «pax-utils» [10,11]. Αυτό το σύνολο βοηθητικών προγραμμάτων παρέχει μια σειρά εργαλείων που βοηθούν στην επικύρωση αρχείων ELF. Για παράδειγμα, το dumpelf αναλύει το αρχείο ELF και επιστρέφει ένα αρχείο κεφαλίδας C που περιέχει τις λεπτομέρειες - δείτε το σχήμα 2.

συμπέρασμα

Χάρη στον συνδυασμό έξυπνου σχεδιασμού και εξαιρετικής τεκμηρίωσης, η μορφή ELF λειτουργεί πολύ καλά και εξακολουθεί να χρησιμοποιείται μετά από 20 χρόνια. Τα βοηθητικά προγράμματα που εμφανίζονται παραπάνω σας επιτρέπουν να δείτε μια εικόνα σε ένα αρχείο ELF και σας επιτρέπουν να καταλάβετε τι κάνει ένα πρόγραμμα. Αυτά είναι τα πρώτα βήματα για την ανάλυση λογισμικού - καλή διασκέδαση!

Σύνδεσμοι και αναφορές
Ευχαριστίες

Ο συγγραφέας θα ήθελε να ευχαριστήσει τον Axel Beckert για την υποστήριξή του σχετικά με την προετοιμασία αυτού του άρθρου.