elf文件格式解析

《深入理解计算机系统》看到第七章链接那一块用linux的可执行格式elf为例讲解,因为没有看过elf的文件格式所以一开始看的很迷茫,啥~啥~这是啥~

找了一些elf的讲解看了一下,觉得需要自己分析一个简单的elf才能加深理解。

首先ELF的整个结构如下图:

本文以一个带有static 变量的hello.c为例

 

nt main()
{
        static int a = 255;
}

 

 

用gcc  -c hello.c编译生成hello.o

 

 

查看hello.o的二进制内容(hexdump hello.o)为:

 

0000000 457f 464c 0101 0001 0000 0000 0000 0000
0000010 0001 0003 0001 0000 0000 0000 0000 0000
0000020 00f8 0000 0000 0000 0034 0000 0000 0028
0000030 000b 0008 8955 5de5 00c3 0000 00ff 0000
0000040 4700 4343 203a 5528 7562 746e 2075 2e34
0000050 2e38 2d34 7532 7562 746e 3175 317e 2e34
0000060 3430 332e 2029 2e34 2e38 0034 0014 0000
0000070 0000 0000 7a01 0052 7c01 0108 0c1b 0404
0000080 0188 0000 001c 0000 001c 0000 0000 0000
0000090 0005 0000 4100 080e 0285 0d42 4105 0cc5
00000a0 0404 0000 2e00 7973 746d 6261 2e00 7473
00000b0 7472 6261 2e00 6873 7473 7472 6261 2e00
00000c0 6574 7478 2e00 6164 6174 2e00 7362 0073
00000d0 632e 6d6f 656d 746e 2e00 6f6e 6574 472e
00000e0 554e 732d 6174 6b63 2e00 6572 2e6c 6865
00000f0 665f 6172 656d 0000 0000 0000 0000 0000
0000100 0000 0000 0000 0000 0000 0000 0000 0000
*
0000120 001b 0000 0001 0000 0006 0000 0000 0000
0000130 0034 0000 0005 0000 0000 0000 0000 0000
0000140 0001 0000 0000 0000 0021 0000 0001 0000
0000150 0003 0000 0000 0000 003c 0000 0004 0000
0000160 0000 0000 0000 0000 0004 0000 0000 0000
0000170 0027 0000 0008 0000 0003 0000 0000 0000
0000180 0040 0000 0000 0000 0000 0000 0000 0000
0000190 0001 0000 0000 0000 002c 0000 0001 0000
00001a0 0030 0000 0000 0000 0040 0000 002c 0000
00001b0 0000 0000 0000 0000 0001 0000 0001 0000
00001c0 0035 0000 0001 0000 0000 0000 0000 0000
00001d0 006c 0000 0000 0000 0000 0000 0000 0000
00001e0 0001 0000 0000 0000 0049 0000 0001 0000
00001f0 0002 0000 0000 0000 006c 0000 0038 0000
0000200 0000 0000 0000 0000 0004 0000 0000 0000
0000210 0045 0000 0009 0000 0000 0000 0000 0000
0000220 0368 0000 0008 0000 0009 0000 0006 0000
0000230 0004 0000 0008 0000 0011 0000 0003 0000
0000240 0000 0000 0000 0000 00a4 0000 0053 0000
0000250 0000 0000 0000 0000 0001 0000 0000 0000
0000260 0001 0000 0002 0000 0000 0000 0000 0000
0000270 02b0 0000 00a0 0000 000a 0000 0009 0000
0000280 0004 0000 0010 0000 0009 0000 0003 0000
0000290 0000 0000 0000 0000 0350 0000 0015 0000
00002a0 0000 0000 0000 0000 0001 0000 0000 0000
00002b0 0000 0000 0000 0000 0000 0000 0000 0000
00002c0 0001 0000 0000 0000 0000 0000 0004 fff1
00002d0 0000 0000 0000 0000 0000 0000 0003 0001
00002e0 0000 0000 0000 0000 0000 0000 0003 0002
00002f0 0000 0000 0000 0000 0000 0000 0003 0003
0000300 0009 0000 0000 0000 0004 0000 0001 0002
0000310 0000 0000 0000 0000 0000 0000 0003 0005
0000320 0000 0000 0000 0000 0000 0000 0003 0006
0000330 0000 0000 0000 0000 0000 0000 0003 0004
0000340 0010 0000 0000 0000 0005 0000 0012 0001
0000350 6800 6c65 6f6c 632e 6100 312e 3733 0030
0000360 616d 6e69 0000 0000 0020 0000 0202 0000
0000370

因为我的机器是32的ub,所以用以下格式来解析文件头

 

 #define EI_NIDENT       16

  typedef struct {
      unsigned char       e_ident[EI_NIDENT];
      Elf32_Half          e_type;
      Elf32_Half          e_machine;
      Elf32_Word          e_version;
      Elf32_Addr          e_entry;
      Elf32_Off           e_phoff;
      Elf32_Off           e_shoff;
      Elf32_Word          e_flags;
      Elf32_Half          e_ehsize;
      Elf32_Half          e_phentsize;
      Elf32_Half          e_phnum;
      Elf32_Half          e_shentsize;
      Elf32_Half          e_shnum;
      Elf32_Half          e_shstrndx;
  } Elf32_Ehdr;


关于声明类型的定义如下:

 


  

 大小对齐用途
Elf32_Addr44无符号程序地址
Elf32_Half22无符号中等大小整数
Elf32_Off44无符号文件偏移
Elf32_Sword44有符号大整数
Elf32_Word44无符号大整数
unsigned char11无符号小整数

 

 

 

解析结果如下:

magic:     7f45 4c46 0101 0100 0000 0000 0000 0000

type:         0x01

machine:    0x03

version:    0x01

entry:         0

phoff:         0

shoff:         0xf8

flags:         0

ehsize:      0x34

phentsize: 0x0

phnum:      0

shentsize:  0x28

shnum:       0x0b

shstrndx:    0x08

 

 

同elfread -h hello.o得到的结果对比

 

 

 

ELF 头:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (可重定位文件)
  Machine:                           Intel 80386
  Version:                           0x1
  入口点地址:               0x0
  程序头起点:          0 (bytes into file)
  Start of section headers:          248 (bytes into file)
  标志:             0x0
  本头的大小:       52 (字节)
  程序头大小:       0 (字节)
  Number of program headers:         0
  节头大小:         40 (字节)
  节头数量:         11
  字符串表索引节头: 8


结果是对的,只不过elfread对各个值进一步解析出来了。

 

看完的header再看后面的节头信息

直接调用elfread -S hello.o查看:

 

节头:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00000000 000034 000005 00  AX  0   0  1
  [ 2] .data             PROGBITS        00000000 00003c 000004 00  WA  0   0  4
  [ 3] .bss              NOBITS          00000000 000040 000000 00  WA  0   0  1
  [ 4] .comment          PROGBITS        00000000 000040 00002c 01  MS  0   0  1
  [ 5] .note.GNU-stack   PROGBITS        00000000 00006c 000000 00      0   0  1
  [ 6] .eh_frame         PROGBITS        00000000 00006c 000038 00   A  0   0  4
  [ 7] .rel.eh_frame     REL             00000000 000368 000008 08      9   6  4
  [ 8] .shstrtab         STRTAB          00000000 0000a4 000053 00      0   0  1
  [ 9] .symtab           SYMTAB          00000000 0002b0 0000a0 10     10   9  4
  [10] .strtab           STRTAB          00000000 000350 000015 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

在这里我们以.text和.symtab以及.strtab为例

根据节头信息,我们找到0x34处长度为5的一段指令:

 

55 89 e5 5d c3


同objdump -d hello.o得到反汇编对比:

 

 

hello.o:     文件格式 elf32-i386


Disassembly of section .text:

00000000 <main>:
   0:    55                       push   %ebp
   1:    89 e5                    mov    %esp,%ebp
   3:    5d                       pop    %ebp
   4:    c3                       ret    

完全一致。

 

再看符号表symtab,在0x2b0处,长度为0xa0

符号表的表头结构定义如下:

 

typedef struct {
Elf32_Word st_name;
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char type:4,
              binding:4;
unsigned char st_other;
Elf32_Half st_shndx;
} Elf32_Sym;


一个符号结构体占16个字节, 根据长度可知hello.o共有10个符号,用readelf -s hello.o查看符号表结果如下:

 

 

Symbol table '.symtab' contains 10 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 00000000     0 FILE    LOCAL  DEFAULT  ABS hello.c
     2: 00000000     0 SECTION LOCAL  DEFAULT    1
     3: 00000000     0 SECTION LOCAL  DEFAULT    2
     4: 00000000     0 SECTION LOCAL  DEFAULT    3
     5: 00000000     4 OBJECT  LOCAL  DEFAULT    2 a.1370
     6: 00000000     0 SECTION LOCAL  DEFAULT    5
     7: 00000000     0 SECTION LOCAL  DEFAULT    6
     8: 00000000     0 SECTION LOCAL  DEFAULT    4
     9: 00000000     5 FUNC    GLOBAL DEFAULT    1 main

完全正确。

 

挑出最后一个符号来看,原数据为0x340处,长度为16字节:

 

0010 0000 0000 0000 0005 0000 0012 0001

根据结构体结构可以知道st_name 为0x10,即在字符表strtab的0x10偏移处。

 

字符表strtab在0x350处,长度为0x15:

 

6800 6c65 6f6c 632e 6100 312e 3733 0030 616d 6e69 00

其中"00"为字符串结尾, 所以我们可以读出最后一个字符恰好偏移为0x10:

 

 

6d 61 69 6e 00

前四个字节表示的ascii为"m", "a", "i",  "n", 即符号表的最后一个符号“main”。
 

新开公众号“码家村”,欢迎关注

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值