使用 readelf 分析 so 文件:ELF 结构解析全攻略

版权归作者所有,如有转发,请注明文章出处:cyrus-studio.github.io/blog/

readelf 介绍

readelf 是 GNU Binutils 提供的一个命令行工具,专门用于读取 ELF(Executable and Linkable Format)文件的结构信息。

readelf 文档:man7.org/linux/man-p...

GNU Binutils

GNU Binutils(GNU Binary Utilities)是一套由 GNU 项目维护的二进制文件处理工具集,主要用于编译、链接、反汇编、调试等场景。

它是 GCC(GNU Compiler Collection)的重要配套组件之一,处理从编译生成的目标文件到最终可执行文件的各个阶段。

主要工具:

工具 作用
as GNU 汇编器(GNU Assembler),把 .s 汇编代码编译成 .o 目标文件
ld GNU 链接器(GNU Linker),把多个 .o 文件和库打包成可执行文件或共享库
ar 归档工具(创建/修改 .a 静态库)
nm 显示符号表(Symbol Table)
objdump 显示目标文件内容(反汇编、节表、符号、重定位等)
objcopy 复制并转换目标文件格式(可用于提取或删除节)
readelf 专门查看 ELF 文件结构(文件头、节表、符号等)
strip 删除二进制文件中的符号信息(瘦身/混淆)
strings 搜索二进制文件中的可打印字符串
size 显示目标文件中各段的大小(text/data/bss)
ranlib 为 .a 静态库生成索引(方便快速链接)

Windows 下使用 readelf

虽然 readelf 工具本身是为 Unix-like 操作系统设计的,在 Windows 上通过 WSL 使用它。WSL 允许你在 Windows 上运行 Linux 发行版,并且可以在其中使用 readelf 工具。

打开 PowerShell 以管理员身份运行,并执行以下命令启用 WSL 并安装 Ubuntu 系统

css 复制代码
wsl --install -d Ubuntu

然后设置用户账户和密码。

通过 wsl 命令进入 Ubuntu 系统。

bash 复制代码
(base) PS E:\> wsl
wsl: Unknown key 'wsl2.memory' in /etc/wsl.conf:5
wsl: Unknown key 'wsl2.swap' in /etc/wsl.conf:6
wsl: 检测到 localhost 代理配置,但未镜像到 WSL。NAT 模式下的 WSL 不支持 localhost 代理。
wsl: Processing /etc/fstab with mount -a failed.
Welcome to Ubuntu 22.04.3 LTS (GNU/Linux 5.15.153.1-microsoft-standard-WSL2 x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage


This message is shown once a day. To disable it please create the
/home/cyrus/.hushlogin file.
cyrus@*:/mnt/e$ cd /mnt/d/Python/anti-app

在 wsl 中 /mnt/d 对应的就是 windows 下的 D 盘,其他同理。

使用 readelf 分析 so 文件

调用 readelf -a 一次性查看文件中的所有信息,包括文件头、程序头、节头、符号表、动态节等。

css 复制代码
readelf -a libGameVMP.so

输出如下:

csharp 复制代码
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00   # ELF 魔数(前 4 个字节 0x7f 'E' 'L' 'F' 表示 ELF 文件格式),后面是 class/endianness/version 等
  Class:                             ELF64                   # 文件是 64 位 ELF
  Data:                              2's complement, little endian  # 使用小端字节序
  Version:                           1 (current)              # ELF 格式版本
  OS/ABI:                            UNIX - System V          # 目标 ABI 是 System V
  ABI Version:                       0                        # ABI 版本号
  Type:                              DYN (Shared object file)  # 文件类型是 DYN(共享库 .so)
  Machine:                           AArch64                   # 目标架构是 AArch64 (ARM 64-bit)
  Version:                           0x1                       # 再次确认 ELF 版本
  Entry point address:               0x2650                    # 程序入口点虚拟地址(so 通常不是 main,而是初始化代码)
  Start of program headers:          64 (bytes into file)      # 程序头表在文件中的偏移
  Start of section headers:          130160 (bytes into file)  # 节区头表的文件偏移
  Flags:                             0x0                       # 架构相关标志
  Size of this header:               64 (bytes)                # ELF Header 本身大小
  Size of program headers:           56 (bytes)                # 每个 program header 的大小
  Number of program headers:         7                         # program header 数量
  Size of section headers:           64 (bytes)                # 每个 section header 的大小
  Number of section headers:         7                         # Section Header 条目数
  Section header string table index: 1                         # 存储 section 名称的字符串表索引

Section Headers:   # 描述 ELF 文件的各个节(section)
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000   # 节 0 永远是 NULL,表示无效
       0000000000000000  0000000000000000           0     0     0
  [ 1] .shstrtab         STRTAB           000000000001fe30  0001fe30   # 节区名称字符串表
       0000000000000035  0000000000000000           0     0     0
  [ 2] .dynsym           DYNSYM           0000000000000518  00000518   # 动态符号表
       0000000000000048  0000000000000018   A       3     1     4
  [ 3] .dynstr           STRTAB           0000000000000ec0  00000ec0   # 动态符号表对应的字符串表
       0000000000000342  0000000000000000   A       0     0     0
  [ 4] .symtab           SYMTAB           000000000001fe65  0001fe65   # 静态符号表
       0000000000000048  0000000000000018   A       5     1     4
  [ 5] .strtab           STRTAB           000000000001fead  0001fead   # 静态符号表的字符串表
       000000000000002b  0000000000000000   A       0     0     1
  [ 6] .dynamic          DYNAMIC          000000000006eae8  0001dae8   # 动态段信息(动态链接器使用)
       0000000000000240  0000000000000008  AX       3     0     4
Key to Flags:  # 节的标志解释
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  D (mbind), p (processor specific)

There are no section groups in this file.  # 文件中没有 section group

Program Headers:   # 描述运行时内存布局(segment)
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000  # 第一个 LOAD 段(代码段等只读执行数据)
                 0x000000000001cba4 0x000000000001cba4  R E    0x10000
  LOAD           0x000000000001d9e8 0x000000000006e9e8 0x000000000006e9e8  # 第二个 LOAD 段(可读可写数据段)
                 0x00000000000021de 0x0000000000002418  RW     0x1000
  DYNAMIC        0x000000000001dae8 0x000000000006eae8 0x000000000006eae8  # 动态链接信息段
                 0x0000000000000240 0x0000000000000240  RW     0x8
  NOTE           0x00000000000001c8 0x00000000000001c8 0x00000000000001c8  # NOTE 段(如 Build ID)
                 0x0000000000000024 0x0000000000000024  R      0x4
  GNU_EH_FRAME   0x0000000000015634 0x0000000000056634 0x0000000000056634  # 异常处理帧(C++ 异常/栈展开信息)
                 0x0000000000001afc 0x0000000000001afc  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000  # 栈段(权限信息)
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x000000000001d9e8 0x000000000006e9e8 0x000000000006e9e8  # 只读段(防止 GOT 改写)
                 0x0000000000000618 0x0000000000000618  R      0x1

 Section to Segment mapping:  # 每个段包含的 section
  Segment Sections...
   00     .dynsym .dynstr 
   01     .dynamic 
   02     .dynamic 
   03     
   04     
   05     
   06     .dynamic 

Dynamic section at offset 0x1dae8 contains 32 entries:  # 动态段信息表
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [liblog.so]       # 依赖库
 0x0000000000000001 (NEEDED)             Shared library: [libandroid.so]
 0x0000000000000001 (NEEDED)             Shared library: [libz.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so]
 0x0000000000000001 (NEEDED)             Shared library: [libm.so]
 0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so]
 0x0000000000000001 (NEEDED)             Shared library: [libdl.so]
 0x000000000000000e (SONAME)             Library soname: [libGameVMP.so]   # 该库自己的 SONAME
 0x000000000000000c (INIT)               0x1bd68                           # 初始化函数地址
 0x0000000000000019 (INIT_ARRAY)         0x6e9e8                           # 初始化函数数组地址
 0x000000000000001b (INIT_ARRAYSZ)       216 (bytes)                       # 初始化函数数组大小
 0x000000000000001a (FINI_ARRAY)         0x6eac0                           # 析构函数数组地址
 0x000000000000001c (FINI_ARRAYSZ)       16 (bytes)                        # 析构函数数组大小
 0x0000000000000004 (HASH)               0x1f0                             # 哈希表地址
 0x0000000000000005 (STRTAB)             0xec0                             # 字符串表地址
 0x0000000000000006 (SYMTAB)             0x518                             # 符号表地址
 0x000000000000000a (STRSZ)              834 (bytes)                       # 字符串表大小
 0x000000000000000b (SYMENT)             24 (bytes)                        # 每个符号表项大小
 0x0000000000000003 (PLTGOT)             0x6ed28                           # PLT/GOT 表地址
 0x0000000000000002 (PLTRELSZ)           1488 (bytes)                      # PLT 重定位表大小
 0x0000000000000014 (PLTREL)             RELA                              # 重定位类型
 0x0000000000000017 (JMPREL)             0x1c78                            # 延迟绑定符号表地址
 0x0000000000000007 (RELA)               0x1330                            # 重定位表地址
 0x0000000000000008 (RELASZ)             2376 (bytes)                      # 重定位表大小
 0x0000000000000009 (RELAENT)            24 (bytes)                        # 每个重定位表项大小
 0x0000000000000018 (BIND_NOW)                                              # 表示启动时立即绑定
 0x000000006ffffffb (FLAGS_1)            Flags: NOW                        # 同上,立即解析
 0x000000006ffffffe (VERNEED)            0x12d0                            # 版本依赖表地址
 0x000000006fffffff (VERNEEDNUM)         3                                 # 版本依赖数量
 0x000000006ffffff0 (VERSYM)             0x1202                            # 版本符号表地址
 0x000000006ffffff9 (RELACOUNT)          76                                # RELA 表项数量
 0x0000000000000000 (NULL)               0x0                               # 表结束

There are no static relocations in this file.  # 没有静态重定位
To see the dynamic relocations add --use-dynamic to the command line.

The decoding of unwind sections for machine type AArch64 is not currently supported.  # 不支持 ARM64 的 unwind 解码

Symbol table '.dynsym' contains 3 entries:  # 动态符号表
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000002650     0 SECTION LOCAL  DEFAULT bad section index[ 10] 
     2: 000000000006ead0     0 SECTION LOCAL  DEFAULT bad section index[ 16] 

Symbol table '.symtab' contains 3 entries:  # 静态符号表
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __stack_chk_fail  # 未定义的外部函数
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __intel_security[...]

Histogram for bucket list length (total of 97 buckets):  # 哈希表桶分布(符号查找用)
 Length  Number     % of total  Coverage
      0  38         ( 39.2%)
      1  29         ( 29.9%)     29.0%
      2  20         ( 20.6%)     69.0%
      3  9          (  9.3%)     96.0%
      4  1          (  1.0%)    100.0%

No version information found in this file.  # 没有版本信息段

Displaying notes found at file offset 0x000001c8 with length 0x00000024:
  Owner                Data size Description
  GNU                  0x00000014NT_GNU_BUILD_ID (unique build ID bitstring)  # GNU build-id
    Build ID: 7e55cc28965ecb053c55055043d9f1ed3260a498

readelf 辅助脚本

新一个 soinfo.sh,编写一个简单的 Shell 脚本,调用 readelf -a 命令并将 ELF文件信息 输出到文件同名的 .txt 文件。

bash 复制代码
#!/bin/bash

# 检查是否提供了SO文件
if [ -z "$1" ]; then
  echo "Usage: $0 <path_to_so_file>"
  exit 1
fi

# 提取输入文件的文件名
so_file="$1"

# 检查文件是否存在
if [ ! -f "$so_file" ]; then
  echo "Error: File '$so_file' not found!"
  exit 1
fi

# 获取文件名(不含扩展名)
base_name=$(basename "$so_file" .so)

# 创建输出文件名(.txt文件)
output_file="${base_name}.txt"

# 调用 readelf -a 并将结果导出到输出文件
readelf -a "$so_file" > "$output_file"

# 提示用户操作成功
echo "Information exported to $output_file"

创建 scripts 目录,并把 soinfo.sh 放到该目录下。

执行 nano ~/.bashrc 命令,把 scripts 目录添加到环境变量

ruby 复制代码
export PATH=$PATH:/mnt/e/scripts

修改保存后,执行 source ~/.bashrc 使其生效。

之后就可以通过调用 soinfo.sh <so_path> 将 so 文件信息导出到 so 同名的 .txt 文件

ruby 复制代码
cyrus@*:/mnt/d/Python/anti-app/app/duapp$ soinfo.sh libGameVMP.so
readelf: Warning: local symbol 1 found at index >= .dynsym's sh_info value of 1
readelf: Warning: local symbol 2 found at index >= .dynsym's sh_info value of 1
Information exported to libGameVMP.txt

libGameVMP.txt

相关推荐
chlk1235 小时前
Linux文件权限完全图解:读懂 ls -l 和 chmod 755 背后的秘密
linux·操作系统
阿巴斯甜5 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
舒一笑5 小时前
Ubuntu系统安装CodeX出现问题
linux·后端
Kapaseker5 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
改一下配置文件6 小时前
Ubuntu24.04安装NVIDIA驱动完整指南(含Secure Boot解决方案)
linux
xq95276 小时前
Andorid Google 登录接入文档
android
黄林晴8 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
深紫色的三北六号15 小时前
Linux 服务器磁盘扩容与目录迁移:rsync + bind mount 实现服务无感迁移(无需修改配置)
linux·扩容·服务迁移
SudosuBash19 小时前
[CS:APP 3e] 关于对 第 12 章 读/写者的一点思考和题解 (作业 12.19,12.20,12.21)
linux·并发·操作系统(os)
冬奇Lab20 小时前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读