String Table
String table sections hold null-terminated character sequences, commonly called strings. The object file uses these strings to represent symbol and section names. One references a string as an index into the string table section. The first byte, which is index zero, is defined to hold a null character. Likewise, a string table's last byte is defined to hold a null character, ensuring null termination for all strings. A string whose index is zero specifies either no name or a null name, depending on the context. An empty string table section is permitted; its section header's sh_size
member would contain zero. Non-zero indexes are invalid for an empty string table.
字符串表节(string table sections)中存储若干以空字符结尾的字符串序列,即通常我们说的字符串。在目标文件中,使用这些字符串来表示符号名称和节的名称。当需要使用这些字符串时,只需要提供字符串在字符串表中的索引值即可。规定字符串表的第一个字节,即索引值为0对应的字节中存储的是空字符。同样的,因为所有的字符串都是空字符结尾,所以字符串表的最后一个字节中存储的也是空字符。索引值为0的字符串代表的是没有名字或者名字为空,这个需要依据上下文来决定。一个空的字符串表是允许的,这种情况下,它的节头表表项成员sh_size数值为0,意思是该节大小为0,所以此时提供一个非零的字符串表索引值是非法的。
A section header's sh_name
member holds an index into the section header string table section, as designated by the e_shstrndx
member of the ELF header. The following figures show a string table with 25 bytes and the strings associated with various indexes.
一个目标文件中一般包含多个字符串表(.dynstr/.shstrtab/.strtab),其中.shstrtab节中存储的是所有节的名字字符串。节头表表项成员sh_name代表的就是该节在.shstrtab中的索引值,该字符串表所在节的节头表索引值由ELF头中的e_shstrndx成员指出。
下图展现的是一个长度为25字节的字符串表。
As the example shows, a string table index may refer to any byte in the section. A string may appear more than once; references to substrings may exist; and a single string may be referenced multiple times. Unreferenced strings also are allowed.
从上面的列子中我们可以看到:
(1)只要是合法的索引值,就可以访问该字符串表中的任何字节。
(2)一个字符串在字符串表中允许出现多次。
(3)可以使用合法的索引值访问一个字符串的子串。
(4)只要提供索引值,一个字符串当然可以引用多次。
(5)甚至允许从不被引用的字符串的存在。
以下程序用于输出ELF所有字符串表的内容(即sh_type为SHT_STRTAB),模仿readelf的--string-dump=xxx选项:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <link.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <elf.h>
#include <error.h>
#include <errno.h>
#include <assert.h>
static void print_strtbl(const char *start, size_t len)
{
assert(start);
const char *p = start;
size_t offset = 0;
for ( ;; ) {
(void)printf(" [%6x] %s\n", (unsigned int)offset, p + offset);
offset += strlen(p + offset) + 1;
if (offset >= len) break;
}
}
int main(int argc, char *argv[])
{
int fd;
char *file_mmbase;
struct stat file_status;
size_t fsize;
ElfW(Ehdr) *ehdr;
ElfW(Shdr) *shdrs;
size_t shnum, shstrndx;
if (argc != 2) {
error(EXIT_FAILURE, 0, "Usage: %s file-name", argv[0]);
}
if ((fd = open(argv[1], O_RDONLY)) < 0) {
error(EXIT_FAILURE, errno, "open %s failed", argv[1]);
}
if (fstat(fd, &file_status) < 0) {
error(EXIT_FAILURE, errno, "get file %s info err", argv[1]);
}
fsize = (size_t)file_status.st_size;
if ((file_mmbase = mmap(NULL, fsize, PROT_READ,
MAP_PRIVATE, fd, (off_t)0)) == MAP_FAILED) {
error(EXIT_FAILURE, errno, "mmap file %s err", argv[1]);
}
ehdr = (ElfW(Ehdr) *)file_mmbase;
shdrs = (ElfW(Shdr) *)(file_mmbase + ehdr->e_shoff);
shnum = ehdr->e_shnum == 0 ? shdrs[0].sh_size : ehdr->e_shnum;
shstrndx = ehdr->e_shstrndx == SHN_XINDEX ? shdrs[0].sh_link : ehdr->e_shstrndx;
for (size_t i = 0; i < shnum; i++) {
ElfW(Shdr) *shdr = &shdrs[i];
if (shdr->sh_type != SHT_STRTAB) continue;
(void)printf("String dump of section '%s':\n",
file_mmbase + shdrs[shstrndx].sh_offset + shdr->sh_name);
print_strtbl(file_mmbase + shdrs[i].sh_offset, shdrs[i].sh_size);
(void)printf("\n");
}
(void)munmap(file_mmbase, fsize);
(void)close(fd);
exit(EXIT_SUCCESS);
}
程序输出结果如下:
[00:49:39@astrol:/tmp]$ readelf --wide --section-headers print_strtbl | grep STRTAB
[ 6] .dynstr STRTAB 0000000000000468 000468 0000dd 00 A 0 0 1
[27] .strtab STRTAB 0000000000000000 002790 000322 00 0 0 1
[28] .shstrtab STRTAB 0000000000000000 002ab2 0000fe 00 0 0 1
[00:49:53@astrol:/tmp]$ readelf --string-dump=6 print_strtbl
String dump of section '.dynstr':
[ 1] libc.so.6
[ b] exit
[ 10] error
[ 16] putchar
[ 1e] __assert_fail
[ 2c] printf
[ 33] mmap
[ 38] strlen
[ 3f] __errno_location
[ 50] munmap
[ 57] close
[ 5d] open
[ 62] __cxa_finalize
[ 71] __libc_start_main
[ 83] __fxstat
[ 8c] GLIBC_2.2.5
[ 98] _ITM_deregisterTMCloneTable
[ b4] __gmon_start__
[ c3] _ITM_registerTMCloneTable
[00:50:06@astrol:/tmp]$ readelf --string-dump=27 print_strtbl
String dump of section '.strtab':
[ 1] crtstuff.c
[ c] deregister_tm_clones
[ 21] __do_global_dtors_aux
[ 37] completed.7696
[ 46] __do_global_dtors_aux_fini_array_entry
[ 6d] frame_dummy
[ 79] __frame_dummy_init_array_entry
[ 98] print_strtbl.c
[ a7] print_strtbl
[ b4] __PRETTY_FUNCTION__.4223
[ cd] __FRAME_END__
[ db] __init_array_end
[ ec] _DYNAMIC
[ f5] __init_array_start
[ 108] __GNU_EH_FRAME_HDR
[ 11b] _GLOBAL_OFFSET_TABLE_
[ 131] __libc_csu_fini
[ 141] putchar@@GLIBC_2.2.5
[ 156] __errno_location@@GLIBC_2.2.5
[ 174] _ITM_deregisterTMCloneTable
[ 190] _edata
[ 197] strlen@@GLIBC_2.2.5
[ 1ab] mmap@@GLIBC_2.2.5
[ 1bd] printf@@GLIBC_2.2.5
[ 1d1] __assert_fail@@GLIBC_2.2.5
[ 1ec] close@@GLIBC_2.2.5
[ 1ff] __libc_start_main@@GLIBC_2.2.5
[ 21e] __data_start
[ 22b] __gmon_start__
[ 23a] __dso_handle
[ 247] _IO_stdin_used
[ 256] __libc_csu_init
[ 266] __fxstat@@GLIBC_2.2.5
[ 27c] __bss_start
[ 288] munmap@@GLIBC_2.2.5
[ 29c] main
[ 2a1] error@@GLIBC_2.2.5
[ 2b4] open@@GLIBC_2.2.5
[ 2c6] __fstat
[ 2ce] exit@@GLIBC_2.2.5
[ 2e0] __TMC_END__
[ 2ec] _ITM_registerTMCloneTable
[ 306] __cxa_finalize@@GLIBC_2.2.5
[00:50:12@astrol:/tmp]$ readelf --string-dump=28 print_strtbl
String dump of section '.shstrtab':
[ 1] .symtab
[ 9] .strtab
[ 11] .shstrtab
[ 1b] .interp
[ 23] .note.ABI-tag
[ 31] .note.gnu.build-id
[ 44] .gnu.hash
[ 4e] .dynsym
[ 56] .dynstr
[ 5e] .gnu.version
[ 6b] .gnu.version_r
[ 7a] .rela.dyn
[ 84] .rela.plt
[ 8e] .init
[ 94] .plt.got
[ 9d] .text
[ a3] .fini
[ a9] .rodata
[ b1] .eh_frame_hdr
[ bf] .eh_frame
[ c9] .init_array
[ d5] .fini_array
[ e1] .dynamic
[ ea] .data
[ f0] .bss
[ f5] .comment
[00:50:15@astrol:/tmp]$ ./print_strtbl print_strtbl
String dump of section '.dynstr':
[ 0]
[ 1] libc.so.6
[ b] exit
[ 10] error
[ 16] putchar
[ 1e] __assert_fail
[ 2c] printf
[ 33] mmap
[ 38] strlen
[ 3f] __errno_location
[ 50] munmap
[ 57] close
[ 5d] open
[ 62] __cxa_finalize
[ 71] __libc_start_main
[ 83] __fxstat
[ 8c] GLIBC_2.2.5
[ 98] _ITM_deregisterTMCloneTable
[ b4] __gmon_start__
[ c3] _ITM_registerTMCloneTable
String dump of section '.strtab':
[ 0]
[ 1] crtstuff.c
[ c] deregister_tm_clones
[ 21] __do_global_dtors_aux
[ 37] completed.7696
[ 46] __do_global_dtors_aux_fini_array_entry
[ 6d] frame_dummy
[ 79] __frame_dummy_init_array_entry
[ 98] print_strtbl.c
[ a7] print_strtbl
[ b4] __PRETTY_FUNCTION__.4223
[ cd] __FRAME_END__
[ db] __init_array_end
[ ec] _DYNAMIC
[ f5] __init_array_start
[ 108] __GNU_EH_FRAME_HDR
[ 11b] _GLOBAL_OFFSET_TABLE_
[ 131] __libc_csu_fini
[ 141] putchar@@GLIBC_2.2.5
[ 156] __errno_location@@GLIBC_2.2.5
[ 174] _ITM_deregisterTMCloneTable
[ 190] _edata
[ 197] strlen@@GLIBC_2.2.5
[ 1ab] mmap@@GLIBC_2.2.5
[ 1bd] printf@@GLIBC_2.2.5
[ 1d1] __assert_fail@@GLIBC_2.2.5
[ 1ec] close@@GLIBC_2.2.5
[ 1ff] __libc_start_main@@GLIBC_2.2.5
[ 21e] __data_start
[ 22b] __gmon_start__
[ 23a] __dso_handle
[ 247] _IO_stdin_used
[ 256] __libc_csu_init
[ 266] __fxstat@@GLIBC_2.2.5
[ 27c] __bss_start
[ 288] munmap@@GLIBC_2.2.5
[ 29c] main
[ 2a1] error@@GLIBC_2.2.5
[ 2b4] open@@GLIBC_2.2.5
[ 2c6] __fstat
[ 2ce] exit@@GLIBC_2.2.5
[ 2e0] __TMC_END__
[ 2ec] _ITM_registerTMCloneTable
[ 306] __cxa_finalize@@GLIBC_2.2.5
String dump of section '.shstrtab':
[ 0]
[ 1] .symtab
[ 9] .strtab
[ 11] .shstrtab
[ 1b] .interp
[ 23] .note.ABI-tag
[ 31] .note.gnu.build-id
[ 44] .gnu.hash
[ 4e] .dynsym
[ 56] .dynstr
[ 5e] .gnu.version
[ 6b] .gnu.version_r
[ 7a] .rela.dyn
[ 84] .rela.plt
[ 8e] .init
[ 94] .plt.got
[ 9d] .text
[ a3] .fini
[ a9] .rodata
[ b1] .eh_frame_hdr
[ bf] .eh_frame
[ c9] .init_array
[ d5] .fini_array
[ e1] .dynamic
[ ea] .data
[ f0] .bss
[ f5] .comment
以下是利用libelf库来输出字符串表的内容:
#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#include <fcntl.h>
#include <gelf.h>
#include <stdint.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
static void print_strtbl(Elf_Scn *scn, size_t len)
{
assert(scn);
Elf_Data *data = NULL;
size_t offset = 0;
for ( ;; ) {
if ((data = elf_getdata(scn, data)) == NULL) break;
const char *p = data->d_buf;
while (p < (char *)data->d_buf + data->d_size) {
(void)printf(" [%6x] %s\n", (unsigned int)offset, p);
offset += strlen(p) + 1;
p += strlen(p) + 1;
if (offset >= len) break;
}
}
}
int main(int argc, const char *argv[])
{
int fd, class;
Elf *pelf = NULL;
Elf_Scn *scn = NULL;
GElf_Shdr shdr;
size_t shstrndx;
if (argc != 2)
errx(EXIT_FAILURE, "usage: %s file-name", argv[0]);
if (elf_version(EV_CURRENT) == EV_NONE)
errx(EXIT_FAILURE, "ELF library initializztion "
"failed: %s", elf_errmsg(-1));
if ((fd = open(argv[1], O_RDONLY, 0)) < 0)
errx(EXIT_FAILURE, "open \"%s\" failed", argv[1]);
if (!(pelf = elf_begin(fd, ELF_C_READ, NULL)))
errx(EXIT_FAILURE, "elf_begin() failed: %s", elf_errmsg(-1));
if (elf_kind(pelf) != ELF_K_ELF)
errx(EXIT_FAILURE, "\"%s\" is not an ELF object.", argv[1]);
// get elf class ()
if ((class = gelf_getclass(pelf)) == ELFCLASSNONE)
errx(EXIT_FAILURE, "getclass() failed: %s.", elf_errmsg(-1));
// get shstrndx
if (elf_getshdrstrndx(pelf, &shstrndx) == -1) {
errx(EXIT_FAILURE, "getshdrstrndx() failed: %s.", elf_errmsg(-1));
}
while (scn = elf_nextscn(pelf, scn)) {
if (gelf_getshdr(scn, &shdr) != &shdr) {
errx(EXIT_FAILURE, "gelf_getshdr() failed: %s.", elf_errmsg(-1));
}
if (shdr.sh_type != SHT_STRTAB) continue;
(void)printf("String dump of section '%s':\n",
elf_strptr(pelf, shstrndx, shdr.sh_name));
print_strtbl(scn, shdr.sh_size);
printf("\n");
}
(void)elf_end(pelf);
(void)close(fd);
exit(EXIT_SUCCESS);
}
参考链接: