ELF文件分析(二) 段节比较和节头表
0x01 段vs节 (Segment vs Section)
| 名称 | 段(Segment) | 节(Section) |
|---|---|---|
| 描述来源 | 程序头表 | 节头表 |
| 面向对象 | 操作系统加载器 | 链接器和调试器 |
| 核心概念 | 运行时视图 | 链接时视图 |
| 描述是否为运行必需 | 是,必须要有程序头表 | 否,节头表可选 |
| 目的 | 加载和执行 | 链接和调试 |
Important
段(Segment)和节(Section)是理解ELF文件的两种不同但相关的视角,分别服务于程序生命周期的不同阶段。
- 节 (Section) 是链接器眼中的链接视图(Linking View)。它将文件内容划分为具有特定类型和属性的逻辑单元,如代码(.text)、只读数据(.rodata)和可读写数据(.data)。这些信息对于链接器拼接目标文件和调试器定位符号至关重要。
-
段 (Segment) 则是加载器眼中的运行时视图(Execution
View)。关键在于,段是链接器为了优化加载过程,而对节进行组织和合并的结果。
链接器会将权限相似的节(如所有只读+可执行的
.text、.plt)合并成一个大的、连续的段。这样,加载器只需进行一次内存映射,而不是对十几个小节分别操作,这极大地提升了程序启动效率。
因此,它们的存在与否取决于ELF文件的类型(e_type)和用途:
- 对于可执行文件 (ET_EXEC, ET_DYN),程序头表是必须的,因为它定义了如何将程序加载到内存执行。而节头表是可选的,可以使用strip命令去除以减小文件大小。
- 对于可重定位文件/目标文件 (ET_REL),节头表是必须的,因为它包含了链接器进行符号解析和重定位所需的全部信息。而这类文件通常没有程序头表,因为它们还不能被直接执行。
0x02 ELF节头
1 | # readelf解析 -l 输出的后半部分显示了节在段中的情况 |
节头的内部结构如下
1 | typedef struct { |
0x03 各种各样的节
下面介绍一些比较常见的节,其用途、数据形式、节类型、一般情况的所属段。
大概稍微看看留意一下就好了,后面会进一步解析,因此此处记忆完全没有任何必要。需要说明的是节标志中A代表alloc(装载),X代表execute(可执行),W代表write(可写),I代表info(信息),如果没填代表没有这些标志。
| 节名称 | 用途 | 数据形式 | 节类型 | 节标志 |
|---|---|---|---|---|
| .text | 存放程序的主要可执行代码(机器指令) | 机器指令 | SHT_PROGBITS | AX |
| .rodata | 存放只读数据,如字符串常量、const
变量 |
程序中定义的常量数据 | SHT_PROGBITS | A |
| .plt | 过程链接表, 用于动态链接中函数的“懒”解析 | 可执行的跳转指令 | SHT_PROGBITS | AX |
| .data | 存放已初始化的全局变量和静态变量 | 程序中定义的变量初始值 | SHT_PROGBITS | WA |
| .bss | 存放未初始化的全局变量和静态变量。加载时由系统清零 | 在文件中不占空间 | SHT_NOBITS | WA |
| .got.plt | 全局偏移量表与 .plt
配合,存储动态链接函数的真实地址 |
函数地址指针的数组 | SHT_PROGBITS | WA |
| .dynsym | 动态符号表,仅包含动态链接所需的符号信息 | ElfN_Sym结构体数组 |
SHT_DYNSYM | A |
| .dynstr | 动态字符串表,存放 .dynsym
中符号的名字字符串 |
以null结尾的字符串 | SHT_STRTAB | A |
| .rel.* | 重定位表,包含需要链接器或加载器在运行时修正地址的信息 | ElfN_Rel/ ElfN_Rela 结构体 |
SHT_REL / SHT_RELA | A/AI |
| .hash | 查找符号的散列表 | 哈希表数据结构 | SHT_HASH / SHT_GNU_HASH | A |
| .symtab | 完整符号表,包含程序所有符号,包括静态函数、局部变量等,主要用于调试和链接。 | ElfN_Sym 结构体数组 |
SHT_SYMTAB | |
| .strtab | 字符串表 ,存放 .symtab
中符号的名字字符串 |
以null结尾的字符串 | SHT_STRTAB | |
| .shstrtab | 节头字符串表 ,存放节头的名字字符串 | 以null结尾的字符串 | SHT_STRTAB |
实例
目标文件(take.o) 节的情况:
可执行文件(take) 节的情况:
0x04 对节头表痛下黑手
节头表对程序的执行不是必需的,但对分析却至关重要。因此,移除或破坏节头表是恶意软件(Malware)和软件加壳(Packer)中一种常见的反静态分析、反逆向工程的手段。接触和了解这些技术,有助于我们分析被处理过的程序。
1. 完全剥离
直接删除运行无关的信息,如本地符号表
1 | # strip 命令的主要功能是移除符号表 (.symtab) 和调试信息,以减小文件体积。 |
2. 丢失联系
原理是修改文件头关于节头表的信息,具体是e_shoff ,
e_shentsize 和 e_shnum ,
下面是一个简单的bash脚本,通过dd命令进行置0修改。
1 |
|
3. 伪造!扭曲!变形!
通过修改文件头和节头表内容,可以实现更多在节上的操作。后续会进行细致的讨论。
0x05 小问题
关于节:
- 目标文件和可执行文件相比少了哪些节,或者说两者在节上有哪些不同?
- 分析以下节头表记录的第一个节,你发现它的特别之处了吗?
- .bss是一个特别的节,节类型是SHT_NOBITS,查阅资料,了解关于其更多的信息。
- 节头表和程序头表在文件和内存中的位置有什么特征?