Monday, October 13, 2014

Tiny ELF 32/64 with nasm

Sometimes I need to create a tiny ELF with some assembly code, because I'm restricted in size or just don't like the bloated binary produced by gcc and the linker. The classic reference about this is A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux. I often use their nasm listing as a template.

I was working with 64bit code the other day and realized there's no 64bit version of it, so here's one.

32 bit

From A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux we can use:
; nasm -f bin -o tiny32 tiny32.asm
BITS 32
                org     0x08048000

ehdr:                                                   ; Elf32_Ehdr
                db      0x7F, "ELF", 1, 1, 1, 0         ;   e_ident
        times 8 db      0
                dw      2                               ;   e_type
                dw      3                               ;   e_machine
                dd      1                               ;   e_version
                dd      _start                          ;   e_entry
                dd      phdr - $$                       ;   e_phoff
                dd      0                               ;   e_shoff
                dd      0                               ;   e_flags
                dw      ehdrsize                        ;   e_ehsize
                dw      phdrsize                        ;   e_phentsize
                dw      1                               ;   e_phnum
                dw      0                               ;   e_shentsize
                dw      0                               ;   e_shnum
                dw      0                               ;   e_shstrndx

  ehdrsize      equ     $ - ehdr

phdr:                                                   ; Elf32_Phdr
                dd      1                               ;   p_type
                dd      0                               ;   p_offset
                dd      $$                              ;   p_vaddr
                dd      $$                              ;   p_paddr
                dd      filesize                        ;   p_filesz
                dd      filesize                        ;   p_memsz
                dd      5                               ;   p_flags
                dd      0x1000                          ;   p_align

  phdrsize      equ     $ - phdr

_start:
  mov al, 1     ; sys_exit
  mov bl, 42    ; int status
  int 0x80

filesize      equ     $ - $$
$ nasm -f bin -o tiny32 tiny32.asm
$ chmod +x tiny32
$ ./tiny32
$ echo $?
42
$ wc -c tiny32
90 tiny32


64 bit

Looking at /usr/include/elf.h and readelf(1) of an existing ELF64 binary, we can build a similar template:
; nasm -f bin -o tiny64 tiny64.asm
BITS 64
  org 0x400000

ehdr:           ; Elf64_Ehdr
  db 0x7f, "ELF", 2, 1, 1, 0 ; e_ident
  times 8 db 0
  dw  2         ; e_type
  dw  0x3e      ; e_machine
  dd  1         ; e_version
  dq  _start    ; e_entry
  dq  phdr - $$ ; e_phoff
  dq  0         ; e_shoff
  dd  0         ; e_flags
  dw  ehdrsize  ; e_ehsize
  dw  phdrsize  ; e_phentsize
  dw  1         ; e_phnum
  dw  0         ; e_shentsize
  dw  0         ; e_shnum
  dw  0         ; e_shstrndx
  ehdrsize  equ  $ - ehdr

phdr:           ; Elf64_Phdr
  dd  1         ; p_type
  dd  5         ; p_flags
  dq  0         ; p_offset
  dq  $$        ; p_vaddr
  dq  $$        ; p_paddr
  dq  filesize  ; p_filesz
  dq  filesize  ; p_memsz
  dq  0x1000    ; p_align
  phdrsize  equ  $ - phdr

_start:
  mov rax, 231 ; sys_exit_group
  mov rdi, 42  ; int status
  syscall

filesize  equ  $ - $$
$ nasm -f bin -o tiny64 tiny64.asm
$ chmod +x tiny64
$ ./tiny64
$ echo $?
42
$ wc -c tiny64
142 tiny64

Place your code under _start and you're good to go!

1 comment:

  1. Aww, not trying to match the smallest version on that site with overlapping headers? You pulled the 90byte version, but there's one half that size. Haven't looked closely though, don't know how possible it is on 64bit.

    ReplyDelete