我正在尝试了解和使用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期刊的代码为基础,而且我认为“试一试”不会使我无所适从

最佳答案

让我们将问题分成几个小部分,然后一次解决。


win32file模块是[GitHub]: mhammond/pywin32 - Python for Windows (pywin32) Extensions的一部分,它是WinAPI的Python包装器


不幸的是,它没有官方的文档页面(或者我不知道),所以下面是我能找到的最好的页面(多年来我一直在使用它)。永不失败(但吸引力较小)的另一种方法是直接查看代码
[ActiveState.Docs]: win32file.DeviceIoControl[MS.Docs]: DeviceIoControl function的包装

DeviceIoControl的行为有所不同,具体取决于dwIoControlCode(第二个参数)。对于FSCTL_GET_NTFS_VOLUME_DATA,它将使用卷特定数据填充缓冲区。从[MS.Docs]: FSCTL_GET_NTFS_VOLUME_DATA IOCTL


  lpOutBuffer指向输出缓冲区的指针,它是一个NTFS_VOLUME_DATA_BUFFER(@CristiFati:!!!!!!!!与输入缓冲区中指定的文件标识符关联的文件记录在此缓冲区中返回。有关如何确定此缓冲区的正确大小的特定信息,请参考NTFS_VOLUME_DATA_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模块,用于在二进制数据和“常规”数据之间进行转换。它包含所有格式字符的含义(q,Q,L,...),等等。您还可以查看[SO]: Python struct.pack() behavior了解更多(实际)详细信息


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

一些注意事项:


如果一个人不知道函数的作用(返回),那么他们可能不应该使用它(当然,请先阅读手册)。尽管如今,Win(对常规用户始终有很多限制)和Nix都“保护用户免受自身攻击”(例如:不再允许root登录,写保护%SystemDrive%,...)
尝试(尝试和错误)显示出缺乏经验(可能每个人都在某个时候做到了,关键不是仅仅依靠它)
“我每次想从DeviceIoControl中获取一些东西时,都必须记住这些格式吗?”


再说一次,如果不知道某个函数确实起作用,调用它的原因是什么?如果您要认真学习NTFS_VOLUME_DATA_BUFFER,则绝对不是这样。您应该仅在使用它时知道它的结构(并且您已经注意到,有一些地方可以从中获得它-包括这篇文章:))

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


您的理解是正确的。但是win32file.DeviceIoControl有时(可能在到达96字节后到达第一个NULL时)在传递大于期望值(通过length参数)的值时截断输出缓冲区。通过较小的时,它将失败(按预期方式)



我还准备了一个虚拟的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\StackOverflow\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,就可以得到E:驱动器的正确字节数(由Win报告)。

关于python - 在win32file.DeviceIoControl上对结构进行解压缩,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53318932/

10-11 10:46