Skip to content

Instantly share code, notes, and snippets.

@tuket
Created February 10, 2021 22:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tuket/6f214486487ab2592adbd4ad1778bc6b to your computer and use it in GitHub Desktop.
Save tuket/6f214486487ab2592adbd4ad1778bc6b to your computer and use it in GitHub Desktop.
Generate a raw ELF executable using C
#include <stdio.h>
#include <string.h>
#include <stdint.h>
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef struct Elf64Header {
char elfMagicNumber[4]; // "\x7fELF"
u8 bitAmount; // 1: 32-bit, 2: 64-bit
u8 endian; // 1: little endian, 2: big endian
u8 elfVersion1; // must be 1
u8 osAbi; // 0: System V, 3: Linux
u8 abiVersion; // in statically linked executables has no effect. In dynamically linked executables, if OS_ABI==3, defines dynamic linker features
u8 unused[7];
u16 objFileType; // ET_EXEC=2, ET_DYN=3 (DYN is used for PIC)
u16 arch; // 0x3E: AMD64
u32 elfVersion2; // the elf version again, must be 1
u64 entryPointOffset; // entry point from where the process should start executing
u64 phtOffset; // start of the program header table
u64 shtOffset; // start of the section header table
u32 processorFlags; // processor-specific flags
u16 headerSize; // the size of this header
u16 phtEntrySize; // the size of one PHT entry
u16 numPhtEntries; // num entries in the PHT
u16 shtEntrySize; // the size of one SHT entry
u16 numShtEntries; // num entries in the SHT
u16 namesSht; // the index of the SHT entry that contains the section names
} Elf64Header;
typedef struct Elf64_PhtEntry {
u32 segmentType; // 1: loadable segment, 2: dynamic linking info
u32 flags; // segment-dependent flags (position for 64-bit structure)
u64 offset; // offset of the segment in the file image
u64 vaddr; // virtual address of the segment in memory
u64 paddr; // on systems where the physical address is relevant, reserved for the physical address of the segment
u64 sizeInFile; // size of the segment in the file image
u64 sizeInMem; // size of the segment in memory
u64 align; // 0 and 1 specify no alignment. Otherwise should be a positive, integral power of 2, with 'vaddr' equating 'offset' modulus 'p_align'
} Elf64_PhtEntry;
// https://godbolt.org/z/vh8aEe
const unsigned char asmCode[] = {
0xb8, 0x01, 0x00, 0x00, 0x00, // mov rax, 1 (syscall: write)
0xbf, 0x01, 0x00, 0x00, 0x00, // mov rdi, 1 (stdout)
0x48, 0x8d, 0x35, 0x10, 0x0, 0x0, 0x0, // lea rsi, [rel helloStr]
0xba, 0x07, 0x00, 0x00, 0x00, // mov rex, sizeof(helloStr)
0x0f, 0x05, // syscall
0xb8, 0x3c, 0x00, 0x00, 0x00, // mov eax, 3c
0x48, 0x31, 0xff, // xor rdi, rdi
0x0f, 0x05 // syscall
};
const char helloStr[] = {'h', 'e', 'l', 'l', 'o', '\n'};
int main()
{
const int headersSize = sizeof(Elf64Header) + sizeof(Elf64_PhtEntry);
const int codeSize = sizeof(asmCode);
const int fileSize = headersSize + codeSize + sizeof(helloStr);
Elf64Header header = {
.elfMagicNumber = {0x7F, 'E', 'L', 'F'},
.bitAmount = 2, // 64-bit
.endian = 1, // little endian
.elfVersion1 = 1,
.osAbi = 0, // system V
.abiVersion = 0,
.unused = {0,0,0,0,0,0,0},
.objFileType = 3,
.arch = 0x3E,
.elfVersion2 = 1,
.entryPointOffset = 0x400000 + headersSize,
.phtOffset = sizeof(Elf64Header),
.shtOffset = 0,
.processorFlags = 0,
.headerSize = 64,
.phtEntrySize = sizeof(Elf64_PhtEntry),
.numPhtEntries = 1,
.shtEntrySize = 0,
.numShtEntries = 0, //1,
.namesSht = 0
};
Elf64_PhtEntry phtEntry = {
.segmentType = 1, // 1: PT_LOAD
.flags = 0x7, // 0: execute, 1: write, 2: read
.offset = headersSize,
.vaddr = 0x400000 + headersSize, // linux
.paddr = 0x400000 + headersSize,
.sizeInFile = codeSize,
.sizeInMem = codeSize,
.align = 0x1000
};
FILE* file = fopen("raw_exe", "w");
fwrite(&header, 1, sizeof(header), file);
fwrite(&phtEntry, 1, sizeof(phtEntry), file);
fwrite(asmCode, 1, sizeof(asmCode), file);
fwrite(helloStr, 1, sizeof(helloStr), file);
fclose(file);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment