I created a tiny (104 bytes) Hello, world! ELF binary for x86-64 linux, as I've seen
this page.
http://shinh.skr.jp/obf/hello_linux_elf_x64.out
This is an assembly code in NASM.
BITS 64
org 0x01000000
hello:
db 0x7F, "ELF" ; e_ident
db "o, world!", 10
db 0, 0
dw 2 ; e_type
dw 62 ; e_machine
dd 1 ; e_version
dq _start ; e_entry
dq phdr - $$ ; e_phoff
; e_shoff
cont2:
mov AL, 4 ; write = 4
int 0x80
xchg EAX, EDI ; exit(0)
xchg EAX, EBX ; exit = 1
int 0x80
phdr:
dd 1 ; e_flags & p_type
dw 7 ; e_ehsize & p_flags
dw 56 ; e_phentsize & p_flags
dw 1 ; e_phnum & p_offset
dw 0 ; e_shentsize & p_offset
dw 0 ; e_shnum & p_offset
dw 0 ; e_shstrndx & p_offset
dq $$ + 1 ; p_vaddr
; p_paddr
_start:
inc EBX ; stdout = 1
mov DL, 14 ; strlen = 14
inc ECX
jmp cont
dq filesize - 1 ; p_filesz
; p_memsz
cont:
shl ECX, 24
db 0x25 ; and EAX, 0 (fall through)
db 0, 0, 0, 0
; p_align
xor dword[RCX], 0x2a202037
jmp cont2
filesize equ $ - $$
I also created
a spreadsheet to explain this.
If this is interesting to you, you may want to check
my collection as well.
My x86-64 code is much bigger than
58B hello because both ELF header and program header on x86-64 are much bigger than on x86-32. I couldn't find a better way to overlap ELF header and program header, and my code has all code and data in these headers. So, I'm assuming 104B is optimal.
Although this work should be easier than binary golf for x86, there were a few challenges:
- 1 byte inc/dec has gone.
- As mmap for small addresses isn't allowed on recent linux (see /proc/sys/vm/mmap_min_addr), I used addresses bigger than 16bit. In fact, 58 bytes hello for x86-32 won't work due to this reason on recent Linux distributions. I needed to use inc&shl to set the address of "Hello, world!\n" to ECX.
- As we cannot access 0x0000-0x1000, most data cannot be executed. For example, 0x0000 is add [EAX], EAX.