
Volatility 3 を Rust に移植。同じ出力で、はるかに高速。
Volatility 3 メモリフォレンジックフレームワークを Python から Rust へ移植したものです。同じメモリイメージを読み込み、同じオプションで同じプラグインを実行し、同じ出力をバイト単位でそのまま出力しますが、かなり高速です。
このプロジェクトは Volatility 3 の移植版であるため、Volatility Software License 1.0 における「Addition」に該当し、同じライセンスの下で公開されています。全文は LICENSE に、またオンラインでは https://www.volatilityfoundation.org/license/vsl-v1.0 にあります。各ソースファイルには、何から派生したかを記録した通知が保持されています。
このプロジェクトは Volatility Foundation とは提携しておらず、承認も受けていません。Volatility という名称は、これが何の移植版であるかを示すためにのみ使用しています。
Rust 1.85 以降が必要です。その後:
cargo build --release
バイナリは target/release/vol-rs に生成されます。オプションの依存関係は一切不要です。シンボルの展開、RC4、DES、AES、YARA マッチング、PNG 出力、tar アーカイブはすべて組み込まれており、Python 版では pycryptodomex、yara-python、pillow を必要とします。命令のデコードは capstone 5.0.6 によって行われ、これは Python 版が同じカラムに対して使用しているのと同じライブラリであり、別途インストールされるのではなくコンパイル時に組み込まれています。
./target/release/vol-rs -f memory.raw windows.pslist.PsList
./target/release/vol-rs -f memory.lime linux.bash.Bash
./target/release/vol-rs -f memory.raw -r csv windows.netscan.NetScan
./target/release/vol-rs windows.malfind.Malfind --help
./target/release/vol-rs --list-plugins
Python版が受け付けるすべてのオプションは、ここでも同じ名前・同じデフォルト値で受け付けます。-o、-r、-s、-c、-e、--filters、--hide-columns、--save-config、--stackers、--single-swap-locations を含みます。各プラグイン固有のオプションも同様に一致しており、ヘルプページに示されています。197個すべてがPython版と1文字違わず同一です。
シンボルパックは $XDG_DATA_HOME/vol-rs/symbols に置きます。この変数が設定されていない場合は ~/.local/share/vol-rs/symbols です。別の場所にあるパックは --symbol-dirs または環境変数 VOLRS_SYMBOL_PATH で指定します。どのパックにも記述されていないWindowsカーネルはMicrosoftシンボルサーバーで検索され、そこで見つかったデータベースはシンボルファイルに変換されて保存されるため、取得は一度だけ行われます。データベース自体は ~/.cache/vol-rs 以下にキャッシュされ、シンボルファイルはパックの隣に書き出されます。
両方を同じキャプチャに対して実行し、ヘッダーのフレーミングや末尾の改行も含めて差分を比較しました:
| キャプチャ | 実行プラグイン数 | 一致 |
|---|---|---|
| Windows 11 24H2、ビルド26100、4.3 GBクラッシュダンプ | 113 | 105 |
| Ubuntu 22.04、カーネル6.5.0-41、4.3 GB VMwareスナップショット | 69 | 61 |
| Windows 10 19041、2.0 GBクラッシュダンプ | 114 | 112 |
| Windows XP、512 MB生キャプチャ | 114 | 113 |
| Linux 3.2、512 MB生キャプチャ | 60 | 60 |
パターンやルールを受け取る8つのプラグインには、除外せずに両側で同一のものを与えました。その方法で見つかった差異のうち3つはこの移植版の欠陥であり、修正済みです。
| その他の検証 | 結果 |
|---|---|
| 抽出系プラグインが書き出したファイル | 24,441件を比較し、すべて一致 |
| プラグインのヘルプページ | 197件中197件が同一 |
| レンダラー | csv、json、jsonl、quick、pretty を複数のプラグインで比較し、すべて一致 |
命令のデコードにはPython版が使用するのと同じライブラリを組み込み、バージョン5.0.6を使用しています。これはこれらの比較で参照実装を固定したバージョンです。バージョン5.0.7ではさらに1つの命令形式がデコードされ、これによりWindows 10キャプチャにおける windows.mbrscan の行の約2パーセントの表記が変わり、それ以外は変わりません。
ここで使用したキャプチャのWindowsシンボルは、Volatility Foundationが公開しているシンボルパックから取得しており、両ツールが読み込みます。ただしWindows 11 24H2カーネルはどのパックにも記述がなく、Microsoftが公開しているデータベースから記述を構築しています。以前のWindows 10 19045キャプチャでは、この移植版が同じデータベースからその方法で構築した記述が、Python版が構築したものと16個の基本型、293個の列挙、1,653個の構造体、40,007個のシンボルすべてにわたって完全に一致しました。
各キャプチャで最も差の大きい10件です。すべての数値と比較したすべてのプラグインは BENCHMARKS.md にあります。
| プラグイン | Rust | Python | 倍率 |
|---|---|---|---|
windows.malfind.Malfind | 0.494 s | 392 s | x793 |
windows.malware.suspicious_threads.SuspiciousThreads | 0.621 s | 488 s | x786 |
windows.malware.malfind.Malfind | 0.569 s | 393 s | x691 |
windows.suspicious_threads.SuspiciousThreads | 0.739 s | 461 s | x624 |
windows.hollowprocesses.HollowProcesses | 0.646 s | 326 s | x504 |
windows.malware.hollowprocesses.HollowProcesses | 0.661 s | 322 s | x487 |
layerwriter.LayerWriter | 0.044 s | 17.4 s | x395 |
configwriter.ConfigWriter | 0.046 s | 16.7 s | x363 |
windows.vadinfo.VadInfo | 1.36 s | 488 s | x358 |
windows.verinfo.VerInfo | 0.541 s | 159 s | x294 |
| プラグイン | Rust | Python | 高速化倍率 |
|---|---|---|---|
linux.sockstat.Sockstat | 6.36 s | 2030 s | x319 |
timeliner.Timeliner | 9.56 s | 1070 s | x112 |
linux.lsmod.Lsmod | 0.282 s | 29.0 s | x103 |
linux.psscan.PsScan | 1.09 s | 111 s | x102 |
linux.mountinfo.MountInfo | 0.697 s | 70.3 s | x101 |
yarascan.YaraScan | 0.909 s | 79.6 s | x88 |
linux.malware.netfilter.Netfilter | 0.6 s | 49.9 s | x83 |
linux.ptrace.Ptrace | 0.253 s | 20.4 s | x81 |
linux.pstree.PsTree | 0.213 s | 16.2 s | x76 |
linux.ip.Link | 0.22 s | 16.4 s | x74 |
| プラグイン | Rust | Python | 高速化倍率 |
|---|---|---|---|
windows.malware.suspicious_threads.SuspiciousThreads | 0.677 s | 346 s | x511 |
windows.suspicious_threads.SuspiciousThreads | 0.677 s | 343 s | x507 |
windows.malfind.Malfind | 0.734 s | 320 s | x436 |
windows.hollowprocesses.HollowProcesses | 0.565 s | 244 s | x432 |
windows.malware.malfind.Malfind | 0.73 s | 313 s | x429 |
windows.vadinfo.VadInfo | 0.983 s | 412 s | x419 |
windows.malware.hollowprocesses.HollowProcesses | 0.648 s | 240 s | x370 |
configwriter.ConfigWriter | 0.043 s | 14.5 s | x337 |
layerwriter.LayerWriter | 0.039 s | 11.9 s | x304 |
windows.registry.hivelist.HiveList | 0.038 s | 7.54 s | x198 |
| プラグイン | Rust | Python | 高速化倍率 |
|---|---|---|---|
windows.suspicious_threads.SuspiciousThreads | 0.184 s | 34.5 s | x188 |
windows.malware.suspicious_threads.SuspiciousThreads | 0.2 s | 33.2 s | x166 |
windows.malware.hollowprocesses.HollowProcesses | 0.209 s | 31.0 s | x148 |
windows.vadinfo.VadInfo | 0.263 s | 38.5 s | x146 |
windows.hollowprocesses.HollowProcesses | 0.207 s | 28.1 s | x136 |
windows.mftscan.ResidentData | 0.205 s | 27.6 s | x135 |
windows.malfind.Malfind | 0.203 s | 26.3 s | x130 |
windows.mftscan.MFTScan | 0.393 s | 48.2 s | x123 |
windows.malware.malfind.Malfind | 0.24 s | 28.2 s | x118 |
windows.verinfo.VerInfo | 0.215 s | 25.1 s | x117 |
| プラグイン | Rust | Python | 高速化倍率 |
|---|---|---|---|
linux.library_list.LibraryList | 0.525 s | 917 s | x1747 |
linux.pscallstack.PsCallStack | 0.31 s | 98.8 s | x319 |
linux.lsmod.Lsmod | 0.059 s | 9.58 s | x162 |
linux.malware.check_idt.Check_idt | 0.11 s | 15.2 s | x138 |
linux.check_idt.Check_idt | 0.115 s | 15.1 s | x131 |
linux.tty_check.tty_check | 0.114 s | 14.3 s | x125 |
linux.malware.tty_check.Tty_Check | 0.129 s | 15.0 s | x116 |
linux.malware.check_modules.Check_modules | 0.055 s | 5.74 s | x104 |
linux.kallsyms.Kallsyms | 0.278 s | 28.9 s | x104 |
linux.capabilities.Capabilities | 0.056 s | 5.82 s | x104 |
これらは意図的なものであり、変更されることはありません:
--save-config と timeliner --record-config は、イメージを記述するすべての設定を記録しますが、Python 版が行う「インポートされたコンポーネントが十分に新しいか」というチェックは記録しません。このポートにはそのようなチェックを記録する機能がないためです。frameworkinfo と isfinfo はイメージではなくツール自体を記述するため、このポート自身のレイヤー、プラグイン、シンボルファイルを報告します。--help 内でキャッシュディレクトリを示す行は、このポート自身のキャッシュを指します。windows.callbacks は、解析できないシャットダウン通知を省略します。64 ビットイメージでは Python 版はそれらに対して一切チェックを行わないため、Windows 11 のキャプチャで 2 件のエントリを報告しますが、これはコールバックではなく ntoskrnl 自身の命令バイトであり、未解析としてマークされます。ここにあるものは一致しません。なぜなら、同じツールであっても出力が 2 回同じになることはないからです:
linux.pagecache.RecoverFs は、書き出す tarball 内のすべてのファイルにプラグインが実行された瞬間のタイムスタンプを付けるため、2 回の実行で一致することはなく、Python での 2 回の実行でも一致しません。テーブルも展開後のツリーも一致します。linux.mountinfo --mount-format は Python の set を結合するため、2 回の Python 実行間で列の順序が変わります。この移植版では、マウントオプションを先に、ファイルシステムオプションを後に、カーネルが保持している順序で一覧表示します。timeliner は、各プラグインの後にタイムラインを再度追加します。また、プラグインが実行される順序は os.walk がプラグインディレクトリを返した順序であり、これはリリースではなくインストール環境に依存する性質です。同じバージョンの Python を 2 つインストールすると、同じイメージに対して異なる合計を報告します。この移植版は固定のリストを実行し、ext4 上の標準インストールが生成する順序に従います。windows.memmap.Memmap は、途中で out of memory killer によって強制終了されます。どこで停止するかはその時点の空きメモリに依存するため、2 回の Python 実行も互いに一致しません。Windows XP のキャプチャでは両方とも完了し、出力は一致します。これらが異なるのは、Python 版が途中で終了し、この移植版は終了しないためです。Python 版が停止する前に書き込んだすべての行は正確に再現され、残りは Python 版が到達しなかった部分です: