在win32file.DeviceIoControl上对结构进行解压缩

时间:2018-11-15 11:53:54

标签: python struct pywin32 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”有效,而“ QQQQQQQQQQQQQQQQQQQQQ”无效。所以我的问题是-

  • 我的理解是,如果缓冲区大于输出,我们可以解压缩,为什么解压缩不起作用?

  • 每次我想从DeviceIoControl中获取内容时,我都必须记住这些格式吗?

将我带到资源上也将是一个额外的收获,因为我需要以代码为基础来阅读USN期刊,而且我认为尝试后尝试不会带我到任何地方

1 个答案:

答案 0 :(得分:2)

让我们将问题分成小块,然后每次都做。

  • win32file 模块是[GitHub]: mhammond/pywin32 - Python for Windows (pywin32) Extensions的一部分,该模块是 WinAPI s

    上的 Python 包装器
  • DeviceIoControl 的行为有所不同,具体取决于 dwIoControlCode (参数2 nd )。对于 FSCTL_GET_NTFS_VOLUME_DATA ,它将使用卷特定数据填充缓冲区。来自[MSDN]: FSCTL_GET_NTFS_VOLUME_DATA control code

      

    lpOutBuffer
    指向输出缓冲区的指针,NTFS_VOLUME_DATA_BUFFER(@ CristiFati: !!!损坏的 URL !!! )结构。与输入缓冲区中指定的文件标识符关联的文件记录在此缓冲区中返回。有关如何确定此缓冲区的正确大小的特定信息,请参考 NTFS_VOLUME_DATA_BUFFER 结构的文档的“备注”部分。

    这是上述损坏的 URL 的替代方法:[MSDN]: NTFS_VOLUME_DATA_BUFFER structure。由于不确定该文件的有效期限,因此我在下面粘贴了结构定义(来自 Windows套件 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]: struct - Interpret bytes as packed binary data模块,用于在二进制数据和“常规”数据之间进行转换。它包含所有格式字符的含义( q Q L 等)。您还可以查看[SO]: Python struct.pack() behavior了解更多(实际)详细信息

浏览完以上材料后,情况应该会更清楚。

一些注意事项:

  • 如果一个人不知道函数的作用(返回),那么他们可能不应该使用它(当然,请先阅读手册)。尽管如今, Win (对常规用户始终有很多限制)和 Ux 都“保护用户免受自身侵害”(例如: root 不再允许登录,写保护%SystemDrive%
  • 尝试(尝试和错误)显示出一些经验不足(可能在某个时候做了每件事,关键是不能完全依靠它)
  • 每次我想从DeviceIoControl中获取某些东西时,我都必须记住这些格式吗?”?
    • 再次,如果不知道某个函数是否起作用,调用它的原因是什么?如果您要认真学习 NTFS_VOLUME_DATA_BUFFER ,则绝对不是这样。您应该仅在使用它时知道它的结构(并且您已经注意到,有一些地方可以从中获得它-包括这篇文章:))
  • 我的理解是,如果缓冲区大于输出,我们可以解压缩,为什么解压缩不起作用?
    • 您的理解是正确的。但是 win32file.DeviceIoControl 似乎有时 (可能是在达到1 st NULL 之后 96 字节)在传递大于期望值(通过 length 参数)的值时截断输出缓冲区。当传递较小的时,它将失败(按预期方式)

我还准备了一个虚拟的 Python 示例。

code.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\StackOverflow\q053318932>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" ./code.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:驱动器。