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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 04:07:33