ELF文件解析

来源:互联网 发布:软件项目阶段性报告 编辑:程序博客网 时间:2024/06/11 15:39

最近项目需要,要做一个小工具用来读取一堆库文件的版本信息,研究了下ELF文件。

实现思路:读取一个库文件,查找库的所有对外接口,如果接口名中带有Version字段,就通过dlsym系列函数读取版本信息。

以下只是学习ELF文件格式的记录。


在linux系统头文件elf.h能找到ELF文件格式相关定义,ELF的组成部分包括:

1、ELF头 (ELF Header)

2、程序段头(Program Header)

3、程序段(Program segment)

4、节区头(section Header)

5、节区(section)


当然不是每个ELF文件都完整的包括以上内容

1、ELF头

/* The ELF file header.  This appears at the start of every ELF file.  */#define EI_NIDENT (16)typedef struct{  unsigned chare_ident[EI_NIDENT];/* Magic number and other info */  Elf32_Halfe_type;/* Object file type */  Elf32_Halfe_machine;/* Architecture */  Elf32_Worde_version;/* Object file version */  Elf32_Addre_entry;/* Entry point virtual address */  Elf32_Offe_phoff;/* Program header table file offset */  Elf32_Offe_shoff;/* Section header table file offset */  Elf32_Worde_flags;/* Processor-specific flags */  Elf32_Halfe_ehsize;/* ELF header size in bytes */  Elf32_Halfe_phentsize;/* Program header table entry size */  Elf32_Halfe_phnum;/* Program header table entry count */  Elf32_Halfe_shentsize;/* Section header table entry size */  Elf32_Halfe_shnum;/* Section header table entry count */  Elf32_Halfe_shstrndx;/* Section header string table index */} Elf32_Ehdr;typedef struct{  unsigned chare_ident[EI_NIDENT];/* Magic number and other info */  Elf64_Halfe_type;/* Object file type */  Elf64_Halfe_machine;/* Architecture */  Elf64_Worde_version;/* Object file version */  Elf64_Addre_entry;/* Entry point virtual address */  Elf64_Offe_phoff;/* Program header table file offset */  Elf64_Offe_shoff;/* Section header table file offset */  Elf64_Worde_flags;/* Processor-specific flags */  Elf64_Halfe_ehsize;/* ELF header size in bytes */  Elf64_Halfe_phentsize;/* Program header table entry size */  Elf64_Halfe_phnum;/* Program header table entry count */  Elf64_Halfe_shentsize;/* Section header table entry size */  Elf64_Halfe_shnum;/* Section header table entry count */  Elf64_Halfe_shstrndx;/* Section header string table index */} Elf64_Ehdr;

Elf32_Ehdr是32位系统的ELF文件头,Elf64_Ehdr为64位系统。

结构体内的解析可以在网上提供的文档找到详细的解释:http://pan.baidu.com/s/1hqkteF6

这里只讲解几个关键的:

e_phoff: 程序段头开始位置在文件的偏移

e_phnum:程序段头 个数

e_phentsize每一个程序段头大小

e_shoff节区头开始位置在文件偏移

e_shnum节区头个数

e_shentsize每一个节区头大小

e_shstrndx节区头名字对应的字符表的处于节区的位置。

这个有点不好理解。每一个节区都存储的一些内容,另外每一个节区都有一个节区头,每一个节区头都有对应的名字。

这个值是一个数字比如27,意思就是第27个节区存储的是节区头名字。


2、程序段头结构体

typedef struct{  Elf32_Wordp_type;/* Segment type */  Elf32_Offp_offset;/* Segment file offset */  Elf32_Addrp_vaddr;/* Segment virtual address */  Elf32_Addrp_paddr;/* Segment physical address */  Elf32_Wordp_filesz;/* Segment size in file */  Elf32_Wordp_memsz;/* Segment size in memory */  Elf32_Wordp_flags;/* Segment flags */  Elf32_Wordp_align;/* Segment alignment */} Elf32_Phdr;

在ELF文件头有指明e_phoff对应这程序头的偏移量,偏移量开始你就能读取到e_phnum个结构段头。

可以根据程序段头找到对应的程序段,程序段头对应的解析查看相关文档,这里不做详细解析。


3、节区头结构体

typedef struct{  Elf64_Wordsh_name;/* Section name (string tbl index) */  Elf64_Wordsh_type;/* Section type */  Elf64_Xwordsh_flags;/* Section flags */  Elf64_Addrsh_addr;/* Section virtual addr at execution */  Elf64_Offsh_offset;/* Section file offset */  Elf64_Xwordsh_size;/* Section size in bytes */  Elf64_Wordsh_link;/* Link to another section */  Elf64_Wordsh_info;/* Additional section information */  Elf64_Xwordsh_addralign;/* Section alignment */  Elf64_Xwordsh_entsize;/* Entry size if section holds table */} Elf64_Shdr;

在ELF文件头有指明e_shoff对应这节区头的偏移量,偏移量开始你就能读取到e_shnum个结构段头。

根据每个节区头能找到对应的节区。以下讲解几个注意事项,其他的查找相关文档,

1..sh_offset是节区偏移量,sh_size是节区大小,根据这个能读到整个节区。

2..sh_name是一个数字,对应的节区名字符表的位置。也就是说需要先把elf文件头里面指明的e_shstrndx节区读到buffer,然后名字字符串就等于buffer+sh_name.

3..sh_type为节区类型,例如字符串节区为SHT_STRTAB,动态链接符号表节区为:SHT_DYNSYM ,符号表:SHT_SYMTAB 
如果你想找到ELF文件对应的函数接口,需要在动态链接符号表节区寻找,就是sh_type为SHT_DYNSYM而且sh_name为.dynsym

4、节区

除了前面说到的字符串节区外,这里在讲解下符号表节区,以.dynsym为例

符号表结构体为:

typedef struct{  Elf32_Wordst_name;/* Symbol name (string tbl index) */  Elf32_Addrst_value;/* Symbol value */  Elf32_Wordst_size;/* Symbol size */  unsigned charst_info;/* Symbol type and binding */  unsigned charst_other;/* Symbol visibility */  Elf32_Sectionst_shndx;/* Section index */} Elf32_Sym;

需要注意的是,符号表的名字需要从他们对应名字的节区寻找,例如.dynsym对应的名字字符节区sh_name为.dynstr和sh_type为SHT_STRTAB

在st_info中可以找到该符号的意义

字符表类型:ELF32_ST_TYPE(st_info)

字符表特征:ELF32_ST_BIND(st_info)

通过相关文档可以查找到。

需要找库的对外接口,ELF32_ST_TYPE(st_info) == STT_FUNC  ELF32_ST_BIND(st_info) == STB_GLOBAL

另外st_shndx>0


附上实现代码:

/* * elf.c * *  Created on: 2015-3-31 *      Author: Young * */#include <errno.h>#include <sys/types.h>#include <sys/stat.h>#include <dirent.h>#include <sys/stat.h>#include <elf.h>#include <stdio.h>#include <fcntl.h>#include <string.h>#include <stdlib.h>#include <dirent.h>#include <dlfcn.h>#define DEBUG#ifdef DEBUG#define DBPRINTF printf#else#define DBPRINTF#endifstatic const char elfFlag[4] = {0x7f, 'E', 'L', 'F'};static int showSysPlatform(Elf32_Ehdr* elfh){DBPRINTF("Machine:\t\t\t\t");switch(elfh->e_machine) {case EM_ARM:DBPRINTF("ARM\n");break;case EM_MIPS:DBPRINTF("MIPS R3000 big-endian\n");break;case EM_MIPS_RS3_LE:DBPRINTF("MIPS R3000 little-endian\n");break;default :DBPRINTF("Other:%d\n", elfh->e_machine);}DBPRINTF("File Type:\t\t\t\t");switch(elfh->e_type) {case ET_NONE:DBPRINTF("No file type\n");break;case ET_REL:DBPRINTF("Relocatable file\n");break;case ET_EXEC:DBPRINTF("Executable file\n");break;case ET_DYN:DBPRINTF("Shared object file\n");break;case ET_CORE:DBPRINTF("Core file\n");break;default :DBPRINTF("Unknown Files\n");return 0;}return 1;}static void showProgramHeader(Elf32_Ehdr* elfh){DBPRINTF("Start of program headers:\t\t0x%x\n", elfh->e_phoff);DBPRINTF("Size of program headers:\t\t0x%x\n", elfh->e_phentsize);DBPRINTF("Number of program headers:\t\t%d\n", elfh->e_phnum);}static void showSectionHeader(Elf32_Ehdr* elfh){DBPRINTF("Start of section headers:\t\t0x%x\n", elfh->e_shoff);DBPRINTF("Size of section headers:\t\t0x%x\n", elfh->e_shentsize);DBPRINTF("Number of section headers:\t\t%d\n", elfh->e_shnum);DBPRINTF("Section header string table index:\t%d\n", elfh->e_shstrndx);}static int checkELFHeader(Elf32_Ehdr* elfh){if (0 != memcmp (elfh, elfFlag, 4)) {DBPRINTF("Error, File is not ELF file\n");return 0;}DBPRINTF ("ELF file header information:\n");if (0 == showSysPlatform(elfh)) {return 0;}showProgramHeader(elfh);showSectionHeader(elfh);return 1;}static char* programType(Elf32_Word type){static char tmp[20] = {0};switch(type) {case PT_NULL:return "PT_NULL";case PT_LOAD:return "PT_LOAD";case PT_DYNAMIC:return "PT_DYNAMIC";case PT_INTERP:return "PT_INTERP";case PT_NOTE:return "PT_NOTE";case PT_SHLIB:return "PT_SHLIB";case PT_PHDR:return "PT_PHDR";case PT_TLS:return "PT_TLS";case PT_NUM:return "PT_NUM";case PT_LOPROC:return "PT_LOPROC";case PT_LOOS:return "PT_LOOS";case PT_GNU_EH_FRAME:return "PT_GNU_EH_FRAME";case PT_GNU_STACK:return "PT_GNU_STACK";case PT_GNU_RELRO:return "PT_GNU_RELRO";case PT_LOSUNW:return "PT_LOSUNW";default :snprintf(tmp, sizeof(tmp), "%x", type);break;}return tmp;}static int checkELFProgram(int fd, Elf32_Ehdr* elfh, Elf32_Phdr* elfp){int i = 0;DBPRINTF ("\nProgram Header Information:\n");DBPRINTF ("[%s]\t%-20s\t%s\t%s\t%s\t%-8s\t%-8s\t%s\t%s\n", "Nr", "Type", "Flg", "Off", "Size", "Vaddr", "Paddr", "Msize", "Alg");for (i = 0; i<elfh->e_phnum; i++) {DBPRINTF("[%02d]\t%-20s\t%d\t%d\t%d\t%08x\t%08x\t%d\t%d\n", i, programType(elfp[i].p_type), elfp[i].p_flags, elfp[i].p_offset,elfp[i].p_filesz, elfp[i].p_vaddr, elfp[i].p_paddr, elfp[i].p_memsz, elfp[i].p_align);}return 1;}static char* sectionType(Elf32_Word type){static char tmp[20] = {0};switch(type) {case SHT_NULL:return "SHT_NULL";case SHT_PROGBITS:return "SHT_PROGBITS";case SHT_SYMTAB:return "SHT_SYMTAB";case SHT_STRTAB:return "SHT_STRTAB";case SHT_RELA:return "SHT_RELA";case SHT_HASH:return "SHT_HASH";case SHT_DYNAMIC:return "SHT_DYNAMIC";case SHT_NOTE:return "SHT_NOTE";case SHT_NOBITS:return "SHT_NOBITS";case SHT_REL:return "SHT_REL";case SHT_DYNSYM:return "SHT_DYNSYM";case SHT_GNU_HASH:return "SHT_GNU_HASH";case SHT_GNU_verneed:return "SHT_GNU_verneed";case SHT_GNU_versym:return "SHT_GNU_versym";default:snprintf(tmp, sizeof(tmp), "0x%x", type);break;}return tmp;}static void getString(char* str, int len){int i = 0;int strflg = 1;DBPRINTF("%s\n", (str+i) ? (str+i) : "NULL");for (i=0; i<len; i++) {if (0 == strflg && 0 != *(str+i)) {DBPRINTF("%s\n", (str+i) ? (str+i) : "NULL");}strflg = *(str+i);}}static int showSymProc(Elf32_Sym *sym, int cnt, char *str){int i = 0;DBPRINTF("[%-2s]\t%-5s\t%-30s\t%s\t%s\t%s\t%-8s\n", "Nr", "Index", "Name", "BIND", "Type", "Size", "Value");for (i = 0; i<cnt; i++) {if (STB_GLOBAL == ELF32_ST_BIND(sym[i].st_info)&& STT_FUNC == ELF32_ST_TYPE(sym[i].st_info)&& sym[i].st_shndx) {DBPRINTF("[%02d]\t%04x\t%-30s\t%x\t%x\t%d\t%08x\n", i, sym[i].st_shndx, str+sym[i].st_name,ELF32_ST_BIND(sym[i].st_info), ELF32_ST_TYPE(sym[i].st_info), sym[i].st_size, sym[i].st_value);}}}static int checkELFSection(int fd, Elf32_Ehdr* elfh, Elf32_Shdr* elfs){Elf32_Sym *tmp_sym  = NULL;char *sectionstring = NULL;char *dynstring = NULL;char *tabstring = NULL;int   len = 0;int   i   = 0;if (NULL == (sectionstring = malloc(elfs[elfh->e_shstrndx].sh_size))) {DBPRINTF("Error, malloc section string fail\n");goto sectionErr;}lseek(fd, elfs[elfh->e_shstrndx].sh_offset, SEEK_SET);if (elfs[elfh->e_shstrndx].sh_size != read(fd, sectionstring, elfs[elfh->e_shstrndx].sh_size)) {DBPRINTF("Error, Get section string fail\n");goto sectionErr;}for (i=0; i<elfh->e_shnum; i++) {if (SHT_STRTAB == elfs[i].sh_type && 0 == strcmp(sectionstring+elfs[i].sh_name, ".dynstr")) {if (NULL == (dynstring = malloc(elfs[i].sh_size))) {DBPRINTF ("Error, Malloc dyn string fail\n");goto sectionErr;}lseek(fd, elfs[i].sh_offset, SEEK_SET);if (elfs[i].sh_size != read(fd, dynstring, elfs[i].sh_size)) {DBPRINTF("Error, Read dyn string fail\n");goto sectionErr;}}if (SHT_STRTAB == elfs[i].sh_type && 0 == strcmp(sectionstring+elfs[i].sh_name, ".strtab")) {if (NULL == (tabstring = malloc(elfs[i].sh_size))) {DBPRINTF ("Error, Malloc dyn string fail\n");goto sectionErr;}lseek(fd, elfs[i].sh_offset, SEEK_SET);if (elfs[i].sh_size != read(fd, tabstring, elfs[i].sh_size)) {DBPRINTF("Error, Read dyn string fail\n");goto sectionErr;}}}DBPRINTF("\nSection Header Information:\n");DBPRINTF("[%-2s]\t%-20s\t%-20s\t%-8s\t%-8s\t%s\t%s\t%-8s\t%s\t%s\n", "Nr", "Name", "Type", "Addr", "Off", "Size", "ES", "Flg", "Lnk", "AL");for (i=0; i<elfh->e_shnum; i++) {DBPRINTF("[%02d]\t%-20s\t%-20s\t%08x\t%08x\t%d\t%02x\t%08x\t%x\t%x\n",i, sectionstring + elfs[i].sh_name, sectionType(elfs[i].sh_type), elfs[i].sh_addr, elfs[i].sh_offset,elfs[i].sh_size, elfs[i].sh_entsize, elfs[i].sh_flags, elfs[i].sh_link, elfs[i].sh_addralign);}for (i=0; i<elfh->e_shnum; i++) {if (0 == strcmp(sectionstring + elfs[i].sh_name, ".dynsym")) {if (NULL == (tmp_sym = (Elf32_Sym*)malloc(elfs[i].sh_size))) {DBPRINTF("Error, Malloc dynsym fail!\n");goto sectionErr;}lseek(fd, elfs[i].sh_offset, SEEK_SET);if (elfs[i].sh_size != read(fd, tmp_sym, elfs[i].sh_size)) {DBPRINTF("Error, read dynsym Fail\n");goto sectionErr;}DBPRINTF("\nShow .dynsym information:\n");showSymProc(tmp_sym, (elfs[i].sh_size/elfs[i].sh_entsize), dynstring);free(tmp_sym);tmp_sym = NULL;}if (0 == strcmp(sectionstring + elfs[i].sh_name, ".symtab")) {if (NULL == (tmp_sym = (Elf32_Sym*)malloc(elfs[i].sh_size))) {DBPRINTF("Error, Malloc symtab fail!\n");goto sectionErr;}lseek(fd, elfs[i].sh_offset, SEEK_SET);if (elfs[i].sh_size != read(fd, tmp_sym, elfs[i].sh_size)) {DBPRINTF("Error, read dynsym Fail\n");goto sectionErr;}DBPRINTF("\nShow .symtab information:\n");showSymProc(tmp_sym, (elfs[i].sh_size/elfs[i].sh_entsize), tabstring);free(tmp_sym);tmp_sym = NULL;}}sectionErr:if (sectionstring) {free(sectionstring);sectionstring = NULL;}if (dynstring) {free(dynstring);dynstring = NULL;}if (tabstring) {free(tabstring);tabstring = NULL;}if (tmp_sym) {free(tmp_sym);tmp_sym = NULL;}return 1;}int main(int argc, char* argv[]){Elf32_Ehdr *elfHeader = NULL;Elf32_Phdr *elfProgram = NULL;Elf32_Shdr *elfSection = NULL;int elfFd = -1;int i     =  0;int ch    =  0;int len   =  0;if (2 != argc) {DBPRINTF("Usage: elf <FileName>\n");return 1;}DBPRINTF ("read file Name:%s\n", argv[1]);if ((elfFd = open(argv[1], O_RDONLY)) < 0) {DBPRINTF("Error, Open file %s error\n", argv[1]);goto errret;}/*分析头信息*/elfHeader = (Elf32_Ehdr*) malloc (sizeof(Elf32_Ehdr));if (NULL == elfHeader) {DBPRINTF("Error, malloc elfHeader fail\n");goto errret;}if (sizeof(Elf32_Ehdr) != read(elfFd, elfHeader, sizeof(Elf32_Ehdr))) {DBPRINTF("Error, Read elf file header Error\n");goto errret;}if (0 == checkELFHeader(elfHeader)) {goto errret;}/* 分析程序头表信息 */if (elfHeader->e_phoff > 0) {len = elfHeader->e_phentsize * elfHeader->e_phnum;elfProgram = (Elf32_Phdr*)malloc(len);if (NULL == elfProgram) {DBPRINTF("Error, malloc elfProgram fail\n");goto errret;}lseek(elfFd, elfHeader->e_phoff, SEEK_SET);if (len != read(elfFd, elfProgram, len)) {DBPRINTF("Error, Read elf Program Error\n");goto errret;}checkELFProgram(elfFd, elfHeader, elfProgram);}/* 分析节区头表信息 */if(elfHeader->e_shoff) {len = elfHeader->e_shentsize * elfHeader->e_shnum;elfSection = (Elf32_Shdr*)malloc(len);if (NULL == elfSection) {DBPRINTF("Error, malloc elfProgram fail\n");goto errret;}lseek(elfFd, elfHeader->e_shoff, SEEK_SET);if (len != read(elfFd, elfSection, len)) {DBPRINTF("Error, Read elf Section fail\n");}if (0 == checkELFSection(elfFd, elfHeader, elfSection)) {goto errret;}}errret:if (elfHeader) {free(elfHeader);elfHeader = NULL;}if (elfProgram) {free (elfProgram);elfProgram = NULL;}if (elfSection) {free(elfSection);elfSection = 0;}if (elfFd >= 0) {close (elfFd);elfFd = -1;}return 1;}



0 0
原创粉丝点击