A memória

Quando o programa é chamado, as sessões são mapeadas para o segmento do processo, e os segmentos são carregados na memória como descritos pelo arquivo ELF.

.text.data.bssHeapempty spaceStack

.text

A secção .text contem a instrução em Assembly do programa. Essa área costuma ser read-only para prevenir que o processo acidentalmente modifique as instruções. Qualquer tentativa de sobrescrever essas instruções vai, inevitavelmente, resultar em segmentation fault.

.data

O secção .data contém variáveis globais e estáticas que estão explicitamente inicializadas com o programa.

.bss

Diversos compiladores e linkers usam a secção .bss como parte do segmento .data, que contém variáveis alocadas estaticamente representadas exclusivamente por bits 0.

Heap

A memória heap é armazenada nessa área. Ela termina logo após o .bss e chega até o endereço mais alto.

Stack

A memória stack é uma estrutura de Last-In-First-Out onde são armazenados endereços de retorno, parâmetros, e, dependendo das opções do compilador, frame de ponteiros são armazenados. Variáveis locais de C e C++ são armazenadas aqui, e você até pode copiar código para a stack. A stack é definida na memória RAM. O linker normalmente coloca a stack no ponto mais baixo abaixo das variáveis globais e estáticas. O conteúdo é acessado através de um ponteiro, definido como a parte mais acida da pilha durante a inicialização. Durante a execução, a parte alocada cresce para baixo até o menor endereço de memória.

Memórias modernas possuem proteção (como DEP/ASLR) que previnem que danos sejam causados por Buffer overflow. DEP (Data Execution Prevention), marca regiões como “read-only”. As áreas marcadas como read-only são regiões onde algumas entradas do usuário são armazenadas (exemplo: o stack), então, a ideia por trás do DEP é prevenir que usuários consigam enviar códigos shell e mudando o ponteiro da memória para o código shell, fazendo com que o programa execute o código.

Ponto mais baixo vs Ponto mais alto

Quando estamos dizendo a respeito da memória da aplicação (memória RAM), dizemos que o ponto mais baixo é o maior endereço de memória, nesse caso, poderia ser um 0xFFFFFFFF, e a memória mais alta é o menor endereço de memória, que pode ser 0x00000000

Registradores

Registradores são componentes essenciais numa CPU. Quase todo registrador oferece uma pequena quantidade de espaço quando uma informação é temporariamente armazenada. Contudo, alguns deles possuem uma funcionalidade especifica.

Esses registradores são divididos em Registradores gerais, Registradores de controle e Registradores de segmento. Os registradores mais críticos são os registradores gerais. Nesses, existe uma subdivisão ainda maior entre Registradores de Informação, Registradores de ponteiro e Registradores de indexadores.

Registradores de dados

32-bit Register64-bit RegisterDescriçãoOutras informações
EAXRAXAcumulador usado para operações aritméticas e input/output
EBXRBXBase é utilizada para indexar o endereçamentoExtended BX: Registrador de proposito geral. (?)
ECXRCXContador é usado para rotacionar instruções e contar loops
EDXRDXData é usado para I/O e operações de multiplicação e divisão com grandes números

Registradores de ponteiro

32-bit Register64-bit RegisterDescrição
EIPRIPPonteiro de Instruções armazenam o desvio de endereço para a próxima instrução a ser executada
ESPRSPPonteiro de Stack aponta para o topo da Stack
EBPRBPPonteiro da Base aponta para a base da Stack

Intel vs MT&T

Existem duas sintaxes diferentes para a linguagem Assembly, uma delas é a Intel, e a outra é a MT&T. Existem algumas diferenças entre elas, uma delas, é a distinção entre a instrução mov.

Intel

InstruçãoDestinoFonte
movebpesp

AT&T

InstruçãoFonteDestino
mov%esp%ebp

Stack Frames

Os stacks frames são divisões lógicas na memória para são reservadas para funções e partes especificas do código. Um stack frame é definido com um começo (EBP) e um fim (ESP), que é colocado na bateria assim que a função é chamada.

Desde que a memória stack seja construída numa estrutura de dados LIFO (parecida uma pilha), o primeiro passo é armazenar o EBP anterior (o ponteiro da base anterior) na pilha, que depois pode ser restaurada quando a função terminar.

Cuidado

Perceba-se que eu usei o termo armazenar, o que significa que somente o valor daquele ponteiro está sendo armazenado na pilha, mas o ponteiro não está sendo alterado nessa etapa.

0x0000054d <+0>:	push   ebp       # <---- 1. Stores previous EBP
0x0000054e <+1>:	mov    ebp,esp
0x00000550 <+3>:	push   ebx
0x00000551 <+4>:	sub    esp,0x404
<...SNIP...>
0x00000580 <+51>:	leave  
0x00000581 <+52>:	ret 

Depois, a base EBP é movida para o topo do stack Portanto, a pilha foi disso:

0x00000000
0x00000001
0x00000002
0x00000003
0x00000004ESP
0x00000005<main stack frame>
0x00000006argv
0x00000007argc
0x00000008EBP
Para isso:
0x00000000
0x00000001
0x00000002EBX
0x000000030x0000008
0x00000004ESP,EBP
0x00000005<main stack frame>
0x00000006argv
0x00000007argc
0x00000008
E depois, move-se o ESP, relativo ao topo da stack, para a diferença entre os tamanhos das variáveis, para deixar um espaço para as variáveis locais da função em específico
0x0000054d <+0>:	push   ebp       # <---- 1. Stores previous EBP
0x0000054e <+1>:	mov    ebp,esp   # <---- 2. Creates new Stack Frame
0x00000550 <+3>:	push   ebx
0x00000551 <+4>:	sub    esp,0x404 # <---- 3. Moves ESP to the top
<...SNIP...>
0x00000580 <+51>:	leave  
0x00000581 <+52>:	ret
0x00000000ESP
0x00000001<func stack frame>
0x00000002EBX
0x000000030x0000008
0x00000004EBP
0x00000005<main stack frame>
0x00000006argv
0x00000007argc
0x00000008

Para sair do stack frame, o contrário é feito, o epilogo. Durante o epilogo, o ESP é trocado com o EBP atual, e seu valor reseta para o valor que tinha anteriormente antes do prologo. O Epilogo é relativamente pequeno, e existem outras maneiras de fazê-lo. No nosso exemplo, é feito com duas funções:

0x0000054d <+0>:	    push   ebp       
0x0000054e <+1>:	    mov    ebp,esp   
0x00000550 <+3>:	    push   ebx
0x00000551 <+4>:	    sub    esp,0x404 
<...SNIP...>
0x00000580 <+51>:	leave  # <----------------------
0x00000581 <+52>:	ret    # <--- Leave stack frame

Registradores de indexação (Index registers)

Register 32-bitRegister 64-bitDescrição
ESIRSISource Index é usado como ponteiro entre operações de string
EDIRDIDestination é usado como ponteiro para operações de string

Veja também