使用 readelf -S
命令查看 ELF 文件的解析
前言
本文档介绍了如何使用 readelf -S
命令查看 ELF 文件的节表(Section Headers),以及如何结合 ASLR(Address Space Layout Randomization)技术,理解启用和禁用 ASLR 时 ELF 文件的差异。
使用 readelf -S
查看 ELF 文件的节表
1. 基本用法
假设有一个名为 example
的 ELF 文件,可以使用以下命令查看其节表:
readelf -S example
2. 输出示例及解析
以下是一个示例输出及其解析:
There are 29 section headers, starting at offset 0x1c20:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 0000000000000200 000200 00001c 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 0000000000000220 000220 000020 00 A 0 0 4
[ 3] .note.gnu.build-i NOTE 0000000000000240 000240 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000000268 000268 000030 00 A 5 0 8
...
[28] .shstrtab STRTAB 0000000000000000 000840 000120 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
3. 解析各字段
- [Nr]:节的索引号。
- Name:节的名称。
- Type:节的类型(例如,
PROGBITS
、NOTE
、STRTAB
等)。 - Address:在内存中的加载地址。
- Off:在文件中的偏移。
- Size:节的大小。
- ES:条目大小(对于含有表项的节,如符号表)。
- Flg:节的标志(例如,A表示在内存中分配,X表示可执行等)。
- Lk:关联的链接。
- Inf:额外的信息。
- Al:对齐。
ASLR(Address Space Layout Randomization)
ASLR 是一种内存保护技术,通过随机化进程的内存地址空间来防止攻击者预测内存地址。
不使用 ASLR
在不使用 ASLR 的情况下,每次程序加载到内存中的地址是固定的。例如,可以通过以下方式禁用 ASLR:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
使用 ASLR
使用 ASLR 时,程序每次加载到内存中的地址都会变化。可以通过以下方式启用 ASLR:
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
查看差异
1. 不使用 ASLR
程序的各个节的加载地址固定。例如,.text
节(代码段)在每次运行时都加载到相同的内存地址。
2. 使用 ASLR
程序的各个节的加载地址是随机的。例如,.text
节在每次运行时加载到不同的内存地址。
可以通过多次运行 readelf -l
命令来查看程序加载地址的变化:
readelf -l ./example | grep LOAD
输出示例
不使用 ASLR
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000 0x001000 0x001000 R E 0x200000
LOAD 0x0000000000001000 0x0000000000401000 0x0000000000401000 0x000100 0x000100 RW 0x200000
使用 ASLR
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000 0x001000 0x001000 R E 0x200000
LOAD 0x0000000000001000 0x0000000000402000 0x0000000000402000 0x000100 0x000100 RW 0x200000
注意到 0x0000000000400000
地址会在每次运行时发生变化,这就是 ASLR 生效的结果。
使用 GDB 查看节的映射表
1. 启动 GDB 并加载程序
gdb ./example
2. 运行程序
在 GDB 提示符下输入:
(gdb) run
3. 查看节的映射
在 GDB 提示符下输入以下命令以查看节的映射信息:
(gdb) info files
输出示例:
Symbols from "/path/to/example".
Local exec file:
`/path/to/example', file type elf64-x86-64.
Entry point: 0x400400
0x0000000000400238 - 0x0000000000400254 is .interp
0x0000000000400254 - 0x0000000000400274 is .note.ABI-tag
0x0000000000400274 - 0x0000000000400298 is .note.gnu.build-id
0x0000000000400298 - 0x00000000004002c8 is .gnu.hash
0x00000000004002c8 - 0x0000000000400300 is .dynsym
0x0000000000400300 - 0x0000000000400328 is .dynstr
0x0000000000400328 - 0x0000000000400330 is .gnu.version
...
0x0000000000601020 - 0x0000000000601040 is .fini_array
0x0000000000601040 - 0x0000000000601048 is .jcr
0x0000000000601048 - 0x0000000000601050 is .dynamic
0x0000000000601050 - 0x0000000000601068 is .got
0x0000000000601068 - 0x0000000000602000 is .got.plt
0x0000000000602000 - 0x0000000000602018 is .data
0x0000000000602018 - 0x0000000000602020 is .bss
该输出显示了各个节的内存地址范围及其名称。
结合 ASLR 查看映射差异
1. 禁用 ASLR
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
2. 再次运行程序并查看映射
重新启动 GDB 并运行程序,查看节的内存映射:
gdb ./example
(gdb) run
(gdb) info files
3. 启用 ASLR
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
4. 再次运行程序并查看映射
重新启动 GDB 并运行程序,查看节的内存映射:
gdb ./example
(gdb) run
(gdb) info files
比较这两次输出,可以观察到 ASLR 启用和禁用时,程序各个节加载地址的变化。这些变化正是 ASLR 工作的结果。