2013-09-24 26 views
2

我一直試圖通過使用libelf的elfutils包來獲取段的地址。從我的節目摘錄:ELF:混淆使用elfutils正確獲取段的地址

fd = open(argv[1], O_RDONLY); 
if (fd < 0) { 
     printf("Error: file %s cannot be opened\n", argv[1]); 
     goto out_ret; 
} 
elf_version(EV_CURRENT); 
elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); 
if (!elf){ 
     printf("Error: elf_begin\n"); 
goto out_close; 
} 

if (gelf_getehdr(elf, &ehdr) == NULL) { 
     printf("Error : gelf_getehdr\n"); 
     goto out_close; 
} 

if (elf_getshdrstrndx(elf, &shstrndx) != 0) { 
     printf("Error : elf_getshdrstrndx\n"); 
goto out_close; 
} 

while ((scn = elf_nextscn(elf, scn)) != NULL) { 
     gelf_getshdr(scn, &shdr); 
name = elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name); 
     printf("name = %s\n, addr = 0x%x\n", name, (unsigned)shdr.sh_addr); 
} 

但是,當我調用該程序與/lib64/libc.so.6或其他libpthread.so,等等,我得到這個作爲輸出:

name = .note.gnu.build-id, addr = 0x47a00270 
name = .note.ABI-tag, addr = 0x47a00294 
name = .gnu.hash, addr = 0x47a002b8 
name = .dynsym, addr = 0x47a03cd8 
name = .dynstr, addr = 0x47a10b48 
name = .gnu.version, addr = 0x47a16338 
name = .gnu.version_d, addr = 0x47a17470 
name = .gnu.version_r, addr = 0x47a17758 
name = .rela.dyn, addr = 0x47a17788 
name = .rela.plt, addr = 0x47a1efd0 
name = .plt, addr = 0x47a1f0e0 
name = .text, addr = 0x47a1f1a0 
name = __libc_freeres_fn, addr = 0x47b60960 
name = __libc_thread_freeres_fn, addr = 0x47b62000 
name = .rodata, addr = 0x47b62300 
name = .stapsdt.base, addr = 0x47b7cc10 
name = .interp, addr = 0x47b7cc20 
name = .eh_frame_hdr, addr = 0x47b7cc3c 
name = .eh_frame, addr = 0x47b83478 
name = .gcc_except_table, addr = 0x47ba97fc 
name = .hash, addr = 0x47ba9bc0 
name = .tdata, addr = 0x47dad6f0 
name = .tbss, addr = 0x47dad700 
name = .init_array, addr = 0x47dad700 

當我使用readelf找出這些部分的地址,我找到了地址的區別:

Section Headers: 
    [Nr] Name    Type    Address   Offset 
     Size    EntSize   Flags Link Info Align 
    [ 0]     NULL    0000000000000000 00000000 
     0000000000000000 0000000000000000   0  0  0 
    [ 1] .note.gnu.build-i NOTE    0000003b47a00270 00000270 
     0000000000000024 0000000000000000 A  0  0  4 
    [ 2] .note.ABI-tag  NOTE    0000003b47a00294 00000294 
     0000000000000020 0000000000000000 A  0  0  4 
    [ 3] .gnu.hash   GNU_HASH   0000003b47a002b8 000002b8 
     0000000000003a20 0000000000000000 A  4  0  8 
    [ 4] .dynsym   DYNSYM   0000003b47a03cd8 00003cd8 
     000000000000ce70 0000000000000018 A  5  3  8 
    [ 5] .dynstr   STRTAB   0000003b47a10b48 00010b48 
     00000000000057ef 0000000000000000 A  0  0  1 
    [ 6] .gnu.version  VERSYM   0000003b47a16338 00016338 
     0000000000001134 0000000000000002 A  4  0  2 
    [ 7] .gnu.version_d VERDEF   0000003b47a17470 00017470 
     00000000000002e4 0000000000000000 A  5 21  8 
    [ 8] .gnu.version_r VERNEED   0000003b47a17758 00017758 
     0000000000000030 0000000000000000 A  5  1  8 
    [ 9] .rela.dyn   RELA    0000003b47a17788 00017788 
     0000000000007848 0000000000000018 A  4  0  8 
    [10] .rela.plt   RELA    0000003b47a1efd0 0001efd0 
     0000000000000108 0000000000000018 A  4 11  8 
    [11] .plt    PROGBITS   0000003b47a1f0e0 0001f0e0 
     00000000000000c0 0000000000000010 AX  0  0  16 

的地址是不同的。對於例如.note.gnu.build-id,假設我的輸出爲addr = 0x47a00270並且在readelf的情況下具有地址3b47a00270。我使用的是fedora 18,我的m/c是64位。我不明白原因。任何人都可以解釋我的理由。提前致謝。

回答

2

當您投射到無符號像這樣並且您在64位二進制文​​件上運行時,您只打印出32位值 - 嘗試使用地址格式字符串%p - 它應該允許您正確打印指針。儘管現在我寫了這些,但我並不完全贊同通用的精靈庫,所以如果您需要同時支持32位和64位代碼,那麼您需要有條件地打印出這些基於這些值的數據在精靈代碼的位模式下,即如果它是32位代碼,則打印32位地址,如果它是64位代碼,則打印64位地址。

只支持64位代碼,並假設你正在構建你的代碼在64位模式,您需要更改打印的代碼來執行:

while ((scn = elf_nextscn(elf, scn)) != NULL) { 
    gelf_getshdr(scn, &shdr); 
    name = elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name); 
    printf("name = %s\n, addr = 0x%p\n", name, (void *)shdr.sh_addr); 
} 

如果你正在運行的32位程序你需要使用顯式強制轉換爲uint64_t和打印PRIx64inttypes.h

printf("name = %s\n, addr = 0x" PRIx64 "\n", name, (uint64_t)shdr.sh_addr); 

懶在我們中間將轉換爲unsigned long long和打印作爲%llx

+0

謝謝你的工作......是的,我犯了打印只有32位值的錯誤。正確,我需要有條件地打印32和64位的值。非常感謝您的幫助。 –