Skip to content

readelf

readelf 是分析 Linux 二进制文件(如可执行文件、共享库 .so、目标文件 .o)的利器,它直接解析 ELF 格式,不依赖 BFD 库,因此提供的信息比 objdump 更底层且未经修饰。

核心前置概念:ELF 文件的“双重视角”

要理解 readelf 的参数,首先需要理解 ELF 文件的两种结构视图。同一个 ELF 文件在编译链接时和运行加载时被看待的方式是不同的:

  1. 链接视图(Linking View)—— 以“节”(Section)为单位

    • 面向链接器(Linker)。

    • 文件被划分为多个逻辑上的“节”,如代码节(.text)、初始化数据节(.data)、未初始化数据节(.bss)、只读数据节(.rodata)。

    • 包含符号表、重定位信息等,方便链接器将多个目标文件拼装在一起。

  2. 执行视图(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 命令,但作用范围严格限制在指定的节,非常适合精确提取只读数据段中的报错提示和静态字符串。