burpow
第11节-ELF文件格式

第11节-ELF文件格式

第 11 节:ELF 文件格式

所属课程:操作系统自学路线:面向网络安全、逆向工程与漏洞分析
所属周次:第 6 周
课程主题:可执行文件与程序装载
本节目标:理解 Linux 下 ELF 可执行文件的基本结构,掌握 ELF Header、Program Header、Section Header、.text.data.bss.rodata.plt.got 等概念,并能使用 readelfobjdumpfile 等工具观察 ELF 文件。


1. 本节课你要学会什么

学完这一节,你应该能回答下面几个问题:

  1. ELF 是什么?
  2. Linux 为什么能识别一个文件是可执行文件?
  3. ELF Header 里保存了什么?
  4. Program Header 和 Section Header 有什么区别?
  5. .text.data.bss.rodata 分别放什么?
  6. 动态链接和静态链接有什么区别?
  7. .plt.got 是什么?
  8. 为什么逆向工程必须理解 ELF?
  9. 为什么漏洞利用经常关注 ELF 保护机制和段权限?
  10. 如何用 readelfobjdumpnmldd 查看 ELF 信息?

本节课的主线是:

1
C 源代码 -> 编译链接 -> ELF 文件 -> 操作系统识别格式 -> 装载到内存 -> 程序运行

前面你已经学习了程序如何运行、汇编、系统调用;这一节开始具体观察 Linux 可执行文件本身的结构。


2. ELF 是什么

ELF 全称是:

1
Executable and Linkable Format

中文常译为:

1
可执行与可链接格式

ELF 是 Linux、Unix-like 系统中常见的二进制文件格式。

常见 ELF 文件包括:

  • 可执行程序
  • 目标文件 .o
  • 共享库 .so
  • core dump 文件

例如:

1
2
3
4
5
/bin/ls
/usr/bin/bash
libc.so.6
hello.o
core

它们都可能是 ELF 格式。


3. ELF 为什么重要

ELF 很重要,因为它连接了几个关键领域。

3.1 对操作系统

操作系统需要根据 ELF 文件中的信息知道:

  • 这是不是可执行文件
  • 面向什么 CPU 架构
  • 程序入口点在哪里
  • 哪些内容需要加载到内存
  • 每个内存区域权限是什么
  • 是否需要动态链接器
  • 依赖哪些共享库

3.2 对逆向工程

逆向 ELF 程序时,你要看:

  • 入口点
  • 函数代码
  • 字符串
  • 符号表
  • 动态库依赖
  • 导入函数
  • PLT/GOT
  • 段权限

这些都来自 ELF 结构。


3.3 对漏洞利用

漏洞利用中常关注:

  • 是否开启 PIE
  • 是否开启 NX
  • 是否有 Canary
  • RELRO 状态
  • GOT 是否可写
  • libc 是否动态链接
  • 程序基址是否固定
  • 可执行段权限

这些都和 ELF 装载、链接和保护机制有关。


4. 准备一个示例程序

创建 elf_demo.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>
#include <stdlib.h>

int global_var = 123;
int global_zero;
const char *message = "hello elf";

void helper() {
printf("helper: %s\n", message);
}

int main() {
int local_var = 456;
int *heap_var = malloc(sizeof(int));

if (heap_var == NULL) {
return 1;
}

*heap_var = 789;

printf("global_var=%d\n", global_var);
printf("global_zero=%d\n", global_zero);
printf("local_var=%d\n", local_var);
printf("heap_var=%d\n", *heap_var);

helper();
free(heap_var);
return 0;
}

编译:

1
gcc -g -O0 elf_demo.c -o elf_demo

后面所有实验都可以围绕这个程序展开。


5. 用 file 识别 ELF

执行:

1
file elf_demo

你可能看到类似:

1
elf_demo: ELF 64-bit LSB pie executable, x86-64, dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID..., with debug_info, not stripped

逐段解释:

字段 含义
ELF 文件格式是 ELF
64-bit 64 位程序
LSB 小端序
pie executable 位置无关可执行文件,支持地址随机化
x86-64 目标 CPU 架构
dynamically linked 动态链接
interpreter 动态链接器路径
with debug_info 包含调试信息
not stripped 没有去除符号

这一条命令能快速告诉你二进制的基本属性。

逆向拿到陌生 Linux 文件时,第一步通常就是:

1
file target

6. ELF 文件的整体结构

一个 ELF 文件可以从两个角度看。

1
2
链接视角:Section
装载视角:Segment

简化图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+----------------------+
| ELF Header |
+----------------------+
| Program Header Table |
+----------------------+
| .text |
| .rodata |
| .data |
| .bss |
| .plt |
| .got |
| .symtab |
| .strtab |
+----------------------+
| Section Header Table |
+----------------------+

注意:

这个图只是简化。

真实 ELF 中各部分位置和数量会根据编译选项、链接方式、是否 strip、是否 PIE 等发生变化。


7. ELF Header 是什么

ELF Header 是 ELF 文件开头的一段头部信息。

它告诉系统:

  • 这是 ELF 文件
  • 32 位还是 64 位
  • 大端还是小端
  • 目标架构是什么
  • 文件类型是什么
  • 程序入口点在哪里
  • Program Header Table 在哪里
  • Section Header Table 在哪里

查看:

1
readelf -h elf_demo

你会看到类似:

1
2
3
4
5
6
7
8
9
ELF Header:
Magic: 7f 45 4c 46 ...
Class: ELF64
Data: 2's complement, little endian
Type: DYN (Position-Independent Executable file)
Machine: Advanced Micro Devices X86-64
Entry point address: 0x1080
Start of program headers: 64 (bytes into file)
Start of section headers: ...

8. ELF Magic Number

ELF 文件开头有魔数。

1
7f 45 4c 46

其中:

1
2
3
45 = 'E'
4c = 'L'
46 = 'F'

所以开头可以理解为:

1
0x7f 'E' 'L' 'F'

操作系统和工具可以通过这个魔数识别 ELF 文件。

你可以用:

1
xxd -l 16 elf_demo

查看前 16 字节。

你会看到类似:

1
00000000: 7f45 4c46 0201 0100 ...

这就是 ELF 标识。


9. Entry point address 是什么

readelf -h 中有一项:

1
Entry point address

它表示程序入口点地址。

注意:

入口点通常不是 main

程序真正开始执行的位置通常是运行时启动代码。

大致流程:

1
2
3
4
ELF entry point
-> 动态链接器/运行时初始化
-> __libc_start_main
-> main

所以你不能简单认为入口点就是业务逻辑开始处。

逆向时,需要从入口点追踪到 main,或者让工具帮你识别 main


10. Program Header 是什么

Program Header Table 描述程序装载时需要的段。

它是给操作系统加载器看的。

查看:

1
readelf -l elf_demo

你会看到多个 Program Header,例如:

1
2
3
4
5
6
Type           Offset             VirtAddr           PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000000000 ... R
LOAD 0x0000000000001000 0x0000000000001000 ... R E
LOAD 0x0000000000002000 0x0000000000002000 ... R
LOAD 0x0000000000003000 0x0000000000004000 ... RW

重点看:

字段 含义
Type 段类型
Offset 文件中偏移
VirtAddr 加载到内存后的虚拟地址
FileSiz 文件中占用大小
MemSiz 内存中占用大小
Flags 权限,如 R/W/E
Align 对齐

11. LOAD Segment

LOAD 类型表示这个段需要被加载到内存。

常见权限:

1
2
3
R    可读
R E 可读 + 可执行
RW 可读 + 可写

例如:

1
2
3
R E 段通常包含代码
RW 段通常包含可写数据
R 段可能包含只读数据

这和内存保护有关。

正常情况下:

1
2
代码段应该可执行但不可写
数据段应该可写但不可执行

这就是 W^X 思想的一部分:

1
Writable 和 Executable 尽量不要同时出现

12. Section Header 是什么

Section Header Table 描述 ELF 中的节。

它更多是给链接器、调试器、逆向工具看的。

查看:

1
readelf -S elf_demo

你会看到很多 section,例如:

1
2
3
4
5
6
7
8
9
.text
.rodata
.data
.bss
.plt
.got
.symtab
.strtab
.debug_info

节更细,段更粗。

一个 Segment 可能包含多个 Section。

可以这样理解:

1
2
Section:链接和分析视角,细粒度
Segment:装载和运行视角,粗粒度

13. Program Header 和 Section Header 的区别

这是 ELF 学习中的重点。

对比 Program Header Section Header
面向对象 操作系统加载器 链接器、调试器、分析工具
关注问题 如何加载到内存 文件内部如何组织内容
单位 Segment Section
命令 readelf -l readelf -S
运行是否必须 可执行装载通常需要 运行时不一定必须

一个被 strip 的 ELF 仍然能运行,甚至 Section Header 也可能被去掉或破坏,但只要必要的 Program Header 和装载信息存在,系统仍可能执行它。

逆向工具则非常依赖 Section 信息来帮助分析。


14. .text

.text 通常存放程序机器指令。

例如:

  • main
  • helper
  • 编译器生成的代码
  • 运行时相关代码

查看反汇编:

1
objdump -d -M intel elf_demo

只看 .text

1
objdump -d -M intel -j .text elf_demo

.text 通常属于可执行段。

权限一般是:

1
R E

即:

1
可读、可执行、不可写

15. .rodata

.rodata 是只读数据节。

常见内容:

  • 字符串常量
  • const 数据
  • switch jump table 等只读表

例如程序中的:

1
2
const char *message = "hello elf";
printf("global_var=%d\n", global_var);

这些字符串通常会放到 .rodata

查看:

1
objdump -s -j .rodata elf_demo

或者:

1
strings elf_demo

逆向中,.rodata 很重要,因为字符串常常是定位关键逻辑的入口。


16. .data

.data 存放已初始化的全局变量和静态变量。

例如:

1
int global_var = 123;

因为它有初始值,所以需要在文件中保存这个初始值。

查看 section:

1
readelf -S elf_demo | grep .data

查看符号:

1
nm elf_demo | grep global_var

你可能看到 global_var 类型为 D,表示它在已初始化数据区。


17. .bss

.bss 存放未初始化或初始化为 0 的全局变量和静态变量。

例如:

1
int global_zero;

它初始值为 0。

.bss 的特点是:

1
2
3
文件中通常不真正保存一大堆 0
只记录内存中需要预留多大空间
加载时由系统初始化为 0

所以 .bss 在文件中占用可能很小,但在内存中会占空间。

这就是 Program Header 中 FileSizMemSiz 可能不同的原因之一。


18. .symtab.strtab

.symtab 是普通符号表。

.strtab 是普通字符串表,保存符号名。

如果程序没有被 strip,你可以用:

1
nm elf_demo

看到:

1
2
3
4
main
helper
global_var
global_zero

如果执行:

1
2
3
cp elf_demo elf_demo_stripped
strip elf_demo_stripped
nm elf_demo_stripped

可能看到:

1
no symbols

这说明普通符号表被移除了。

但程序仍然可以运行。


19. .dynsym.dynstr

动态链接程序还需要动态符号表。

.dynsym 保存动态链接相关符号。

.dynstr 保存动态符号名字符串。

查看动态符号:

1
readelf --dyn-syms elf_demo

或:

1
objdump -T elf_demo

你可能看到:

1
2
3
4
printf
malloc
free
__libc_start_main

这些动态符号用于运行时动态链接。

即使程序被 strip,动态符号往往仍然保留一部分,因为动态链接器需要它们。


20. 动态链接是什么

动态链接表示程序运行时依赖共享库。

例如:

1
ldd elf_demo

可能看到:

1
2
3
linux-vdso.so.1
libc.so.6
/lib64/ld-linux-x86-64.so.2

说明程序依赖 libc 和动态链接器。

动态链接优点:

  • 可执行文件较小
  • 多个程序共享同一份库
  • 库可以单独更新

缺点:

  • 运行时需要依赖库存在
  • 分析时需要考虑外部库调用
  • 地址可能受 ASLR 影响

21. 静态链接是什么

静态链接会把库代码尽量打包进可执行文件。

编译示例:

1
gcc -static -O0 elf_demo.c -o elf_demo_static

可能会生成较大的文件。

查看:

1
2
file elf_demo_static
ldd elf_demo_static

你可能看到:

1
not a dynamic executable

静态链接优点:

  • 依赖少
  • 部署方便

缺点:

  • 文件大
  • 逆向时库代码混入程序,函数更多
  • 安全更新不如动态库灵活

22. .plt 是什么

.plt 全称:

1
Procedure Linkage Table

可以理解为动态函数调用跳板。

当程序调用外部库函数,比如:

1
printf("hello\n");

反汇编中可能看到:

1
call printf@plt

这表示程序不是直接调用 libc 中最终地址,而是先调用 PLT 跳板。

PLT 会配合 GOT 和动态链接器找到真实函数地址。

入门理解:

1
PLT 是调用外部函数的中转站。

23. .got 是什么

.got 全称:

1
Global Offset Table

GOT 中保存一些运行时需要解析的地址。

对于动态链接函数,GOT 可能保存最终解析出的 libc 函数地址。

简化理解:

1
2
PLT 负责跳转逻辑
GOT 保存目标地址

这对漏洞利用很重要。

早期或特定保护较弱情况下,如果攻击者能改写 GOT 表项,就可能把某个函数调用重定向到别的位置。

例如把 printf 的 GOT 项改成 system

现代系统通过 RELRO 等机制增加 GOT 篡改难度。


24. 观察 PLT 和 GOT

查看 PLT:

1
objdump -d -M intel -j .plt elf_demo

也可以看反汇编中外部函数调用:

1
objdump -d -M intel elf_demo | grep '@plt'

你可能看到:

1
2
3
call printf@plt
call malloc@plt
call free@plt

查看重定位信息:

1
readelf -r elf_demo

你可能看到和外部函数相关的重定位项。

这些信息和动态链接密切相关。


25. 重定位是什么

重定位可以理解为:

某些地址在编译链接时还不能最终确定,需要在链接或加载时修正。

例如动态库函数地址运行时才知道。

因为 ASLR 会让 libc 每次加载地址不同。

所以程序不能简单写死:

1
printf 永远在某个固定地址

动态链接器需要在运行时解析地址,并把相关位置修正好。

查看重定位:

1
readelf -r elf_demo

你可能看到:

1
2
3
R_X86_64_JUMP_SLOT printf
R_X86_64_JUMP_SLOT malloc
R_X86_64_JUMP_SLOT free

这些通常和动态函数调用有关。


26. PIE 是什么

PIE 全称:

1
Position Independent Executable

中文可理解为:

1
位置无关可执行文件

启用 PIE 后,程序代码可以加载到随机基址。

这配合 ASLR 提高漏洞利用难度。

查看:

1
file elf_demo

如果看到:

1
pie executable

说明它是 PIE。

编译非 PIE:

1
gcc -g -O0 -no-pie elf_demo.c -o elf_demo_no_pie

对比:

1
file elf_demo elf_demo_no_pie

非 PIE 程序代码地址通常更固定。


27. NX 是什么

NX 表示:

1
No eXecute

即某些内存区域不可执行。

例如栈和堆通常不应该执行代码。

查看 GNU_STACK:

1
readelf -W -l elf_demo | grep GNU_STACK

你可能看到权限类似:

1
RW

如果没有 E,说明栈不可执行。

NX 对缓冲区溢出利用影响很大。

早期攻击可能把 shellcode 放在栈上执行。

NX 开启后,栈上数据不能直接作为代码执行。


28. RELRO 是什么

RELRO 全称:

1
Relocation Read-Only

它和重定位、GOT 保护有关。

简化理解:

1
RELRO 让某些重定位完成后的区域变成只读,降低 GOT 被篡改风险。

常见状态:

  • No RELRO
  • Partial RELRO
  • Full RELRO

如果安装了 checksec,可以查看:

1
checksec --file=elf_demo

如果没有 checksec,可以先记住概念,后面漏洞防护章节会再讲。


29. Canary 和 ELF

Stack Canary 是编译器插入的栈保护机制。

它不只是 ELF 格式本身的字段,但会反映在二进制中。

如果程序启用 Canary,可能会导入:

1
__stack_chk_fail

查看:

1
readelf --dyn-syms elf_demo | grep stack_chk

如果看到 __stack_chk_fail,说明可能启用了栈保护。

编译关闭 Canary:

1
gcc -g -O0 -fno-stack-protector elf_demo.c -o elf_demo_no_canary

后面漏洞防护章节会更深入分析。


30. ELF 和逆向工程

逆向 ELF 程序时,可以按这个顺序观察:

  1. file 看文件类型
  2. readelf -h 看 ELF Header
  3. readelf -l 看装载段和权限
  4. readelf -S 看节表
  5. strings 看字符串
  6. nm 看符号
  7. objdump -d 看反汇编
  8. ldd 看动态库依赖
  9. readelf -r 看重定位
  10. Ghidra / IDA 深入分析函数逻辑

这些工具不是互相替代,而是从不同角度观察同一个 ELF。


31. ELF 和漏洞利用

漏洞利用中,ELF 信息能回答很多关键问题:

问题 相关信息
程序代码地址是否固定 PIE
栈是否可执行 NX / GNU_STACK
GOT 是否容易改写 RELRO
是否有栈保护 Canary / __stack_chk_fail
是否动态链接 libc ldd / dynamic section
外部函数怎么调用 PLT/GOT
字符串在哪里 .rodata
函数名是否保留 .symtab / strip

所以做 Pwn 题或分析崩溃时,第一步经常是检查二进制保护和 ELF 结构。


32. ELF 和恶意代码分析

Linux 恶意样本也可能是 ELF。

分析时关注:

  • 是否静态链接
  • 是否 strip
  • 是否 UPX 加壳
  • 是否包含可疑字符串
  • 是否调用网络相关函数
  • 是否调用 execveforkptrace
  • 是否使用 mprotect 修改内存权限
  • 是否使用 dlopen / dlsym 动态解析函数
  • 是否有异常节名或异常权限

例如:

1
2
3
4
5
file sample
strings sample
readelf -S sample
readelf -l sample
objdump -T sample

这些命令能快速建立样本画像。


33. Linux 实验:完整观察 ELF

围绕 elf_demo 执行:

1
2
3
4
5
6
7
8
9
file elf_demo
readelf -h elf_demo
readelf -l elf_demo
readelf -S elf_demo
ldd elf_demo
nm elf_demo | grep -E 'main|helper|global'
objdump -d -M intel -j .text elf_demo
objdump -s -j .rodata elf_demo
readelf -r elf_demo

你要记录:

  • ELF 类型
  • 是否 64 位
  • 是否 PIE
  • 入口点地址
  • LOAD 段权限
  • .text.rodata.data.bss 是否存在
  • 动态库依赖
  • 是否能看到 main / helper
  • 是否能看到字符串 hello elf
  • 是否有 printf@pltmalloc@pltfree@plt

34. Linux 实验:strip 前后对比

执行:

1
2
cp elf_demo elf_demo_stripped
strip elf_demo_stripped

对比:

1
2
3
4
5
file elf_demo elf_demo_stripped
nm elf_demo | head
nm elf_demo_stripped
strings elf_demo_stripped | grep elf
objdump -d -M intel elf_demo_stripped | head -50

思考:

  • strip 后程序还能运行吗?
  • strip 后 nm 是否还能看到普通符号?
  • 字符串是否还在?
  • 反汇编是否还能看到代码?
  • 逆向难度增加在哪里?

35. Linux 实验:PIE 对地址的影响

编译两个版本:

1
2
gcc -g -O0 elf_demo.c -o elf_demo_pie
gcc -g -O0 -no-pie elf_demo.c -o elf_demo_no_pie

修改程序或用 GDB 打印 main 地址。

也可以运行:

1
gdb ./elf_demo_pie

GDB 中:

1
2
3
4
break main
run
p/x main
quit

多运行几次。

再对 elf_demo_no_pie 做同样操作。

观察:

  • PIE 版本 main 地址是否变化
  • no-PIE 版本 main 地址是否更固定
  • 这和 ASLR 有什么关系

36. 本节重点总结

你需要记住这些核心结论:

  1. ELF 是 Linux 常见的可执行、目标文件、共享库格式。
  2. ELF Header 描述文件基本属性和入口点等信息。
  3. Program Header 面向装载,描述哪些段加载到内存及其权限。
  4. Section Header 面向链接和分析,描述 .text.data.bss 等节。
  5. .text 通常存放代码,.rodata 存放只读数据和字符串。
  6. .data 存放已初始化全局变量,.bss 存放未初始化或零初始化全局变量。
  7. 动态链接程序依赖共享库,通常通过 PLT/GOT 调用外部函数。
  8. PIE、NX、RELRO、Canary 等保护机制会影响漏洞利用难度。
  9. readelfobjdumpnmlddstrings 是分析 ELF 的基础工具。
  10. 理解 ELF 是 Linux 逆向、Pwn、恶意代码分析的重要基础。

37. 本节课后作业

作业 1:ELF Header 分析

elf_demo 执行:

1
2
file elf_demo
readelf -h elf_demo

提交内容:

1
2
3
4
5
6
7
1. 文件是否是 ELF
2. 32 位还是 64 位
3. 大端还是小端
4. 目标架构是什么
5. 文件类型是什么
6. Entry point address 是多少
7. 为什么入口点通常不是 main

作业 2:Program Header 和权限分析

执行:

1
readelf -l elf_demo

提交内容:

1
2
3
4
5
1. 有几个 LOAD 段
2. 每个 LOAD 段权限是什么
3. 哪个段可能包含代码
4. 哪个段可能包含可写数据
5. FileSiz 和 MemSiz 是否有不同,可能为什么

作业 3:Section 分析

执行:

1
2
3
readelf -S elf_demo
objdump -s -j .rodata elf_demo
objdump -d -M intel -j .text elf_demo

提交内容:

1
2
3
4
5
6
1. 是否存在 .text
2. 是否存在 .rodata
3. 是否存在 .data
4. 是否存在 .bss
5. 在 .rodata 中找到哪些字符串
6. 在 .text 中找到哪些函数或 call

作业 4:符号和 strip 对比

执行:

1
2
3
4
5
nm elf_demo | grep -E 'main|helper|global'
cp elf_demo elf_demo_stripped
strip elf_demo_stripped
nm elf_demo_stripped
strings elf_demo_stripped | grep elf

提交内容:

1
2
3
4
5
1. strip 前能看到哪些符号
2. strip 后 nm 输出有什么变化
3. strip 后字符串是否还在
4. strip 是否影响程序运行
5. strip 为什么会增加逆向难度

作业 5:PLT/GOT 和动态链接观察

执行:

1
2
3
ldd elf_demo
objdump -d -M intel elf_demo | grep '@plt'
readelf -r elf_demo

提交内容:

1
2
3
4
5
1. 程序依赖哪些动态库
2. 看到哪些 @plt 调用
3. readelf -r 中有哪些外部函数重定位
4. PLT 和 GOT 大致分别起什么作用
5. 为什么动态链接函数地址不能简单写死

作业 6:PIE 对比

编译:

1
2
gcc -g -O0 elf_demo.c -o elf_demo_pie
gcc -g -O0 -no-pie elf_demo.c -o elf_demo_no_pie

对比:

1
file elf_demo_pie elf_demo_no_pie

用 GDB 多次查看 main 地址。

提交内容:

1
2
3
4
5
1. 哪个是 PIE
2. 哪个不是 PIE
3. 多次运行 main 地址是否变化
4. PIE 和 ASLR 有什么关系
5. PIE 为什么会增加漏洞利用难度

38. 自测题

题 1

ELF 的全称是什么?常见于哪些类型文件?

题 2

ELF Header 中有哪些重要信息?

题 3

Program Header 和 Section Header 有什么区别?

题 4

.text.rodata.data.bss 分别通常存放什么?

题 5

为什么 .bss 在文件中可能不真正保存大量 0?

题 6

动态链接和静态链接有什么区别?

题 7

PLT 和 GOT 的直觉作用是什么?

题 8

PIE、NX、RELRO 分别大致和什么安全目标有关?

题 9

为什么 strip 后程序还能运行,但逆向更难?


39. 自测题参考答案

答 1

ELF 全称是 Executable and Linkable Format。它常见于 Linux/Unix-like 系统中的可执行文件、目标文件 .o、共享库 .so 和 core dump 文件。

答 2

ELF Header 包含魔数、32/64 位、大小端、目标架构、文件类型、入口点地址、Program Header Table 位置、Section Header Table 位置等信息。

答 3

Program Header 面向操作系统加载器,描述哪些段要加载到内存及权限;Section Header 面向链接器、调试器和分析工具,描述文件内部各个节如 .text.data 等。

答 4

.text 通常存放机器指令;.rodata 存放只读数据和字符串常量;.data 存放已初始化全局/静态变量;.bss 存放未初始化或零初始化全局/静态变量。

答 5

因为 .bss 中的数据初始值为 0,文件中只需记录需要多少空间,加载时由系统把这块内存清零即可,不需要在文件中保存大量 0 字节。

答 6

动态链接在运行时依赖共享库,文件较小且库可共享;静态链接把库代码打入可执行文件,依赖少但文件更大,库代码也会混入程序中。

答 7

PLT 可以理解为调用外部函数的跳板;GOT 保存运行时解析出来的外部函数或全局地址。二者配合动态链接器完成外部函数调用。

答 8

PIE 让程序基址可随机化,配合 ASLR 增加地址预测难度;NX 让栈/堆等数据区域不可执行;RELRO 保护重定位后的区域,降低 GOT 等位置被篡改的风险。

答 9

因为 CPU 执行程序主要需要机器指令、入口点、装载信息和必要动态链接信息,不需要普通符号名和调试信息。strip 去掉这些人类友好的信息后,程序仍能运行,但函数名、变量名、调试信息缺失,逆向更困难。


40. 下一节预告

下一节课会讲:

1
PE 文件格式与 Windows 装载

你会学习:

  • DOS Header
  • PE Header
  • Section Table
  • .text.rdata.data.rsrc
  • Import Table 和 IAT
  • DLL 加载
  • PE 与 ELF 的对照
  • 如何用 PE-bear、Detect It Easy、Ghidra 和 x64dbg 分析 Windows 可执行文件
隐藏
换装
本文作者:burpow
本文链接:https://youthfulnesszxx.github.io/2026/05/28/第11节-ELF文件格式/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可