Skip to content

Instantly share code, notes, and snippets.

@RYSF13
Last active January 9, 2026 13:24
Show Gist options
  • Select an option

  • Save RYSF13/3322baf0762d07352b5627bb68f99490 to your computer and use it in GitHub Desktop.

Select an option

Save RYSF13/3322baf0762d07352b5627bb68f99490 to your computer and use it in GitHub Desktop.
A fully functional Snake game written in GNU Assembler without libc
/* -------------------------------------------------------------------
PROGRAM: SNAKE (ASSEMBLY & ELF HACK)
TARGET: Linux (x86 32-bit)
AUTHOR: Robert Ryan(gh@RYSF13)
[COMPILE COMMAND]
as --32 snake.asm -o s.o && \
ld -m elf_i386 --oformat binary -Ttext 0x08048000 s.o -o snake && \
rm -f s.o && chmod +x snake && ./snake
------------------------------------------------------------------- */
.intel_syntax noprefix
.global _start
/* --- Constants --- */
.equ BASE_ADDR, 0x08048000
.equ SYS_EXIT, 1
.equ SYS_READ, 3
.equ SYS_WRITE, 4
.equ SYS_IOCTL, 54
.equ SYS_SLEEP, 162
.text
/* Force alignment to prevent offset errors in ELF header */
.align 1
_start:
/* ===================================================================
1. ELF Header Injection
=================================================================== */
/* e_ident[0..3]: Magic Number */
.byte 0x7F, 'E', 'L', 'F'
/* e_ident[4..6]: Class=32, Data=LE, Version=1, ABI=0 */
.byte 1, 1, 1, 0
/* [HACK] Padding Area Entry Point (8 bytes)
We inject code into e_ident[8..15] */
_entry_point:
xor ebx, ebx
mul ebx
jmp SHORT _init
nop
nop
/* --------------------------------------------------------------- */
.word 2 /* e_type: ET_EXEC */
.word 3 /* e_machine: I386 */
.long 1 /* e_version: Current (4 bytes) */
.long BASE_ADDR + (_entry_point - _start) /* e_entry */
.long 52 /* e_phoff: Offset of Program Header */
.long 0 /* e_shoff: Unused */
.long 0 /* e_flags: Unused */
.word 52 /* e_ehsize: Header Size */
.word 32 /* e_phentsize: Phdr Entry Size */
.word 1 /* e_phnum: 1 Segment */
.word 0, 0, 0 /* e_shentsize, e_shnum, e_shstrndx */
/* ===================================================================
2. Program Header
=================================================================== */
_phdr:
.long 1 /* p_type: PT_LOAD */
.long 0 /* p_offset: File start */
.long BASE_ADDR /* p_vaddr */
.long BASE_ADDR /* p_paddr */
.long _file_end - _start /* p_filesz */
.long 0x10000 /* p_memsz: 64KB Memory */
.long 7 /* p_flags: RWX */
.long 0x1000 /* p_align */
/* ===================================================================
3. Initialization
=================================================================== */
_init:
/* Setup Terminal (Raw Mode) */
mov al, SYS_IOCTL
mov ecx, 0x5401 /* TCGETS */
mov edx, esp
int 0x80
and BYTE PTR [esp + 12], 0xF5 /* Disable ICANON | ECHO */
mov BYTE PTR [esp + 23], 0 /* VMIN = 0 (Non-blocking) */
mov al, SYS_IOCTL
mov cl, 0x02 /* TCSETS */
int 0x80
/* Initialize Variables in BSS area */
mov DWORD PTR [_file_end + 1080], 0 /* Head=0, Tail=0 */
mov WORD PTR [_file_end + 1084], 1 /* Dir=1 (Right) */
/* Setup Snake Head */
mov edi, OFFSET _file_end + 360
mov WORD PTR [edi], 155
mov BYTE PTR [_file_end + 155], 1
call _spawn_food
/* ===================================================================
4. Game Loop
=================================================================== */
_game_loop:
/* --- Frame Delay (200ms) --- */
push 200000000
push 0
mov eax, SYS_SLEEP
mov ebx, esp
xor ecx, ecx
int 0x80
add esp, 8
/* --- Input Handling --- */
mov eax, SYS_READ
xor ebx, ebx
mov ecx, esp
mov edx, 3
int 0x80
test eax, eax
jle _update_logic
/* Parse Input */
mov al, [esp]
/* Check for Ctrl+D (EOF, ASCII 0x04) */
cmp al, 4
je _game_over
/* Check for Arrow Keys (Esc Sequence) */
cmp al, 0x1B
je _check_arrow
/* Check for WASD */
cmp al, 'w'; je _go_up
cmp al, 's'; je _go_down
cmp al, 'a'; je _go_left
cmp al, 'd'; jne _update_logic
jmp _go_right
_check_arrow:
cmp BYTE PTR [esp+1], '['
jne _update_logic
mov al, [esp+2]
cmp al, 'A'; je _go_up
cmp al, 'B'; je _go_down
cmp al, 'D'; je _go_left
cmp al, 'C'; je _go_right
jmp _update_logic
/* Direction Setting */
_go_right: mov si, 1; jmp _try_change_dir
_go_left: mov si, -1; jmp _try_change_dir
_go_down: mov si, 30; jmp _try_change_dir
_go_up: mov si, -30
_try_change_dir:
/* Suicide Prevention Check (New Dir + Old Dir != 0) */
mov di, [_file_end + 1084] /* Load OLD dir */
mov ax, si /* NEW dir */
add ax, di /* Sum */
cmp ax, 0 /* If sum is 0 (180 turn), ignore input */
je _update_logic
mov [_file_end + 1084], si
_update_logic:
/* --- Game Logic --- */
movzx ebp, WORD PTR [_file_end + 1080] /* Head Idx */
movzx ebx, WORD PTR [_file_end + 1082] /* Tail Idx */
movsx esi, WORD PTR [_file_end + 1084] /* Dir */
/* Calc New Head */
mov ax, [_file_end + 360 + ebp]
add ax, si
/* Boundary Wrap */
cmp ax, 0
jge _check_upper
add ax, 360
jmp _collision_check
_check_upper:
cmp ax, 360
jl _collision_check
sub ax, 360
_collision_check:
movzx ecx, ax /* New Head Pos */
/* Check Collision */
mov al, [_file_end + ecx]
cmp al, 1; je _game_over
cmp al, 2; je _eat_food
/* Move Tail */
mov dx, [_file_end + 360 + ebx]
movzx edx, dx
mov BYTE PTR [_file_end + edx], 0
add ebx, 2
cmp ebx, 720
jl _save_tail
xor ebx, ebx
_save_tail:
mov [_file_end + 1082], bx
jmp _move_head
_eat_food:
push ecx
call _spawn_food
pop ecx
_move_head:
add ebp, 2
cmp ebp, 720
jl _write_head
xor ebp, ebp
_write_head:
mov [_file_end + 1080], bp
mov [_file_end + 360 + ebp], cx
mov BYTE PTR [_file_end + ecx], 1
call _render_frame
jmp _game_loop
_game_over:
/* Clean Exit with Message */
/* Print "GAME OVER" */
mov eax, SYS_WRITE
mov ebx, 1
mov ecx, OFFSET _msg_gameover
mov edx, 12
int 0x80
/* Exit(0) */
mov eax, SYS_EXIT
xor ebx, ebx
int 0x80
/* ===================================================================
Subroutines & Data
=================================================================== */
_spawn_food:
rdtsc
xor edx, edx
mov ecx, 360
div ecx
cmp BYTE PTR [_file_end + edx], 0
jne _spawn_food
mov BYTE PTR [_file_end + edx], 2
ret
_render_frame:
mov edi, OFFSET _file_end + 1200
mov eax, 0x485B1B /* Esc [ H */
stosd
dec edi
xor ecx, ecx
_draw_loop:
test ecx, ecx
jz _draw_char
mov eax, ecx
mov dl, 30
div dl
test ah, ah
jnz _draw_char
mov al, 10
stosb
_draw_char:
mov al, [_file_end + ecx]
cmp al, 0; je _c_sp
cmp al, 1; je _c_sn
mov al, 'X'; jmp _put
_c_sn: mov al, '#'; jmp _put
_c_sp: mov al, '.'
_put:
stosb
inc ecx
cmp ecx, 360
jl _draw_loop
mov edx, edi
sub edx, OFFSET _file_end + 1200
mov eax, SYS_WRITE
mov ebx, 1
mov ecx, OFFSET _file_end + 1200
int 0x80
ret
/* Game Over Message */
_msg_gameover:
.ascii "\nGAME OVER\n"
_file_end:
@RYSF13
Copy link
Author

RYSF13 commented Jan 9, 2026

🐍 Snake (ELF Header Hack Edition)

A fully functional Snake game written in pure x86 Assembly (GNU Assembler) for Linux.
This program bypasses the standard linker behavior, manually constructing the ELF header to achieve a compiled size of only ~624 bytes.

✨ Features

  • Extreme Size: ~624 bytes binary (Strip/No-libc).
  • Zero Dependencies: Direct Linux syscalls only.
  • ELF Abuse: Entry point injected into the ELF header padding bytes; Program Header overlaps the ELF Header.
  • Controls: Supports WASD and Arrow Keys.
  • Game Logic:
    • O(1) movement using a Ring Buffer algorithm.
    • Suicide Prevention: Prevents accidental 180° turns.
    • Toroidal Map: Walk through walls (wrap-around).
  • Graceful Exit: Ctrl+D to quit, plus a "GAME OVER" screen.

🛠 Compile & Run (Linux x86)

No special tools required—just as and ld (binutils).

as --32 snake.asm -o s.o && \
ld -m elf_i386 --oformat binary -Ttext 0x08048000 s.o -o snake && \
rm -f s.o && chmod +x snake && ./snake

🧠 How it works

  1. Header Injection: The first CPU instructions (xor, mul, jmp) are hidden inside the unused padding bytes of the ELF e_ident field.
  2. Memory Mapping: The file defines a p_memsz (64KB) much larger than p_filesz (~600b), allowing the OS to automatically allocate BSS memory for the game variables immediately after the file code.
  3. Raw Binary: Uses ld --oformat binary to strip all section headers, leaving only the bare minimum executable bytes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment