ELF文件格式分析

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ELF文件格式分析相关的知识,希望对你有一定的参考价值。

要求:

1.分析文件头。

 

2.通过文件头找到section header table,理解其内容。

 

3.通过section header table找到各section。

 

4.理解常见的.text .strtab .symtab .rodata 等section。

 

5.报告独立完成,格式规范。

一、基础知识

        ELF全称Executable and Linkable Format,可执行连接格式,ELF格式的文件用于存储Linux程序。 学习ELF格式,ELF是一种对象文件的格式,用于定义不同类型的对象文件(Object files)中都放了什么东西、以及都以什么样的格式去放这些东西。编译器生成的目标文件时,会在文件的开始处会有一个elf头,描绘了整个文件的组织结构。它还包括很多节(section)。这些节有的是系统定义好的,有些是用户在文件在通过.section命令自定义的,链接器会将多个输入目标文件中的相同的节合并。

二、分析学习

 

以自编简单程序一个“ElF_1.c”为例

#include <stdio.h>
int f(int x){
    return g(x);
}

int main(void){
    static int a=7;
    char str[]="变量为:";
    printf("%s%d",str,f(a)+1);
    return f(a)+1;
}

使用下面命令来编译  gcc -c ELF_1.c  –o  ELF_1.o 

再使用下面命令来显示生成的目标文件ELF_1.o的类型    file ELF_1.o

技术分享

技术分享

ELF_1.o是一个可重定位文件

使用命令ls -l ELF_1.o查看ELF_1.o的大小

 技术分享

根据输出结果可以知道,ELF_1.o大小为1944字节

使用下面命令hexdump -x ELF_1.o 来用16进制的数字来显示ELF_1.o的内容

使用下面命令objdump –x ELP_1.o来显示ELF_1.o中各个段以及符号表的相关信息

技术分享

也可以用命令readelf –a ELF_1.o来查看各个段信息

技术分享

对应要求通过文件头,找到section header table,又通过section header table找到各section

技术分享

 

段表Section header table

技术分享

符号表 Symbol table

 

技术分享

三、文件内容分析

ELF头与section table的数据结构分析

 

1.ElF文件头(定义在/usr/include/elf.h)//64位的系统ELF文件头包括以下两个部分

 

#define EI_NIDENT (16)
typedef struct
{
  unsigned char    e_ident[EI_NIDENT];    /* Magic number and other info */
  Elf32_Half    e_type;            /* Object file type */
  Elf32_Half    e_machine;        /* Architecture */
  Elf32_Word    e_version;        /* Object file version */
  Elf32_Addr    e_entry;        /* Entry point virtual address */
  Elf32_Off    e_phoff;        /* Program header table file offset */
  Elf32_Off    e_shoff;        /* Section header table file offset */
  Elf32_Word    e_flags;        /* Processor-specific flags */
  Elf32_Half    e_ehsize;        /* ELF header size in bytes */
  Elf32_Half    e_phentsize;        /* Program header table entry size */
  Elf32_Half    e_phnum;        /* Program header table entry count */
  Elf32_Half    e_shentsize;        /* Section header table entry size */
  Elf32_Half    e_shnum;        /* Section header table entry count */
  Elf32_Half    e_shstrndx;        /* Section header string table index */
} Elf32_Ehdr;

typedef struct
{
  unsigned char    e_ident[EI_NIDENT];    /* Magic number and other info */
  Elf64_Half    e_type;            /* Object file type */
  Elf64_Half    e_machine;        /* Architecture */
  Elf64_Word    e_version;        /* Object file version */
  Elf64_Addr    e_entry;        /* Entry point virtual address */
  Elf64_Off    e_phoff;        /* Program header table file offset */
  Elf64_Off    e_shoff;        /* Section header table file offset */
  Elf64_Word    e_flags;        /* Processor-specific flags */
  Elf64_Half    e_ehsize;        /* ELF header size in bytes */
  Elf64_Half    e_phentsize;        /* Program header table entry size */
  Elf64_Half    e_phnum;        /* Program header table entry count */
  Elf64_Half    e_shentsize;        /* Section header table entry size */
  Elf64_Half    e_shnum;        /* Section header table entry count */
  Elf64_Half    e_shstrndx;        /* Section header string table index */
} Elf64_Ehdr;

 

       从上表可以看出32位系统和64位系统下ELF头的结构基本相同,不同的是两种结构中的某个成员字段占用字节个数有所变化。比如e_entry由32位下占4个字节的Elf32_Addr变为64位下占8个字节的Elf64_Addr,这是因为两种系统下CPU寻址能力不同造成的。同理,文件偏移也从4字节的Elf32_Off变为8字节的Elf64_Off。有些成员字段虽然类型声名从Elf32_XXXX变成了Elf64_XXXX,该域所占的字节个数并未改变。如Elf32_Half和Elf64_Half都占两个字节,Elf32_Word、Elf32_Sword、Elf64_Word、Elf64_Sword全都是4个字节。

      因此,我们可以发现32位系统的ELF头文件大小是52字节,但是64位系统的ELF头文件变成了64字节!!!(十进制的64用16进制表示是40,因此下图白色部分是ELF文件头的信息)

      注意:由于intel及其兼容处理器使用小端法,此处的-x命令选项是一次性输出两个字节,457f实际表示为7f45,464c实际上是4c46,依次类推。

 技术分享

第一行:

(对应e_ident[EI_NIDENT]):实际表示内容为7f45 4c46 0201 0100 0000 0000 0000 0000。

前四个字节7f45 4c46(0x45,0x4c,0x46是’e‘,‘l‘,‘f‘对应的ASCⅡ)是一个魔数(magic number),表示这是一个ELF对象,接下来的一个字节02表示是一个64位对象(32位的对象是01),再接下来的一个字节01表示采用小端法表示,再接下来的一个字节01表示文件头版本,剩下的默认都设置为0。

第二行:

e_type(两个字节)值为0x0001,表示是一个重定位文件。

e_machine(两个字节)值为0x003e,表示是X86-64的处理器体系结构。

e_version(四个字节)值为0x00000001,表示是当前版本。

e_entry(八个字节)值为0x0000000000000000,表示没有入口点。

第三行:

e_phoff(八个字节)值为0x0000000000000000,表示没有程序头表。

e_shoff(八个字节)值为0x0000000000000458,表示段表的偏移地址。

第四行:

e_flags(四个字节)值为0x00000000,表示未知处理器特定标志(#define EF_SH_UNKNOWN 0x0);e_ehsize(两个字节)值为0x0040,表示elf文件头大小;

e_phentsize(两个字节)值均为0x0000,因为重定位文件没有程序头表。

e_phnum(两个字节)的值为0x0000,因为重定位文件没有程序头表。

e_ehentsize(两个字节)值为0x0040表示段头大小为64个字节(由这里知道section header table里面每个header的大小)。

e_shnum(两个字节)值为0x000d,表示段表入口有13个(由这里知道段表有13个段)。

e_shstrndx(两个字节)值为0x000a,表示段名串表的在段表中的索引号(由这里知道.shstrtab段(符号表)的信息在段表的索引号是10)。

2.段表 (section table)的分析   //64位的系统段表包括以下两个部分

段入口的类型定义如下(/usr/include/elf.h)

 

typedef struct
{
  Elf32_Word    sh_name;        /* Section name (string tbl index) */
  Elf32_Word    sh_type;        /* Section type */
  Elf32_Word    sh_flags;        /* Section flags */
  Elf32_Addr    sh_addr;        /* Section virtual addr at execution */
  Elf32_Off    sh_offset;        /* Section file offset */
  Elf32_Word    sh_size;        /* Section size in bytes */
  Elf32_Word    sh_link;        /* Link to another section */
  Elf32_Word    sh_info;        /* Additional section information */
  Elf32_Word    sh_addralign;        /* Section alignment */
  Elf32_Word    sh_entsize;        /* Entry size if section holds table */
} Elf32_Shdr;

typedef struct
{
  Elf64_Word    sh_name;        /* Section name (string tbl index) */
  Elf64_Word    sh_type;        /* Section type */
  Elf64_Xword    sh_flags;        /* Section flags */
  Elf64_Addr    sh_addr;        /* Section virtual addr at execution */
  Elf64_Off    sh_offset;        /* Section file offset */
  Elf64_Xword    sh_size;        /* Section size in bytes */
  Elf64_Word    sh_link;        /* Link to another section */
  Elf64_Word    sh_info;        /* Additional section information */
  Elf64_Xword    sh_addralign;        /* Section alignment */
  Elf64_Xword    sh_entsize;        /* Entry size if section holds table */
} Elf64_Shdr;

 

与文件头大小变化方式同理,我们可以发现32位系统的段表定义了每个段头大小是28H字节,但是64位系统变成了40H字节即64个字节。

以上两个表涉及两个数据结构,ELF文件头的数据结构存储在位偏移为0x 0000000000000000-0000000000000039之间,一共40H(十进制为64)个字节,而段表存储在0x00000000000001c8-0000000000000340之间,一共40H*13=340H字节。

 

3.符号表的数据结构分析

符号表入口的类型定义如下(/usr/include/elf.h)

/* Symbol table entry.  */
typedef struct
{
  Elf32_Word    st_name;        /* Symbol name (string tbl index) */
  Elf32_Addr    st_value;        /* Symbol value */
  Elf32_Word    st_size;        /* Symbol size */
  unsigned char    st_info;        /* Symbol type and binding */
  unsigned char    st_other;        /* Symbol visibility */
  Elf32_Section    st_shndx;        /* Section index */
} Elf32_Sym;
typedef struct
{
  Elf64_Word    st_name;        /* Symbol name (string tbl index) */
  unsigned char    st_info;        /* Symbol type and binding */
  unsigned char st_other;        /* Symbol visibility */
  Elf64_Section    st_shndx;        /* Section index */
  Elf64_Addr    st_value;        /* Symbol value */
  Elf64_Xword    st_size;        /* Symbol size */
} Elf64_Sym;

该结构大小为16个字节,,所以总共可以分成16个符号(有些符号未使用)。

32位系统的符号表结构体大小是16个字节,但是64位系统的符号表结构体大小变成了22个字节,而整个符号表段的大小为168H(360D)个字节,360=22*16,所以总共可以分成16个符号(有些符号未使用)这16个符号刚好和前面readelf –s ELF_1.o命令输出的符号表(15个符号,第一个符号为空,所以被忽略)结果相对应:

技术分享

符号表在ELF文件中的具体内容见下文。

找到并分析常见的section

技术分享

.text section

.text section是可执行指令的集合, .data 和.text 都是属于PROGBITS类型的section,这是将来要正常运行的程序和代码。

查询段表可知.text section的位偏移为0x0000040,size=98H,使用hexdump -x ELF_1.o命令可以查看.text section的内容,如下图。

 技术分享

使用命令objdump –d ELF_1.o对ELF_1.o进行反汇编,得到的汇编代码对应ELF_1.o的ELF文件

技术分享

技术分享

 .data section

.data是初始化后数据的集合,.data section 的位偏移为00000d8,size=4H,红框内的值是初始化的数据,即ELF_1.c中a=7的反映。

技术分享

.symtab section

symtab section存放所有section中定义的的符号名字,如 "data_items","start_loop" 。.symtab是属于SYMTAB类型的section,它描述了.strtab中的符号在"内存"中对应的"内存地址",当然这里的还不是真正的内存地址,只是一个偏移量,等到链接之后就是真正的了。 

.strtab section的位偏移=0x0000168,size=168H

技术分享

 .strtab section

.shstrtab和.strtab属于STRTAB类型的section,可以在文件中看到,它们都存着字符串,shstrtab存的是section的名字,而.strtab存的是符号的名字(符号表示一个固定的内存地址)。下图显示的是.strtab section.

.strtab section的位偏移=00002d0,size=2fH

技术分享

 

以上是关于ELF文件格式分析的主要内容,如果未能解决你的问题,请参考以下文章

Linux及安全实践四——ELF文件格式分析

ELF文件格式分析

ELF文件格式分析

ELF文件格式分析

Linux内核分析——ELF文件格式分析

Android 逆向ELF 文件格式 ( ELF 文件简介 | ELF 文件结构 )