Sziasztok!
Egy/tobb ISA szimulatorhoz keresek egy egyszeru megkozelitest amivel *.elf fileokkal is tudnam etetni a rendszert. Neztem a `libelf-dev` csomagot, de ott az RTFM finoman szolva is foghijjas (lasd: `man elf_begin`). Talaltam meg ezt a leirast, most epp ezt nezem, hatha. Ami kell nekem az kb a kovetkezo: az objcopy -j ... -j ... -O binary xyz.elf xyz.bin-nek megfelelo funkcionalitas, illetve az `nm`-nek megfelelo symbol lookup. Persze ilyesmik is jol jonnek hogy az adott szegmens (.text, .rodata) mettol meddig tart es mettol meddig van benne valami. A legjobb lenne egy sima C library, mint a libelf, felteve hogy ez... erre valo. Amiben csak amiatt ketelkedek mert az `ldd` szerint ezek (objcopy, nm) nem linkelik magukhoz a libelf.so-t.
Barkinek barmi otlet, ilyen getting started jelleggel? A fenti leirast elkezdem nezni, de ott nekem elsore aranytalanul sok a "creating" meg a "modifying" resz, ami itt biztos nem kell, kifejezett dump-olast meg vagy nem lattam ebben vagy vak vagyok.
thx, A.
- 424 megtekintés
Hozzászólások
Minek neked a libelf? Miért nem írod meg magad? Kilistázni egy ELF tartalmát pofonegyszerű, csak pár struct lista, tényleg csak pár sor. A structokat megtalálod az /usr/include/elf.h fájlban.
Pár példa:
- POSIX-UEFI, ebben még a struct defek is megtalálhatók
- bootboot, ez meg a szekciókon meg a szimbólumokon iterál végig
- easyboot végignyálazza az ELF szegmenseket
- easyboot plgld, ez a legteljesebb, szegmensek, szekciók, szimbólumok, de még relokációs listára is van benne példa
Tényleg nem nagy kunszt. Ha csak annyi kell, hogy kidumpold, mint ahogy az nm teszi, az 10-20 sor, nem több (ha értelmezni meg ellenőrizni is akarod, na az már más kérdés).
ehdr = (Elf64_Ehdr *)data; // ELF header shdr = (Elf64_Shdr *)(data + ehdr->e_shoff); // section header // megkeressük a szimbólum táblát meg a string táblát for(i = 0; i < ehdr->e_shnum; i++) { s = ((Elf64_Shdr *)((uint8_t *)shdr + i * ehdr->e_shentsize)); // köv. section if(s->sh_type == SHT_STRTAB) { if(!strs) strs = s; } else if(s->sh_type == SHT_SYMTAB) { if(!syms) syms = s; } } // kilistázzuk a szimbólumokat for(i = 0; i < syms->sh_size; i += syms->sh_entsize) { sym = (Elf64_Sym *)(data + syms->sh_offset + i); printf("%08x %s\n", sym->st_value, (char*)data + strs->sh_offset + sym->st_name); }
Ennyi. Amire figyelni kell, hogy ne léptesd a struct pointert, hanem mindig a fejlécben szereplő "entsize" mezőket használd, hogy jövőbiztos meg multiplatform legyen.
- A hozzászóláshoz be kell jelentkezni
Koszi! Ha csak ennyi az boven jo lehet ;)
Igy kiprobaltam gyorsan, annyi (talan nem is annyira apro?) kulonbseggel hogy 32 bites ELF-eken kell most dolgozzak (erosen embedded cuccok, 32 bit boven eleg). Szoval a tipusokat atirtam Elf32_*-ra, igy is hiba nelkul lefordul, es tenyleg ad egy nm-szeru kimenetet. Vagyis, vannak jo entry-k is, csak azt sejtem igy elsore hogy az a
(char*)data + strs->sh_offset + sym->st_name
resz az nem feltetlen egy null-terminalt sztring ebben az esetben es/vagy nem jo helyre mutat. Ha ezt kiszedem ugy kb jot ad vissza, de a stringekre meg ki kell talalni valamit. Lehet hogy 32 bites esetben mashogy kell? Ez a fenti pelda teljesen logikusnak tunik, szoval fura lenne hogy 32 bites esetben mashogy kene csinalni mint 64-nel.
Szerk: a nyers *.elf-ben valoban null-terminalt sztringek vannak. Ugyhogy itt inkabb az lehet hogy a fenti valami nem pont a nevre mutat. Lehet hogy az offset itt 32 biten megis mashogy van?
Szerk-szerk: Oh, megvan: ket SHT_STRTAB is van ebben a *.elf-ben... es hat nem mindegy hogy melyiket hasznaljuk. Az elsonel zagyvasag jon ki, a masodikra tokeletes es pont azt adja ki ami nekem kell.
- A hozzászóláshoz be kell jelentkezni
kulonbseggel hogy 32 bites ELF-eken kell most dolgozzak [...] Szoval a tipusokat atirtam Elf32_*-ra
Igen, tényleg csak ennyi.
Oh, megvan: ket SHT_STRTAB is van ebben a *.elf-ben... es hat nem mindegy hogy melyiket hasznaljuk.
Ja, igen, lehet több sztringtábla is, tipikusan ilyenkor van egy, amiben a szekciónevek vannak (ehdr->e_shstrndx indexű szekcióban), meg egy másik csak a szimbólumok neveivel. De ez csak konvenció kérdése, lehet egyben is a kettő, az ELF megengedi.
Ha tutibiztosra akarsz menni, akkor a szekció típuskódja helyett a szekciónevet kell nézni:
// szekciónevek sztringtáblája strt = (Elf32_Shdr *)((uint8_t *)shdr + ehdr->e_shstrndx * ehdr->e_shentsize); shstr = (char*)data + strt->sh_offset; // nem típuskódot, hanem szekcióneveket nézünk for(i = 0; i < ehdr->e_shnum; i++) { s = ((Elf32_Shdr *)((uint8_t *)shdr + i * ehdr->e_shentsize)); // köv. section if(!memcmp(shstr + shdr->sh_name, ".strtab", 8)) strs = s; else if(!memcmp(shstr + shdr->sh_name, ".symtab", 8)) syms = s; } // a többi már ugyanaz
A szimbólumneveket tartalmazó sztringtábla neve mindig ".strtab" és ilyenből mindig csak egy van. Ha a nevet nézed, akkor működik úgy is, ha csak egy sztringtábla van, akkor is, ha kettő, meg akkor is, ha a másodikban vannak a szekciónevek (ezt sem köti meg az ELF, elvileg bármi lehet a sorrend).
- A hozzászóláshoz be kell jelentkezni
Szuper! :) Mindjart (vagyis kicsit kesobb) megnezem jobban.
Szerk: most neztem meg. Picit finomhangolni kellett a forrason, de lehet hogy csak az en hulyesegem miatt. Lenyeg a lenyeg hogy ez igy most jonak tunik:
if ( s->sh_type == SHT_STRTAB && memcmp(shstr + s->sh_name, ".strtab", 8) ==0 )
{ strs = s;
}
else if ( s->sh_type == SHT_SYMTAB && memcmp(shstr + s->sh_name, ".symtab", 8) == 0 )
{ syms = s;
}
Es akkor a tobbi valtozatlan, ahogy mondod. Szepen megy a kod az osszes kritikus/erdekes architektura (msp430, avr, riscv32/rv32eac, armv6-m) toolchain-jei altal generalt *.elf-ekre is meg regebbi x86-os binarisokra is amit talaltam elfekvovben, valtoztatas nelkul. Kiprobalom majd a 32/64 bites megkulonboztetest... van par riscv64-es implementaciom is, azokra sem baj ha megy majd :)
- A hozzászóláshoz be kell jelentkezni
Illetve meg debuggolas kozben annyi kijott hogy az elso SHT_STRTAB neve az `.shstrtab` (ez nem kell most), es a masodiknak ez a `.strtab` ami nekunk kell. Just for the record :)
- A hozzászóláshoz be kell jelentkezni
Kiprobalom majd a 32/64 bites megkulonboztetest
Az meg
if(ehdr->e_ident[EI_CLASS] == ELFCLASS64) { // 64 bites, Elf64_* struktok } else { // 32 bites, Elf32_* struktok }
Ja, és ha mindenféle architektúrára akarod használni, akkor elfordulhat, hogy big-endian számokba is belefutsz. Ezt
if(ehdr->e_ident[EI_DATA] == ELFDATA2LSB) { // little endian (x86, arm, risc, stb.) } else { // big endian (ősrégi procik, pl. M68K) }
kóddal lehet egységesen kezelni. Elvileg, 20 éve láttam utoljára nem little endian masinát, ELF big endian fájlt meg talán még sose volt a kezeim között, ha jól emlékszem (utoljára Sparc-on használtam olyant, de azokat nem kellett soha feldolgoznom, csak futtattam őket).
Biztos tudod, de azért leírom, ha valaki weben keresve találna ide, hogy ha little endianra fordítod a programod (valószínű) és egy big endian ELF-be botlanál, akkor meg kell hívni a megfelelő méretű __builtin_bswap16(), __builtin_bswap32() vagy __builtin_bswap64() függvényt az ELF struktok mezőire. Elvileg libc függvények, de én jobb szeretem a fordító beépített verzióit használni, hatékonyabb kódot generál hozzá.
Vagy, ha azt akarod, a programod portolható legyen és működjön big endian vasra lefordítva is, akkor érdemes inkább az endian.h függvényeit használni (ezek a wrapperek fordításkor döntik el, hogy kell-e a bswap() vagy sem).
Illetve meg debuggolas kozben annyi kijott hogy az elso SHT_STRTAB neve az `.shstrtab`
Igen, általában, de ne vegyél arra mérget, hogy mindig az első lesz. Az ehdr->e_shstrndx index a biztos.
- A hozzászóláshoz be kell jelentkezni
Nem használtam ilyeneket, de pl. FreeBSD portsban van egy vagon:
https://elfio.sourceforge.net/
https://crates.io/crates/elfcat
https://sourceware.org/elfutils/
https://dl.packetstormsecurity.net/linux/reverse-engineering/elf-0.5.4p…
https://www.muppetlabs.com/~breadbox/software/elfkickers.html
- A hozzászóláshoz be kell jelentkezni