Skip to content

Instantly share code, notes, and snippets.

@decagondev
Created February 20, 2026 02:57
Show Gist options
  • Select an option

  • Save decagondev/8144013171b18d4fb75b5159db7e53f3 to your computer and use it in GitHub Desktop.

Select an option

Save decagondev/8144013171b18d4fb75b5159db7e53f3 to your computer and use it in GitHub Desktop.

Reverse Engineering Audit — hello Binary

Overview

  • Binary: hello
  • Source: hello.c (standard "Hello, World!" program)
  • Tool: Radare2 v5.5.0
  • Architecture: x86-64 (Linux, PIE enabled)

Source Code

#include <stdio.h>

int main(void) {
    printf("Hello, World!\n");
    return 0;
}

Function List (afl)

r2 identified 10 functions in the binary:

Address Size Name Description
0x00001000 27 sym._init ELF init section
0x00001040 10 fcn.00001040 PLT stub (lazy resolver)
0x00001050 10 sym.imp.puts PLT stub for puts() from libc
0x00001060 38 entry0 ELF entry point (_start)
0x00001090 41 sym.deregister_tm_clones glibc TLS cleanup
0x000010c0 57 sym.register_tm_clones glibc TLS setup
0x00001100 57 sym.__do_global_dtors_aux Destructor runner
0x00001140 9 entry.init0 frame_dummy (init array)
0x00001149 30 main User code
0x00001168 13 sym._fini ELF fini section

Most of these are CRT (C Runtime) boilerplate — inserted automatically by the linker and glibc. The only user-written function is main.


Disassembly of main

30: int main (int argc, char **argv, char **envp);

0x00001149      f30f1efa       endbr64
0x0000114d      55             push rbp
0x0000114e      4889e5         mov rbp, rsp
0x00001151      488d05ac0e00.  lea rax, str.Hello__World_   ; 0x2004 ; "Hello, World!"
0x00001158      4889c7         mov rdi, rax                 ; const char *s
0x0000115b      e8f0feffff     call sym.imp.puts            ; int puts(const char *s)
0x00001160      b800000000     mov eax, 0
0x00001165      5d             pop rbp
0x00001166      c3             ret

Instruction-by-Instruction Breakdown

1. endbr64 — Indirect Branch Tracking (CET)

  • Opcode: f3 0f 1e fa
  • This is an Intel Control-flow Enforcement Technology (CET) instruction. It marks this address as a valid target for indirect branches (call via function pointer, jmp via vtable, etc.).
  • If CET is enabled in hardware and an indirect branch lands somewhere without endbr64, the CPU raises a fault. This mitigates ROP (Return-Oriented Programming) attacks.
  • On CPUs without CET, it is treated as a NOP — harmless and zero overhead.

2. push rbp — Save the old frame pointer

  • Opcode: 55
  • Pushes the caller's base pointer (rbp) onto the stack so it can be restored later. This is the first half of the standard x86-64 function prologue.

3. mov rbp, rsp — Set up new stack frame

  • Opcode: 48 89 e5
  • Copies the current stack pointer (rsp) into rbp, establishing a new stack frame. Together with push rbp, this creates a linked list of stack frames that debuggers (like gdb) and tools (like backtrace()) walk to produce stack traces.
  • Since main has no local variables, rsp is never decremented further (no sub rsp, N).

4. lea rax, str.Hello__World_ — Load string address

  • Opcode: 48 8d 05 ac 0e 00 00
  • LEA = Load Effective Address. This does not read memory — it computes the address RIP + 0xeac and stores it in rax.
  • The result is 0x2004, which is the offset in the .rodata section where the string "Hello, World!" lives.
  • r2 helpfully annotates this: ; 0x2004 ; "Hello, World!"
  • This is RIP-relative addressing — standard in x86-64 position-independent code (PIE). The instruction encodes a relative offset rather than an absolute address, so the binary can be loaded at any base address by ASLR.

5. mov rdi, rax — Set up the first function argument

  • Opcode: 48 89 c7
  • In the System V AMD64 ABI (used on Linux), the first function argument is passed in register rdi. This moves the pointer to "Hello, World!" into rdi so puts() will receive it.
  • r2 annotates: const char *s — the parameter name from the puts prototype.

6. call sym.imp.puts — Call puts()

  • Opcode: e8 f0 fe ff ff
  • Calls puts() via the PLT (Procedure Linkage Table). The PLT is a trampoline: on the first call, the dynamic linker resolves the real address of puts in libc and patches the GOT (Global Offset Table). Subsequent calls jump directly to libc.
  • Compiler optimisation note: The source code says printf("Hello, World!\n"), but GCC detected that the format string has no % specifiers and ends with \n. It replaced printf(...) with puts(...) (which appends a newline automatically), saving the overhead of format-string parsing. The trailing \n is stripped from the string — that's why it's "Hello, World!" not "Hello, World!\n".

7. mov eax, 0 — Set return value to 0

  • Opcode: b8 00 00 00 00
  • Sets eax (the lower 32 bits of rax) to 0. In the System V ABI, the return value of a function is passed in rax/eax. This corresponds to return 0; in the source.
  • Writing to eax implicitly zero-extends to the full 64-bit rax register (an x86-64 rule), so rax = 0.
  • The compiler chose mov eax, 0 (5 bytes) over xor eax, eax (2 bytes) here because optimisation is at -O0 (default). At -O2, GCC would use the shorter xor form.

8. pop rbp — Restore the caller's frame pointer

  • Opcode: 5d
  • Pops the saved rbp from the stack, restoring the caller's stack frame. This is the function epilogue, paired with push rbp from step 2.

9. ret — Return to caller

  • Opcode: c3
  • Pops the return address off the stack into RIP, transferring control back to the caller (__libc_start_main in the CRT, which called main). The CRT then calls exit(0) with the value main returned.

Summary

Source Line Assembly Purpose
int main(void) { endbr64 + push rbp + mov rbp, rsp CET landing pad + stack frame setup
printf("Hello, World!\n"); lea rax, "Hello, World!" + mov rdi, rax + call puts GCC optimised printfputs
return 0; mov eax, 0 Put 0 in the return register
} pop rbp + ret Tear down frame, return to CRT

The function is 30 bytes total, with no branches — a single straight-line basic block. Clean, minimal, and exactly what you'd expect from an unoptimised "Hello, World!".


Audit generated with Radare2 v5.5.0 on Linux x86-64.

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