mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-10 03:37:39 +02:00
Merge pull request #789 from fgomulka/actions-testing-framework
Add testing framework
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
name: Test Volatility3
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python 3.6
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.6'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install Cmake
|
||||
pip install setuptools wheel
|
||||
pip install -U pytest
|
||||
pip install -r ./test/requirements-testing.txt
|
||||
|
||||
- name: Build PyPi packages
|
||||
run: |
|
||||
python setup.py sdist --formats=gztar,zip
|
||||
python setup.py bdist_wheel
|
||||
|
||||
- name: Download images
|
||||
run: |
|
||||
curl -sLO "https://downloads.volatilityfoundation.org/volatility3/images/linux-sample-1.bin.gz"
|
||||
gunzip linux-sample-1.bin.gz
|
||||
curl -sLO "https://downloads.volatilityfoundation.org/volatility3/images/win-xp-laptop-2005-06-25.img.gz"
|
||||
gunzip win-xp-laptop-2005-06-25.img.gz
|
||||
|
||||
- name: Download and Extract symbols
|
||||
run: |
|
||||
cd ./volatility3/symbols
|
||||
curl -sLO https://downloads.volatilityfoundation.org/volatility3/symbols/linux.zip
|
||||
unzip linux.zip
|
||||
cd -
|
||||
|
||||
- name: Testing...
|
||||
run: |
|
||||
py.test ./test/test_volatility.py --volatility=vol.py --image win-xp-laptop-2005-06-25.img -k test_windows -v
|
||||
py.test ./test/test_volatility.py --volatility=vol.py --image linux-sample-1.bin -k test_linux -v
|
||||
|
||||
- name: Clean up post-test
|
||||
run: |
|
||||
rm -rf *.lime
|
||||
rm -rf *.img
|
||||
cd volatility3/symbols
|
||||
rm -rf linux
|
||||
rm -rf linux.zip
|
||||
cd -
|
||||
@@ -0,0 +1,34 @@
|
||||
# Volatility 3 Testing Framework
|
||||
|
||||
## Requirements
|
||||
|
||||
The Volatility 3 Testing Framework requires the same version of Python as Volatility3 itself. To install the current set of dependencies that the framework requires, use a command like this:
|
||||
|
||||
```shell
|
||||
pip3 install -r requirements-testing.txt
|
||||
```
|
||||
|
||||
NOTE: `requirements-testing.txt` can be found in this current `test/` directory.
|
||||
|
||||
## Quick Start: Manual Testing
|
||||
|
||||
1. To test Volatility 3 on an image, first download one with a command such as:
|
||||
|
||||
```shell
|
||||
curl -sLO "https://downloads.volatilityfoundation.org/volatility3/images/win-xp-laptop-2005-06-25.img.gz"
|
||||
gunzip win-xp-laptop-2005-06-25.img.gz
|
||||
```
|
||||
|
||||
2. In many cases, more symbols are required to be downloaded to the `./volatility3/symbols` directory.
|
||||
|
||||
3. To manually run the tests, run a command, such as:
|
||||
|
||||
```shell
|
||||
py.test ./test/test_volatility.py --volatility=vol.py --image win-xp-laptop-2005-06-25.img -k test_windows
|
||||
```
|
||||
|
||||
The above command runs all available tests for windows on the `win-xp-laptop-2005-06-25.img` image. To choose a more specific set of tests, change the phrase after `-k` in this command.
|
||||
|
||||
## Github Actions
|
||||
|
||||
This framework currently tests two images (one linux image and one windows image) after every push on any branch. For more information/context, find the actions setup in `./github/workflows/test.yaml`
|
||||
@@ -0,0 +1,40 @@
|
||||
# This file is used to augment the test configuration
|
||||
|
||||
import os
|
||||
import pytest
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--volatility", action="store", default=None,
|
||||
required=True,
|
||||
help="path to the volatility script")
|
||||
|
||||
parser.addoption("--python", action="store", default="python3",
|
||||
help="The name of the interpreter to use when running the volatility script")
|
||||
|
||||
parser.addoption("--image", action="append", default=[],
|
||||
help="path to an image to test")
|
||||
|
||||
parser.addoption("--image-dir", action="append", default=[],
|
||||
help="path to a directory containing images to test")
|
||||
|
||||
def pytest_generate_tests(metafunc):
|
||||
"""Parameterize tests based on image names"""
|
||||
|
||||
images = metafunc.config.getoption('image')
|
||||
for image_dir in metafunc.config.getoption('image_dir'):
|
||||
images = images + [os.path.join(image_dir, dir) for dir in os.listdir(image_dir)]
|
||||
|
||||
# tests with "image" parameter are run against images
|
||||
if 'image' in metafunc.fixturenames:
|
||||
metafunc.parametrize("image",
|
||||
images,
|
||||
ids=[os.path.basename(image) for image in images])
|
||||
|
||||
# Fixtures
|
||||
@pytest.fixture
|
||||
def volatility(request):
|
||||
return request.config.getoption("--volatility")
|
||||
|
||||
@pytest.fixture
|
||||
def python(request):
|
||||
return request.config.getoption("--python")
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"windows_dumpfiles": {
|
||||
"win-xp-laptop-2005-06-25.img": {
|
||||
"0x82220e78": [
|
||||
"9bdd5532286f1660f3778e68bc36efe6",
|
||||
"e3bc1e9e7370e3b5a661ebe591ecf4ec"
|
||||
],
|
||||
"0x82350bf8": [
|
||||
"e5c5e8d97b6280745b41f6572c85d1f0",
|
||||
"8589f1463422884dbf1411aaad278465"
|
||||
],
|
||||
"0x81eaf418": [
|
||||
"f7a1ae2060a58f8470b97affdb46dccf",
|
||||
"54fd611021fa784912530b8007545986"
|
||||
],
|
||||
"0x820588e8": "458efbc8fdb859488a6ab2b200cce809"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# These packages are required for core functionality.
|
||||
pefile>=2017.8.1 #foo
|
||||
|
||||
# The following packages are optional.
|
||||
# If certain packages are not necessary, place a comment (#) at the start of the line.
|
||||
|
||||
# This is required for the yara plugins
|
||||
yara-python>=3.8.0
|
||||
@@ -0,0 +1,380 @@
|
||||
# volatility3 tests
|
||||
#
|
||||
|
||||
#
|
||||
# IMPORTS
|
||||
#
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import shutil
|
||||
import tempfile
|
||||
import hashlib
|
||||
import ntpath
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
#
|
||||
# HELPER FUNCTIONS
|
||||
#
|
||||
|
||||
def runvol(args, volatility, python):
|
||||
volpy = volatility
|
||||
python_cmd = python
|
||||
|
||||
cmd = [python_cmd, volpy] + args
|
||||
print(" ".join(cmd))
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
stdout, stderr = p.communicate()
|
||||
print("stdout:")
|
||||
sys.stdout.write(str(stdout))
|
||||
print("")
|
||||
print("stderr:")
|
||||
sys.stdout.write(str(stderr))
|
||||
print("")
|
||||
|
||||
return p.returncode, stdout, stderr
|
||||
|
||||
def runvol_plugin(plugin, img, volatility, python, pluginargs=[], globalargs=[]):
|
||||
args = globalargs + [
|
||||
"--single-location",
|
||||
img,
|
||||
"-q",
|
||||
plugin,
|
||||
] + pluginargs
|
||||
|
||||
return runvol(args, volatility, python)
|
||||
|
||||
#
|
||||
# TESTS
|
||||
#
|
||||
|
||||
# WINDOWS
|
||||
|
||||
def test_windows_pslist(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("windows.pslist.PsList", image, volatility, python)
|
||||
out = out.lower()
|
||||
assert out.find(b"system") != -1
|
||||
assert out.find(b"csrss.exe") != -1
|
||||
assert out.find(b"svchost.exe") != -1
|
||||
assert out.count(b"\n") > 10
|
||||
assert rc == 0
|
||||
assert rc == 0
|
||||
|
||||
rc, out, err = runvol_plugin(
|
||||
"windows.pslist.PsList", image, volatility, python, pluginargs=["--pid", "4"])
|
||||
out = out.lower()
|
||||
assert out.find(b"system") != -1
|
||||
assert out.count(b"\n") < 10
|
||||
assert rc == 0
|
||||
assert rc == 0
|
||||
|
||||
def test_windows_psscan(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("windows.psscan.PsScan", image, volatility, python)
|
||||
out = out.lower()
|
||||
assert out.find(b"system") != -1
|
||||
assert out.find(b"csrss.exe") != -1
|
||||
assert out.find(b"svchost.exe") != -1
|
||||
assert out.count(b"\n") > 10
|
||||
assert rc == 0
|
||||
assert rc == 0
|
||||
|
||||
def test_windows_dlllist(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("windows.dlllist.DllList", image, volatility, python)
|
||||
out = out.lower()
|
||||
assert out.count(b"\n") > 10
|
||||
assert rc == 0
|
||||
assert rc == 0
|
||||
|
||||
def test_windows_modules(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("windows.modules.Modules", image, volatility, python)
|
||||
out = out.lower()
|
||||
assert out.count(b"\n") > 10
|
||||
assert rc == 0
|
||||
assert rc == 0
|
||||
|
||||
def test_windows_hivelist(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("windows.registry.hivelist.HiveList", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
not_xp = out.find(b"\\systemroot\\system32\\config\\software")
|
||||
if not_xp == -1:
|
||||
assert out.find(b"\\device\\harddiskvolume1\\windows\\system32\\config\\software") != -1
|
||||
|
||||
assert out.count(b"\n") > 10
|
||||
assert rc == 0
|
||||
|
||||
def test_windows_dumpfiles(image, volatility, python):
|
||||
|
||||
json_file = open('./test/known_files.json')
|
||||
|
||||
known_files = json.load(json_file)
|
||||
|
||||
failed_chksms = 0
|
||||
|
||||
if sys.platform == 'win32':
|
||||
file_name = ntpath.basename(image)
|
||||
else:
|
||||
file_name = os.path.basename(image)
|
||||
|
||||
try:
|
||||
for addr in known_files["windows_dumpfiles"][file_name]:
|
||||
|
||||
path = tempfile.mkdtemp()
|
||||
|
||||
rc, out, err = runvol_plugin("windows.dumpfiles.DumpFiles", image, volatility, python, globalargs=["-o", path], pluginargs=["--virtaddr", addr])
|
||||
|
||||
for file in os.listdir(path):
|
||||
with open(os.path.join(path, file), "rb") as fp:
|
||||
if hashlib.md5(fp.read()).hexdigest() not in known_files["windows_dumpfiles"][file_name][addr]:
|
||||
failed_chksms += 1
|
||||
|
||||
shutil.rmtree(path)
|
||||
|
||||
json_file.close()
|
||||
|
||||
assert failed_chksms == 0
|
||||
assert rc == 0
|
||||
except Exception as e:
|
||||
json_file.close()
|
||||
print("Key Error raised on " + str(e))
|
||||
assert False
|
||||
|
||||
def test_windows_handles(image, volatility, python):
|
||||
rc, out, err = runvol_plugin(
|
||||
"windows.handles.Handles", image, volatility, python, pluginargs=["--pid", "4"])
|
||||
|
||||
assert out.find(b"System Pid 4") != -1
|
||||
assert out.find(b"MACHINE\\SYSTEM\\CONTROLSET001\\CONTROL\\SESSION MANAGER\\MEMORY MANAGEMENT\\PREFETCHPARAMETERS") != -1
|
||||
assert out.find(b"MACHINE\\SYSTEM\\SETUP") != -1
|
||||
assert out.count(b"\n") > 500
|
||||
assert rc == 0
|
||||
|
||||
def test_windows_svcscan(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("windows.svcscan.SvcScan", image, volatility, python)
|
||||
|
||||
assert out.find(b"Microsoft ACPI Driver") != -1
|
||||
assert out.count(b"\n") > 250
|
||||
assert rc == 0
|
||||
|
||||
def test_windows_privileges(image, volatility, python):
|
||||
rc, out, err = runvol_plugin(
|
||||
"windows.privileges.Privs", image, volatility, python, pluginargs=["--pid", "4"])
|
||||
|
||||
assert out.find(b"SeCreateTokenPrivilege") != -1
|
||||
assert out.find(b"SeCreateGlobalPrivilege") != -1
|
||||
assert out.find(b"SeAssignPrimaryTokenPrivilege") != -1
|
||||
assert out.count(b"\n") > 20
|
||||
assert rc == 0
|
||||
|
||||
def test_windows_getsids(image, volatility, python):
|
||||
rc, out, err = runvol_plugin(
|
||||
"windows.getsids.GetSIDs", image, volatility, python, pluginargs=["--pid", "4"])
|
||||
|
||||
assert out.find(b"Local System") != -1
|
||||
assert out.find(b"Administrators") != -1
|
||||
assert out.find(b"Everyone") != -1
|
||||
assert out.find(b"Authenticated Users") != -1
|
||||
assert rc == 0
|
||||
|
||||
def test_windows_envars(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("windows.envars.Envars", image, volatility, python)
|
||||
|
||||
assert out.find(b"PATH") != -1
|
||||
assert out.find(b"PROCESSOR_ARCHITECTURE") != -1
|
||||
assert out.find(b"USERNAME") != -1
|
||||
assert out.find(b"SystemRoot") != -1
|
||||
assert out.find(b"CommonProgramFiles") != -1
|
||||
assert out.count(b"\n") > 500
|
||||
assert rc == 0
|
||||
|
||||
def test_windows_callbacks(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("windows.callbacks.Callbacks", image, volatility, python)
|
||||
|
||||
assert out.find(b"PspCreateProcessNotifyRoutine") != -1
|
||||
assert out.find(b"KeBugCheckCallbackListHead") != -1
|
||||
assert out.find(b"KeBugCheckReasonCallbackListHead") != -1
|
||||
assert out.count(b"KeBugCheckReasonCallbackListHead ") > 5
|
||||
assert rc == 0
|
||||
|
||||
# LINUX
|
||||
|
||||
def test_linux_pslist(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("linux.pslist.PsList", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
assert ((out.find(b"init") != -1) or (out.find(b"systemd") != -1))
|
||||
assert out.find(b"watchdog") != -1
|
||||
assert out.count(b"\n") > 10
|
||||
assert rc == 0
|
||||
|
||||
def test_linux_check_idt(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("linux.check_idt.Check_idt", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
assert out.count(b"__kernel__") >= 10
|
||||
assert out.count(b"\n") > 10
|
||||
assert rc == 0
|
||||
|
||||
def test_linux_check_syscall(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("linux.check_syscall.Check_syscall", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
assert out.find(b"sys_close") != -1
|
||||
assert out.find(b"sys_open") != -1
|
||||
assert out.count(b"\n") > 100
|
||||
assert rc == 0
|
||||
|
||||
def test_linux_lsmod(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("linux.lsmod.Lsmod", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
assert out.count(b"\n") > 10
|
||||
assert rc == 0
|
||||
|
||||
def test_linux_lsof(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("linux.lsof.Lsof", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
assert out.count(b"socket:") >= 10
|
||||
assert out.count(b"\n") > 35
|
||||
assert rc == 0
|
||||
|
||||
def test_linux_proc_maps(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("linux.proc.Maps", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
assert out.count(b"anonymous mapping") >= 10
|
||||
assert out.count(b"\n") > 100
|
||||
assert rc == 0
|
||||
|
||||
def test_linux_tty_check(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("linux.tty_check.tty_check", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
assert out.find(b"__kernel__") != -1
|
||||
assert out.count(b"\n") >= 5
|
||||
assert rc == 0
|
||||
|
||||
# MAC
|
||||
|
||||
def test_mac_pslist(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("mac.pslist.PsList", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
assert ((out.find(b"kernel_task") != -1) or (out.find(b"launchd") != -1))
|
||||
assert out.count(b"\n") > 10
|
||||
assert rc == 0
|
||||
|
||||
def test_mac_check_syscall(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("mac.check_syscall.Check_syscall", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
assert out.find(b"chmod") != -1
|
||||
assert out.find(b"chown") != -1
|
||||
assert out.find(b"nosys") != -1
|
||||
assert out.count(b"\n") > 100
|
||||
assert rc == 0
|
||||
|
||||
def test_mac_check_sysctl(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("mac.check_sysctl.Check_sysctl", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
assert out.find(b"__kernel__") != -1
|
||||
assert out.count(b"\n") > 250
|
||||
assert rc == 0
|
||||
|
||||
def test_mac_check_trap_table(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("mac.check_trap_table.Check_trap_table", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
assert out.count(b"kern_invalid") >= 10
|
||||
assert out.count(b"\n") > 50
|
||||
assert rc == 0
|
||||
|
||||
def test_mac_ifconfig(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("mac.ifconfig.Ifconfig", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
assert out.find(b"127.0.0.1") != -1
|
||||
assert out.find(b"false") != -1
|
||||
assert out.count(b"\n") > 9
|
||||
assert rc == 0
|
||||
|
||||
def test_mac_lsmod(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("mac.lsmod.Lsmod", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
assert out.find(b"com.apple") != -1
|
||||
assert out.count(b"\n") > 10
|
||||
assert rc == 0
|
||||
|
||||
def test_mac_lsof(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("mac.lsof.Lsof", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
assert out.count(b"\n") > 50
|
||||
assert rc == 0
|
||||
|
||||
def test_mac_malfind(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("mac.malfind.Malfind", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
assert out.count(b"\n") > 20
|
||||
assert rc == 0
|
||||
|
||||
def test_mac_mount(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("mac.mount.Mount", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
assert out.find(b"/dev") != -1
|
||||
assert out.count(b"\n") > 7
|
||||
assert rc == 0
|
||||
|
||||
def test_mac_netstat(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("mac.netstat.Netstat", image, volatility, python)
|
||||
|
||||
assert out.find(b"TCP") != -1
|
||||
assert out.find(b"UDP") != -1
|
||||
assert out.find(b"UNIX") != -1
|
||||
assert out.count(b"\n") > 10
|
||||
assert rc == 0
|
||||
|
||||
def test_mac_proc_maps(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("mac.proc_maps.Maps", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
assert out.find(b"[heap]") != -1
|
||||
assert out.count(b"\n") > 100
|
||||
assert rc == 0
|
||||
|
||||
def test_mac_psaux(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("mac.psaux.Psaux", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
assert out.find(b"executable_path") != -1
|
||||
assert out.count(b"\n") > 50
|
||||
assert rc == 0
|
||||
|
||||
def test_mac_socket_filters(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("mac.socket_filters.Socket_filters", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
assert out.count(b"\n") > 9
|
||||
assert rc == 0
|
||||
|
||||
def test_mac_timers(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("mac.timers.Timers", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
assert out.count(b"\n") > 6
|
||||
assert rc == 0
|
||||
|
||||
def test_mac_trustedbsd(image, volatility, python):
|
||||
rc, out, err = runvol_plugin("mac.trustedbsd.Trustedbsd", image, volatility, python)
|
||||
out = out.lower()
|
||||
|
||||
assert out.count(b"\n") > 10
|
||||
assert rc == 0
|
||||
Reference in New Issue
Block a user