当前位置: 代码迷 >> Android >> Android漫纪行(2)-ELF可执行文件格式
  详细解决方案

Android漫纪行(2)-ELF可执行文件格式

热度:70   发布时间:2016-04-28 05:18:54.0
Android漫游记(2)---ELF可执行文件格式

    ELF是类Unix类系统,当然也包括Android系统上的可执行文件格式(也包括.so和.o类文件)。可以理解为Android系统上的exe或者dll文件格式。理解ELF文件规范,是理解Android系统上进程加载、执行的前提。下面我们就来一步步了解这ELF到底是个啥玩意儿(以Arm 32 ELF格式为主)!当然,网上关于ELF的介绍已经非常多,最好的手册还是直接看ELF官方的手册,我这里只是对ELF的文件做个纲领性介绍,然后直奔主题,比如.GOT .PLT或者R_Arm_Jump_Slot,R_Arm_Relative之类的玩意儿微笑

    还是以libc.so为例来介绍,先看通过arm-linux-androideabi-readelf生成的ELF文件,很长,我们先看一个片段:


我们挑几个有意思的字段内容来说明。

首先ELF Header:顾名思义,这是所有ELF文件都有的”头“。里面包含了ELF文件的”纲领性“信息,如”Machine“表示当前CPU架构,该例为arm,”Start of Sections“表示”区(Section)头”的偏移字节数等等。

而Section Headers则列出了所有包含在文件中的Section区信息列表。如.data表示数据区,.text表示代码区等等。下面用一张图来对ELF的文件格式有个总览:


左边是静态视图,而右边则是链接加载时的视图,都是同一个文件的两种状态。

/* ELF Header */typedef struct elfhdr {	unsigned char	e_ident[EI_NIDENT]; /* ELF Identification */	Elf32_Half	e_type;		/* object file type */	Elf32_Half	e_machine;	/* machine */	Elf32_Word	e_version;	/* object file version */	Elf32_Addr	e_entry;	/* virtual entry point */	Elf32_Off	e_phoff;	/* program header table offset */	Elf32_Off	e_shoff;	/* section header table offset */	Elf32_Word	e_flags;	/* processor-specific flags */	Elf32_Half	e_ehsize;	/* ELF header size */	Elf32_Half	e_phentsize;	/* program header entry size */	Elf32_Half	e_phnum;	/* number of program header entries */	Elf32_Half	e_shentsize;	/* section header entry size */	Elf32_Half	e_shnum;	/* number of section header entries */	Elf32_Half	e_shstrndx;	/* section header table's "section 					   header string table" entry offset */} Elf32_Ehdr;

其中的e_shoff就是上面我们看到的“Start of section headers”,而e_shstrndx是指Sections的名字串在串表(String Table,也是一个Section)中的起始索引位置。

下面我们再看一下Section Header的定义:

/* Section Header */typedef struct {	Elf32_Word	sh_name;	/* name - index into section header					   string table section */	Elf32_Word	sh_type;	/* type */	Elf32_Word	sh_flags;	/* flags */	Elf32_Addr	sh_addr;	/* address */	Elf32_Off	sh_offset;	/* file offset */	Elf32_Word	sh_size;	/* section size */	Elf32_Word	sh_link;	/* section header table index link */	Elf32_Word	sh_info;	/* extra information */	Elf32_Word	sh_addralign;	/* address alignment */	Elf32_Word	sh_entsize;	/* section entry size */} Elf32_Shdr;
这个我们要详细看看,后面能用到:

字段:

sh_name:顾名思义,Section的名字,类型是Elf32_Word,实际上它是指向串表的索引值

sh_flags:类型。.dynsym的类型为DYNSYM表示该节区包含了要动态链接的符号等等

sh_addr:地址。该节区在内存中,相对于基址的偏移

sh_offset:偏移。表示该节区到文件头部的字节偏移。

sh_size:节区大小

sh_link:表示与当前section有link关系的section索引,不同类型的section,其解释不同。如上面的libc.so,其.dynsym的link为2,而2正好是.dynstr的索引,实际上就是动态符号串表的索引

sh_info:一些附加信息

sh_addralign:节区的地址对齐

sh_entsize:节区项的大小(bytes)

上面这么多乱七八糟的看起来很多,实际上记住一点就可以了:所有这些信息,都是linker在加载elf的时候要用到的“参考表”。

老习惯,我们直接写个elf读取的小程序,来验证下我们的理解。

/* *  elf32 reader *  Created on: 2014-6 *  Author: Chris.Z */#include <stdio.h>#include <stdlib.h>#include <elf.h>#include <errno.h>#include <fcntl.h>#ifdef __x86_64    #define Elf_Ehdr Elf64_Ehdr    #define Elf_Shdr Elf64_Shdr    #define Elf_Sym Elf64_Sym    #define Elf_Rel Elf64_Rela    #define ELF_R_SYM ELF64_R_SYM    #define REL_DYN ".rela.dyn"    #define REL_PLT ".rela.plt"#else    #define Elf_Ehdr Elf32_Ehdr    #define Elf_Shdr Elf32_Shdr    #define Elf_Sym Elf32_Sym    #define Elf_Rel Elf32_Rel    #define ELF_R_SYM ELF32_R_SYM    #define REL_DYN ".rel.dyn"    #define REL_PLT ".rel.plt"#endif#define LOG(...) printf(__VA_ARGS__);/** * lookup the start address of a specific module(libc.so...) within current process * return 0 if FAILED */static uint32_t get_module_base(pid_t pid, const char *module_path) {	FILE *fp = NULL;	char *pch = NULL;	char filename[32];	char line[512];	uint32_t addr = 0;	LOG("[+] get libc base...\n");	if (pid < 0)		snprintf(filename, sizeof(filename), "/proc/self/maps");	else		snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);	if ((fp = fopen(filename, "r")) == NULL) {		LOG("[-]open %s failed!", filename);		return 0;	}	while (fgets(line, sizeof(line), fp)) {		if (strstr(line, module_path)) {			pch = strtok(line, "-");			addr = strtoul(pch, NULL, 16);			break;		}	}	fclose(fp);	LOG("[+] libc base:0x%x...\n",addr);	return addr;}/** * read the elf header * return 0 if SUCCESS */static int read_header(int d, Elf_Ehdr **header)//read elf header structure{    *header = (Elf_Ehdr *)malloc(sizeof(Elf_Ehdr));    if (lseek(d, 0, SEEK_SET) < 0)//seek to the begin of file    {        free(*header);        return errno;    }    if (read(d, *header, sizeof(Elf_Ehdr)) <= 0)//read from begin,read sizof(Elf_Ehdr) bytes ==> header    {        free(*header);        return errno = EINVAL;    }    return 0;}/** * read the section header * return 0 if SUCCESS */static int read_section_table(int d, Elf_Ehdr const *header, Elf_Shdr **table)//read elf header,find section header base address{    size_t size;    if (NULL == header)        return EINVAL;    size = header->e_shnum * sizeof(Elf_Shdr);//section numbers and total size    *table = (Elf_Shdr *)malloc(size);    if (lseek(d, header->e_shoff, SEEK_SET) < 0)//point to section header,offset 0    {        free(*table);        return errno;    }    if (read(d, *table, size) <= 0)//read section header structure to **table    {        free(*table);        return errno = EINVAL;    }    return 0;}/** * read the string section table * return 0 if SUCCESS */static int read_string_table(int d, Elf_Shdr const *section, char const **strings){    if (NULL == section)//section == > .dynstr section        return EINVAL;    *strings = (char const *)malloc(section->sh_size);    if (lseek(d, section->sh_offset, SEEK_SET) < 0)    {        free((void *)*strings);        return errno;    }    if (read(d, (char *)*strings, section->sh_size) <= 0)//strings include all strings in .dynstr sections    {        free((void *)*strings);        return errno = EINVAL;    }    return 0;}int main(){    LOG("[+]Arm ELF32 reader...\n");    uint32_t lic_base = get_module_base(-1,"/system/lib/libc.so");    int descriptor = open("/system/lib/libc.so", O_RDONLY);//open libc.so,and return the handle    Elf_Ehdr *header = NULL;//elf header    Elf_Shdr *section_header = NULL;//section header array ptr    char const *strings = NULL;//string table ptr    read_header(descriptor,&header);    LOG("[+]libc.so elf header:\n");    LOG("[+]e_ident[EI_NIDENT]:   %s\n",header->e_ident);    LOG("[+]e_type:%d(ET_DYN:%d,DYN (Shared object file))\n",header->e_type,ET_DYN);    LOG("[+]e_machine:%d(EM_ARM:%d,Advanced RISC Machines)\n",header->e_machine,EM_ARM);    LOG("[+]e_shoff:%d bytes\n",header->e_shoff);    LOG("[+]libc.so section header:\n");    read_section_table(descriptor,header,§ion_header);    read_string_table(descriptor,§ion_header[header->e_shstrndx], &strings);//header->e_shstrndx ==>the index of string section header in section headers    int i = 0;    for(i = 0;i<header->e_shnum;++i)    {        LOG("Section[%d] name:%s,type:%d,addr:0x%x,offset:0x%x,size:%dbytes,etc...\n",i,&strings[section_header[i].sh_name],section_header[i].sh_type,section_header[i].sh_addr,section_header[i].sh_offset,section_header[i].sh_size);    }    close(descriptor);    return 0;}

我们看看运行结果:



对照看一下readelf生成的结果,果然如此,Enjoy IT!微笑

转载请注明出处:生活秀

  相关解决方案