I'm trying to generate a simple static ELF using libelf, but I seem to be having troubles.
I do not wish to generate an object file and then link it w/ LD, instead I wish to generate it on my own.
The main purpose of this program is to generate a static ELF with one LOAD segment and to execute the code.
The main problem is not in the shellcode itself,but probably in some of headers I try to produce in a wrong way. When I try to run the generated ELF it gets killed as if the kernel doesn't manage to find the segment it just loaded, etc.
I would be fond if you guys could hint me.
create_elf.3.c
#include <err.h>
#include <fcntl.h>
#include <libelf.h>
#include <stdio.h>
#include <stdlib.h>
#include <sysexits.h>
#include <unistd.h>
unsigned char code[] =
"\x0b\x58\x99\x52\x66\x68\x2d\x70"
"\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61"
"\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52"
"\x51\x53\x89\xe1\xcd\x80";
int main(int argc, char *argv[])
{
int fd;
Elf *e;
Elf_Scn *scn;
Elf_Data *data;
Elf32_Ehdr *ehdr;
Elf32_Phdr *phdr;
Elf32_Shdr *shdr;
if (argc != 2)
errx(EX_USAGE,"input... ./%s filename\n",argv[0]);
if (elf_version(EV_CURRENT) == EV_NONE)
errx(EX_SOFTWARE,"elf_version is ev_none, wtf? %s\n",elf_errmsg(-1));
if ((fd = open(argv[1], O_WRONLY | O_CREAT, 0777)) < 0)
errx(EX_OSERR, "open %s\n",elf_errmsg(-1));
if ((e = elf_begin(fd, ELF_C_WRITE, NULL)) == NULL)
errx(EX_SOFTWARE,"elf_begin %s\n",elf_errmsg(-1));
if ((ehdr = elf32_newehdr(e)) == NULL)
errx(EX_SOFTWARE,"elf32_newehdr %s\n",elf_errmsg(-1));
/*
without these definitions objdump/readelf/strace/elf loader
will fail to load the binary correctly
be sure to pick them carefully and correctly, preferred exactly like the
ones like the system you are running on (so if you are running x86,
pick the same values you seen on a regular readelf -a /bin/ls
*/
ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
ehdr->e_ident[EI_CLASS] = ELFCLASS32;
ehdr->e_machine = EM_386;
ehdr->e_type = ET_EXEC;
ehdr->e_entry = 0x8040800;
if ((phdr = elf32_newphdr(e,1)) == NULL)
errx(EX_SOFTWARE,"elf32_newphdr %s\n",elf_errmsg(-1));
if ((scn = elf_newscn(e)) == NULL)
errx(EX_SOFTWARE,"elf32_newscn %s\n",elf_errmsg(-1));
if ((data = elf_newdata(scn)) == NULL)
errx(EX_SOFTWARE,"elf32_newdata %s\n",elf_errmsg(-1));
data->d_align = 4;
data->d_off = 0LL;
data->d_buf = code;
data->d_type = ELF_T_WORD; // code :x
data->d_size = sizeof(code);
data->d_version = EV_CURRENT;
if ((shdr = elf32_getshdr(scn)) == NULL)
errx(EX_SOFTWARE,"elf32_getshdr %s\n",elf_errmsg(-1));
shdr->sh_name = 0;
shdr->sh_type = SHT_PROGBITS;
shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
shdr->sh_entsize = 0; // only used if we hold a table
if (elf_update(e, ELF_C_NULL) < 0)
errx(EX_SOFTWARE,"elf_update_1 %s\n",elf_errmsg(-1));
phdr->p_type = PT_LOAD;
phdr->p_offset = ehdr->e_phoff;
phdr->p_filesz = elf32_fsize(ELF_T_PHDR, 1, EV_CURRENT);
phdr->p_vaddr = 0x8040800;
phdr->p_paddr = 0x8040800;
phdr->p_align = 4;
phdr->p_filesz = sizeof(code);
phdr->p_memsz = sizeof(code);
phdr->p_flags = PF_X | PF_R;
elf_flagphdr(e, ELF_C_SET, ELF_F_DIRTY);
if (elf_update(e, ELF_C_WRITE) < 0 )
errx(EX_SOFTWARE,"elf32_update_2 %s\n",elf_errmsg(-1));
elf_end(e);
close(fd);
return 1;
}
I would be fond if someone could hint me what's wrong here
Thanks
edit
Sorry for not giving more details,
The ELF generation seems to be working ok, I do not get any syntax errors etc, however whenever I try to run ELF I generated, e.g ./create_elf.3 foo14 (and foo14 is the generated ELF)
It gets killed, as if execve/kernel doesn't wish to load it properly I tried loading it w/ IDA but IDA shows the disassembled code fine enough
here's the output from readelf
readelf -a foo14
ELF Header:
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: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x8040800
Start of program headers: 52 (bytes into file)
Start of section headers: 116 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 1
Size of section headers: 40 (bytes)
Number of section headers: 2
Section header string table index: 0
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] <no-name> NULL 00000000 000000 000000 00 0 0 0
[ 1] <no-name> PROGBITS 00000000 000054 000020 00 AX 0 0 4
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)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000034 0x08040800 0x08040800 0x00021 0x00021 R E 0x4
There is no dynamic section in this file.
There are no relocations in this file.
There are no unwind sections in this file.
No version information found in this file.
First, it would be a good idea during testing to replace the code fragment containing (naughty) shell code with something harmless, say:
unsigned char code[] = {
0xBB, 0x2A, 0x00, 0x00, 0x00, /* movl $42, %ebx */
0xB8, 0x01, 0x00, 0x00, 0x00, /* movl $1, %eax */
0xCD, 0x80 /* int $0x80 */
};
On an i386 GNU/Linux system this revised code fragment causes the process to immediately exit with an exit code of 42.
The following ASCII art illustrates the layout of the ELF executable being built:
+----------------------------------+ <- LOADADDR (0x08048000)
| The ELF Exec Header. |
+----------------------------------+
| The ELF PHDR Table. |
+----------------------------------+ <- ehdr->e_entry points here.
| The ".text" section. |
+----------------------------------+ <- The end of loadable region
| The section name string table | for this object.
| (optional). |
+----------------------------------+
| Section headers: |
| - Header for section ".text". |
| - Section name string table |
| header. |
+----------------------------------+
The section name string table is optional. It helps to neaten the output of readelf.
#define LOADADDR 0x08048000
The executable will be loaded at the virtual address named by
LOADADDR
. The value for LOADADDR
is system dependent---a value of
0x08048000 seems to work well on my system.
The executable code fragment is placed just after the PHDR table. The
e_entry
field of the ELF Executable Header holds the virtual address
to which control will be transferred to. The value of the field
should therefore be:
size_t ehdrsz, phdrsz;
ehdrsz = elf32_fsize(ELF_T_EHDR, 1, EV_CURRENT);
phdrsz = elf32_fsize(ELF_T_PHDR, 1, EV_CURRENT);
/* ... */
ehdr->e_entry = LOADADDR + ehdrsz + phdrsz;
Code segments would use a data type of ELF_T_BYTE
and a section type
of SHT_PROGBITS
, with an alignment of 1.
if ((scn = elf_newscn(e)) == NULL)
errx(EX_SOFTWARE,"elf32_newscn %s\n", elf_errmsg(-1));
if ((data = elf_newdata(scn)) == NULL)
errx(EX_SOFTWARE,"elf32_newdata %s\n", elf_errmsg(-1));
data->d_align = 1;
data->d_off = 0LL;
data->d_buf = code;
data->d_type = ELF_T_BYTE;
data->d_size = sizeof(code);
data->d_version = EV_CURRENT;
The sh_addr
field of the section header table entry holds the
virtual address of the start of the section's data.
if ((shdr = elf32_getshdr(scn)) == NULL)
errx(EX_SOFTWARE,"elf32_getshdr %s\n", elf_errmsg(-1));
shdr->sh_name = 1; /* Offset of ".text", see below. */
shdr->sh_type = SHT_PROGBITS;
shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
shdr->sh_addr = LOADADDR + ehdrsz + phdrsz;
The sole entry in the ELF Program Header table covers the area to be loaded, starting with the ELF header and including the executable code.
if ((phdr = elf32_newphdr(e,1)) == NULL)
errx(EX_SOFTWARE,"elf32_newphdr %s\n", elf_errmsg(-1));
phdr->p_type = PT_LOAD;
phdr->p_offset = 0;
phdr->p_filesz = ehdrsz + phdrsz + sizeof(code);
phdr->p_memsz = phdr->p_filesz;
phdr->p_vaddr = LOADADDR;
phdr->p_paddr = phdr->p_vaddr;
phdr->p_align = 4;
phdr->p_flags = PF_X | PF_R;
A section name string table is optional, and makes for nicer output from readelf. A hand-rolled string table suffices:
unsigned char strtab[] = {
0, '.', 't', 'e', 'x', 't', 0,
'.', 's', 'h', 's', 't', 'r', 't', 'a', 'b', 0
};
The code to add the string table to the executable is:
/*
* Allocate a string table for section names.
*/
if ((scn = elf_newscn(e)) == NULL)
errx(EX_SOFTWARE,"elf32_newscn %s\n", elf_errmsg(-1));
if ((data = elf_newdata(scn)) == NULL)
errx(EX_SOFTWARE,"elf32_newdata %s\n", elf_errmsg(-1));
data->d_align = 1;
data->d_off = 0LL;
data->d_buf = strtab;
data->d_type = ELF_T_BYTE;
data->d_size = sizeof(strtab);
data->d_version = EV_CURRENT;
if ((shdr = elf32_getshdr(scn)) == NULL)
errx(EX_SOFTWARE,"elf32_getshdr %s\n", elf_errmsg(-1));
shdr->sh_name = 7; /* Offset of ".shstrtab". */
shdr->sh_type = SHT_STRTAB;
shdr->sh_flags = SHF_STRINGS;
With these changes the ELF binary created by your program should be runnable.
% cc a.c -lelf
% ./a.out foo
% ./foo; echo $?
42
The structure of the generated executable would be as follows:
% readelf -a foo
ELF Header:
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: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x8048054
Start of program headers: 52 (bytes into file)
Start of section headers: 116 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 1
Size of section headers: 40 (bytes)
Number of section headers: 3
Section header string table index: 2
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 08048054 000054 00000c 00 AX 0 0 1
[ 2] .shstrtab STRTAB 00000000 000060 000011 00 S 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x08048000 0x08048000 0x00060 0x00060 R E 0x4
Section to Segment mapping:
Segment Sections...
00 .text
There is no dynamic section in this file.
There are no relocations in this file.
There are no unwind sections in this file.
No version information found in this file.
The reason kernel refuses to run your program is quite simple:
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000034 0x08040800 0x08040800 0x00021 0x00021 R E 0x4
This is invalid executable because the kernel can not mmap your .text
with offset 0x34
at virtual address 0x08040800
. The file offset and the VirtAddr
must have the same alignment.
Usually the first LOAD
segment just includes the ELF
header itself, i.e. has Offset
of 0 (you would want to set size to 0x55
(== 0x21 + 0x34
)). Alternatively, you can arrange for Offset
to stay at 0x000034
and have a VirtAddr
of 0x08040834
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With