ᛉ Yggdrasil

Binary

Introduction to Assembly Language

15 Sep 2026 · 7,769 words · 35 min

Contents

Legend: [x86] = 32-bit | [x64] = 64-bit | [NASM] = Intel syntax | [GAS] = AT&T syntax | [LIN] = Linux | [WIN] = Windows


1. What is Assembly Language

What it means: Assembly is the lowest-level human-readable programming language — a direct symbolic representation of a CPU's machine code instructions. Every assembly instruction maps 1:1 to an actual CPU opcode (a sequence of bytes). Unlike C or Python, there is no compiler abstraction — you control every single byte the CPU executes.

Abstraction layers (top to bottom):

┌──────────────────────────────────────────────────┐  
│ Python / JavaScript (interpreted, managed)       │ High Level  
│ C / C++ (compiled)                               │ ↕  
│ Assembly Language (assembled)                    │ ↕  
│ Machine Code (raw bytes: 0x31 0xC0...)           │ Low Level  
│ CPU microcode (hardware execution)               │  
└──────────────────────────────────────────────────┘

Why Learn Assembly?

✔ Understand how exploits work at the byte level
✔ Write custom shellcode (OSCP / red teaming)
✔ Reverse engineer binaries (malware analysis, CTFs)
✔ Understand buffer overflows, ROP chains, format strings
✔ Debug crashes at the instruction level (GDB, WinDbg)
✔ Understand compiler output (reading disassembly)
✔ Write performance-critical code (cryptography, AV bypasses)

Assembly Dialects

Dialect Syntax Assembler Used in
Intel x86/x64 mov dst, src NASM, MASM, FASM Windows, shellcode
AT&T x86/x64 movl src, dst GAS (GNU) Linux compiler output
ARM MOV R0, #1 GNU as Android, iOS, embedded
MIPS add $t0,$t1,$t2 GNU as CTFs, embedded

This note focuses on x86/x64 Intel syntax (NASM) — the most relevant for penetration testing, shellcoding, and exploit development.


2. Computer Architecture Basics

What it means: To write assembly, you must understand how the CPU, memory, and operating system interact. Assembly instructions directly manipulate CPU registers, read and write memory, and trigger kernel operations via syscalls.


2.1 The Von Neumann Architecture

+--------------------------------------------------------------+
|                           CPU                                |
|                                                              |
|  +----------------------+    +----------------------------+  |
|  |    Control Unit      |    |         Registers          |  |
|  | (decodes & executes |    | EAX EBX ECX EDX ESP EBP     |  |
|  |  instructions)       |    | ESI EDI                    |  |
|  +----------------------+    | EIP EFLAGS (x86)           |  |
|                              | RAX RBX ... R15 RIP (x64)  |  |
|                              +----------------------------+  |
|                                                              |
|            ↕ (fast)                ||                        |
|          +-----------+             ||                        |
|          |   Cache   |             ||                        |
|          +-----------+             ||                        |
+--------------------------------------------------------------+
                              |
                              | (Bus)
                              |
+-------------------------------------------------------------+
|                       Memory (RAM)                          |
|                                                             |
|  +-------------------------------------------------------+  |
|  | Stack | Heap | .bss | .data | .text                   |  |
|  | (↓)   | (↑)  | uninit | init_data | code              |  |
|  +-------------------------------------------------------+  |
|                                                             |
+-------------------------------------------------------------+

2.2 Memory Segments

.text → Executable code (your instructions live here)
.data → Initialized global/static variables
.bss → Uninitialized global/static variables (zeroed at start)
.stack → Function call stack (local vars, return addresses, args)
.heap → Dynamic memory (malloc/new — grows upward)

2.3 Endianness

Little-Endian (x86/x64 — Intel/AMD):
Value 0x12345678 stored in memory as:
Address: 0x00 0x01 0x02 0x03
Byte: 0x78 0x56 0x34 0x12 ← LSB first
(Least Significant Byte at lowest address)

Big-Endian (Network byte order, some RISC CPUs):
Address: 0x00 0x01 0x02 0x03
Byte: 0x12 0x34 0x56 0x78 ← MSB first

Why it matters in shellcode:
Pushing "/bin" onto x86 stack requires reversed bytes:
"/bin" = 0x2f62696e → push 0x6e69622f (reversed in memory = "/bin")


3. Registers — Deep Dive

What it means: Registers are ultra-fast storage locations inside the CPU — there are only a handful of them and each has a specific role. Understanding what each register does, how sub-registers relate, and which ones are preserved across function calls is fundamental to reading and writing assembly.


3.1 x86 (32-bit) Registers

General Purpose Registers (32-bit):

┌─────────┬────────────────────────────────────────────────────┐  
│ Register│ Purpose / Convention                               │  
├─────────┼────────────────────────────────────────────────────┤  
│ EAX     │ Accumulator — return values, syscall number        │  
│ EBX     │ Base — syscall arg1 (Linux x86), PIC base          │  
│ ECX     │ Counter — loop counter, syscall arg2               │  
│ EDX     │ Data — I/O, multiply overflow, syscall arg3        │  
│ ESI     │ Source Index — string operations source            │  
│ EDI     │ Dest Index — string operations destination         │  
│ ESP     │ Stack Pointer — top of stack (DO NOT clobber)      │  
│ EBP     │ Base Pointer — stack frame base                    │  
│ EIP     │ Instruction Pointer — address of next instruction  │  
└─────────┴────────────────────────────────────────────────────┘

3.2 Sub-Register Breakdown

32-bit register breakdown:
Bit: 31 16 15 8 7 0
EAX: [ Higher 16 | AH | AL]
[ AX]
[ EAX]
[ RAX (64-bit)]

Examples:
RAX = 64-bit full register
EAX = lower 32 bits of RAX
AX = lower 16 bits of EAX
AH = bits 8-15 of AX (high byte)
AL = bits 0-7 of AX (low byte)

Same pattern:
RBX → EBX → BX → BH/BL
RCX → ECX → CX → CH/CL
RDX → EDX → DX → DH/DL
RSI → ESI → SI → SIL (8-bit)
RDI → EDI → DI → DIL (8-bit)
RSP → ESP → SP → SPL (8-bit)
RBP → EBP → BP → BPL (8-bit)

x64 additional:
R8 → R8D → R8W → R8B
R9 → R9D → R9W → R9B
R10 → R10D → R10W → R10B
...
R15 → R15D → R15W → R15B

3.3 x64 (64-bit) Registers

x64 General Purpose Registers:

┌─────────┬──────────────────────────────────────────────────────┐  
│ Register│ Purpose (System V AMD64 ABI — Linux calling conv)    │  
├─────────┼──────────────────────────────────────────────────────┤  
│ RAX     │ Return value, syscall number                         │  
│ RBX     │ Callee-saved (preserve across calls)                 │  
│ RCX     │ 4th arg (Windows), clobbered by syscall              │  
│ RDX     │ 3rd function arg / syscall arg                       │  
│ RSI     │ 2nd function arg / syscall arg                       │  
│ RDI     │ 1st function arg / syscall arg                       │  
│ RSP     │ Stack pointer (always keep 16-byte aligned)          │  
│ RBP     │ Base pointer / callee-saved                          │  
│ R8      │ 5th function arg                                     │  
│ R9      │ 6th function arg                                     │  
│ R10     │ Syscall arg (replaces RCX in syscalls)               │  
│ R11     │ Clobbered by syscall instruction                     │  
│ R12-R15 │ Callee-saved (preserve across calls)                 │  
│ RIP     │ Instruction pointer                                  │  
│ RFLAGS  │ CPU flags (ZF, CF, SF, OF, PF, AF, DF, IF, TF)       │  
└─────────┴──────────────────────────────────────────────────────┘

3.4 Caller vs Callee Saved Registers

Caller-saved (volatile) — function may destroy these:
x86: EAX, ECX, EDX
x64: RAX, RCX, RDX, RSI, RDI, R8, R9, R10, R11

Callee-saved (non-volatile) — function MUST preserve these:
x86: EBX, ESI, EDI, EBP, ESP
x64: RBX, RBP, R12, R13, R14, R15, RSP

Rule: If you use a callee-saved register in your function,
PUSH it at the start and POP it before returning.


4. Memory & Addressing Modes

What it means: Assembly can access memory in several ways — directly via an address, indirectly via a register, or through a register + offset formula. Understanding addressing modes is essential for working with arrays, structs, and stack frames.


4.1 Immediate Mode (Literal Value)

; Operand is a literal constant embedded in the instruction
mov eax, 42         ; EAX = 42 (decimal)
mov eax, 0x2a       ; EAX = 42 (hex)
mov eax, 0b101010   ; EAX = 42 (binary)
mov al, 'A'         ; AL  = 65 (ASCII 'A')

4.2 Register Mode

; Operand is a register
mov eax, ebx        ; EAX = EBX (copy register to register)
mov esi, edi        ; ESI = EDI
add eax, ecx        ; EAX = EAX + ECX

4.3 Direct Memory Mode

; Operand is a memory address (brackets = dereference)
mov eax, [0x804a000]    ; EAX = value at memory address 0x804a000
mov [0x804a000], eax    ; memory[0x804a000] = EAX

; In data section:
section .data
myvar: dd 0x1234         ; define dword at label 'myvar'

section .text
mov eax, [myvar]         ; EAX = value of myvar (0x1234)
mov [myvar], ebx         ; myvar = EBX

4.4 Register Indirect Mode

; Bracket around register = use register value as memory address
mov eax, [ebx]      ; EAX = memory[EBX]  (dereference pointer in EBX)
mov [ecx], eax      ; memory[ECX] = EAX
mov al, [esi]       ; AL = byte at memory[ESI]

; Example: dereference array pointer
; EBX = 0x1000 (start of array)
; [EBX] = first element of array

4.5 Base + Offset / Base + Index Addressing

; Most powerful and common — used for arrays, structs, stack frames
mov eax, [ebx + 4]       ; EAX = memory[EBX + 4]
mov eax, [ebp - 8]       ; EAX = local variable 8 bytes below frame
mov eax, [ebx + ecx*4]   ; EAX = memory[EBX + ECX*4]  (array indexing)
mov eax, [ebx + ecx*4 + 8]  ; base + index*scale + displacement

; Scale factor can be: 1, 2, 4, 8
; Used for indexing into arrays of byte(1), word(2), dword(4), qword(8)

; Common stack frame patterns:
mov eax, [ebp + 8]    ; first argument passed to function
mov eax, [ebp + 12]   ; second argument
mov eax, [ebp - 4]    ; first local variable
mov eax, [ebp - 8]    ; second local variable

4.6 LEA — Load Effective Address

; LEA calculates the address (does NOT dereference)
; Useful for pointer arithmetic and PIC addressing

lea eax, [ebx + 4]      ; EAX = EBX + 4  (just the math — no memory access)
lea eax, [ebp - 8]      ; EAX = address of local variable
lea rdi, [rel string]   ; RDI = RIP-relative address of 'string' label (PIC x64)

; Difference between MOV and LEA:
mov eax, [ebx + 4]      ; EAX = *( EBX + 4 )  ← dereferences memory
lea eax, [ebx + 4]      ; EAX =    EBX + 4     ← just the pointer value

5. The Stack

What it means: The stack is a Last-In-First-Out (LIFO) data structure in memory used for function calls, local variables, and temporary storage. The stack grows DOWNWARD on x86/x64 — pushing something onto the stack DECREMENTS ESP/RSP. This is the most important concept for understanding function calls, buffer overflows, and ROP chains.


5.1 Stack Mechanics

Memory Layout (x86):

High Addresses ─────────────────────  
│ command line args                  │  
│ environment vars                   │  
│ .text section                      │  
│ .data section                      │  
│ heap (grows up ↑)                  │  
│ ↑                                  │  
│                                    │  
│ ↓                                  │  
│ stack (grows down)│ ← ESP points here  
Low Addresses ─────────────────────

PUSH: ESP -= 4; memory[ESP] = value (decrements first, then writes)
POP: value = memory[ESP]; ESP += 4 (reads first, then increments)

5.2 PUSH and POP

; PUSH — decrements SP, writes value
push eax            ; ESP -= 4; [ESP] = EAX
push 42             ; ESP -= 4; [ESP] = 42
push 0xdeadbeef     ; ESP -= 4; [ESP] = 0xdeadbeef

; POP — reads value, increments SP
pop eax             ; EAX = [ESP]; ESP += 4
pop ebx             ; EBX = [ESP]; ESP += 4

; PUSHAD / POPAD — push/pop all general registers
pushad              ; push EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI
popad               ; pop  EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX

; Manually adjust stack pointer
sub esp, 16         ; allocate 16 bytes of local space
add esp, 16         ; deallocate (same as 4 POPs)

5.3 Stack Frame (Function Prologue & Epilogue)

; Standard x86 function prologue — creates stack frame
func:
    push ebp            ; save caller's base pointer
    mov ebp, esp        ; set our base pointer (frame base)
    sub esp, 16         ; allocate space for local variables

    ; Access arguments (above saved EBP):
    ; [ebp + 4]  = return address
    ; [ebp + 8]  = first argument
    ; [ebp + 12] = second argument

    ; Access locals (below EBP):
    ; [ebp - 4]  = first local variable (4 bytes)
    ; [ebp - 8]  = second local variable
    ; [ebp - 16] = third local variable

    ; ... function body ...

    ; Standard function epilogue — tears down stack frame
    mov esp, ebp        ; restore ESP (deallocate locals)
    pop ebp             ; restore caller's base pointer
    ret                 ; pop return address into EIP

; Compact epilogue alternative:
    leave               ; equivalent to: mov esp,ebp; pop ebp
    ret

5.4 Stack Frame Diagram

Memory after function prologue (sub esp, 8):

┌───────────────┬────────---------------------------─-------────-┐  
│ Address       │                  Contents                      │  
├───────────────┼────────────----------------------------------─-┤  
│ EBP + 12      │ arg2      │ ← second argument                  |
│ EBP + 8       │ arg1      │ ← first argument                   |
│ EBP + 4       │ ret addr  │ ← return address (pushed by CALL)  |  
│ EBP + 0       │ saved EBP │ ← previous frame pointer ← EBP     | 
│ EBP - 4       │ local1    │ ← first local variable             |
│ EBP - 8       │ local2    │ ← second local variable ← ESP      |
└───────────────┴────────-----------------------------------─────┘

Stack grows downward (lower addresses = deeper stack)


6. Data Types & Sizes

What it means: Assembly works with raw bytes at specific sizes. NASM provides size directives for declaring data in the .data section and size specifiers for unambiguous memory operations. Getting these wrong causes silent data corruption bugs.


6.1 Data Size Reference

Name NASM Bytes Bits C Equivalent Range (unsigned)
───────────────────────────────────────────────────────────────────
Byte db 1 8 uint8_t / char 0 to 255
Word dw 2 16 uint16_t 0 to 65,535
Doubleword dd 4 32 uint32_t / int 0 to 4,294,967,295
Quadword dq 8 64 uint64_t 0 to 2^64-1
Tenbyte dt 10 80 long double

6.2 Declaring Data in .data Section

section .data

; Initialized data
my_byte:    db  0x41            ; single byte: 0x41 ('A')
my_word:    dw  0x1234          ; 2 bytes: 0x34 0x12 (little endian)
my_dword:   dd  0xdeadbeef      ; 4 bytes
my_qword:   dq  0x123456789abc  ; 8 bytes
my_float:   dd  3.14            ; 32-bit float
my_double:  dq  3.14159         ; 64-bit double

; Strings
msg:        db  "Hello, World!", 0x0a, 0  ; string + newline + null
binsh:      db  "/bin/sh", 0              ; null-terminated string
raw:        db  0x41, 0x42, 0x43          ; bytes 'A','B','C'

; Arrays
arr:        dd  1, 2, 3, 4, 5            ; array of 5 dwords
zeros:      times 10 db 0                 ; 10 zero bytes (like memset)

section .bss
; Uninitialized data (allocated at runtime, initially 0)
buffer:     resb 64     ; reserve 64 bytes
wordvar:    resw 1      ; reserve 1 word (2 bytes)
dwordvar:   resd 1      ; reserve 1 dword
qwordvar:   resq 1      ; reserve 1 qword

6.3 Size Specifiers in Instructions

; When NASM can't determine size from context, use explicit specifier
; BYTE PTR, WORD PTR, DWORD PTR, QWORD PTR

mov byte  [eax], 0x41       ; write 1 byte at [EAX]
mov word  [eax], 0x4142     ; write 2 bytes at [EAX]
mov dword [eax], 0xdeadbeef ; write 4 bytes at [EAX]
mov qword [rax], 0x1234     ; write 8 bytes at [RAX]

; Without specifier (NASM infers from register size):
mov al, [eax]    ; byte read (AL = 8-bit)
mov ax, [eax]    ; word read (AX = 16-bit)
mov eax, [ebx]   ; dword read (EAX = 32-bit)
mov rax, [rbx]   ; qword read (RAX = 64-bit)

7. NASM Syntax & Program Structure

What it means: NASM (Netwide Assembler) is the most popular assembler for x86/x64 shellcode and exploit development. It uses Intel syntax (destination first), supportsmacros, and produces clean ELF binaries. Every NASM program has a predictable structure you'll use in every shellcode you write.


7.1 Basic Program Structure

; Comments start with semicolon

; ─── Sections ───
section .data           ; initialized data
    msg db "Hello!", 0xa, 0   ; string with newline
    len equ $ - msg           ; length = current pos - msg label
                              ; (equ = constant, not memory)

section .bss            ; uninitialized data
    buf resb 64         ; reserve 64 bytes

section .text           ; executable code
global _start           ; entry point (must be global for linker)

_start:                 ; program entry label
    ; your instructions here

    ; exit cleanly (Linux x86)
    mov eax, 1          ; syscall: exit
    xor ebx, ebx        ; exit code: 0
    int 0x80

7.2 NASM Directives & Operators

; ─── Directives ───
global _start           ; export symbol (linker needs this)
extern printf           ; import external symbol
bits 32                 ; specify 32-bit mode (default for elf32)
bits 64                 ; specify 64-bit mode

; ─── EQU — define constant (no memory, no label) ───
SYS_WRITE equ 4
SYS_EXIT  equ 1
STDOUT    equ 1

; ─── $ and $$ ───
$       ; current address (position in source)
$$      ; start address of current section

; Calculate string length:
msg:  db "Hello World"
len:  equ $ - msg       ; len = 11 (no null needed for write syscall)

; ─── TIMES — repeat ───
times 10 db 0           ; 10 zero bytes
times 4  nop            ; 4 NOP instructions (padding)

; ─── INCBIN — include raw binary ───
incbin "shellcode.bin"  ; embed raw file bytes

; ─── Macros ───
%define STDOUT 1        ; text substitution macro

%macro mymacro 2        ; macro with 2 arguments
    mov eax, %1
    mov ebx, %2
%endmacro

mymacro 4, 1            ; expands to: mov eax,4; mov ebx,1

7.3 Labels

; Labels mark addresses in code/data
; They are just symbolic names for memory positions

_start:                 ; global label (exported)
.loop:                  ; local label (scoped to nearest global)
    dec ecx
    jnz .loop           ; can reference local labels with dot prefix

func:                   ; function label
    push ebp
    mov ebp, esp
    ; ...
    pop ebp
    ret

; Numeric labels (backward/forward reference)
1:  push eax
    jmp 1b              ; jump backward to label 1
    jmp 2f              ; jump forward to label 2
2:  pop eax

8. Data Movement Instructions

What it means: Data movement instructions are the most-used in any assembly program. They move data between registers, between registers and memory, and onto/off the stack.Mastering these — especially with different addressing modes — is the foundation of writing any meaningful assembly code.


8.1 MOV — The Workhorse

; MOV dst, src — copy data (does NOT modify flags)
; dst and src must be same size
; Cannot MOV memory-to-memory directly

; Register ← Immediate
mov eax, 42             ; EAX = 42
mov rax, 0x123456789abc ; RAX = large 64-bit value
mov al,  0x41           ; AL  = 0x41

; Register ← Register
mov eax, ebx            ; EAX = EBX
mov rsi, rdi            ; RSI = RDI

; Register ← Memory
mov eax, [0x804a000]    ; EAX = dword at 0x804a000
mov eax, [ebx]          ; EAX = dword at address in EBX
mov eax, [ebx + 4]      ; EAX = dword at EBX+4
mov al, [esi]           ; AL  = byte at address in ESI

; Memory ← Register
mov [0x804a000], eax    ; store EAX at address
mov [ebx], eax          ; store EAX at address in EBX
mov [ebp - 4], ecx      ; store ECX in local variable

; Memory ← Immediate
mov byte  [eax], 0x90   ; write NOP byte at [EAX]
mov dword [ebx], 0      ; write 0 (null) at [EBX]

8.2 MOVZX / MOVSX — Zero/Sign Extension

; MOVZX — move with zero extension (pads with zeros)
movzx eax, al           ; EAX = zero-extend AL (upper 3 bytes = 0)
movzx eax, ax           ; EAX = zero-extend AX
movzx eax, byte [esi]   ; read byte from memory, zero-extend to dword

; MOVSX — move with sign extension (preserves sign bit)
movsx eax, al           ; EAX = sign-extend AL (if AL=0x80, EAX=0xffffff80)
movsx eax, ax
movsx eax, byte [esi]

; Why needed: mixing register sizes
movzx ecx, byte [esi]   ; safely load byte into 32-bit register

8.3 XCHG — Exchange

; XCHG — swap two values (no temp register needed)
xchg eax, ebx           ; swap EAX and EBX
xchg [var], eax         ; swap memory and register (LOCK prefix makes atomic)

; Common trick: XCHG EAX,EAX = NOP (1 byte: 0x90)
xchg eax, eax           ; NOP!

8.4 LEA — Load Effective Address

; LEA computes address arithmetic WITHOUT memory access
lea eax, [ebx + 4]      ; EAX = EBX + 4 (arithmetic, no memory read)
lea eax, [ebx + ecx*4]  ; EAX = EBX + ECX*4
lea eax, [ebp - 8]      ; EAX = address of local variable

; Hack: use LEA for fast multiplication
lea eax, [eax + eax*2]  ; EAX = EAX + EAX*2 = EAX*3 (fast multiply by 3)
lea eax, [eax*4]        ; EAX = EAX * 4
lea eax, [eax + eax*4]  ; EAX = EAX * 5

8.5 String Move Instructions

; REP MOVSB — copy ECX bytes from [ESI] to [EDI]
; ESI = source, EDI = destination, ECX = count
; After each iteration: ESI++, EDI++, ECX-- (if DF=0, direction forward)

cld                     ; clear direction flag (DF=0 → forward)
mov esi, src            ; source address
mov edi, dst            ; destination address
mov ecx, 64            ; count
rep movsb               ; copy 64 bytes

; MOVSB/MOVSW/MOVSD/MOVSQ — without REP (single copy)
movsb                   ; copy 1 byte
movsw                   ; copy 2 bytes
movsd                   ; copy 4 bytes
movsq                   ; copy 8 bytes (x64)

; REP STOSB — fill ECX bytes at [EDI] with AL value
xor al, al              ; fill with zeros
mov edi, buf
mov ecx, 64
rep stosb               ; zero 64 bytes (like memset(buf, 0, 64))

9. Arithmetic Instructions

What it means: Arithmetic instructions perform math on register and memory operands. Unlike high-level languages, you deal directly with overflow, carry, sign, and zero flags that are set automatically as side effects of arithmetic operations.


9.1 ADD / SUB

; ADD dst, src — dst = dst + src  (modifies CF, ZF, SF, OF, PF, AF)
add eax, 5          ; EAX = EAX + 5
add eax, ebx        ; EAX = EAX + EBX
add [var], eax      ; memory += EAX
add al, 0x41        ; AL = AL + 0x41

; SUB dst, src — dst = dst - src
sub eax, 3          ; EAX = EAX - 3
sub esp, 16         ; allocate 16 bytes on stack
sub eax, ebx        ; EAX = EAX - EBX

9.2 INC / DEC

; INC — increment by 1 (does NOT modify CF — useful in loops)
inc eax             ; EAX++
inc byte [esi]      ; byte at [ESI]++
inc ecx             ; ECX++ (common loop counter)

; DEC — decrement by 1
dec ecx             ; ECX-- (common loop counter)
dec byte [edi]

9.3 IMUL / MUL — Multiplication

; MUL (unsigned) — multiplies EAX by operand
; Result: EDX:EAX (64-bit result split across two registers)
mov eax, 10
mov ebx, 5
mul ebx             ; EDX:EAX = 10 * 5 = 50 → EAX=50, EDX=0

; IMUL (signed) — three forms
imul ebx            ; EDX:EAX = EAX * EBX (like MUL but signed)
imul eax, ebx       ; EAX = EAX * EBX (two-operand form, result truncated)
imul eax, ebx, 5    ; EAX = EBX * 5  (three-operand form)

; Trick: xor eax,eax; mul anything → zeroes EAX and EDX (useful in shellcode!)
xor eax, eax        ; EAX = 0
mul eax             ; EDX:EAX = 0*0 = 0 (zeroes both EAX and EDX!)

9.4 IDIV / DIV — Division

; DIV (unsigned) — divides EDX:EAX by operand
; Quotient → EAX, Remainder → EDX
mov eax, 17
xor edx, edx        ; MUST zero EDX before DIV (it's part of dividend)
mov ebx, 5
div ebx             ; EAX = 17/5 = 3, EDX = 17%5 = 2

; IDIV (signed) — same but signed
cdq                 ; sign-extend EAX into EDX (for signed division)
idiv ebx

9.5 NEG / NOT

; NEG — two's complement negation (dst = 0 - dst)
neg eax             ; EAX = -EAX
neg byte [esi]

; NOT — bitwise NOT (one's complement)
not eax             ; EAX = ~EAX (flip all bits)
not byte [esi]

10. Logical & Bitwise Instructions

What it means: Bitwise instructions manipulate individual bits. They're critical for shellcode (zeroing registers without null bytes), flag testing, bit masking, and encoding. XOR is perhaps the most important single instruction in shellcode writing.


10.1 AND / OR / XOR

; AND — bitwise AND (sets bits where BOTH are 1)
and eax, 0xFF       ; mask lower byte (EAX & 0xFF — keeps only low 8 bits)
and eax, 0xFFFFFFFE ; clear lowest bit
and eax, eax        ; test if EAX=0 (sets ZF, SF — doesn't change EAX)

; OR — bitwise OR (sets bits where EITHER is 1)
or  eax, 0x01       ; set lowest bit
or  eax, eax        ; test if EAX=0 (sets flags)
or  dx,  0xFFF      ; set lower 12 bits (used in egg hunter for page alignment)

; XOR — bitwise XOR (sets bits where inputs DIFFER)
; Most important: A XOR A = 0 (ZERO WITHOUT NULL BYTES!)
xor eax, eax        ; EAX = 0  ← critical shellcode technique (no \x00)
xor ebx, ebx        ; EBX = 0
xor ecx, ecx        ; ECX = 0
xor byte [esi], 0x41 ; XOR-encrypt/decrypt byte in memory
xor eax, ebx        ; EAX = EAX ^ EBX (toggle bits)

10.2 TEST

; TEST dst, src — perform AND but only set flags (don't store result)
; Most common use: check if register is zero
test eax, eax       ; if EAX == 0: ZF=1; if EAX != 0: ZF=0
jz  label           ; jump if ZF=1 (EAX was zero)
jnz label           ; jump if ZF=0 (EAX was not zero)

; Check specific bit
test eax, 0x01      ; test bit 0 (is it odd?)
jnz odd             ; jump if bit 0 was set

; Faster than CMP for zero check:
; test eax,eax = 2 bytes
; cmp  eax,0   = 3 bytes

10.3 SHL / SHR / SAR — Bit Shifts

; SHL — shift left (multiply by 2 per shift, fills with 0)
shl eax, 1          ; EAX = EAX * 2
shl eax, 3          ; EAX = EAX * 8
shl eax, cl         ; shift by amount in CL register

; SHR — shift right unsigned (divide by 2, fills with 0)
shr eax, 1          ; EAX = EAX / 2 (unsigned)
shr eax, 2          ; EAX = EAX / 4

; SAR — shift right signed (divide by 2, fills with sign bit)
sar eax, 1          ; EAX = EAX / 2 (signed — preserves sign)

; ROR / ROL — rotate (no bits lost, wrap around)
ror eax, 8          ; rotate right 8 bits (upper byte → lower byte)
rol eax, 8          ; rotate left 8 bits
; Used in: hash functions (ROR13), encoding, crypto

11. Control Flow — Jumps & Loops

What it means: Control flow instructions change the order of execution. Unlike high-level languages with if/else/for/while, assembly uses explicit jumps based on CPU flags. Understanding conditional jumps and building loop patterns is essential.


11.1 Unconditional Jump

jmp label           ; jump to label (unconditional — always taken)
jmp eax             ; jump to address in EAX (indirect jump)
jmp [eax]           ; jump to address stored at memory[EAX]
jmp short label     ; short jump (within -128 to +127 bytes)
jmp near  label     ; near jump (within same segment — default)

11.2 Conditional Jumps

; After CMP or TEST, flags are set — use conditional jumps

; CMP dst, src — computes dst - src, sets flags, discards result
cmp eax, 5          ; set flags based on EAX - 5

; ─── Jump based on equality ───
je  label           ; jump if equal          (ZF=1)
jne label           ; jump if not equal      (ZF=0)
jz  label           ; jump if zero           (ZF=1) same as JE
jnz label           ; jump if not zero       (ZF=0) same as JNE

; ─── Jump based on unsigned comparison ───
ja  label           ; jump if above          (CF=0 and ZF=0)
jae label           ; jump if above or equal (CF=0)
jb  label           ; jump if below          (CF=1)
jbe label           ; jump if below or equal (CF=1 or ZF=1)
jc  label           ; jump if carry          (CF=1)
jnc label           ; jump if no carry       (CF=0)

; ─── Jump based on signed comparison ───
jg  label           ; jump if greater        (ZF=0 and SF=OF)
jge label           ; jump if >= (greater or equal)
jl  label           ; jump if less           (SF != OF)
jle label           ; jump if <= (less or equal)

; ─── Jump based on individual flags ───
js  label           ; jump if sign flag set  (SF=1) — result negative
jns label           ; jump if sign flag clear(SF=0) — result positive
jo  label           ; jump if overflow       (OF=1)
jno label           ; jump if no overflow    (OF=0)
jp  label           ; jump if parity even    (PF=1)
jnp label           ; jump if parity odd     (PF=0)

11.3 Loop Patterns

; ─── Classic LOOP instruction ───
; LOOP decrements ECX and jumps if ECX != 0
; (like: dec ecx; jnz label)

    mov ecx, 10         ; loop 10 times
.loop_top:
    ; ... body ...
    loop .loop_top      ; ECX--; if ECX != 0: jump to .loop_top

; ─── Manual loop with JNZ (more flexible) ───
    xor ecx, ecx
    mov cl, 3           ; 3 iterations (use CL to avoid null bytes)
dup2_loop:
    dec cl
    ; ... body ...
    jnz dup2_loop       ; repeat until CL = 0

; ─── Count-up loop ───
    xor ecx, ecx        ; counter = 0
.loop:
    ; ... body using ECX as index ...
    inc ecx
    cmp ecx, 10         ; done when ECX = 10
    jl .loop            ; if ECX < 10: continue

; ─── Do-while equivalent ───
.do_while:
    ; ... body ...
    cmp eax, 0
    jne .do_while       ; repeat while EAX != 0

; ─── If-else equivalent ───
    cmp eax, 5
    je  .equal          ; if (eax == 5)
    jg  .greater        ; else if (eax > 5)
    ; falls through to: less than case
    jmp .end
.equal:
    ; equal case
    jmp .end
.greater:
    ; greater case
.end:
    ; continue

12. Procedures & the Call Stack

What it means: Procedures (functions) in assembly use CALL and RET to transfer control. CALL pushes the return address (next instruction after CALL) onto the stack and jumps to the procedure. RET pops the return address and jumps to it. Understanding this mechanism is the key to understanding how buffer overflows overwrite return addresses.


12.1 CALL and RET

; CALL label  ≡  push EIP+size_of_call; jmp label
; RET         ≡  pop EIP

; Calling a function:
push arg2           ; push arguments right to left (x86 cdecl)
push arg1
call my_function    ; push return addr; jump to function
add esp, 8          ; caller cleans up (cdecl) — 2 args × 4 bytes

; Function definition:
my_function:
    push ebp            ; save caller's EBP (prologue)
    mov ebp, esp        ; set frame pointer
    sub esp, 8          ; local variable space

    mov eax, [ebp+8]    ; access first argument
    mov ecx, [ebp+12]   ; access second argument
    ; ... function body ...
    mov eax, result     ; return value in EAX

    mov esp, ebp        ; epilogue: restore ESP
    pop ebp             ; restore EBP
    ret                 ; return to caller

; RET with cleanup (stdcall — callee cleans):
    ret 8               ; pop 8 bytes from stack (2 args) after return

12.2 How CALL Works (Buffer Overflow Context)

Before CALL:
Stack: [ arg2][ arg1] ← ESP

CALL my_function executes:
1. Push EIP (address of instruction AFTER call) → return address
2. Jump to my_function

After CALL (inside function, before prologue):
Stack: [ arg2][ arg1][ return_address] ← ESP
↑ If we overwrite this → control EIP!

After prologue (push ebp; mov ebp,esp):
Stack: [ arg2][ arg1][ ret_addr][ saved_EBP][ locals...]
← EBP ← ESP

Buffer overflow overwrites locals → overflows into saved_EBP → ret_addr
→ RET pops our value into EIP → we control execution!


13. Flags Register

What it means: The EFLAGS register contains single-bit status flags automatically updated by arithmetic and logical instructions. Conditional jumps check these flags. As an exploit developer, understanding which instructions set which flags — and using that knowledge to check conditions — is essential.


EFLAGS Register (32-bit):
Bit: 31...12 11 10 9 8 7 6 5 4 3 2 1 0
Flag: OF DF IF TF SF ZF - AF - PF - CF

CF = Carry Flag — set when result carries/borrows beyond bit width
PF = Parity Flag — set when low byte of result has even # of 1 bits
AF = Adjust Flag — BCD arithmetic carry (rarely used)
ZF = Zero Flag — set when result is zero (most used for comparisons!)
SF = Sign Flag — set when result is negative (MSB=1)
TF = Trap Flag — debug single-step mode
IF = Interrupt Flag — enables/disables hardware interrupts
DF = Direction Flag — string operation direction (0=forward, 1=backward)
OF = Overflow Flag — set when signed arithmetic overflows

; Instructions that SET flags (common ones):
; ADD, SUB, AND, OR, XOR, CMP, TEST, INC, DEC, SHL, SHR

; Instructions that DO NOT modify flags:
; MOV, PUSH, POP, LEA, CALL, RET, JMP (and all conditional jumps)

; Flag manipulation:
cld                 ; clear DF (direction flag → forward string ops)
std                 ; set DF (direction flag → backward string ops)
cli                 ; clear IF (disable interrupts — ring 0 only)
sti                 ; set  IF (enable  interrupts — ring 0 only)
clc                 ; clear CF (carry flag)
stc                 ; set  CF
cmc                 ; complement CF (toggle)

; Read/write flags via LAHF/SAHF:
lahf                ; AH = lower 8 bits of EFLAGS
sahf                ; EFLAGS lower 8 bits = AH

14. Linux Syscalls in Assembly

What it means: On Linux, programs interact with the kernel via syscalls. In assembly, you load the syscall number and arguments into the correct registers, then invoke the kernel. This is how shellcode spawns shells, opens sockets, and reads files — without any library calls at all.


14.1 Complete Hello World (x86)

; hello_x86.asm — print "Hello, World!\n" and exit
; NASM syntax, x86 Linux

section .data
    msg db "Hello, World!", 0x0a    ; string + newline
    len equ $ - msg                  ; length = 14

section .text
global _start

_start:
    ; write(1, msg, len)
    mov eax, 4          ; syscall: write (4)
    mov ebx, 1          ; fd: stdout (1)
    mov ecx, msg        ; buf: address of message
    mov edx, len        ; count: length
    int 0x80            ; invoke kernel

    ; exit(0)
    mov eax, 1          ; syscall: exit (1)
    xor ebx, ebx        ; status: 0
    int 0x80
# Compile and run
nasm -f elf32 hello_x86.asm -o hello_x86.o
ld -m elf_i386 -o hello_x86 hello_x86.o
./hello_x86
# Output: Hello, World!

14.2 Complete Hello World (x64)

; hello_x64.asm — x64 Linux
section .data
    msg db "Hello, World!", 0x0a
    len equ $ - msg

section .text
global _start

_start:
    ; write(1, msg, len)
    mov rax, 1          ; syscall: write (1)
    mov rdi, 1          ; fd: stdout
    lea rsi, [rel msg]  ; buf: RIP-relative address of msg
    mov rdx, len        ; count
    syscall

    ; exit(0)
    mov rax, 60         ; syscall: exit (60)
    xor rdi, rdi        ; status: 0
    syscall
nasm -f elf64 hello_x64.asm -o hello_x64.o
ld -o hello_x64 hello_x64.o
./hello_x64

14.3 Read User Input (x64)

; read(0, buf, 64) — read 64 bytes from stdin
section .bss
    buf resb 64

section .text
global _start

_start:
    ; read(0, buf, 64)
    xor rax, rax        ; syscall: read (0)
    xor rdi, rdi        ; fd: stdin (0)
    lea rsi, [rel buf]  ; buf address
    mov rdx, 64         ; max bytes
    syscall             ; RAX = bytes read

    ; write(1, buf, bytes_read)
    mov rdx, rax        ; count = bytes_read (return value of read)
    mov rax, 1          ; syscall: write
    mov rdi, 1          ; stdout
    lea rsi, [rel buf]
    syscall

    ; exit(0)
    mov rax, 60
    xor rdi, rdi
    syscall

15. Assembling, Linking & Debugging

What it means: The workflow for assembly development: write .asm → assemble to .o (object file) → link to executable → test → debug with GDB. Knowing this workflow cold is essential for shellcode development and exploit writing.


15.1 NASM Assembly Commands

# x86 (32-bit ELF)
nasm -f elf32 program.asm -o program.o
ld -m elf_i386 -o program program.o

# x64 (64-bit ELF)
nasm -f elf64 program.asm -o program.o
ld -o program program.o

# With debug info
nasm -f elf64 -g -F dwarf program.asm -o program.o
ld -o program program.o

# Windows (PE format)
nasm -f win64 program.asm -o program.obj
link /subsystem:console program.obj kernel32.lib

# Raw binary (no ELF wrapper — pure shellcode)
nasm -f bin program.asm -o program.bin

# List file (show hex alongside source)
nasm -f elf64 -l program.lst program.asm -o program.o

# Check output
objdump -d -M intel program          # disassemble
objdump -d -M intel -j .text program # .text section only
hexdump -C program.bin               # hex dump raw binary
xxd program.bin                      # alternative hex dump

15.2 GDB — Debugging Assembly

# Launch
gdb ./program
gdb -q ./program     # quiet (no banner)

# ─── Breakpoints ───
(gdb) break _start          # break at entry
(gdb) break *0x08048060     # break at specific address
(gdb) break *main+20        # break at offset from label
(gdb) info breakpoints      # list all breakpoints
(gdb) delete 1              # delete breakpoint 1

# ─── Execution ───
(gdb) run                   # start execution
(gdb) run arg1 arg2         # run with arguments
(gdb) continue / c          # continue to next breakpoint
(gdb) ni / nexti            # next instruction (step OVER calls)
(gdb) si / stepi            # step instruction (step INTO calls)
(gdb) finish                # run until current function returns
(gdb) until *0x08048080     # run until address

# ─── Inspect Registers ───
(gdb) info registers        # all registers
(gdb) info registers eax    # specific register
(gdb) p/x $eax              # print EAX in hex
(gdb) p/d $eax              # print EAX in decimal
(gdb) p/t $eax              # print EAX in binary

# ─── Inspect Memory ───
(gdb) x/10xb $esp           # 10 hex bytes at ESP
(gdb) x/10xw $esp           # 10 hex words (4 bytes each)
(gdb) x/10xg $rsp           # 10 hex qwords (8 bytes each) [x64]
(gdb) x/20i $eip            # 20 instructions from EIP
(gdb) x/s $ebx              # print string at EBX
(gdb) x/s 0x804a000         # print string at address

# ─── Modify Values ───
(gdb) set $eax = 0          # set register
(gdb) set *(int*)0x804a000 = 0x90  # write to memory

# ─── PEDA / GEF / pwndbg (enhanced GDB plugins) ───
# Install pwndbg:
# git clone https://github.com/pwndbg/pwndbg && cd pwndbg && ./setup.sh
# Shows registers, stack, disassembly, and heap automatically on each break

15.3 pwndbg Display (Automatic Context)

# After each breakpoint hit, pwndbg shows:
# ─── REGISTERS ─────────────────────────────
# EAX  0x00000000
# EBX  0x00000000
# ESP  0xffffd000 ← esp
# EIP  0x08048060 ← eip
# ─── DISASM ────────────────────────────────
# 0x08048060  mov eax, 0x4    ← EIP
# 0x08048065  mov ebx, 0x1
# ─── STACK ─────────────────────────────────
# 00: 0xffffd000 ← esp
# 04: 0xffffd004

16. x86 vs x64 Differences

What it means: x86 and x64 are NOT interchangeable. The calling convention, syscall mechanism, register names, and stack alignment rules all differ. You must know which architecture your target uses and write appropriate code.


Feature x86 (32-bit) x64 (64-bit)
──────────────────────────────────────────────────────────────
Register width 32-bit (EAX, EBX...) 64-bit (RAX, RBX...)
General regs 8 (EAX-EDI) 16 (RAX-R15)
Syscall instruction int 0x80 syscall
Syscall number reg EAX RAX
Arg registers EBX,ECX,EDX,ESI,EDI,EBP RDI,RSI,RDX,R10,R8,R9
Return value EAX RAX
Calling conv (C) cdecl (args on stack) System V (args in regs)
Stack alignment 4 bytes 16 bytes (before CALL)
Pointer size 4 bytes 8 bytes
Max address space 4 GB 128 TB
PUSH/POP size 4 bytes 8 bytes
JMP/CALL target 32-bit offset 64-bit offset
PIC technique JMP/CALL/POP RIP-relative LEA


17. AT&T vs Intel Syntax

What it means: The same x86 assembly can be written in two syntaxes. GDB's default output and GCC's inline assembly use AT&T syntax. NASM and shellcode use Intel syntax. You'll encounter both — being able to read both fluently is essential for exploit dev.


Intel Syntax (NASM) AT&T Syntax (GAS/GDB default)
──────────────────────────────────────────────────────────────
mov eax, 1 movl $1, %eax
; registers have % prefix
; immediates have $ prefix
; instruction has size suffix (b/w/l/q)

mov eax, ebx movl %ebx, %eax
; note: src and dst REVERSED in AT&T!

mov eax, [ebx] movl (%ebx), %eax
; memory: square brackets → parentheses

mov eax, [ebx+4] movl 4(%ebx), %eax
; displacement BEFORE base reg

mov eax, [ebx+ecx*4] movl (%ebx,%ecx,4), %eax
; format: disp(base, index, scale)

mov eax, [ebx+ecx*4+8] movl 8(%ebx,%ecx,4), %eax

add eax, 5 addl $5, %eax

; Size suffixes in AT&T:
; b = byte (8-bit)
; w = word (16-bit)
; l = long/dword (32-bit)
; q = quad (64-bit)
; movb = move byte, movw = move word, movl = move dword, movq = move qword

; Switch GDB to Intel syntax:
(gdb) set disassembly-flavor intel


18. Practical Examples

What it means: The best way to solidify assembly knowledge is through complete working programs. These examples demonstrate real patterns used in shellcode, exploits, and system programming.


18.1 Count Down Loop (x86)

; Count from 5 to 0, printing each value
; Demonstrates loop, syscall, and number-to-char conversion

section .data
    nl db 0x0a          ; newline

section .text
global _start

_start:
    mov cl, 5           ; counter = 5

print_loop:
    ; Convert digit in CL to ASCII character
    mov al, cl
    add al, '0'         ; '0' = 0x30, so 5 + 0x30 = '5'
    push ecx            ; save counter (write syscall clobbers ECX)

    ; Print the digit
    push eax            ; push char onto stack to get its address
    mov ecx, esp        ; ECX = address of char on stack
    mov eax, 4          ; syscall: write
    mov ebx, 1          ; stdout
    mov edx, 1          ; 1 byte
    int 0x80
    pop eax             ; clean up char from stack

    ; Print newline
    mov eax, 4
    mov ebx, 1
    mov ecx, nl
    mov edx, 1
    int 0x80

    pop ecx             ; restore counter
    dec cl
    jnz print_loop      ; repeat while CL != 0

    ; exit(0)
    mov eax, 1
    xor ebx, ebx
    int 0x80

18.2 String Length Function (x64)

; strlen(str) — returns length of null-terminated string in RAX

section .data
    test_str db "Hello World", 0

section .text
global _start

; strlen: RDI = string pointer → RAX = length
strlen:
    xor rax, rax            ; length counter = 0
.loop:
    cmp byte [rdi + rax], 0 ; check if current byte is null
    je  .done               ; if null → done
    inc rax                 ; length++
    jmp .loop
.done:
    ret

_start:
    lea rdi, [rel test_str]
    call strlen
    ; RAX = 11 (length of "Hello World")

    ; exit
    mov rax, 60
    xor rdi, rdi
    syscall

18.3 Stack String Building (Shellcode Style)

; Build "/bin/sh\0" on the stack without a .data section
; Demonstrates the technique used in all x86 shellcode

section .text
global _start

_start:
    xor eax, eax        ; EAX = 0 (for null terminator)

    ; Push "/bin//sh" in reverse (8 bytes, no nulls)
    push eax            ; push 0 (null terminator for string)
    push 0x68732f2f     ; push "//sh" (0x68='h', 0x73='s', 0x2f='/', 0x2f='/')
    push 0x6e69622f     ; push "/bin" (0x6e='n', 0x69='i', 0x62='b', 0x2f='/')

    ; ESP now points to "/bin//sh\0" on the stack
    mov ebx, esp        ; EBX = pointer to string

    ; Verify by printing: write(1, ebx, 8)
    mov eax, 4
    mov ecx, ebx
    mov edx, 8
    int 0x80

    ; exit
    mov eax, 1
    xor ebx, ebx
    int 0x80

19. Cheat Sheet

Instruction Quick Reference

Instruction Operation Flags
──────────────────────────────────────────────────────────────────

mov dst, src dst = src none  
xor dst, src dst = dst ^ src ZF, SF, CF=0, OF=0  
add dst, src dst = dst + src CF, ZF, SF, OF, AF  
sub dst, src dst = dst - src CF, ZF, SF, OF, AF  
inc dst dst++ ZF, SF, OF (not CF!)  
dec dst dst-- ZF, SF, OF (not CF!)  
mul src EDX:EAX = EAX * src CF, OF  
div src EAX=quot, EDX=rem undefined  
and dst, src dst = dst & src ZF, SF, CF=0, OF=0  
or dst, src dst = dst | src ZF, SF, CF=0, OF=0  
not dst dst = ~dst none  
neg dst dst = 0 - dst CF, ZF, SF, OF  
shl dst, n dst = dst << n CF, ZF, SF  
shr dst, n dst = dst >> n CF, ZF, SF  
cmp a, b sets flags for a-b CF, ZF, SF, OF  
test a, b sets flags for a&b ZF, SF, CF=0, OF=0  
push src ESP-=4; [ESP]=src none  
pop dst dst=[ESP]; ESP+=4 none  
call addr push EIP; jmp addr none  
ret EIP=[ESP]; ESP+=4 none  
lea dst, [mem] dst = effective address none

Zero a Register (No Null Bytes)

xor eax, eax    ; 2 bytes: 31 c0  ← BEST
xor rax, rax    ; 3 bytes: 48 31 c0
sub eax, eax    ; 2 bytes: 29 c0
mul edx         ; 2 bytes: F7 E2  (also zeroes EDX — if EDX=0)

Calling Conventions Summary

Convention Who cleans Args passed Return Used by
──────────────────────────────────────────────────────────────────
cdecl(x86) Caller Stack (R to L) EAX C (Linux/Win)
stdcall Callee Stack (R to L) EAX WinAPI 32-bit
fastcall Callee ECX,EDX then stack EAX MSVC
SysV x64 Caller RDI,RSI,RDX,RCX, RAX Linux x64
R8,R9, then stack
MS x64 Caller RCX,RDX,R8,R9, RAX Windows x64
then stack
Linux x86 — EBX,ECX,EDX, EAX Syscalls
syscall ESI,EDI,EBP
Linux x64 — RDI,RSI,RDX, RAX Syscalls
syscall R10,R8,R9

NASM Compile One-Liners

# x86 ELF — assemble + link
nasm -f elf32 file.asm -o file.o && ld -m elf_i386 -o file file.o && ./file

# x64 ELF — assemble + link
nasm -f elf64 file.asm -o file.o && ld -o file file.o && ./file

# Extract shellcode bytes (inline)
objdump -d ./file | grep "^\s" | cut -f2 | \
  perl -pe 's/(\w{2})\s*/\\x$1/g' | tr -d '\n' && echo

# Check for null bytes
objdump -d ./file | grep -P "\s00\s"

# Disassemble raw bytes
echo -ne "\x31\xc0\x50\xb0\x0b\xcd\x80" | ndisasm -b 32 -

# GDB with Intel syntax auto-set
echo "set disassembly-flavor intel" >> ~/.gdbinit

← Binary top ↑