Skip to content

Instantly share code, notes, and snippets.

@santa4nt
Last active May 28, 2023 11:31
Show Gist options
  • Star 20 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save santa4nt/11068180 to your computer and use it in GitHub Desktop.
Save santa4nt/11068180 to your computer and use it in GitHub Desktop.
A Python-ctypes script to dispatch IOCTL in Windows
#!C:\Python27\python.exe
# The MIT License (MIT)
#
# Copyright © 2014-2016 Santoso Wijaya <santoso.wijaya@gmail.com>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sub-license, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import ctypes
import ctypes.wintypes as wintypes
from ctypes import windll
LPDWORD = ctypes.POINTER(wintypes.DWORD)
LPOVERLAPPED = wintypes.LPVOID
LPSECURITY_ATTRIBUTES = wintypes.LPVOID
GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
GENERIC_EXECUTE = 0x20000000
GENERIC_ALL = 0x10000000
CREATE_NEW = 1
CREATE_ALWAYS = 2
OPEN_EXISTING = 3
OPEN_ALWAYS = 4
TRUNCATE_EXISTING = 5
FILE_ATTRIBUTE_NORMAL = 0x00000080
INVALID_HANDLE_VALUE = -1
NULL = 0
FALSE = wintypes.BOOL(0)
TRUE = wintypes.BOOL(1)
def _CreateFile(filename, access, mode, creation, flags):
"""See: CreateFile function
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
"""
CreateFile_Fn = windll.kernel32.CreateFileW
CreateFile_Fn.argtypes = [
wintypes.LPWSTR, # _In_ LPCTSTR lpFileName
wintypes.DWORD, # _In_ DWORD dwDesiredAccess
wintypes.DWORD, # _In_ DWORD dwShareMode
LPSECURITY_ATTRIBUTES, # _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes
wintypes.DWORD, # _In_ DWORD dwCreationDisposition
wintypes.DWORD, # _In_ DWORD dwFlagsAndAttributes
wintypes.HANDLE] # _In_opt_ HANDLE hTemplateFile
CreateFile_Fn.restype = wintypes.HANDLE
return wintypes.HANDLE(CreateFile_Fn(filename,
access,
mode,
NULL,
creation,
flags,
NULL))
def _DeviceIoControl(devhandle, ioctl, inbuf, inbufsiz, outbuf, outbufsiz):
"""See: DeviceIoControl function
http://msdn.microsoft.com/en-us/library/aa363216(v=vs.85).aspx
"""
DeviceIoControl_Fn = windll.kernel32.DeviceIoControl
DeviceIoControl_Fn.argtypes = [
wintypes.HANDLE, # _In_ HANDLE hDevice
wintypes.DWORD, # _In_ DWORD dwIoControlCode
wintypes.LPVOID, # _In_opt_ LPVOID lpInBuffer
wintypes.DWORD, # _In_ DWORD nInBufferSize
wintypes.LPVOID, # _Out_opt_ LPVOID lpOutBuffer
wintypes.DWORD, # _In_ DWORD nOutBufferSize
LPDWORD, # _Out_opt_ LPDWORD lpBytesReturned
LPOVERLAPPED] # _Inout_opt_ LPOVERLAPPED lpOverlapped
DeviceIoControl_Fn.restype = wintypes.BOOL
# allocate a DWORD, and take its reference
dwBytesReturned = wintypes.DWORD(0)
lpBytesReturned = ctypes.byref(dwBytesReturned)
status = DeviceIoControl_Fn(devhandle,
ioctl,
inbuf,
inbufsiz,
outbuf,
outbufsiz,
lpBytesReturned,
None)
return status, dwBytesReturned
class DeviceIoControl(object):
def __init__(self, path):
self.path = path
self._fhandle = None
def _validate_handle(self):
if self._fhandle is None:
raise Exception('No file handle')
if self._fhandle.value == wintypes.HANDLE(INVALID_HANDLE_VALUE).value:
raise Exception('Failed to open %s. GetLastError(): %d' %
(self.path, windll.kernel32.GetLastError()))
def ioctl(self, ctl, inbuf, inbufsiz, outbuf, outbufsiz):
self._validate_handle()
return _DeviceIoControl(self._fhandle, ctl, inbuf, inbufsiz, outbuf, outbufsiz)
def __enter__(self):
self._fhandle = _CreateFile(
self.path,
GENERIC_READ | GENERIC_WRITE,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL)
self._validate_handle()
return self
def __exit__(self, typ, val, tb):
try:
self._validate_handle()
except Exception:
pass
else:
windll.kernel32.CloseHandle(self._fhandle)
if __name__ == '__main__':
# sample code using \\.\PhysicalDrive0
# See: http://msdn.microsoft.com/en-us/library/windows/desktop/aa363147(v=vs.85).aspx
# first, define the Structure in ctypes language
class DISK_GEOMETRY(ctypes.Structure):
"""See: http://msdn.microsoft.com/en-us/library/aa363972(v=vs.85).aspx"""
_fields_ = [
('Cylinders', wintypes.LARGE_INTEGER),
('MediaType', wintypes.BYTE), # MEDIA_TYPE
('TracksPerCylinder', wintypes.DWORD),
('SectorsPerTrack', wintypes.DWORD),
('BytesPerSector', wintypes.DWORD)
]
IOCTL_DISK_GET_DRIVE_GEOMETRY = 0x70000
disk_geometry = DISK_GEOMETRY()
p_disk_geometry = ctypes.pointer(disk_geometry)
with DeviceIoControl(r'\\.\PhysicalDrive0') as dctl:
status, junk = dctl.ioctl(IOCTL_DISK_GET_DRIVE_GEOMETRY,
None, 0, # no input buffer
p_disk_geometry, ctypes.sizeof(DISK_GEOMETRY))
if status:
print 'Returned Disk Geometry:'
for field, typ in DISK_GEOMETRY._fields_:
print '%s=%d' % (field, getattr(disk_geometry, field))
else:
print 'IOCTL returned failure. GetLastError(): %d' % windll.kernel32.GetLastError()
@retsyo
Copy link

retsyo commented Apr 20, 2017

on my win7 64 bits, Python 2.7.12 |Anaconda 2.2.0 (32-bit) says

Traceback (most recent call last):
  File "ioctl.py", line 171, in <module>
    with DeviceIoControl(r'\\.\PhysicalDrive0') as dctl:
  File "ioctl.py", line 139, in __enter__
    self._validate_handle()
  File "ioctl.py", line 126, in _validate_handle
    (self.path, windll.kernel32.GetLastError()))
Exception: Failed to open \\.\PhysicalDrive0. GetLastError(): 5

@CeruleanSky
Copy link

CeruleanSky commented Feb 2, 2018

@retsyo For those having that error, running in a admin command prompt can usually resolve it.

An alterative is to change the "GENERIC_READ | GENERIC_WRITE" permissions in the create file call to no permissions requested aka "0x0".

"if this parameter is zero, the application can query certain metadata such as file, directory, or device attributes without accessing that file or device, even if GENERIC_READ access would have been denied."

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment