Windows下闪存设备扇区写入异常及地址受限问题求助
问题分析与解决方案
核心问题根源
- 打开目标对象错误:你当前打开的
\\.\D:是逻辑分区,受Windows文件系统保护,写入超出文件系统允许范围(如受控区域)会被拦截,错误5就是系统权限/保护机制导致的结果。 - 写入第一个扇区破坏FAT:分区的第一个扇区是引导扇区,包含FAT文件系统的BPB核心参数(分区大小、FAT表位置等),直接覆盖会彻底破坏文件系统结构。
正确实现步骤
1. 打开物理磁盘而非逻辑分区
要绕过文件系统的保护,必须直接打开整个物理磁盘,而非逻辑分区。物理磁盘的路径格式为\\.\PhysicalDriveN,其中N是磁盘编号(可通过diskpart工具执行list disk命令查看对应U盘的编号)。
2. 调整CreateFile关键参数
直接访问物理磁盘必须添加两个强制标志:
FILE_FLAG_NO_BUFFERING:禁用系统缓存,确保写入操作直接作用于硬件,避免缓存干扰FILE_FLAG_WRITE_THROUGH:绕过系统缓存,强制数据直接写入磁盘,防止数据丢失
同时需设置正确的权限与共享模式:GENERIC_READ | GENERIC_WRITE权限,FILE_SHARE_READ | FILE_SHARE_WRITE共享模式。
3. 转换偏移地址
如果你的position是相对于D盘分区的偏移,需要转换为整个物理磁盘的字节偏移:磁盘字节偏移 = 分区起始扇区 × 扇区大小 + 分区内目标偏移
分区起始扇区可通过DeviceIoControl发送IOCTL_DISK_GET_PARTITION_INFO_EX命令获取,避免手动硬编码。
4. 规避文件系统元数据区
绝对不要写入分区的引导扇区、FAT表、根目录等元数据区域。可通过查询分区BPB参数确定用户数据区的起始位置,仅在该区域内写入。例如FAT32中,用户数据区起始地址为:ReservedSectors + NumberOfFATs × FATSizeSectors(均为BPB中的参数)。
修正后的代码示例
#include <windows.h> #include <iostream> #include <string> // 打开指定编号的物理磁盘 HANDLE openPhysicalDisk(int diskNumber) { char diskPath[64]; sprintf_s(diskPath, "\\\\.\\PhysicalDrive%d", diskNumber); return CreateFileA( diskPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, NULL ); } // 写入物理磁盘扇区(diskByteOffset为整个磁盘的字节偏移) bool writeDiskSector(HANDLE hDisk, size_t diskByteOffset, const uint8_t* buffer, DWORD sectorSize) { if (hDisk == INVALID_HANDLE_VALUE) return false; // 直接访问磁盘要求偏移、缓冲区大小必须是扇区的整数倍 if (diskByteOffset % sectorSize != 0 || sectorSize % 512 != 0) { std::cerr << "偏移或扇区大小未对齐磁盘扇区" << std::endl; CloseHandle(hDisk); return false; } LARGE_INTEGER li; li.QuadPart = diskByteOffset; if (!SetFilePointerEx(hDisk, li, NULL, FILE_BEGIN)) { std::cerr << "设置指针失败,错误码:" << GetLastError() << std::endl; CloseHandle(hDisk); return false; } DWORD bytesWritten; BOOL bRet = WriteFile(hDisk, buffer, sectorSize, &bytesWritten, NULL); if (!bRet) { std::cerr << "WriteFile失败,错误码:" << GetLastError() << std::endl; CloseHandle(hDisk); return false; } if (bytesWritten != sectorSize) { std::cerr << "写入字节数不符,实际写入:" << bytesWritten << std::endl; CloseHandle(hDisk); return false; } CloseHandle(hDisk); return true; } int main() { // 替换为你的U盘对应的磁盘编号(通过diskpart查询) const int targetDiskNumber = 1; // 假设D盘在磁盘上的起始扇区为2048(需实际查询获取) const size_t partitionStartSector = 2048; const DWORD sectorSize = 512; // 要写入的分区内偏移:1MB(2048扇区),转换为磁盘字节偏移 size_t targetDiskOffset = (partitionStartSector + 2048) * sectorSize; uint8_t buffer[512] = {0xAA}; // 测试写入数据 HANDLE hDisk = openPhysicalDisk(targetDiskNumber); if (hDisk == INVALID_HANDLE_VALUE) { std::cerr << "打开物理磁盘失败,错误码:" << GetLastError() << std::endl; return 1; } writeDiskSector(hDisk, targetDiskOffset, buffer, sectorSize) ? std::cout << "写入成功" << std::endl : std::cout << "写入失败" << std::endl; return 0; }
关键注意事项
- 管理员权限:直接访问物理磁盘必须以管理员身份运行程序,否则会返回错误5(拒绝访问)。
- 磁盘对齐:
FILE_FLAG_NO_BUFFERING要求写入的偏移、缓冲区地址、数据大小都必须是扇区大小的整数倍,否则WriteFile会执行失败。 - 获取分区起始扇区:建议通过
DeviceIoControl调用IOCTL_DISK_GET_PARTITION_INFO_EX自动获取分区起始偏移,避免手动硬编码导致的错误。 - 数据备份:操作物理磁盘存在极高风险,务必先备份U盘内所有数据,防止误操作导致数据丢失。
内容的提问来源于stack exchange,提问作者A. Amori
相关产品推荐
相关产品推荐

