共计 5734 个字符,预计需要花费 15 分钟才能阅读完成。
判断文件类型
1、对于一个二进制文件:file可能会输出:
[root@qemuswtpmsm4 seabios-tpm]# file /usr/bin/file
/usr/bin/file: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=58f6c8b82ee70887d7136a7181ffc8fa18030cde, stripped
其中,/usr/bin/file
是文件名,ELF 64-bit LSB executable
表示文件类型为 ELF 64 位可执行文件。
2、对于一个文本文件它的结果为:
[root@qemuswtpmsm4 seabios-tpm]# file test.txt
test.txt: ASCII text
3、对于一个图片文件,他的结果为:
[root@qemuswtpmsm4 ~]# file f1c2c7d2f335ce7a67f315433a53e0fec9997bdd.jpg
f1c2c7d2f335ce7a67f315433a53e0fec9997bdd.jpg: JPEG image data, JFIF standard 1.01
可以看出他是jpg格式的文件。
4、于是,可以使用 file命令:查看seabios编译后产生的bios.bin文件的类型
[root@qemuswtpmsm4 seabios-tpm]# file out/bios.bin
out/bios.bin: data
根据 file
命令的输出结果 out/bios.bin: data
,可以看出 out/bios.bin
文件的类型为 data,即数据文件。这意味着 file
命令无法确定该文件的具体类型,因为该文件不属于任何已知的文件类型。
elf文件
ELF(Executable and Linkable Format)是一种可执行文件和可链接文件的文件格式标准,常用于 Unix 和 Linux 等操作系统上的可执行文件和库文件。ELF 文件具有可移植性、可扩展性和安全性等优点,已经成为主流的可执行文件和库文件格式之一。
ELF主要包括三种类型文件:
可重定位文件(relocatable)
:编译器和汇编器产生的.o文件,被Linker所处理可执行文件(executable)
:Linker对.o文件进行处理输出的文件,进程映像共享对象文件(shared object)
:动态库文件.so
#include <stdio.h>
int add(int a,int b)
{
printf("Number are added together\n");
return a + b;
}
int main()
{
int a,b;
a = 3;
b = 4;
int ret = add(a,b);
printf("Result:%u\n",ret);
exit(0);
}
gcc test.c -o test #生成可执行文件
gcc test.c -c -o test.o # -c参数表示“生成目标文件,不生成可执行文件”,此时只编译了,未链接
查看一个可重定位文件:
[root@qemuswtpmsm4 ~]# file test.o
test.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
查看可执行文件:
[root@qemuswtpmsm4 ~]# file /usr/bin/file
/usr/bin/file: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=58f6c8b82ee70887d7136a7181ffc8fa18030cde, stripped
使用file命令查看一个库文件:
[root@qemuswtpmsm4 ~]# file /usr/lib64/libgdbm.so.4.0.0
/usr/lib64/libgdbm.so.4.0.0: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=8883331bdb71df9fbd10305b90f89a8f830ea182, stripped
ELF文件结构
一个典型的ELF文件布局如下:
它包含:
- 一个ELF文件头(ELF Header):该文件头,始终从文件偏移量0处开始,其定义了文件使用32位还是64位地址,以及程序头表和节头表的偏移量等。
- 一个程序头表(Program Header Table):是一组数据结构,规定了系统运行时如何创建进程镜像,每一个数据结构描述了系统准备执行程序所需要的段(Segment)或其他信息,仅针对的是可执行文件或者可重定位目标文件。
- 一个节头表(Section Header Table):定义了文件的所有节(Section)的位置,记录了二进制文件中每个节到文件起始位置的字节偏移量。
文件格式定义的所有数据结构有可能包含显示填充,以确保对象的4字节对齐,强制要求数据结构大小为4的倍数。
查看elf文件内容
1、使用readelf工具(也可以使用objdump,这里就不说了)查看ELF文件头:
[root@qemuswtpmsm4 ~]# readelf -h test
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 #ELF 文件的魔数,用于标识该文件是否为 ELF 格式文件。
Class: ELF64
Data: 2's complement, little endian #ELF 文件的字节序,这里是小端字节序。
Version: 1 (current)
OS/ABI: UNIX - System V #ELF 文件所运行的操作系统和 ABI,这里是 UNIX - System V。
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64 #ELF 文件所运行的 CPU 架构,这里是 AMD X86-64。
Version: 0x1
Entry point address: 0x4004d0
Start of program headers: 64 (bytes into file) #程序头表的起始位置,即程序头表相对于文件开头的偏移量,这里是 64 字节。
Start of section headers: 6568 (bytes into file) #节头表的起始位置,即节头表相对于文件开头的偏移量,这里是 6568 字节。
Flags: 0x0
Size of this header: 64 (bytes) #ELF 头部的大小,这里是 64 字节。
Size of program headers: 56 (bytes) #程序头表中程序头的大小,这里是 56 字节。
Number of program headers: 9 #程序头表中程序头的数量,这里是 9。
Size of section headers: 64 (bytes) #节头表中单个节头的大小,这里是 64 字节。
Number of section headers: 30 # 节头数量
Section header string table index: 29 # 节头表中 “节区名称字符串表” 的索引。
由此可见,这个 ELF 头部信息描述了一个 64 位小端字节序的可执行文件,该文件是一个 AMD X86-64 架构的可执行文件,程序入口地址是 0x4004d0,包含了 9 个程序头部和 30 个节区头部,其中第 29 个节区是节区名称字符串表。该可执行文件的程序头部表起始位置相对于文件开头的偏移量是 64 字节,节区头部表的起始位置相对于文件开头的偏移量是 6568 字节。
使用hexdump查看test字节码,可以看到文件头部对应。
上图中,字节码0x19a8=6568,代表节头表起始位置,最后的0x001d=29,代表节头表中“节区名称字符串表” 的索引。依次对应。
2、查看程序头表:
[root@qemuswtpmsm4 ~]# readelf -l test -W
Elf file type is EXEC (Executable file)
Entry point 0x4004d0
There are 9 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000040 0x0000000000400040 0x0000000000400040 0x0001f8 0x0001f8 R E 0x8
INTERP 0x000238 0x0000000000400238 0x0000000000400238 0x00001c 0x00001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x00083c 0x00083c R E 0x200000
LOAD 0x000e10 0x0000000000600e10 0x0000000000600e10 0x000234 0x000238 RW 0x200000
DYNAMIC 0x000e28 0x0000000000600e28 0x0000000000600e28 0x0001d0 0x0001d0 RW 0x8
NOTE 0x000254 0x0000000000400254 0x0000000000400254 0x000044 0x000044 R 0x4
GNU_EH_FRAME 0x0006e8 0x00000000004006e8 0x00000000004006e8 0x00003c 0x00003c R 0x4
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
GNU_RELRO 0x000e10 0x0000000000600e10 0x0000000000600e10 0x0001f0 0x0001f0 R 0x1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
04 .dynamic
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
08 .init_array .fini_array .jcr .dynamic .got
这是一个 ELF 可执行文件或共享库的程序头部表中各个字段的含义:
Type
:程序头部类型,描述了该程序头部的用途和内容,如 ELF 文件头部、代码段、数据段等等。常见的程序头部类型包括:PHDR
(程序头部表)、LOAD
(可加载的段)、DYNAMIC
(动态链接信息段)等等。Offset
:该程序头部在 ELF 文件中的偏移量,即该程序头部相对于 ELF 文件开头的字节偏移量。VirtAddr
:该程序头部的虚拟地址,即该程序头部在被加载到内存中时在进程虚拟地址空间中的地址。PhysAddr
:该程序头部的物理地址,即该程序头部在被加载到内存中时在物理地址空间中的地址(对于可执行文件,通常与虚拟地址相同)。FileSiz
:该程序头部在 ELF 文件中的大小,即该程序头部占用的字节数。MemSiz
:该程序头部在内存中的大小,即该程序头部被加载到内存中后占用的字节数,通常大于或等于FileSiz
。Flg
:该程序头部的标志位,用于描述该程序头部的属性和特征,如可执行、可写、可读等等。Align
:该程序头部的对齐方式,即该程序头部在 ELF 文件中和内存中的起始地址要按照多少字节对齐。
以下是该 ELF 可执行文件或共享库的各个程序头部的含义:
PHDR
:程序头部表,描述 ELF 文件中所有程序头部的位置和大小信息。INTERP
:动态链接器路径,描述 ELF 文件使用的动态链接器路径。LOAD
:可加载的段,描述 ELF 文件中可加载的代码和数据段信息。该可执行文件中包含两个LOAD
类型的程序头部,分别对应代码段和数据段,FileSiz
和MemSiz
字段的值相同,代表在 ELF 文件中和内存中占用的空间大小相同。DYNAMIC
:动态链接信息段,描述 ELF 文件中动态链接器需要读取的信息,如动态链接表、符号表等信息。NOTE
:注释段,描述 ELF 文件中的注释信息。GNU_EH_FRAME
:GCC 增强型异常处理框架,描述 ELF 文件中的异常处理框架信息。GNU_STACK
:栈段,描述 ELF 文件中栈的起始地址和大小。GNU_RELRO
:重定位只读段,描述 ELF 文件中只读数据段的重定位信息。
我们分析这个ELF文件中的代码段和数据段就是LOAD
这个类型指示的,其中代码段是只读可执行的:如下图所示,
该文件代码段起始与ELF的0x000000,大小位0x00083c,标志E代表可执行。
改代码段包含以下节:
分别是:.interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame