每日总结-第四十三天-ELF文件格式

2020-05-25  本文已影响0人  SamiraG

参考

https://blog.csdn.net/xuehuafeiwu123/article/details/72963229
https://felixzhang00.github.io/2016/12/24/2016-12-24-ELF%E6%96%87%E4%BB%B6%E8%A3%85%E8%BD%BD%E9%93%BE%E6%8E%A5%E8%BF%87%E7%A8%8B%E5%8F%8Ahook%E5%8E%9F%E7%90%86/

ELF文件组成

ELF头部(ELF_Header): 每个ELF文件都必须存在一个ELF_Header,这里存放了很多重要的信息用来描述整个文件的组织,如: 版本信息,入口信息,偏移信息等。程序执行也必须依靠其提供的信息。

程序头部表(Program_Header_Table): 可选的一个表,用于告诉系统如何在内存中创建映像,在图中也可以看出来,有程序头部表才有段,有段就必须有程序头部表。其中存放各个段的基本信息(包括地址指针)。

节区头部表(Section_Header_Table): 类似与Program_Header_Table,但与其相对应的是节区(Section)。

节区(Section): 将文件分成一个个节区,每个节区都有其对应的功能,如符号表,哈希表等。

段(Segment): 将文件分成一段一段映射到内存中。段中通常包括一个或多个节区。


image

ELF头

$ readelf -h main
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x660
  Start of program headers:          64 (bytes into file)
  Start of section headers:          6616 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         29
  Section header string table index: 28
/* ELF Header */
#define EI_NIDENT 16
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 Header 中各个字段的说明如表:


image

程序头

ep@EP:/mnt/d/code/linux$ readelf -l main

Elf file type is DYN (Shared object file)
Entry point 0x660
There are 9 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
                 0x00000000000001f8 0x00000000000001f8  R      0x8
  INTERP         0x0000000000000238 0x0000000000000238 0x0000000000000238
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x00000000000009e8 0x00000000000009e8  R E    0x200000
  LOAD           0x0000000000000d98 0x0000000000200d98 0x0000000000200d98
                 0x0000000000000278 0x0000000000000280  RW     0x200000
  DYNAMIC        0x0000000000000da8 0x0000000000200da8 0x0000000000200da8
                 0x00000000000001f0 0x00000000000001f0  RW     0x8
  NOTE           0x0000000000000254 0x0000000000000254 0x0000000000000254
                 0x0000000000000044 0x0000000000000044  R      0x4
  GNU_EH_FRAME   0x00000000000008a4 0x00000000000008a4 0x00000000000008a4
                 0x000000000000003c 0x000000000000003c  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x0000000000000d98 0x0000000000200d98 0x0000000000200d98
                 0x0000000000000268 0x0000000000000268  R      0x1

 Section to Segment mapping:
  Segment Sections...
   00
   01     .interp
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame
   03     .init_array .fini_array .dynamic .got .data .bss
   04     .dynamic
   05     .note.ABI-tag .note.gnu.build-id
   06     .eh_frame_hdr
   07
   08     .init_array .fini_array .dynamic .got

可执行文件或者共享目标文件的程序头部是一个结构数组,每个结构描述了一个段 或者系统准备程序执行所必需的其它信息。目标文件的“段”包含一个或者多个“节区”, 也就是“段内容(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;

其中各个字段说明:

PT_LOAD

一个可执行文件至少有一个PT_LOAD 类型的段。这类程序头描述的是可装载的段,也就是说,这种类型的段将被装载或者映射到内存中。例如,一个需要动态链接的ELF 可执行文件通常包含以下两个可装载的段(类型为PT_LOAD):

  1. 存放程序代码的text 段;
  2. 存放全局变量和动态链接信息的data 段。

PT_DYNAMIC——动态段的Phdr

动态段是动态链接可执行文件所特有的,包含了动态链接器所必需的一些信息。在动态段中包含了一些标记值和指针,包括但不限于以下内容:

PT_INTERP

PT_INTERP 段只将位置和大小信息存放在一个以null 为终止符的字符串中,是对程序解释器位置的描述。例如,/lib/linux-ld.so.2 一般是指动态链接器的位置,也即程序解释器的位置。

PT_PHDR

PT_PHDR 段保存了程序头表本身的位置和大小。Phdr 表保存了所有的Phdr 对文件(以及内存镜像)中段的描述信息。

ELF节头

ep@EP:/mnt/d/code/linux$ readelf -S main
There are 29 section headers, starting at offset 0x19d8:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000000254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000000274  00000274
       0000000000000024  0000000000000000   A       0     0     4
......
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

节头的数据结构:

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

各个字段的解释如下:



sh_type 字段 节区类型定义:



常用节区:

节与段

节,不是段。段是程序执行的必要组成部分,在每个段中,会有代码或者数据被划分为不同的节。节头表是对这些节的位置和大小的描述,主要用于链接和调试。节头对于程序的执行来说不是必需的,没有节头表,程序仍可以正常执行,因为节头表没有对程序的内存布局进行描述,对程序内存布局的描述是程序头表的任务。节头是对程序头的补充。readelf –l 命令可以显示一个段对应有哪些节,可以很直观地看到节和段之间的关系。
如果二进制文件中缺少节头,并不意味着节就不存在。只是没有办法通过
节头来引用节,对于调试器或者反编译程序来说,只是可以参考的信息变少了而已。

字符串表(String Table)

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


那么偏移与他们对用的字符串如下表:
!](https://img.haomeiwen.com/i11884068/f4e855e0a0f35dd7.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
这样在ELF中引用字符串只需要给出一个数组下标即可。字符串表在ELF也以段的形式保存,常见的段名为”.strtab”或”.shstrtab”。这两个字符串表分别为字符串表(String Table)和段表字符串表(Header String Table),字符串表保存的是普通的字符串,而段表字符串表用来保存段表中用到的字符串,比如段名。

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

  1. 字符串表索引可以引用节区中任意字节。
  2. 字符串可以出现多次
  3. 可以存在对子字符串的引用
  4. 同一个字符串可以被引用多次。
  5. 字符串表中也可以存在未引用的字符串。

符号表(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;

各个字段的含义如下:


符号是对某些类型的数据或者代码(如全局变量或函数)的符号引用。例如,printf()函数会在动态符号表.dynsym 中存有一个指向该函数的符号条目。在大多数共享库和动态链接可执行文件中,存在两个符号表。如前面使用readelf –S 命令输出的内容中,可以看到有两个节:.dynsym.symtab
.dynsym保存了引用来自外部文件符号的全局符号,如printf 这样的库函数,.dynsym 保存的符号是.symtab 所保存符号的子集,.symtab 中还保存了可执行文件的本地符号,如全局变量,或者代码中定义的本地函数等。因此,.symtab 保存了所有的符号,而.dynsym 只保存动态/全局符号。
因此,就存在这样一个问题:既然.symtab 中保存了.dynsym 中所有的符号,那么为什么还需要两个符号表呢?使用readelf –S 命令查看可执行文件的输出,可以看到一部分节被标记为了A(ALLOC)、WA(WRITE/ALLOC)或者AX(ALLOC/EXEC)。.dynsym 是被标记了ALLOC 的,而.symtab则没有标记。
ALLOC 表示有该标记的节会在运行时分配并装载进入内存,而.symtab不是在运行时必需的,因此不会被装载到内存中。.dynsym 保存的符号只能在运行时被解析,因此是运行时动态链接器所需要的唯一符号。.dynsym 符号表对于动态链接可执行文件的执行来说是必需的,而.symtab 符号表只是用来进行调试和链接的,有时候为了节省空间,会将.symtab符号表从生产二进制文件中删掉。
ep@EP:/mnt/d/code/linux$ readelf -s main

Symbol table '.dynsym' contains 11 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.2.5 (2)
     4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND read@GLIBC_2.2.5 (2)
     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
     6: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     7: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND malloc@GLIBC_2.2.5 (2)
     8: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
     9: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND sleep@GLIBC_2.2.5 (2)
    10: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.2.5 (2)

Symbol table '.symtab' contains 67 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000238     0 SECTION LOCAL  DEFAULT    1
     2: 0000000000000254     0 SECTION LOCAL  DEFAULT    2
     3: 0000000000000274     0 SECTION LOCAL  DEFAULT    3
     4: 0000000000000298     0 SECTION LOCAL  DEFAULT    4
     5: 00000000000002b8     0 SECTION LOCAL  DEFAULT    5
     6: 00000000000003c0     0 SECTION LOCAL  DEFAULT    6
     7: 000000000000045c     0 SECTION LOCAL  DEFAULT    7
     8: 0000000000000478     0 SECTION LOCAL  DEFAULT    8
     9: 0000000000000498     0 SECTION LOCAL  DEFAULT    9
    10: 0000000000000558     0 SECTION LOCAL  DEFAULT   10
    11: 00000000000005d0     0 SECTION LOCAL  DEFAULT   11
    12: 00000000000005f0     0 SECTION LOCAL  DEFAULT   12
    13: 0000000000000650     0 SECTION LOCAL  DEFAULT   13
    14: 0000000000000660     0 SECTION LOCAL  DEFAULT   14
    15: 0000000000000864     0 SECTION LOCAL  DEFAULT   15
    16: 0000000000000870     0 SECTION LOCAL  DEFAULT   16
    17: 00000000000008a4     0 SECTION LOCAL  DEFAULT   17
    18: 00000000000008e0     0 SECTION LOCAL  DEFAULT   18
    19: 0000000000200d98     0 SECTION LOCAL  DEFAULT   19
    20: 0000000000200da0     0 SECTION LOCAL  DEFAULT   20
    21: 0000000000200da8     0 SECTION LOCAL  DEFAULT   21
    22: 0000000000200f98     0 SECTION LOCAL  DEFAULT   22
    23: 0000000000201000     0 SECTION LOCAL  DEFAULT   23
    24: 0000000000201010     0 SECTION LOCAL  DEFAULT   24
    25: 0000000000000000     0 SECTION LOCAL  DEFAULT   25
    26: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    27: 0000000000000690     0 FUNC    LOCAL  DEFAULT   14 deregister_tm_clones
    28: 00000000000006d0     0 FUNC    LOCAL  DEFAULT   14 register_tm_clones
    29: 0000000000000720     0 FUNC    LOCAL  DEFAULT   14 __do_global_dtors_aux
    30: 0000000000201010     1 OBJECT  LOCAL  DEFAULT   24 completed.7698
    31: 0000000000200da0     0 OBJECT  LOCAL  DEFAULT   20 __do_global_dtors_aux_fin
    32: 0000000000000760     0 FUNC    LOCAL  DEFAULT   14 frame_dummy
    33: 0000000000200d98     0 OBJECT  LOCAL  DEFAULT   19 __frame_dummy_init_array_
    34: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS main.c
    35: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    36: 00000000000009e4     0 OBJECT  LOCAL  DEFAULT   18 __FRAME_END__
    37: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS
    38: 0000000000200da0     0 NOTYPE  LOCAL  DEFAULT   19 __init_array_end
    39: 0000000000200da8     0 OBJECT  LOCAL  DEFAULT   21 _DYNAMIC
    40: 0000000000200d98     0 NOTYPE  LOCAL  DEFAULT   19 __init_array_start
    41: 00000000000008a4     0 NOTYPE  LOCAL  DEFAULT   17 __GNU_EH_FRAME_HDR
    42: 0000000000200f98     0 OBJECT  LOCAL  DEFAULT   22 _GLOBAL_OFFSET_TABLE_
    43: 0000000000000860     2 FUNC    GLOBAL DEFAULT   14 __libc_csu_fini
    44: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
    45: 0000000000201000     0 NOTYPE  WEAK   DEFAULT   23 data_start
    46: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@@GLIBC_2.2.5
    47: 0000000000201010     0 NOTYPE  GLOBAL DEFAULT   23 _edata
    48: 0000000000000864     0 FUNC    GLOBAL DEFAULT   15 _fini
    49: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@@GLIBC_2.2.5
    50: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND read@@GLIBC_2.2.5
    51: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_
    52: 0000000000201000     0 NOTYPE  GLOBAL DEFAULT   23 __data_start
    53: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    54: 0000000000201008     0 OBJECT  GLOBAL HIDDEN    23 __dso_handle
    55: 0000000000000870     4 OBJECT  GLOBAL DEFAULT   16 _IO_stdin_used
    56: 00000000000007f0   101 FUNC    GLOBAL DEFAULT   14 __libc_csu_init
    57: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND malloc@@GLIBC_2.2.5
    58: 0000000000201018     0 NOTYPE  GLOBAL DEFAULT   24 _end
    59: 0000000000000660    43 FUNC    GLOBAL DEFAULT   14 _start
    60: 0000000000201010     0 NOTYPE  GLOBAL DEFAULT   24 __bss_start
    61: 000000000000076a   126 FUNC    GLOBAL DEFAULT   14 main
    62: 0000000000201010     0 OBJECT  GLOBAL HIDDEN    23 __TMC_END__
    63: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
    64: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND sleep@@GLIBC_2.2.5
    65: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@@GLIBC_2.2
    66: 00000000000005d0     0 FUNC    GLOBAL DEFAULT   11 _init
上一篇 下一篇

猜你喜欢

热点阅读