
Volatility 3를 Rust로 포팅했습니다. 동일한 출력, 훨씬 더 빠른 속도.
Python으로 작성된 Volatility 3 메모리 포렌식 프레임워크를 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 버전과 문자 하나까지 동일합니다.
심볼 팩은 $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 |
패턴이나 규칙을 받는 여덟 개의 플러그인은 제외하지 않고 양쪽에 동일하게 하나씩 지정했습니다. 그렇게 찾은 차이 중 세 가지는 이 포팅의 결함이었으며 수정되었습니다.
| 기타 검사 | 결과 |
|---|---|
| 추출 플러그인이 기록한 파일 | 24,441개 비교, 전부 동일 |
| 플러그인 도움말 페이지 | 197개 중 197개 동일 |
| 렌더러 | csv, json, jsonl, quick, pretty를 여러 플러그인에서 비교, 전부 동일 |
명령어는 Python 버전이 사용하는 것과 동일한 라이브러리로 디코딩되며, 버전 5.0.6으로 컴파일되어 내장되었습니다. 이는 이 비교들을 위해 참조가 고정된 버전입니다. 버전 5.0.7은 명령어 형식을 하나 더 디코딩하는데, 이로 인해 Windows 10 캡처에서 windows.mbrscan 줄의 약 2퍼센트가 표기되는 방식이 달라지며 그 외에는 아무것도 달라지지 않습니다.
여기 사용된 캡처의 Windows 심볼은 Volatility Foundation이 배포하는 심볼 팩에서 가져오며, 두 도구 모두 이를 읽습니다. 단, Windows 11 24H2 커널은 어떤 팩도 설명하지 않으므로 Microsoft가 배포하는 데이터베이스로부터 설명을 구성합니다. 이전 Windows 10 19045 캡처의 경우, 이 포팅이 같은 데이터베이스로부터 그렇게 구성한 설명이 Python 버전이 구성한 것과 16개 기본 타입, 293개 열거형, 1,653개 구조체, 40,007개 심볼 전반에 걸쳐 정확히 일치했습니다.
각 캡처에서 가장 큰 격차 열 개입니다. 모든 수치와 비교한 모든 플러그인은 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 캡처에서 콜백이 아닌 ntoskrnl 자체의 명령 바이트인 두 항목을 파싱되지 않음으로 표시하여 보고합니다.여기서는 어떤 것도 일치할 수 없습니다. 단일 도구에서도 출력이 매번 동일하지 않기 때문입니다:
linux.pagecache.RecoverFs는 작성하는 tarball의 모든 파일에 플러그인이 실행된 시점을 기록하므로, 두 번의 실행이 일치하지 않으며 심지어 두 번의 Python 실행조차 일치하지 않습니다. 테이블도 일치하고 압축 해제된 트리도 일치합니다.linux.mountinfo --mount-format은 Python set을 조인하므로 두 번의 Python 실행 사이에 컬럼 순서가 바뀝니다. 이 포트는 마운트 옵션을 먼저 나열하고 파일시스템 옵션을 그 뒤에 나열하며, 커널이 보유한 순서를 따릅니다.timeliner는 모든 플러그인 이후에 타임라인을 다시 추가하며, 플러그인이 실행되는 순서는 os.walk가 플러그인 디렉터리를 반환한 순서인데, 이는 릴리스가 아니라 설치의 속성입니다. 같은 버전의 두 Python 설치가 같은 이미지에서 서로 다른 총계를 보고합니다. 이 포트는 ext4에서 표준 설치가 생성하는 순서인 고정된 목록을 실행합니다.windows.memmap.Memmap은 도중에 out of memory killer에 의해 종료되며, 어디서 멈추는지는 당시의 여유 메모리에 따라 달라지므로 두 번의 Python 실행도 서로 일치하지 않습니다. Windows XP 캡처에서는 둘 다 완료되고 출력이 일치합니다.이들은 Python 버전이 일찍 종료되고 이 포트는 그렇지 않기 때문에 다릅니다. Python 버전이 중단되기 전에 작성한 모든 줄은 정확히 재현되며, 나머지는 도달하지 못한 부분입니다: