Conjunto de instruções (ISA)
A ISA (Instruction Set Architecture) é o contrato entre software e hardware: define quais operações a CPU entende, como os operandos são especificados e quais flags cada instrução afeta.
O vocabulário da CPU
Uma linguagem de programação tem palavras-chave como if, for,
return. A CPU tem sua própria "linguagem" — o conjunto de instruções (ISA).
Em x86-64, existem centenas de instruções, mas você pode ler 90% do assembly do mundo
com apenas ~20 delas: MOV, ADD, SUB,
CMP, JMP, JE, CALL, RET,
PUSH, POP.
Cada instrução é codificada em bytes. A CPU lê esses bytes da memória, decodifica (descobre o que fazer), executa e escreve o resultado. O conjunto de instruções define exatamente essa codificação — por isso software compilado para x86 não roda em ARM sem recompilação ou emulação.
REP MOVS (copia blocos de memória). ARM/RISC-V são RISC — instruções de 32 bits
fixos, operações simples, mais registradores. Internamente, CPUs x86 modernas traduzem
instruções CISC em micro-operações RISC para execução no pipeline.
Explorador do conjunto de instruções
Selecione uma categoria e depois uma instrução para ver sua sintaxe, descrição, exemplo e quais flags ela afeta.
MOV dst, srcCopia valor de src para dst. O operando de origem não é alterado.
MOV EAX, EBX ; EAX ← EBX MOV EAX, 42 ; EAX ← 42 MOV [EBX], EAX ; memória[EBX] ← EAX
Codificação binária das instruções
// Como uma instrução x86 é codificada em bytes // (formato legado de comprimento variável: 1–15 bytes) // MOV EAX, 42 → B8 2A 00 00 00 (5 bytes) // B8 = opcode de MOV com destino EAX // 2A 00 00 00 = imediato 42 em little-endian // ADD EAX, EBX → 01 D8 (2 bytes) // 01 = opcode ADD r/m32, r32 // D8 = ModRM: mod=11 (reg), reg=EBX (011), r/m=EAX (000) // JMP rel32 (salto relativo) → E9 XX XX XX XX (5 bytes) // E9 = opcode JMP near // XX = deslocamento de 32 bits relativo ao endereço após a instrução // Por que comprimento variável? // x86 nasceu em 1978 com 8 bits de opcode. // Extensões (80386, MMX, SSE, AVX) adicionaram prefixos: // REX (64-bit), VEX (AVX), EVEX (AVX-512). // ARM usa instrução de 32 bits fixo — mais simples de decodificar!
Padrões idiomáticos — assembly que todo dev reconhece
// Padrões idiomáticos de assembly — aprenda a reconhecê-los // Zerar registrador (menor e mais rápido que MOV EAX, 0) xor eax, eax // EAX = 0, ZF=1 // Verificar se EAX é zero (sem alterar EAX) test eax, eax // flags = EAX & EAX; não muda EAX jz is_zero // Multiplicar por potência de 2 (mais rápido que IMUL) shl eax, 3 // EAX *= 8 // Swap sem variável temporária (usando XOR) xor eax, ebx xor ebx, eax xor eax, ebx // EAX e EBX trocados // Calcular índice em array de structs (tamanho 12 bytes) imul eax, ecx, 12 // EAX = ECX * 12 (índice * tamanho) add eax, [base] // EAX = endereço do elemento
xor eax, eax porque
(1) processadores modernos reconhecem o padrão e o tratam como idiom de zeragem,
não criando dependência de dados em EAX, e (2) é 3 bytes menor no código gerado.
Lendo assembly gerado pelo compilador
Mini projeto: escreva uma função simples em C (if/else, loop for, chamada de função) e gere o assembly com gcc -O0 -S -o out.s prog.c. Identifique cada instrução usando a referência do explorador acima. Mapeie cada linha de C para as instruções correspondentes.
Projeto principal: implemente um mini "disassembler" em Python: dado um array de bytes de instruções simples (MOV reg, imm32: B8–BF seguido de 4 bytes), imprima o mnemônico e o valor. Tabela de opcodes para começar: 0xB8=MOV EAX, 0xB9=MOV ECX, 0xC3=RET, 0x90=NOP, 0xEB=JMP short.
Desafio extra: escreva assembly diretamente em arquivo .s e compile com gcc -nostdlib. Implemente uma função que calcule o comprimento de uma string (strlen) usando REP SCAS: mov edi, ptr / xor al, al / repne scasb / not ecx / lea eax, [ecx-1]. Chame-a de C e compare a velocidade com a versão de loop simples.
Teste sua intuição
Onde você encontra isso
Godbolt Compiler Explorer
godbolt.org mostra em tempo real o assembly gerado pelo GCC, Clang, MSVC para código C/C++/Rust/Go. Essencial para entender o que o compilador faz com suas otimizações. Permite comparar x86-64, ARM64, RISC-V, WebAssembly lado a lado para o mesmo código.
Antivírus e sandboxing
Antivírus modernos dissasemblam código suspeito para detectar padrões maliciosos sem executá-lo (análise estática). Sandboxes como Cuckoo executam o código em ambiente isolado e monitoram syscalls — que em x86-64 são feitas com a instrução syscall (Linux) ou int 0x2e / sysenter (Windows).
Cheat engines e mods de jogos
Ferramentas como Cheat Engine lêem a memória de um processo em execução e permitem alterar valores (vidas, dinheiro). Internamente, fazem injection de código assembly (code caves): encontram espaço livre no executável, escrevem bytes de instrução diretamente e redirecionam a execução com um JMP de 5 bytes.