Izpratne par ELF faila formātu

Understanding Elf File Format



No avota koda līdz binārajam kodam

Programmēšana sākas ar gudru ideju un avota koda rakstīšanu izvēlētajā programmēšanas valodā, piemēram, C, un avota koda saglabāšanu failā. Izmantojot atbilstošu kompilatoru, piemēram, GCC, jūsu avota kods vispirms tiek pārvērsts objekta kodā. Galu galā saistītājs objekta kodu pārvērš binārā failā, kas saista objekta kodu ar atsauces bibliotēkām. Šis fails satur atsevišķus norādījumus kā mašīnas kodu, ko saprot CPU, un tie tiek izpildīti, tiklīdz tiek palaista apkopotā programma.

Iepriekš minētajam binārajam failam ir īpaša struktūra, un viens no visizplatītākajiem ir ELF, kas saīsina izpildāmo un saistāmo formātu. To plaši izmanto izpildāmiem failiem, pārvietojamu objektu failiem, koplietotām bibliotēkām un galvenajām izgāztuvēm.







Pirms divdesmit gadiem-1999. gadā-86open projekts izvēlējās ELF kā standarta bināro failu formātu Unix un Unix līdzīgām sistēmām x86 procesoros. Par laimi, ELF formāts iepriekš tika dokumentēts gan sistēmas V lietojumprogrammu binārajā saskarnē, gan rīka interfeisa standartā [4]. Šis fakts ievērojami vienkāršoja vienošanos par standartizāciju starp dažādiem Unix balstītu operētājsistēmu pārdevējiem un izstrādātājiem.



Šā lēmuma iemesls bija ELF dizains-elastība, paplašināmība un dažādu platformu atbalsts dažādiem endian formātiem un adrešu izmēriem. ELF dizains neaprobežojas tikai ar noteiktu procesoru, instrukciju kopu vai aparatūras arhitektūru. Detalizētu izpildāmo failu formātu salīdzinājumu skatiet šeit [3].



Kopš tā laika ELF formātu izmanto vairākas dažādas operētājsistēmas. Cita starpā tas ietver Linux, Solaris/Illumos, Free-, Net- un OpenBSD, QNX, BeOS/Haiku un Fuchsia OS [2]. Turklāt to atradīsit mobilajās ierīcēs, kurās darbojas operētājsistēma Android, Maemo vai Meego OS/Sailfish OS, kā arī spēļu konsolēs, piemēram, PlayStation Portable, Dreamcast un Wii.





Specifikācija neskaidro ELF failu faila nosaukuma paplašinājumu. Tiek izmantotas dažādas burtu kombinācijas, piemēram, .axf, .bin, .elf, .o, .prx, .puff, .ko, .so un .mod vai nav.

ELF faila struktūra

Linux terminālī komandu man elf sniedz jums ērtu kopsavilkumu par ELF faila struktūru:



1. saraksts: ELF struktūras rokasgrāmata

$ man vienpadsmit

ELF (5) Linux programmētāja rokasgrāmata ELF (5)

NAME
elf - izpildāmā un sasaistes formāta (ELF) failu formāts

SINOPSE
#iekļaut

APRAKSTS
Galvenes fails nosaka ELF izpildāmā binārā formātu
failus. Starp šiem failiem ir parastie izpildāmie faili, kurus var pārvietot
objektu failus, galvenos failus un koplietotās bibliotēkas.

Izpildāms fails, izmantojot ELF faila formātu, sastāv no ELF galvenes,
kam seko programmas galvenes tabula vai sadaļas galvenes tabula, vai abas.
ELF galvene vienmēr atrodas faila nulles nobīdē. Programma
galvenes tabula un sadaļas galvenes tabulas nobīde failā ir
definēts ELF galvenē. Abas tabulas apraksta pārējo
lietas īpatnības.

...

Kā redzat iepriekš aprakstā, ELF fails sastāv no divām sadaļām - ELF galvenes un faila datiem. Failu datu sadaļa var sastāvēt no programmas galvenes tabulas, kurā aprakstīti nulles vai vairāki segmenti, sadaļas galvenes tabulas, kas apraksta nulli vai vairāk sadaļas, kam seko dati, uz kuriem atsaucas ieraksti no programmas galvenes tabulas, un sadaļas galvenes tabulas. Katrā segmentā ir informācija, kas nepieciešama faila izpildei izpildes laikā, savukārt sadaļās ir svarīgi dati saistīšanai un pārvietošanai. 1. attēls to shematiski ilustrē.

ELF galvene

ELF galvenes garums ir 32 baiti, un tas identificē faila formātu. Tas sākas ar četru unikālu baitu secību, kas ir 0x7F, kam seko 0x45, 0x4c un 0x46, kas nozīmē trīs burtus E, L un F. Citu vērtību starpā galvene arī norāda, vai tas ir ELF fails 32 vai 64 bitu formāts, izmanto nelielu vai lielu endiansi, parāda ELF versiju, kā arī to, kurai operētājsistēmai fails tika apkopots, lai sadarbotos ar pareizo lietojumprogrammas bināro saskarni (ABI) un CPU instrukciju kopu.

Binārā faila pieskāriena heksdump izskatās šādi:

. 2. saraksts: binārā faila heksdump

$ hd/usr/bin/touch | galva -5
00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 | .PATS ........... |
00000010 02 00 3e 00 01 00 00 00 e3 25 40 00 00 00 00 | ..> ......% @ ..... |
00000020 40 00 00 00 00 00 00 00 28 e4 00 00 00 00 00 | @ ....... (....... |
00000030 00 00 00 00 40 00 38 00 09 00 40 00 1b 00 1a 00 | [aizsargāts e -pasts] @..... |
00000040 06 00 00 00 05 00 00 00 40 00 00 00 00 00 00 | | [aizsargāts pa e -pastu] |

Debian GNU/Linux piedāvā lasīšanas komandu, kas ir nodrošināta GNU paketē “binutils”. Kopā ar slēdzi -h (īsa versija –file -header) tas lieliski parāda ELF faila galveni. Saraksts 3 to ilustrē komandu pieskārienam.

. 3. saraksts: ELF faila galvenes parādīšana

$ readelf -h/usr/bin/touch
ELF galvene:
Maģija: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Klase: ELF64
Dati: 2 papildinājums, mazais endiāns
Versija: 1 (pašreizējā)
OS / ABI: UNIX - sistēma V
ABI versija: 0
Tips: EXEC (izpildāms fails)
Mašīna: uzlabotas mikroierīces X86-64
Versija: 0x1
Ieejas punkta adrese: 0x4025e3
Programmas galvenes sākums: 64 (baiti failā)
Sadaļas galvenes sākums: 58408 (baiti failā)
Karogi: 0x0
Šīs galvenes izmērs: 64 (baiti)
Programmas galvenes lielums: 56 (baiti)
Programmu virsrakstu skaits: 9
Sadaļas galvenes lielums: 64 (baiti)
Sadaļu virsrakstu skaits: 27
Sadaļas galvenes virknes tabulas indekss: 26

Programmas galvene

Programmas galvene parāda izpildes laikā izmantotos segmentus un norāda sistēmai, kā izveidot procesa attēlu. 2. saraksta galvene parāda, ka ELF fails sastāv no 9 programmu galvenēm, kuru katra izmērs ir 56 baiti, un pirmā galvene sākas ar 64. baitu.

Atkal komanda readelf palīdz iegūt informāciju no ELF faila. Slēdzis -l (saīsinājums no –program -headers vai –segments) atklāj sīkāku informāciju, kā parādīts 4. sarakstā.

. 4. saraksts: parādīt informāciju par programmu galvenēm

$ readelf -l/usr/bin/touch

Elf faila tips ir EXEC (izpildāms fails)
Ieejas punkts 0x4025e3
Ir 9 programmu galvenes, sākot ar 64 nobīdi

Programmas galvenes:
Ierakstiet nobīdi VirtAddr PhysAddr
FileSiz MemSiz karodziņu izlīdzināšana
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001f8 0x00000000000001f8 R E 8
INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238
0x000000000000001c 0x000000000000001c R 1
[Pieprasot programmas tulku: /lib64/ld-linux-x86-64.so.2]
IELĀDĒT 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x000000000000d494 0x000000000000d494 R E 200000
LOAD 0x000000000000de10 0x000000000060de10 0x0000000000606010
0x0000000000000524 0x0000000000000748 RW 200000
DINAMIKA 0x000000000000de28 0x000000000060de28 0x0000000000606028
0x00000000000001d0 0x00000000000001d0 RW 8
PIEZĪME 0x0000000000000254 0x0000000000400254 0x0000000000400254
0x0000000000000044 0x0000000000000044 R 4
GNU_EH_FRAME 0x000000000000bc40 0x000000000040bc40 0x000000000040bc40
0x00000000000003a4 0x00000000000003a4 R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 10
GNU_RELRO 0x000000000000de10 0x000000000060de10 0x000000000060de10
0x00000000000001f0 0x00000000000001f0 R 1

Sadaļa līdz segmentu kartēšanai:
Segmentēt sadaļas ...
00
01 .interp
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 .dinamisks
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
08 .init_array .fini_array .jcr .dynamic .got

Sadaļas virsraksts

ELF struktūras trešā daļa ir sadaļas galvene. Tas ir paredzēts, lai uzskaitītu atsevišķās binārā sadaļas. Slēdzis -S (saīsinājums –section -headers vai –sections) uzskaita dažādas galvenes. Runājot par pieskārienu komandu, ir 27 sadaļu galvenes, un 5. sarakstā ir parādīti tikai pirmie četri no tiem un pēdējais. Katra rinda aptver sadaļas lielumu, sadaļas veidu, kā arī tās adresi un atmiņas nobīdi.

5. saraksts: sadaļas detaļas, ko atklāj pats

$ readelf -S/usr/bin/touch
Ir 27 sadaļu galvenes, sākot ar nobīdi 0xe428:

Sadaļu galvenes:
[Nr] Nosaukums Tips Adreses nobīde
Izmērs EntSize Karogi Saites informācija Līdzināt
[0] NULL 0000000000000000 00000000
0000000000000000 000000000000000000 0 0 0
[1] .interp PROBITI 0000000000400238 00000238
000000000000001c 0000000000000000 A 0 0 1
[2] .piezīme.ABI-tag PIEZĪME 0000000000400254 00000254
0000000000000020 0000000000000000 A 0 0 4
[3] .note.gnu.build-i PIEZĪME 0000000000400274 00000274
...
...
[26] .shstrtab STRTAB 0000000000000000 0000e334
00000000000000ef 0000000000000000 0 0 1
Karogu atslēga:
W (rakstīt), A (piešķirt), X (izpildīt), M (sapludināt), S (virknes), l (liels)
I (informācija), L (saišu secība), G (grupa), T (TLS), E (izslēgt), x (nezināms)
O (nepieciešama papildu OS apstrāde) o (specifiska operētājsistēmai), p (specifisks procesors)

Rīki ELF faila analīzei

Kā jau minējāt iepriekš minētajos piemēros, GNU/Linux ir papildināts ar vairākiem noderīgiem rīkiem, kas palīdz analizēt ELF failu. Pirmais kandidāts, kuru mēs apskatīsim, ir failu utilīta.

fails parāda pamatinformāciju par ELF failiem, ieskaitot instrukciju kopas arhitektūru, kurai paredzēts kods pārvietojamā, izpildāmā vai koplietotā objekta failā. 6. sarakstā tiek norādīts, ka/bin/touch ir 64 bitu izpildāms fails, kas seko Linux Standard Base (LSB), ir dinamiski saistīts un izveidots GNU/Linux kodola versijai 2.6.32.

6. saraksts: pamatinformācija, izmantojot failu

$ file /bin /touch
/bin/touch: ELF 64 bitu LSB izpildāmā versija, x86-64, versija 1 (SYSV), dinamiski saistīta, tulks/lib64/l,
GNU/Linux 2.6.32, BuildID [sha1] = ec08d609e9e8e73d4be6134541a472ad0ea34502, noņemts
$

Otrs kandidāts ir pats. Tas parāda detalizētu informāciju par ELF failu. Slēdžu saraksts ir salīdzinoši garš un aptver visus ELF formāta aspektus. Izmantojot slēdzi -n (saīsinājums no –notes), 7. sarakstā ir redzamas tikai piezīmju sadaļas, kas atrodas faila pieskārienā -ABI versijas tags un būvējuma ID bitu virkne.

7. saraksts: parādīt ELF faila atlasītās sadaļas

$ readelf -n/usr/bin/touch

Tiek parādītas piezīmes, kas atrastas faila nobīdē 0x00000254 ar garumu 0x00000020:
Īpašnieks Datu lielums Apraksts
GNU 0x00000010 NT_GNU_ABI_TAG (ABI versijas tags)
OS: Linux, ABI: 2.6.32

Tiek parādītas piezīmes, kas atrastas faila nobīdē 0x00000274 ar garumu 0x00000024:
Īpašnieks Datu lielums Apraksts
GNU 0x00000014 NT_GNU_BUILD_ID (unikāla būves ID bitu virkne)
Būvējuma ID: ec08d609e9e8e73d4be6134541a472ad0ea34502

Ņemiet vērā, ka saskaņā ar Solaris un FreeBSD utilīta elfdump [7] atbilst readelf. Kopš 2019. gada kopš 2003. gada nav bijis neviena jauna laidiena vai atjauninājuma.

Trešais numurs ir pakete ar nosaukumu elfutils [6], kas ir pieejama tikai Linux. Tas nodrošina alternatīvus rīkus GNU Binutils, kā arī ļauj pārbaudīt ELF failus. Ņemiet vērā, ka visi pakotnē norādīto pakalpojumu nosaukumi sākas ar eu “elf utils”.

Visbeidzot, bet ne mazāk svarīgi, mēs pieminēsim objdump. Šis rīks ir līdzīgs lasīšanai, bet koncentrējas uz objektu failiem. Tas nodrošina līdzīgu informācijas klāstu par ELF failiem un citiem objektu formātiem.

. 8. saraksts: Objdump iegūtā informācija par failu

$ objdump -f /bin /touch

/bin/touch: faila formāts elf64-x86-64
arhitektūra: i386: x86-64, karodziņi 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
sākuma adrese 0x00000000004025e3

$

Ir arī programmatūras pakotne ar nosaukumu “elfkickers” [9], kas satur rīkus ELF faila satura nolasīšanai, kā arī manipulācijām ar to. Diemžēl izlaidumu skaits ir diezgan zems, tāpēc mēs to vienkārši pieminējam un nerādām citus piemērus.

Tā vietā kā izstrādātājs varat apskatīt “pax-utils” [10,11]. Šis utilītu komplekts nodrošina vairākus rīkus, kas palīdz apstiprināt ELF failus. Piemēram, dumpelf analizē ELF failu un atgriež C galvenes failu, kurā ir informācija - skatīt 2. attēlu.

Secinājums

Pateicoties gudra dizaina un lieliskas dokumentācijas kombinācijai, ELF formāts darbojas ļoti labi un tiek izmantots arī pēc 20 gadiem. Iepriekš redzamās utilītas ļauj ieskatīties ELF failā un ļauj saprast, ko programma dara. Šie ir pirmie soļi programmatūras analīzei - laimīgu uzlaušanu!

Saites un atsauces
Pateicības

Rakstnieks vēlas pateikties Akselim Bekertam par atbalstu šī raksta sagatavošanā.