-
-
[原创] ELF文件中的各个节区
-
发表于: 2021-12-25 13:54 22084
-
版权声明:本文为CSDN博主「ashimida@」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lidan113lidan/article/details/119901186
更多内容可关注微信公众号
ELF节区信息概述:
节区名
节区说明
备注
.rodata1
和rodata类似,只存放只读数据
.comment
存放编译器版本信息,如字符串 "GCC:(GNU)4.2.0"
.debug
存放调试信息
.shstrtab
节区头部表名字的字符串表(Section Header String Table)
.plt
过程链接表(Procedure Linkage Table),用来保存长跳转格式的函数调用
.got
全局偏移表(Global Offset Table),在地址无关代码中才需要,所有只读段需要修复的位置都间接引用到此表, 因此只读段自身就无需修复,只需修复此got表即可.
.got表是在编译期间确定,静态链接期间生成的
而.plt表是在静态链接期间确定,静态链接期间生成的
可执行文件通常不论如何编译都有got表,这是因为是否加入got表是由编译(cc1)期间决定的,而可执行文件默认连接的多个目标文件默认都有got表元素.
.got.plt
.symtab
(静态链接)符号表的作用是保存当前目标文件中所有对符号的定义和引用.
* 符号表中UND的符号不是当前目标文件定义的,也就是对符号的引用
* 符号表中其他非UND的符号,全部是定义在当前目标文件中的,也就是对符号的定义
默认所有非.L开头的符号都要输出,.L开头的不输出
.strtab
静态链接字符串表,其中记录的是静态链接符号表中使用到的字符串,这些字符串仅供静态链接符号表使用,strip的时候会将.symtab和.strtab两个段完全清除.
符号表的第一个元素必须是STN_UNDEF,其代表一个未定义的符号索引,此符号表项内部所有值都为0
.group
是用来记录多个节区的相关信息的,比如说代码段引用了数据段,这种信息是为了保证二进制处理时候不会操作错误的,像是elf自动生成的,细节见链接
File Format (Linker and Libraries Guide)
section groups
动态链接相关节区
.interp
.interp整个段的内容就是一个字符串,此字符串为系统中动态链接器的路径,如:
/lib/ld-linux-aarch64.so.1
linux的可执行文件加载时会去寻找可执行文件所需的动态链接器
.dynamic
.interp保存的是动态链接器路径,.dynamic中保存的是动态链接器用到的基本信息,如动态链接符号表(.dynsym),字符串表(.dynstr),重定位表(.rela.dyn/rela.plt),依赖的运行时库,库查找路径等
.rela.dyn
记录所有变量的动态链接重定位信息(.rela.plt记录的是函数),与.rela.plt一起,是系统中唯二的两张动态链接重定位表。
.rela.dyn记录除了.plt段之外所有段的动态链接重定位信息,若开启了地址无关代码,那么这些信息都应该只与.got段的地址有关.
.rela.plt
过程连接表的动态链接重定位表,只要有过程链表,通常就会有此表,因为plt导致了绝对跳转,那么所有plt表中所有需要动态链接/重定位的绝对地址(可能在.got.plt或.got中,依赖于是否开启延迟绑定),都需要通过.rela.plt记录
此表中记录所有全局函数(长跳转函数)的动态链接重定位信息,与.rela.dyn一起,是系统中唯二的两张动态链接重定位表。
.rela.plt实际上记录的是.plt段的动态链接重定位信息,若未开启lazy binding,则这这些信息应该都只与.got段的地址有关;若开启lazy binding,则这些信息应该都只与.got.plt段的地址有关;
需要动态链接重定位的原因主要是模块有导入符号的存在,这些符号在运行时才能确定,地址无关代码并不能改变未定符号的本质(即不影响模块是否需要动态链接重定位),但地址无关代码可以让重定位变得简单(如仅重定位 .got/ .data/ .got.plt)
.dynsym
动态链接符号表,其格式和.symtab一样,readelf -s会尝试同时输出.dynsym和.symtab,如右图.
动态链接符号表是静态链接符号表的子集,其只保留了与动态链接相关的符号信息,所有模块内部符号则不保留(因此静态符号表是可以被strip的,其只对于目标文件有用).
动态链接符号表中未定义的符号(符号引用),又称为导入符号(类似导入表)
动态链接符号表中已定义的符号(符号定义),又称为导出符号(类似导出表)
全局符号默认是直接导出到动态链接重定位表的
.dynstr
动态链接符号表用到的字符串表,其与静态链接字符串表(.strtab)分开的原因应该是.strtab是可以完全strip的
链接过程图:
1.ELF文件头
ELF的基础文件格式如下图所示:
由ELF文件的第一字节开始的一个ELF32_Ehdr结构体是ELF头,其中记录ELF的基本信息包括:
* 32/64位
* 大/小端
* ELF版本号
* 文件类型(ET_REL/ET_EXE/ET_DYN,分别对应relocatable/pde/[pie|dll]文件)
* 体系结构
* 入口地址
* 程序头部表偏移,表项大小,表项个数(见2,程序头部表)
* 节区头部表偏移,表项大小,表项个数,对应的字符串表(见1,节区头部表)
* ELF文件头大小
2.节区头部表(Section Heade Table)、节区头部表字符串表(shstrtab)和字符串表(strtab)
节区头部表实际上是一个Elf_Shdr[m]数组,其中的每一个元素(表项)记录系统中一个节区的信息,包括如节区名,类型,flag,内存/文件起始地址,大小,对齐等信息,如:
通过readelf -S可读取其内容,如:
其中每一行是节区头部表中的一个表项,也代表着此elf文件中的一个节区信息(ELF文件中定位节区的方式是: ELF头=>节区头部表=>节区首地址及其他信息)
节区头部表字符串表是给节区头部表专门准备的字符串表,ELF文件中通常存在两个字符串表:
* 一个是代码中所有使用到的字符串的表,名称为.strtab
* 一个是记录所有节区名的字符串表,名称为.shstrtab
二者通常是没有关系的,节区头部表字符串表中只记录节区名称,如下:
而字符串表则记录符号表中符号相关的字符串信息,如:
3.程序头部表(Program Header Table)
程序头部表实际上是一个Elf_Phdr[n]数组,其元素(表项)个数与节区头部表项个数(m)一般是不同的.
程序头部表关注的是ELF文件加载时会有几个不同的属性的段,关心如何将相同属性的段合并为一个segment,以优化时间和空间消耗(只有dll/exe文件拥有程序头部表,relocatable文件没有程序头部表)
程序头部表格式如下:
通过readelf -l 可查看程序头部表,如下:
这里可以看到Elf_Phdr[n]数组只有8个元素,每个段(segment)包含多少个节应该是根据section的地址排序确定的,其中:
* INTERP: 此表项的offset实际上指向.interp段的内容,也就是动态链接器的全路径的字符串,如/lib/ld-linux-aarch64.so.1
* LOAD: 代表可加载的segment,只有此类型的segment在运行时会载入内存
* DYNAMIC:对于动态链接的二进制,此segment记录动态链接器信息段(.dynamic)的指针
* NOTE: 记录辅助信息,如对于core dump文件,此segment包含core dump文件创建时的进程状态信息,如导致crash的signal,pending & held signals, 进程/父进程UID,nice,寄存器值(包括当前pc)等.
* GNU_STACK: 此段并没有任何内容,但其属性用来代表当前进程的stack是否可执行.
* GNU_RELRO: 此段代表重定位之后哪些段需要设置为RO的(若.got.plt没有在这里,代表其最后不受到RO保护,因为延迟绑定,是正常的,解决方法是关闭延迟绑定)
* GNU_EH_FRAME: 保存栈帧的unwind信息,若存在此段通常指向.eh_fram_hdr(eh = Exception Handling)
* TLS: 代表线程局部存储的信息
4..text/.data/.rodata/.bss节区
这四个节区是ELF的基本节区,其中:
* .text是默认的代码段
* .data是默认的rw数据段
* .rodata是默认的只读数据段
* .bss是未初始化或初值为0的全局变量所在节区
.bss 中的数据因为默认值为0,故不需要在文件中为其分配空间,程序运行时在内存申请空间即可
这些节区实际上是在编译(cc1)的时候就确定下来了,在直接编译出的汇编代码(.s)中可以直接看到如.text/.data的定义,这就是目标文件中这些节区的由来, as只是忠实的将汇编代码转换为目标文件中对应的节区, 如:
而对于链接后的文件(exe/dll/relocatable)来说,还涉及如将多个.o中的.text段合并的过程,合并方法记录在默认的链接脚本中,可通过 ld -verbose来查看,如:
5.符号表(symtab)节区
符号表中符号的实际上指的是汇编代码或汇编器(as)中的对符号的定义。根据as手册,符号是一个中心概念,程序使用符号来命名事物,链接器使用符号来链接,调试器使用符号来调试(as并没有将目标文文件中的符号以其出现顺序排序),如:
以上代码中xx/.LC0/main 均为符号,除此之外:如file和每个section都会在最终目标文件中输出一个符号, 而以.L开头的符号则默认不会输出到目标文件(除非指定-L)选项,如:
所以说ELF文件中符号表的作用实际上是记录汇编代码中出现的符号(根据as选项不同决定.L开头的符号是否输出)。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
赞赏
- [原创] AArch64中va_list/va_start/va_arg/...的实现 19733
- [原创] AArch64函数栈的分配,指令生成与GCC实现(上) 19402
- [原创] 内核模块的加载流程 54946
- [原创] linux中的信号处理与SROP 26083
- [原创] AARCH64平台的栈回溯 30073