Artigo :: Usando syscalls do Linux em Assembly
Introdução
Este tutorial prático ensina como escrever programas assembly que invocam chamadas de sistema do Linux diretamente, ignorando a biblioteca C e interagindo com o kernel no nível mais baixo.
Visão Geral
Quando um programa precisa interagir com o sistema operacional; para escrever em um arquivo, alocar memória ou finalizar graciosamente; ele faz uma chamada de sistema (syscall). A maioria dos programadores usa essas chamadas indiretamente através de wrappers da biblioteca C, mas entender como invocar syscalls diretamente do assembly te dá uma visão profunda de como programas realmente funcionam.
Neste tutorial, vamos construir um programa Linux x64 simples que imprime “Hack The Planet” na tela usando syscalls puras, sem nenhuma dependência da biblioteca C.
Pré-requisitos
Antes de começar, você deve ter:
- Conhecimento básico de linguagem assembly (sintaxe NASM)
- Compreensão de registradores x64 (RAX, RDI, RSI, etc.)
- Assembler NASM e linker ld instalados
- Familiaridade com programação C ajuda mas não é obrigatória
sudo apt install nasm
No Fedora/RHEL: sudo yum install nasmObjetivo
Vamos construir um binário Linux ELF64 que usa syscalls básicas para imprimir uma mensagem na tela. No caminho, vamos aprender:
- Como encontrar números de syscalls
- Como configurar registradores para invocação de syscalls
- Por que programas sofrem segfault sem tratamento adequado de saída
Encontrando Números de Syscalls
Cada syscall no Linux tem um número único. Para x64, estes são definidos em um arquivo header. Vamos encontrar a syscall write:
| |
A syscall básica write é o número 1. Agora vamos consultar o manual para entender seus argumentos:
| |

Como podemos ver, a syscall write tem três argumentos:
*ssize_t write(int fd, const void buf, size_t count);
- fd - Descritor de arquivo (0=stdin, 1=stdout, 2=stderr)
- buf - Ponteiro para os dados a escrever
- count - Número de bytes a escrever
Convenções de Registradores para Syscalls
Lendo o manual de syscalls, aprendemos como configurar os registradores x64 para invocar syscalls:

Mapeamento de registradores para syscalls x64:
- RAX - Número da syscall
- RDI - Primeiro argumento
- RSI - Segundo argumento
- RDX - Terceiro argumento
- R10 - Quarto argumento
- R8 - Quinto argumento
- R9 - Sexto argumento
Escrevendo o Código Assembly
Para nosso programa, vamos:
- Usar descritor de arquivo 1 (stdout) para imprimir na tela
- Apontar RSI para nossa string de mensagem “Hack The Planet”
- Definir RDX como 16 (o comprimento da nossa mensagem incluindo newline)
| |
equ $-msg calcula o comprimento subtraindo o endereço inicial de msg da posição atual $. Isso nos dá automaticamente o tamanho da string.Compilando o Código
Vamos compilar e linkar este código assembly:
| |
Flags explicadas:
-felf64- Formato de saída é ELF 64-bit (Executable and Linkable Format)ld- Linka o arquivo objeto em um binário executável
Quando rodamos nosso programa com ./syscall-001.bin, vemos que nossa mensagem “Hack The Planet” é impressa, mas recebemos um erro Segmentation Fault:

Adicionando a Syscall Exit
Vamos encontrar o número da syscall exit:
| |
A syscall exit é o número 60. Vamos consultar sua página de manual:
| |

Existe apenas um argumento: int status (o código de saída, como 0 para sucesso ou diferente de zero para erros).
| |
Vamos adicionar a syscall exit ao nosso programa. Usaremos status de saída 1 para teste:
| |
Agora compile novamente:
| |
Após executar ./syscall-001.bin, podemos verificar que o status de saída é 1 como projetado:

Perfeito! Sem segfault. O programa sai limpo.
Analisando com strace
Se usarmos strace para analisar este binário ELF64, veremos detalhes interessantes. Quando executamos o binário, o shell usa execve com nosso binário como argumento junto com variáveis de ambiente:


Fluxo de Execução de Syscalls
Veja como as syscalls funcionam nos bastidores:


O processo:
- Programa do usuário configura registradores (RAX para número da syscall, RDI/RSI/RDX para argumentos)
- Instrução
syscalldispara uma troca de contexto para modo kernel - Kernel valida argumentos e executa a operação solicitada
- Kernel retorna controle ao userland com resultado em RAX
- Programa continua a execução
Pontos-Chave
- Syscalls são a interface entre programas em userland e o kernel
- Números de syscalls x64 são definidos em
/usr/include/x86_64-linux-gnu/asm/unistd_64.h - Convenção de registradores: RAX=número da syscall, RDI/RSI/RDX/R10/R8/R9 para argumentos
- Sempre chame exit para terminar programas graciosamente (syscall 60 no x64)
- strace é essencial para entender e depurar comportamento de syscalls
- Não precisa de biblioteca C - você pode interagir diretamente com o kernel
Exercícios Práticos
- Modifique o programa para escrever em stderr (descritor de arquivo 2) em vez de stdout
- Crie um programa que use a syscall
read(número 0) para obter entrada do usuário - Escreva um programa que abre um arquivo usando a syscall
open(número 2) - Adicione verificação de erros examinando o valor de retorno em RAX após cada syscall
- Use strace em utilitários do sistema como
lse identifique as syscalls que eles fazem
Leitura Adicional
- Depurando com strace - Aprenda a rastrear e depurar syscalls
- Começando com GDB - Depure seus programas assembly
- Cheat Sheet de Compilação GCC - Referência de compilação e linkagem
- Referência de Syscalls do Linux - Documentação completa de syscalls
- Documentação ABI x64 - Convenções de chamada x86-64 oficiais