Skip to content

Add a windows drive container type to load Windows logical or physical drive from a windows host.#1775

Open
william-billaud wants to merge 26 commits into
fox-it:mainfrom
william-billaud:add_windows_drive_container
Open

Add a windows drive container type to load Windows logical or physical drive from a windows host.#1775
william-billaud wants to merge 26 commits into
fox-it:mainfrom
william-billaud:add_windows_drive_container

Conversation

@william-billaud

Copy link
Copy Markdown
Contributor

Add a windows drive container type

Currenlty, Target could not be loaded from a drive (Physical or logical (\\.\O:) on a windows systems, except using the local loader that will load all drives.
The mains issue is that drive are not seekable, and could not be natively handled by RawContainer
Selecting specific drive may allows to collect/analyses a specific drive encrypted with a veracrypt like encrypted solution, without having copying the whole drive in a file before.

Proposed Changes

  • Add a containers to load drive, which is intended to works only on Windows and allows to select drive for collect/analysis.
    • Reuse most of the logic from the local loader, but to support identification of the size of logical drive, I added a function that use a new IOCTL function : IOCTL_DISK_GET_LENGTH_INFO.
    • All this logic is required as Windows Device are not seekable and IOCTL_DISK_GET_DRIVE_GEOMETRY_EX return the whole disk size, not just the one of the volume/partition.
  • Move some function used in local loader to a new file dissect/target/helpers/windows_ffi.py
    • For _windows_createfile: Add extra params, but keep as default previously used value. These changes are required as unlike IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, IOCTL_DISK_GET_LENGTH_INFO required Generic read privileges (and thus must also set the file share mode).
    • Add some comment + add err code description to raised error, to make them more user friendly.
  • Add a test, mainly to test the detection, but not for the usage of IOCTL_DISK_GET_LENGTH_INFO. (would add flakiness + would require specific runner setup).
  • Add a small test to RawContainer (missing in Add tests data related to containers (EWF, VDI, VHD, VHDX and VMDK) #1495). An issue in rawContainer will make a lot of test fail, but this one can help to identify that it came from RawContainer.
  • On loader.find_loader, also catch OsError, some load may raise PermissionError when testing from path existence (E.g. PermissionError: [WinError 31] A device attached to the system is not functioning: '\\\\.\\PhysicalDrive0\\MFT.bin' for the vb loader, and this make dissect crash.

Issue

It does not works with multiraw, as the original literal string is converted as path with the '+', and then splited, maybe the multiraw._split_paths function should be done on the original literal provided, and not the Path version of the string.

from pathlib import Path
str(Path("\\\\.\\O:+\\\\.\\F:"))
> '\\\\.\\O:+\\F:'
# F: is not longer a Drive path.

Checklist

@william-billaud

william-billaud commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Just tested manually after rebase / new target path

PS C:\Users\Username\Documents\git\dissect.target> git rev-parse HEAD
2e650e89bece9f7e441ed5a74fc8c2fd3997fbf8
PS C:\Users\Username\Documents\git\dissect.target> uv run --python 3.13 --extra full target-query -f hostname \\.\PhysicalDrive0
<Target \\.\PhysicalDrive0> DESKTOP-XXXXXXX
PS C:\Users\Username\Documents\git\dissect.target> uv run --python 3.13 --extra full target-query -f hostname \\.\C:
2026-06-11T15:28:42.048024Z [warning  ] <Target \\.\C:>: Can't identify volume system or no volumes found, adding as raw volume instead: <Container type=windows_drive size=53075599872 vs=None> [dissect.target.target]
<Target \\.\C:> DESKTOP-XXXXXXX
PS C:\Users\Username\Documents\git\dissect.target>

But raise the following error for python < 3.12

PS C:\Users\Username\Documents\git\dissect.target> uv run --python 3.11 --extra full target-query -f hostname \\.\PhysicalDrive0
Using CPython 3.11.15
Removed virtual environment at: .venv
Creating virtual environment at: .venv
Installed 67 packages in 2.91s
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "C:\Users\Username\Documents\git\dissect.target\.venv\Scripts\target-query.exe\__main__.py", line 10, in <module>
  File "C:\Users\Username\Documents\git\dissect.target\dissect\target\tools\utils\cli.py", line 475, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Username\Documents\git\dissect.target\dissect\target\tools\query.py", line 157, in main
    for target in open_targets(args):
  File "C:\Users\Username\Documents\git\dissect.target\dissect\target\tools\utils\cli.py", line 253, in open_targets
    for target in targets:
  File "C:\Users\Username\Documents\git\dissect.target\dissect\target\target.py", line 442, in open_all
    if not path.exists():
           ^^^^^^^^^^^^^
  File "C:\Users\Username\AppData\Roaming\uv\python\cpython-3.11-windows-x86_64-none\Lib\pathlib.py", line 1235, in exists
    self.stat()
  File "C:\Users\Username\AppData\Roaming\uv\python\cpython-3.11-windows-x86_64-none\Lib\pathlib.py", line 1013, in stat
    return os.stat(self, follow_symlinks=follow_symlinks)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
PermissionError: [WinError 31] A device attached to the system is not functioning: '\\\\.\\PhysicalDrive0\\'

I put issue to draft until I solve the issue (which was probably already present before)

@william-billaud
william-billaud marked this pull request as draft June 11, 2026 15:35
@william-billaud

Copy link
Copy Markdown
Contributor Author

Before python 3.11 the str(path) representation return a different value as the original path (\ is appended), Thus when the value is forwarded to os.stat, this fail.

Making this works for python < 3.12 seems to be a lot of modification of loading logic. Regarding the fact that python 3.11 is end of life in ~1 year maybe bug can be "accepted"? If not I can try to look how I can fix it.

Python 3.11

from pathlib from Path
str(Path("\\\\.\\PhysicalDrive0"))
>  '\\\\.\\PhysicalDrive0\\'

Python 3.12

from pathlib from Path
str(Path("\\\\.\\PhysicalDrive0"))
>  '\\\\.\\PhysicalDrive0'

@william-billaud
william-billaud marked this pull request as ready for review June 12, 2026 14:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for direct invocation on Windows Logical or physicalDrive

1 participant