===== Sažetak =====
Format ELF (engl. //Executable and Linkable Format//) glavni je format za izvršne datoteke, objektne datoteke i biblioteke na Linux-u i drugim, UNIX-u sličnim, operacijskim sustavima. Zbog raširenosti ovog formata, važno je poznavati njegova svojstva i strukturu, što može biti od znatne pomoći u forenzici datoteka. Ovdje je stoga opisana općenita struktura ELF datoteka, predstavljeni su alati za njihovu analizu te je na primjerima demonstriran pristup njihovoj analizi.
===== Uvod =====
Format ELF, u kratici za //Executable and Linkable Format//, datotečni je format koji služi za pohranu izvršnih programa, objektnih datoteka i dijeljenih biblioteka. Svojevrsni je analogon formatu PE kojim su predstavljene izvršne datoteke na operacijskom sustavu Windows.
Umjesto poznate ekstenzije //.exe// koju koriste izvršne PE datoteke, izvršne ELF datoteke u pravilu uopće nemaju ekstenziju. Ostale ELF datoteke, kojima je predstavljen objektni kod, najčešće imaju ekstenziju //.o//, dok dijeljene biblioteke najčešće imaju ekstenziju //.so//. U uporabi su i ekstenzije poput //.elf//, //.bin//, i ostalih.
Zbog svoje fleksibilnosti, ELF je danas prisutan na mnogim operacijskim sustavima i arhitekturama. Najčešće ga se može pronaći na operacijskim sustavima naliku na UNIX, kao što su: Linux, FreeBSD, OpenBSD, NetBSD, Solaris, i ostali. (Važno je naglasiti da macOS nije na tom popisu; macOS i iOS koriste poseban format, zvan Mach-O.) Ipak, osim navedenih, ELF se koristi i na mnogim drugim operacijskim sustavima, čak i onima koji ne vuku korijene iz UNIX-a. Osim toga, ELF je prisutan i na nekim igraćim konzolama, kao što su: PlayStation (sve iteracije), Nintendo 64, GameCube, Wii, Wii U, i ostali. Nije začuđujuće da je ELF datoteke moguće pronaći i na mobilnim operacijskim sustavima. Od mobilnih operacijskih sustava koji koriste ELF, Android je trenutno glavni predstavnik.
===== Struktura ELF datoteke =====
Svaka ELF datoteka sastoji se ELF zaglavlja (koje je obavezno), nakon kojeg slijede dvije tablice: //program header table// i //section header table//. Te dvije tablice dalje pobliže opisuju strukturu datoteke.
**Slika 1** prikazuje grubu strukturu tipične ELF datoteke. Na početku (bez odmaka) se nalazi ELF zaglavlje. Na određenom odmaku od ELF zaglavlja nalazi se tablica //program header table// koja opisuje različite segmente. Na nekom drugom odmaku od ELF zaglavlja nalazi se tablica //section header table// koja pak opisuje različite sekcije datoteke.
{{ :racfor_wiki:elf:stuktura_elf_datoteke.png?350 |}}
**Slika 1**: Općenita struktura ELF datoteke [[https://commons.wikimedia.org/wiki/File:Elf-layout--en.svg|Izvor]]
==== ELF zaglavlje ====
ELF zaglavlje duljine je 52 bajta za 32-bitnu arhitekturu, a duljine 64 bajta za 64-bitnu arhitekturu.
Prva četiri bajta zaglavlja čini magični broj koji definira da se radi o datoteci formatiranoj u ELF. Ta četiri magična bajta redom su: 0x7F, 0x45, 0x4C, 0x46. To jest, na prvom je mjestu bajt 0x7F, iza kojega slijede znakovi 'E', 'L' i 'F', kodirani u ASCII.
Nastavak zaglavlja opisuje, između ostaloga:
* arhitekturu (32-bitna ili 64-bitna),
* informaciju koristi li se vrsta zapisa //little-endian// ili //big-endian//,
* verziju zaglavlja,
* operacijski sustav, tj. ciljani ABI (engl. //application binary interface//),
* tip datoteke (//relocatable//, //executable//, //shared object// ili //core file//),
* ciljanu arhitektura procesora, tj. ciljani //instruction set architecture//,
* ulaznu točku programa (engl. //entry point//),
* početak i veličinu tablice //program header table//,
* početak i veličinu tablice //section header table//,
i još toga. Detaljniji opis strukture ELF zaglavlja može se pronaći u [1] i [2].
==== Program header table ====
//Program header table// sastoji se od niza zaglavlja, svako naziva //program header//. Broj tih zaglavlja ovisi o datoteci, a može ih biti prisutno nula ili više.
Zaglavlja tipa //program header// opisuju informacije koje su bitne za pokretanje i izvođenje datoteke, tj. za njen //run-time//. Pomoću informacija iz tih zaglavlja, operacijski sustav zna kako kreirati proces iz datoteke. Stoga //program header table// ima smisla samo za izvršne datoteke i dijeljene objektne datoteke.
Konkretnije, svaki //program header// opisuje jedan memorijski segment, a svaki se pak segment sastoji od nula ili više sekcija. Ti su segmenti zapravo dijelovi koji se učitavaju u memoriju prilikom podizanja datoteke u proces. Način kako se to točno događa detaljnije je opisan u [3].
Detaljniji opis strukture tablice //program header table// može se pronaći u [1] i [2].
==== Section header table ====
//Section header table// čini nula ili više zaglavlja tipa //section header//. Ta zaglavlja sadrže informacije o sekcijama datoteke. Naime, dok segmenti nose informacije bitne za //run-time//, sekcije pak nose informacije koje se koriste prilikom povezivanja (engl. //linking//) datoteke.
Tipovi sekcija koji se mogu najčešće naći u izvršnim datotekama jesu:
* //.text// - sadrži instrukcije, tj. izvršni kod,
* //.data// - predstavlja inicijalizirane statičke varijable,
* //.rodata// - predstavlja konstante,
* //.bss// - predstavlja neinicijalizirane statičke varijable.
Detaljniji opis strukture tablice //section header table// može se pronaći u [1] i [2].
===== Analiza ELF datoteke =====
Poznavanje strukture ELF datoteka osobito je korisno za forenziku datoteka. Kao što je već opisano, ELF datoteke specifično su posložene i sadrže brojne podatke na temelju kojih forenzičari mogu izvući korisne informacije o datoteci, a koje nisu očigledne na prvi pogled. U nastavku je stoga navedeno nekoliko primjera analize ELF datoteka.
Iako je za analizu ELF datoteke moguće upotrijebiti običan //hex editor//, postoje i alati koji mogu olakšati taj proces. Neki od jednostavnijih i popularnijih alata za tu svrhu su:
* ''file'',
* ''readelf'',
* ''elfutils'',
* ''objdump''.
Analize u nastavku fokusiraju se na korištenje alata ''file'' i ''readelf''.
Kao radni primjer poslužit će jednostavan "Hello World" program, napisan u programskom jeziku C.
#include
int main(void)
{
printf("Hello World!\n");
return 0;
}
==== Primjer 1 ====
Ovaj primjer ilustrira analizu tipa ELF datoteke. Naime, promatranjem vrijednosti iz ELF zaglavlja moguće je odrediti radi li se o izvršnoj datoteci, objektnoj datoteci ili nekom drugom tipu datoteke.
Prevedimo za početak program ''hello.c'' u izvršnu datoteku ''hello'', npr. korištenjem alata ''gcc'':
gcc hello.c -o hello
Također, transformirajmo program u čisti objektni kod:
gcc -c hello.c
Time je nastala datoteka ''hello.o''. Ona se razlikuje od izvršne datoteke ''hello'' po tome što joj je potreban još jedan korak da bi postala izvršiva - povezivanje (engl. //linking//). Ovako ostaje samo obična objektna datoteka koja sadrži instrukcije u strojnom kodu, ali nije spremna za pokretanje.
Tu razliku očekujemo vidjeti i analizom njihovih ELF zaglavlja.
Alat ''file'' za izvršnu datoteku ''hello'' daje sljedeći ispis:
$ file hello
hello: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=25741b5479aba6aa8ecdaeac3b3d91a05b0cc8d0, for GNU/Linux 3.2.0, not stripped
Isti alat za objektnu datoteku ''hello.o'' daje ovakav ispis:
$ file hello.o
hello.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
Dakle, jasno je vidljiva razlika, izvršna datoteka ''hello'' označena je kao //executable//, dok je objektna datoteka ''hello.o'' označena kao //relocatable//.
Ista je razlika vidljiva ako se za provjeru koristi alat ''readelf''.
Ispis za datoteku ''hello'':
$ readelf -h hello
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Position-Independent Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x1060
Start of program headers: 64 (bytes into file)
Start of section headers: 13968 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 13
Size of section headers: 64 (bytes)
Number of section headers: 31
Section header string table index: 30
Ispis za datoteku ''hello.o'':
$ readelf -h hello.o
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 600 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 64 (bytes)
Number of section headers: 14
Section header string table index: 13
Ipak, ovdje je dodatno vidljiva još jedna zanimljivost, a to je da objektna datoteka ''hello.o'' nema niti jedan //program header//. Štoviše, nema ni postavljenu početnu adresu (engl. //entry point address//). Naravno, to oboje ima smisla, budući da ''hello.o'' nije izvršna datoteka. S druge strane, jasno je vidljivo da izvršna datoteka ''hello'' ima postavljenu i početnu adresu, a ima i neprazan skup //program header//-a.
==== Primjer 2 ====
Ovaj primjer ilustrira razliku između statički povezane izvršne datoteke i dinamički povezane izvršne datoteke.
Statički povezanu izvršnu datoteku moguće je dobiti predajom zastavice ''-static'' alatu ''gcc'':
gcc -static hello.c -o hello_static
Dobivenu datoteku najlakše je analizirati alatom ''file'':
$ file hello_static
hello_static: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=5d3e7284fc0022d9ffb945f31c6056533a3e517d, for GNU/Linux 3.2.0, not stripped
Dinamički povezanu izvršnu datoteku zapravo ne treba posebno pripremati jer je to zadano ponašanje alata ''gcc''. Stoga je ranija datoteka ''hello'' primjer dinamički povezane izvršne datoteke.
Vidljivo je da alat ''file'' ispravno prepoznaje razliku između ova dva načina povezivanja. Ručna analiza ELF datoteke za ovaj bi primjer bila nešto složenija jer informacija o načinu povezivanja nije direktno vidljiva iz ELF zaglavlja, već bi bilo potrebno pomnije promotriti strukturu datoteke. U takvim situacijama, ovakvi alati mogu znatno olakšati analizu.
===== Zaključak =====
Kao što je pokazano na primjerima, iz ELF datoteka moguće je izvući brojne korisne informacije. Primjerice, može se identificirati tip datoteke ili arhitektura za koju je datoteka pripremljena. Od naprednijih pristupa, poznavanjem ulazne točke programa u kombinaciji s informacijama o segmentima, moguće je odrediti (ili barem procijeniti) kako će datoteka biti učitana u memoriju i na koje lokacije te kako će se ponašati prilikom izvršavanja. Dakako, moguće su još i razne druge, složenije analize. Razumijevanje strukture ELF datoteka stoga je vrijedna vještina za forenzičare.
===== Literatura =====
[1] [[https://en.wikipedia.org/wiki/Executable_and_Linkable_Format]]
[2] [[https://man7.org/linux/man-pages/man5/elf.5.html]]
[3] [[https://wiki.osdev.org/ELF]]
[4] [[https://linuxhint.com/understanding_elf_file_format]]
[5] [[https://linux-audit.com/elf-binaries-on-linux-understanding-and-analysis]]
[6] [[https://ferhr-my.sharepoint.com/:p:/g/personal/mf51454_fer_hr/EUHtxPPULnhJkPjE9HxkOOsBEEUIANDeI_s2qjKyKptBTw?e=vUdg0P|Autorska prezentacija]]