Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android漫游記(2)---ELF可執行文件格式

Android漫游記(2)---ELF可執行文件格式

編輯:關於Android編程

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的文件格式有個總覽:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+PGltZyBzcmM9"/uploadfile/Collfiles/20140609/20140609091245187.jpg" alt="\">

左邊是靜態視圖,而右邊則是鏈接加載時的視圖,都是同一個文件的兩種狀態。

/* 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 
#include 
#include 
#include 
#include 
#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;ie_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!微笑

轉載請注明出處:生活秀

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved