简介:这是一份面向数据恢复、系统调试与安全分析人员的PPT演示文稿,以磁盘结构为主线,结合WinHex界面逐步截图,由浅入深地讲解MBR中分区表(16字节/项)、0x55AA结束标识的识别,以及DBR中FAT表个数、起始扇区数等关键字段的读取方法。通过具体数字(如分区起始2048、FAT表3234、每表14767扇区)推导演算,并重点说明了分区内扇区与物理磁盘扇区的偏移换算,避免因相对定位导致失败。后续还展示了目录项中高低字节合成起始簇号的方法,以及1簇=16扇区的转换关系,最终算出该文件首扇区为34896。整个流程步骤清晰,配有界面截图与算式,便于跟练和复盘。资源包仅1个pptx文件,大小约1.1MB,已获得76人次学习。
1. 用 WinHex 找文件第一扇区:一切磁盘诊断的起点
拿到一块有坏道的旧硬盘,或者一个被标记为“已感染”的 U 盘时,普通资源管理器只能告诉你“文件存在”或“文件打不开”。真正的问题往往不是文件能不能打开,而是它到底写在磁盘的哪一个 LBA 扇区上——只有先定位到这个物理位置,才能判断它是否落在坏道区域、是否被其他文件覆盖过、是否真的被修改过。市面上能回答这个问题的工具不多,WinHex 是其中操作最直观、也最经得起现场复核的一个。本文就用 WinHex 把“文件 → 目录条目 → 起始簇 → LBA 扇区”这条链路完整拆开,适合做数据恢复、数字取证和磁盘故障排查的从业者照着复现。
2. 打开物理盘还是镜像:先选对入口再谈扇区地址
2.1 能用镜像就别碰物理盘,先给磁盘留后悔药
很多人拿到 WinHex 的第一反应是直接“文件 → 打开磁盘”,选择一个物理驱动器就开始看。这个做法在只读查看时问题不大,但一旦你想在盘上做恢复实验、写回修改、甚至反复定位多个扇区,直接在物理盘上操作就是在消耗现场。磁盘里的数据不会动,但你的每一次误操作都可能改变它。做取证和恢复的常规做法是:先用 dd、FTK Imager 或 WinHex 自带的“克隆磁盘”功能做一份镜像,再在镜像文件上做所有定位和分析。WinHex 支持打开 .dd、.img、.E01 等格式的镜像文件,也支持直接挂载 VMware 的 .vmdk 虚拟磁盘。
打开物理盘和打开镜像,在 WinHex 里看到的扇区编号基准不一样,这是第一个必须分清的边界。物理磁盘从 0 号扇区开始,0 号扇区通常是 MBR 或 GPT 头;而一个逻辑分区是从它自己的起始扇区开始的,分区内部的“第一个扇区”指的是卷引导扇区,不是磁盘的 0 号扇区。如果你打开的是整个物理盘,文件的第一扇区地址是“绝对 LBA”,包含了前面 MBR、分区表、其他分区的偏移;如果打开的是某一个分区(WinHex 也允许直接打开卷),那地址就只是卷内相对偏移。
这两种地址的换算关系是:
文件在物理盘上的 LBA = 分区起始 LBA + 文件在卷内的起始簇 × 每簇扇区数2.2 用 WinHex 打开镜像并确认扇区大小
WinHex 打开镜像文件的路径是:菜单“文件 → 打开”,选择镜像文件后,WinHex 会自动识别镜像中的分区结构。如果你只是打开一个原始分区镜像(比如从分区备份出来的 .img),它会直接按卷来处理;如果是整盘镜像,WinHex 会弹出一个分区选择窗口,让你选择要浏览的分区。
打开之后,界面左侧是“目录浏览器”,中间是十六进制区,右侧是数据解释器和文件信息。十六进制区每一行左侧的偏移量列,就是当前光标位置在文件或磁盘中的绝对字节偏移。鼠标点任意位置,底部的状态栏会显示对应的十进制字节偏移和十六进制偏移。
在做任何计算前,先确认扇区大小。绝大多数 SATA 磁盘逻辑扇区是 512 字节,但很多新出的 4K 高级格式化盘和 NVMe 固态盘逻辑扇区是 4096 字节。WinHex 默认按 512 字节显示扇区编号,如果盘是 4K 扇区,直接按默认值计算会得到错误的扇区号。
可以在 WinHex 里查看引导扇区的 BPB 字段来确认:
def parse_bpb(sector_data): """解析引导扇区前 512 字节,返回文件系统关键参数 sector_data: 引导扇区原始字节,长度至少 512 """ bytes_per_sector = int.from_bytes(sector_data[11:13], "little") sectors_per_cluster = sector_data[13] reserved_sectors = int.from_bytes(sector_data[14:16], "little") fat_count = sector_data[16] print(f"每扇区字节数: {bytes_per_sector}") print(f"每簇扇区数: {sectors_per_cluster}") print(f"保留扇区数: {reserved_sectors}") print(f"FAT 表副本数: {fat_count}") return bytes_per_sector, sectors_per_cluster这个脚本只做一件事:把引导扇区里最关键的几个 BPB 字段解码出来。第 11 到 12 字节是“每扇区字节数”,一般就是 512 或 4096;第 13 字节是“每簇扇区数”,这是后面把簇号换算成扇区号的关键参数。第 14 到 15 字节是保留扇区数,FAT32 通常为 32 左右,NTFS 通常为 8。第 16 字节是 FAT 表副本数,常见值是 2。
拿到这些参数后,你才能确定“每簇扇区数”这个乘数,换算才靠谱。
2.3 分区起始地址怎么计入总偏移
如果打开的是整盘镜像,WinHex 会直接解析分区表,你在目录浏览器里看到的文件树是自动挂载好的。但如果 WinHex 没能自动识别分区,或者你只拿到了一段裸分区数据,就需要手动从 MBR 里读分区起始地址。
MBR 位于 0 号扇区,分区表从偏移 446 字节处开始,每条分区表项 16 字节。第 8 到 11 字节是该分区起始 LBA,用小端序存储。举个例子:起始 LBA 为 0x00000800,换算成十进制就是 2048,这也是 Windows 创建分区时最常见的对齐值。
手动读分区起始地址时,注意区分 GPT 磁盘。GPT 磁盘的 0 号扇区是保护性 MBR,真正的分区表起始于 1 号扇区的 GPT 头。WinHex 对 GPT 支持很好,但手动解析时要找对地方,别把保护性 MBR 里的分区项当成真实分区。
2.4 打开镜像后必看的四个偏移位置
定位文件第一扇区之前,有四个十六进制位置建议先跳过去看一眼,建立“偏移感”:
- 0 号扇区(绝对偏移 0x00000000):MBR 或 GPT 保护性 MBR,里面能看到分区表。
- 分区起始扇区(比如 LBA 2048):卷引导扇区,包含上面的 BPB 字段。
- $MFT 起始位置(NTFS):引导扇区偏移 0x30 处的 8 字节记录了 $MFT 的起始簇号,乘以每簇字节数就是 MFT 文件在卷内的偏移。
- FAT1 表起始位置(FAT32):从引导扇区的 BPB 计算,保留扇区数就是 FAT1 的起始扇区。
这四个位置分别对应分区表入口、文件系统参数、主文件表和分配表,任何一个读错,后续定位文件的首扇区都会跟着错。
3. 定位文件第一个扇区:目录浏览器跳转与簇号换算
3.1 在目录浏览器里选中文件,切到数据视图
打开镜像后,左侧的“目录浏览器”会按目录树展示卷里的文件和文件夹。找到目标文件,单击它,WinHex 的十六进制区会跳到两个地方之一:文件的数据内容,或者文件的目录记录。区分这两个视图非常重要——WinHex 通过不同的入口进入,显示的偏移量完全不同。
在 WinHex 中,目录浏览器默认展示文件系统条目,单击文件后,十六进制区展示的是该文件对应的目录条目或 MFT 记录,并不是文件内容本身。你会在十六进制区看到文件名、时间戳、属性等元数据。这时要查看文件内容,需要在 WinHex 的“文件系统”菜单里选择“由目录记录跳转到数据”或类似操作,WinHex 会重新定位到文件数据的第一个字节。
在数据视图下,十六进制区显示的是文件内容的原始字节,左侧偏移量是这些字节在卷或镜像中的绝对位置。这就是你要找的“第一个扇区”的字节偏移。
判断自己处于哪个视图很简单:如果十六进制区里能直接看到文件的文件名 ASCII 字符串,说明停在目录记录;如果看到的是文件内容的文件头签名,比如 PNG 文件的 89 50 4E 47,说明已经停在数据区。
3.2 FAT32 卷:从目录条目读起始簇
如果目标盘是 FAT32(常见于 U 盘和小容量 SD 卡),定位方式是从目录条目里读起始簇号。FAT32 目录条目固定 32 字节,其中偏移 0x1A 到 0x1B 两个字节是文件起始簇号的高位部分,偏移 0x14 到 0x15 是低位部分。但在删除后重命名等场景下,这两个字段的位置可能变化,实际读取时要看条目属性。
在 WinHex 的目录条目视图里,找到目标文件对应的高亮条目,从偏移 0x14 和 0x1A 读出两个 16 位值,组合成 32 位簇号。比如低 16 位是 0x0003,高 16 位是 0x0000,起始簇就是 3。
得到起始簇后,文件首扇区的计算是:
数据区起始扇区 = 保留扇区数 + FAT 表数 × 每个 FAT 表的扇区数 首扇区 = 数据区起始扇区 + (起始簇 - 2) × 每簇扇区数这里减 2 是因为 FAT32 的前两个簇号被保留,数据区从簇 2 开始。
3.3 NTFS 卷:从 $MFT 记录的 runlist 里找数据起始
NTFS 的定位方式和 FAT32 不同。NTFS 文件的元数据存在 $MFT 的文件记录里,记录大小通常是 1024 字节。文件记录中,0x80 属性是数据属性,属性体里有一串 runlist,每个 run 描述了数据在卷上的物理位置。
WinHex 目录浏览器点击文件后,十六进制区展示的就是这个 MFT 记录。你可以在 0x80 属性的属性头偏移 0x20 到 0x27 处读到“起始 VCN”(虚拟簇号),但真正有用的不是 VCN,而是 runlist 里记录的 LCN(逻辑簇号)。runlist 的格式是:一个字节的高 4 位表示长度字段的字节数,低 4 位表示偏移字段的字节数,随后是长度值和偏移值。
简单文件的 runlist 往往只有一项,比如 0x21 18 00 60 3C 00 00,含义是:长度字段占 1 字节,偏移字段占 2 字节;长度 0x18 个簇,偏移 0x3C60 簇。这个文件的起始 LCN 就是 0x3C60,乘以每簇扇区数,再加上卷内偏移,得到首扇区。
如果 runlist 有多项,说明文件是碎片化的。每项表示一段连续簇,但各段在磁盘上不一定相邻。只有第一项的偏移值才是文件数据真正开始的位置。
3.4 簇号到 LBA 扇区号的换算脚本
手工读偏移字段容易出错,尤其是 runlist 压缩存储后。我通常会把读出来的簇号丢进下面这个小脚本里换算,省得心算错一个字节导致满盘皆输:
import sys def cluster_to_lba(cluster, sectors_per_cluster, partition_start_lba): """ 文件系统簇号转磁盘 LBA 扇区号 cluster: 起始簇号(FAT32 从目录条目读出的 32 位值,NTFS 从 runlist 读出) sectors_per_cluster: 每簇扇区数,引导扇区 BPB 偏移 13 读 partition_start_lba: 分区在整盘镜像中的起始 LBA """ # 文件系统数据区逻辑上从簇 2 开始 relative_sector = (cluster - 2) * sectors_per_cluster return partition_start_lba + relative_sector def lba_to_offset(lba, sector_size=512): """LBA 扇区号转字节偏移,WinHex 里 Ctrl+G 跳转用的是这个""" return lba * sector_size if __name__ == "__main__": cluster = int(sys.argv[1]) spc = int(sys.argv[2]) start_lba = int(sys.argv[3]) if len(sys.argv) > 3 else 2048 lba = cluster_to_lba(cluster, spc, start_lba) offset = lba_to_offset(lba) print(f"起始簇: {cluster}") print(f"每簇扇区数: {spc}") print(f"分区起始 LBA: {start_lba}") print(f"文件第一个数据扇区 LBA: {lba}") print(f"对应字节偏移 (十六进制): 0x{offset:X}")参数说明:第一个参数填起始簇号,第二个填每簇扇区数,第三个填分区起始 LBA。如果打开的是单独的分区镜像而不是整盘镜像,第三个参数填 0 即可。脚本输出的 LBA 就是文件首扇区的地址,输出的十六进制偏移可以直接放进 WinHex 的“导航 → 转到偏移量”对话框里跳转。
4. 拿到扇区号之后:文件头校验与碎片追踪
4.1 用前 48 字节验证文件签名
跳转到计算出的扇区后,先别急着信这个结果。验证方法很简单:看十六进制区的前几个字节是不是这个文件类型应有的文件头签名。常见的文件签名:
| 文件类型 | 十六进制签名 |
|---|---|
| PNG 图片 | 89 50 4E 47 0D 0A 1A 0A |
| JPEG 图片 | FF D8 FF E0 或 FF D8 FF E1 |
| PDF 文档 | 25 50 44 46 |
| ZIP 压缩包 | 50 4B 03 04 |
| Windows PE 执行文件 | 4D 5A 90 00 |
| Office 2007+ 文档 | 50 4B 03 04(本质是 ZIP) |
签名匹配后,再检查文件大小和扇区范围是否自洽。比如一个 PNG 文件大小为 153,600 字节,首扇区是 LBA 2048,那么理论上文件应结束在 2048 + 153600 / 512 = 2048 + 300 = 2348 扇区附近。跳到 LBA 2348,应该能看到文件末尾的 IEND 块(PNG)或 00 填充字节。
4.2 连续文件的扇区区间估算
当文件没有碎片时,它的数据在磁盘上是连续的一段。知道首扇区后,可以直接推算出整段范围:占用扇区数约等于文件大小向上对齐到簇大小后的值。比如 NTFS 卷每簇 4096 字节,文件 100,000 字节,会占用 25 个簇(100000 / 4096 向上取整),对应 200 个扇区。从首扇区开始连续数 200 个扇区,就是文件的全部数据。
这种估算适合快速确认文件没有跨到某个坏道区域。如果这段范围内的某个扇区读取超时或返回全 0xFF,说明文件可能落到了坏道区。
怀疑文件碎片化时,在上述估算区间内会看到一部分数据是其他文件的头部签名——这是最直接的碎片证据。
4.3 碎片文件的 runlist 怎么追完
NTFS 的碎片文件在 0x80 属性里会有多段 runlist。每一段的偏移字段是相对前一段的增量,不是绝对位置。第一段的偏移值是绝对 LCN,第二段的偏移值是在第一段结束位置上的增量,以此类推。
举个例子:第一段长度 0x20 簇,偏移 0x1000 簇;第二段长度 0x40 簇,偏移 0x2000 簇。那么第一段占据 LCN 0x1000 到 0x101F,第二段实际起始是 0x1000 + 0x20 + 0x2000 = 0x3020 簇。
如果手工追多段 runlist,建议每段都单独换算成 LBA 并记录在一张表里,不要只记首扇区。WinHex 对多段 runlist 的解析比较直接,但底层原理是这个,知道以后你才能识别 WinHex 有没有解析错。
FAT32 的碎片追踪则是顺着 FAT 表走。在目录条目里读到起始簇号后,到 FAT1 表里读取该簇号对应的 32 位值,这个值指向下一簇。值为 0x0FFFFFFF 表示文件结束,值为 0x0FFFFFF7 表示坏簇。逐项读下去,就能画出完整的簇链。
4.4 用其他工具交叉验证
定位结果不能只信一个工具。我最常用的交叉验证组合是:WinHex 算出首扇区后,用 DiskGenius 的“扇区编辑”功能跳到同一个 LBA,看文件签名是否一致。DiskGenius 的扇区跳转界面按 LBA 编号,天然与 WinHex 对得上,两者互相印证基本可以排除软件解析错误。
另一个补充手段是 chkdsk。在 Windows 下对目标卷运行chkdsk /r,它会报告坏扇区的 LBA 范围。如果这个范围恰好覆盖了文件首扇区,多半就能解释文件为什么读取异常。注意,/r参数会尝试修复,只读验证用chkdsk不带参数即可。
同步检查 Windows 事件查看器里的磁盘警告日志,许多坏道问题会在事件里记录“设备 \Device\Harddisk0\DR0 上有坏块”之类的条目,配合扇区定位能确认故障源。
5. WinHex 找扇区 5 个常见坑:扇区大小、镜像错位与缓存
5.1 打开整盘镜像却用卷内偏移计算,扇区号差了好几个 GB
现象:在目录浏览器里找到文件,按 WinHex 显示的偏移量跳转,却落在了一堆无关数据上。
原因:WinHex 打开整盘镜像时,十六进制区的偏移量包含分区表的偏移;而你按 BPB 算出来的偏移往往是卷内相对偏移。两者之间差了一个分区起始地址。
解决:先看 WinHex 底部的扇区编号是“物理扇区”还是“相对扇区”。WinHex 20 以上版本在打开整盘镜像时,状态栏会标注当前扇区的绝对编号。如果分不清,直接在脚本里把分区起始 LBA 加上。这比盯着界面猜要稳妥。
5.2 4K 扇区盘按 512 字节计算,簇号实际是扇区号的 1/8
现象:算出的 LBA 指向的位置文件头总差一点,偏移量和实际文件位置相差 8 倍。
原因:4K 高级格式化盘的物理扇区是 4096 字节,虽然逻辑扇区仍然是 512 字节(为了兼容旧系统),但在做底层映射时一次读写的最小单位是 4096。WinHex 打开磁盘时可能按物理介质属性显示扇区大小为 4096,导致扇区号的含义发生偏移。
解决:打开磁盘后第一时间在 WinHex 右侧信息面板确认“介质参数 — 字节/扇区”的值。如果显示 4096,后续公式里的 sector_size 就用 4096。在 WinHex 里跳转扇区时,注意对话框里也有扇区大小选项,默认是 512,手动改成 4096。
5.3 打开了逻辑卷而不是物理盘,LBA 对不上故障盘报告
现象:用 WinHex 打开“C:”这种逻辑卷,定位出来的扇区号只有几十万,但 BIOS 或磁盘检测软件报告的坏道地址是几千万。
原因:逻辑卷访问只露出了卷内相对位置,没有包含 MBR 之后的大偏移。卷的起始 LBA 可能是 2048,也可能是 1048576,取决于分区在磁盘上的位置。
解决:定位前用磁盘管理器或 DiskGenius 查看分区的起始 LBA,记下来加到最终结果上。或者干脆在 WinHex 里打开整个物理盘而不是所在分区,只是在里面找到对应分区再操作。
5.4 校验文件头时看到的内容和资源管理器里不一样
现象:WinHex 数据视图里文件头不是预期签名,而是 NTFS 的 $MFT 记录头 46 49 4C 45。
原因:你还在 MFT 记录视图里,没切到数据视图。NTFS 下,目录浏览器点选文件默认进入的是记录视图而不是数据视图。
解决:用菜单里的“打开数据”或双击目录浏览器项,确认十六进制区显示的是文件内容的开头。判断标准就是看文件头签名,不是看文件名。
5.5 被占用的系统盘打不开或读取超时
现象:尝试在 WinHex 中打开正在使用的系统盘,提示无法访问或读取到一半卡住。
原因:Windows 对正在挂载的卷有独占锁限制,系统分区尤其明显。另外,写缓存未刷新会导致读到的扇区不是实际落盘的数据。
解决:把盘上关键数据先做镜像,再在镜像上操作。无法离线时,用管理员身份运行 WinHex 并开启“只读模式”,至少能规避写入风险。读取超时往往发生在坏道附近,配合 chkdsk 报告判断是否确属物理故障。
6. 进阶技巧:把首扇区手工拼回完整文件
大多数情况下 WinHex 能直接帮你提取文件,不需要手工拼。真正值得用手工法是这么一种场景:文件被删除后目录记录被覆盖,文件系统已经不知道它叫什么名字了。你在 FAT 表或 NTFS 空闲簇扫描里找到了一段连续数据,怀疑是一个被删除的文件,但不知道它从哪里开始。这时你能做的就是:在空闲空间里搜索文件签名,找到签名出现的第一个扇区,把它当作首扇区,然后按文件类型估算长度,手工提取数据。
我常用的做法是:用搜索功能全盘搜 16 进制签名(比如搜 JPEG 的 FFD8FF),每命中一次就跳转到对应扇区,检查文件头后面的结构是否完整。命中后从首扇区开始,按文件大小提取扇区范围,写进一个新文件里,再用看图工具或文档工具打开验证。如果打开的图片只有一半,说明签名后面还有碎片,需要继续搜下一个签名,把两段拼接起来。
这个方法的边界在于:如果文件被删除后磁盘又写入了新数据,首扇区之后的部分可能已经不连续,手工拼接成功率会下降。拼接时优先找文件内部结构清晰、有固定的段标记的文件类型。
另一个我一直沿用的习惯是:每定位一个文件的首扇区,就把 LBA、文件路径、扇区大小、验证签名记在一个文本里。后续排查同一个磁盘的坏道问题时,这套记录能帮助你快速排除“哪些文件不受坏道影响”。用 WinHex 定位扇区这件事,麻烦不在操作,而在操作后能不能留下可追溯的证据链。希望这个流程对你上手有帮助。
本文还有配套的精品资源,点击获取