readelf
readelf 是分析 Linux 二进制文件(如可执行文件、共享库 .so、目标文件 .o)的利器,它直接解析 ELF 格式,不依赖 BFD 库,因此提供的信息比 objdump 更底层且未经修饰。
核心前置概念:ELF 文件的“双重视角”
要理解 readelf 的参数,首先需要理解 ELF 文件的两种结构视图。同一个 ELF 文件在编译链接时和运行加载时被看待的方式是不同的:
-
链接视图(Linking View)—— 以“节”(Section)为单位
-
面向链接器(Linker)。
-
文件被划分为多个逻辑上的“节”,如代码节(
.text)、初始化数据节(.data)、未初始化数据节(.bss)、只读数据节(.rodata)。 -
包含符号表、重定位信息等,方便链接器将多个目标文件拼装在一起。
-
-
执行视图(Execution View)—— 以“段”(Segment)为单位
-
面向操作系统加载器(Loader)。
-
文件被划分为多个“段”(Program Headers),一个段通常包含一个或多个属性相同的“节”。
-
加载器不关心
.text或.rodata的具体区分,它只关心一块内存区域是可读且可执行(R E)还是可读可写(RW)。
-
常用参数速查表
| 短参数 | 长参数 | 核心作用 |
|---|---|---|
-h |
--file-header |
显示 ELF 文件头(全局信息) |
-S |
--section-headers |
显示所有节(Sections)的头部信息 |
-l |
--program-headers |
显示所有段(Segments)的加载信息 |
-e |
--headers |
等价于 -h -l -S 的组合 |
-s |
--syms |
显示符号表(函数、全局变量等) |
-r |
--relocs |
显示重定位条目(分析 GOT/PLT 必备) |
-d |
--dynamic |
显示动态链接信息(依赖的 .so 等) |
-x <名/号> |
--hex-dump=<name> |
以十六进制和 ASCII 形式转储指定节的内容 |
-p <名/号> |
--string-dump=<name> |
以字符串形式提取并显示指定节中的内容 |
-n |
--notes |
显示核心转储(Core dump)或特定的 Note 段信息 |
核心参数深度解析与应用场景
1. 文件全局概览:-h (File Header)
解析 ELF 文件的最开头部分。它定义了整个文件的基本属性。
-
关键关注点:
-
Magic(魔数):
7f 45 4c 46,标识这是一个 ELF 文件。 -
Class & Data: 标识文件是 32 位还是 64 位,以及字节序(Little-endian 还是 Big-endian)。
-
Type: 文件类型。常见的有
EXEC(可执行文件)、DYN(共享目标文件/PIE 可执行文件)、REL(可重定位对象.o)。 -
Machine: 目标架构(如 Advanced Micro Devices X86-64, ARM AArch64)。
-
Entry point address: 程序的真正入口点(如
_start的地址),在二进制分析和逆向工程中,这是追踪程序控制流的第一站。
-
2. 链接视图剖析:-S (Section Headers)
列出所有的节表项及其属性。
-
核心概念与应用:
-
查看各节的 Size(大小) 和 Address(虚拟地址)。
-
Flags(标志位):
W(Write),A(Alloc),X(Execute)。例如,.text应该是AX,.data应该是WA。 -
异常排查/安全分析: 如果发现某个节(如
.bss或自建的非常规节)同时具有W和X(可读写且可执行)权限,这通常是壳(Packer)的特征,或者是易受 Shellcode 攻击的脆弱点。
-
3. 执行视图剖析:-l (Program Headers)
展示操作系统如何将该文件映射到虚拟内存中。
-
核心概念与应用:
-
Type = LOAD: 这些段会被实际加载到内存中。通常有两个主要的 LOAD 段:一个映射代码和只读数据(权限
R E),另一个映射可写数据(权限RW)。 -
Type = INTERP: 指明动态链接器的路径(如
/lib64/ld-linux-x86-64.so.2)。如果该路径在目标系统上不存在,程序将报 "No such file or directory" 错误(即使二进制文件本身存在)。 -
GNU_STACK: 指示栈的权限。如果在安全分析中看到此段带有
E(可执行)标志,说明系统的 NX(No-eXecute)保护未对该文件生效。
-
4. 符号表查看:-s (Symbols)
列出程序中定义的和引用的符号(函数名、变量名)。
-
核心概念与应用:
-
.symtab:静态符号表,包含所有符号,通常在执行strip命令后会被剥离(去除调试信息)。 -
.dynsym:动态符号表,仅包含动态链接所需的符号,无法被 strip 移除,否则程序无法动态运行。 -
在编写 Exploit 或进行反汇编定位时,可以通过该命令快速查找
system、printf等关键库函数的导入情况。
-
5. 重定位信息:-r (Relocations)
显示文件中的重定位记录,指示链接器在加载时需要修改哪些位置的地址。
-
核心概念与应用:
-
对于动态链接的程序,重点关注
.rel.dyn(数据引用)和.rel.plt(函数调用)。 -
这些信息直接映射了 GOT(全局偏移表) 和 PLT(过程链接表) 的结构。在内存破坏漏洞利用中,通过
-r获取目标函数在 GOT 表中的确切虚拟地址,是实施 GOT 覆写(GOT Overwrite)攻击劫持控制流的前提。
-
6. 动态链接属性:-d (Dynamic)
解析 .dynamic 节,显示动态链接器需要的信息。
-
核心概念与应用:
-
NEEDED: 明确列出该二进制文件直接依赖的共享库(如
libc.so.6)。 -
RPATH / RUNPATH: 程序在运行时搜索共享库的硬编码首选路径。如果配置不当,可能导致动态库劫持(Library Hijacking)。
-
7. 数据提取与分析:-x 与 -p
用于直接审查特定节的二进制内容。
-
-x <name>(Hex dump): 例如readelf -x .rodata binary_file。常用于提取硬编码的常量、密钥、或者验证特定的汇编字节码。 -
-p <name>(String dump): 例如readelf -p .rodata binary_file。类似于strings命令,但作用范围严格限制在指定的节,非常适合精确提取只读数据段中的报错提示和静态字符串。