
Análise forense de memória Linux com zero dependências, utilizando BTF embutido no kernel e kallsyms para análise de memória com conhecimento de tipos, sem informações de depuração externas.
mquire, um trocadilho com as palavras memory e inquire, é uma ferramenta de consulta de memória inspirada pelo osquery.
mquire pode analisar snapshots de memória do kernel Linux sem precisar de símbolos de depuração externos.
Tudo o que é necessário para a análise já está incorporado no próprio dump de memória. Isso significa que você pode analisar:
Requisitos de versão do kernel:
scripts/kallsyms.c)mquire analisa a memória do kernel lendo dois tipos de informações que estão embutidas nos kernels Linux modernos:
/proc/kallsyms)Ao combinar informações de tipo com localizações de símbolos, mquire pode encontrar e ler estruturas de dados complexas do kernel, como:
Isso torna possível extrair arquivos diretamente do cache de arquivos do kernel, mesmo que tenham sido excluídos do disco.
O scanner Kallsyms depende do formato de dados de scripts/kallsyms.c no código-fonte do kernel. Se versões futuras do kernel mudarem este formato, as heurísticas do scanner podem precisar de atualizações.
mquire fornece tabelas SQL para consultar diferentes aspectos do sistema ou do estado da própria ferramenta.
mquire não é um banco de dados. Cada consulta reconstrói estruturas de dados do kernel varrendo a memória e seguindo ponteiros. Não há índices pré-computados ou resultados em cache: cada acesso a uma tabela é uma travessia de dados do kernel. Use
AS MATERIALIZEDpara evitar varreduras redundantes (consulte Query Optimization), e forneça restrições comotaskao consultar tabelas por processo comotask_open_filesememory_mappingspara limitar a varredura a um único processo.
Princípio de design: endereços virtuais como chaves de junção. As tabelas usam
virtual_address(o endereço do kernel da estrutura de dados subjacente) como a chave de junção canônica: nãopidou outros identificadores visíveis ao usuário. Isso é intencional, porque o mesmo PID pode aparecer várias vezes em diferentes fontes de descoberta e tarefas raiz, enquanto um endereço virtual identifica exclusivamente um objeto específico do kernel. Tanto as tabelas SQL quanto a APILinuxOperatingSystemsubjacente são construídas em torno desta convenção.
/proc/kallsyms)dmesg)task para análise direcionada, ou consulte todas as tarefas de uma vez)task para análise direcionada, ou consulte todas as tarefas de uma vez)task, então faça um JOIN com tasks/processes (ex: JOIN task_capabilities c ON c.task = p.virtual_address).task.mem de um objeto de módulo do kernel. Requer uma restrição kernel_module, então faça um JOIN com kernel_modules (ex: JOIN kernel_module_mem_entries r ON r.kernel_module = m.virtual_address).struct ftrace_ops, percorridos a partir do símbolo ftrace_ops_list por padrão. Restrinja virtual_address para ler um único nó, ou start_vaddr (opcionalmente limitado por end_vaddr) para percorrer a partir de um nó arbitrário.mquire fornece três comandos principais:
mquire shell - Iniciar um shell SQL interativo para consultar snapshots de memóriamquire query - Executar uma única consulta SQL e exibir resultados (suporta formato JSON ou tabela)mquire command - Executar comandos personalizados em snapshots de memória (ex.: .task_tree, .system_version, .dump)mquire fornece comandos especiais prefixados com um ponto (.) para distingui-los de consultas SQL.
Estes comandos funcionam no shell interativo e com mquire query:
.tables - Listar todas as tabelas disponíveis.schema - Mostrar esquema de todas as tabelas.schema <tabela> - Mostrar esquema de uma tabela específica.commands - Listar todos os comandos personalizados disponíveis.exit - Sair do shell interativo (apenas shell)Estes comandos funcionam no shell interativo e com mquire command:
Use --help com qualquer comando para ver opções disponíveis e informações de uso. Por exemplo: .task_tree --help
.system_versionExibir informações da versão do sistema operacional.
Este é um comando de conveniência equivalente a SELECT * FROM os_version, mas com saída formatada.
.task_treeExibir uma árvore hierárquica de processos e threads em execução, semelhante ao comando pstree no Linux.
Opções:
--show-threads - Incluir threads além dos processos. Quando ativado, exibe tanto TGID quanto TID para cada entrada.--use-real-parent - Usar o campo real_parent em vez de parent para construir a estrutura da árvore. O campo real_parent mostra o processo pai original antes de qualquer reparentização (útil para rastrear cadeias de criação de processos mesmo após os processos pais terem saído).Notas:
[TGID TID] quando exibindo threads, ou [TGID] quando threads estão ocultos. TGID (Thread Group ID) é o que comumente chamamos de PID. Para threads principais (onde TGID == TID), ambos os valores serão iguais..carveExtrair uma região de memória virtual para o disco. Este comando extrai conteúdo bruto de memória de um intervalo específico de endereço virtual usando uma tabela de páginas fornecida, útil para extrair memória de processo, conteúdo de heap ou outras regiões de memória.
Argumentos:
ROOT_PAGE_TABLE - O endereço físico da tabela de páginas raiz (string hex com prefixo 0x opcional). Isso determina o espaço de endereçamento a ser usado para tradução.VIRTUAL_ADDRESS - O endereço virtual para começar a extrair (string hex com prefixo 0x opcional).SIZE - Número de bytes a extrair.DESTINATION_PATH - Caminho do arquivo de saída onde a memória extraída será escrita.Notas:
.dumpExtrair arquivos do cache de arquivos do kernel para recuperar arquivos diretamente da memória. Este comando itera por todas as tarefas e seus descritores de arquivo abertos, extraindo o conteúdo dos arquivos do cache de página.
Argumentos:
OUTPUT - Diretório de saída para os arquivos extraídos. Os arquivos são organizados por TGID (ex.: tgid_1234/caminho/para/arquivo).Notas:
mquire é projetado para:
Pacotes pré-compilados estão disponíveis como artefatos de execuções do CI. Você pode baixá-los na aba Actions selecionando uma execução de workflow bem-sucedida e baixando os artefatos. Os seguintes formatos de pacote estão disponíveis:
.deb para Debian/Ubuntu.rpm para Fedora/RHEL/CentOS.tar.gz genérico para Linuxmquire é escrito em Rust. Para compilá-lo:```bash
git clone https://github.com/trailofbits/mquire cd mquire
cargo build --release
## Formatos de snapshot suportados
O mquire suporta os seguintes formatos de snapshot de memória, detectados pela extensão do arquivo:
| Formato | Extensão | Descrição |
|---------|----------|-------------|
| Raw | `.raw` | Despejo de memória física plana (cópia byte a byte do espaço de endereço físico) |
| LiME | `.lime` | Formato [Linux Memory Extractor](https://github.com/504ensicsLabs/LiME) com cabeçalhos de intervalo de endereços |
| ELF core | `.elf` | Despejo de núcleo ELF com segmentos PT_LOAD (como produzido por `virsh dump` ou QEMU `dump-guest-memory`) |
## Adquirindo um snapshot de memória
### Usando AVML (recomendado)
Recomendamos o [AVML](https://github.com/microsoft/avml) para adquirir snapshots de memória de sistemas Linux ativos. O [LiME](https://github.com/504ensicsLabs/LiME) foi sugerido anteriormente, mas não é mais mantido ativamente.```bash
sudo avml output.lime
Importante: Não use
--compressao adquirir snapshots para o mquire. mquire não suporta snapshots AVML comprimidos. Se você possui um snapshot comprimido, useavml-convertpara descomprimi-lo primeiro:avml-convert compressed.lime uncompressed.lime
Consulte a documentação do AVML para opções adicionais.
Para máquinas virtuais libvirt/KVM, use virsh dump para produzir um dump de núcleo ELF:```bash
virsh -c qemu:///system dump output.elf --memory-only --format elf
A VM é pausada durante o dump e retomada após. Adicione `--live` para evitar pausar, ao custo da consistência do snapshot.
## Primeiros passos
Uma vez que você tenha um snapshot de memória, você pode interagir com ele usando consultas SQL e comandos personalizados. O mquire oferece três maneiras de interagir com snapshots:
### Shell interativo
Inicie um shell SQL interativo:```bash
mquire shell /path/to/memory.raw
Isto abre um prompt onde você pode executar consultas SQL e comandos interativamente:```bash mquire> .tables # List all available tables mquire> .schema tasks # Show schema for a specific table mquire> SELECT * FROM tasks; # Run SQL queries mquire> .task_tree --show-threads # Run custom commands mquire> .exit # Exit the shell
### Consultas SQL avulsas
Execute uma única consulta SQL ou comando interno a partir da linha de comando:```bash
# Output as JSON (default)
mquire query /path/to/memory.raw "SELECT * FROM os_version"
# Output as table format
mquire query /path/to/memory.raw "SELECT * FROM tasks" --format table
# Built-in commands work too
mquire query /path/to/memory.raw ".tables"
mquire query /path/to/memory.raw ".schema tasks"
Execute comandos personalizados para análise especializada:```bash
mquire command /path/to/memory.raw
mquire command /path/to/memory.raw ".system_version"
mquire command /path/to/memory.raw ".task_tree"
mquire command /path/to/memory.raw ".task_tree --show-threads"
mquire command /path/to/memory.raw ".task_tree --help"
## Arquivos SQL de Autostart
mquire carrega e executa automaticamente arquivos SQL de `$HOME/.config/trailofbits/mquire/autostart/` na inicialização. Os arquivos são organizados por sistema operacional e arquitetura:```
autostart/
common/common/ # All platforms and architectures
common/{arch}/ # All platforms, specific architecture
{os}/common/ # Specific platform, all architectures
{os}/{arch}/ # Specific platform and architecture
Os ficheiros dentro de cada diretório estão ordenados alfabeticamente. Os diretórios são examinados na ordem mostrada acima.
Funcionalidades:
.sqlmquire_diagnostics mas não bloqueiam a execuçãomquire shell e mquire querymquire fornece vistas SQL reutilizáveis no diretório sql/views/. Instale-as com just install-views. Consulte o views README para a lista completa, convenção de numeração e estrutura do diretório.
Vistas Linux (sql/views/linux/common/):
000_processes.sql - Lista de processos deduplicada em todas as fontes de descoberta, filtrada para líderes de processos em espaço de utilizador. Consulte com SELECT * FROM processes.100_process_network_connections.sql - Mapa das conexões de rede para os processos proprietários associando através de descritores de ficheiros. Consulte com SELECT * FROM process_network_connections WHERE comm = 'sshd'.120_process_capabilities.sql - Visão geral rápida de capacidades, uma linha por processo com cada conjunto como uma lista compacta (ALL, '', ou nomes de cap; NULL se ilegível). Consulte com SELECT * FROM process_capabilities.As consultas mquire requerem a reconstrução de estruturas de dados do kernel a partir da memória virtual, desreferenciando ponteiros usando informações de tipo embutidas e símbolos de depuração. Este processamento pode ser caro, por isso use técnicas de otimização de consultas para melhorar drasticamente o desempenho.
AS MATERIALIZEDUse a dica AS MATERIALIZED para armazenar em cache os resultados das tabelas quando as tabelas são usadas em JOINs ou acedidas várias vezes.
Quando materializar:
tasks requer percorrer listas ligadas de estruturas de processo, desreferenciando múltiplos ponteiros por processo)Exemplo:```sql -- Find network connections for a specific process using materialization WITH target_tasks AS MATERIALIZED ( SELECT * FROM tasks WHERE comm = 'sshd' AND type = 'thread_group_leader' ),
network_connections_mat AS MATERIALIZED ( SELECT * FROM network_connections )
SELECT t.tgid, t.comm, nc.local_address, nc.local_port, nc.remote_address, nc.remote_port, nc.state, nc.protocol FROM target_tasks t JOIN task_open_files tof ON tof.task = t.virtual_address JOIN network_connections_mat nc ON nc.inode = tof.inode;
**Nota:** As tabelas `task_open_files` e `memory_mappings` usam a coluna `task` como entrada do gerador. Quando unida com a tabela `tasks`, o SQLite passa automaticamente a restrição por meio de joins de loop aninhado, tornando os JOINs diretos eficientes.
**Impacto no desempenho:** A materialização pode fornecer uma aceleração significativa para consultas com JOINs (normalmente 2 a 5 vezes mais rápido)
**Exemplo de resultados de benchmark:**
*Teste realizado em um snapshot do Ubuntu 24.04 (kernel 6.8.0-63), 351 processos, 50 conexões, 2142 arquivos abertos. O desempenho varia de acordo com o tamanho do snapshot, versão do kernel e hardware.*
| Método | Tempo Real | Tempo de Usuário | Aceleração |
|--------|-----------|-----------|---------|
| SEM materialização | 12.067s | 16.373s | base |
| COM materialização | 3.171s | 8.786s | **3.8x mais rápido** |
### Otimização da Ordem do JOIN
**Comece pela tabela menor e faça JOIN em direção a tabelas maiores** para minimizar as linhas processadas no início do pipeline da consulta.
**Tamanhos típicos de tabelas:**
- `network_connections`: menor - apenas processos com atividade de rede
- `tasks`: médio - todos os processos
- `task_open_files`: maior - todos os descritores de arquivo abertos
**Ordem ideal:**
Comece pela tabela tasks filtrada e faça JOIN em direção a tabelas maiores:```sql
FROM target_tasks t -- filtered tasks
JOIN task_open_files tof ON tof.task = t.virtual_address -- open files
JOIN network_connections_mat nc ON nc.inode = tof.inode -- matching connections
Use EXPLAIN QUERY PLAN para ver como o SQLite executa sua consulta:```sql
EXPLAIN QUERY PLAN
SELECT ...
FROM target_tasks t
JOIN task_open_files tof ON tof.task = t.virtual_address
JOIN network_connections_mat nc ON nc.inode = tof.inode;
Look for:
- **BLOOM FILTER**: otimização do SQLite para grandes JOINs
- **AUTOMATIC COVERING INDEX**: Índices temporários criados para buscas
- **SCAN**: Varredura completa da tabela (esperada para a tabela condutora)
- **SEARCH**: Busca baseada em índice (eficiente)
### Melhores Práticas
1. **Sempre materialize tabelas caras** usadas em JOINs
2. **Comece com a menor tabela** como sua tabela condutora
3. **Use SQL multilinha** em scripts para legibilidade
4. **Verifique planos de consulta** com `EXPLAIN QUERY PLAN` para consultas complexas
5. **Evite `SELECT *`** em produção - especifique apenas as colunas necessárias
### Extração de arquivos
Extraia arquivos da memória para o disco:```bash
mquire command /path/to/memory.raw ".dump /output/directory"
Todas as consultas usam sintaxe SQL padrão.```bash $ mquire shell ubuntu2404_6.14.0-37-generic.lime mquire> SELECT * FROM os_version; arch:"x86_64" kernel_version:"6.14.0-37-generic" system_version:"#37~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov 20 10:25:38 UTC 2"
#### Informações do sistema```bash
$ mquire shell ubuntu2404_6.14.0-37-generic.lime
mquire> SELECT * FROM system_info;
domain:"(none)" hostname:"ubuntu2404"
$ mquire shell ubuntu2404_6.14.0-37-generic.lime mquire> SELECT name, state, src_version, parameters FROM kernel_modules LIMIT 5; name:"snd_seq_dummy" state:"live" src_version:"7A40E0FD47A0746D1C9CD85" parameters:"ump (perm: 0o444), duplex (perm: 0o444), ports (perm: 0o444)" name:"snd_hrtimer" state:"live" src_version:"81EE6D58896E2C2E63E252D" parameters:"" name:"qrtr" state:"live" src_version:"473C5AB47E04ECEA0106681" parameters:"" name:"virtio_rng" state:"live" src_version:"0852940240D554836D22CB2" parameters:"" name:"intel_rapl_msr" state:"live" src_version:"34853C4F5EB8FCAD28ACFB3" parameters:""
#### Executando tarefas```bash
$ mquire shell ubuntu2404_6.14.0-37-generic.lime
mquire> SELECT comm, binary_path, command_line FROM tasks WHERE command_line NOT NULL AND comm LIKE "%systemd%";
comm:"systemd" binary_path:"/usr/lib/systemd/systemd" command_line:"/sbin/init splash"
comm:"systemd-oomd" binary_path:"/usr/lib/systemd/systemd-oomd" command_line:"/usr/lib/systemd/systemd-oomd"
comm:"systemd-resolve" binary_path:"/usr/lib/systemd/systemd-resolved" command_line:"/usr/lib/systemd/systemd-resolved"
comm:"systemd-udevd" binary_path:"/usr/bin/udevadm" command_line:"/usr/lib/systemd/systemd-udevd"
comm:"systemd" binary_path:"/usr/lib/systemd/systemd" command_line:"/usr/lib/systemd/systemd --user"
comm:"systemd-logind" binary_path:"/usr/lib/systemd/systemd-logind" command_line:"/usr/lib/systemd/systemd-logind"
comm:"systemd-journal" binary_path:"/usr/lib/systemd/systemd-journald" command_line:"/usr/lib/systemd/systemd-journald"
comm:"systemd-timesyn" binary_path:"/usr/lib/systemd/systemd-timesyncd" command_line:"/usr/lib/systemd/systemd-timesyncd"
Encontre conexões de rede para um processo específico unindo tasks, task_open_files e network_connections.```bash $ mquire shell ubuntu2404_6.14.0-37-generic.lime mquire> SELECT t.tgid, t.comm, nc.protocol, nc.local_address, nc.local_port, nc.remote_address, nc.remote_port, nc.state FROM tasks t JOIN task_open_files tof ON tof.task = t.virtual_address JOIN network_connections nc ON nc.inode = tof.inode WHERE t.comm = 'sshd'; tgid:"1134" comm:"sshd" protocol:"tcp" local_address:"0.0.0.0" local_port:"22" remote_address:"" remote_port:"" state:"listen" tgid:"1134" comm:"sshd" protocol:"tcp" local_address:"::" local_port:"22" remote_address:"" remote_port:"" state:"listen"
#### Tarefa de arquivos abertos
Lista arquivos abertos para processos específicos ao combinar `tasks` com `task_open_files`:```bash
$ mquire shell ubuntu2404_6.14.0-37-generic.lime
mquire> SELECT t.comm, tof.path
FROM tasks t
JOIN task_open_files tof ON tof.task = t.virtual_address
WHERE t.comm LIKE '%systemd%'
LIMIT 10;
comm:"systemd" path:"/null"
comm:"systemd" path:"/null"
comm:"systemd" path:"/null"
comm:"systemd" path:"/kmsg"
comm:"systemd" path:"[eventpoll]"
comm:"systemd" path:"[signalfd]"
comm:"systemd" path:"inotify"
comm:"systemd" path:"/"
comm:"systemd" path:"[timerfd]"
comm:"systemd" path:"/usr/lib/systemd/systemd-executor"
$ mquire query --format=json ubuntu2404_6.14.0-37-generic.lime "SELECT * FROM os_version" [ { "arch": "x86_64", "kernel_version": "6.14.0-37-generic", "system_version": "#37~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov 20 10:25:38 UTC 2" } ]
##### Saída de Tabela```bash
$ mquire query --format=table ubuntu2404_6.14.0-37-generic.lime "SELECT * FROM os_version"
arch:"x86_64" kernel_version:"6.14.0-37-generic" system_version:"#37~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov 20 10:25:38 UTC 2"
$ mquire command ubuntu2404_6.14.0-37-generic.lime Available commands: .carve Carve a region of virtual memory to disk .dump Dump all open files from tasks to disk .system_version Display the operating system version .task_tree Display a hierarchical task tree
##### Exibir versão do sistema```bash
$ mquire command ubuntu2404_6.14.0-37-generic.lime ".system_version"
System Version: #37~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov 20 10:25:38 UTC 2
Kernel Version: 6.14.0-37-generic
Architecture: x86_64
$ mquire command ubuntu2404_6.8.0-63-generic.lime .task_tree | head -n 10 Parent: task_struct::parent Threads: Disabled Page Table: paddr(0x0000000001a60000)
└─ [0] (ffffffff90c0fcc0) swapper/0 ╎ ↳ [0] (ffff982a00e33518) \xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd,)\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd\x0e ├─ [1] (ffff982a0084a8c0) systemd │ ├─ [430] (ffff982a0d27a8c0) systemd-journal │ ├─ [495] (ffff982a08a88000) systemd-udevd │ ├─ [786] (ffff982a07a60000) systemd-oomd
**Nota:** Quando múltiplas entradas `task_struct` existem com o mesmo TID (Thread ID, que pode ocorrer devido a corrupção de memória ou timing de snapshot), as entradas duplicadas são exibidas com o símbolo de continuação `╎ ↳` indentado sob a entrada primária. O formato é `[TGID] (virtual_address) name` quando as threads estão ocultas, ou `[TGID TID]` quando exibindo threads (onde TGID é o Thread Group ID, comumente conhecido como PID).
#### Extrair arquivos da memória```bash
$ mquire command ubuntu2404_6.14.0-37-generic.lime ".dump ./extracted_files"
Legend: SK = skipped, OK = all good, ER = errored
Summary:
Total files processed: 1234
Successfully dumped: 1156
Skipped: 45
Errors: 33
File Status:
OK /usr/lib/systemd/systemd (TGID 1)
OK /etc/passwd (TGID 1)
SK /dev/null (TGID 1)
...
Use a flag --debug para ativar mensagens de debug detalhadas durante a inicialização e análise:```bash
mquire --debug command /path/to/memory.raw ".system_version"
- **`shell` e `query` modos**: Mensagens de depuração são armazenadas na tabela SQL `mquire_diagnostics`. Consulte-as com `SELECT * FROM mquire_diagnostics;`
- **`command` modo**: Mensagens de depuração são impressas diretamente no stderr.
Para problemas de inicialização que impeçam o mquire de carregar o snapshot com sucesso, recomenda-se usar o modo `command` com um comando simples como `.system_version`, pois ele imprime a saída de depuração no stderr imediatamente, sem necessidade de consultar a tabela `mquire_diagnostics`.
## Configuração
O mquire pode ser configurado através de um arquivo TOML em `$HOME/.config/trailofbits/mquire/config.toml`. Se o arquivo não existir, os valores padrão são usados.
### Opções disponíveis e valores padrão```toml
[database]
# Maximum number of entries retained in the mquire_diagnostics table.
# During initialization, all entries are kept regardless of this limit.
# Once initialization completes, new log entries trigger eviction of the
# oldest entries when the total exceeds this value.
mquire_diagnostics_max_entries = 1000
Este projeto utiliza just como executor de comandos. Execute just para ver os comandos disponíveis:
Estes testes verificam se o mquire produz a saída correta para consultas contra snapshots de memória.
just integration-test - Executa testes e compara a saída com os arquivos JSON esperadosjust integration-update - Atualiza os arquivos JSON esperados com a saída real (use ao alterar esquemas de tabelas)Após executar integration-update, revise o git diff para garantir que as alterações correspondam às suas expectativas antes de efetuar o commit.
Adicionando novos testes: Crie um arquivo .sql e um arquivo .json correspondente no diretório de snapshot apropriado, depois execute just integration-update para preencher a saída esperada.
Contribuições são bem-vindas! Ao contribuir, siga estas diretrizes:
init_taskEste projeto está licenciado sob a Licença Apache 2.0. Consulte o arquivo LICENSE para obter detalhes.
130_process_ptrace_flags.sqlPT_*''SELECT * FROM process_ptrace_flags200_tasklist_pidns_differences.sql - Deteta processos visíveis numa fonte de descoberta mas ausentes noutra, útil para deteção de rootkits. Consulte com SELECT * FROM tasklist_pidns_differences.230_unbacked_ftrace_ops.sql - Callbacks ftrace registados que apontam para fora do texto do kernel e todos os módulos listados em kernel_modules. Consulte com SELECT * FROM unbacked_ftrace_ops.| Comando | Descrição |
|---|
just check | Executa todas as verificações (cargo check, cargo clippy, cargo fmt, ruff, mypy) |
just test | Executa testes unitários |
just format | Formata o código (cargo fmt, ruff) |
just integration-test | Executa testes de integração de consultas SQL |
just integration-update | Atualiza a saída esperada dos testes |
just package | Constrói pacotes de lançamento |