Ferramenta de aquisição forense de memória em Go puro, sem dependências externas. Gera imagem + hash de custódia + índice de segmentos + metadados + relatório, com upload opcional para o servidor de evidências.
go build -o memacq .
# binário estático, para levar no kit de resposta a incidente
CGO_ENABLED=0 go build -ldflags="-s -w" -o memacq .# memória física (LiME = imagem completa e atômica)
sudo ./memacq --lime-module ./lime.ko --case-id INC-2026-014 --operator csantos
# memória física sem módulo de kernel, via /proc/kcore
sudo ./memacq --physical-method kcore --case-id INC-2026-014
# espaço de endereçamento de um processo
sudo ./memacq --pid 4242 --case-id INC-2026-014 --verify
# com envio para bucket (URL pré-assinada do S3/GCS/Azure funciona direto)
sudo ./memacq --pid 4242 --upload-url "https://bucket.s3.amazonaws.com/ev.gz?X-Amz-..."./memacq --help lista todas as flags.
Cada execução produz um conjunto de evidências em --out-dir (padrão ./evidence):
| Arquivo | Conteúdo |
|---|---|
<prefixo>.raw.gz |
a imagem de memória |
<prefixo>.raw.gz.sha256 |
digest no formato sha256sum -c |
<prefixo>.segments.json |
índice das regiões: endereço → offset no dump |
<prefixo>.metadata.json |
host, kernel, RAM, uptime, invocação, hash do próprio binário |
<prefixo>.report.json / .report.txt |
relatório de custódia (máquina e humano) |
<prefixo>.log |
log completo da aquisição |
Conferência:
sha256sum -c evidence/*.raw.gz.sha256/proc/<pid>/mem não pode ser lido sequencialmente. É um espaço virtual
esparso de 64 bits e o offset 0 é não-mapeado — dd if=/proc/<pid>/mem retorna
EIO sem capturar nada. A ferramenta lê /proc/<pid>/maps e faz pread() em
cada região mapeada, registrando onde cada uma caiu no arquivo.
Dois hashes, calculados durante a aquisição. sha256_raw é o hash da
imagem de memória em si e não muda com a compressão — é o hash de custódia.
sha256_stored cobre o arquivo em disco, é o que o sha256sum -c valida no
.gz. Ambos saem do mesmo stream, então uma imagem de 64 GB nunca é lida
duas vezes só para hashear.
O processo é congelado (SIGSTOP) durante o dump e retomado no defer,
inclusive em Ctrl-C. Sem isso o espaço de endereçamento muda durante a
leitura e a imagem vira uma mistura de instantes diferentes. Use --no-freeze
se congelar o alvo for inaceitável — o relatório passa a registrar o aviso.
Métodos físicos, em ordem de qualidade:
- LiME (
--lime-module lime.ko) — imagem completa e atômica. É o método de referência; compile o módulo para a versão exata do kernel do alvo. - AVML (
--avml /caminho/avml) — binário estático da Microsoft, sem módulo de kernel. /proc/kcore— puro Go, sem ferramenta externa. Imagem de endereços virtuais do kernel; os segmentos maiores que a RAM instalada (vmalloc) são descartados e registrados no índice./dev/mem— último recurso.CONFIG_STRICT_DEVMEM, ativo em qualquer distro padrão, corta a leitura no primeiro MiB. A ferramenta detecta a truncagem e avisa no relatório em vez de entregar uma imagem incompleta em silêncio.
--physical-method auto (padrão) tenta os quatro nessa ordem.
Layout do arquivo. Em dump físico com --pad (padrão) os buracos de MMIO
são preenchidos com zeros, de modo que offset no arquivo == endereço físico —
é o formato que Volatility e Rekall esperam. O padding é praticamente de graça
depois do gzip. Dump de processo nunca é preenchido: o espaço de 64 bits é
esparso demais, então o mapeamento vem pelo .segments.json.
A compressão é paralela. DEFLATE em uma thread satura em ~50 MB/s, o que
faz da compressão — não do disco — o gargalo de uma aquisição de 12 GB. O
compressor emite membros gzip independentes que concatenam num único fluxo
válido, lido normalmente por gunzip e por qualquer ferramenta. Medido em dump
de 1,6 GiB: 50,7 s com uma thread, 23,0 s com quatro, ao custo de 0,11% de
tamanho. --gzip-workers 1 volta ao compressor mono-thread, que rende o melhor
tamanho.
A ferramenta não despeja a memória que está capturando. Escrever gigabytes
pelo page cache faz o kernel liberar páginas para abrir espaço — possivelmente
as do próprio material sendo adquirido. A cada 64 MiB o dump é sincronizado e o
cache devolvido. Medido: dd de 200 MiB deixa 100% das páginas em cache; um
dump de 1,6 GiB deixa 0%.
--verify lê do disco de verdade. Sem descartar o cache, reler um arquivo
recém-escrito é servido da memória e a verificação não prova nada. Se o
descarte falhar, o relatório diz isso em vez de afirmar verificação limpa.
Falhas parciais não abortam a aquisição. Região desmapeada no meio da
leitura, EIO em faixa protegida — cada erro vai para o índice de segmentos e
a captura continua. Melhor uma imagem parcial documentada do que nenhuma.
O relatório traz três números que dizem, antes de qualquer análise, se a aquisição valeu:
Raw size : 1.00 GiB (1073741824 bytes)
Stored size : 34.31 MiB (35975043 bytes, 29.85x)
Zero bytes : 13.4% (137.33 MiB)
Fill pattern : 0xaf in 75.3% of sampled bytes (free-page poison)
Razão de compressão alta é sinal de alerta, não de eficiência. Memória viva comprime 2–4×. Acima de 10× significa que a maior parte do que foi capturado é padrão repetido, não conteúdo.
Zero bytes alto aponta memória não alocada ou leitura bloqueada — típico
de /dev/mem barrado por CONFIG_STRICT_DEVMEM. Acima de 90% vira aviso.
Fill pattern é o byte dominante quando não é zero. Kernels com
CONFIG_PAGE_POISONING preenchem páginas liberadas com um padrão fixo; essas
páginas aparecem no dump como blocos uniformes alinhados a 4 KiB. É RAM livre,
não falha de aquisição — mas define quanto da imagem tem conteúdo útil.
No exemplo acima, de 1 GiB adquiridos só ~11% carregam dados: o resto é página livre envenenada e zero. A aquisição está correta; a máquina é que estava com a memória baixa ociosa.
- O upload tenta de novo com backoff exponencial e, se o servidor informar conteúdo parcial, retoma do offset em vez de reenviar tudo. Erros 4xx não são repetidos: reenviar uma requisição não autorizada só queima a janela.
- A URL de upload é redigida (
?<redacted>) no log e no relatório, para que credenciais de URL pré-assinada não vazem para a evidência. - O digest vai no cabeçalho
X-Content-SHA256junto com o upload, para o receptor conferir de forma independente. - Dumps saem com modo
0600e o diretório de evidências com0700. - O dump é gravado em disco, não em memória — mas grave em mídia externa, não no volume do alvo, para não sobrescrever espaço não alocado que ainda seja relevante.