Android安全–ELF文件格式解析

1、简介

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

   目标文件有三种类型:html

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

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

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

  • 目标文件所有是程序的二进制表示,目的是直接在某种处理器上直接执行。数据结构

2、目标文件格式

   目标文件既要参与程序连接又要参与程序执行。出于方便性和效率考虑,目标文件格式提供了两种并行视图,分别反映了这些活动的不一样需求。架构

   image

   文件开始处是一个ELF 头部(ELF Header),用来描述整个文件的组织。节区部分包含连接视图的大量信息:指令、数据、符号表、重定位信息等等。
程序头部表(Program Header Table),若是存在的话,告诉系统如何建立进程映像。用来构造进程映像的目标文件必须具备程序头部表,可重定位文件不须要这个表。
节区头部表(Section Heade Table)包含了描述文件节区的信息,每一个节区在表中都有一项,每一项给出诸如节区名称、节区大小这类信息。用于连接的目标文件必须包含节区头部表,其余目标文件能够有,也能够没有这个表。并发

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

   目标文件中的数据表示ide

   image

   目标文件中的全部数据结构都听从“天然”大小和对齐规则。若是必要,数据结构能够包含显式的补齐,例如为了确保4字节对象按4字节边界对齐。数据对齐一样适用于文件内部。

   这里使用android-ndk下面的例子hello-jni.so来分析。

3、ELF Header部分

   elf header数据以下:

   image

   ELF Header部分能够用下图中的数据结构表示:

   #define EI_NIDENT 16
typedef struct{
unsigned char e_ident[EI_NIDENT];    //目标文件标识信息
Elf32_Half e_type;                             //目标文件类型
Elf32_Half e_machine;                       //目标体系结构类型
Elf32_Word e_version;                      //目标文件版本
Elf32_Addr e_entry;                          //程序入口的虚拟地址,若没有,可为0
Elf32_Off e_phoff;                            //程序头部表格(Program Header Table)的偏移量(按字节计算),若没有,可为0
Elf32_Off e_shoff;                            //节区头部表格(Section Header Table)的偏移量(按字节计算),若没有,可为0
Elf32_Word e_flags;                        //保存与文件相关的,特定于处理器的标志。标志名称采用 EF_machine_flag的格式。
Elf32_Half e_ehsize;                        //ELF 头部的大小(以字节计算)。
Elf32_Half e_phentsize;                 //程序头部表格的表项大小(按字节计算)。
Elf32_Half e_phnum;                      //程序头部表格的表项数目。能够为 0。
Elf32_Half e_shentsize;                  //节区头部表格的表项大小(按字节计算)。
Elf32_Half e_shnum;                      //节区头部表格的表项数目。能够为 0。
Elf32_Half e_shstrndx;                  //节区头部表格中与节区名称字符串表相关的表项的索引。若是文件没有节区名称字符串表,此参数能够为 SHN_UNDEF。
}Elf32_Ehdr;

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

   image

   数听说明:

   ①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)低位在前。

   ④e_ident[EI_VERSION]](即e_ident[6])ELF头部的版本号码,此值必须是EV_CURRENT。

   ⑤e_ident[EI_PAD](即e_ident[7])标记e_ident中未使用字节的开始,初始化为0。

   其它各个字段的含义以下:

   image

4、Program Header Table部分

   数据结构以下所示:

   /* Program segment header. */ typedef struct { Elf32_Word p_type; /* Segment type */ Elf32_Off p_offset; /* Segment file offset */ Elf32_Addr p_vaddr; /* Segment virtual address */ Elf32_Addr p_paddr; /* Segment physical address */ Elf32_Word p_filesz; /* Segment size in file */ Elf32_Word p_memsz; /* Segment size in memory */ Elf32_Word p_flags; /* Segment flags */ Elf32_Word p_align; /* Segment alignment */ } Elf32_Phdr;

   其中p_type的定义以下:

/* Legal values for p_type (segment type).  */

#define PT_NULL         0               /* Program header table entry unused */
#define PT_LOAD         1               /* Loadable program segment */
#define PT_DYNAMIC      2               /* Dynamic linking information */
#define PT_INTERP       3               /* Program interpreter */
#define PT_NOTE         4               /* Auxiliary information */
#define PT_SHLIB        5               /* Reserved */
#define PT_PHDR         6               /* Entry for header table itself */
#define PT_TLS          7               /* Thread-local storage segment */
#define PT_NUM          8               /* Number of defined types */
#define PT_LOOS         0x60000000      /* Start of OS-specific */
#define PT_GNU_EH_FRAME 0x6474e550      /* GCC .eh_frame_hdr segment */
#define PT_LOSUNW       0x6ffffffa
#define PT_SUNWBSS      0x6ffffffa      /* Sun Specific segment */
#define PT_SUNWSTACK    0x6ffffffb      /* Stack segment */
#define PT_HISUNW       0x6fffffff
#define PT_HIOS         0x6fffffff      /* End of OS-specific */
#define PT_LOPROC       0x70000000      /* Start of processor-specific */
#define PT_HIPROC       0x7fffffff      /* End of processor-specific */

   p_flag表示该该段是否可读可写可执行

/* Legal values for p_flags (segment flags).  */

#define PF_X            (1 << 0)        /* Segment is executable */
#define PF_W            (1 << 1)        /* Segment is writable */
#define PF_R            (1 << 2)        /* Segment is readable */
#define PF_MASKOS       0x0ff00000      /* OS-specific */
#define PF_MASKPROC     0xf0000000      /* Processor-specific */

5、Section Header Table部分

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

   数据结构以下:

   typedef struct{
Elf32_Word sh_name;   //节区名,是节区头部字符串表节区(Section Header String Table Section)的索引。名字是一个 NULL 结尾的字符串。
Elf32_Word sh_type;    //为节区类型
Elf32_Word sh_flags;    //节区标志
Elf32_Addr sh_addr;    //若是节区将出如今进程的内存映像中,此成员给出节区的第一个字节应处的位置。不然,此字段为 0。
Elf32_Off sh_offset;    //此成员的取值给出节区的第一个字节与文件头之间的偏移。
Elf32_Word sh_size;   //此 成 员 给 出 节 区 的 长 度 ( 字 节 数 )。
Elf32_Word sh_link;   //此成员给出节区头部表索引连接。其具体的解释依赖于节区类型。
Elf32_Word sh_info;       //此成员给出附加信息,其解释依赖于节区类型。
Elf32_Word sh_addralign;    //某些节区带有地址对齐约束.
Elf32_Word sh_entsize;    //某些节区中包含固定大小的项目,如符号表。对于这类节区,此成员给出每一个表项的长度字节数。
}Elf32_Shdr;

   sh_type字段:

   image

   sh_flags字段:

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

   SHF_WRITE   0×1

   SHF_ALLOC   0×2

   SHF_EXECINSTR   0×4

   SHF_MASKPROC   0xF0000000

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

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

   SHF_ALLOC: 此节区在进程执行过程当中占用内存。某些控制节区并不出现于目标文件的内存映像中,对于那些节区,此位应设置为 0。

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

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

   sh_link和sh_info字段:

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

   image

6、特殊节区

   image在分析这些节区的时候,须要注意以下事项:

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

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

   ③目标文件中也能够包含多个名字相同的节区。

   ④保留给处理器体系结构的节区名称通常构成为:处理器体系结构名称简写 + 节区名称。

   ⑤处理器名称应该与 e_machine 中使用的名称相同。例如 .FOO.psect 街区是由FOO 体系结构定义的 psect 节区。

7、字符串表(String Table)

   首先要知道,字符串表它自己就是一个节区,从第二章描述中可知,每个节区都存在一个节区头部表项与之对应,因此字符串表这个节区也存在一个节区头部表项对应,而在elf文件头部结构中存在一个成员e_shstrndx给出这个节区头部表项的索引位置。所以能够经过

   shstrab  = (rt_uint8_t *)module_ptr +shdr[elf_module->e_shstrndx].sh_offset;

   字符串表节区包含以NULL(ASCII码0)结尾的字符序列,一般称为字符串。ELF目标文件一般使用字符串来表示符号和节区名称。对字符串的引用一般以字符串在字符串表中的下标给出。
通常,第一个字节(索引为 0)定义为一个空字符串。相似的,字符串表的最后一个字节也定义为 NULL,以确保全部的字符串都以NULL结尾。索引为0的字符串在不一样的上下文中能够表示无名或者名字为 NULL的字符串。
容许存在空的字符串表节区,其节区头部的sh_size成员应该为0。对空的字符串表而言,非0的索引值是非法的。

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

   ①字符串表索引能够引用节区中任意字节

   ②字符串能够出现屡次

   ③能够存在对子字符串的引用

   ④同一个字符串能够被引用屡次

   ⑤字符串表中也能够存在未引用的字符串

   

   先介绍这么多,其它的信息你们本身参考:

   非虫大大附图:http://bbs.pediy.com/attachment.php?attachmentid=74501&d=1355835585

   ELF文件格式分析:http://staff.ustc.edu.cn/~sycheng/sst/exp_crack/ELF.pdf

   ELF文件格式官方文档:http://download.csdn.net/detail/walkingman321/3016369

  做者:AloneMonkey    来源: Coder

问啊-定制化IT教育平台,牛人一对一服务,有问必答,开发编程社交头条 官方网站:www.wenaaa.com 下载问啊APP,参与官方悬赏,赚百元现金。

QQ群290551701 汇集不少互联网精英,技术总监,架构师,项目经理!开源技术研究,欢迎业内人士,大牛及新手有志于从事IT行业人员进入!

相关文章
相关标签/搜索