binary hacks读数笔记(readelf基本命令)
一、首先对readelf常用的参数进行简单说明:
readelf命令是Linux下的分析ELF文件的命令,这个命令在分析ELF文件格式时非常有用,下面以ELF格式可执行文件test为例详细介绍:
1、readelf -v 显示版本
2、readelf -h 显示帮助
3、readelf -a test 显示test的全部信息
4、readelf -h test 显示test的ELF Header的文件头信息(就是ELF文件开始的前52个字节)
5、readelf -l test 显示test的Program Header Table中的每个Prgram Header Entry的信息(如果有)
6、readelf -S test 显示test的Section Header Table中的每个Section Header Entry的信息(如果有)
7、readelf -g test 显示test的Section Group的信息(如果有)
8、readelf -s test 显示test的Symbol Table中的每个Symbol Table Entry的信息(如果有)
9、readelf -e test 显示test的全部头信息(包括ELF Header,Section Header和Program Header,等同与 readelf -h -l -S test)
10、readelf -n test 显示test的note段的信息(如果有)
11、readelf -r test 显示test中的可重定位段的信息(如果有)
12、readelf -d test 显示test中的Dynamic Section的信息(如果有)
二、接着对几个常用的进行详细说明:
1. 读取ELF文件头:
$ readelf -h sign
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)(.so文件DYN (Shared object file)、.o文件REL (Relocatable file)、Core dump文件(CORE))
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x400510
Start of program headers: 64 (bytes into file)
Start of section headers: 3072 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 8
Size of section headers: 64 (bytes)
Number of section headers: 31
Section header string table index: 28
在 readelf 的输出中:
第 1 行,ELF Header: 指名 ELF 文件头开始。
第 2 行,Magic 魔数,用来指名该文件是一个 ELF 目标文件。第一个字节 7F 是个固定的数;后面的 3 个字节正是 E, L, F 三个字母的 ASCII 形式。
第 3 行,CLASS 表示文件类型,这里是 64位的 ELF 格式。
第 4 行,Data 表示文件中的数据是按照什么格式组织(大端或小端)的,不同处理器平台数据组织格式可能就不同,如x86平台为小端存储格式。
第 5 行,当前 ELF 文件头版本号,这里版本号为 1 。
第 6 行,OS/ABI ,指出操作系统类型,ABI 是 Application Binary Interface 的缩写。
第 7 行,ABI 版本号,当前为 0 。
第 8 行,Type 表示文件类型。ELF 文件有 3 种类型,一种是如上所示的 Relocatable file 可重定位目标文件,一种是可执行文件(Executable),另外一种是共享库(Shared Library) 。
第 9 行,机器平台类型。
第 10 行,当前目标文件的版本号。
第 11 行,程序的虚拟地址入口点,因为这还不是可运行的程序,故而这里为零。
第 12 行,与 11 行同理,这个目标文件没有 Program Headers。
第 13 行,sections 头开始处,这里 208 是十进制,表示从地址偏移 0xD0 处开始。
第 14 行,是一个与处理器相关联的标志,x86 平台上该处为 0 。
第 15 行,ELF 文件头的字节数。
第 16 行,因为这个不是可执行程序,故此处大小为 0。
第 17 行,同理于第 16 行。
第 18 行,sections header 的大小,这里每个 section 头大小为 40 个字节。
第 19 行,一共有多少个 section 头,这里是 8 个。
第 20 行,section 头字符串表索引号,从 Section Headers 输出部分可以看到其内容的偏移在 0xa0 处,从此处开始到0xcf 结束保存着各个 sections 的名字,如 .data,.text,.bss等。
在 Section Headers 这里,可以看到 .bss 和 .shstrtab 的偏移都为 0xa0 。这是因为,没有被初始化的全局变量,会在加载阶段被用 0 来初始化,这时候它和 .data 段一样可读可写。但在编译阶段,.data 段会被分配一部分空间已存放数据(这里从偏移 0x6c 开始),而 .bss 则没有,.bss 仅有的是 section headers 。
链接器从 .rel.text 就可以知道哪些地方需要进行重定位(relocate) 。
.symtab 是符号表。
Ndx 是符号表所在的 section 的 section header 编号。如 .data 段的 section header 编号是 3,而string1,string2,lenght 都是在 .data 段的。
2. 显示程序头表(目标文件没有该表):
$ readelf -l sign
Elf file type is EXEC (Executable file)
Entry point 0x400510
There are 8 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040 0x00000000000001c0 0x00000000000001c0 R E 8
INTERP 0x0000000000000200 0x0000000000400200 0x0000000000400200 0x000000000000001c 0x000000000000001c R 1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000 0x00000000000008a4 0x00000000000008a4 R E 200000
LOAD 0x00000000000008a8 0x00000000006008a8 0x00000000006008a8 0x0000000000000220 0x0000000000000230 RW 200000
DYNAMIC 0x00000000000008d0 0x00000000006008d0 0x00000000006008d0 0x00000000000001a0 0x00000000000001a0 RW 8
NOTE 0x000000000000021c 0x000000000040021c 0x000000000040021c 0x0000000000000044 0x0000000000000044 R 4
GNU_EH_FRAME 0x00000000000007d8 0x00000000004007d8 0x00000000004007d8 0x000000000000002c 0x000000000000002c R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 RW 8
Section to Segment mapping: (段到节的映射)
Segment Sections...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .hash .gnu.hash .dynsym .dynstr
.gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
03 .ctors .dtors .jcr .dynamic .got .got.plt .data .bss
04 .dynamic
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
上述各段组成了最终在内存中执行的程序,其还提供了各段在虚拟地址空间和物理地址空间中的大小、位置、标志、访问授权和对齐方面的信息。各段语义如下:
PHDR保存程序头表
INTERP指定程序从可行性文件映射到内存之后,必须调用的解释器,它是通过链接其他库来满足未解析的引用,用于在虚拟地址空间中插入程序运行所需的动态库。
LOAD表示一个需要从二进制文件映射到虚拟地址空间的段,其中保存了常量数据(如字符串),程序目标代码等。
DYNAMIC段保存了由动态连接器(即INTERP段中指定的解释器)使用的信息。
3. 读取节头表:
$ readelf -S sign.o
There are 13 section headers, starting at offset 0x210:(这里指定的offset是相对于二进制文件)
Section Headers:
[Nr] Name Type Address Offset Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000000000 00000040 00000000000000e5 0000000000000000 AX 0 0 4
[ 2] .rela.text RELA 0000000000000000 000006f8 00000000000000a8 0000000000000018 11 1 8
[ 3] .data PROGBITS 0000000000000000 00000128 0000000000000000 0000000000000000 WA 0 0 4
[ 4] .bss NOBITS 0000000000000000 00000128 0000000000000000 0000000000000000 WA 0 0 4
[ 5] .rodata PROGBITS 0000000000000000 00000128 000000000000000b 0000000000000000 A 0 0 1
[ 6] .comment PROGBITS 0000000000000000 00000133 000000000000001d 0000000000000001 MS 0 0 1
[ 7] .note.GNU-stack PROGBITS 0000000000000000 00000150 0000000000000000 0000000000000000 0 0 1
[ 8] .eh_frame PROGBITS 0000000000000000 00000150 0000000000000058 0000000000000000 A 0 0 8
[ 9] .rela.eh_frame RELA 0000000000000000 000007a0 0000000000000030 0000000000000018 11 8 8
[10] .shstrtab STRTAB 0000000000000000 000001a8 0000000000000061 0000000000000000 0 0 1
[11] .symtab SYMTAB 0000000000000000 00000550 0000000000000168 0000000000000018 12 9 8
[12] .strtab STRTAB 0000000000000000 000006b8 0000000000000039 0000000000000000 0 0 1
- .text:已编译程序的机器代码。
- .rodata:只读数据,比如printf语句中的格式串和开关(switch)语句的跳转表。
- .data:已初始化的全局C变量。局部C变量在运行时被保存在栈中,既不出现在.data中,也不出现在.bss节中。
- .bss:未初始化的全局C变量。在目标文件中这个节不占据实际的空间,它仅仅是一个占位符。目标文件格式区分初始化和未初始化变量是为了空间效率在:在目标文件中,未初始化变量不需要占据任何实际的磁盘空间。
- .symtab:一个符号表(symbol table),它存放在程序中被定义和引用的函数和全局变量的信息。一些程序员错误地认为必须通过-g选项来编译一个程序,得到符号表信息。实际上,每个可重定位目标文件在.symtab中都有一张符号表。然而,和编译器中的符号表不同,.symtab符号表不包含局部变量的表目。
- .rel.text:当链接噐把这个目标文件和其他文件结合时,.text节中的许多位置都需要修改。一般而言,任何调用外部函数或者引用全局变量的指令都需要修改。另一方面调用本地函数的指令则不需要修改。注意,可执行目标文件中并不需要重定位信息,因此通常省略,除非使用者显式地指示链接器包含这些信息。
- .rel.data:被模块定义或引用的任何全局变量的信息。一般而言,任何已初始化全局变量的初始值是全局变量或者外部定义函数的地址都需要被修改。
- .debug:一个调试符号表,其有些表目是程序中定义的局部变量和类型定义,有些表目是程序中定义和引用的全局变量,有些是原始的C源文件。只有以-g选项调用编译驱动程序时,才会得到这张表。
- .line:原始C源程序中的行号和.text节中机器指令之间的映射。只有以-g选项调用编译驱动程序时,才会得到这张表。
- .strtab:一个字符串表,其内容包括.symtab和.debug节中的符号表,以及节头部中的节名字。字符串表就是以null结尾的字符串序列。
$ readelf -S sign
There are 31 section headers, starting at offset 0xc00
Section Headers:
[Nr] Name Type Address Offset Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000400200 00000200 000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.ABI-tag NOTE 000000000040021c 0000021c 0000000000000020 0000000000000000 A 0 0 4
[ 3] .note.gnu.build-id NOTE 000000000040023c 0000023c 0000000000000024 0000000000000000 A 0 0 4
[ 4] .hash HASH 0000000000400260 00000260 0000000000000030 0000000000000004 A 6 0 8
[ 5] .gnu.hash GNU_HASH 0000000000400290 00000290 000000000000001c 0000000000000000 A 6 0 8
[ 6] .dynsym DYNSYM 00000000004002b0 000002b0 00000000000000a8 0000000000000018 A 7 1 8
[ 7] .dynstr STRTAB 0000000000400358 00000358 0000000000000065 0000000000000000 A 0 0 1
[ 8] .gnu.version VERSYM 00000000004003be 000003be 000000000000000e 0000000000000002 A 6 0 2
[ 9] .gnu.version_r VERNEED 00000000004003d0 000003d0 0000000000000030 0000000000000000 A 7 1 8
[10] .rela.dyn RELA 0000000000400400 00000400 0000000000000018 0000000000000018 A 6 0 8
[11] .rela.plt RELA 0000000000400418 00000418 0000000000000078 0000000000000018 A 6 13 8
[12] .init PROGBITS 0000000000400490 00000490 0000000000000018 0000000000000000 AX 0 0 4
[13] .plt PROGBITS 00000000004004a8 000004a8 0000000000000060 0000000000000010 AX 0 0 4
[14] .text PROGBITS 0000000000400510 00000510 00000000000002a8 0000000000000000 AX 0 0 16
[15] .fini PROGBITS 00000000004007b8 000007b8 000000000000000e 0000000000000000 AX 0 0 4
[16] .rodata PROGBITS 00000000004007c8 000007c8 000000000000000f 0000000000000000 A 0 0 4
[17] .eh_frame_hdr PROGBITS 00000000004007d8 000007d8 000000000000002c 0000000000000000 A 0 0 4
[18] .eh_frame PROGBITS 0000000000400808 00000808 000000000000009c 0000000000000000 A 0 0 8
[19] .ctors PROGBITS 00000000006008a8 000008a8 0000000000000010 0000000000000000 WA 0 0 8
[20] .dtors PROGBITS 00000000006008b8 000008b8 0000000000000010 0000000000000000 WA 0 0 8
[21] .jcr PROGBITS 00000000006008c8 000008c8 0000000000000008 0000000000000000 WA 0 0 8
[22] .dynamic DYNAMIC 00000000006008d0 000008d0 00000000000001a0 0000000000000010 WA 7 0 8
[23] .got PROGBITS 0000000000600a70 00000a70 0000000000000008 0000000000000008 WA 0 0 8
[24] .got.plt PROGBITS 0000000000600a78 00000a78 0000000000000040 0000000000000008 WA 0 0 8
[25] .data PROGBITS 0000000000600ab8 00000ab8 0000000000000010 0000000000000000 WA 0 0 8
[26] .bss NOBITS 0000000000600ac8 00000ac8 0000000000000010 0000000000000000 WA 0 0 8
[27] .comment PROGBITS 0000000000000000 00000ac8 0000000000000038 0000000000000001 MS 0 0 1
[28] .shstrtab STRTAB 0000000000000000 00000b00 00000000000000fe 0000000000000000 0 0 1
[29] .symtab SYMTAB 0000000000000000 000013c0 0000000000000678 0000000000000018 30 47 8
[30] .strtab STRTAB 0000000000000000 00001a38 000000000000023c 0000000000000000 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)
PROGBITS(程序必须解释的信息,如二进制代码),STRTAB用于存储与ELF格式有关的字符串,但与程序没有直接关联,如各个节的名称(.text, .comment)
.data保存初始化过的数据,这是普通程序数据的一部分,可以在程序运行期间修改。
.rodata保存了只读数据,可以读取但不能修改,例如printf语句中的所有静态字符串封装到该节。
.init和.fini保存了进程初始化和结束所用的代码,这通常是由编译器自动添加的。
.hash是一个散列表,允许在不对全表元素进行线性搜索的情况下,快速访问所有符号表项。
4. 符号表机制(readelf -s)
符号表保存了程序实现或使用的所有全局变量和函数,如果程序引用一个自身代码未定义的符号,则称之为未定义符号,这类引用必须在静态链接期间用其他目标模块或库解决,或在加载时通过动态链接解决。
实现:
.symtab确定符号的名称与其值之间的关联,其中名称不是直接以字符串形式出现的,而是表示为某一字符串数组(.strtab)的索引。
.strtab保存了字符串数组(.shstrtab包含了节名称字符串表)。
.hash保存了一个散列表,以帮助快速查找符号。
typedef struct elf64_sym {
Elf64_Word st_name; // 符号名称,字符串表中的索引
// STT_OBJECT表示符号关联到一个数据对象,如变量、数组或指针;
// STT_FUNC表示符号关联到一个函数;
// STT_NOTYPE表示符号类型未指定,用于未定义引用
unsigned char st_info; // 类型和绑定属性:STB_LOCAL/STB_GLOBAL/STB_WEAK;
unsigned char st_other; // 语义未定义,0
Elf64_Half st_shndx; // 相关节的索引,符号将绑定到该节,此外SHN_ABS指定符号是绝对值,不因重定位而改变,SHN_UNDEF标识未定义符号。
Elf64_Addr st_value; // 符号的值
Elf64_Xword st_size; // 符号的长度,如一个指针的长度或struct对象中包含的字节数。
}Elf64_Sym;
实例:
readelf 用来显示 ELF 格式文件信息,该命令选项很多,其中 -a 选项可以用来显示 ELF 文件的所有信息。
下面对 -a 选项的输出内容进行分析。
源码如下:
进行gcc编译,等操作:
a.info即为a.out 的ELF文件。
打开可见上面介绍的各个部分的内容。
以上就是-a选项所有符号表的内容。。。
下面是一个应用,例如我想解析出我在a.c文件中写的全局变量的内容。
我们可以看162-233行的内容,其中字段中有OBJECT 和 GLOBAL 的即为全局变量,我们在a.c中定义的全局变量会出现在里面,221和225行。
以及一些函数等内容都可以在这段区域内找到相应的地址和大小信息等。。。
具体的参数及用法如下图:
binary hacks读数笔记(readelf基本命令)的更多相关文章
- binary hacks读数笔记(readelf命令)
可以用readelf命令来查看elf文件内容,跟objdump相比,这个命令更详细. 1. readelf -h SimpleSection.o ELF Header: Magic: 7f 45 4c ...
- binary hacks读数笔记(堆、栈 VMA的分布)
一.首先看一个简单的程序: #include<stdlib.h> int main() { while(1) { sleep(1000); } return 0; } gcc -stati ...
- binary hacks读数笔记(装载)
1.地址空间 在linux系统中,每个进程拥有自己独立的虚拟地址空间,这个虚拟地址空间的大小是由计算机硬件决定的,具体地说,是由CPU的位数决定的.比如,32位硬件平台决定的虚拟地址空间大小:0--2 ...
- binary hacks读数笔记(ld 链接讲解 二)
这块将介绍一下ld链接命令的具体使用.ld的作用:ld是GNU binutils工具集中的一个,是众多Linkers(链接器)的一种.完成的功能自然也就是链接器的基本功能:把各种目标文件和库文件链接起 ...
- binary hacks读数笔记(ld 链接讲解 一)
首先我们先看两段代码: a.c extern int shared; int main(){ int a=100; swap(&a,&shared); } b.c int shared ...
- binary hacks读数笔记(objdump命令)
一.首先看一下几个常用参数的基本含义: objdump命令是Linux下的反汇编目标文件或者可执行文件的命令,它还有其他作用,下面以ELF格式可执行文件test为例详细介绍: 1.objdump -f ...
- binary hacks读数笔记(共享库)
共享库从文件结构上来讲,与共享对象没什么区别.Linux下,共享库就是普通的ELF共享对象. 1.共享库命名: libname.so.x.y.z :其中最前面使用前缀lib,中间是库的名字和后缀&qu ...
- binary hacks读数笔记(dlopen、dlsym、dlerror、dlclose)
1.dlopen是一个强大的库函数.该函数将打开一个动态库,并把它装入内存.该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的.比如 Apache Web 服务器利用这个函数在运行过程中加载 ...
- binary hacks读数笔记(nm命令)
nm命令(names):输出包含三个部分:1 符号值.默认显示十六进制,也可以指定: 2 符号类型.小写表示是本地符号,大写表示全局符号(external); 3 符号名称. 例如:nm Simple ...
随机推荐
- 程序3-6 WERTYU
把手放在键盘上时,稍不注意就会往右错一 位.这样,输入Q会变成输入W,输入J会变成输 入K等.键盘如图3-2所示. 输入一个错位后敲出的字符串(所有字母均 大写),输出打字员本来想打出的句子.输入保 ...
- 帮你解读什么是Redis缓存穿透和缓存雪崩(包含解决方案)
一.缓存处理流程 前台请求,后台先从缓存中取数据,取到直接返回结果,取不到时从数据库中取,数据库取到更新缓存,并返回结果,数据库也没取到,那直接返回空结果. 二.缓存穿透 描述: 缓存穿透是指缓存和数 ...
- tamcat7.0(安装文件下载)
安装包:http://files.cnblogs.com/files/chenghu/apache-tomcat-7.0.27.rar http://files.cnblogs.com/files/c ...
- Linux用户和组管理命令-用户属性修改usermod
用户属性修改 usermod 命令可以修改用户属性 格式: usermod [OPTION] login 常见选项: -u UID: 新UID -g GID: 新主组 -G GROUP1[,GROUP ...
- 通俗的讲解Python中的__new__()方法
2020-3-17更新本文,对本文中存争议的例子进行了更新! 曾经我幼稚的以为认识了python的__init__()方法就相当于认识了类构造器,结果,__new__()方法突然出现在我眼前,让我突然 ...
- 国产化即时通信系统开发 -- 实现GGTalk的登录界面(Linux、Ubuntu、UOS、中标麒麟)
距离2013年开源GGTalk以来,7年已经过去了,GGTalk现在有了完整的PC版.安卓版.iOS版(即将发布),以及Xamarin版本. 然而,时代一直在变化,在今天,有个趋势越来越明显,那就是政 ...
- UI设计学习总结
UI设计学习总结 平面设计基础 平面构成 三大构成:点线面 重复构成 相同,有规律的重复 近似构成 形状,大小,色彩,肌理相似 渐变构成 色彩逐渐变化 发射构成 通过一点向四周扩散犹如绽放的花朵 密集 ...
- if else 太多?看我用 Java 8 轻松干掉!
之前我用 Java 8 写了一段逻辑,就是类似下面这样的例子: /* * 来源公众号:Java技术栈 */ if(xxxOrder != null){ if(xxxOrder.getXxxShippi ...
- drf ( 学习第四部 )
目录 DRF框架中常用的组件 分页Pagination 异常处理Exceptions 自动生成接口文档 安装依赖 设置接口文档访问路径 访问接口文档网页 Admin 列表页配置 详情页配置 Xadmi ...
- Vulkan Driver for VC4(Raspberry Pi 3b) base on mesa
这是一篇关于在raspberry Pi 3b上移植实现vulkan 驱动的文章. 经过一段时间的代码搬运,终于实现了零的突破,可以在树莓派3B上运行Vulkan triangle/texture.当然 ...