typedef struct elf64_shdr {
  Elf64_Word sh_name;           /* Section name, index in string tbl */
  Elf64_Word sh_type;           /* Type of section */
  Elf64_Xword sh_flags;         /* Miscellaneous section attributes */
  Elf64_Addr sh_addr;           /* Section virtual addr at execution */
  Elf64_Off sh_offset;          /* Section file offset */
  Elf64_Xword sh_size;          /* Size of section in bytes */
  Elf64_Word sh_link;           /* Index of another section */
  Elf64_Word sh_info;           /* Additional section information */
  Elf64_Xword sh_addralign;     /* Section alignment */
  Elf64_Xword sh_entsize;       /* Entry size if section holds table */
} Elf64_Shdr;

(gdb) p/d (Elf64_Shdr *)0 + 1
$13 = 64

/* sh_type */
#define SHT_NULL        0
#define SHT_PROGBITS    1
#define SHT_SYMTAB      2
#define SHT_STRTAB      3
#define SHT_RELA        4
#define SHT_HASH        5
#define SHT_DYNAMIC     6
#define SHT_NOTE        7
#define SHT_NOBITS      8
#define SHT_REL         9
#define SHT_SHLIB       10
#define SHT_DYNSYM      11
#define SHT_NUM         12
#define SHT_LOPROC      0x70000000
#define SHT_HIPROC      0x7fffffff
#define SHT_LOUSER      0x80000000
#define SHT_HIUSER      0xffffffff

/* sh_flags */
#define SHF_WRITE               0x1
#define SHF_ALLOC               0x2
#define SHF_EXECINSTR           0x4
#define SHF_RELA_LIVEPATCH      0x00100000
#define SHF_RO_AFTER_INIT       0x00200000
#define SHF_MASKPROC            0xf0000000
// 根据ELF file header: 
// section headers 位于文件的 offset:37336 ~ 39256(37336+64*30 0x9958)
// 共 1920(0x780) bytes
Start of section headers:          37336 (bytes into file)
Size of section headers:           64 (bytes)
Number of section headers:         30
Section header string table index: 29

// 37336 == 0x91d8
// 37336 + 64 == 37400
$ hd /bin/echo -s 37400 -n 64
00009218  0b 00 00 00 01 00 00 00  02 00 00 00 00 00 00 00  |................|
00009228  18 03 00 00 00 00 00 00  18 03 00 00 00 00 00 00  |................|
00009238  1c 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00009248  01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00009258

Elf64_Word sh_name = 0x0000000b -> %s , 0x90b4+0xb : .interp
// $ hd /bin/echo -s $(((16#90b4)+11)) -n 8
// 000090bf  2e 69 6e 74 65 72 70 00                           |.interp.|
Elf64_Word sh_type = 0x00000001 -> SHT_PROGBITS
Elf64_Xword sh_flags = 0x0000000000000002 -> SHF_ALLOC
Elf64_Addr sh_addr = 0x0000000000000318
Elf64_Off sh_offset = 0x0000000000000318
Elf64_Xword sh_size = 0x000000000000001c
Elf64_Word sh_link = 0x00000000
Elf64_Word sh_info = 0x00000000
Elf64_Xword sh_addralign = 0x0000000000000001
Elf64_Xword sh_entsize = 0x0000000000000000

...

$ hd /bin/echo -s 39192 -n 64
00009918  01 00 00 00 03 00 00 00  00 00 00 00 00 00 00 00  |................|
00009928  00 00 00 00 00 00 00 00  b4 90 00 00 00 00 00 00  |................|
00009938  1d 01 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00009948  01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00009958

Elf64_Word sh_name = 0x00000001 -> %s , 0x90b4+1 : .shstrtab
// $ hd /bin/echo -s $(((16#90b4)+1)) -n 10
// 000090b5  2e 73 68 73 74 72 74 61  62 00                    |.shstrtab.|

Elf64_Word sh_type = 0x00000003 -> SHT_STRTAB
Elf64_Xword sh_flags = 0x0000000000000000 
Elf64_Addr sh_addr = 0x0000000000000000
Elf64_Off sh_offset = 0x00000000000090b4
Elf64_Xword sh_size = 0x000000000000011d
Elf64_Word sh_link = 0x00000000
Elf64_Word sh_info = 0x00000000
Elf64_Xword sh_addralign = 0x0000000000000001
Elf64_Xword sh_entsize = 0x0000000000000000

$ hd /bin/echo -s 0x90b4 -n 285
000090b4  00 2e 73 68 73 74 72 74  61 62 00 2e 69 6e 74 65  |..shstrtab..inte|
000090c4  72 70 00 2e 6e 6f 74 65  2e 67 6e 75 2e 70 72 6f  |rp..note.gnu.pro|
000090d4  70 65 72 74 79 00 2e 6e  6f 74 65 2e 67 6e 75 2e  |perty..note.gnu.|
000090e4  62 75 69 6c 64 2d 69 64  00 2e 6e 6f 74 65 2e 41  |build-id..note.A|
000090f4  42 49 2d 74 61 67 00 2e  67 6e 75 2e 68 61 73 68  |BI-tag..gnu.hash|
00009104  00 2e 64 79 6e 73 79 6d  00 2e 64 79 6e 73 74 72  |..dynsym..dynstr|
00009114  00 2e 67 6e 75 2e 76 65  72 73 69 6f 6e 00 2e 67  |..gnu.version..g|
00009124  6e 75 2e 76 65 72 73 69  6f 6e 5f 72 00 2e 72 65  |nu.version_r..re|
00009134  6c 61 2e 64 79 6e 00 2e  72 65 6c 61 2e 70 6c 74  |la.dyn..rela.plt|
00009144  00 2e 69 6e 69 74 00 2e  70 6c 74 2e 67 6f 74 00  |..init..plt.got.|
00009154  2e 70 6c 74 2e 73 65 63  00 2e 74 65 78 74 00 2e  |.plt.sec..text..|
00009164  66 69 6e 69 00 2e 72 6f  64 61 74 61 00 2e 65 68  |fini..rodata..eh|
00009174  5f 66 72 61 6d 65 5f 68  64 72 00 2e 65 68 5f 66  |_frame_hdr..eh_f|
00009184  72 61 6d 65 00 2e 69 6e  69 74 5f 61 72 72 61 79  |rame..init_array|
00009194  00 2e 66 69 6e 69 5f 61  72 72 61 79 00 2e 64 61  |..fini_array..da|
000091a4  74 61 2e 72 65 6c 2e 72  6f 00 2e 64 79 6e 61 6d  |ta.rel.ro..dynam|
000091b4  69 63 00 2e 64 61 74 61  00 2e 62 73 73 00 2e 67  |ic..data..bss..g|
000091c4  6e 75 5f 64 65 62 75 67  6c 69 6e 6b 00           |nu_debuglink.|

hd /bin/echo -s $((16#90b4)+1) -n 10 

$ readelf -S -W /bin/echo
There are 30 section headers, starting at offset 0x91d8:

Section Headers:
  [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        0000000000000318 000318 00001c 00   A  0   0  1
  [ 2] .note.gnu.property NOTE           0000000000000338 000338 000020 00   A  0   0  8
  [ 3] .note.gnu.build-id NOTE           0000000000000358 000358 000024 00   A  0   0  4
  [ 4] .note.ABI-tag     NOTE            000000000000037c 00037c 000020 00   A  0   0  4
  [ 5] .gnu.hash         GNU_HASH        00000000000003a0 0003a0 000068 00   A  6   0  8
  [ 6] .dynsym           DYNSYM          0000000000000408 000408 0005b8 18   A  7   1  8
  [ 7] .dynstr           STRTAB          00000000000009c0 0009c0 000301 00   A  0   0  1
  [ 8] .gnu.version      VERSYM          0000000000000cc2 000cc2 00007a 02   A  6   0  2
  [ 9] .gnu.version_r    VERNEED         0000000000000d40 000d40 000060 00   A  7   1  8
  [10] .rela.dyn         RELA            0000000000000da0 000da0 000258 18   A  6   0  8
  [11] .rela.plt         RELA            0000000000000ff8 000ff8 0003d8 18  AI  6  25  8
  [12] .init             PROGBITS        0000000000002000 002000 00001b 00  AX  0   0  4
  [13] .plt              PROGBITS        0000000000002020 002020 0002a0 10  AX  0   0 16
  [14] .plt.got          PROGBITS        00000000000022c0 0022c0 000010 10  AX  0   0 16
  [15] .plt.sec          PROGBITS        00000000000022d0 0022d0 000290 10  AX  0   0 16
  [16] .text             PROGBITS        0000000000002560 002560 003712 00  AX  0   0 16
  [17] .fini             PROGBITS        0000000000005c74 005c74 00000d 00  AX  0   0  4
  [18] .rodata           PROGBITS        0000000000006000 006000 00128c 00   A  0   0 32
  [19] .eh_frame_hdr     PROGBITS        000000000000728c 00728c 00028c 00   A  0   0  4
  [20] .eh_frame         PROGBITS        0000000000007518 007518 000be0 00   A  0   0  8
  [21] .init_array       INIT_ARRAY      0000000000009c10 008c10 000008 08  WA  0   0  8
  [22] .fini_array       FINI_ARRAY      0000000000009c18 008c18 000008 08  WA  0   0  8
  [23] .data.rel.ro      PROGBITS        0000000000009c20 008c20 000058 00  WA  0   0 32
  [24] .dynamic          DYNAMIC         0000000000009c78 008c78 0001f0 10  WA  7   0  8
  [25] .got              PROGBITS        0000000000009e68 008e68 000188 08  WA  0   0  8
  [26] .data             PROGBITS        000000000000a000 009000 000080 00  WA  0   0 32
  [27] .bss              NOBITS          000000000000a080 009080 000198 00  WA  0   0 32
  [28] .gnu_debuglink    PROGBITS        0000000000000000 009080 000034 00      0   0  4
  [29] .shstrtab         STRTAB          0000000000000000 0090b4 00011d 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

插入section

根据上面的知识,要插入一个section:

  1. Number of section headers + 1

  2. section header table 的长度 + 64

  3. shstrtab 的长度 + x

#define EI_NIDENT 16
typedef struct elf64_hdr {
  unsigned char e_ident[EI_NIDENT];     // off: 0, size: 16
  Elf64_Half e_type;  // off: 16, size: 2
  Elf64_Half e_machine; // off: 18, size: 2
  Elf64_Word e_version; // off: 20, size: 4
  Elf64_Addr e_entry;   // off: 24, size: 8  Entry point virtual address
  Elf64_Off e_phoff;    // off: 32, size: 8 Program header table file offset 
  Elf64_Off e_shoff;    // off: 40, size: 8 Section header table file offset 
  Elf64_Word e_flags;   // off: 48, size: 4
  Elf64_Half e_ehsize;   // off: 52, size: 2
  Elf64_Half e_phentsize; // off: 54, size: 2
  Elf64_Half e_phnum;      // off: 56, size: 2
  Elf64_Half e_shentsize;   // off: 58, size: 2
  Elf64_Half e_shnum;       // off: 60, size: 2
  Elf64_Half e_shstrndx;
} Elf64_Ehdr;

typedef struct elf64_shdr {
  Elf64_Word sh_name;           /* 4 Section name, index in string tbl */
  Elf64_Word sh_type;           /* 4 Type of section */
  Elf64_Xword sh_flags;         /* 8 Miscellaneous section attributes */
  Elf64_Addr sh_addr;           /* 8 Section virtual addr at execution */
  Elf64_Off sh_offset;          /* 8 Section file offset */
  Elf64_Xword sh_size;          /* 8 Size of section in bytes */
  Elf64_Word sh_link;           /* 4 Index of another section */
  Elf64_Word sh_info;           /* 4 Additional section information */
  Elf64_Xword sh_addralign;     /* 8 Section alignment */
  Elf64_Xword sh_entsize;       /* 8 Entry size if section holds table */
} Elf64_Shdr;

定位 number of section headers

$ readelf -h /bin/ls|grep "Number of section"
  Number of section headers:         30

$ hd /bin/ls -s 60 -n 2
0000003c  1e 00                                             |..|
0000003e

定位 section headers table

#!/bin/bash
# print_shdrs.sh

if [[ $1 ]]; then
        target=$1
else
        target="/bin/ls"
fi
echo "target:" $target

# 1. Number of section headers, offset: 60, size: 2
num=$[0x$(hd $target -s 60 -n 2 |awk '{print $3$2}')]
echo -n "Number of section headers:" $num
printf ' (0x%x)\\n' $num

# 2. Start of section headers, offset: 40, size: 8
off=$[0x$(hd $target -s 40 -n 8|awk '{print $9$8$7$6$5$4$3$2}')]
echo -n "Start of section headers:" $off
printf ' (0x%x)\\n\\n' $off

# 3. Section headers
echo "**********************  Section headers  *************************************"
echo -n "Section headers start:" $off
printf ' (0x%x)\\n' $off
echo -n "Section headers end:" $[$off + 64 * $num]
printf ' (0x%x)\\n' $[$off + 64 * $num]

for i in $(seq 1 $num)
do
        echo "[$[$i - 1]]"
        hd $target -s $[$off + 64*($i-1)] -n 64
        echo "******************************************************************************"
done