Skip to content

Assembly Addressing Modes — Memory Access Patterns

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you will learn about Assembly Addressing Modes. We cover key concepts, practical examples, and best practices to help you master this topic.

Assembly addressing modes define how CPU instructions access memory through register indirect, indexed, scaled, and RIP-relative addressing for flexible data access patterns.

What You'll Learn

  • x86-64 addressing modes
  • Register indirect addressing
  • Indexed and scaled indexing
  • RIP-relative addressing

Why It Matters

Understanding addressing modes is essential for array traversal, struct access, and reverse engineering. Durga Antivirus Pro uses advanced addressing modes in its memory scanning engine to efficiently search for malware signatures.

Real-World Use

Array iteration, struct field access, stack frame navigation, pointer chasing, and any code that accesses data structures at computed addresses.

flowchart LR
    A["Addressing Modes"] --> B["Direct"]
    A --> C["Register Indirect"]
    A --> D["Indexed"]
    A --> E["RIP-Relative"]
    B --> F["[address]"]
    C --> G["[reg]"]
    D --> H["[reg + offset]"]
    style A fill:#2563eb,stroke:#2563eb,color:#fff
    style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style E fill:#dbeafe,stroke:#2563eb,color:#1e40af

Direct Addressing

; Direct: address is a fixed constant
mov rax, [0x601020]    ; load from absolute address
mov [0x601020], rbx    ; store to absolute address

; Usually with a label
section .data
    msg db "Hello", 0

section .text
    mov rax, [msg]     ; load from msg address

Register Indirect

; Register contains the address
mov rax, rbx           ; rax = rbx (value, not memory)
mov rax, [rbx]         ; rax = *rbx (load from address in rbx)
mov [rbx], rax         ; *rbx = rax (store to address in rbx)

; Example
mov rbx, msg           ; rbx = address of msg
mov al, [rbx]          ; al = first byte of msg ('H')

Base + Displacement

; [base + displacement]
mov rax, [rbp + 16]     ; load from rbp+16 (stack parameter)
mov [rbp - 8], rax      ; store at rbp-8 (local variable)

; Accessing struct fields
; struct { int x; int y; int z; };
mov eax, [rdi]          ; struct.x (offset 0)
mov eax, [rdi + 4]      ; struct.y (offset 4)
mov eax, [rdi + 8]      ; struct.z (offset 8)

Indexed Addressing

; [base + index * scale]
; scale can be 1, 2, 4, or 8

; Array of 8-byte elements
mov rcx, 0              ; index
loop_start:
    mov rax, [array + rcx * 8]  ; array[index]
    ; process rax...
    inc rcx
    cmp rcx, 10
    jl loop_start

; With displacement
mov rax, [rbx + rcx * 4 + 16]  ; base + index*4 + displ

Scaled Index Example

section .data
    array dq 10, 20, 30, 40, 50

section .text
    global _start

_start:
    mov rsi, 0          ; index

print_loop:
    mov rax, [array + rsi * 8]  ; load array element
    ; Print rax via syscall (simplified)
    inc rsi
    cmp rsi, 5
    jl print_loop

    mov rax, 60         ; exit
    xor rdi, rdi
    syscall

RIP-Relative Addressing

; Modern x86-64 default: [rip + displacement]
; Used for position-independent code

; NASM syntax
lea rax, [rel msg]      ; load effective address RIP-relative
mov rax, [rel global_var] ; load global variable

section .data
    global_var dq 42
    msg db "Hello", 0

RIP-relative addressing is the default in 64-bit mode for most instructions.

LEA — Load Effective Address

; LEA computes the address without accessing memory
lea rax, [rbx + rcx * 4]  ; rax = rbx + rcx * 4
lea rax, [array + rsi * 8] ; rax = address of array[rsi]

; Useful for arithmetic
lea rdx, [rax + rax * 2]   ; rdx = rax * 3
lea rdx, [rax * 8]         ; rdx = rax * 8

Addressing Mode Summary

Mode Syntax Example
Direct [addr] mov rax, [0x601020]
Register [rax] mov rbx, [rax]
Base+disp [rax + 8] mov rcx, [rax + 8]
Index+scale [rax + rbx*4] mov rdx, [rax + rbx*4]
Scaled index [rax + rbx*4 + 8] mov r8, [rax + rbx*4 + 8]
RIP-relative [rel label] mov r9, [rel msg]

Common Mistakes

1. Using wrong scale factor

Scale must be 1, 2, 4, or 8. A scale of 3 for a 24-byte struct requires manual multiplication.

2. Forgetting brackets for memory access

mov rax, rbx copies value. mov rax, [rbx] loads from memory. Missing brackets is the most common bug.

3. Mixing register sizes in addressing

mov rax, [eax] is invalid in 64-bit mode. Use 64-bit registers for addresses.

4. RIP-relative in 32-bit code

RIP-relative addressing is only available in 64-bit mode. 32-bit code uses absolute addresses.

5. LEA vs MOV confusion

lea rax, [rbx] loads address. mov rax, [rbx] loads value. Both have different uses.

Practice Questions

1. What does mov rax, [rbx + rcx * 8] do?

Loads an 8-byte value from address rbx + rcx * 8 into rax. Used for array access.

2. What is the difference between LEA and MOV with brackets?

LEA computes the address without accessing memory. MOV with brackets dereferences the address.

3. Why is RIP-relative addressing used in 64-bit code?

It enables position-independent code that works regardless of where the code is loaded in memory.

4. What scale factors are valid in x86-64 addressing?

1, 2, 4, and 8, corresponding to byte, word, dword, and qword element sizes.

Challenge: Write assembly that sums an array of 8 integers using indexed addressing.

Solution
section .data
    array dq 10, 20, 30, 40, 50, 60, 70, 80

section .text
    global _start

_start:
    xor rax, rax
    xor rcx, rcx

sum_loop:
    add rax, [array + rcx * 8]
    inc rcx
    cmp rcx, 8
    jl sum_loop

    ; rax = 360 (sum)
    mov rdi, rax
    mov rax, 60
    syscall

FAQ

{{< faq question="What is the maximum displacement in addressing?" >}} In 64-bit mode, displacement is a signed 32-bit value, allowing access within +/- 2GB of the base address. {{< /faq >}}

{{< faq question="Can I use two index registers?" >}} No, x86-64 addressing mode has at most one base register, one index register with scale, and a displacement. {{< /faq >}}

{{< faq question="Does NASM support [rel] by default?" >}} In 64-bit mode, NASM defaults to RIP-relative for labels. Use default rel or [rel label] explicitly. {{< /faq >}}

{{< faq question="What happens if the address computation overflows?" >}} In 64-bit mode, addresses are truncated to 64 bits (canonical form). Overflow wraps within the 64-bit address space. {{< /faq >}}

{{< faq question="Can I use addressing modes with stack operations?" >}} Yes. push [rax] and pop [rax] work with memory operands, but are less common. {{< /faq >}}

Mini Project

Write a program that copies an array using scaled indexed addressing.

section .data
    src dq 1, 2, 3, 4, 5
    len equ 5

section .bss
    dst resq 5

section .text
    global _start

_start:
    xor rcx, rcx
copy_loop:
    mov rax, [src + rcx * 8]
    mov [dst + rcx * 8], rax
    inc rcx
    cmp rcx, len
    jl copy_loop

    mov rax, 60
    xor rdi, rdi
    syscall

What's Next

Now that you understand addressing modes, proceed to procedures and function calls.

Topic Description Link
Procedures Function calls and stack {{< ref "10-procedures" >}}
Instructions CPU instruction set {{< ref "06-instructions" >}}
Memory Memory organization {{< ref "04-memory" >}}

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro