summaryrefslogtreecommitdiff
path: root/md/writeup/create_elf_file_from_scratch.md
blob: 2bed7bc480b6e3bebcfa121ffae411439dfb8877 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
title:Create ELF file from scratch
keywords:elf,linux

# Create ELF file from scratch
## Creating smallest possible elf file.

### Structure of ELF file:
Elf header  
Program header  
Code Part  
Data Part  

C structure of ELF header /usr/include/elf.h:

```c
typedef struct
{
  unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
  Elf64_Half    e_type;             /* Object file type */
  Elf64_Half    e_machine;          /* Architecture */
  Elf64_Word    e_version;          /* Object file version */
  Elf64_Addr    e_entry;            /* Entry point virtual address */
  Elf64_Off     e_phoff;            /* Program header table file offset */
  Elf64_Off     e_shoff;            /* Section header table file offset */
  Elf64_Word    e_flags;            /* Processor-specific flags */
  Elf64_Half    e_ehsize;           /* ELF header size in bytes */
  Elf64_Half    e_phentsize;        /* Program header table entry size */
  Elf64_Half    e_phnum;            /* Program header table entry count */
  Elf64_Half    e_shentsize;        /* Section header table entry size */
  Elf64_Half    e_shnum;            /* Section header table entry count */
  Elf64_Half    e_shstrndx;         /* Section header string table index */
} Elf64_Ehdr;
```

Structure of Program header file /usr/include/elf.h:

```c
typedef struct
{
  Elf64_Word    p_type;         /* Segment type */
  Elf64_Word    p_flags;        /* Segment flags */
  Elf64_Off     p_offset;       /* Segment file offset */
  Elf64_Addr    p_vaddr;        /* Segment virtual address */
  Elf64_Addr    p_paddr;        /* Segment physical address */
  Elf64_Xword   p_filesz;       /* Segment size in file */
  Elf64_Xword   p_memsz;        /* Segment size in memory */
  Elf64_Xword   p_align;        /* Segment alignment */
} Elf64_Phdr;
```

This structures is all what we need to make our ELF file.
Now we will look inside kernel source and see that
we need only one program header for our program. All big programs
using usually two program headers one for code and one for data.

/linux-3.3.1/fs/binfmt_elf.c:605

```c
if (loc->elf_ex.e_phnum < 1 ||
    loc->elf_ex.e_phnum > 65536U / sizeof(struct elf_phdr))
    goto out;
```

Step by step there should be filled all
fields of the ELF header structure.

```c
typedef struct
{
  unsigned char e_ident[EI_NIDENT]; /* default values of ELFMAG,ELFCLASS64,ELFDATA2LSB */
  Elf64_Half    e_type;             /* we making executable then it would be ET_EXEC  */
  Elf64_Half    e_machine;          /* Architecture is 0x3e(EM_X86_64) 
                                     (not from elf header 
                                     from /binutils/include/elf/common.h) */
  Elf64_Word    e_version;          /* Object file version EV_CURRENT */
  Elf64_Addr    e_entry;            /* Entry point virtual address points to
                                     main function it is with label entrypoint */
  Elf64_Off     e_phoff;            /* Program header table file offset */
                                      offset of program header sizeof(Elf64_Ehdr)
  Elf64_Off     e_shoff;            /* Section header table file offset 
                                        there is no section header */
  Elf64_Word    e_flags;            /* No processor-specific flags 
                                        */
  Elf64_Half    e_ehsize;           /* ELF header size in bytes 
                                        0x40 sizeof(Elf64_Ehdr)
  Elf64_Half    e_phentsize;        /* Program header table entry size 
                                        0x38 sizeof(Elf64_Phdr) */
  Elf64_Half    e_phnum;            /* Program header table entry count 
                                        0x01 */
  Elf64_Half    e_shentsize;        /* Section header table entry size 
                                        I put 0x40 */
  Elf64_Half    e_shnum;            /* Section header table entry count 
                                        0x00 */
  Elf64_Half    e_shstrndx;         /* There is no section header and 
                                     string table index is 0x0 then */
} Elf64_Ehdr;
```

With program header we will tell kernel how to load our file in memory
and with part of file will be mmaped to needed address. As our data
and code is placed in one address space and kernel ELF source says
that there is enough with 1 program header then we will use only 1.

```c
typedef struct
{
  Elf64_Word    p_type;         /* Segment type PT_LOAD */
  Elf64_Word    p_flags;        /* Segment flags PF_X,PF_R,PF_W
                                as our memory should be readable, writable and
                                executable as it contains code and data */
  Elf64_Off     p_offset;       /* Segment file offset 
                                    point to offset of entry point label offset
                                    in file */
  Elf64_Addr    p_vaddr;        /* Segment virtual address 
                                    64bits programs is usually at 0x400000+code_file_offset*/
  Elf64_Addr    p_paddr;        /* Segment physical address 
                                    same as above*/
  Elf64_Xword   p_filesz;       /* Segment size in file 
                                    size of code and data if file */
  Elf64_Xword   p_memsz;        /* Segment size in memory 
                                    same as above */
  Elf64_Xword   p_align;        /* Segment alignment 
                                    same as all programs have on my CPU*/
} Elf64_Phdr;
```

Now everything is ready. Only thing that is left is code some small code
that uses data. And it would be hello world

```asm
mov eax, 1
mov edx, 12
mov rsi, qword 0x040009c ;address of string 
mov edi, 1
syscall
 
xor edi, edi
mov eax, 60
syscall
 
msg db 'Hello World',0xA
```

To calculate offsets of code and data labels is used macro:

```asm
macro doffset
{   
    bits = 16
    display ' 0x'
    repeat bits/4
        d = '0' + $ shr (bits-%*4) and 0Fh
        if d > '9'
            d = d + 'A'-'9'-1
        end if
        display d
    end repeat
    display 13,10
}
```


Total size of executable on 64bit system:
ELF header size 0x40  
Program header 0x38  
Code size 0x24  
Data size 0xc  
Total: 168 bytes  

If 32 bit system is used then need to find definitions of data structures
and retype some bytes. Also architecture variable need to be changed.

## Future plans:
Add some shared libs and compile smallest possible program using
SDL graphics lib.

## Code
Code is written and tested on x86_64.



## Links
http://refspecs.freestandards.org/elf/elf.pdf  

## Source

http://archive.main.lv/files/writeup/create_elf_file_from_scratch/small_elf_file.zip