磁盘文件恢复本质是直接读取物理扇区并解析残留文件系统结构,而非调用高级api打开已删文件;需以管理员权限用createfilea或open获取原始设备句柄,严格扇区对齐、禁用缓存,并手动解码mft运行列表或fat目录项等二进制结构。

磁盘文件恢复不是靠“读取已删除文件”,而是靠解析文件系统残留结构
直接调用 fopen 或 std::ifstream 打开一个“被删的文件名”必然失败——操作系统早已把该文件的目录项标记为可用、并可能覆盖其元数据。真正可行的路径是:以只读方式打开物理磁盘(如 \\.\PhysicalDrive0),逐扇区扫描,结合文件系统(NTFS/FAT32/ext4)的格式规范识别残留的文件记录、MFT条目、FAT链或inode块。
这意味着你必须:
- 用
CreateFileA(Windows)或open("/dev/sda", O_RDONLY)(Linux)获取原始设备句柄,且需管理员/root权限 - 手动解析二进制结构:比如 NTFS 的 MFT 记录起始偏移、属性类型(
$DATA、$FILE_NAME)、运行列表(RUN_LIST)解码逻辑簇号(LCN)到物理扇区 - 不依赖任何高级抽象(如
std::filesystem),所有字节操作都用uint8_t*+memcpy+ 位域结构体完成
Windows 下用 CreateFileA 打开物理驱动器必须绕过缓存和独占锁
常见错误是传入 GENERIC_READ 但没加关键标志,导致返回 INVALID_HANDLE_VALUE 并报错 ERROR_ACCESS_DENIED。正确做法是:
- 路径必须是
"\\.\PhysicalDriveN"格式(N 从 0 开始),不能是"C:"或"\\?\C:" -
dwFlagsAndAttributes必须包含FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH,否则系统可能拒绝访问 - 调用前需用
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED)防止休眠中断扫描 - 每次
ReadFile的缓冲区大小必须是磁盘扇区对齐(通常 512 或 4096 字节),且内存地址也需对齐(用_aligned_malloc)
示例关键片段:
HANDLE hDrive = CreateFileA("\\.\PhysicalDrive0",
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr,
OPEN_EXISTING,
FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH,
nullptr);
if (hDrive == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError(); // 检查是否为 ERROR_ACCESS_DENIED
}
NTFS 文件恢复核心:从 MFT 提取未被覆盖的 $DATA 属性并映射扇区
NTFS 删除文件后,只要 MFT 记录未被复用,就能还原文件名、大小和数据位置。难点不在读 MFT,而在解析 $DATA 属性的运行列表(RUN_LIST)——它用变长编码存储簇偏移,不是简单指针。
- MFT 起始位置由引导扇区中
bytes_per_mft_record和mft_start_lcn计算得出,不能硬编码为 0x30000 - 每个 MFT 记录含多个属性,需遍历
ATTRIBUTE_HEADER找到类型为0x80($DATA)且non_resident_flag == 1的属性 - 运行列表解码时,首字节低 4 位是长度字节数,高 4 位是偏移字节数;后续每组字节按此规则拆出增量 LCN 和长度簇数
- 得到 LCN 后,需乘以
sectors_per_cluster再加上磁盘起始扇区,才能换算成ReadFile的实际扇区偏移
漏掉任一环节(比如忽略 non_resident_flag 直接读 resident data),就会把小文件内容当垃圾跳过。
别碰 FAT32 的根目录区,优先扫描数据区找 0x00/0xE5 前缀的目录项
FAT32 删除文件时,目录项首字节被改写为 0xE5(或清零),但文件数据本身仍留在数据区,直到被新文件覆盖。比起解析 FAT 表查簇链,更可靠的是:
- 跳过 FAT 表和根目录区(FAT32 根目录已不限大小),直接从数据区起始扇区开始扫描
- 对每个扇区检查是否匹配常见文件头(如
0x89 0x50 0x4E 0x47对应 PNG,0xFF 0xD8 0xFF对应 JPEG) - 一旦命中,反向查找最近的
0xE5或0x00目录项,提取文件名、大小、起始簇号 - 用 FAT 表验证该簇链是否连续且未被标记为坏簇(值不为
0xFFF7或0x0FFFFFF7)
这种“文件头扫描法”虽无法恢复文件名和时间戳,但对照片、文档等大文件恢复率更高,且绕开了 FAT 表损坏带来的解析失败风险。
最易被忽略的是扇区对齐和权限控制——哪怕解析逻辑完全正确,一次未对齐的 ReadFile 或漏掉 FILE_FLAG_NO_BUFFERING,整个程序就卡死在权限拒绝上,连第一个扇区都读不出来。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











