Triage de firmware e fluxo de engenharia reversa
TL;DR
- Você vai aprender um fluxo prático para sair de um firmware desconhecido (
.bin,.img,.fw, às vezes.elf) para um entendimento estruturado:- identificar tipo de arquivo e arquitetura,
- localizar limites entre código/dados,
- recuperar endereços de carga e entry points,
- e escolher as próximas ferramentas certas (disassembly, decompilação, emulação ou tracing no QEMU).
- Vai praticar etapas de triagem repetíveis que funcionam bem para alvos embarcados (incluindo RISC-V).
1. Tipos de arquivos de firmware: o que você pode receber
Formatos comuns
- ELF: melhor cenário (símbolos, seções, entry point podem existir)
- Binário bruto (
.bin): bytes planos, sem endereços - Imagens contêiner: podem embutir sistema de arquivos ou múltiplas partições (ex.: bundles de update)
- Blobs comprimidos: LZMA, gzip etc.
Uma realidade central
Um binário bruto não informa:
- onde ele carrega na memória
- onde a execução começa
- qual é a arquitetura
Você precisa inferir isso pelo contexto.
2. Checklist de triagem (faça sempre)
Passo 1: Identifique o tipo de arquivo
| |
Se for ELF, você está em situação muito mais fácil.
Passo 2: Entropia/estrutura rápida
| |
Procure por:
- strings ASCII (mensagens de boot, paths, versões)
- bytes mágicos (ex.:
7f 45 4c 46para ELF) - longas sequências de
00ouff(geralmente padding/flash apagada)
strings mostrar muitos paths legíveis como /etc/ ou /bin/, você talvez esteja vendo uma imagem de filesystem Linux embarcado.Passo 3: Procure assinaturas
Mesmo sem ferramentas especializadas, você pode buscar padrões:
| |
Isso indica se há um ELF embutido dentro de um blob maior.
Passo 4: Se for ELF: extraia estrutura imediatamente
| |
Perguntas-chave:
- É ELF32 ou ELF64?
- Machine = RISC-V?
- Qual é o entry point?
- Quais segmentos são carregáveis (
PT_LOAD)?
3. Se você tem um .bin: como recuperar um endereço de carga provável
Estratégia A: A partir do mapa de memória da plataforma
Se você conhece o mapa de memória do alvo (por exemplo, QEMU virt), muitas vezes conhece endereços típicos de RAM/flash.
- Muitos exemplos RV32 bare-metal começam em
0x80000000para RAM no QEMU virt. - SoCs reais variam bastante, use datasheets ou logs de boot.
Estratégia B: Vetores de reset / padrões de boot
Em algumas arquiteturas, o vetor de reset tem uma estrutura reconhecível. No RISC-V, o boot geralmente começa com um prólogo pequeno e um salto; os padrões são menos padronizados do que tabelas de vetor do ARM, mas você ainda pode procurar:
- sequências de prólogo plausíveis
- referências a regiões MMIO conhecidas
Estratégia C: Endereços absolutos no código
Se o firmware inclui endereços absolutos (registradores MMIO, ranges de RAM), esses endereços podem revelar a plataforma.
- Faça varredura por valores alinhados de 32 bits que parecem endereços (ex.: bits altos consistentes)
4. Um “primeiro disassembly” prático (sem se comprometer cedo demais)
Mesmo sem uma ferramenta GUI, você pode fazer um disassembly sanity se souber a arquitetura.
Exemplo: disassemble um binário bruto como RV32
Se você tem GNU binutils com suporte a RISC-V:
| |
Se a saída for majoritariamente lixo/ilegais, suas suposições podem estar erradas:
- arquitetura errada (rv64 vs rv32)
- endianness errado (raro em RISC-V)
- endereço base errado (para branches relativos, isso importa)
objdump -b binary não sabe o endereço de carga correto. O disassembly é “endereçado” a partir de 0, a menos que você compense isso na sua ferramenta de análise.5. Carving: extraindo sub-imagens de um blob
Se você encontrar um ELF embutido no offset O, extraia:
| |
Se for um ELF real, você pode usar todos os métodos do Capítulo 2.
Se você encontrar uma assinatura de filesystem ou compressão, pode precisar de ferramentas especializadas (comuns em firmware), mas o fluxo continua:
- identificar
- extrair
- validar
6. Transforme achados em um mapa (habilidade subestimada)
Crie uma anotação simples como:
| |
Isso torna seu trabalho reproduzível e mais fácil de compartilhar.
7. Laboratório minimalista de “estilo firmware” (usando seu próprio sample)
- Pegue
build/ld_demo.elf(do Capítulo 7) e converta para.bin. - Finja que você não sabe o que é.
- Use apenas
file,hexdump,stringseobjdump -b binarypara identificar. - Escreva seu melhor palpite sobre:
- arquitetura,
- endereço de carga,
- o que o código faz.
Depois, compare com a verdade usando readelf no ELF original.
Exercícios
- Embuta um ELF em um blob maior (ex.: concatenando com padding) e pratique carving com
grep -aobedd. - Crie um binário bruto com um endereço base conhecido (ex.: seu linker origin) e veja como o disassembly muda se você assumir um base errado.
- Escolha 5 strings de uma imagem de firmware e escreva hipóteses sobre a que subsistemas elas se relacionam.
Como testar suas respostas
- Você consegue produzir um “mapa de análise” curto que outra pessoa conseguiria seguir.
- Suas sub-imagens extraídas validam com
fileereadelf(quando aplicável).
Resumo
Você aprendeu um fluxo de triagem de firmware repetível: identificar → extrair → validar → mapear → escolher o próximo passo de análise.
A seguir: análise dinâmica com tracing no QEMU, quando isso se aplica a IoT/firmware, quais são as limitações e como fazer experimentos reprodutíveis e seguros.