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头部表以外,其它部分都没有严格的顺序。
#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 -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
7f 45 4c 46
(.ELF),和windows下PE(Portable Executable)文件的4D 5A
(MZ)类似,改掉会崩溃└─$ 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
不能随意修改
名称 | 值 | 意义 |
---|---|---|
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
共享对象。
可运行的机器架构
不能随意修改
名称 | 值 | 意义 |
---|---|---|
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 |
可修改
系统版本
不可修改
系统转交控制权给 ELF 中相应代码的虚拟地址,也就是给pc的值,很重要 ,可以做入口点hook
不可修改
Program Header table OFFset
程序头部表在elf中的偏移
可修改
Section Header table OFFset
节头表在elf中偏移
可修改
具体架构版本
可修改
ELF HEADER 长度
不可修改
Program Header ENTry SIZE
program header table是个结构体数组,参数描述结构体大小
不可修改
Program Header entry NUMber
program header table结构体个数
可修改
Section Header ENTry SIZE
section结构体大小
可修改
Section Header NUMber
section结构体个数
可修改
关于Program Header table 的项基本都不可以改,Section 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;
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 篡改,提高程序的安全性。
我们只关注一下需要加载到内存的段就可以
可以看到有四个段需要加载到内存的可加载段,在此之前,需要有两个段(静态链接不需要),一个是Program Header 另一个是 Interpreter Path(解释器路径)
对程序加载又有的只有p_offset_FROM_FILE_BEGIN和p_vaddr_virtual_addrees这两个值又必须和ELF Header里的e_phoff一样,这样做是为了在解析elf时如果给的文件指针是program header table地址,而又需要elf header时(比如需要程序入口点),将该指针减去这个offset就可以找到elf header,毕竟只有elf header位置固定在文件开头
表示在文件偏移offset 为0x318的位置读取LENGTH为28字节的解释器地址,来加载下面的loadable段到进程内存
/lib64/ld-linux-x86-64.so.2
是 Linux x86-64 系统下的动态链接器(dynamic linker)文件,它的主要作用是在程序运行时,将程序所需的共享库(shared library)加载到内存中,并将这些共享库中未定义的符号与程序中定义的符号进行链接,从而使程序能够正常执行。
Loadable segment
利用mmap向pie+virtual_address处映射从file_begin开始raw_length大小的数据,前面映射必须从页对齐开始,后面必须按4096对齐(页对齐)结束
Dynamic segment
这里面保存了动态链接器所需的基本信息,如依赖于哪些共享对象、动态链接符号表的位置、共享对象初始化代码的地址等
从这里开始我们已经把磁盘上的文件加载到了内存,也就是说我们不会再用到这里的任何和文件相关的地址
这个段标记的数据,也是一个结构体数组,如下
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
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 */前32位2 位存储着重定位类型信息,后32位存储着符号表索引从1开始
Elf64_Sxword r_addend; 8 /* Addend */
} Elf64_Rela;
选取一个分析
符号表
字符串表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
从0x232b0开始memsz 3208个byte为只读
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;