Skip to content

Commit 65d3ff6

Browse files
authored
Merge branch 'develop' into stable
2 parents a7d64ad + 51f0a30 commit 65d3ff6

File tree

28 files changed

+649
-264
lines changed

28 files changed

+649
-264
lines changed

.github/workflows/codeql.yml

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ name: "CodeQL"
1313

1414
on:
1515
push:
16-
branches: [ "develop" ]
16+
branches: ["develop"]
1717
pull_request:
1818
# The branches below must be a subset of the branches above
19-
branches: [ "develop" ]
19+
branches: ["develop"]
2020
# schedule:
2121
# - cron: '16 8 * * 0'
2222

@@ -32,43 +32,42 @@ jobs:
3232
strategy:
3333
fail-fast: false
3434
matrix:
35-
language: [ 'python' ]
35+
language: ["python"]
3636
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
3737
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
3838

3939
steps:
40-
- name: Checkout repository
41-
uses: actions/checkout@v3
40+
- name: Checkout repository
41+
uses: actions/checkout@v3
4242

43-
# Initializes the CodeQL tools for scanning.
44-
- name: Initialize CodeQL
45-
uses: github/codeql-action/init@v2
46-
with:
47-
languages: ${{ matrix.language }}
48-
# If you wish to specify custom queries, you can do so here or in a config file.
49-
# By default, queries listed here will override any specified in a config file.
50-
# Prefix the list here with "+" to use these queries and those in the config file.
43+
# Initializes the CodeQL tools for scanning.
44+
- name: Initialize CodeQL
45+
uses: github/codeql-action/init@v3
46+
with:
47+
languages: ${{ matrix.language }}
48+
# If you wish to specify custom queries, you can do so here or in a config file.
49+
# By default, queries listed here will override any specified in a config file.
50+
# Prefix the list here with "+" to use these queries and those in the config file.
5151

52-
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
53-
queries: security-and-quality # ,security-extended
52+
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
53+
queries: security-and-quality # ,security-extended
5454

55+
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
56+
# If this step fails, then you should remove it and run the build manually (see below)
57+
- name: Autobuild
58+
uses: github/codeql-action/autobuild@v3
5559

56-
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
57-
# If this step fails, then you should remove it and run the build manually (see below)
58-
- name: Autobuild
59-
uses: github/codeql-action/autobuild@v2
60+
# ℹ️ Command-line programs to run using the OS shell.
61+
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
6062

61-
# ℹ️ Command-line programs to run using the OS shell.
62-
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
63+
# If the Autobuild fails above, remove it and uncomment the following three lines.
64+
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
6365

64-
# If the Autobuild fails above, remove it and uncomment the following three lines.
65-
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
66+
# - run: |
67+
# echo "Run, Build Application using script"
68+
# ./location_of_script_within_repo/buildscript.sh
6669

67-
# - run: |
68-
# echo "Run, Build Application using script"
69-
# ./location_of_script_within_repo/buildscript.sh
70-
71-
- name: Perform CodeQL Analysis
72-
uses: github/codeql-action/analyze@v2
73-
with:
74-
category: "/language:${{matrix.language}}"
70+
- name: Perform CodeQL Analysis
71+
uses: github/codeql-action/analyze@v3
72+
with:
73+
category: "/language:${{matrix.language}}"

CODING_STYLE.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
Coding Standards
2+
================
3+
4+
The coding standards for volatility are mostly by our linter and our code formatter.
5+
All code submissions will be vetted automatically through tests from both and the submission will not be accepted if either of these fail.
6+
7+
Code Linter: Ruff
8+
Code Formatter: Black
9+
10+
In addition, there are some coding practices that we employ to prevent specific failure cases and ensure consistency across the codebase. These are documented below along with the rationale for the decision.
11+
12+
This is heavily based upon https://google.github.io/styleguide/pyguide.html with minor modifications for volatility use.
13+
14+
Imports
15+
-------
16+
17+
Use import statements for packages and modules only, not for individual types, classes, or functions and ideally not aliased unless the imported name would cause confusion. This is to prevent people from importing something that was itself imported from elsewhere (which can lead to confusion and add in an unnecessary dependency in the import chain).
18+
19+
* Use `import x` for importing packages and modules.
20+
* Use `from x import y` where x is the package prefix and y is the module name with no prefix.
21+
* Use `from x import y as z` in any of the following circumstances:
22+
* Two modules named `y` are to be imported.
23+
* `y` conflicts with a top-level name defined in the current module.
24+
* `y` conflicts with a common parameter name that is part of the public API (e.g., `features`).
25+
* `y` is an inconveniently long name.
26+
* `y` is too generic in the context of your code (e.g., `from storage.file_system import options as fs_options`).
27+
28+
Exemptions from this rule:
29+
30+
* Symbols from the following modules are used to support static analysis and type checking:
31+
* `typing` module
32+
* `collections.abc` module
33+
* `typing_extensions` module
34+
35+
Function calls
36+
--------------
37+
38+
For longer function calls, where line length is no longer an issue, favour using keyword arguments for clarity over unnamed positional arguments.
39+
This helps coders learning the code from examples to know what parameters to pass in and avoids ordering mistakes.
40+
41+
Global Mutable State
42+
--------------------
43+
44+
Avoid mutable global state.
45+
46+
In those rare cases where using global state is warranted, mutable global entities should be declared at the module level or as a class attribute and made internal by prepending an _ to the name. If necessary, external access to mutable global state must be done through public functions or class methods. See Naming below. Please explain the design reasons why mutable global state is being used in a comment or a doc linked to from a comment.
47+
48+
Module-level constants are permitted and encouraged. For example: _MAX_HOLY_HANDGRENADE_COUNT = 3 for an internal use constant or SIR_LANCELOTS_FAVORITE_COLOR = "blue" for a public API constant. Constants must be named using all caps with underscores. See Naming below.
49+
50+
Exceptions
51+
----------
52+
53+
Never use catch-all except: statements, or catch Exception or StandardError, unless you are
54+
55+
* re-raising the exception, or
56+
* creating an isolation point in the program where exceptions are not propagated but are recorded and suppressed instead, such as protecting a thread from crashing by guarding its outermost block.
57+
58+
Python is very tolerant in this regard and except: will really catch everything including misspelled names, sys.exit() calls, Ctrl+C interrupts, unittest failures and all kinds of other exceptions that you simply don’t want to catch.
59+
60+
Versioning
61+
----------
62+
63+
Modules that inherit from `VersionableInterface` define a `_version` attribute which states their version. This is a tuple of `(MAJOR, MINOR, PATCH)` numbers, which can then be used for Semantic Versioning (where modifications that change the API in a non-backwards compatible way bump the `MAJOR` version (and set the `MINOR` and `PATCH` to 0) and additive changes increase the `MINOR` version (and set the `PATCH` to 0). Changes that have no effect on the external interface (either input or output form) should have their `PATCH` number incremented. This allows for callers of the interface to determine when changes have happened and whether their code will still work with it. Volatility carries out these checks through the requirements system, where a plugin can define what requirements it has.
64+
65+
Shared functionality
66+
--------------------
67+
68+
Within a plugin, there may be functions that are useful to other plugins. These are created as `classmethod`s so that the plugin can be depended upon by other plugins in their requirements section, without needing to instantiate a whole copy of the plugin. It is not a staticmethod, because the caller may wish to determine information about the class the method is defined in, and this is not easily accessible for staticmethods.
69+
A classmethod usually takes a `context` for its first method (and if it requires one, a configuration string for it second). All other parameters should generally be basic types (such as strings, numbers, etc) so that future work requiring parallelization does not have complex types to have to keep in sync. In particular, the idea was to ensure only one context was used per method (and each object brings its own context with it, meaning the function signature should not include objects to avoid discrepancies).
70+
71+
Comprehensions
72+
--------------
73+
74+
Comprehensions are allowed, however multiple for clauses or filter expressions are not permitted. Optimize for readability, not conciseness.
75+
76+
Lambda functions
77+
----------------
78+
79+
Okay for one-liners. Prefer generator expressions over map() or filter() with a lambda.
80+
81+
Default Arguments
82+
-----------------
83+
84+
Default arguments are fine, but not with mutable types (because they're constructed once at module load time and can lead to confusion/errors.)
85+
86+
Format strings
87+
--------------
88+
Generally f-strings are preferred, and where possible a format modifier should be used over a separate method call. As an example, hex output should be `f"0x{offset:x}"` rather than `f"{hex(offset)}"`.
89+
F-strings should be used over other formatting methods *except* in cases of logging where the f-string gets calculated/executed whether the log message is displayed or not (where as parameters are not evaluated if not needed).
90+
The ruff linter should alert about these situations and exceptions can be maded if needed.
91+
92+
True/False Evaluations
93+
----------------------
94+
95+
Use the “implicit” false if possible, e.g., if foo: rather than if foo != []:. There are a few caveats that you should keep in mind though:
96+
97+
* Always use `if foo is None:` (or `is not None`) to check for a `None` value. E.g., when testing whether a variable or argument that defaults to `None` was set to some other value. The other value might be a value that’s false in a boolean context!
98+
* Never compare a boolean variable to `False` using `==`. Use `if not x:` instead. If you need to distinguish `False` from `None` then chain the expressions, such as `if not x and x is not None:`.
99+
* For sequences (strings, lists, tuples), use the fact that empty sequences are false, so `if seq:` and `if not seq:` are preferable to `if len(seq):` and `if not len(seq):` respectively.
100+
101+
Logging
102+
-------
103+
104+
We do allow f-string usage in log messages, although technically it should be avoided since it will be evaluated even if the log message is never emitted.

README.md

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,33 @@ technical and performance challenges associated with the original
1414
code base that became apparent over the previous 10 years. Another benefit
1515
of the rewrite is that Volatility 3 could be released under a custom
1616
license that was more aligned with the goals of the Volatility community,
17-
the Volatility Software License (VSL). See the
18-
[LICENSE](https://www.volatilityfoundation.org/license/vsl-v1.0) file for
17+
the Volatility Software License (VSL). See the
18+
[LICENSE](https://www.volatilityfoundation.org/license/vsl-v1.0) file for
1919
more details.
2020

21+
## Quick Start
22+
23+
1. Install the required dependencies:
24+
25+
```shell
26+
pip install --user -e ".[full]"
27+
```
28+
29+
2. See available options:
30+
31+
```shell
32+
vol -h
33+
```
34+
35+
3. To get more information on a Windows memory sample and to make sure Volatility supports that sample type, run `vol -f <imagepath> windows.info`:
36+
37+
```shell
38+
vol -f /home/user/samples/stuxnet.vmem windows.info
39+
```
40+
41+
4. Run some other plugins. The `-f` or `--single-location` is not strictly required, but most plugins expect a single sample.
42+
Some also require/accept other options. Run `vol <plugin> -h` for more information on a particular command.
43+
2144
## Installing
2245

2346
Volatility 3 requires Python 3.8.0 or later and is published on the [PyPi registry](https://pypi.org/project/volatility3).
@@ -38,38 +61,19 @@ python3 -m venv venv && . venv/bin/activate
3861
pip install -e ".[dev]"
3962
```
4063

41-
## Quick Start
42-
43-
1. Install Volatility 3 as documented in the Installing section of the readme.
44-
45-
2. See available options:
46-
47-
```shell
48-
vol -h
49-
```
50-
51-
3. To get more information on a Windows memory sample and to make sure Volatility supports that sample type, run `vol -f <imagepath> windows.info`:
52-
53-
```shell
54-
vol -f /home/user/samples/stuxnet.vmem windows.info
55-
```
56-
57-
4. Run some other plugins. The `-f` or `--single-location` is not strictly required, but most plugins expect a single sample.
58-
Some also require/accept other options. Run `vol <plugin> -h` for more information on a particular command.
59-
6064
## Symbol Tables
6165

6266
Symbol table packs for the various operating systems are available for download at:
6367

64-
<https://downloads.volatilityfoundation.org/volatility3/symbols/windows.zip>
65-
<https://downloads.volatilityfoundation.org/volatility3/symbols/mac.zip>
66-
<https://downloads.volatilityfoundation.org/volatility3/symbols/linux.zip>
68+
<https://downloads.volatilityfoundation.org/volatility3/symbols/windows.zip>
69+
<https://downloads.volatilityfoundation.org/volatility3/symbols/mac.zip>
70+
<https://downloads.volatilityfoundation.org/volatility3/symbols/linux.zip>
6771

6872
The hashes to verify whether any of the symbol pack files have downloaded successfully or have changed can be found at:
6973

70-
<https://downloads.volatilityfoundation.org/volatility3/symbols/SHA256SUMS>
71-
<https://downloads.volatilityfoundation.org/volatility3/symbols/SHA1SUMS>
72-
<https://downloads.volatilityfoundation.org/volatility3/symbols/MD5SUMS>
74+
<https://downloads.volatilityfoundation.org/volatility3/symbols/SHA256SUMS>
75+
<https://downloads.volatilityfoundation.org/volatility3/symbols/SHA1SUMS>
76+
<https://downloads.volatilityfoundation.org/volatility3/symbols/MD5SUMS>
7377

7478
Symbol tables zip files must be placed, as named, into the `volatility3/symbols` directory (or just the symbols directory next to the executable file).
7579

test/plugins/windows/windows.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,51 @@ def test_windows_generic_kpcrs(self, volatility, python, image):
746746
assert test_volatility.count_entries_flat(json.loads(out)) > 0
747747

748748

749+
class TestWindowsSymlinkScan:
750+
def test_windows_generic_symlinkscan(self, volatility, python, image):
751+
rc, out, _err = test_volatility.runvol_plugin(
752+
"windows.symlinkscan.SymlinkScan",
753+
image,
754+
volatility,
755+
python,
756+
globalargs=("-r", "json"),
757+
)
758+
assert rc == 0
759+
assert test_volatility.count_entries_flat(json.loads(out)) > 0
760+
761+
def test_windows_specific_symlinkscan(self, volatility, python):
762+
image = WindowsSamples.WINDOWSXP_GENERIC.value.path
763+
rc, out, _err = test_volatility.runvol_plugin(
764+
"windows.symlinkscan.SymlinkScan",
765+
image,
766+
volatility,
767+
python,
768+
globalargs=("-r", "json"),
769+
)
770+
assert rc == 0
771+
json_out = json.loads(out)
772+
assert test_volatility.count_entries_flat(json_out) > 5
773+
expected_rows = [
774+
{
775+
"CreateTime": "2005-06-25T16:47:28+00:00",
776+
"From Name": "AUX",
777+
"Offset": 453082584,
778+
"To Name": "\\DosDevices\\COM1",
779+
"__children": []
780+
},
781+
{
782+
"CreateTime": "2005-06-25T16:47:28+00:00",
783+
"From Name": "UNC",
784+
"Offset": 453176664,
785+
"To Name": "\\Device\\Mup",
786+
"__children": []
787+
}
788+
]
789+
790+
for expected_row in expected_rows:
791+
assert test_volatility.match_output_row(expected_row, json_out)
792+
793+
749794
class TestWindowsLdrModules:
750795
def test_windows_specific_ldrmodules(self, volatility, python):
751796
image = WindowsSamples.WINDOWSXP_GENERIC.value.path

volatility3/cli/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,9 @@ def run(self):
505505
try:
506506
# Construct and run the plugin
507507
if constructed:
508+
vollog.debug(
509+
f"Successfully constructed {args.plugin} {constructed.version}"
510+
)
508511
grid = constructed.run()
509512
renderer = renderers[args.renderer]()
510513
renderer.filter = text_filter.CLIFilter(grid, args.filters)

volatility3/cli/volshell/generic.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,17 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]
8585
return reqs
8686

8787
def run(
88-
self, additional_locals: Dict[str, Any] = {}
88+
self, additional_locals: Dict[str, Any] = None
8989
) -> interfaces.renderers.TreeGrid:
9090
"""Runs the interactive volshell plugin.
9191
9292
Returns:
9393
Return a TreeGrid but this is always empty since the point of this plugin is to run interactively
9494
"""
9595

96+
if additional_locals is None:
97+
additional_locals = {}
98+
9699
# Try to enable tab completion
97100
if not has_ipython:
98101
try:

volatility3/framework/constants/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# We use the SemVer 2.0.0 versioning scheme
22
VERSION_MAJOR = 2 # Number of releases of the library with a breaking change
33
VERSION_MINOR = 26 # Number of changes that only add to the interface
4-
VERSION_PATCH = 0 # Number of changes that do not change the interface
4+
VERSION_PATCH = 2 # Number of changes that do not change the interface
55
VERSION_SUFFIX = ""
66

77
PACKAGE_VERSION = (

volatility3/framework/constants/windows/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,5 @@
2828

2929
# CR3 register within structures describing initial processor state to be started
3030
PROCESSOR_START_BLOCK_CR3_OFFSET = 0xA0 # PROCESSOR_START_BLOCK->ProcessorState->SpecialRegisters->Cr3, ULONG64 8 bytes
31+
32+
MAX_PID = 0xFFFFFFFC

volatility3/framework/interfaces/layers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -678,7 +678,7 @@ def del_layer(self, name: str) -> None:
678678
if name in self._layers[layer].dependencies:
679679
raise exceptions.LayerException(
680680
self._layers[layer].name,
681-
f"Layer {self._layers[layer].name} is depended upon by {layer}",
681+
f"Layer {name} is depended upon by {layer}",
682682
)
683683
# Otherwise, wipe out the layer
684684
self._layers[name].destroy()

volatility3/framework/layers/qemu.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def __init__(
102102

103103
@classmethod
104104
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
105-
return [
105+
return super().get_requirements() + [
106106
requirements.VersionRequirement(
107107
name="regex_scanner",
108108
component=scanners.RegExScanner,
@@ -115,7 +115,7 @@ def _check_header(
115115
cls, base_layer: interfaces.layers.DataLayerInterface, name: str = ""
116116
):
117117
header = base_layer.read(0, 8)
118-
if header[:4] != b"\x51\x45\x56\x4D":
118+
if header[:4] != b"\x51\x45\x56\x4d":
119119
raise exceptions.LayerException(name, "No QEMU magic bytes")
120120
if header[4:] != b"\x00\x00\x00\x03":
121121
raise exceptions.LayerException(name, "Unsupported QEMU version found")

0 commit comments

Comments
 (0)