已获取磁盘设备,如何遍历磁盘文件并获取其设备路径?
遍历磁盘文件并获取设备路径的实现方案
核心实现步骤
要完成遍历磁盘文件并输出\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(¤tFilePath, VolumeDevicePath); if (!NT_SUCCESS(status)) break; status = RtlUnicodeStringCatString(¤tFilePath, &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(¤tFilePath, 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", ¤tFilePath, status)); } ObDereferenceObject(fileObj); ObDereferenceObject(devObj); } else { KdPrint(("IoGetDeviceObjectPointer failed for %wZ: 0x%X\n", ¤tFilePath, 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
相关产品推荐
相关产品推荐

