Assembly Addressing Modes — Memory Access Patterns
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