ELF 节标头中标志的含义?

逆向工程 小精灵
2021-06-22 08:28:33

鉴于这个经典的helloworld.c例子,

#include <stdio.h>

int main() {
    printf("Hello world!\n");
}

另外,在以下sections的输出,为的值Flags呈现几个不同的值,例如AAIAXWA,等。

man elf,我理解A大概对应于SHF_ALLOCWfor SHF_WRITEXfor SHF_EXECINSTR但是AI呢?

是否记录了这些长格式和短格式之间的对应关系?

$ readelf -S helloworld

There are 34 section headers, starting at offset 0x2188:

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         0000000000000238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000000254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000000274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000000298  00000298
       000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000000002b8  000002b8
       00000000000000a8  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           0000000000000360  00000360
       0000000000000082  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           00000000000003e2  000003e2
       000000000000000e  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          00000000000003f0  000003f0
       0000000000000020  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             0000000000000410  00000410
       00000000000000c0  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             00000000000004d0  000004d0
       0000000000000018  0000000000000018  AI       5    22     8
  [11] .init             PROGBITS         00000000000004e8  000004e8
       0000000000000017  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         0000000000000500  00000500
       0000000000000020  0000000000000010  AX       0     0     16
  [13] .plt.got          PROGBITS         0000000000000520  00000520
       0000000000000008  0000000000000008  AX       0     0     8
  [14] .text             PROGBITS         0000000000000530  00000530
       00000000000001a2  0000000000000000  AX       0     0     16
  [15] .fini             PROGBITS         00000000000006d4  000006d4
       0000000000000009  0000000000000000  AX       0     0     4
  [16] .rodata           PROGBITS         00000000000006e0  000006e0
       0000000000000011  0000000000000000   A       0     0     4
  [17] .eh_frame_hdr     PROGBITS         00000000000006f4  000006f4
       000000000000003c  0000000000000000   A       0     0     4
  [18] .eh_frame         PROGBITS         0000000000000730  00000730
       0000000000000108  0000000000000000   A       0     0     8
  [19] .init_array       INIT_ARRAY       0000000000200db8  00000db8
       0000000000000008  0000000000000008  WA       0     0     8
  [20] .fini_array       FINI_ARRAY       0000000000200dc0  00000dc0
       0000000000000008  0000000000000008  WA       0     0     8
  [21] .dynamic          DYNAMIC          0000000000200dc8  00000dc8
       00000000000001f0  0000000000000010  WA       6     0     8
  [22] .got              PROGBITS         0000000000200fb8  00000fb8
       0000000000000048  0000000000000008  WA       0     0     8
  [23] .data             PROGBITS         0000000000201000  00001000
       0000000000000010  0000000000000000  WA       0     0     8
  [24] .bss              NOBITS           0000000000201010  00001010
       0000000000000008  0000000000000000  WA       0     0     1

More following...

让我们以二进制形式转储节头表,以查看实际存储的内容Flags

hexdump -v -s 0x2188 -n 768 -e '4/1 "%02x" " " 4/1 "%02x" " " 8/1 "%02x" " " 48/1 "%02x" "\n"' helloworld

根据Elf64_Shdr结构,以下输出中的第 3 列是 flags( sh_flags)的二进制形式

00000000 00000000 0000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
1b000000 01000000 0200000000000000 380200000000000038020000000000001c00000000000000000000000000000001000000000000000000000000000000
23000000 07000000 0200000000000000 540200000000000054020000000000002000000000000000000000000000000004000000000000000000000000000000
31000000 07000000 0200000000000000 740200000000000074020000000000002400000000000000000000000000000004000000000000000000000000000000
44000000 f6ffff6f 0200000000000000 980200000000000098020000000000001c00000000000000050000000000000008000000000000000000000000000000
4e000000 0b000000 0200000000000000 b802000000000000b802000000000000a800000000000000060000000100000008000000000000001800000000000000
56000000 03000000 0200000000000000 600300000000000060030000000000008200000000000000000000000000000001000000000000000000000000000000
5e000000 ffffff6f 0200000000000000 e203000000000000e2030000000000000e00000000000000050000000000000002000000000000000200000000000000
6b000000 feffff6f 0200000000000000 f003000000000000f0030000000000002000000000000000060000000100000008000000000000000000000000000000
7a000000 04000000 0200000000000000 10040000000000001004000000000000c000000000000000050000000000000008000000000000001800000000000000
84000000 04000000 4200000000000000 d004000000000000d0040000000000001800000000000000050000001600000008000000000000001800000000000000
8e000000 01000000 0600000000000000 e804000000000000e8040000000000001700000000000000000000000000000004000000000000000000000000000000

作为以上两个输出的组合,观察到标志的二进制形式和文字形式之间的以下对应关系。

02 = 0000 0010 = A
42 = 0100 0010 = AI
06 = 0000 0110 = AX

这些通信是否记录在案?

2个回答

字母“I”的标志缩写代表SHF_INFO_LINK

由于readelf是开源的,您可以访问其代码来验证这一点。这还将向您显示标记可能性的完整列表:

...

switch (flag)
        {
        case SHF_WRITE:     *p = 'W'; break;
        case SHF_ALLOC:     *p = 'A'; break;
        case SHF_EXECINSTR:     *p = 'X'; break;
        case SHF_MERGE:     *p = 'M'; break;
        case SHF_STRINGS:       *p = 'S'; break;
        case SHF_INFO_LINK:     *p = 'I'; break;
        case SHF_LINK_ORDER:    *p = 'L'; break;
        case SHF_OS_NONCONFORMING:  *p = 'O'; break;
        case SHF_GROUP:     *p = 'G'; break;
        case SHF_TLS:       *p = 'T'; break;
        case SHF_EXCLUDE:       *p = 'E'; break;
        case SHF_COMPRESSED:    *p = 'C'; break;
        case SHF_GNU_MBIND:     *p = 'D'; break;

...

Github 上的相关行可以在以下链接中找到

从部分标题的 readelf 输出:

标志的关键:

W(写入)、A(分配)、X(执行)、M(合并)、S(字符串)I(信息)、L(链接顺序)、G(组)、T(TLS)、E(排除)、 x(未知) O(需要额外的操作系统处理)o(特定于操作系统),p(特定于处理器)