Re: [python-win32] DeviceIOControl using IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS fails with 'Incorrect function.'

2021-02-10 Thread Doug Campbell
Thanks again, Eryk.

I was able to play around today with the code you provided below and was able 
to tweak it to list the volumes, find the one that matched the disk/partition 
combination I was searching for and return its offset on the disk.  Very 
helpful!


From: Eryk Sun 
Sent: Tuesday, February 9, 2021 11:32 PM
To: python-win32@python.org 
Cc: Doug Campbell 
Subject: Re: [python-win32] DeviceIOControl using 
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS fails with 'Incorrect function.'

On 2/9/21, Doug Campbell  wrote:
>
> Again, you expand my knowledge!  It seems so obvious now after reading what
> you wrote that I would not be able to get volume disk extents for a physical
> partition but yet this is what I wanted to do because I was attempting to
> find out the partition's offset on the disk.

In the case of a basic disk device, it can have multiple partitions
that each correspond to a volume device, \Device\HarddiskVolume. In
this case, each volume device consists of a single extent on a single
disk.

> The only way I can find to accomplish this is to iterate through each volume

You can get the list of volume GUID names of volume devices that are
registered with the mountpoint manager via FindFirstVolumeW /
FindNextVolumeW. Unfortunately, PyWin32 doesn't wrap the volume-find
functions. They can be called using ctypes. For example:

import ctypes
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

class HANDLE(ctypes.c_void_p):
def __eq__(self, other):
if hasattr(other, 'value'):
return self.value == other.value
return False

INVALID_HANDLE_VALUE = HANDLE(-1)
ERROR_NO_MORE_FILES = 18

kernel32.FindFirstVolumeW.restype = HANDLE

def list_volumes():
volumes = []
buf = (ctypes.c_wchar * 260)()
hfind = kernel32.FindFirstVolumeW(buf, len(buf))
if hfind == INVALID_HANDLE_VALUE:
raise ctypes.WinError(ctypes.get_last_error())
try:
while True:
# Strip the trailing backslash that FindNextVolumeW appends.
# The trailing backslash is the mountpoint of the filesystem
# that mounts the volume device, but we only want the GUID
# device name.
volumes.append(buf.value.rstrip('\\'))
if not kernel32.FindNextVolumeW(hfind, buf, len(buf)):
error = ctypes.get_last_error()
if error != ERROR_NO_MORE_FILES:
raise ctypes.WinError(error)
break
finally:
kernel32.FindVolumeClose(hfind)
return volumes

I subclassed c_void_p to create the HANDLE type in order to avoid the
otherwise automatic conversion of the return value to a builtin Python
type. This is a simple way around needing to declare argtypes for the
functions that take the find handle (actually a pointer to memory) as
an argument.
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] DeviceIOControl using IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS fails with 'Incorrect function.'

2021-02-09 Thread Doug Campbell
Eryk,

Again, you expand my knowledge!  It seems so obvious now after reading what you 
wrote that I would not be able to get volume disk extents for a physical 
partition but yet this is what I wanted to do because I was attempting to find 
out the partition's offset on the disk.

Now, I think I see that I need to figure out the partition's volume device name 
using the \\?\Volume{GUID} format and then I will be able to determine what its 
offset is on the disk.  The only way I can find to accomplish this is to 
iterate through each volume and using IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS to 
get its disk and IOCTL_DISK_GET_PARTITION_INFO_EX to get its partition and then 
see if I have a match for the disk/partition combination that I am looking for.

Thanks for sharing the extra details on how to handle the 
wintype.ERROR_MORE_DATA error.  That was likely to be my next issue :)  I did 
find an alternative method for handling this that is used in the multibootusb 
script/win32.py<https://github.com/mbusb/multibootusb/blob/master/scripts/win32.py>
 function findVolumeGuids().  They use BytesIO to create an io stream and then 
unpack the bytes on-the-fly.  I think I likely go with your method.

Anyway, thanks again for your excellent explanations.

Doug


From: Eryk Sun 
Sent: Tuesday, February 9, 2021 3:05 PM
To: python-win32@python.org 
Cc: Doug Campbell 
Subject: Re: [python-win32] DeviceIOControl using 
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS fails with 'Incorrect function.'

On 2/9/21, Doug Campbell  wrote:
>
> win32file.DeviceIoControl(hDevice,
> winioctlcon.IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
> None, extents, None)
> pywintypes.error: (1, 'DeviceIoControl', 'Incorrect function.')
>
> I have tried with all three of the disks on my system:  \\.\PhysicalDrive0,
> \\.\PhysicalDrive1, and \\.\PhyscialDrive2 but the results are always the
> same.

IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS requests the disk extents of a
volume device. Disk devices do not implement this request. A volume
device such as "\\.\C:" can span multiple extents across one or more
physical disks such as "\\.\PhysicalDrive0" and "\\.\PhysicalDrive1".

If the request fails with winerror.ERROR_MORE_DATA, you need to be
prepared to resize the structure according to NumberOfDiskExtents.
ctypes doesn't make this all that easy or convenient at a high level.
What I do is to make the array field private and implement a property
that takes the dynamic length into account. For example:

ANYSIZE_ARRAY = 1

class VOLUME_DISK_EXTENTS(ctypes.Structure):
_fields_ = (('NumberOfDiskExtents', ctypes.c_ulong),
('_Extents', DISK_EXTENT * ANYSIZE_ARRAY))

@property
def Extents(self):
offset = type(self)._Extents.offset
array_t = DISK_EXTENT * self.NumberOfDiskExtents
return array_t.from_buffer(self, offset)

def resize(self):
if self.NumberOfDiskExtents < 1:
self.NumberOfDiskExtents = 1
offset = type(self)._Extents.offset
array_size = ctypes.sizeof(DISK_EXTENT) * self.NumberOfDiskExtents
ctypes.resize(self, offset + array_size)

If you passed `vde = VOLUME_DISK_EXTENTS()`, and the request fails
with winerror.ERROR_MORE_DATA, the call should have set
NumberOfDiskExtents to the required length. Call vde.resize(), and try
again. It's a bit clumsy, but it works.

> hDevice = win32file.CreateFile(
> disk, win32con.GENERIC_READ,
> win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
> None, win32con.OPEN_EXISTING, win32con.FILE_ATTRIBUTE_NORMAL,
> None)

IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS is defined to require
FILE_ANY_ACCESS. This means the desired access and access sharing can
both be 0, and should be since there's no reason to request access
that you don't need that could cause the call to fail with a
permission error (e.g. access denied, or a sharing violation if read
access isn't shared). For example:

volume = r'\\.\C:'
hDevice = win32file.CreateFile(volume, 0, 0, None,
win32file.OPEN_EXISTING, 0, None)

---

Volume Device Names

The base name of a volume device is typically something like
"\Device\HarddiskVolume" or "\Device\CdRom", but these are
automatic names that can vary in the device number N each time a
volume comes online. The mountpoint manager usually will also assign
persistent names to a volume device, which are stored in the registry
if necessary (HKLM\System\MountedDevices) and are set globally in the
"\GLOBAL??" device-alias directory when the volume comes online.
Usually it assigns a GUID name of the form
"Volume{12345678----123456789ABC}". A GUID name is used
when mounting a volume on an existing directory (e.g. mounting a
volume as "C:\Mount\BackupDrive

[python-win32] DeviceIOControl using IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS fails with 'Incorrect function.'

2021-02-09 Thread Doug Campbell
I am trying to get the disk extents given a disk number but I am getting back 
the following error.

Traceback (most recent call last):
  File "test2.py", line 29, in 

win32file.DeviceIoControl(hDevice,winioctlcon.IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
 None, extents, None)
pywintypes.error: (1, 'DeviceIoControl', 'Incorrect function.')

I have tried with all three of the disks on my system:  \\.\PhysicalDrive0, 
\\.\PhysicalDrive1, and \\.\PhyscialDrive2 but the results are always the same.

Thanks for any insights as to why I am getting this "Incorrect function" error 
and how to resolve it.

Doug


Here is the code that I am using.

import ctypes
import winioctlcon
import win32file
import win32con

disk = r'\\.\PhysicalDrive1'

hDevice = win32file.CreateFile(
disk, win32con.GENERIC_READ,
win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
None, win32con.OPEN_EXISTING, win32con.FILE_ATTRIBUTE_NORMAL,  None)

class DISK_EXTENT(ctypes.Structure):
_fields_ = (
('DiskNumber', ctypes.c_ulong),
('StartingOffset', ctypes.c_longlong),
('ExtentLength', ctypes.c_longlong))

ANYSIZE_ARRAY = 1

class VOLUME_DISK_EXTENTS(ctypes.Structure):
_fields_ = (
('NumberOfDiskExtents', ctypes.c_ulong),
('Extents', DISK_EXTENT * ANYSIZE_ARRAY))

extents = VOLUME_DISK_EXTENTS()

try:

win32file.DeviceIoControl(hDevice,winioctlcon.IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
 None, extents, None)
finally:
hDevice.close()




___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] DeviceIOControl calls respond with parameter incorrect

2021-02-09 Thread Doug Campbell
Thanks Eryk!

That was exactly what I needed.  I will have to read up on the _pack_ directive 
to understand it but for now things are running the way they should be.

I did have one typo in what I originally pasted that made its way into what you 
provided.

('volumeID', ctypes.c_wchar * VOLUME_ID_SIZE),

should actually be

('volumeID', ctypes.c_char * VOLUME_ID_SIZE),

So, if someone else comes along and tries this out they should make that one 
change.

Anyway, you made my day.  Thanks again!

Doug


From: Eryk Sun 
Sent: Monday, February 8, 2021 8:24 PM
To: python-win32@python.org 
Cc: Doug Campbell 
Subject: Re: [python-win32] DeviceIOControl calls respond with parameter 
incorrect

On 2/8/21, Doug Campbell  wrote:
> In my python 2 script, I am trying to connect to the VeraCrypt device driver
> to get some information on my mounted volumes.

The VeraCrypt repo on GitHub [1] indicates that all structures are
defined with #pragma pack(1). In ctypes this is the _pack_ directive.
Try the following:

import ctypes
import winioctlcon
import win32file

def VC_IOCTL(CODE):
return winioctlcon.CTL_CODE(winioctlcon.FILE_DEVICE_UNKNOWN,
0x800 + CODE, winioctlcon.METHOD_BUFFERED,
winioctlcon.FILE_ANY_ACCESS)

VC_IOCTL_GET_MOUNTED_VOLUMES = VC_IOCTL(6)
VC_IOCTL_GET_VOLUME_PROPERTIES = VC_IOCTL(7)
VC_IOCTL_GET_BOOT_ENCRYPTION_STATUS = VC_IOCTL(18)
VC_IOCTL_GET_BOOT_DRIVE_VOLUME_PROPERTIES = VC_IOCTL(22)
VC_IOCTL_EMERGENCY_CLEAR_KEYS = VC_IOCTL(41)

MAX_PATH = 260
VOLUME_LABEL_LENGTH = 33 # 32 + null
VOLUME_ID_SIZE = 32
WIN32_ROOT_PREFIX DRIVER_STR = r'\\.\VeraCrypt'

class VOLUME_PROPERTIES_STRUCT(ctypes.Structure):
_pack_ = 1
_fields_ = (
('driveNo', ctypes.c_int),
('uniqueId', ctypes.c_int),
('wszVolume', ctypes.c_wchar * MAX_PATH),
('diskLength', ctypes.c_uint64),
('ea', ctypes.c_int),
('mode', ctypes.c_int),
('pkcs5', ctypes.c_int),
('pkcs5Iterations', ctypes.c_int),
('hiddenVolume', ctypes.c_int),
('readOnly', ctypes.c_int),
('removable', ctypes.c_int),
('partitionInInactiveSysEncScope', ctypes.c_int),
('volFormatVersion', ctypes.c_uint32),
('totalBytesRead', ctypes.c_uint64),
('totalBytesWritten', ctypes.c_uint64),
('hiddenVolProtection', ctypes.c_int),
('volFormatVersion', ctypes.c_int),
('volumePim', ctypes.c_int),
('wszLabel', ctypes.c_wchar * VOLUME_LABEL_LENGTH),
('bDriverSetLabel', ctypes.c_int),
('volumeID', ctypes.c_wchar * VOLUME_ID_SIZE),
('mountDisabled', ctypes.c_int))


prop = VOLUME_PROPERTIES_STRUCT(driveNo = ord('F') - ord('A'))

hDevice = win32file.CreateFile(WIN32_ROOT_PREFIX DRIVER_STR, 0, 0, None,
win32file.OPEN_EXISTING, 0, None)
try:
info = win32file.DeviceIoControl(hDevice,
VC_IOCTL_GET_VOLUME_PROPERTIES, prop, prop)
finally:
hDevice.close()

---
[1] https://github.com/veracrypt/VeraCrypt/blob/master/src/Common/Apidrvr.h
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


[python-win32] DeviceIOControl calls respond with parameter incorrect

2021-02-08 Thread Doug Campbell
In my python 2 script, I am trying to connect to the VeraCrypt device driver to 
get some information on my mounted volumes.

This is what I have so far.  I tried a bunch of different ideas on how to 
construct the input buffer for the DeviceIoControl function call but I keep 
getting the following response.

Traceback (most recent call last):
  File "test.py", line 188, in 

info=win32file.DeviceIoControl(hDisk,VC_IOCTL_GET_VOLUME_PROPERTIES,b'x05x00x00x00'
 + (b'x00' * 702),65536)
pywintypes.error: (87, 'DeviceIoControl', 'The parameter is incorrect.')


I would appreciate any direction anyone can give.  I have seen success with 
making calls to win32file.DeviceIoControl when an input buffer wasn't needed 
but this one needs this information passed to it for it to work.

Thanks!
Doug

===
import ctypes
import win32api
import win32file
import win32con
import winioctlcon
import struct

# 
https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/d4drvif/nf-d4drvif-ctl_code
# 
https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/specifying-device-types
FILE_DEVICE_UNKNOWN=0x0022

METHOD_BUFFERED=0
METHOD_IN_DIRECT=1
METHOD_OUT_DIRECT=2
METHOD_NEITHER=3

FILE_ANY_ACCESS=0x
FILE_READ_ACCESS=0x0001
FILE_WRITE_ACCESS=0x0002

def CTL_CODE(DeviceType, Function, Method, Access):
return (DeviceType << 16) | (Access << 14) | (Function << 2) | Method

#define VC_IOCTL(CODE) (CTL_CODE (FILE_DEVICE_UNKNOWN, 0x800 + (CODE), 
METHOD_BUFFERED, FILE_ANY_ACCESS))
def VC_IOCTL(CODE):
return (CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800 + (CODE), METHOD_BUFFERED, 
FILE_ANY_ACCESS))

VC_IOCTL_GET_MOUNTED_VOLUMES = VC_IOCTL(6)
VC_IOCTL_GET_VOLUME_PROPERTIES = VC_IOCTL (7)
VC_IOCTL_GET_BOOT_ENCRYPTION_STATUS = VC_IOCTL (18)
VC_IOCTL_GET_BOOT_DRIVE_VOLUME_PROPERTIES = VC_IOCTL (22)
VC_IOCTL_EMERGENCY_CLEAR_KEYS = VC_IOCTL (41)

INVALID_HANDLE_VALUE=-1
FILE_SHARE_READ=0x0001
FILE_SHARE_WRITE=0x0002
OPEN_EXISTING=3
path = ".\\VeraCrypt"
access_flag = 0
share_flag = FILE_SHARE_READ | FILE_SHARE_WRITE

hDisk = 
win32file.CreateFile(path,0,win32file.FILE_SHARE_READ|win32file.FILE_SHARE_WRITE,None,win32file.OPEN_EXISTING,0,None)

class VOLUME_PROPERTIES_STRUCT(ctypes.Structure):
_fields_ = [('driveNo', ctypes.c_int),
('uniqueId', ctypes.c_int),
('wszVolume', ctypes.c_wchar * 260),
('diskLength', ctypes.c_uint64),
('ea', ctypes.c_int),
('mode', ctypes.c_int),
('pkcs5', ctypes.c_int),
('pkcs5Iterations', ctypes.c_int),
('hiddenVolume', ctypes.c_long),
('readOnly', ctypes.c_long),
('removable', ctypes.c_long),
('partitionInInactiveSysEncScope', ctypes.c_long),
('volFormatVersion', ctypes.c_uint32),
('totalBytesRead', ctypes.c_uint64),
('totalBytesWritten', ctypes.c_uint64),
('hiddenVolProtection', ctypes.c_int),
('volFormatVersion', ctypes.c_int),
('volumePim', ctypes.c_int),
('wszLabel', ctypes.c_wchar * 33),
('bDriverSetLabel', ctypes.c_long),
('volumeID', ctypes.c_wchar * 32),
('mountDisabled', ctypes.c_long)]


prop = VOLUME_PROPERTIES_STRUCT()
prop.driveNo = 5

#info=win32file.DeviceIoControl(hDisk,VC_IOCTL_GET_VOLUME_PROPERTIES,struct.pack('ii520sQLQQiii66sL64sL',prop),17424)
#info=win32file.DeviceIoControl(hDisk,VC_IOCTL_GET_VOLUME_PROPERTIES,struct.pack('ii520sQLQQiii66sL64sL',5,0,'a'
 * 520,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'a' * 66,0,'a' * 64,0),17424)
info=win32file.DeviceIoControl(hDisk,VC_IOCTL_GET_VOLUME_PROPERTIES,b'0x00' + 
b'x05x00x00x00' + b'x00' * 702),65536)


I based what I have done on the following C code from VeraStatus 
(veracrypt/VeraStatus: Command line tool to get technical information about 
VeraCrypt mounted volumes and system encryption 
(github.com)).  Here are what I 
believe are relevant sections.


#define VC_IOCTL(CODE) (CTL_CODE (FILE_DEVICE_UNKNOWN, 0x800 + (CODE), 
METHOD_BUFFERED, FILE_ANY_ACCESS))

#define VC_IOCTL_GET_MOUNTED_VOLUMES VC_IOCTL (6)

#define VOLUME_ID_SIZE 32

typedef struct
{
int driveNo;
int uniqueId;
wchar_t wszVolume[260];
unsigned __int64 diskLength;
int ea;
int mode;
int pkcs5;
int pkcs5Iterations;
BOOL hiddenVolume;
BOOL readOnly;
BOOL removable;
BOOL partitionInInactiveSysEncScope;
unsigned __int32 volumeHeaderFlags;
unsigned __int64 totalBytesRead;
unsigned __int64 totalBytesWritten;
int hiddenVolProtection;
int volFormatVersion;
int volumePim;
wchar_t wszLabel[33];
BOOL bDriverSetLabel;
unsigned char volumeID[VOLUME_ID_SIZE];
BOOL mountDisabled;
} VOLUME_PROPERTIES_STRUCT;

VOLUME_PROPERTIES_STRUCT prop;

prop.driveNo = _totupper(argv[1][0]) - TEXT('A');

if (DeviceIoControl (hDriver, VC_IOCTL_GET_VOLUME_PROPERTIES, , sizeof 
(prop), , sizeof