在 win32file.DeviceIoControl 上解压结构
Struct unpack on win32file.DeviceIoControl
我正在尝试理解和使用 win32file。我需要获取 USN 期刊,并且很难理解我在网上找到的代码片段。这是我找到的代码片段 -
format = 'qqqqqLLLLqqqqq'
length = struct.calcsize(format)
out_buffer = win32file.DeviceIoControl(volh, winioctlcon.FSCTL_GET_NTFS_VOLUME_DATA, None, length)
data = struct.unpack(format, out_buffer)
现在我对 C 及其结构感到很生疏。我现在所了解的是 format
是 96 字节缓冲区,它将从 DeviceIoControl
获得输出
所以我尝试将格式更改为 'QQQQQQQQQQQQQQQQQQQ'
看看会发生什么(因为我有点不知道实际会发生什么)结果我得到了一个更大的 out_buffer
这个时间。所以我想解压它 -
struct.unpack(format, out_buffer)
令我惊讶的是,我得到了 -
struct.error: unpack requires a string argument of length 152
所以我添加了另一个 'Q' 来增加大小并得到相同的结果。我不明白为什么 'qqqqqLLLLqqqqq' 有效而 'QQQQQQQQQQQQQQQQQQQ' 无效。所以我的问题是 -
我的理解是如果缓冲区大于输出我们可以解压,那么为什么解压不起作用?
每次我想从 DeviceIoControl 中取出一些东西时,我是否必须记住这些格式?
向我指出资源也将是一个额外的好处,因为我需要在代码的基础上构建以阅读 USN 期刊,而且我不认为点击尝试会让我到任何地方
让我们把问题分解成更小的部分,一次一个。
win32file 模块是 [GitHub]: mhammond/pywin32 - Python for Windows (pywin32) Extensions 的一部分,它是 Python 的包装器WinAPIs
不幸的是,它没有官方文档页面(或者我不知道有一个),所以下面是我能找到的最好的(我已经使用了很多年了) ).另一种永远不会失败(但不太吸引人)的方法是直接查看代码
[ActiveState.Docs]: win32file.DeviceIoControl is a wrapper over [MS.Docs]: DeviceIoControl function
DeviceIoControl 行为不同,取决于 dwIoControlCode (2nd 争论)。对于 FSCTL_GET_NTFS_VOLUME_DATA,它用特定于卷的数据填充缓冲区。来自 [MS.Docs]: FSCTL_GET_NTFS_VOLUME_DATA IOCTL:
lpOutBuffer
A pointer to the output buffer, an NTFS_VOLUME_DATA_BUFFER (@CristiFati: !!! Broken URL !!!) structure. The file record associated with the file identifier specified in the input buffer is returned in this buffer. Refer to the Remarks section of the documentation for the NTFS_VOLUME_DATA_BUFFER structure for specific information on how to determine the correct size of this buffer.
这里有一个替代上述损坏的URL:[MSDN]: NTFS_VOLUME_DATA_BUFFER structure。由于我不确定它的有效期有多长,我将结构定义粘贴到下面(来自 Windows Kits 8.1:winioctl.h(行#4987)):
typedef struct {
LARGE_INTEGER VolumeSerialNumber;
LARGE_INTEGER NumberSectors;
LARGE_INTEGER TotalClusters;
LARGE_INTEGER FreeClusters;
LARGE_INTEGER TotalReserved;
DWORD BytesPerSector;
DWORD BytesPerCluster;
DWORD BytesPerFileRecordSegment;
DWORD ClustersPerFileRecordSegment;
LARGE_INTEGER MftValidDataLength;
LARGE_INTEGER MftStartLcn;
LARGE_INTEGER Mft2StartLcn;
LARGE_INTEGER MftZoneStart;
LARGE_INTEGER MftZoneEnd;
} NTFS_VOLUME_DATA_BUFFER, *PNTFS_VOLUME_DATA_BUFFER;
[Python 3.Docs]: struct - Interpret bytes as packed binary data module, is used for conversions between binary and "normal" data. It contains all the format characters meanings (q, Q, L, ...), and much more. You could also take a look at [SO]: Python struct.pack() behavior更多(实用)细节
看完上面的材料,事情应该变得更清楚了。
几个注意事项:
- 如果不知道函数的作用 (returns),他们可能不应该使用它(当然不阅读手册)。尽管如今,Win(对普通用户总是有很多限制)和 Nix "protect users from themselves"(例如:root 不再允许登录,写保护 %SystemDrive%, ...)
- 尝试(试错)显示出一些经验不足(可能每个人都在某个时候做过,关键是不要完全依赖它)
- "每次我想从 DeviceIoControl 中获取某些内容时,我是否必须记住这些格式?"?
- 同样,如果不知道一个函数的作用,那么调用它的原因是什么?如果你的意思是背诵NTFS_VOLUME_DATA_BUFFER,那肯定不是这样的。只有在使用它时你才应该知道它的结构(正如你所注意到的,你可以从一些地方得到它 - 包括这个post :))
- "我的理解是如果缓冲区大于输出我们可以解压,那么为什么解压不起作用?"
- 您的理解是正确的。但是 win32file.DeviceIoControl 似乎 有时 (可能在达到 1st NULL 在 96 字节之后)在传递大于预期值的值时截断输出缓冲区(通过 长度 参数)。通过较小的时,它会失败(如预期的那样)
我还准备了一个虚拟的Python例子。
code00.py:
#!/usr/bin/env python3
import sys
import struct
import win32file
import win32api
import win32con
import winioctlcon
VOLUME_LETTER = "E"
FILE_READ_ATTRIBUTES = 0x0080
FILE_EXECUTE = 0x0020
vol_data_buf_fmt = "qqqqqLLLLqqqqq" # This is the format that matches NTFS_VOLUME_DATA_BUFFER definition (96 bytes). Note: Instead of each 'q' you could also use 'Ll' as 'LARGE_INTEGER' is an union
BINARY_FORMAT_LIST = [
vol_data_buf_fmt,
"QQQQQQQQQQQQQQQQQQQ",
]
def print_formats(): # Dummy func
print("Formats and lengths:")
for format in BINARY_FORMAT_LIST:
print(" {:s}: {:d}".format(format, struct.calcsize(format)))
def main():
#print_formats()
vol_unc_name = "\\.\{:s}:".format(VOLUME_LETTER)
print("volume: ", vol_unc_name)
access_flags = FILE_READ_ATTRIBUTES | FILE_EXECUTE # Apparently, doesn't work without FILE_EXECUTE
share_flags = win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE # Doesn't work withou FILE_SHARE_WRITE
creation_flags = win32con.OPEN_EXISTING
attributes_flags = win32con.FILE_ATTRIBUTE_NORMAL
vol_handle = win32file.CreateFile(vol_unc_name, access_flags, share_flags, None, creation_flags, attributes_flags, None)
buf_len = struct.calcsize(vol_data_buf_fmt)
for i in [buf_len]:
print(" Passing a buffer size of: {:d}".format(i))
buf = win32file.DeviceIoControl(vol_handle, winioctlcon.FSCTL_GET_NTFS_VOLUME_DATA, None, i)
print(" DeviceIocontrol returned a {:d} bytes long {:}".format(len(buf), type(buf)))
out = struct.unpack_from(vol_data_buf_fmt, buf)
print("\n NumberSectors: {:}\n TotalClusters: {:}\n BytesPerCluster: {:}".format(out[1], out[2], out[6]))
win32api.CloseHandle(vol_handle)
if __name__ == "__main__":
print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
main()
输出:
(py35x64_test) e:\Work\Dev\Whosebug\q053318932>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" ./code00.py
Python 3.5.4 (v3.5.4:3f56838, Aug 8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32
volume: \.\E:
Passing a buffer size of: 96
DeviceIocontrol returned a 96 bytes long <class 'bytes'>
NumberSectors: 494374911
TotalClusters: 61796863
BytesPerCluster: 4096
不用说,将 TotalClusters 乘以 BytesPerCluster,我得到了正确的字节数(由 Win 报告) 我的 E: 驱动器。
我正在尝试理解和使用 win32file。我需要获取 USN 期刊,并且很难理解我在网上找到的代码片段。这是我找到的代码片段 -
format = 'qqqqqLLLLqqqqq'
length = struct.calcsize(format)
out_buffer = win32file.DeviceIoControl(volh, winioctlcon.FSCTL_GET_NTFS_VOLUME_DATA, None, length)
data = struct.unpack(format, out_buffer)
现在我对 C 及其结构感到很生疏。我现在所了解的是 format
是 96 字节缓冲区,它将从 DeviceIoControl
所以我尝试将格式更改为 'QQQQQQQQQQQQQQQQQQQ'
看看会发生什么(因为我有点不知道实际会发生什么)结果我得到了一个更大的 out_buffer
这个时间。所以我想解压它 -
struct.unpack(format, out_buffer)
令我惊讶的是,我得到了 -
struct.error: unpack requires a string argument of length 152
所以我添加了另一个 'Q' 来增加大小并得到相同的结果。我不明白为什么 'qqqqqLLLLqqqqq' 有效而 'QQQQQQQQQQQQQQQQQQQ' 无效。所以我的问题是 -
我的理解是如果缓冲区大于输出我们可以解压,那么为什么解压不起作用?
每次我想从 DeviceIoControl 中取出一些东西时,我是否必须记住这些格式?
向我指出资源也将是一个额外的好处,因为我需要在代码的基础上构建以阅读 USN 期刊,而且我不认为点击尝试会让我到任何地方
让我们把问题分解成更小的部分,一次一个。
win32file 模块是 [GitHub]: mhammond/pywin32 - Python for Windows (pywin32) Extensions 的一部分,它是 Python 的包装器WinAPIs
不幸的是,它没有官方文档页面(或者我不知道有一个),所以下面是我能找到的最好的(我已经使用了很多年了) ).另一种永远不会失败(但不太吸引人)的方法是直接查看代码
[ActiveState.Docs]: win32file.DeviceIoControl is a wrapper over [MS.Docs]: DeviceIoControl function
DeviceIoControl 行为不同,取决于 dwIoControlCode (2nd 争论)。对于 FSCTL_GET_NTFS_VOLUME_DATA,它用特定于卷的数据填充缓冲区。来自 [MS.Docs]: FSCTL_GET_NTFS_VOLUME_DATA IOCTL:
lpOutBuffer
A pointer to the output buffer, an NTFS_VOLUME_DATA_BUFFER (@CristiFati: !!! Broken URL !!!) structure. The file record associated with the file identifier specified in the input buffer is returned in this buffer. Refer to the Remarks section of the documentation for the NTFS_VOLUME_DATA_BUFFER structure for specific information on how to determine the correct size of this buffer.这里有一个替代上述损坏的URL:[MSDN]: NTFS_VOLUME_DATA_BUFFER structure。由于我不确定它的有效期有多长,我将结构定义粘贴到下面(来自 Windows Kits 8.1:winioctl.h(行#4987)):
typedef struct { LARGE_INTEGER VolumeSerialNumber; LARGE_INTEGER NumberSectors; LARGE_INTEGER TotalClusters; LARGE_INTEGER FreeClusters; LARGE_INTEGER TotalReserved; DWORD BytesPerSector; DWORD BytesPerCluster; DWORD BytesPerFileRecordSegment; DWORD ClustersPerFileRecordSegment; LARGE_INTEGER MftValidDataLength; LARGE_INTEGER MftStartLcn; LARGE_INTEGER Mft2StartLcn; LARGE_INTEGER MftZoneStart; LARGE_INTEGER MftZoneEnd; } NTFS_VOLUME_DATA_BUFFER, *PNTFS_VOLUME_DATA_BUFFER;
[Python 3.Docs]: struct - Interpret bytes as packed binary data module, is used for conversions between binary and "normal" data. It contains all the format characters meanings (q, Q, L, ...), and much more. You could also take a look at [SO]: Python struct.pack() behavior更多(实用)细节
看完上面的材料,事情应该变得更清楚了。
几个注意事项:
- 如果不知道函数的作用 (returns),他们可能不应该使用它(当然不阅读手册)。尽管如今,Win(对普通用户总是有很多限制)和 Nix "protect users from themselves"(例如:root 不再允许登录,写保护 %SystemDrive%, ...)
- 尝试(试错)显示出一些经验不足(可能每个人都在某个时候做过,关键是不要完全依赖它)
- "每次我想从 DeviceIoControl 中获取某些内容时,我是否必须记住这些格式?"?
- 同样,如果不知道一个函数的作用,那么调用它的原因是什么?如果你的意思是背诵NTFS_VOLUME_DATA_BUFFER,那肯定不是这样的。只有在使用它时你才应该知道它的结构(正如你所注意到的,你可以从一些地方得到它 - 包括这个post :))
- "我的理解是如果缓冲区大于输出我们可以解压,那么为什么解压不起作用?"
- 您的理解是正确的。但是 win32file.DeviceIoControl 似乎 有时 (可能在达到 1st NULL 在 96 字节之后)在传递大于预期值的值时截断输出缓冲区(通过 长度 参数)。通过较小的时,它会失败(如预期的那样)
我还准备了一个虚拟的Python例子。
code00.py:
#!/usr/bin/env python3
import sys
import struct
import win32file
import win32api
import win32con
import winioctlcon
VOLUME_LETTER = "E"
FILE_READ_ATTRIBUTES = 0x0080
FILE_EXECUTE = 0x0020
vol_data_buf_fmt = "qqqqqLLLLqqqqq" # This is the format that matches NTFS_VOLUME_DATA_BUFFER definition (96 bytes). Note: Instead of each 'q' you could also use 'Ll' as 'LARGE_INTEGER' is an union
BINARY_FORMAT_LIST = [
vol_data_buf_fmt,
"QQQQQQQQQQQQQQQQQQQ",
]
def print_formats(): # Dummy func
print("Formats and lengths:")
for format in BINARY_FORMAT_LIST:
print(" {:s}: {:d}".format(format, struct.calcsize(format)))
def main():
#print_formats()
vol_unc_name = "\\.\{:s}:".format(VOLUME_LETTER)
print("volume: ", vol_unc_name)
access_flags = FILE_READ_ATTRIBUTES | FILE_EXECUTE # Apparently, doesn't work without FILE_EXECUTE
share_flags = win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE # Doesn't work withou FILE_SHARE_WRITE
creation_flags = win32con.OPEN_EXISTING
attributes_flags = win32con.FILE_ATTRIBUTE_NORMAL
vol_handle = win32file.CreateFile(vol_unc_name, access_flags, share_flags, None, creation_flags, attributes_flags, None)
buf_len = struct.calcsize(vol_data_buf_fmt)
for i in [buf_len]:
print(" Passing a buffer size of: {:d}".format(i))
buf = win32file.DeviceIoControl(vol_handle, winioctlcon.FSCTL_GET_NTFS_VOLUME_DATA, None, i)
print(" DeviceIocontrol returned a {:d} bytes long {:}".format(len(buf), type(buf)))
out = struct.unpack_from(vol_data_buf_fmt, buf)
print("\n NumberSectors: {:}\n TotalClusters: {:}\n BytesPerCluster: {:}".format(out[1], out[2], out[6]))
win32api.CloseHandle(vol_handle)
if __name__ == "__main__":
print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
main()
输出:
(py35x64_test) e:\Work\Dev\Whosebug\q053318932>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" ./code00.py Python 3.5.4 (v3.5.4:3f56838, Aug 8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32 volume: \.\E: Passing a buffer size of: 96 DeviceIocontrol returned a 96 bytes long <class 'bytes'> NumberSectors: 494374911 TotalClusters: 61796863 BytesPerCluster: 4096
不用说,将 TotalClusters 乘以 BytesPerCluster,我得到了正确的字节数(由 Win 报告) 我的 E: 驱动器。