Skip to content

x86-64 Assembly

To exploit binaries and do reversing you need to read (and sometimes write) assembly: it’s the language code compiles to and the level you work at when manipulating execution. You don’t need to master it like a systems developer, but you do need to understand registers, basic instructions, how the stack works, and calling conventions. This card is the practical base for pwn-reversing.

# general purpose (64-bit; the 32/16/8-bit versions are sub-registers)
RAX RBX RCX RDX RSI RDI RBP RSP R8-R15
# size equivalences (RAX)
RAX (64) -> EAX (32) -> AX (16) -> AL (8)
# special
RIP instruction pointer (what runs) <- the exploit's goal
RSP stack pointer (top of the stack)
RBP base pointer (function frame)
RFLAGS flags (result of comparisons: ZF, CF, SF...)
mov dst, src copy lea dst, [addr] load address
add/sub dst, src arithmetic xor/and/or logical
push/pop push/pop the stack
call func call (push RIP + jump) ret return (pop RIP)
cmp a, b compare (sets flags)
jmp addr unconditional jump
je/jne/jg/jl... conditional jumps (by flags)
nop do nothing (0x90) -> useful in shellcode (NOP sled)
syscall system call (kernel)
# the stack grows toward LOWER ADDRESSES
# when calling a function:
call func -> push the return address, jump to func
# typical prologue:
push rbp ; save the previous RBP
mov rbp, rsp ; new frame
sub rsp, N ; reserve space for locals
# epilogue:
leave ; mov rsp, rbp ; pop rbp
ret ; pop rip (returns to the saved return address)

ret pops the address to return to off the top of the stack and puts it in RIP. If you control what’s at that stack position, you control RIP (basis of stack overflow, see Stack Buffer Overflow).

Calling convention (System V, Linux x86-64)

Section titled “Calling convention (System V, Linux x86-64)”

Key for exploits: knowing which registers hold arguments.

# the first 6 arguments go in registers, in this order:
RDI, RSI, RDX, RCX, R8, R9
# the return value goes in RAX
# example: system("/bin/sh") -> RDI = pointer to "/bin/sh", then call system
# additional arguments (7+) go on the stack

This is why in ROP (Return-Oriented Programming (ROP)) you look for pop rdi; ret gadgets — to put the argument in RDI before calling a function.

# Intel (used by most pwn tools, more readable)
mov rax, rbx ; destination, source
# AT&T (gdb default, GCC)
mov %rbx, %rax ; source, destination (reversed order, % and $)
# in gdb: set disassembly-flavor intel
objdump -d -M intel ./binary # disassemble
gdb -> disassemble main # in the debugger (pwndbg/GEF)
# in reversing: ghidra/IDA give pseudo-C + assembly side by side (see rev-static)

Understanding assembly isn’t a defense in itself, but it’s the basis for: analyzing malware (see malware), auditing your own binaries, understanding exactly what an exploit does, and writing precise detections. On the development side, compilers with mitigations (canaries, CFI) insert checks at the assembly level worth knowing.

  • I recognize the registers and their sizes (RAX/EAX/AX/AL)
  • I understand RIP/RSP/RBP and their role
  • I read the basic instructions (mov, call, ret, jmp, cmp…)
  • I can explain the prologue/epilogue and how ret controls RIP
  • I know the calling convention (RDI, RSI, RDX…)
  • I distinguish Intel from AT&T syntax
  • I disassemble with objdump/gdb/ghidra
  • I understand how syscall makes kernel calls