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 无符号小整型

ELF文件解析 ELF File Parsing_第1张图片

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签名,其他没有用,改掉后程序依旧可以正常运行,只是会迷惑掉解析软件罢了

在这里插入图片描述

└─$ 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 为例分析

ELF文件解析 ELF File Parsing_第2张图片

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

  • Program Header

ELF文件解析 ELF File Parsing_第3张图片

对程序加载又有的只有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

ELF文件解析 ELF File Parsing_第4张图片

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

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

  • Loadable segment

    ELF文件解析 ELF File Parsing_第5张图片

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

  • Dynamic segment

    ELF文件解析 ELF File Parsing_第6张图片

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

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

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

    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无用

    ELF文件解析 ELF File Parsing_第7张图片

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

在这里插入图片描述

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

在这里插入图片描述

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

    在这里插入图片描述

    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

    在这里插入图片描述

    导入表的size在d_tag=2处

    在这里插入图片描述

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

    选取一个分析

    ELF文件解析 ELF File Parsing_第8张图片

    符号表

    在这里插入图片描述

    字符串表0x1040+0x344=1384

    在这里插入图片描述

    ida

    在这里插入图片描述

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

    在这里插入图片描述

    结构和导入表一样

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

    在这里插入图片描述

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

  • Read-only After Relocation

    ELF文件解析 ELF File Parsing_第9张图片

​ 从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;

你可能感兴趣的:(linux,安全)