Linux判断文件类型以及查看ELF文件结构

81次阅读
没有评论

共计 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文件布局如下:

Linux判断文件类型以及查看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字节码,可以看到文件头部对应。

Linux判断文件类型以及查看ELF文件结构

上图中,字节码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 类型的程序头部,分别对应代码段和数据段,FileSizMemSiz 字段的值相同,代表在 ELF 文件中和内存中占用的空间大小相同。
  • DYNAMIC:动态链接信息段,描述 ELF 文件中动态链接器需要读取的信息,如动态链接表、符号表等信息。
  • NOTE:注释段,描述 ELF 文件中的注释信息。
  • GNU_EH_FRAME:GCC 增强型异常处理框架,描述 ELF 文件中的异常处理框架信息。
  • GNU_STACK:栈段,描述 ELF 文件中栈的起始地址和大小。
  • GNU_RELRO:重定位只读段,描述 ELF 文件中只读数据段的重定位信息。

我们分析这个ELF文件中的代码段和数据段就是LOAD这个类型指示的,其中代码段是只读可执行的:如下图所示,

Linux判断文件类型以及查看ELF文件结构

该文件代码段起始与ELF的0x000000,大小位0x00083c,标志E代表可执行。

改代码段包含以下节:

Linux判断文件类型以及查看ELF文件结构

分别是:.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

正文完
 
landery
版权声明:本站原创文章,由 landery 2023-05-27发表,共计5734字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)