{"id":49829496,"url":"https://github.com/trailofbits/mquire","last_synced_at":"2026-05-30T10:00:37.968Z","repository":{"id":341570533,"uuid":"943954284","full_name":"trailofbits/mquire","owner":"trailofbits","description":"Zero-dependency Linux memory forensics PoC — leverages kernel-embedded BTF and kallsyms for type-aware memory analysis without external debug info.","archived":false,"fork":false,"pushed_at":"2026-05-17T08:00:39.000Z","size":3596,"stargazers_count":155,"open_issues_count":4,"forks_count":8,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-05-17T09:42:47.397Z","etag":null,"topics":["forensics","kernel","linux","memory","rust","sql"],"latest_commit_sha":null,"homepage":"https://blog.trailofbits.com/2026/02/25/mquire-linux-memory-forensics-without-external-dependencies/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/trailofbits.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-03-06T14:43:06.000Z","updated_at":"2026-05-17T07:59:17.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/trailofbits/mquire","commit_stats":null,"previous_names":["trailofbits/mquire"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/trailofbits/mquire","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trailofbits%2Fmquire","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trailofbits%2Fmquire/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trailofbits%2Fmquire/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trailofbits%2Fmquire/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/trailofbits","download_url":"https://codeload.github.com/trailofbits/mquire/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trailofbits%2Fmquire/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33687722,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["forensics","kernel","linux","memory","rust","sql"],"created_at":"2026-05-13T20:00:48.389Z","updated_at":"2026-05-30T10:00:37.955Z","avatar_url":"https://github.com/trailofbits.png","language":"Rust","funding_links":[],"categories":["linux"],"sub_categories":[],"readme":"# mquire\n\nmquire, a play on the `memory` and `inquire` words, is a memory querying tool inspired by [osquery](https://github.com/osquery/osquery).\n\n## Key advantage: No external debug symbols needed\n\n**mquire can analyze Linux kernel memory snapshots without requiring external debug symbols.**\n\nEverything needed for analysis is already embedded in the memory dump itself. This means you can analyze:\n- Unknown or custom kernels you've never seen before\n- Any Linux distribution without preparation\n- Memory snapshots where external debug symbols are unavailable or lost\n\n### Requirements\n\n**Kernel version requirements**:\n- **BTF support**: Kernel 4.18 or newer with BTF enabled (most modern distributions enable it by default)\n- **Kallsyms support**: Kernel 6.4 or newer (due to changes in `scripts/kallsyms.c` format)\n\n## How it works\n\nmquire analyzes kernel memory by reading two types of information that are embedded in modern Linux kernels:\n\n1. **Type information from BTF** ([BPF Type Format](https://www.kernel.org/doc/html/next/bpf/btf.html)) - Describes the structure and layout of kernel data types. BTF data is parsed using the [btfparse crate](https://crates.io/crates/btfparse).\n2. **Symbol information from Kallsyms** - Provides the memory locations of kernel symbols (same data used by `/proc/kallsyms`)\n\nBy combining type information with symbol locations, mquire can find and read complex kernel data structures like:\n- Process memory mappings (using maple tree structures)\n- Cached file data (using XArray structures)\n- Kernel log messages\n\nThis makes it possible to extract files directly from the kernel's file cache, even if they've been deleted from disk.\n\n### Compatibility notes\n\nThe Kallsyms scanner depends on the data format from `scripts/kallsyms.c` in the kernel source. If future kernel versions change this format, the scanner heuristics may need updates.\n\n## Capabilities\n\n### Tables\n\nmquire provides SQL tables to query different aspects of the system or the state of the tool itself.\n\n\u003e **mquire is not a database.** Each query reconstructs kernel data structures by scanning memory and following pointers. There are no precomputed indexes or cached results: every table access is a traversal of kernel data. Use `AS MATERIALIZED` to avoid redundant scans (see [Query Optimization](#query-optimization)), and provide constraints like `task` when querying per-process tables like `task_open_files` and `memory_mappings` to limit the scan to a single process.\n\n\u003e **Design principle: virtual addresses as join keys.** Tables use `virtual_address` (the kernel address of the underlying data structure) as the canonical join key: not `pid` or other user-visible identifiers. This is intentional, because the same PID can appear multiple times across different discovery sources and root tasks, while a virtual address uniquely identifies a specific kernel object. Both the SQL tables and the underlying `LinuxOperatingSystem` API are built around this convention.\n\n#### System information\n\n- **os_version** - Kernel version and architecture\n- **system_info** - Hostname and domain name\n- **boot_time** - System boot time\n- **kallsyms** - Kernel symbol addresses (same data as `/proc/kallsyms`)\n- **dmesg** - Kernel ring buffer messages (same data as `dmesg` command)\n\n#### Process information\n\n- **tasks** - Running processes with command lines and binary paths. Each task is discovered via multiple independent sources, which is useful for rootkit detection. See [Comparing task enumeration methods for rootkit detection](#comparing-task-enumeration-methods-for-rootkit-detection) and [Deduplicated process list](#deduplicated-process-list).\n- **task_open_files** - Files opened by each process (provide a `task` constraint for targeted analysis, or query all tasks at once)\n- **memory_mappings** - Memory regions mapped by each process (provide a `task` constraint for targeted analysis, or query all tasks at once)\n\n#### Kernel modules\n\n- **kernel_modules** - Loaded kernel modules with metadata (name, state, version, parameters, taint flags)\n\n#### Network information\n\n- **network_connections** - Active network connections (TCP sockets)\n- **network_interfaces** - Network interfaces with IP addresses and MAC addresses\n\n#### File system\n\n- **syslog_file** - System logs read from the kernel's file cache (works even if log files are deleted or unavailable, as long as they're cached in memory)\n\n#### Debugging\n\n- **mquire_diagnostics** - Internal mquire logs showing analysis progress, warnings, and errors\n\n## Commands\n\nmquire provides three main commands:\n\n- **`mquire shell`** - Start an interactive SQL shell to query memory snapshots\n- **`mquire query`** - Execute a single SQL query and output results (supports JSON or table format)\n- **`mquire command`** - Execute custom commands on memory snapshots (e.g., `.task_tree`, `.system_version`, `.dump`)\n\n## Dot Commands\n\nmquire provides special commands prefixed with a dot (`.`) to distinguish them from SQL queries.\n\n### Built-in Commands\n\nThese commands work in the interactive shell and with `mquire query`:\n\n- **`.tables`** - List all available tables\n- **`.schema`** - Show schema for all tables\n- **`.schema \u003ctable\u003e`** - Show schema for a specific table\n- **`.commands`** - List all available custom commands\n- **`.exit`** - Exit the interactive shell (shell only)\n\n### Custom Commands\n\nThese commands work in the interactive shell and with `mquire command`:\n\nUse `--help` with any command to see available options and usage information. For example: `.task_tree --help`\n\n#### `.system_version`\n\nDisplay the operating system version information.\n\nThis is a convenience command equivalent to `SELECT * FROM os_version`, but with formatted output.\n\n#### `.task_tree`\n\nDisplay a hierarchical tree of running processes and threads, similar to the `pstree` command on Linux.\n\n**Options:**\n- `--show-threads` - Include threads in addition to processes. When enabled, displays both TGID and TID for each entry.\n- `--use-real-parent` - Use the `real_parent` field instead of `parent` for building the tree structure. The `real_parent` field shows the original parent process before any reparenting (useful for tracking process creation chains even after parent processes exit).\n\n**Notes:**\n- The format is `[TGID TID]` when showing threads, or `[TGID]` when threads are hidden. TGID (Thread Group ID) is what's commonly called PID. For main threads (where TGID == TID), both values will be the same.\n\n#### `.carve`\n\nCarve a region of virtual memory to disk. This command extracts raw memory content from a specific virtual address range using a given page table, useful for extracting process memory, heap contents, or other memory regions.\n\n**Arguments:**\n- `ROOT_PAGE_TABLE` - The physical address of the root page table (hex string with optional 0x prefix). This determines the address space to use for translation.\n- `VIRTUAL_ADDRESS` - The virtual address to start carving from (hex string with optional 0x prefix).\n- `SIZE` - Number of bytes to carve.\n- `DESTINATION_PATH` - Output file path where the carved memory will be written.\n\n**Notes:**\n- The command shows a summary of mapped vs unmapped regions before writing.\n- Unmapped regions are filled with zeros in the output file.\n\n#### `.dump`\n\nExtract files from the kernel's file cache to recover files directly from memory. This command iterates through all tasks and their open file descriptors, extracting file contents from the page cache.\n\n**Arguments:**\n- `OUTPUT` - Output directory for extracted files. Files are organized by TGID (e.g., `tgid_1234/path/to/file`).\n\n**Notes:**\n- Currently works with files opened through file descriptors (from the process file descriptor table).\n- Does not yet support extracting data from memory-mapped files.\n- Empty files (no data in page cache) are skipped.\n- Regions with read errors are zero-padded in the output.\n\n## Use cases\n\nmquire is designed for:\n\n- **Forensic analysis** - Analyze memory snapshots from compromised systems to understand what was running and what files were accessed\n- **Incident response** - Quickly query memory dumps to find evidence of malicious activity\n- **Security research** - Study kernel internals and process behavior from memory snapshots\n- **Malware analysis** - Examine running processes and their file operations without detection\n- **Custom tooling** - Build your own analysis tools using the **mquire** library crate, which provides a reusable API for kernel memory analysis\n\n## Building and installation\n\n### Pre-built packages from CI\n\nPre-built packages are available as artifacts from CI runs. You can download them from the [Actions tab](https://github.com/trailofbits/mquire/actions) by selecting a successful workflow run and downloading the artifacts. The following package formats are available:\n\n- **linux-deb-package** - Debian/Ubuntu `.deb` package\n- **linux-rpm-package** - Fedora/RHEL/CentOS `.rpm` package\n- **linux-tgz-package** - Generic Linux `.tar.gz` archive\n\n### Building from source\n\nmquire is written in Rust. To build it:\n\n```bash\n# Clone the repository\ngit clone https://github.com/trailofbits/mquire\ncd mquire\n\n# Build the project\ncargo build --release\n\n# The binary will be in target/release/\n# - mquire: Unified tool with shell, query, and command modes\n```\n\n## Supported snapshot formats\n\nmquire supports the following memory snapshot formats, detected by file extension:\n\n| Format | Extension | Description |\n|--------|-----------|-------------|\n| Raw | `.raw` | Flat physical memory dump (byte-for-byte copy of physical address space) |\n| LiME | `.lime` | [Linux Memory Extractor](https://github.com/504ensicsLabs/LiME) format with address range headers |\n| ELF core | `.elf` | ELF core dump with PT_LOAD segments (as produced by `virsh dump` or QEMU `dump-guest-memory`) |\n\n## Acquiring a memory snapshot\n\n### Using AVML (recommended)\n\nWe recommend [AVML](https://github.com/microsoft/avml) for acquiring memory snapshots from live Linux systems. [LiME](https://github.com/504ensicsLabs/LiME) was previously suggested but is no longer actively maintained.\n\n```bash\nsudo avml output.lime\n```\n\n\u003e **Important:** Do not use `--compress` when acquiring snapshots for mquire. mquire does not support compressed AVML snapshots. If you have a compressed snapshot, use `avml-convert` to decompress it first: `avml-convert compressed.lime uncompressed.lime`\n\nSee the [AVML documentation](https://github.com/microsoft/avml) for additional options.\n\n### Using virsh (virtual machines)\n\nFor libvirt/KVM virtual machines, use `virsh dump` to produce an ELF core dump:\n\n```bash\nvirsh -c qemu:///system dump \u003cdomain\u003e output.elf --memory-only --format elf\n```\n\nThe VM is paused during the dump and resumed after. Add `--live` to avoid pausing, at the cost of snapshot consistency.\n\n## Getting started\n\nOnce you have a memory snapshot, you can interact with it using SQL queries and custom commands. mquire provides three ways to interact with snapshots:\n\n### Interactive shell\n\nStart an interactive SQL shell:\n\n```bash\nmquire shell /path/to/memory.raw\n```\n\nThis opens a prompt where you can run both SQL queries and commands interactively:\n\n```bash\nmquire\u003e .tables                      # List all available tables\nmquire\u003e .schema tasks                # Show schema for a specific table\nmquire\u003e SELECT * FROM tasks;         # Run SQL queries\nmquire\u003e .task_tree --show-threads    # Run custom commands\nmquire\u003e .exit                        # Exit the shell\n```\n\n### One-off SQL queries\n\nExecute a single SQL query or built-in command from the command line:\n\n```bash\n# Output as JSON (default)\nmquire query /path/to/memory.raw \"SELECT * FROM os_version\"\n\n# Output as table format\nmquire query /path/to/memory.raw \"SELECT * FROM tasks\" --format table\n\n# Built-in commands work too\nmquire query /path/to/memory.raw \".tables\"\nmquire query /path/to/memory.raw \".schema tasks\"\n```\n\n### Execute custom commands\n\nRun custom commands for specialized analysis:\n\n```bash\n# List all available commands (default behavior)\nmquire command /path/to/memory.raw\n\n# Display system version\nmquire command /path/to/memory.raw \".system_version\"\n\n# Show process tree\nmquire command /path/to/memory.raw \".task_tree\"\n\n# Show process tree with threads\nmquire command /path/to/memory.raw \".task_tree --show-threads\"\n\n# Get help for a command\nmquire command /path/to/memory.raw \".task_tree --help\"\n```\n\n## Autostart SQL Files\n\nmquire automatically loads and executes SQL files from `$HOME/.config/trailofbits/mquire/autostart/` on startup. Files are organized by operating system and architecture:\n\n```\nautostart/\n  common/common/    # All platforms and architectures\n  common/{arch}/    # All platforms, specific architecture\n  {os}/common/      # Specific platform, all architectures\n  {os}/{arch}/      # Specific platform and architecture\n```\n\nFiles within each directory are sorted alphabetically. Directories are scanned in the order shown above.\n\n**Features:**\n- Files must have a `.sql` extension\n- Errors are logged to `mquire_diagnostics` but don't block execution\n- Works with both `mquire shell` and `mquire query` commands\n\n### Shipped views\n\nmquire ships reusable SQL views in the [`sql/views/`](sql/views/) directory. Install them with `just install-views`. See the [views README](sql/views/README.md) for the full list, numbering convention, and directory structure.\n\n**Linux views** (`sql/views/linux/common/`):\n\n- [**`000_processes.sql`**](sql/views/linux/common/000_processes.sql) - Deduplicated process list across all discovery sources, filtered to user-space process leaders. Query with `SELECT * FROM processes`.\n- [**`100_process_network_connections.sql`**](sql/views/linux/common/100_process_network_connections.sql) - Maps network connections to owning processes by joining through file descriptors. Query with `SELECT * FROM process_network_connections WHERE comm = 'sshd'`.\n- [**`200_hidden_process_detection.sql`**](sql/views/linux/common/200_hidden_process_detection.sql) - Detects processes visible in one discovery source but missing from another, useful for rootkit detection. Query with `SELECT * FROM hidden_processes`.\n\n## Query Optimization\n\n**mquire queries require reconstructing kernel data structures from virtual memory by dereferencing pointers using embedded type information and debug symbols. This processing can be expensive, so use query optimization techniques to improve performance dramatically.**\n\n### Materialization with `AS MATERIALIZED`\n\nUse the `AS MATERIALIZED` hint to cache table results when tables are used in JOINs or accessed multiple times.\n\n**When to materialize:**\n- Tables that are expensive to generate (e.g., `tasks` requires walking linked lists of process structures, dereferencing multiple pointers per process)\n- Tables used in JOINs (accessed multiple times during query execution)\n- Tables referenced multiple times in the same query\n\n**Example:**\n\n```sql\n-- Find network connections for a specific process using materialization\nWITH\n  target_tasks AS MATERIALIZED (\n    SELECT * FROM tasks WHERE comm = 'sshd' AND type = 'thread_group_leader'\n  ),\n\n  network_connections_mat AS MATERIALIZED (\n    SELECT * FROM network_connections\n  )\n\nSELECT\n  t.tgid,\n  t.comm,\n  nc.local_address,\n  nc.local_port,\n  nc.remote_address,\n  nc.remote_port,\n  nc.state,\n  nc.protocol\nFROM target_tasks t\nJOIN task_open_files tof ON tof.task = t.virtual_address\nJOIN network_connections_mat nc ON nc.inode = tof.inode;\n```\n\n**Note:** The `task_open_files` and `memory_mappings` tables use the `task` column as a generator input. When joined with the `tasks` table, SQLite automatically passes the constraint via nested loop joins, making direct JOINs efficient.\n\n**Performance impact:** Materialization can provide significant speedup for queries with JOINs (typically 2-5x faster)\n\n**Example benchmark results:**\n\n*Test performed on an Ubuntu 24.04 snapshot (kernel 6.8.0-63), 351 processes, 50 connections, 2142 open files. Performance will vary based on snapshot size, kernel version, and hardware.*\n\n| Method | Real Time | User Time | Speedup |\n|--------|-----------|-----------|---------|\n| WITHOUT materialization | 12.067s | 16.373s | baseline |\n| WITH materialization | 3.171s | 8.786s | **3.8x faster** |\n\n### JOIN Order Optimization\n\n**Start with the smallest table and JOIN toward larger tables** to minimize rows processed early in the query pipeline.\n\n**Typical table sizes:**\n- `network_connections`: smallest - only processes with network activity\n- `tasks`: medium - all processes\n- `task_open_files`: largest - all open file descriptors\n\n**Optimal order:**\n\nStart with the filtered tasks table and join toward larger tables:\n\n```sql\nFROM target_tasks t                                       -- filtered tasks\nJOIN task_open_files tof ON tof.task = t.virtual_address  -- open files\nJOIN network_connections_mat nc ON nc.inode = tof.inode   -- matching connections\n```\n\n### Understanding Query Execution\n\nUse `EXPLAIN QUERY PLAN` to see how SQLite executes your query:\n\n```sql\nEXPLAIN QUERY PLAN\nSELECT ...\nFROM target_tasks t\nJOIN task_open_files tof ON tof.task = t.virtual_address\nJOIN network_connections_mat nc ON nc.inode = tof.inode;\n```\n\nLook for:\n- **BLOOM FILTER**: SQLite's optimization for large JOINs\n- **AUTOMATIC COVERING INDEX**: Temporary indexes created for lookups\n- **SCAN**: Full table scan (expected for the driving table)\n- **SEARCH**: Index-based lookup (efficient)\n\n### Best Practices\n\n1. **Always materialize expensive tables** used in JOINs\n2. **Start with the smallest table** as your driving table\n3. **Use multiline SQL** in scripts for readability\n4. **Check query plans** with `EXPLAIN QUERY PLAN` for complex queries\n5. **Avoid `SELECT *`** in production - specify only needed columns\n\n### File extraction\n\nExtract files from memory to disk:\n\n```bash\nmquire command /path/to/memory.raw \".dump /output/directory\"\n```\n\n### Example queries\n\nAll queries use standard SQL syntax.\n\n#### System version\n\n```bash\n$ mquire shell ubuntu2404_6.14.0-37-generic.lime\nmquire\u003e SELECT * FROM os_version;\narch:\"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\"\n```\n\n#### System information\n\n```bash\n$ mquire shell ubuntu2404_6.14.0-37-generic.lime\nmquire\u003e SELECT * FROM system_info;\ndomain:\"(none)\" hostname:\"ubuntu2404\"\n```\n\n#### Kernel modules\n\n```bash\n$ mquire shell ubuntu2404_6.14.0-37-generic.lime\nmquire\u003e SELECT name, state, src_version, parameters FROM kernel_modules LIMIT 5;\nname:\"snd_seq_dummy\" state:\"live\" src_version:\"7A40E0FD47A0746D1C9CD85\" parameters:\"ump (perm: 0o444), duplex (perm: 0o444), ports (perm: 0o444)\" \nname:\"snd_hrtimer\" state:\"live\" src_version:\"81EE6D58896E2C2E63E252D\" parameters:\"\u003cnull\u003e\" \nname:\"qrtr\" state:\"live\" src_version:\"473C5AB47E04ECEA0106681\" parameters:\"\u003cnull\u003e\" \nname:\"virtio_rng\" state:\"live\" src_version:\"0852940240D554836D22CB2\" parameters:\"\u003cnull\u003e\" \nname:\"intel_rapl_msr\" state:\"live\" src_version:\"34853C4F5EB8FCAD28ACFB3\" parameters:\"\u003cnull\u003e\" \n```\n\n#### Running tasks\n\n```bash\n$ mquire shell ubuntu2404_6.14.0-37-generic.lime\nmquire\u003e SELECT comm, binary_path, command_line FROM tasks WHERE command_line NOT NULL AND comm LIKE \"%systemd%\";\ncomm:\"systemd\" binary_path:\"/usr/lib/systemd/systemd\" command_line:\"/sbin/init splash\"\ncomm:\"systemd-oomd\" binary_path:\"/usr/lib/systemd/systemd-oomd\" command_line:\"/usr/lib/systemd/systemd-oomd\"\ncomm:\"systemd-resolve\" binary_path:\"/usr/lib/systemd/systemd-resolved\" command_line:\"/usr/lib/systemd/systemd-resolved\"\ncomm:\"systemd-udevd\" binary_path:\"/usr/bin/udevadm\" command_line:\"/usr/lib/systemd/systemd-udevd\"\ncomm:\"systemd\" binary_path:\"/usr/lib/systemd/systemd\" command_line:\"/usr/lib/systemd/systemd --user\"\ncomm:\"systemd-logind\" binary_path:\"/usr/lib/systemd/systemd-logind\" command_line:\"/usr/lib/systemd/systemd-logind\"\ncomm:\"systemd-journal\" binary_path:\"/usr/lib/systemd/systemd-journald\" command_line:\"/usr/lib/systemd/systemd-journald\"\ncomm:\"systemd-timesyn\" binary_path:\"/usr/lib/systemd/systemd-timesyncd\" command_line:\"/usr/lib/systemd/systemd-timesyncd\"\n```\n\n#### Connections\n\nFind network connections for a specific process by joining tasks, task_open_files, and network_connections.\n\n```bash\n$ mquire shell ubuntu2404_6.14.0-37-generic.lime\nmquire\u003e SELECT\n  t.tgid,\n  t.comm,\n  nc.protocol,\n  nc.local_address,\n  nc.local_port,\n  nc.remote_address,\n  nc.remote_port,\n  nc.state\nFROM tasks t\nJOIN task_open_files tof ON tof.task = t.virtual_address\nJOIN network_connections nc ON nc.inode = tof.inode\nWHERE t.comm = 'sshd';\ntgid:\"1134\" comm:\"sshd\" protocol:\"tcp\" local_address:\"0.0.0.0\" local_port:\"22\" remote_address:\"\u003cnull\u003e\" remote_port:\"\u003cnull\u003e\" state:\"listen\"\ntgid:\"1134\" comm:\"sshd\" protocol:\"tcp\" local_address:\"::\" local_port:\"22\" remote_address:\"\u003cnull\u003e\" remote_port:\"\u003cnull\u003e\" state:\"listen\"\n```\n\n#### Task open files\n\nList open files for specific processes by joining `tasks` with `task_open_files`:\n\n```bash\n$ mquire shell ubuntu2404_6.14.0-37-generic.lime\nmquire\u003e SELECT t.comm, tof.path\nFROM tasks t\nJOIN task_open_files tof ON tof.task = t.virtual_address\nWHERE t.comm LIKE '%systemd%'\nLIMIT 10;\ncomm:\"systemd\" path:\"/null\"\ncomm:\"systemd\" path:\"/null\"\ncomm:\"systemd\" path:\"/null\"\ncomm:\"systemd\" path:\"/kmsg\"\ncomm:\"systemd\" path:\"[eventpoll]\"\ncomm:\"systemd\" path:\"[signalfd]\"\ncomm:\"systemd\" path:\"inotify\"\ncomm:\"systemd\" path:\"/\"\ncomm:\"systemd\" path:\"[timerfd]\"\ncomm:\"systemd\" path:\"/usr/lib/systemd/systemd-executor\"\n```\n\n#### Command-line query examples\n\n##### JSON output (default)\n\n```bash\n$ mquire query --format=json ubuntu2404_6.14.0-37-generic.lime \"SELECT * FROM os_version\"\n[\n  {\n    \"arch\": \"x86_64\",\n    \"kernel_version\": \"6.14.0-37-generic\",\n    \"system_version\": \"#37~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov 20 10:25:38 UTC 2\"\n  }\n]\n```\n\n##### Table output\n\n```bash\n$ mquire query --format=table ubuntu2404_6.14.0-37-generic.lime \"SELECT * FROM os_version\"\narch:\"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\"\n```\n\n#### Custom command examples\n\n##### List available commands\n\n```bash\n$ mquire command ubuntu2404_6.14.0-37-generic.lime\nAvailable commands:\n  .carve               Carve a region of virtual memory to disk\n  .dump                Dump all open files from tasks to disk\n  .system_version      Display the operating system version\n  .task_tree           Display a hierarchical task tree\n```\n\n##### Display system version\n\n```bash\n$ mquire command ubuntu2404_6.14.0-37-generic.lime \".system_version\"\nSystem Version: #37~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov 20 10:25:38 UTC 2\nKernel Version: 6.14.0-37-generic\nArchitecture: x86_64\n```\n\n##### Display process tree\n\n```bash\n$ mquire command ubuntu2404_6.8.0-63-generic.lime .task_tree | head -n 10\nParent: task_struct::parent\nThreads: Disabled\nPage Table: paddr(0x0000000001a60000)\n\n└─ [0] (ffffffff90c0fcc0) swapper/0\n   ╎   ↳ [0] (ffff982a00e33518) \\xef\\xbf\\xbd\\xef\\xbf\\xbd\\xef\\xbf\\xbd,)\\xef\\xbf\\xbd\\xef\\xbf\\xbd\\xef\\xbf\\xbd\\x0e\n   ├─ [1] (ffff982a0084a8c0) systemd\n   │  ├─ [430] (ffff982a0d27a8c0) systemd-journal\n   │  ├─ [495] (ffff982a08a88000) systemd-udevd\n   │  ├─ [786] (ffff982a07a60000) systemd-oomd\n```\n\n**Note:** When multiple `task_struct` entries exist with the same TID (Thread ID, which can occur due to memory corruption or snapshot timing), duplicate entries are displayed with the continuation symbol `╎   ↳` indented under the primary entry. The format is `[TGID] (virtual_address) name` when threads are hidden, or `[TGID TID]` when showing threads (where TGID is the Thread Group ID, commonly known as PID).\n\n#### Extract files from memory\n\n```bash\n$ mquire command ubuntu2404_6.14.0-37-generic.lime \".dump ./extracted_files\"\nLegend: SK = skipped, OK = all good, ER = errored\n\nSummary:\n  Total files processed: 1234\n  Successfully dumped: 1156\n  Skipped: 45\n  Errors: 33\n\nFile Status:\n  OK /usr/lib/systemd/systemd (TGID 1)\n  OK /etc/passwd (TGID 1)\n  SK /dev/null (TGID 1)\n  ...\n```\n\n## Troubleshooting\n\nUse the `--debug` flag to enable verbose debug messages during initialization and analysis:\n\n```bash\nmquire --debug command /path/to/memory.raw \".system_version\"\n```\n\n- **`shell` and `query` modes**: Debug messages are stored in the `mquire_diagnostics` SQL table. Query them with `SELECT * FROM mquire_diagnostics;`\n- **`command` mode**: Debug messages are printed directly to stderr.\n\nFor initialization issues that prevent mquire from successfully loading the snapshot, using `command` mode with a simple command like `.system_version` is recommended, as it prints debug output to stderr immediately without needing to query the `mquire_diagnostics` table.\n\n## Configuration\n\nmquire can be configured via a TOML file at `$HOME/.config/trailofbits/mquire/config.toml`. If the file does not exist, default values are used.\n\n### Available options and default values\n\n```toml\n[database]\n# Maximum number of entries retained in the mquire_diagnostics table.\n# During initialization, all entries are kept regardless of this limit.\n# Once initialization completes, new log entries trigger eviction of the\n# oldest entries when the total exceeds this value.\nmquire_diagnostics_max_entries = 1000\n```\n\n## Development\n\nThis project uses [just](https://github.com/casey/just) as a command runner. Run `just` to see available commands:\n\n| Command | Description |\n|---------|-------------|\n| `just check` | Run all checks (cargo check, cargo clippy, cargo fmt, ruff, mypy) |\n| `just test` | Run unit tests |\n| `just format` | Format code (cargo fmt, ruff) |\n| `just integration-test` | Run SQL query integration tests |\n| `just integration-update` | Update expected test output |\n| `just package` | Build release packages |\n\n### SQL query integration tests\n\nThese tests verify mquire produces correct output for queries against memory snapshots.\n\n- **`just integration-test`** - Run tests and compare output to expected JSON files\n- **`just integration-update`** - Update expected JSON files with actual output (use when changing table schemas)\n\nAfter running `integration-update`, review the git diff to ensure changes match your expectations before committing.\n\n**Adding new tests:** Create a `.sql` file and matching `.json` file in the appropriate snapshot directory, then run `just integration-update` to populate the expected output.\n\n## Contributing\n\nContributions are welcome! When contributing, please follow these guidelines:\n\n1. **Test your changes** - Make sure your changes work correctly before submitting\n2. **Keep dependencies minimal** - Only add new dependencies when absolutely necessary\n3. **Avoid caching volatile data** - Do not cache values that could move or change within kernel objects. Only cache stable references like:\n   - Kallsyms location\n   - `init_task` virtual address\n   - BTF data\n\n## License\n\nThis project is licensed under the Apache License 2.0. See the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrailofbits%2Fmquire","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftrailofbits%2Fmquire","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrailofbits%2Fmquire/lists"}