ELF字符串表分析

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);
}

参考链接:

String Table

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值