在之前的专栏中,我们已经将结果了在Windows下可执行文件(PE)文件格式了,个人认为在学习了PE文件结构之后,学习ELF文件结构会容易很多,大家可以去这个专栏学习PE文件格式:PE文件结构学习,本章内容ELF文件概述也会结合着PE文件结构来讲解。
这里先给出PE文件格式的图,大家可以了解了解:
这里先给出一张图,来大致了解ELF文件结构:
本图出自:星盟安全团队公开课PPT
在图中,我们可以看到,ELF文件大致分为三个部分:文件Header,节区块,与节区头表部分。
我们现在就分别来讲讲这三大部分:
在文件头中,又包含了ELF文件头和程序头表。
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"的下标
};
我们来逐一讲解一下其中每一个字段的含义:
程序头表是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
};
我们来逐一讲解每一个字段的含义:
这里为什么先将节区头表呢?了解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;
};
我们还是逐一来解释每一个字段的含义:
很多人都对段与节不是很理解,这里就给大家讲解讲解,ELF文件的程序头表不是描述了段的信息嘛?实际上这里的段就是几个相同权限的节区组合而成的,比如说,像只读,不可写,不可执行的几个节区,组成一个段,这个段描述保存在程序头表中,但是每一个节区的描述还是在节区头表中有记载,所以我们通过节区头表还是能精确地找到这个段中的每一个节。
我们来举一个例子,就是说,我们现在使用的PC,运行内存有16个G,如果说每一个运行的程序时,都将自身的文件复制到内存中执行,那我们的16个G内存,肯定运行不了几个进程,所以就提出来了虚拟内存的概念,就是让每一个应用程序相信自己有独立的4GB空间,然后将自己的映像贴在该内存中,然后执行。
在加载到内存中后,已经是可以执行的状态了,但是还是不能执行,因为还有高2G的内核空间
所有的应用程序,都共用一个内核内存,这一个内核,就可以处理所有的应用程序需求。如图所示;
更底层的知识,就需要大家进行体系化学习了,今天的讲解就到此结束,如果发现有什么理解上的错误,还请大佬指正,我们大家共同进步!!!