ELF文件解析(ELF File Parsing)

ELF File Parsing

Executable and Linkable Format

实验材料:010editor readelf objdump linux_ls文件(x86-64)

资料讲解32位elf,实验解析64位elf

ls文件信息

链接:https://pan.baidu.com/s/17ElUYwRhtW0eRRED4SgNDA
提取码:ldss

┌──(kali㉿kali)-[~/Desktop/test]
└─$ checksec ./ls
[*] '/home/kali/Desktop/test/ls'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled
┌──(kali㉿kali)-[~/Desktop/test]
└─$ file ./ls
./ls: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=15dfff3239aa7c3b16a71e6b2e3b6e4009dab998, for GNU/Linux 3.2.0, stripped
┌──(kali㉿kali)-[~/Desktop/test]
└─$ ldd ./ls
linux-vdso.so.1 (0x00007ffcd2d86000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007ff70107a000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff700e99000)
libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007ff700dff000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff7010e6000)
名称 长度 对齐方式 用途
Elf32_Addr 4 4 无符号程序地址
Elf32_Half 2 2 无符号半整型
Elf32_Off 4 4 无符号文件偏移
Elf32_Sword 4 4 有符号大整型
Elf32_Word 4 4 无符号大整型
unsigned char 1 1 无符号小整型

image-20230221233642860

ELF Header

  • ELF文件头表(ELF header)
    • 记录了ELF文件的组织结构

除了ELF头部表以外,其它部分都没有严格的顺序。

#define EI_NIDENT   16

typedef struct {
unsigned char e_ident[EI_NIDENT]; /* ELF文件标识 */
Elf32_Half e_type; /* 文件类型 */
Elf32_Half e_machine; /* 机器类型 */
Elf32_Word e_version; /* 文件版本 */
Elf32_Addr e_entry; /* 程序入口地址 */
Elf32_Off e_phoff; /* 程序头表偏移 */
Elf32_Off e_shoff; /* 节头表偏移 */
Elf32_Word e_flags; /* 文件标志 */
Elf32_Half e_ehsize; /* ELF头大小 */
Elf32_Half e_phentsize; /* 程序头表项大小 */
Elf32_Half e_phnum; /* 程序头表项数量 */
Elf32_Half e_shentsize; /* 节头表项大小 */
Elf32_Half e_shnum; /* 节头表项数量 */
Elf32_Half e_shstrndx; /* 节头表字符串表索引 */
} Elf32_Ehdr;

readelf

└─$ readelf -h ./ls
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 (Position-Independent Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x61d0
Start of program headers: 64 (bytes into file)
Start of section headers: 149360 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 13
Size of section headers: 64 (bytes)
Number of section headers: 31
Section header string table index: 30

e_dient

  • 共16个字节
  • 头4个字节,被称作 “魔数”,标识该文件是一个 ELF 目标文件。固定为7f 45 4c 46(.ELF),和windows下PE(Portable Executable)文件的4D 5A(MZ)类似,改掉会崩溃
  • 下一个字节标识文件的类型
    • 0无效类型
    • 1 32文件
    • 2 64位文件
  • 下一个字节标识数据的编码方式
    • 0 无效
    • 1 小端序 LSB
    • 2 大端序 MSB
  • 后面字节不看了,因为除了.ELF签名,其他没有用,改掉后程序依旧可以正常运行,只是会迷惑掉解析软件罢了

image-20230221231314941

└─$ readelf -h ./ls
ELF Header:
Magic: 7f 45 4c 46 0e ee ee ee ee ee ee ee ee ee ee ee
Class: <unknown: e>
Data: <unknown: ee>
Version: 238 <unknown>
OS/ABI: <unknown: ee>
ABI Version: 238
Type: DYN (Shared object file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x61d0
Start of program headers: 0 (bytes into file)
Start of section headers: 64 (bytes into file)
Flags: 0x0
Size of this header: 18288 (bytes)
Size of program headers: 2 (bytes)
Number of program headers: 0
Size of section headers: 0 (bytes)
Number of section headers: 0
Section header string table index: 0
readelf: Warning: possibly corrupt ELF file header - it has a non-zero section header offset, but no section headers

e_type

不能随意修改

名称 意义
ET_NONE 0 无文件类型
ET_REL 1 可重定位文件
ET_EXEC 2 可执行文件
ET_DYN 3 共享目标文件
ET_CORE 4 核心转储文件
ET_LOPROC 0xff00 处理器指定下限
ET_HIPROC 0xffff 处理器指定上限

这里ls头部信息中的类型竟然是共享库文件,而我们查看的是可执行文件,发现开启pie的程序都会被识别为3

PIE能使程序像共享库一样在主存任何位置装载,这需要将程序编译成位置无关,并链接为ELF共享对象。

e_machine

可运行的机器架构

不能随意修改

名称 意义
EM_NONE 0 无机器类型
EM_M32 1 AT&T WE 32100
EM_SPARC 2 SPARC
EM_386 3 Intel 80386
EM_68K 4 Motorola 68000
EM_88K 5 Motorola 88000
EM_860 7 Intel 80860
EM_MIPS 8 MIPS RS3000

e_version

可修改

系统版本

e_entry

不可修改

系统转交控制权给 ELF 中相应代码的虚拟地址,也就是给pc的值,很重要 ,可以做入口点hook

e_phoff

不可修改

Program Header table OFFset

程序头部表在elf中的偏移

e_shoff

可修改

Section Header table OFFset

节头表在elf中偏移

e_flags

可修改

具体架构版本

e_ehsize

可修改

ELF HEADER 长度

e_phentsize

不可修改

Program Header ENTry SIZE

program header table是个结构体数组,参数描述结构体大小

e_phnum

不可修改

Program Header entry NUMber

program header table结构体个数

e_shentsize

可修改

Section Header ENTry SIZE

section结构体大小

e_shnum

可修改

Section Header NUMber

section结构体个数

e_shstrndx

可修改

关于Program Header table 的项基本都不可以改,Section Header Table基本可改,不影响执行

Program Header table

程序的头部只有对于可执行文件和共享目标文件有意义。

  • 程序头表/段表(Program header table)
    • 告诉系统如何创建进程
    • 生成进程的可执行文件必须拥有此结构
    • 重定位文件不一定需要
typedef struct
{
Elf64_Word p_type; /* Segment type */
Elf64_Word p_flags; /* Segment flags */
Elf64_Off p_offset; /* Segment file offset */
Elf64_Addr p_vaddr; /* Segment virtual address */
Elf64_Addr p_paddr; /* Segment physical address */
Elf64_Xword p_filesz; /* Segment size in file */
Elf64_Xword p_memsz; /* Segment size in memory */
Elf64_Xword p_align; /* Segment alignment */
} Elf64_Phdr;

p_type

PT_LOAD 1 此类型段为一个可加载的段,大小由 p_filesz 和 p_memsz 描述。文件中的字节被映射到相应内存段开始处。如果 p_memsz 大于 p_filesz,“剩余” 的字节都要被置为 0。p_filesz 不能大于 p_memsz。可加载的段在程序头部中按照 p_vaddr 的升序排列。

PT_GNU_RELRO 用于指示 GNU ld linker 链接器如何将某些段放置到进程地址空间中以实现地址空间的保护。PT_GNU_RELRO 中的 RELRO 是 “RELocation Read-Only” 的缩写。它指定了一个区段,该区段包含了程序的只读数据段 (如 .rodata) 中所有的全局偏移表 (Global Offset Table,简称 GOT) 和重定位表 (Relocation Table),并将这个区段设置为只读。这样做的目的是防止程序在运行时被攻击者利用 GOT 篡改,提高程序的安全性。

我们只关注一下需要加载到内存的段就可以

以ls program head table 为例分析

image-20230223004348967

可以看到有四个段需要加载到内存的可加载段,在此之前,需要有两个段(==静态链接不需要==),一个是Program Header 另一个是 Interpreter Path(解释器路径)

  • Program Header

    image-20230223163500827

    对程序加载又有的只有p_offset_FROM_FILE_BEGIN和p_vaddr_virtual_addrees这两个值又必须和ELF Header里的e_phoff一样,这样做是为了在解析elf时如果给的文件指针是program header table地址,而又需要elf header时(比如需要程序入口点),将该指针减去这个offset就可以找到elf header,毕竟只有elf header位置固定在文件开头

  • Interpreter Path

    image-20230223165238248

    表示在文件偏移offset 为0x318的位置读取LENGTH为28字节的解释器地址,来加载下面的loadable段到进程内存

    /lib64/ld-linux-x86-64.so.2 是 Linux x86-64 系统下的动态链接器(dynamic linker)文件,它的主要作用是在程序运行时,将程序所需的共享库(shared library)加载到内存中,并将这些共享库中未定义的符号与程序中定义的符号进行链接,从而使程序能够正常执行。

  • Loadable segment

    image-20230309185924942

    利用mmap向pie+virtual_address处映射从file_begin开始raw_length大小的数据,前面映射必须从页对齐开始,后面必须按4096对齐(页对齐)结束

  • Dynamic segment

    image-20230318221408017

    这里面保存了动态链接器所需的基本信息,如依赖于哪些共享对象、动态链接符号表的位置、共享对象初始化代码的地址等

    从这里开始我们已经把磁盘上的文件加载到了内存,也就是说我们不会再用到这里的任何和文件相关的地址

    这个段标记的数据,也是一个结构体数组,如下

    typedef struct
    {
    Elf64_Sxword d_tag; /* Dynamic entry type *64位程序占8bit 用于区分各种指定信息类型的标记,该结构中的共用体根据该标志进行解释/
    union
    {
    Elf64_Xword d_val; /* Integer value */
    Elf64_Addr d_ptr; /* Address value */
    } d_un; 64位程序占8bit 或者保存一个虚拟地址,或者保存一个整数,可以根据特定的标志进行解释。
    } Elf64_Dyn;
    /* Legal values for d_tag (dynamic entry type). */

    #define DT_NULL 0 /* Marks end of dynamic section */
    #define DT_NEEDED 1 /* Name of needed library */
    #define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */
    #define DT_PLTGOT 3 /* Processor defined value */
    #define DT_HASH 4 /* Address of symbol hash table */
    #define DT_STRTAB 5 /* Address of string table */
    #define DT_SYMTAB 6 /* Address of symbol table */
    #define DT_RELA 7 /* Address of Rela relocs */
    #define DT_RELASZ 8 /* Total size of Rela relocs */
    #define DT_RELAENT 9 /* Size of one Rela reloc */
    #define DT_STRSZ 10 /* Size of string table */
    #define DT_SYMENT 11 /* Size of one symbol table entry */
    #define DT_INIT 12 /* Address of init function */
    #define DT_FINI 13 /* Address of termination function */
    #define DT_SONAME 14 /* Name of shared object */
    #define DT_RPATH 15 /* Library search path (deprecated) */
    #define DT_SYMBOLIC 16 /* Start symbol search here */
    #define DT_REL 17 /* Address of Rel relocs */
    #define DT_RELSZ 18 /* Total size of Rel relocs */
    #define DT_RELENT 19 /* Size of one Rel reloc */
    #define DT_PLTREL 20 /* Type of reloc in PLT */
    #define DT_DEBUG 21 /* For debugging; unspecified */
    #define DT_TEXTREL 22 /* Reloc might modify .text */
    #define DT_JMPREL 23 /* Address of PLT relocs */
    #define DT_BIND_NOW 24 /* Process relocations of object */
    #define DT_INIT_ARRAY 25 /* Array with addresses of init fct */
    #define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */
    #define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */
    #define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */
    #define DT_RUNPATH 29 /* Library search path */
    #define DT_FLAGS 30 /* Flags for the object being loaded */
    #define DT_ENCODING 32 /* Start of encoded range */
    #define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/
    #define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */
    #define DT_SYMTAB_SHNDX 34 /* Address of SYMTAB_SHNDX section */
    #define DT_NUM 35 /* Number used */
    #define DT_LOOS 0x6000000d /* Start of OS-specific */
    #define DT_HIOS 0x6ffff000 /* End of OS-specific */
    #define DT_LOPROC 0x70000000 /* Start of processor-specific */
    #define DT_HIPROC 0x7fffffff /* End of processor-specific */
    #define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */
    #define DT_ADDRRNGLO 0x6ffffe00
    #define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */
    #define DT_TLSDESC_PLT 0x6ffffef6
    #define DT_TLSDESC_GOT 0x6ffffef7
    #define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */
    #define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */
    #define DT_CONFIG 0x6ffffefa /* Configuration information. */
    #define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */
    #define DT_AUDIT 0x6ffffefc /* Object auditing. */
    #define DT_PLTPAD 0x6ffffefd /* PLT padding. */
    #define DT_MOVETAB 0x6ffffefe /* Move table. */
    #define DT_SYMINFO 0x6ffffeff /* Syminfo table. */
    #define DT_ADDRRNGHI 0x6ffffeff
    #define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */
    #define DT_ADDRNUM 11
    • 字符串表地址d_tag=5的地址,后面八位就是地址,其实section里的.dynstr也能找到,但是不一定准确,因为我们程序加载执行时那个setion无用

      image-20230318230730833

      image-20230817101327570

    • 导入库表d_tag=1 后面时字符串表的偏移,可能不止一个导入库表

      image-20230318231041729

      0x1040+0x0542=0x1582 0x1040+0x0552=0x1592

      image-20230318231247771

    • 符号表 d_tag=6 对应section .dynsym

      image-20230318232353517

      typedef struct
      {
      Elf64_Word st_name; 4 /* Symbol name (string tbl index) */
      unsigned char st_info; 1 /* Symbol type and binding */
      unsigned char st_other; 1 /* Symbol visibility */
      Elf64_Section st_shndx; 2 /* Section index */
      Elf64_Addr st_value; 8 /* Symbol value */
      Elf64_Xword st_size; 8 /* Symbol size */
      } Elf64_Sym; 24
    • 导入表 d_tag=23=0x17

      image-20230319004444739

      导入表的size在d_tag=2处

      image-20230319004959046

      typedef struct
      {
      Elf64_Addr r_offset; 8 /* Address */,写入导入地址,也就是got表
      Elf64_Xword r_info; 8 /* Relocation type and symbol index */32位存储着重定位类型信息,后32位存储着符号表索引从1开始
      Elf64_Sxword r_addend; 8 /* Addend */
      } Elf64_Rela;

      选取一个分析

      image-20230319014528225

      符号表

      image-20230319014737236

      字符串表0x1040+0x344=1384

      image-20230319014926764

      ida

      image-20230319015029874

    • 重定位表 d_tag=7 size在d_tag=8

      image-20230319123925728

      结构和导入表一样

    • 哈希表 #define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */

      image-20230319134353748

      用于加速符号查找,在 DT_GNU_HASH 中,符号名称被哈希到一个桶中,每个桶中保存了一个指向符号表的指针。当需要查找符号时,动态链接器可以使用哈希表来快速定位符号的位置,而不需要遍历整个符号表。

  • Read-only After Relocation

    加载到内存重定位后最后一个segment还会指导进行只读改写image-20230223005339928

​ 从0x232b0开始memsz 3208个byte为只读

Section Header Table

  • 节头表(Section header table)
    • 记录了ELF文件的节区信息
    • 用于链接的目标文件必须拥有此结构
    • 其它类型目标文件不一定需要

section Header Table内容对程序执行完全不影响,可以完全改掉,正常执行(这个也和动态linker版本有关)

typedef struct {
Elf32_Word sh_name; // 节名称在 .shstrtab 节中的索引
Elf32_Word sh_type; // 节类型
Elf32_Word sh_flags; // 节标志
Elf32_Addr sh_addr; // 节的内存地址
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;

image-20230222232634953