版权归作者所有,如有转发,请注明文章出处: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
