Förstå ELF -filformat

Understanding Elf File Format



Från källkod till binär kod

Programmering börjar med att ha en smart idé och skriva källkod på ett valfritt programmeringsspråk, till exempel C, och spara källkoden i en fil. Med hjälp av en lämplig kompilator, till exempel GCC, översätts din källkod först till objektkod. Så småningom översätter länken objektkoden till en binär fil som länkar objektkoden till de refererade biblioteken. Denna fil innehåller de enda instruktionerna som maskinkod som förstås av CPU: n och körs så snart det kompilerade programmet körs.

Den binära filen som nämns ovan följer en specifik struktur, och en av de vanligaste heter ELF som förkortar körbart och länkbart format. Det används i stor utsträckning för körbara filer, flyttbara objektfiler, delade bibliotek och kärndumpar.







För tjugo år sedan-1999-har 86open-projektet valt ELF som standard binärt filformat för Unix och Unix-liknande system på x86-processorer. Lyckligtvis hade ELF -formatet tidigare dokumenterats i både System V Application Binary Interface och Tool Interface Standard [4]. Detta faktum förenklade enormt avtalet om standardisering mellan de olika leverantörerna och utvecklarna av Unix-baserade operativsystem.



Anledningen bakom det beslutet var utformningen av ELF-flexibilitet, utökningsbarhet och plattformsstöd för olika endianformat och adressstorlekar. ELFs design är inte begränsad till en specifik processor, instruktionsuppsättning eller hårdvaruarkitektur. För en detaljerad jämförelse av körbara filformat, ta en titt här [3].



Sedan dess har ELF -formatet använts av flera olika operativsystem. Bland annat inkluderar detta Linux, Solaris/Illumos, Free-, Net- och OpenBSD, QNX, BeOS/Haiku och Fuchsia OS [2]. Dessutom hittar du det på mobila enheter som kör Android, Maemo eller Meego OS/Sailfish OS samt på spelkonsoler som PlayStation Portable, Dreamcast och Wii.





Specifikationen förtydligar inte filnamnstillägget för ELF -filer. En mängd olika bokstavskombinationer används, till exempel .axf, .bin, .elf, .o, .prx, .puff, .ko, .so och .mod, eller ingen.

Strukturen för en ELF -fil

På en Linux -terminal ger kommandot man elf dig en praktisk sammanfattning om strukturen för en ELF -fil:



Listning 1: Hemsidan för ELF -strukturen

$ man elva

ELF (5) Linux Programmerings Manual ELF (5)

NAMN
elf - formatet på körbara och länkande format (ELF) -filer

SYNOPSIS
#omfatta

BESKRIVNING
Rubrikfilen definierar formatet för den körbara binären ELF
filer. Bland dessa filer finns normala körbara filer, flyttbara
objektfiler, kärnfiler och delade bibliotek.

En körbar fil med ELF -filformat består av en ELF -rubrik,
följt av en programhuvudtabell eller en sektionsrubriktabell, eller båda.
ELF -rubriken är alltid vid förskjuten noll för filen. Programmet
rubrik tabell och sektion rubrik tabell förskjutning i filen är
definieras i ELF -rubriken. De två tabellerna beskriver resten av
särdragen i filen.

...

Som du kan se från beskrivningen ovan består en ELF -fil av två sektioner - en ELF -rubrik och fildata. Fildatasektionen kan bestå av en programhuvudtabell som beskriver noll eller flera segment, en sektionsrubriktabell som beskriver noll eller fler sektioner, som följs av data som refereras till av poster från programhuvudtabellen och sektionstabellstabellen. Varje segment innehåller information som är nödvändig för körning av filen under körning, medan sektioner innehåller viktiga data för att länka och flytta. Figur 1 illustrerar detta schematiskt.

ELF Header

ELF -rubriken är 32 byte lång och identifierar filens format. Det börjar med en sekvens med fyra unika byte som är 0x7F följt av 0x45, 0x4c och 0x46 som översätts till de tre bokstäverna E, L och F. Bland andra värden anger rubriken också om det är en ELF -fil för 32 eller 64-bitars format, använder liten eller stor slutlighet, visar ELF-versionen samt för vilket operativsystem filen kompilerades för att samverka med rätt binärt gränssnitt (ABI) och cpu-instruktionsuppsättning.

Hexdumpen för den binära filtouchen ser ut så här:

.Lista 2: Den binära filens hexdump

$ hd/usr/bin/touch | huvud -5
00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 | .ELF ........... |
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 00 38 00 09 00 40 00 1b 00 1a 00 | [e -postskyddad] @..... |
00000040 06 00 00 00 05 00 00 00 40 00 00 00 00 00 00 00 | [e -postskyddad] |

Debian GNU/Linux erbjuder readelf -kommandot som finns i GNU -paketet binutils. Tillsammans med switch -h (kort version för –file -header) visar det fint rubriken till en ELF -fil. Listning 3 illustrerar detta för kommandotouch.

.Lista 3: Visar rubriken för en ELF -fil

$ readelf -h/usr/bin/touch
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Klass: ELF64
Data: 2: s komplement, lilla endian
Version: 1 (nuvarande)
OS / ABI: UNIX - System V
ABI -version: 0
Typ: EXEC (körbar fil)
Maskin: Advanced Micro Devices X86-64
Version: 0x1
Ingångsadress: 0x4025e3
Start av programrubriker: 64 (byte i fil)
Start av sektionsrubriker: 58408 (byte i fil)
Flaggor: 0x0
Storleken på denna rubrik: 64 (byte)
Storlek på programrubriker: 56 (byte)
Antal programrubriker: 9
Storleken på sektionsrubriker: 64 (byte)
Antal sektionsrubriker: 27
Sektionsrubrik strängtabellindex: 26

Programrubriken

Programhuvudet visar segmenten som används vid körning och berättar för systemet hur man skapar en processbild. Rubriken från lista 2 visar att ELF -filen består av 9 programrubriker som har en storlek på 56 byte var och den första rubriken börjar med byte 64.

Återigen hjälper readelf -kommandot att extrahera informationen från ELF -filen. Omkopplaren -l (kort för –program -rubriker eller –segment) avslöjar fler detaljer som visas i lista 4.

.Lista 4: Visa information om programhuvudena

$ readelf -l/usr/bin/touch

Elf -filtypen är EXEC (körbar fil)
Ingångspunkt 0x4025e3
Det finns 9 programrubriker, som börjar vid offset 64

Programrubriker:
Typ Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001f8 0x00000000000001f8 R E 8
INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238
0x000000000000001c 0x000000000000001c R 1
[Begär programtolk: /lib64/ld-linux-x86-64.so.2]
LADDA 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x000000000000d494 0x000000000000d494 R E 200000
LOAD 0x000000000000de10 0x000000000060de10 0x000000000060de10
0x0000000000000524 0x0000000000000748 RW 200000
DYNAMISK 0x000000000000de28 0x000000000060de28 0x000000000060de28
0x00000000000001d0 0x00000000000001d0 RW 8
OBS 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

Avsnitt till segmenteringskartläggning:
Segmentavsnitt ...
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. Dynamisk
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
08 .init_array .fini_array .jcr .dynamic .got

Sektionsrubriken

Den tredje delen av ELF -strukturen är sektionsrubriken. Det är tänkt att lista de enskilda sektionerna i binären. Omkopplaren -S (kort för –sektion -rubriker eller –sektioner) listar de olika rubrikerna. När det gäller pekskommandot finns det 27 sektionsrubriker, och lista 5 visar de fyra första plus bara den sista. Varje rad täcker sektionsstorleken, sektionstypen samt dess adress och minnesförskjutning.

.Lista 5: Avsnittsdetaljer avslöjade av sig själv

$ readelf -S/usr/bin/touch
Det finns 27 sektionsrubriker, som börjar vid offset 0xe428:

Avdelningsrubriker:
[Nr] Namntyp Adress Offset
Storlek EntSize Flags Länkinformation Justera
[0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[1] .interp PROGBITS 0000000000400238 00000238
000000000000001c 0000000000000000 A 0 0 1
[2] .not.ABI-tag OBS 0000000000400254 00000254
0000000000000020 0000000000000000 A 0 0 4
[3] .note.gnu.build-i OBS 0000000000400274 00000274
...
...
[26] .shstrtab STRTAB 0000000000000000 0000e334
00000000000000ef 0000000000000000 0 0 1
Nyckel till flaggor:
W (skriv), A (fördela), X (exekvera), M (sammanfoga), S (strängar), l (stor)
I (info), L (länkordning), G (grupp), T (TLS), E (exkludera), x (okänd)
O (extra OS -bearbetning krävs) o (OS -specifik), p (processorspecifik)

Verktyg för att analysera en ELF -fil

Som du kanske har noterat från exemplen ovan har GNU/Linux ett antal användbara verktyg som hjälper dig att analysera en ELF -fil. Den första kandidaten vi kommer att titta på är filverktyget.

filen visar grundläggande information om ELF -filer, inklusive instruktionsuppsättningsarkitekturen för vilken koden i en flyttbar, körbar eller delad objektfil är avsedd. I notering 6 berättar det att/bin/touch är en 64-bitars körbar fil som följer Linux Standard Base (LSB), dynamiskt länkad och byggd för GNU/Linux-kärnversionen 2.6.32.

.Lista 6: Grundläggande information med hjälp av fil

$ file /bin /touch
/bin/touch: ELF 64-bitars LSB körbar, x86-64, version 1 (SYSV), dynamiskt länkad, tolk/lib64/l,
för GNU/Linux 2.6.32, BuildID [sha1] = ec08d609e9e8e73d4be6134541a472ad0ea34502, avskalad
$

Den andra kandidaten är själv. Den visar detaljerad information om en ELF -fil. Listan över switchar är jämförelsevis lång och täcker alla aspekter av ELF -formatet. Med hjälp av switch -n (kort för –notes) Listning 7 visar endast de notavsnitt som finns i filtrycket -ABI -versionstaggen och bit -strängen för build -ID.

.Lista 7: Visa utvalda delar av en ELF -fil

$ readelf -n/usr/bin/touch

Visar anteckningar som finns vid filförskjutningen 0x00000254 med längden 0x00000020:
Ägare Datastorlek Beskrivning
GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag)
OS: Linux, ABI: 2.6.32

Visar anteckningar som finns vid filförskjutningen 0x00000274 med längden 0x00000024:
Ägare Datastorlek Beskrivning
GNU 0x00000014 NT_GNU_BUILD_ID (unikt build -ID bitsträng)
Bygg -ID: ec08d609e9e8e73d4be6134541a472ad0ea34502

Observera att under Solaris och FreeBSD motsvarar verktyget elfdump [7] med readelf. Från och med 2019 har det inte funnits någon ny version eller uppdatering sedan 2003.

Nummer tre är paketet med namnet elfutils [6] som är rent tillgängligt för Linux. Den tillhandahåller alternativa verktyg för GNU Binutils och möjliggör även validering av ELF -filer. Observera att alla namn på verktygen som ingår i paketet börjar med eu för 'elf utils'.

Sist men inte minst kommer vi att nämna objdump. Detta verktyg liknar readelf men fokuserar på objektfiler. Den ger ett liknande utbud av information om ELF -filer och andra objektformat.

.Lista 8: Filinformation extraherad av objdump

$ objdump -f /bin /touch

/bin/touch: filformat elf64-x86-64
arkitektur: i386: x86-64, flaggor 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
startadress 0x00000000004025e3

$

Det finns också ett mjukvarupaket som heter 'elfkickers' [9] som innehåller verktyg för att läsa innehållet i en ELF -fil samt manipulera den. Tyvärr är antalet utgåvor ganska lågt, och det är därför vi bara nämner det och inte visar fler exempel.

Som utvecklare kan du titta på 'pax-utils' [10,11], istället. Denna uppsättning verktyg tillhandahåller ett antal verktyg som hjälper till att validera ELF -filer. Som ett exempel analyserar dumpelf ELF -filen och returnerar en C -huvudfil med detaljerna - se figur 2.

Slutsats

Tack vare en kombination av smart design och utmärkt dokumentation fungerar ELF -formatet mycket bra och används fortfarande efter 20 år. Verktygen som visas ovan ger dig en inblick i en ELF -fil och låter dig ta reda på vad ett program gör. Detta är de första stegen för analys av programvara - lycklig hackning!

Länkar och referenser
Kvitteringar

Författaren vill tacka Axel Beckert för hans stöd när det gäller förberedelsen av denna artikel.