(That I could achieve)
Final size: 142 bytes
Intro
This page is a combination tutorial/documentary about my attempts at creating the smallest x86 ELF binary that would execute saying Hello World on Ubuntu Linux. My first attempts started with C then progressed to x86 assembly and finally to a hexeditor. I ended up compromising and switching to a "Hi World" app instead in order to fit the string data into the elf magic number. The final result is a completely corrupted x86 ELF Binary that still runs.
From start to finish.
- The first thing you need to do is get an a proper environment setup.
- Install Ubuntu (or a distro of your choice)
- run: sudo apt-get install g++ gcc nasm
- My first attempts started with C, the following is what I used for chello.c
- My initial executable was 6363 bytes.
- You can use readelf to dump the ELF header from the executable.
- ldd is useful for showing all the dynamic libraries an executable is linked to.
- file will give you a description of what a file is.
- The "not stripped" returned from the file command means that the debugging symbols haven't been stripped from the excutable.
- After stripping the executable was now 2984 bytes, still unacceptable! Time to take drastic measures...
- I scratched the C attempt and dropped using printf, instead opting for nasm x86 assembly.
- Before stripping the file was 770 bytes after stripping 448 bytes. However there is still useless headers and sections to destroy.
- Open the binary in your favorite hex editor, I use the curses hexeditor and ghex2.
-
- Delete everything including and past offset 0xAD, this will drop it down to 173 bytes
-
-
- Move 0xA4-0xAC to 0x7 and Change offset 0x86 from 0xA4 to its new location 0x07. Delete 0xA2 and 0xA3
-
- The file should be 164 bytes and now its time to enter the twilight zone... The rest is a lot to explain, basically I attempted to find what I could change in the elf head with out having it segfault on me.I added some jmps and completely corrupted the executable, however it still runs :). Here is some useful information: In x86 0xD9D0 is nop or no operation, useful for just filling space if you need to. 0xEB followed by a single signed byte is a relative jmp. Really you should read the intel docs on x86 instructions A-M N-Z .
-
typedef struct { unsigned char e_ident[EI_NIDENT]; Elf32_Half e_type; Elf32_Half e_machine; Elf32_Word e_version; Elf32_Addr e_entry; Elf32_Off e_phoff; Elf32_Off e_shoff; Elf32_Word e_flags; Elf32_Half e_ehsize; Elf32_Half e_phentsize; Elf32_Half e_phnum; Elf32_Half e_shentsize; Elf32_Half e_shnum; Elf32_Half e_shtrndx; } Elf32_Ehdr; -
Conclusion.
Final size: 142 bytes
helloworld.tar.gzI am certain that there are ways to get it even smaller. There may also be more things that can be removed from the header to increase size, but I didn't spend the enough time fully researching the ELF header format. Another option might be to use the a.out format instead of ELF may allow you to get even smaller.
Comments, suggestions, and critical criticism accepted: henszey@gmail.com