首页
社区
课程
招聘
[原创] ELF文件中的各个节区
发表于: 2021-12-25 13:54 22084

[原创] 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期)

最后于 2022-2-19 15:32 被ashimida编辑 ,原因:
收藏
免费 7
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//