Skip to content

Repository files navigation

memacq - dump de memória para linux.

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 .

Uso

# 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.

Saída

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

Decisões que importam

/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:

  1. 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.
  2. AVML (--avml /caminho/avml) — binário estático da Microsoft, sem módulo de kernel.
  3. /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.
  4. /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.

Interpretando a imagem

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.

Segurança operacional

  • 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-SHA256 junto com o upload, para o receptor conferir de forma independente.
  • Dumps saem com modo 0600 e o diretório de evidências com 0700.
  • 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.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages