Python NtQueryDirectoryFile(文件信息结构)

标签 python python-3.x ctypes ntdll nt-native-api

我编写了一个简单的(测试)脚本来列出选定目录中的文件。不使用 FindFirstFile; 只有 native API。 当我执行脚本并观察时,Win32API 监视器告诉我 STATUS_SUCCESS。 我的文件信息缓冲区是 c_buffer(1024),没有使用 Unicode 缓冲区来查看原始数据。

所以在调用 NtQueryDirectoryFile 之后一切正常。 当我以原始模式编写 c_buffer 以控制台查看目录中的文件时,输出没有结构化。 我创建了一个 FILE_DIRECTORY_INFORMATION 结构,但它在 Windows 7 X86 上不起作用,或者我的代码中存在问题。

我的问题: 请告诉我在 Windows 7 X86 或任何变体上使用哪个 FILE_DIRECTORY_INFORMATION 结构

from ctypes import *

hFile = windll.kernel32.CreateFileW("C:\\a",0x80000000,0,0,3,0x02000000,0)

class Info(Union):
    _fields_ = [('STATUS',c_long),
                ('Pointer',c_ulong),]


class io_stat(Structure):
    _fields_ = [('Stat',Info),
                ('Information',c_ulong),]


class FILE_OBJECT(Structure):
    _fields_ = [('Next',c_ulong),
                ('FileIndex',c_ulong),
                ('ctime',c_longlong),
                ('lat',c_longlong),
                ('wtime',c_longlong),
                ('ch',c_longlong),
                ('Endogfile',c_longlong),
                ('allo',c_longlong),
                ('Fileattr',c_ulong),
                ('Filenalen',c_ulong),
                ('Filename',c_wchar * 2),]

b = io_stat()
a = c_buffer(1024)

windll.ntdll.NtQueryDirectoryFile(hFile,0,0,0,byref(b),byref(a),sizeof(a), 1,0,None,0)

print(a.raw)

未优化。

最佳答案

NtQueryDirectoryFile 应该在循环中调用,直到它返回 STATUS_NO_MORE_FILES。如果返回的状态为 STATUS_BUFFER_OVERFLOW 或状态为成功(非负)且状态 block Information 为 0,则将缓冲区大小加倍并重试。对于每次成功的传递,将 FILE_DIRECTORY_INFORMATION 记录复制出缓冲区。每条记录的大小都必须包含 FileName。当 Next 字段为 0 时,您已到达结尾。

以下示例将 FILE_DIRECTORY_INFORMATION 子类化为 DirEntry 类,该类具有 listbuf 类方法来列出查询缓冲区中的记录。它跳过了“。”和“..”条目。它在 ntlistdir 函数中使用此类,该函数通过 NtQueryDirectoryFile 列出给定目录的 DirEntry 记录。它支持将打开的文件描述符作为 path 参数传递,这类似于 os.listdir 在 POSIX 系统上的工作方式。

ctypes 定义

import os
import msvcrt
import ctypes

from ctypes import wintypes

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

def NtError(status):
    err = ntdll.RtlNtStatusToDosError(status)
    return ctypes.WinError(err)

NTSTATUS = wintypes.LONG
STATUS_BUFFER_OVERFLOW = NTSTATUS(0x80000005).value
STATUS_NO_MORE_FILES = NTSTATUS(0x80000006).value
STATUS_INFO_LENGTH_MISMATCH = NTSTATUS(0xC0000004).value

ERROR_DIRECTORY = 0x010B
INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value
GENERIC_READ = 0x80000000
FILE_SHARE_READ = 1
OPEN_EXISTING = 3
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
FILE_ATTRIBUTE_DIRECTORY = 0x0010

FILE_INFORMATION_CLASS = wintypes.ULONG
FileDirectoryInformation = 1
FileBasicInformation = 4

LPSECURITY_ATTRIBUTES = wintypes.LPVOID
PIO_APC_ROUTINE = wintypes.LPVOID
ULONG_PTR = wintypes.WPARAM

class UNICODE_STRING(ctypes.Structure):
    _fields_ = (('Length',        wintypes.USHORT),
                ('MaximumLength', wintypes.USHORT),
                ('Buffer',        wintypes.LPWSTR))

PUNICODE_STRING = ctypes.POINTER(UNICODE_STRING)

class IO_STATUS_BLOCK(ctypes.Structure):
    class _STATUS(ctypes.Union):
        _fields_ = (('Status',  NTSTATUS),
                    ('Pointer', wintypes.LPVOID))
    _anonymous_ = '_Status',
    _fields_ = (('_Status',     _STATUS),
                ('Information', ULONG_PTR))

PIO_STATUS_BLOCK = ctypes.POINTER(IO_STATUS_BLOCK)

ntdll.NtQueryInformationFile.restype = NTSTATUS
ntdll.NtQueryInformationFile.argtypes = (
    wintypes.HANDLE,        # In  FileHandle
    PIO_STATUS_BLOCK,       # Out IoStatusBlock
    wintypes.LPVOID,        # Out FileInformation
    wintypes.ULONG,         # In  Length
    FILE_INFORMATION_CLASS) # In  FileInformationClass

ntdll.NtQueryDirectoryFile.restype = NTSTATUS
ntdll.NtQueryDirectoryFile.argtypes = (
    wintypes.HANDLE,        # In     FileHandle
    wintypes.HANDLE,        # In_opt Event
    PIO_APC_ROUTINE,        # In_opt ApcRoutine
    wintypes.LPVOID,        # In_opt ApcContext
    PIO_STATUS_BLOCK,       # Out    IoStatusBlock
    wintypes.LPVOID,        # Out    FileInformation
    wintypes.ULONG,         # In     Length
    FILE_INFORMATION_CLASS, # In     FileInformationClass
    wintypes.BOOLEAN,       # In     ReturnSingleEntry
    PUNICODE_STRING,        # In_opt FileName
    wintypes.BOOLEAN)       # In     RestartScan

kernel32.CreateFileW.restype = wintypes.HANDLE
kernel32.CreateFileW.argtypes = (
    wintypes.LPCWSTR,      # In     lpFileName
    wintypes.DWORD,        # In     dwDesiredAccess
    wintypes.DWORD,        # In     dwShareMode
    LPSECURITY_ATTRIBUTES, # In_opt lpSecurityAttributes
    wintypes.DWORD,        # In     dwCreationDisposition
    wintypes.DWORD,        # In     dwFlagsAndAttributes
    wintypes.HANDLE)       # In_opt hTemplateFile

class FILE_BASIC_INFORMATION(ctypes.Structure):
    _fields_ = (('CreationTime',   wintypes.LARGE_INTEGER),
                ('LastAccessTime', wintypes.LARGE_INTEGER),
                ('LastWriteTime',  wintypes.LARGE_INTEGER),
                ('ChangeTime',     wintypes.LARGE_INTEGER),
                ('FileAttributes', wintypes.ULONG))

class FILE_DIRECTORY_INFORMATION(ctypes.Structure):
    _fields_ = (('_Next',          wintypes.ULONG),
                ('FileIndex',      wintypes.ULONG),
                ('CreationTime',   wintypes.LARGE_INTEGER),
                ('LastAccessTime', wintypes.LARGE_INTEGER),
                ('LastWriteTime',  wintypes.LARGE_INTEGER),
                ('ChangeTime',     wintypes.LARGE_INTEGER),
                ('EndOfFile',      wintypes.LARGE_INTEGER),
                ('AllocationSize', wintypes.LARGE_INTEGER),
                ('FileAttributes', wintypes.ULONG),
                ('FileNameLength', wintypes.ULONG),
                ('_FileName',      wintypes.WCHAR * 1))

    @property
    def FileName(self):
        addr = ctypes.addressof(self) + type(self)._FileName.offset
        size = self.FileNameLength // ctypes.sizeof(wintypes.WCHAR)
        return (wintypes.WCHAR * size).from_address(addr).value

DirEntryntlistdir

class DirEntry(FILE_DIRECTORY_INFORMATION):
    def __repr__(self):
        return '<{} {!r}>'.format(self.__class__.__name__, self.FileName)

    @classmethod
    def listbuf(cls, buf):
        result = []
        base_size = ctypes.sizeof(cls) - ctypes.sizeof(wintypes.WCHAR)
        offset = 0
        while True:
            fdi = cls.from_buffer(buf, offset)
            if fdi.FileNameLength and fdi.FileName not in ('.', '..'):
                cfdi = cls()
                size = base_size + fdi.FileNameLength
                ctypes.resize(cfdi, size)
                ctypes.memmove(ctypes.byref(cfdi), ctypes.byref(fdi), size)
                result.append(cfdi)
            if fdi._Next:
                offset += fdi._Next
            else:
                break
        return result

def isdir(path):
    if not isinstance(path, int):
        return os.path.isdir(path)
    try:
        hFile = msvcrt.get_osfhandle(path)
    except IOError:
        return False
    iosb = IO_STATUS_BLOCK()
    info = FILE_BASIC_INFORMATION()
    status = ntdll.NtQueryInformationFile(hFile, ctypes.byref(iosb),
                ctypes.byref(info), ctypes.sizeof(info),
                FileBasicInformation)
    return bool(status >= 0 and info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)

def ntlistdir(path=None):
    result = []

    if path is None:
        path = os.getcwd()

    if isinstance(path, int):
        close = False
        fd = path
        hFile = msvcrt.get_osfhandle(fd)
    else:
        close = True
        hFile = kernel32.CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
                    None, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, None)
        if hFile == INVALID_HANDLE_VALUE:
            raise ctypes.WinError(ctypes.get_last_error())
        fd = msvcrt.open_osfhandle(hFile, os.O_RDONLY)

    try:
        if not isdir(fd):
            raise ctypes.WinError(ERROR_DIRECTORY)
        iosb = IO_STATUS_BLOCK()
        info = (ctypes.c_char * 4096)()
        while True:
            status = ntdll.NtQueryDirectoryFile(hFile, None, None, None,
                        ctypes.byref(iosb), ctypes.byref(info),
                        ctypes.sizeof(info), FileDirectoryInformation,
                        False, None, False)
            if (status == STATUS_BUFFER_OVERFLOW or
                iosb.Information == 0 and status >= 0):
                info = (ctypes.c_char * (ctypes.sizeof(info) * 2))()
            elif status == STATUS_NO_MORE_FILES:
                break
            elif status >= 0:
                sublist = DirEntry.listbuf(info)
                result.extend(sublist)
            else:
                raise NtError(status)
    finally:
        if close:
            os.close(fd)

    return result

示例

if __name__ == '__main__':
    import sys
    for entry in ntlistdir(sys.exec_prefix):
        print(entry)

输出:

<DirEntry 'DLLs'>
<DirEntry 'include'>
<DirEntry 'Lib'>
<DirEntry 'libs'>
<DirEntry 'LICENSE.txt'>
<DirEntry 'NEWS.txt'>
<DirEntry 'python.exe'>
<DirEntry 'python.pdb'>
<DirEntry 'python3.dll'>
<DirEntry 'python36.dll'>
<DirEntry 'python36.pdb'>
<DirEntry 'python36_d.dll'>
<DirEntry 'python36_d.pdb'>
<DirEntry 'python3_d.dll'>
<DirEntry 'pythonw.exe'>
<DirEntry 'pythonw.pdb'>
<DirEntry 'pythonw_d.exe'>
<DirEntry 'pythonw_d.pdb'>
<DirEntry 'python_d.exe'>
<DirEntry 'python_d.pdb'>
<DirEntry 'Scripts'>
<DirEntry 'tcl'>
<DirEntry 'Tools'>
<DirEntry 'vcruntime140.dll'>

关于Python NtQueryDirectoryFile(文件信息结构),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27439379/

相关文章:

python - 5x5 滑动拼图快速低移动解决方案

python - 如何在Python中按另一列的条件过滤时取一列的平均值

python-3.x - 如何在 boto3 中不使用低级客户端的情况下获取 CommonPrefixes?

python - 如何使用 ctypes 将列表列表的 Python 列表转换为 C 数组?

Python ctypes cdll.LoadLibrary,实例化一个对象,执行其方法,私有(private)变量地址被截断

python - 等效于 Python 的 NUnit 或 Jasmine 断言?

python - 根据 pandas 数据框第 3 列中的标准,按天分组的 2 列的加权平均值

python - AIOHTTP - Application.make_handler(...) 已弃用 - 添加多处理

python - 将包含 11 位整数的字节数组转换为 16 位整数数组

python - 表格:FileNotFoundError:[Errno 2](但文件路径正确)