Atualizado: 18/10/2025

Este conteúdo é original e não foi gerado por inteligência artificial.

Endereçamento Indireto em Assembly NASM

O endereçamento indireto é utilizado para acessar dados armazenados em um endereço específico da memória. O NASM permite especificar endereços com base em expressões e cálculos, o que oferece grande flexibilidade na manipulação de dados.

Uma variável em Assembly representa seu endereço na memória, e não o valor armazenado nela. Para acessar o conteúdo desse endereço, o nome da variável deve ser colocado entre colchetes:

[number]   ; valor da variável number

A seguir, um exemplo em Linux que carrega o endereço de uma variável e obtém o valor armazenado nela:

global _start

section .data
num dq 12

section .text
_start:
    mov rax, [num]      ; carrega o valor de num em RAX
    add rax, 3
    mov [num], rax      ; grava o novo valor de RAX em num
    mov rdi, [num]      ; RDI = 15
    mov rax, 60
    syscall

Primeiro, o valor 12 é carregado em RAX, incrementado em 3 e armazenado de volta na variável num. O registrador RDI recebe então o novo valor (15).

Em Windows, a lógica é idêntica, mas o acesso à variável deve ser feito com o operador rel:

global _start

section .data
num dq 12

section .text
_start:
    mov rdi, [rel num]
    add rdi, 3
    mov [rel num], rdi
    mov rax, [rel num]
    ret

O acesso pelo nome da variável é apenas um caso particular de endereçamento em NASM. A seguir, veremos todos os modos de endereçamento disponíveis.


Modos de Endereçamento no NASM

O acesso a um endereço em NASM segue a forma geral:

[base + (index * scale) + offset]

Componentes da fórmula

  • base: registrador base, que contém um endereço (qualquer registrador geral de 32 ou 64 bits, ou RSP).
  • index: registrador de índice, que contém um deslocamento relativo ao endereço base (também pode ser um registrador geral de 32 ou 64 bits).
  • scale: fator de multiplicação aplicado ao índice. Pode ser 1, 2, 4 ou 8.
  • offset: valor de deslocamento, que pode ser um número (constante de 32 bits) ou o nome de uma variável.

Nem todos os componentes precisam ser usados simultaneamente. Alguns exemplos:

Forma de EndereçamentoExemploDescrição
[base][rdx]Apenas o registrador base
[offset][0F3h] ou [variavel]Endereço absoluto ou variável
[base + offset][rcx + 33h]Base + deslocamento
[base + index][rax + ecx]Base + índice
[index * scale][rbx * 4]Índice multiplicado
[index * scale + offset][rbx * 4 + 65]Índice multiplicado com deslocamento
[base + index * scale][rsp + rbx * 4]Base + índice multiplicado
[base + index * scale + offset][rsp + rbx * 4 + 65]Base + índice multiplicado + deslocamento

Deslocamentos

O acesso direto a uma variável, como [num], é um exemplo de endereçamento por deslocamento, em que o deslocamento é o endereço da variável.

mov rax, [num]

Podemos somar um deslocamento numérico:

global _start

section .data
nums dq 12, 13, 14, 15, 16

section .text
_start:
    mov rdi, [nums + 16]    ; RDI = 14
    mov rax, 60
    syscall

Aqui, [nums + 16] representa o endereço 16 bytes após o início de nums. Como cada elemento ocupa 8 bytes, esse endereço corresponde ao terceiro elemento, ou seja, 14.

Deslocamentos também podem ser negativos:

global _start

section .data
number dq 33
nums dq 12, 13, 14, 15, 16

section .text
_start:
    mov rdi, [nums - 8]     ; RDI = 33
    mov rax, 60
    syscall

Nesse caso, [nums - 8] acessa o valor armazenado 8 bytes antes do início de nums, isto é, a variável number.

O deslocamento máximo permitido é de 32 bits.


Registrador Base

Um registrador base armazena um endereço. Podemos carregar o endereço de uma variável e acessá-la indiretamente.

Exemplo (Linux):

global _start

section .data
nums dq 12, 13, 14, 15, 16

section .text
_start:
    mov rbx, nums       ; RBX = endereço de nums
    mov rdi, [rbx]      ; RDI = 12
    mov rax, 60
    syscall

No Windows, o tipo de dado deve ser explicitado:

global _start

section .data
nums dq 12, 13, 14, 15, 16

section .text
_start:
    mov rbx, nums
    mov rax, qword [rbx]
    ret

Registrador Base com Deslocamento

É possível somar deslocamentos ao endereço armazenado no registrador base:

global _start

section .data
nums dq 12, 13, 14, 15, 16

section .text
_start:
    mov rbx, nums
    mov rdi, [rbx + 8]   ; RDI = 13
    mov rax, 60
    syscall

Também é possível somar valores de registradores:

global _start

section .data
nums dq 12, 13, 14, 15, 16

section .text
_start:
    mov rbx, 16
    mov rdi, [nums + rbx]   ; RDI = 14
    mov rax, 60
    syscall

O resultado é o mesmo: o terceiro elemento (14) é acessado. Também é permitido somar um valor adicional:

mov rdi, [nums + rbx + 8]   ; RDI = 15

⚠️ Em Windows, quando há uma variável no deslocamento, é obrigatório o uso de rel:

mov rdi, [rel nums]

Entretanto, não é permitido combinar rel com um registrador base:

mov rax, qword [rel nums + rbx]   ; Inválido

Se isso for necessário, deve-se carregar o endereço em um registrador base antes, ou ajustar os parâmetros do vinculador:

  • ld (GCC): --default-image-base-low
  • link.exe (Microsoft): /LARGEADDRESSAWARE:NO

Registrador de Índice

O registrador de índice especifica um elemento dentro de uma estrutura de dados, como um array.

Exemplo (Linux):

global _start

section .data
nums db 12, 13, 14, 15, 16

section .text
_start:
    mov rbx, nums
    mov rsi, 2
    movzx rdi, byte [rbx + rsi]   ; RDI = 14
    mov rax, 60
    syscall

O registrador RBX contém o endereço base e RSI contém o índice do elemento. [rbx + rsi] soma o índice ao endereço base. Como cada elemento ocupa 1 byte, rsi = 2 acessa o terceiro elemento (14).

Em Windows:

global _start

section .data
nums db 12, 13, 14, 15, 16

section .text
_start:
    mov rbx, nums
    mov rsi, 2
    movzx rax, byte [rbx + rsi]
    ret

Para tipos maiores, o índice deve ser multiplicado pelo tamanho de cada elemento, usando o fator de escala (scale):

global _start

section .data
nums dq 112, 113, 114, 115, 116

section .text
_start:
    mov rbx, nums
    mov rsi, 2
    mov rdi, qword [rbx + rsi * 8]   ; RDI = 114
    mov rax, 60
    syscall

Também é possível incluir um deslocamento adicional:

mov rdi, qword [rbx + rsi * 8 + 16]   ; RDI = 116

Ou somar o índice diretamente à variável:

mov rsi, 1
mov rdi, qword [nums + rsi * 8]   ; RDI = 113

Instrução LEA

A instrução LEA (Load Effective Address) é usada para carregar um endereço em um registrador. Ela permite calcular endereços com a mesma fórmula [base + index * scale + offset].

Exemplo (Linux):

global _start

section .data
nums dq 112, 113, 114, 115, 116

section .text
_start:
    lea rbx, [nums]
    mov rdi, qword [rbx]   ; RDI = 112
    mov rax, 60
    syscall

Em Windows:

global _start

section .data
nums dq 112, 113, 114, 115, 116

section .text
_start:
    lea rbx, [rel nums]
    mov rax, qword [rbx]
    ret

Além de carregar endereços de variáveis, LEA pode ser usada para cálculos aritméticos:

global _start

section .text
_start:
    mov rdi, 8
    mov rcx, 5
    lea rdi, [rdi + rcx * 2]   ; RDI = 8 + 5*2 = 18
    mov rax, 60
    syscall

Resumo

  • O nome de uma variável representa seu endereço.
  • Colchetes [ ] acessam o valor armazenado no endereço.
  • A forma geral de endereçamento é [base + index * scale + offset].
  • base: registrador base; index: registrador de índice; scale: fator de multiplicação; offset: deslocamento.
  • rel: acesso relativo ao registrador RIP (necessário no Windows 64 bits).
  • LEA: carrega endereços e realiza cálculos de forma eficiente.
Política de Privacidade

Copyright © www.programicio.com Todos os direitos reservados

É proibida a reprodução do conteúdo desta página sem autorização prévia do autor.

Contato: programicio@gmail.com