ELF文件结构 整体结构 ELF文件在链接视图(.o)文件和执行视图(可执行文件)有所差别,见下图
链接视图是以节(section)为单位,执行视图是以段(segment)为单位。
显示命令:  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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:                              REL (Relocatable file)   Machine:                           Advanced Micro Devices X86-64   Version:                           0x1   Entry point address:               0x0   Start of program headers:          0 (bytes into file)   Start of section headers:          832 (bytes into file)   Flags:                             0x0   Size of this header:               64 (bytes)   Size of program headers:           0 (bytes)   Number of program headers:         0   Size of section headers:           64 (bytes)   Number of section headers:         1   Section header string table index: 1 <corrupt: out of range>
 
7f 、45、4c、46分别对应ascii码的Del(删除)、字母E、字母L、字母F。这四个字节被称为ELF文件的魔数,操作系统在加载可执行文件时会确认魔数是否正确,如果不正确则拒绝加载。 第五个字节标识ELF文件是32位(01)还是64位(02)的。 第六个字节标识该ELF文件字节序是小端(01)还是大端(02)的。 第七个字节指示ELF文件的版本号,一般是01。 后九个字节ELF标准未做定义。一般为00.
ELF文件头定义了ELF魔数、文件机器字节长度、数据存储方式、版本、运行平台等一系列参数。
详细结构: ELF Header的详细结构定义如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 typedef  struct {      unsigned  char  e_ident[EI_NIDENT];          Elf64_Half    e_type;                      Elf64_Half    e_machine;                   Elf64_Word    e_version;                   Elf64_Addr    e_entry;                     Elf64_Off     e_phoff;                     Elf64_Off     e_shoff;                     Elf64_Word    e_flags;                     Elf64_Half    e_ehsize;                    Elf64_Half    e_phentsize;                 Elf64_Half    e_phnum;                     Elf64_Half    e_shentsize;                 Elf64_Half    e_shnum;                     Elf64_Half    e_shstrndx;              } Elf64_Ehdr; 
 
e_entry:程序入口地址
e_ehsize:ELF Header结构大小
e_phoff、e_phentsize、e_phnum:描述Program Header Table的偏移、大小、结构。
e_shoff、e_shentsize、e_shnum:描述Section Header Table的偏移、大小、结构。
e_shstrndx:这一项描述的是字符串表在Section Header Table中的索引,值25表示的是Section Header Table中第25项是字符串表(String Table)
其中各个类型所占字节的大小
Elf64_Word :4字节
Elf64_Off :8字节
 Elf64_Addr:8字节
Elf64_Half :2字节
Elf64_Xword:8字节
显示命令: 段表是ELF除了文件以外的最重要结构体,它描述了ELF的各个段的信息,ELF文件的段结构就是由段表决定的。编译器、链接器和装载器都是依靠段表来定位和访问各个段的属性的。 如上面ELF Header所示,段表在ELF文件中的位置由ELF文件头的“e_shoff”成员决定的。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 Section Headers:   [Nr] Name              Type             Address           Offset        Size              EntSize          Flags  Link  Info  Align   [ 0]                   NULL             0000000000000000  00000000        0000000000000000  0000000000000000           0     0     0   [ 1] .strtab           STRTAB           0000000000000000  00000280        00000000000000bc  0000000000000000           0     0     1   [ 2] .text                PROGBITS         0000000000000000  00000040        000000000000003a  0000000000000000 WAX       0     0     16   [ 3] .rela.text        RELA             0000000000000000  00000220        0000000000000018  0000000000000018   I      12     2     8   [ 4] data                PROGBITS         0000000000000000  0000007c        0000000000000008  0000000000000000   A       0     0     4   [ 5] .rodata.str1.1    PROGBITS         0000000000000000  00000084        000000000000000b  0000000000000001 AMS       0     0     1   [ 6] .bss              NOBITS           0000000000000000  00000090        0000000000000004  0000000000000000  WA       0     0     4   [ 7] .comment          PROGBITS         0000000000000000  00000090        0000000000000026  0000000000000001  MS       0     0     1   [ 8] .note.GNU-stack   PROGBITS         0000000000000000  000000b6        0000000000000000  0000000000000000           0     0     1   [ 9] .eh_frame         X86_64_UNWIND    0000000000000000  000000b8        0000000000000078  0000000000000000   A       0     0     8   [10] .rela.eh_frame    RELA             0000000000000000  00000238        0000000000000048  0000000000000018   I      12     9     8   [11] .llvm_addrsig     LOOS+0xfff4c03   0000000000000000  00000280        0000000000000000  0000000000000000   E      12     0     1   [12] .symtab           SYMTAB           0000000000000000  00000130        00000000000000f0  0000000000000018  
 
其中比较重要的段:
.text段主要存储函数代码
.bss段主要存储未初始化的全局变量以及局部静态变量
.rodata:存储全局常量,字符串常量。
.data:已初始化的全局变量和局部静态变量。
详细结构: Section Header table 本质上是Elf64_Shdr 结构体的数组,Elf64_Shdr的结构定义如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 typedef  struct {      Elf64_Word    sh_name;                     Elf64_Word    sh_type;                     Elf64_Xword   sh_flags;                    Elf64_Addr    sh_addr;                     Elf64_Off     sh_offset;                   Elf64_Xword   sh_size;                     Elf64_Word    sh_link;                     Elf64_Word    sh_info;                     Elf64_Xword   sh_addralign;                Elf64_Xword   sh_entsize;              } Elf64_Shdr;
 
sh_name:表示section的名称。由于每个section名称的长度不相同,并且为了节约空间,于是就将所有section的名称都存放在一个特定的名叫.shstrtab的section中,所以这里的sh_name的值指的就是在这个特定section中的偏移量,通过它可以获得一个字符串,也就是所需要的section名。0值表示无名称,一般用于类型为SHT_NULL的section中。
sh_type:表示section的类型。
sh_offset表示该section在文件中的偏移量。
sh_size该section的大小。
sh_link、sh_info:如果该部分与链接相关,则“sh_link”和“sh_info”具有特殊的含义。
sh_addralign:该部分在进程的虚拟地址空间中的对齐方式。
sh_entsize:如果该部分包含表,如符号表、重新定位表,sh_entsize是表中每个条目的大小。
.strtab section  .strtab是字符串表,保存了节中用到所有的字符串,如下图所示,保存了所有的字符串,其他节通过索引偏移指定字符串的具体值
可通过下面命令查询所有的字符串具体的偏移量。
1 $ readelf -p .strtab <ELF_FILE> 
 
以.text段举例,如最开始命令所示,.text段在整体文件的偏移为0x40,.text section table在.strtab section table的后面,根据ELF table Header,每个section header为64字节,开始在832字节。所以.text section table的位置为 0x340(832字节)+0x40(64字节)*2 = 0x3C0开始
开始字段即为sh_name为06,即从.strtab段开头偏移6,查表可知偏移6即为.text字符串。此即为.strtab段的作用。
.symtab section symbol介绍 在 ELF(可执行和可链接格式)文件中,symbol起着重要作用。 此上下文中的symbol本质上是一个命名实体。它可以是一个变量,一个函数, 或程序中任何其他可识别的代码段。
在 ELF 文件中符号的主要作用包括
1.链接:符号允许链接器解析引用。当您想要构建程序时 对于多个源文件,每个 .c 文件都编译为单独的可重定位对象文件。 如果一个目标文件引用另一个目标文件中定义的函数或变量,它通过一个符号来实现。在链接阶段,链接器将解析这些符号,将对符号的每个引用替换为所引用实体的实际地址。
2.分析:symbol对于 objdump/gdb/ghidra 等分析工具至关重要,没有symbol的程序比有symbol的更难分析。
3.动态链接:symbol也用于动态链接,它们允许程序确定运行时要从动态加载的库中调用哪些函数或要使用的变量。
常见symbol 每个对象文件都有自己的符号表 (.symtab),其中包含在 对象文件。符号表中的符号可以是以下类型之一:
全局symbol:这些符号对其他对象文件和库可见,例如函数(’main’、’foo’)和全局变量。 
外部symbol:这些符号在其他对象文件或库中定义,例如 libc 中的“printf”。 3.节名:这些符号总是由编译器和汇编器生成,用于标记节的开头。 
局部symbol:这些符号仅对目标文件本身可见,例如静态函数和静态变量。 5.调试符号:这些符号由编译器和汇编器生成,用于调试,通常采用“DWARF”格式。 
 
详细结构 .symtab和Section Header Table一样,是一个存储Elf64_Sym结构体的数组。
Elf64_Sym的具体结构如下。
1 2 3 4 5 6 7 8 9 10 typedef  struct {      Elf64_Word    st_name;                     unsigned  char  st_info;                     unsigned  char  st_other;                    Elf64_Section st_shndx;                    Elf64_Addr    st_value;                    Elf64_Xword   st_size;                 } Elf64_Sym;
 
st_name:symbol的名字在.strtab段中的偏移。
st_shndx:symbol段的索引。
st_info:高4位表示 Symbol Binding,低4位表示 Symbol Type
st_value:根据不同的上下文,有不同的定义.
1.对于 Relocatable file,如果 st_shndx 的值为 SHN_COMMON,那么 st_value 表示对齐约束。(上面 st_shndx = SHN_COMMON 已经提及) 2.对于 Relocatable file,如果 st_shndx 的值为关联的 Section Header index,那么 st_value 表示从该 Section 起始位置开始的 offset。(上面 st_shndx = Section Header index 已经提及) 3.对于 Executable file 和 Shared object file,st_value 则是已经计算好的虚存地址,这是为了方便 dynamic linker(动态链接器)
st_size:表示 Symbol 的大小,例如数据对象占据多少字节。如果 Symbol 没有大小或者大小未知,则值为0。
可通过命令来查看所有的symbol。
 
如上图所示即为各个symbol的详细信息。
查看进程虚拟地址范围文件
/proc/<pid>/maps
ELF的Program Header Table用于描述ELF可执行文件的内存布局。
操作系统并不关心ELF部分的详细数据,它主要关心每个部分的权限:是否可读,可写,可执行。因此,一些具有相同权限的ELF部分的可以合并到一个segment中。
segment由链接器创建,描述segment的结构就是Program Header Table。只有ELF可执行文件和共享库才有Program Header Table,这是一个数组Elf64_Phdr结构,每个这样的结构定义一个segment。
详细结构 1 2 3 4 5 6 7 8 9 10 11 typedef  struct  {     Elf64_Word    p_type;        Elf64_Word    p_flags;       Elf64_Off     p_offset;      Elf64_Addr    p_vaddr;       Elf64_Addr    p_paddr;       Elf64_Xword   p_filesz;      Elf64_Xword   p_memsz;       Elf64_Xword   p_align;   } Elf64_Phdr;  
 
详细的介绍已经在注释中写明。
显示命令  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 Elf file type is EXEC (Executable file) Entry point 0x401690 There are 11 program headers, starting at offset 64 Program Headers:   Type           Offset             VirtAddr           PhysAddr                  FileSiz            MemSiz              Flags  Align   LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000                  0x0000000000000520 0x0000000000000520  R      0x1000   LOAD           0x0000000000001000 0x0000000000401000 0x0000000000401000                  0x0000000000074315 0x0000000000074315  R E     0x1000   LOAD           0x0000000000076000 0x0000000000476000 0x0000000000476000                  0x0000000000029133 0x0000000000029133  R      0x1000   LOAD           0x000000000009f470 0x00000000004a0470 0x00000000004a0470                  0x0000000000065620 0x000000000006ae98  RW     0x1000   NOTE           0x00000000000002a8 0x00000000004002a8 0x00000000004002a8                  0x0000000000000020 0x0000000000000020  R      0x8   NOTE           0x00000000000002c8 0x00000000004002c8 0x00000000004002c8                  0x0000000000000044 0x0000000000000044  R      0x4   TLS            0x000000000009f470 0x00000000004a0470 0x00000000004a0470                  0x0000000000000018 0x0000000000000058  R      0x8   GNU_PROPERTY   0x00000000000002a8 0x00000000004002a8 0x00000000004002a8                  0x0000000000000020 0x0000000000000020  R      0x8   GNU_EH_FRAME   0x0000000000091960 0x0000000000491960 0x0000000000491960                  0x0000000000002174 0x0000000000002174  R      0x4   GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000                  0x0000000000000000 0x0000000000000000  RW     0x10   GNU_RELRO      0x000000000009f470 0x00000000004a0470 0x00000000004a0470                  0x0000000000063b90 0x0000000000063b90  R      0x1  Section to Segment mapping:   Segment Sections...    00     .note.gnu.property .note.gnu.build-id .note.ABI-tag .rela.plt     01     .init .plt .text .fini     02     .rodata .stapsdt.base rodata.cst32 .eh_frame_hdr .eh_frame .gcc_except_table     03     .tdata .init_array .fini_array .data.rel.ro .got .got.plt .data .bss     04     .note.gnu.property     05     .note.gnu.build-id .note.ABI-tag     06     .tdata .tbss     07     .note.gnu.property     08     .eh_frame_hdr     09         10     .tdata .init_array .fini_array .data.rel.ro .got 
 
plt表和got表 
静态链接和动态链接的区别,静态链接在编译为可执行文件时将用户文件和库文件打包到一起,动态链接打包库函数寻址的程序片段,加载到内存空间后在再进行寻找。
plt 表是一个数组,数组中的每个元素都一个可执行的代码片段 。数组中每个元素都与一个外部函数相对应(如strlen@plt。每当程序试图调用一个外部函数时,就会跳转到.plt表的对应代码片段,再由该代码片段跳转至对应函数。
got.plt  表是一个数组,数组中的每个元素都一个地址。数组中每个元素都与一个外部函数相对应(如strlen@got)。每当程序试图调用一个外部函数时,.plt表的跳转地址正是来自于.got.plt表。
每当程序试图调用一个外部函数时,就会跳转到.plt表的对应代码片段,再由该代码片段跳转至目标函数。
got表劫持 可以事先将got表中的函数地址改为后门函数地址当执行时plt表直接跳转到后门函数从而劫持获取权限。
通过命令即可查询到每个.plt表对应的got表。
1 $ objdump -d -j .plt /challenge/level41