Exercise 1
Analyze the execution header.
xxd compilation_example | head -n 15
Check the first 15 lines of binaries from the file using xxd.
00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000 .ELF............
00000010: 0200 3e00 0100 0000 3004 4000 0000 0000 ..>.....0.@.....
00000020: 4000 0000 0000 0000 e819 0000 0000 0000 @...............
00000030: 0000 0000 4000 3800 0900 4000 1f00 1c00 ....@.8...@.....
00000040: 0600 0000 0500 0000 4000 0000 0000 0000 ........@.......
00000050: 4000 4000 0000 0000 4000 4000 0000 0000 @.@.....@.@.....
00000060: f801 0000 0000 0000 f801 0000 0000 0000 ................
00000070: 0800 0000 0000 0000 0300 0000 0400 0000 ................
00000080: 3802 0000 0000 0000 3802 4000 0000 0000 8.......8.@.....
00000090: 3802 4000 0000 0000 1c00 0000 0000 0000 8.@.............
000000a0: 1c00 0000 0000 0000 0100 0000 0000 0000 ................
000000b0: 0100 0000 0500 0000 0000 0000 0000 0000 ................
000000c0: 0000 4000 0000 0000 0000 4000 0000 0000 ..@.......@.....
000000d0: 0c07 0000 0000 0000 0c07 0000 0000 0000 ................
000000e0: 0000 2000 0000 0000 0100 0000 0600 0000 .. .............
In the first line, it can be seen that the magic byte of the ELF header starts with 0x7f.
To understand what the following bytes mean, I used readelf command.
readelf -h compliation_example
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x400430
Start of program headers: 64 (bytes into file)
Start of section headers: 6632 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 31
Section header string table index: 28
The descriptions of each byte in the magic byte continue until the ABI version.
Exercise 2
Analyze the sections and the segments of the file.
We can see information on sections from the section header and segments from the program header.
Firstly, let’s check the sections.
readelf --sections --wide compilation_example
There are 31 section headers, starting at offset 0x19e8:
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 0000000000400238 000238 00001c 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 0000000000400254 000254 000020 00 A 0 0 4
[ 3] .note.gnu.build-id NOTE 0000000000400274 000274 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000400298 000298 00001c 00 A 5 0 8
[ 5] .dynsym DYNSYM 00000000004002b8 0002b8 000060 18 A 6 1 8
[ 6] .dynstr STRTAB 0000000000400318 000318 00003d 00 A 0 0 1
[ 7] .gnu.version VERSYM 0000000000400356 000356 000008 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 0000000000400360 000360 000020 00 A 6 1 8
[ 9] .rela.dyn RELA 0000000000400380 000380 000018 18 A 5 0 8
[10] .rela.plt RELA 0000000000400398 000398 000030 18 AI 5 24 8
[11] .init PROGBITS 00000000004003c8 0003c8 00001a 00 AX 0 0 4
[12] .plt PROGBITS 00000000004003f0 0003f0 000030 10 AX 0 0 16
[13] .plt.got PROGBITS 0000000000400420 000420 000008 00 AX 0 0 8
[14] .text PROGBITS 0000000000400430 000430 000192 00 AX 0 0 16
[15] .fini PROGBITS 00000000004005c4 0005c4 000009 00 AX 0 0 4
[16] .rodata PROGBITS 00000000004005d0 0005d0 000012 00 A 0 0 4
[17] .eh_frame_hdr PROGBITS 00000000004005e4 0005e4 000034 00 A 0 0 4
[18] .eh_frame PROGBITS 0000000000400618 000618 0000f4 00 A 0 0 8
[19] .init_array INIT_ARRAY 0000000000600e10 000e10 000008 00 WA 0 0 8
[20] .fini_array FINI_ARRAY 0000000000600e18 000e18 000008 00 WA 0 0 8
[21] .jcr PROGBITS 0000000000600e20 000e20 000008 00 WA 0 0 8
[22] .dynamic DYNAMIC 0000000000600e28 000e28 0001d0 10 WA 6 0 8
[23] .got PROGBITS 0000000000600ff8 000ff8 000008 08 WA 0 0 8
[24] .got.plt PROGBITS 0000000000601000 001000 000028 08 WA 0 0 8
[25] .data PROGBITS 0000000000601028 001028 000010 00 WA 0 0 8
[26] .bss NOBITS 0000000000601038 001038 000008 00 WA 0 0 1
[27] .comment PROGBITS 0000000000000000 001038 000034 01 MS 0 0 1
[28] .shstrtab STRTAB 0000000000000000 0018da 00010c 00 0 0 1
[29] .symtab SYMTAB 0000000000000000 001070 000648 18 30 47 8
[30] .strtab STRTAB 0000000000000000 0016b8 000222 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
It says there are 31 sections.
Secondly, the segment information.
readelf --wide --segments compilation_example
Elf file type is EXEC (Executable file)
Entry point 0x400430
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 0x00070c 0x00070c R E 0x200000
LOAD 0x000e10 0x0000000000600e10 0x0000000000600e10 0x000228 0x000230 RW 0x200000
DYNAMIC 0x000e28 0x0000000000600e28 0x0000000000600e28 0x0001d0 0x0001d0 RW 0x8
NOTE 0x000254 0x0000000000400254 0x0000000000400254 0x000044 0x000044 R 0x4
GNU_EH_FRAME 0x0005e4 0x00000000004005e4 0x00000000004005e4 0x000034 0x000034 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 .plt.got .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
There are some interesting findings here.
Below, there’s information about section-to-segment mapping.
According to the description, it can be known that a segment is a group of sections.
Also, it can be seen that the virtual address follows the offset. The same information can be found in the section information in the section header.
Exercise 3
Skipped
Exercise 4
About lazy binding.
The .plt section and .got.plt sections play a key role in lazy binding.
So, let’s analyze the plt section for investigation.
objdump -d -M intel --section .plt compilation_example
compilation_example: file format elf64-x86-64
Disassembly of section .plt:
00000000004003f0 <puts@plt-0x10>:
4003f0: ff 35 12 0c 20 00 push QWORD PTR [rip+0x200c12] # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>
4003f6: ff 25 14 0c 20 00 jmp QWORD PTR [rip+0x200c14] # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>
4003fc: 0f 1f 40 00 nop DWORD PTR [rax+0x0]
0000000000400400 <puts@plt>:
400400: ff 25 12 0c 20 00 jmp QWORD PTR [rip+0x200c12] # 601018 <_GLOBAL_OFFSET_TABLE_+0x18>
400406: 68 00 00 00 00 push 0x0
40040b: e9 e0 ff ff ff jmp 4003f0 <_init+0x28>
0000000000400410 <__libc_start_main@plt>:
400410: ff 25 0a 0c 20 00 jmp QWORD PTR [rip+0x200c0a] # 601020 <_GLOBAL_OFFSET_TABLE_+0x20>
400416: 68 01 00 00 00 push 0x1
40041b: e9 d0 ff ff ff jmp 4003f0 <_init+0x28>
In this example, puts function is called using the library.
Then the order of it being called is like below.
.plt -> .got.plt -> (dynamic linker) -> puts
There is also .plt.got section as well. This is used for instant binding, not lazy binding.
And, it can be checked from the disassembly result above. In plt sections, there’s the jump instruction to the global offset table.