Jak mohu vytisknout verzi sestavení programu (umístěnou v sekci .note.gnu.build-id elf) ze samotného programu?
-
Musíte si přečíst
ElfW(Ehdr)
(na začátku souboru), abyste našli záhlaví programu ve vašem binárním souboru (.e_phoff
a.e_phnum
vám řekne, kde jsou hlavičky programů a kolik jich ke čtení). -
Potom čtete hlavičky programů, dokud nenajdete
PT_NOTE
segmentu vašeho programu. Tento segment vám řekne offset na začátek všech not ve vaší binární soustavě. -
Poté si musíte přečíst
ElfW(Nhdr)
a přeskočte zbytek poznámky (celková velikost poznámky jesizeof(Nhdr) + .n_namesz + .n_descsz
, správně zarovnané), dokud nenajdete poznámku s.n_type == NT_GNU_BUILD_ID
. -
Jakmile najdete
NT_GNU_BUILD_ID
poznámka, přeskočte za jeho.n_namesz
a přečtěte si.n_descsz
bajtů ke čtení skutečného ID sestavení.
Můžete ověřit, že čtete správná data, porovnáním toho, co čtete, s výstupem readelf -n a.out
.
P.S.
Pokud se chystáte projít problémy s dekódováním build-id, jak je uvedeno výše, a pokud váš spustitelný soubor není odstraněn, může být pro vás lepší pouze dekódovat a vytisknout symbol názvy (tj. replikovat to, co backtrace_symbols
ano) – ve skutečnosti je to jednodušší než dekódování poznámek ELF, protože tabulka symbolů obsahuje položky s pevnou velikostí.
V podstatě je to kód, který jsem napsal na základě odpovědi na mou otázku. Abych mohl kód zkompilovat, musel jsem provést nějaké změny a doufám, že to bude fungovat pro co nejvíce typů platforem. Byl však testován pouze na jednom stavebním stroji. Jedním z předpokladů, které jsem použil, bylo, že program byl postaven na stroji, na kterém je spuštěn, takže nemá smysl kontrolovat kompatibilitu endianness mezi programem a strojem.
[email protected]:~/$ uname -s -r -m -o
Linux 3.2.0-45-generic x86_64 GNU/Linux
[email protected]:~/$ g++ test.cpp -o test
[email protected]:~/$ readelf -n test | grep Build
Build ID: dc5c4682e0282e2bd8bc2d3b61cfe35826aa34fc
[email protected]:~/$ ./test
Build ID: dc5c4682e0282e2bd8bc2d3b61cfe35826aa34fc
#include <elf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#if __x86_64__
# define ElfW(type) Elf64_##type
#else
# define ElfW(type) Elf32_##type
#endif
/*
detecting build id of a program from its note section
http://stackoverflow.com/questions/17637745/can-a-program-read-its-own-elf-section
http://www.scs.stanford.edu/histar/src/pkg/uclibc/utils/readelf.c
http://www.sco.com/developers/gabi/2000-07-17/ch5.pheader.html#note_section
*/
int main (int argc, char* argv[])
{
char *thefilename = argv[0];
FILE *thefile;
struct stat statbuf;
ElfW(Ehdr) *ehdr = 0;
ElfW(Phdr) *phdr = 0;
ElfW(Nhdr) *nhdr = 0;
if (!(thefile = fopen(thefilename, "r"))) {
perror(thefilename);
exit(EXIT_FAILURE);
}
if (fstat(fileno(thefile), &statbuf) < 0) {
perror(thefilename);
exit(EXIT_FAILURE);
}
ehdr = (ElfW(Ehdr) *)mmap(0, statbuf.st_size,
PROT_READ|PROT_WRITE, MAP_PRIVATE, fileno(thefile), 0);
phdr = (ElfW(Phdr) *)(ehdr->e_phoff + (size_t)ehdr);
while (phdr->p_type != PT_NOTE)
{
++phdr;
}
nhdr = (ElfW(Nhdr) *)(phdr->p_offset + (size_t)ehdr);
while (nhdr->n_type != NT_GNU_BUILD_ID)
{
nhdr = (ElfW(Nhdr) *)((size_t)nhdr + sizeof(ElfW(Nhdr)) + nhdr->n_namesz + nhdr->n_descsz);
}
unsigned char * build_id = (unsigned char *)malloc(nhdr->n_descsz);
memcpy(build_id, (void *)((size_t)nhdr + sizeof(ElfW(Nhdr)) + nhdr->n_namesz), nhdr->n_descsz);
printf(" Build ID: ");
for (int i = 0 ; i < nhdr->n_descsz ; ++i)
{
printf("%02x",build_id[i]);
}
free(build_id);
printf("\n");
return 0;
}