You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

已获取磁盘设备,如何遍历磁盘文件并获取其设备路径?

遍历磁盘文件并获取设备路径的实现方案

核心实现步骤

要完成遍历磁盘文件并输出\Device\Harddisk0\Volume4...格式的设备路径,需依次执行以下操作:

  • 从物理磁盘设备对象枚举关联的逻辑卷设备路径
  • 打开每个卷的根目录,通过内核API遍历目录内容
  • 对每个文件/目录,从其文件对象中提取完整设备路径
  • 严格管理内核资源,避免内存泄漏和对象引用遗漏

关键内核API说明

  • IoGetVolumeDeviceNames:从物理磁盘设备对象获取所有关联的卷设备路径
  • ZwCreateFile:打开卷根目录或单个文件,获取内核句柄
  • ZwQueryDirectoryFile:遍历目录中的文件/目录项
  • IoGetDevicePathName:从文件对象生成对应的设备路径字符串
  • ObDereferenceObject:释放文件/设备对象的引用计数

修改后的完整代码

#define _NTIFS_INCLUDED_

#include <initguid.h>
#include <ntddk.h>
#include <ntdef.h>
#include <ntstrsafe.h>
#include <ntifs.h>
#include <ntddstor.h>

VOID DriverUnload(PDRIVER_OBJECT DriverObject);
NTSTATUS EnumerateVolumeFiles(PUNICODE_STRING VolumeDevicePath);
NTSTATUS GetFileDevicePath(PFILE_OBJECT FileObject, PUNICODE_STRING OutPath);

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
    NTSTATUS status;

    DriverObject->DriverUnload = DriverUnload;

    PZZWSTR paths = NULL;
    WCHAR wctmp[2048];

    // 获取所有磁盘设备接口
    status = IoGetDeviceInterfaces(&GUID_DEVINTERFACE_DISK, NULL, 0, &paths);
    if (!NT_SUCCESS(status)) {
        KdPrint(("IoGetDeviceInterfaces failed: 0x%X\n", status));
        return status;
    }

    ULONG index = 0;
    UNICODE_STRING diskInterfaceStr;
    diskInterfaceStr.MaximumLength = sizeof(wctmp);
    diskInterfaceStr.Buffer = wctmp;

    while (paths[index] != 0) {
        ULONG pathLen = wcslen(paths + index);
        diskInterfaceStr.Length = pathLen * sizeof(WCHAR);
        RtlCopyMemory(wctmp, paths + index, diskInterfaceStr.Length);

        // 获取磁盘的设备对象和文件对象
        PFILE_OBJECT diskFileObj = NULL;
        PDEVICE_OBJECT diskDevObj = NULL;
        status = IoGetDeviceObjectPointer(&diskInterfaceStr, GENERIC_READ, &diskFileObj, &diskDevObj);
        if (NT_SUCCESS(status)) {
            // 获取该磁盘关联的所有卷设备路径
            PUNICODE_STRING volumePaths = NULL;
            status = IoGetVolumeDeviceNames(diskDevObj, &volumePaths);
            if (NT_SUCCESS(status)) {
                ULONG volIndex = 0;
                while (volumePaths[volIndex].Buffer != NULL) {
                    KdPrint(("Found volume: %wZ\n", &volumePaths[volIndex]));
                    // 枚举该卷下的所有文件
                    EnumerateVolumeFiles(&volumePaths[volIndex]);
                    volIndex++;
                }
                ExFreePool(volumePaths);
            } else {
                KdPrint(("IoGetVolumeDeviceNames failed for disk %wZ: 0x%X\n", &diskInterfaceStr, status));
            }

            // 释放对象引用
            ObDereferenceObject(diskFileObj);
            ObDereferenceObject(diskDevObj);
        } else {
            KdPrint(("IoGetDeviceObjectPointer failed for %wZ: 0x%X\n", &diskInterfaceStr, status));
        }

        index += pathLen + 1;
    }

    ExFreePool(paths);
    return STATUS_SUCCESS;
}

// 枚举单个卷下的所有文件
NTSTATUS EnumerateVolumeFiles(PUNICODE_STRING VolumeDevicePath) {
    NTSTATUS status;
    HANDLE hRootDir = NULL;
    OBJECT_ATTRIBUTES objAttr;
    
    // 构造卷根目录路径:VolumeDevicePath + L"\\"
    WCHAR rootDirBuf[2048];
    UNICODE_STRING rootPath = {0};
    rootPath.Buffer = rootDirBuf;
    rootPath.MaximumLength = sizeof(rootDirBuf);

    status = RtlUnicodeStringCopy(&rootPath, VolumeDevicePath);
    if (!NT_SUCCESS(status)) {
        return status;
    }
    status = RtlUnicodeStringCatString(&rootPath, &RTL_CONSTANT_STRING(L"\\"));
    if (!NT_SUCCESS(status)) {
        return status;
    }

    InitializeObjectAttributes(&objAttr, &rootPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);

    // 打开根目录
    IO_STATUS_BLOCK ioStatus;
    status = ZwCreateFile(&hRootDir,
                          FILE_LIST_DIRECTORY | SYNCHRONIZE,
                          &objAttr,
                          &ioStatus,
                          NULL,
                          FILE_ATTRIBUTE_DIRECTORY,
                          FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                          FILE_OPEN,
                          FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
                          NULL,
                          0);
    if (!NT_SUCCESS(status)) {
        KdPrint(("ZwCreateFile failed for volume %wZ: 0x%X\n", VolumeDevicePath, status));
        return status;
    }

    // 分配目录查询缓冲区
    PFILE_DIRECTORY_INFORMATION dirInfo = (PFILE_DIRECTORY_INFORMATION)ExAllocatePoolWithTag(PagedPool, 4096, 'DirE');
    if (!dirInfo) {
        ZwClose(hRootDir);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    // 遍历目录
    ULONG bytesReturned;
    UNICODE_STRING currentFilePath;
    WCHAR filePathBuf[2048];
    currentFilePath.Buffer = filePathBuf;
    currentFilePath.MaximumLength = sizeof(filePathBuf);

    while (TRUE) {
        status = ZwQueryDirectoryFile(hRootDir,
                                      NULL,
                                      NULL,
                                      NULL,
                                      &ioStatus,
                                      dirInfo,
                                      4096,
                                      FileDirectoryInformation,
                                      FALSE,
                                      NULL,
                                      FALSE);
        if (!NT_SUCCESS(status)) {
            if (status == STATUS_NO_MORE_FILES) {
                status = STATUS_SUCCESS;
            }
            break;
        }

        PFILE_DIRECTORY_INFORMATION entry = dirInfo;
        do {
            // 跳过.和..目录
            if (entry->FileName[0] == L'.' && 
                (entry->FileNameLength == sizeof(WCHAR) || 
                (entry->FileNameLength == 2*sizeof(WCHAR) && entry->FileName[1] == L'.'))) {
                entry = (PFILE_DIRECTORY_INFORMATION)((PUCHAR)entry + entry->NextEntryOffset);
                continue;
            }

            // 构造当前文件的完整路径
            status = RtlUnicodeStringCopy(&currentFilePath, VolumeDevicePath);
            if (!NT_SUCCESS(status)) break;
            status = RtlUnicodeStringCatString(&currentFilePath, &RTL_CONSTANT_STRING(L"\\"));
            if (!NT_SUCCESS(status)) break;
            
            if (currentFilePath.Length + entry->FileNameLength > currentFilePath.MaximumLength) {
                KdPrint(("File path too long: %.*ws\n", entry->FileNameLength/sizeof(WCHAR), entry->FileName));
                entry = (PFILE_DIRECTORY_INFORMATION)((PUCHAR)entry + entry->NextEntryOffset);
                continue;
            }
            
            currentFilePath.Length += entry->FileNameLength;
            RtlCopyMemory(currentFilePath.Buffer + (currentFilePath.Length - entry->FileNameLength)/sizeof(WCHAR), 
                          entry->FileName, entry->FileNameLength);

            // 获取文件对象指针,进而获取设备路径
            PFILE_OBJECT fileObj = NULL;
            PDEVICE_OBJECT devObj = NULL;
            status = IoGetDeviceObjectPointer(&currentFilePath, GENERIC_READ, &fileObj, &devObj);
            if (NT_SUCCESS(status)) {
                UNICODE_STRING devicePath;
                WCHAR devPathBuf[2048];
                devicePath.Buffer = devPathBuf;
                devicePath.MaximumLength = sizeof(devPathBuf);
                status = GetFileDevicePath(fileObj, &devicePath);
                if (NT_SUCCESS(status)) {
                    KdPrint(("File device path: %wZ\n", &devicePath));
                } else {
                    KdPrint(("GetFileDevicePath failed for %wZ: 0x%X\n", &currentFilePath, status));
                }
                ObDereferenceObject(fileObj);
                ObDereferenceObject(devObj);
            } else {
                KdPrint(("IoGetDeviceObjectPointer failed for %wZ: 0x%X\n", &currentFilePath, status));
            }

            entry = (PFILE_DIRECTORY_INFORMATION)((PUCHAR)entry + entry->NextEntryOffset);
        } while (entry->NextEntryOffset != 0);
    }

    // 释放资源
    ExFreePool(dirInfo);
    ZwClose(hRootDir);
    return status;
}

// 从文件对象获取设备路径
NTSTATUS GetFileDevicePath(PFILE_OBJECT FileObject, PUNICODE_STRING OutPath) {
    NTSTATUS status;
    UNICODE_STRING deviceName;

    // 获取文件对象对应的设备名称
    status = IoGetDevicePathName(FileObject, &deviceName);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    // 复制到输出缓冲区
    status = RtlUnicodeStringCopy(OutPath, &deviceName);
    ExFreePool(deviceName.Buffer);
    return status;
}

VOID DriverUnload(PDRIVER_OBJECT DriverObject) {
    KdPrint(("Driver unloaded.\n"));
}

重要注意事项

  • 内核模式下必须严格遵循资源管理规则:所有分配的池内存必须释放,所有通过IoGetDeviceObjectPointer获取的对象必须调用ObDereferenceObject释放引用
  • IoGetVolumeDeviceNames仅支持Windows Vista及以上版本的Windows系统
  • 遍历目录时需处理STATUS_NO_MORE_FILES状态,以此判断遍历完成
  • 必须跳过.和..目录,避免出现无限递归遍历的情况
  • 输出的设备路径格式由系统卷命名规则决定,\Device\HarddiskX\VolumeY是标准的卷设备路径格式

内容的提问来源于stack exchange,提问作者CukiPid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 06:59:50