首页 » 漏洞 » ELF文件格式分析

ELF文件格式分析

 
文章目录

NDK开发最后生成的so文件是基于ELF文件格式。而so是共享目标文件,所以要想对so文件进行加密就必须了解ELF文件格式。

原文链接: ELF文件格式分析

0x00 简介

可执行链接格式(Executable and Linking Format)最初是由 UNIX 系统实验室(UNIX System Laboratories,USL)开发并发布的,作为应用程序二进制接口(Application Binary Interface,ABI)的一部分。工具接口标准(Tool Interface Standards,TIS)委员会将还 在发展的 ELF 标准选作为一种可移植的目标文件格式,可以在 32 位 Intel 体系结构上的 很多操作系统中使用。

目标文件有三种类型:

  • 可重定位文件(Relocatable File) .o)包含适合于与其他目标文件链接来创建可执行文件或者共享目标文件的代码和数据。

  • 可执行文件(Executable File) .exe) 包含适合于执行的一个程序,此文件规定了exec() 如何创建一个程序的进程映像。

  • 共享目标文件(Shared Object File) .so) 包含可在两种上下文中链接的代码和数据。首先链接编辑器可以将它和其它可重定位文件和共享目标文件一起处理, 生成另外一个目标文件。其次动态链接器(Dynamic Linker)可能将它与某 个可执行文件以及其它共享目标一起组合,创建进程映像。

目标文件全部是程序的二进制表示,目的是直接在某种处理器上直接执行。

0x01 ELF文件格式

目标文件格式

目标文件既要参与程序链接又要参与程序执行。出于方便性和效率考虑,目标文件

格式提供了两种并行视图,分别反映了这些活动的不同需求。

ELF文件格式分析

文件开始处是一个 ELF 头部(ELF Header),用来描述整个文件的组织。节区部 分包含链接视图的大量信息:指令、数据、符号表、重定位信息等等。

程序头部表(Program Header Table),如果存在的话,告诉系统如何创建进程映像。 用来构造进程映像的目标文件必须具有程序头部表,可重定位文件不需要这个表。

节区头部表(Section Heade Table)包含了描述文件节区的信息,每个节区在表中 都有一项,每一项给出诸如节区名称、节区大小这类信息。用于链接的目标文件必须包 含节区头部表,其他目标文件可以有,也可以没有这个表。

注意:尽管图中显示的各个组成部分是有顺序的,实际上除了 ELF 头部表以外, 其他节区和段都没有规定的顺序

目标文件中数据表示

目标文件格式支持 8 位字节/32 位体系结构。不过这种格式是可以扩展的,比如现在的64位机器,目标文件因此以某些机器独立的格式表达某些控制数据,使得能够以一种公共的方式来识别和解释其内容。目标文件中的其它数据使用目标处理器的编码结构,而不管文件在何种机器上创建。

ELF文件格式分析

目标文件中的所有数据结构都遵从“自然”大小和对齐规则。如果必要,数据结构可 以包含显式的补齐,例如为了确保 4 字节对象按 4 字节边界对齐。数据对齐同样适用于文件内部。

ELF Hearder部分

文件的最开始几个字节给出如何解释文件的提示信息。这些信息独立于处理器,也

独立于文件中的其余内容。ELF Header 部分可以用以下的数据结构表示:

/* 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;  typedef struct {     unsigned char    e_ident[EI_NIDENT];    /* Id bytes */     Elf64_Quarter    e_type;            /* file type */     Elf64_Quarter    e_machine;        /* machine type */     Elf64_Half    e_version;        /* version number */     Elf64_Addr    e_entry;        /* entry point */     Elf64_Off    e_phoff;        /* Program hdr offset */     Elf64_Off    e_shoff;        /* Section hdr offset */     Elf64_Half    e_flags;        /* Processor flags */     Elf64_Quarter    e_ehsize;        /* sizeof ehdr */     Elf64_Quarter    e_phentsize;        /* Program header entry size */     Elf64_Quarter    e_phnum;        /* Number of program headers */     Elf64_Quarter    e_shentsize;        /* Section header entry size */     Elf64_Quarter    e_shnum;        /* Number of section headers */     Elf64_Quarter    e_shstrndx;        /* String table index */ } Elf64_Ehdr;

其中,e_ident 数组给出了 ELF 的一些标识信息,这个数组中不同下标的含义如表所示:

ELF文件格式分析

这些索引访问包含以下数值的字节:

ELF文件格式分析

e_ident[EI_MAG0]~e_ident[EI_MAG3]即e_ident[0]~e_ident[3]被称为魔数(Magic Number),其值一般为0x7f,'E','L','F'。

e_ident[EI_CLASS](即e_ident[4])识别目标文件运行在目标机器的类别,取值可为三种值:ELFCLASSNONE(0)非法类别;ELFCLASS32(1)32位目标;ELFCLASS64(2)64位目标。

e_ident[EI_DATA](即e_ident[5]):给出处理器特定数据的数据编码方式。即大端还是小端方式。取值可为3种:ELFDATANONE(0)非法数据编码;ELFDATA2LSB(1)高位在前;ELFDATA2MSB(2)低位在前。

ELF Header 中各个字段的说明如表:

ELF文件格式分析

一个实际可执行文件的头文件头部形式如下:

$greadelf -h hello.so ELF 头:   Magic:  7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00   类别:                             ELF32   数据:                             2 补码,小端序 (little endian)   版本:                             1 (current)   OS/ABI:                           UNIX - System V   ABI 版本:                         0   类型:                             DYN (共享目标文件)   系统架构:                          ARM   版本:                             0x1   入口点地址:                       0x0   程序头起点:                       52 (bytes into file)   Start of section headers:        61816 (bytes into file)   标志:                            0x5000000, Version5 EABI   本头的大小:                       52 (字节)   程序头大小:                       32 (字节)   Number of program headers:       9   节头大小:                        40 (字节)   节头数量:                        24   字符串表索引节头:                 23

注:linux环境下可以利用命令readelf,mac需要安装binutils。用brew安装很方便,命令: brew update && brew install binutils

程序头部(Program Header)

可执行文件或者共享目标文件的程序头部是一个结构数组,每个结构描述了一个段 或者系统准备程序执行所必需的其它信息。目标文件的“段”包含一个或者多个“节区”, 也就是“段内容(Segment Contents)”。程序头部仅对于可执行文件和共享目标文件 有意义。

可执行目标文件在 ELF 头部的 e_phentsize 和 e_phnum 成员中给出其自身程序头部 的大小。程序头部的数据结构:

/* Program Header */ typedef struct {     Elf32_Word    p_type;        /* segment type */     Elf32_Off    p_offset;    /* segment offset */     Elf32_Addr    p_vaddr;    /* virtual address of segment */     Elf32_Addr    p_paddr;    /* physical address - ignored? */     Elf32_Word    p_filesz;    /* number of bytes in file for seg. */     Elf32_Word    p_memsz;    /* number of bytes in mem. for seg. */     Elf32_Word    p_flags;    /* flags */     Elf32_Word    p_align;    /* memory alignment */ } Elf32_Phdr;  typedef struct {     Elf64_Half    p_type;        /* entry type */     Elf64_Half    p_flags;    /* flags */     Elf64_Off    p_offset;    /* offset */     Elf64_Addr    p_vaddr;    /* virtual address */     Elf64_Addr    p_paddr;    /* physical address */     Elf64_Xword    p_filesz;    /* file size */     Elf64_Xword    p_memsz;    /* memory size */     Elf64_Xword    p_align;    /* memory & file alignment */ } Elf64_Phdr;

其中各个字段说明:

  • p_type 此数组元素描述的段的类型,或者如何解释此数组元素的信息。具体如下图。

  • p_offset 此成员给出从文件头到该段第一个字节的偏移。

  • p_vaddr 此成员给出段的第一个字节将被放到内存中的虚拟地址。

  • p_paddr 此成员仅用于与物理地址相关的系统中。因为 System V 忽略所有应用程序的物理地址信息,此字段对与可执行文件和共享目标文件而言具体内容是指定的。

  • p_filesz 此成员给出段在文件映像中所占的字节数。可以为 0。

  • p_memsz 此成员给出段在内存映像中占用的字节数。可以为 0。

  • p_flags 此成员给出与段相关的标志。

  • p_align 可加载的进程段的 p_vaddr 和 p_offset 取值必须合适,相对于对页面大小的取模而言。此成员给出段在文件中和内存中如何 对齐。数值 0 和 1 表示不需要对齐。否则 p_align 应该是个正整数,并且是 2 的幂次数,p_vaddr 和 p_offset 对 p_align 取模后应该相等。

可执行 ELF 目标文件中的段类型:

ELF文件格式分析

节区(Sections)

节区中包含目标文件中的所有信息,除了:ELF 头部、程序头部表格、节区头部

表格。节区满足以下条件:

  1. 目标文件中的每个节区都有对应的节区头部描述它,反过来,有节区头部不意 味着有节区。

  2. 每个节区占用文件中一个连续字节区域(这个区域可能长度为 0)。

  3. 文件中的节区不能重叠,不允许一个字节存在于两个节区中的情况发生。

  4. 目标文件中可能包含非活动空间(INACTIVE SPACE)。这些区域不属于任何头部和节区,其内容指定。

节区头部表格

ELF 头部中,e_shoff 成员给出从文件头到节区头部表格的偏移字节数;e_shnum 给出表格中条目数目;e_shentsize 给出每个项目的字节数。从这些信息中可以确切地定 位节区的具体位置、长度。

每个节区头部数据结构描述:

/* 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;  typedef struct {     Elf64_Half    sh_name;    /* section name */     Elf64_Half    sh_type;    /* section type */     Elf64_Xword    sh_flags;    /* section flags */     Elf64_Addr    sh_addr;    /* virtual address */     Elf64_Off    sh_offset;    /* file offset */     Elf64_Xword    sh_size;    /* section size */     Elf64_Half    sh_link;    /* link to another */     Elf64_Half    sh_info;    /* misc info */     Elf64_Xword    sh_addralign;    /* memory alignment */     Elf64_Xword    sh_entsize;    /* table entry size */ } Elf64_Shdr;

对其中各个字段的解释如下:

ELF文件格式分析

索引为零(SHN_UNDEF)的节区头部是存在的,尽管此索引标记的是未定义的节区应用。这个节区固定为:

ELF文件格式分析

sh_type 字段

节区类型定义:

ELF文件格式分析

其他的节区类型是保留的。

sh_flags 字段

sh_flags 字段定义了一个节区中包含的内容是否可以修改、是否可以执行等信息。 如果一个标志位被设置,则该位取值为 1。 定义的各位都设置为 0。

ELF文件格式分析

其中已经定义了的各位含义如下:

  • SHF_WRITE: 节区包含进程执行过程中将可写的数据。

  • SHF_ALLOC: 此节区在进程执行过程中占用内存。某些控制节区并不出现于目标

    文件的内存映像中,对于那些节区,此位应设置为 0。

  • SHF_EXECINSTR: 节区包含可执行的机器指令。

  • SHF_MASKPROC: 所有包含于此掩码中的四位都用于处理器专用的语义。

sh_link 和 sh_info 字段

根据节区类型的不同,sh_link 和 sh_info 的具体含义也有所不同:

ELF文件格式分析

特殊节区

很多节区中包含了程序和控制信息。下面的表中给出了系统使用的节区,以及它们 的类型和属性。

ELF文件格式分析

在分析这些节区的时候,需要注意如下事项:

  • 以“.”开头的节区名称是系统保留的。应用程序可以使用没有前缀的节区名称,以避 免与系统节区冲突。

  • 目标文件格式允许人们定义不在上述列表中的节区。

  • 目标文件中也可以包含多个名字相同的节区。

  • 保留给处理器体系结构的节区名称一般构成为:处理器体系结构名称简写 + 节区

    名称。

  • 处理器名称应该与 e_machine 中使用的名称相同。例如 .FOO.psect 街区是由

    FOO 体系结构定义的 psect 节区。

另外,有些编译器对如上节区进行了扩展,这些已存在的扩展都使用约定俗成的名

称,如:

  • .sdata

  • .tdesc

  • .sbss

  • .lit4

  • .lit8

  • .reginfo

  • .gptab

  • .liblist

  • .conflict

  • ...

字符串表(String Table)

字符串表节区包含以 NULL(ASCII 码 0)结尾的字符序列,通常称为字符串。ELF 目标文件通常使用字符串来表示符号和节区名称。对字符串的引用通常以字符串在字符 串表中的下标给出。

一般,第一个字节(索引为 0)定义为一个空字符串。类似的,字符串表的最后一 个字节也定义为 NULL,以确保所有的字符串都以 NULL 结尾。索引为 0 的字符串在 不同的上下文中可以表示无名或者名字为 NULL 的字符串。

允许存在空的字符串表节区,其节区头部的 sh_size 成员应该为 0。对空的字符串 表而言,非 0 的索引值是非法的。

例如:对于各个节区而言,节区头部的 sh_name 成员包含其对应的节区头部字符串 表节区的索引,此节区由 ELF 头的 e_shstrndx 成员给出。下图给出了包含 25 个字节 的一个字符串表,以及与不同索引相关的字符串。

ELF文件格式分析

上图中包含的字符串如下:

ELF文件格式分析

在使用、分析字符串表时,要注意以下几点:

  • 字符串表索引可以引用节区中任意字节。

  • 字符串可以出现多次

  • 可以存在对子字符串的引用

  • 同一个字符串可以被引用多次。

  • 字符串表中也可以存在未引用的字符串。

符号表(Symbol Table)

目标文件的符号表中包含用来定位、重定位程序中符号定义和引用的信息。符号表 索引是对此数组的索引。索引 0 表示表中的第一表项,同时也作为 定义符号的索引。

符号表项的格式如下:

/* Symbol Table Entry */ typedef struct elf32_sym {     Elf32_Word    st_name;    /* name - index into string table */     Elf32_Addr    st_value;    /* symbol value */     Elf32_Word    st_size;    /* symbol size */     unsigned char    st_info;    /* type and binding */     unsigned char    st_other;    /* 0 - no defined meaning */     Elf32_Half    st_shndx;    /* section header index */ } Elf32_Sym;  typedef struct {     Elf64_Half    st_name;    /* Symbol name index in str table */     Elf_Byte    st_info;    /* type / binding attrs */     Elf_Byte    st_other;    /* unused */     Elf64_Quarter    st_shndx;    /* section index of symbol */     Elf64_Xword    st_value;    /* value of symbol */     Elf64_Xword    st_size;    /* size of symbol */ } Elf64_Sym;

其中各个字段的含义说明:

ELF文件格式分析