Android逆向进阶-ELF文件分析(1)

本文章首发于i春秋,未经允许,禁止转载。

0x00 前言

有价之宝:戳这里
无价之宝:戳这里

内容

ELF文件分析
(1)ELF Header
(2)Program Headers

说明

文件分析,分析起来真的是头有点炸。不过又是必经之路,不分析又不行。之后可能考虑使用各种语言对elf文件进行一个分析。

0x01 ELF 分析。

ELF介绍

ELF(可执行链接格式),最初由UNIX系统实验室开发并发布的,作为应用程序二进制接口的一部分。

ELF标准的目的是为软件开发人员提供一组二进制接口定义。

ELF文件格式

简介:

文件的三种格式

1.可重定位文件:适用于其他目标文件链接来创建可执行文件或者共享目标文件的代码和数据。

2.可执行文件:包含于执行的一个程序,此文件规定了exec()如何创建一个程序的进程映像。

3.共享目标文件:包含可在两种上下文中链接的代码和数据。

目标文件格式

文件开始是一个ELF头部,用来描述整个文件的组织。

节区部分包含连接视图的大量信息。

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

节区头部表:包含了描述文件节区的信息,每个节区在表中都有一项,每一项给诸如节区名称。节区大小等信息。

ELF Header

文件最开始几个字节给出如何解释文件的提示信息。

ELF Header :

#define EI_NIDENT 16
typedef struce{
    unsigned char e_ident[EI_NIDENT];
    ELf32_Half e_type;
    Elf32_half e_machine;
    Elf32_Word e_version;
    Elf32_Addr e_entry;
    Elf32_Off e_phoff;
    Elf32_Off e_short;
    Elf32_Word e_flags;
    ELf32_Half e_ehsize;
    Elf32_half e_phentsize;
    Elf32_half e_phnum;
    Elf32_half e_shentsize;
    Elf32_half e_shnum;
    Elf32_Half e_shstrndx;
};

首先是 e_ident[EI_NIDENT]
这里有一个每一个元素的表。


接下来对e_ident[]的内容进行说明。

e_ident

魔数部分

魔数,标志此文件是一个ELF文件

EI_MAG0 0x7f
EI_MAG1 ‘E’
EI_MAG2 ‘L’
EI_MAG3 ‘F’

容量

EI_CLASS

分类:
ELFCLASSNONE,取值为0,非法类别
ELFCLASS32,取值为1, 32位目标
ELFCLASS64,取值为2,64位目标

数据编码方式

EI_DATA

取值为0的时候,ELFDATANONE,非法数据编码

取值为1的时候,ELFDATA2LSB,高危在前

取值为2的时候,ELFDATA2MSB,低位在前

版本号

EI_VERSION

未使用字节的开始

EI_PAD,初始化为0。

实例分析

readelf -h 显示elf文件开始的文件头信息. 

首先是e_ident

前四位是魔数部分:7f 45 4c 46

第五位代表文件类型 这里是01,对比之后我们发现,这是一个32位文件。

第六位代表数据编码格式:这里是01,也就是说是高位在前的编码格式。

第七位代表头部版本号:文件版本号是01。

有了实例之后发现理解一下子就上来了。

我们再来看一下hex文件。

e_type 文件类型 2个字节

这里看到 数据是 03 00,对比一下知道这个文件是共享文件。

再来看下readelf显示。

e_machine 体系结构类型 2个字节


因为资料的原因,所有ARM处理器比ELF文件出现的更晚。

e_version 文件版本 4个字节

当为1的时候 为当前版本

e_entry 4个字节

程序入口的虚拟地址,如果目标文件没有程序入口,可以为0

这里是00 00 00 00所以 没有程序入口。

ps:之前找到的资料,要嘛就是有问题,要嘛就不是分析so的,虽然说北京实验室的资料是高级论文,但是,它没有告诉我每一个字段的大小啊,或许是自己的愚笨,没有猜到。

真的是。

e_phoff 4个字节

程序头部表格的偏移量,如果文件没有程序头部表格,可以为0

首先来看hex文件分析。

34 00 ,也就是十进制的52

e_shoff 4个字节

节区头部表格的偏移量。如果文件没有节区头部表格,可以为0

来看hex文件分析

这里是 30 31 00 00,转换成二进制就是 12592,我们和readelf来对比

e_flags 4个字节

保存于文件相关的,特定于处理器的标志。标志名称采用EF_machine_flag的格式

先来看hex文件,这里是 00 00 00 05

然后来对比readelf。

e_ehsize 2个字节

ELF 头部的大小(字节计算)

怎么办,我懒的截图了。。。算了,先截图吧,没有必要我就不截图了。

34 00就是十进制 52,没有什么好说的,发现分析了elf文件格式,都知道简单的16转10进制的数字了。

e_phentsize 2个字节

程序头部表格的表项大小

20 00,就是二进制的32

这个是readelf文件格式

e_phnum 2个字节

程序头部表格的表项数目


这里是07 00 ,就是7

突然想起来,我是不是应该用各种语言进行一个二进制流的分析。万一面试的问我说有没有linux下编程的经历。这我就尴尬了。

e_shentsize 2个字节

节区头部表格的表项大小(字节计算)

这里很简单直接对比就可以了。

e_shnum 2个字节

节区头部表格的表项数目。可以为 0。

这里是10进制的21

e_shstrndx

节区头部表格中与节区名称字符串表相关的表项的索引。如果文件没有节
区名称字符串表,此参数可以为 SHN_UNDEF。


这里就是十进制的20。

还是自己比较一下,自己过一遍的理解深刻。这个倒是事实。

Program Headers

elf文件除了包含整体的文件头以外,还包含各个部分的program headers。这些program headers向操作系统描述了程序load和execute所需要的一切信息,它描述的是系统准备程序运行所需要的一个段和其他信息。

这里找到了一个好的文章:http://www.jollen.org/blog/2007/03/elf_program_loading_1_segment.html

文件格式

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;

说明

segment 在ELF Header中进行说明。

用这个命令测试一下:

readelf -l xxxx.so

p_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 */

先来看看实例。

这里是06 00 00 00,也就是10进制的6 ,对比资料就知道是PT_PHDR

Entry for header table itself ,也就是Header自己本身的Entry。

readelf分析

p_offset 4个字节

我们来看实例分析。

这里不懂。。。我去看.h源码了

好了找到了。

p_offset, File offset of contents,就是文件的偏移量。

相当于这个实例的偏移量是34。

p_vaddr 4个字节

含义:Virtual address in memory image

含义:内存映像中的虚拟地址

实例:

readelf 解析:

p_paddr 4个字节

Physical address (not used)
物理地址(未使用)

实例

readelf分析。

p_filesz 4个字节

Size of contents in file
文件的内容大小

实例:

实例里说明是:E0 00 00 00
readelf对比:

p_memsz 4个字节

Size of contents in memory
内存中内容的大小

实例:

实例就是:E0 00 00 00

对比readelf:

p_flags 4个字节

Access permission flags.
访问许可标志。
来看下资料:

/* Values for p_flags. */
#define PF_X        0x1 /* Executable. */
#define PF_W        0x2 /* Writable. */
#define PF_R        0x4 /* Readable. */

实例分析。

这里是 04 00 00 00,对比资料可以发现是PF_R,也就是Readable,就是可读的意思。

readelf对比分析

p_align四个字节

Alignment in memory and file.
在内存和文件中的对齐。

实例分析。

readelf进行对比:

总结

大小总共是32bytes。和header相对应。

0x02 结束语

新年快乐~

以上。

你可能感兴趣的:(Android逆向-操刀天下)