ELF文件概述,虚拟内存装载,段与节

发布时间:2024年01月17日

在之前的专栏中,我们已经将结果了在Windows下可执行文件(PE)文件格式了,个人认为在学习了PE文件结构之后,学习ELF文件结构会容易很多,大家可以去这个专栏学习PE文件格式:PE文件结构学习,本章内容ELF文件概述也会结合着PE文件结构来讲解。
这里先给出PE文件格式的图,大家可以了解了解:
PE文件结构

ELF文件结构:

这里先给出一张图,来大致了解ELF文件结构:
本图出自:星盟安全团队公开课PPT
ELF文件结构
在图中,我们可以看到,ELF文件大致分为三个部分:文件Header,节区块,与节区头表部分。
我们现在就分别来讲讲这三大部分:

一.文件头

在文件头中,又包含了ELF文件头和程序头表。

ELF文件头(ELF Header):

ELF文件头是ELF文件开头的部分,包含了关于整个文件的基本信息。

#define EI_NIDENT 16
 
struct Elf32_Ehdr            //共52个字节    //Ehdr表示ELF header
{
  unsigned char  e_ident[EI_NIDENT]; //
  Elf32_Half e_type;        //类型包括:可执行文件、可重定向文件、共享目标文件等
  Elf32_Half e_machine;     //有X86、arm之类
  Elf32_Word e_version;
  Elf32_Addr e_entry;       //可执行程序的入口地址
  Elf32_Off e_phoff;        //Program头表的偏移地址
  Elf32_Off e_shoff;        //Section头表的偏移地址
  Elf32_Word e_flags;
  Elf32_Half e_ehsize;      //本结构体的size
  Elf32_Half e_phentsize;   //单个Program头的size
  Elf32_Half e_phnum;       //Segment头表中Segment头的个数
  Elf32_Half e_shentsize;   //单个Section头的szie
  Elf32_Half e_shnum;       //Section头表中Section头的个数
  Elf32_Half e_shstrndx;    //储存Section名字集合的Section的下标,指".shstrtab"的下标
};

我们来逐一讲解一下其中每一个字段的含义:

  • e_ident[EI_NIDENT]:占据16个字节,包含用于标识文件为ELF格式的特定字节序列,如果是32位ELF文件,对应魔数为:‘/x7FELF’,如果是64位ELF文件,对应的魔数为:‘/x7FELF/XO2’。
  • e_type:文件类型
    占据两个字节,指定文件的类型,可以是可执行文件,目标文件,动态链接库等等。
  • e_machine:机器架构
    占据两个字节,指定文件所运行的目标体系结构,如x86,x64,arm,mips等。
  • e_version:版本
    占据两个字节,指定ELF文件的版本
  • e_entry:入口地址
    占据4个字节(32位),或8个字节(64位),标识可执行文件的入口点,就像PE文件结构里,可选头(拓展头)里的OEP
  • e_phoof:程序头表偏移
    占据4个字节或8个字节,指定程序头表在文件中的偏移量
  • e_shoof:节区头表偏移
    占据4个字节或8个字节,指定节区投标在文件中的偏移量
  • e_flags:标志
    占据4个字节,包含于文件相关的一些标志,如是否使用地址重定位等
  • e_ehsize:本结构体(ELF文件头)的大小
    占据2个字节,指定ELF文件头的大小,用于解析文件的其他部分
  • e_phentsize:程序头表条目大小
    占据2个字节,指定程序头表中每个条目的大小
  • e_phnum:程序头表条目数量
    占据2个字节,指定程序头表中的条目数量
  • e_shentsize:节区投标条目大小
    占据2个字节,指定节区头表中每个条目的大小
  • e_shnum:节区投标条目数量
    占据2个字节,指定节区投标中的条目数量
  • e_shstrndx:字符串表索引
    储存Section名字集合的Section的下标,也就是".shstrtab"节的下标

程序头表(Program Header Table):

程序头表是ELF文件头的一部分,它描述了程序在内存中的布局信息,尤其是可执行文件在运行时需要加载到内存中的各个段(Segment)属性,每个程序头表条目对应一个段,指导操作系统加载和执行可执行文件。
这里关于节和段的描述,我们在后面马上讲解到,这里先记住:段是一些具有相同权限的节的集合。

struct Elf32_phdr            //32个字节    //phdr表示Program header
{
    Elf32_Word p_type;       //如PT_LOAD表示,对应Segment可被加载到内存中
    Elf32_Off p_offset;      //Segment在ELF文件中的偏移量
    Elf32_Addr p_vaddr;      //Segment映射到内存后的虚拟地址
    Elf32_Addr p_paddr;      //Segment映射到内存后的物理地址,此时与虚拟地址相同
    Elf32_Word p_filesz;     //Segment在ELF文件中占用的size
    Elf32_Word p_memsz;      //Segment映射到内存后占用的size
    Elf32_Word p_flage;      //读、写、执行权限
    Elf32_Word p_align;      //字节对齐,p_vaddr和p_paddr对p_align取模后为0
};

我们来逐一讲解每一个字段的含义:

  • p_type:段类型
    占据4个字节,描述段的类型,如可加载的代码段,可加载的数据段,动态链接信息等。
  • p_offset:文件偏移
    占据4个字节或8个字节,指定该段在文件中的偏移量
  • p_offset:虚拟地址
    占据4个字节或8个字节,指定该段在内存中的加载地址
  • p_paddr:物理地址
    占据4个字节或8个字节,指定该段在物理内存中的地址,对可执行文件可能没有意义
  • p_filesz:文件大小
    占据4个字节或8个字节,指定该段在文件中的大小
  • p_memsz:内存大小
    占据4个字节或8个字节,指定该段在内存中的大小,可能大于文件大小
  • p_flage:段标志
    占据4个字节或8个字节,描述段的属性,如权限(读,写,执行等)
  • p_align:对齐
    占据4个字节或8个字节,指定段在内存中的对齐方式
    这就是每一个条目的结构,这里要注意的是:程序头表的起始位置,由ELF文件头的e_phoff字段指定,在一个ELF文件中,程序头表中不止一个这样的结构,通常含有多个条目,因为不同的段,含有的权限不同,在程序加载的时候,需要按照段来加载

二.节区头表

这里为什么先将节区头表呢?了解PE文件结构的哥们应该知道,PE文件结构中也有节表,而节区要按照节表区寻找。

struct Elf32_Shdr              //共40个字节    //Shdl表示Section header
{
    Elf32_Word sh_name;        //所指向Section的名字,如".text"、".data"、".bss"等
    Elf32_Word sh_type;        //所指向Section的类型,如:符号表、字符串表等
    Elf32_Word sh_flags;       
    Elf32_Addr sh_addr;
    Elf32_Off sh_offset;       //所指向Section在ELF文件中的偏移量
    Elf32_Word sh_size;        //所指向Section的size
    Elf32_Word sh_link;        //和其关联的Section头的下标索引
    Elf32_Word sh_info;
    Elf32_Word sh_addralign;   //字节对齐
    Elf32_Word sh_entsize;
};

我们还是逐一来解释每一个字段的含义:

  • sh_name:名称索引
    占据4个字节,指定一个字符串表中的索引,该字符串表包含了所有节区的名称,这个字段实际上就是:在ELF文件头中e_shstrndx字段指向的字符串表中,该节的名称字符串索引
  • sh_type:类型
    指定节区的类型,常见的类型包括:SHT_NULL(未使用的节区),SHT_PROGBITS(包含程序定义的信息,如代码段,数据段),SHT_SYMTAB(包含符号表),SHT_STRTAB(包含字符串表)
  • sh_flags:标志
    描述节区的属性,包含权限(读,写,执行等)
  • sh_addr:虚拟地址
    指定节区在内存中的虚拟地址,对于不在内存中执行的节区,这个字段可能没有意义
  • sh_offset:文件偏移
    指定节区在ELF文件中的偏移量,标识该截取在文件中的位置
  • sh_size:文件大小
    指定节区在文件中的大小,表示该节区在ELF文件中所占据的字节数
  • sh_link:链接
    对于某些类型的节区,此字段可能是符号表索引或者字符串表索引,表示与该节区相关联的符号表或者字符串表
  • sh_info:信息
    对于某些类型的节区,此字段提供额外的信息,具体含义取决于节区的类型
  • sh_addralign:字节对齐
    指定节区在内存中的对齐大小,即节区在内存中的起始地址需要满足对齐的要求
  • sh_entsize:
    对于某些类型的节区,此字段指定节区中每个条目的大小,适用于包含数组的节区。
    到这里节区头表的各个字段的含义我们就介绍完了,需要注意的是,这里的节区的起始位置,由ELF文件头的e_shoff字段指定,与程序头表一样,这里不止一个该结构,每一个节区有一个节区头表,他们是相连的,根据节区头表,我们就可以在ELF文件中找到对应的节区了。
    至此,我们了解到了ELF文件结构中的所有字段,我们来给出一张图,让大家深入理解ELF文件结构:
    ELF文件结构连接图
    我们来看看:首先,通过ELF文件头,可以找到程序头表,在这里,是段的描述,包含了每个段的权限,然后通过程序头表,我们又可以找到节区头表,这样,我们就可以精确得找到每一个我们想找的节区。

段与节:

很多人都对段与节不是很理解,这里就给大家讲解讲解,ELF文件的程序头表不是描述了段的信息嘛?实际上这里的段就是几个相同权限的节区组合而成的,比如说,像只读,不可写,不可执行的几个节区,组成一个段,这个段描述保存在程序头表中,但是每一个节区的描述还是在节区头表中有记载,所以我们通过节区头表还是能精确地找到这个段中的每一个节。

虚拟内存装载

我们来举一个例子,就是说,我们现在使用的PC,运行内存有16个G,如果说每一个运行的程序时,都将自身的文件复制到内存中执行,那我们的16个G内存,肯定运行不了几个进程,所以就提出来了虚拟内存的概念,就是让每一个应用程序相信自己有独立的4GB空间,然后将自己的映像贴在该内存中,然后执行。

  • 我们先来讲讲,如何将磁盘上存的文件如何贴到内存中:
    在磁盘上,为了节省空间,每一个节区或者段之间的距离都缩短了很多,大家在学习PE文件结构和ELF文件结构的时候都发现了两个概念:虚拟内存地址和文件地址,那么将磁盘上的文件加载到内存中的可执行状态,如何加载呢?我们这里以ELF文件为例来讲解一下,首先,将ELF文件头加载到内存(不管是ELF文件还是PE文件,文件头在磁盘和内存中的状态都是一样的),然后找到程序头表,在每一个程序头表的文件偏移上找到该段的文件位置,然后加载到内存中(就是程序头表中记载的虚拟内存地址),在这中间还要注意对齐问题,将所有的段加载完之后,还要兼顾后面的节区头表进行修改,最终符合程序头表的内存地址,大小,对齐和节区头表的内存地址,大小和对齐就算加载完成。

在加载到内存中后,已经是可以执行的状态了,但是还是不能执行,因为还有高2G的内核空间
所有的应用程序,都共用一个内核内存,这一个内核,就可以处理所有的应用程序需求。如图所示;
虚拟内存
更底层的知识,就需要大家进行体系化学习了,今天的讲解就到此结束,如果发现有什么理解上的错误,还请大佬指正,我们大家共同进步!!!

文章来源:https://blog.csdn.net/qq_73985089/article/details/135653479
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。