第 16 章
开发更多磁盘实用程序
介绍
在本章中,我们将讨论如何使用 MBR、DBR、FAT 和根目录信息来开发可以帮助我们管理数据、优化存储或解决许多磁盘故障排除问题的实用程序。
通常这些程序是针对特定问题的解决方案。本章讨论了一些实用程序及其编程。
隐藏部分
通常,分区隐藏实用程序由那些在用户正在使用的同一计算机系统上工作的用户使用。如果一台计算机上有很多用户,那么另一个用户的数据被读取、窃取或删除的可能性就很大。
在这种情况下,如果用户在同一台计算机上拥有一些重要数据或机密信息,他可能希望隐藏其数据所在的分区,以便操作系统无法访问该分区,从而其他用户也无法访问。
当用户想要在系统上工作时,他只需打开该部分即可重新访问该部分。通常,此类事件发生在专业机构中,许多学生都使用计算机,但高年级学生总是担心他们的重要数据或项目工作。由于缺乏知识,新生可能会损坏甚至删除他的数据。
某个部分如何被隐藏?
下表显示了MBR分区表中的分区格式:
补偿 |
意义 |
尺寸 |
描述 |
00小时 |
启动类型指示字节 |
1 字节 |
如果该字节为00H,则该分区处于非活动状态,如果该字节为80H,则该分区处于活动状态(或可启动)。 |
01小时 |
起始节号 |
1 字节 |
起始分区号(十六进制) |
02小时 |
分区起始处的扇区和柱面号 |
2 个字节 |
第一个字节的 6 位组成起始扇区号,其余 2 位(作为最高有效位 2 位)和另一个字节的 8 位(10 位数的其余 8 个最低有效位)的组合组成该分区的起始柱面号。 |
04 小时 |
文件系统指示字节 |
1 字节 |
十六进制的文件系统指示字节(有关分区指示字节的完整列表,请参阅本书前面讨论的“磁盘和操作系统的逻辑方法”一章) |
05小时 |
章节结束标题编号 |
1 字节 |
结束分区号(十六进制) |
06小时 |
段末尾的扇区和柱面号 |
2 个字节 |
第一个字节的 6 位组成最终的扇区号,其余 2 位(作为两个最高有效位)和另一个字节的 8 位(10 位数的其余 8 个最低有效位)的组合组成该分区的最终柱面号。 |
08小时 |
分区起始的绝对扇区号 |
4 个字节 |
MBR 和分区中第一个扇区之间的扇区数 |
0H |
分区末尾的绝对扇区号 |
4 个字节 |
区段中的扇区数 |
|
|
总计 = 16 个字节 |
|
在偏移量 04H 处,在每个分区条目中都有一个文件系统指示字节。这个指示字节代表该分区的文件系统类型。如果此字节的值发生变化,则部分标识也会发生变化。
例如,“DOS 12 位 FAT”的分区指示字节值为 0x01。如果该值更改为 0x11,则分区表条目中的文件系统标识符将更改为“隐藏的 DOS 12 位 FAT”(有关分区指示字节的完整列表,请参阅本书前面讨论的“磁盘和操作系统的逻辑方法”一章)。
下表显示了一些分区类型的文件系统指示字节的更多示例:
分区类型指示字节 |
分区文件系统描述 |
0x01 |
DOS 12 位 FAT |
0x11 |
隐藏的 DOS 12 位 FAT |
0x04 |
DOS 16 位 FAT(<=32 MB) |
0x14 |
隐藏的 DOS 16 位 FAT(<=32 MB) |
0x05 |
DOS扩展 |
0x15 |
隐藏 DOS 扩展 |
0x06 |
DOS 16 位大(>32 MB) |
0x16 |
隐藏 DOS 16 位大文件 (>32 MB) |
0x07 |
NTFS |
0x17 |
隐藏的 NTFS |
0x0B |
Windows FAT32 |
0x1B |
隐藏的 Windows FAT32 |
0x0C |
Windows FAT32(LBA) |
0x1C |
隐藏的 Windows FAT32 (LBA) |
0x0E |
Windows FAT16(LBA) |
0x1E |
隐藏的 Windows FAT16 (LBA) |
0x0F |
窗口展开 |
0x1F |
隐藏窗口已展开 |
这里我们看到,通过将值 0x10 添加到其系统指示字节,可以找到任何文件系统对应的隐藏分区。
虽然这不是隐藏分区的硬性规定,但它甚至适用于大多数文件系统。其背后的原因是,当我们更改分区指示字节的值时,分区表条目中的文件系统标识也会更改。而且,新文件系统也受同一操作系统支持的情况非常少见。
编写程序隐藏分区
下面给出的程序用于使用 MBR 分区表中该分区的分区条目来隐藏分区。如果您想隐藏扩展卷中的其他逻辑分区,则应访问扩展 MBR。
该程序的编码如下所示:
/*程序使用该分区的分区表条目从 MBR 隐藏该分区*/
#包括 <bios.h>
#包括 <stdio.h>
int main(空)
{
结构diskinfo_t dinfo;
int 结果,隐藏;
int 我;
static char dbuf[512];/* 数据缓冲区读写
行业信息 */
clrscr();
dinfo.drive = 0x80; /* 第一个驱动器号
硬盘 */
dinfo.head = 0; /* 磁盘头号 */
dinfo.track = 0; /* 轨道号 */
dinfo.sector = 1; /* 扇区号 */
dinfo.nsectors = 1; /* 扇区数 */
dinfo.buffer = dbuf; /* 数据缓冲区 */
/* 读取磁盘的第一个扇区 */
结果 = _bios_disk(_DISK_READ,&dinfo);
如果 ((结果 & 0xff00) == 0)
{
printf("四个分区条目的分区代码为,
0x%02x, 0x%02x, 0x%02x 和 0x%02x.\n",
dbuf[450] & 0xff, dbuf[466] & 0xff,
dbuf[482] & 0xff, dbuf[498] & 0xff);
文本颜色(15);
gotoxy(5,5);cprintf("MBR 中的分区条目如下
如下:“”);
gotoxy(10,7);cprintf("1. ");showtype(dbuf[450] & 0xff);
gotoxy(10,8);cprintf("2. ");showtype(dbuf[466] & 0xff);
gotoxy(10,9);cprintf("3. ");showtype(dbuf[482] & 0xff);
gotoxy(10,10);cprintf("4. ");showtype(dbuf[498] & 0xff);
/*获取隐藏分区的用户输入*/
乙氧基(1,15);
printf("请输入要隐藏的分区号,
或按任何其他键退出...”);
隐藏=getche();
切换(隐藏)
{
案例'1':/*隐藏分区表中的第一个分区*/
dbuf[450] = dbuf[450] +16;
结果 = _bios_disk(_DISK_WRITE, &dinfo);
休息;
案例'2':/*隐藏分区表中的第二个分区*/
dbuf[466] = dbuf[466]+16;
结果 = _bios_disk(_DISK_WRITE, &dinfo);
休息;
case '3': /* 隐藏分区表中的第三个分区 */
dbuf[482] = dbuf[482] +16;
结果 = _bios_disk(_DISK_WRITE, &dinfo);
休息;
case '4': /* 隐藏分区表中的第四个分区 */
dbuf[498] = dbuf[498]+16;
结果 = _bios_disk(_DISK_WRITE, &dinfo);
休息;
默认:
退出(0);
}
如果 ((结果 & 0xff00) == 0)
{
printf("\n\n四分区新分区码
条目为 0x%02x、0x%02x、0x%02x 和 0x%02x。\n",
dbuf[450] & 0xff, dbuf[466] & 0xff,
dbuf[482] & 0xff, dbuf[498] & 0xff);
获取();
}
别的
{
printf("无法更改字节,状态 = 0x%02x\n",
结果);
获取();
}
}
返回0;
}
编码注释:
程序读取MBR分区表中所有四个分区表项的文件系统指示字节,使用showtype()函数显示文件系统指示字节对应值的文件系统名称。
用户从屏幕上显示的菜单中选择要隐藏的分区,然后将 16(0x10)添加到该分区的文件系统指示字节的值以将其隐藏。
函数showtype()的代码如下:
/*函数显示与文件系统指示字节的值相对应的文件系统名称*/
显示类型(i)
{
开关(一)
{
案例 0x00:cprintf(“空”);中断;
案例 0x01:cprintf(“DOS 12 位 FAT”);中断;
案例 0x02:cprintf(“XENIX root”);中断;
案例 0x03:cprintf(“XENIX usr”);中断;
情况 0x04 :cprintf("DOS 16 位 <32M"); 中断;
案例 0x05:cprintf(“扩展”);中断;
案例 0x06 :cprintf(“DOS 16 位 >=32M”);中断;
案例 0x07:cprintf(“OS/2 HPFS”);中断;
案例 0x08:cprintf(“AIX”);中断;
案例 0x09:cprintf(“AIX 可启动”);中断;
案例 0xa:cprintf(“OS/2 Boot Manag”);中断;
案例 0xb:cprintf(“Win95/98/ME FAT32”);中断;
案例 0xc:cprintf(“Win95/98/ME FAT32(LBA)”);中断;
案例 0xd:cprintf(“Win95 FAT16”);中断;
案例 0xe:cprintf(“Win95 FAT16(LBA)”);中断;
案例 0xf:cprintf(“Win95 Extended”);中断;
案例 0x11:cprintf(“隐藏的 FAT-12”);中断;
案例 0x12:cprintf(“Compaq 诊断程序”);中断;
案例 0x14:cprintf(“隐藏的 FAT-16(<32)”);中断;
案例 0x15:cprintf(“隐藏扩展”);中断;
案例 0x16 :cprintf("隐藏的 FAT-16");中断;
案例 0x17:cprintf(“NTFS”);中断;
案例 0x40:cprintf(“Venix 80286”);中断;
案例 0x51:cprintf(“Novell?”);中断;
案例0x52:cprintf(“Microport”);中断;
案例 0x63:cprintf(“GNU HURD”);中断;
案例 0x64:
案例 0x65:cprintf(“Novell Netware”);中断;
案例 0x75:cprintf(“PC/IX”);中断;
案例 0x80 :cprintf(“旧 MINIX”);中断;
案例 0x81:cprintf(“Linux / MINIX”);中断;
案例 0x82:cprintf(“Linux swap”);中断;
案例 0x83 :cprintf(“Linux 本机”);中断;
案例 0x85:cprintf(“Linux Extended”);中断;
案例 0x93 :cprintf(“ Amoeba”); 中断;
案例 0x94:cprintf(“Amoeba BBT”);中断;
案例 0xa5:cprintf(“BSD / 386”);中断;
案例 0xa6:cprintf(“OpenBSD”);中断;
案例 0xa7:cprintf(“NEXTSTEP”);中断;
案例 0xb7:cprintf(“BSDI fs”);中断;
案例 0xb8:cprintf(“BSDI 交换”);中断;
案例 0xc7:cprintf(“Syrinx”);中断;
案例 0xdb:cprintf(“CP / M”);中断;
案例 0xe1:cprintf(“DOS 访问”);中断;
案例 0xe3 :cprintf(“DOS R/O”);中断;
case 0xf2 :cprintf("DOS 次要"); 中断;
案例 0xff:cprintf(“BBT”);中断;
默认:cprintf("UNKOWN");
}
返回0;
}
编写程序来取消隐藏分区
取消隐藏分区的程序与隐藏程序的程序正好相反。在这个程序中,我们从隐藏分区的文件系统指示字节的值中减去 16 (0x10)。
该程序的编码如下:
/*取消隐藏前一个程序隐藏的分区的程序*/
#包括 <bios.h>
#包括 <stdio.h>
int main(空)
{
结构diskinfo_t dinfo;
int 结果,隐藏;
int 我;
static char dbuf[512];/* 数据缓冲区 */
clrscr();
dinfo.drive = 0x80; /* 驱动器号
第一个硬盘 */
dinfo.head = 0; /* 磁盘头号 */
dinfo.track = 0; /* 轨道号 */
dinfo.sector = 1; /* 扇区号 */
dinfo.nsectors = 1; /* 扇区数 */
dinfo.buffer = dbuf; /* 数据缓冲区 */
结果 = _bios_disk(_DISK_READ,&dinfo);
如果 ((结果 & 0xff00) == 0)
{
printf("四个分区的分区代码
条目为 0x%02x、0x%02x、0x%02x 和 0x%02x。\n",
dbuf[450] & 0xff, dbuf[466] & 0xff,
dbuf[482] & 0xff, dbuf[498] & 0xff);
文本颜色(15);
乙氧基(5,5);
cprintf("MBR中的分区条目如下:");
gotoxy(10,7);cprintf("1. ");showtype(dbuf[450] & 0xff);
gotoxy(10,8);cprintf("2. ");showtype(dbuf[466] & 0xff);
gotoxy(10,9);cprintf("3. ");showtype(dbuf[482] & 0xff);
gotoxy(10,10);cprintf("4. ");showtype(dbuf[498] & 0xff);
/*获取使用输入来取消隐藏分区*/
gotoxy(1,15);printf("请输入要
取消隐藏,或者按任何其他键
出口... ”);
隐藏=getche();
切换(隐藏)
{
/*取消隐藏分区表的第一个分区*/
案例‘1’:
dbuf[450] = dbuf[450] -16;
结果 = _bios_disk(_DISK_WRITE, &dinfo);
休息;
/*取消隐藏分区表的第二个分区*/
案例 ‘2’:
dbuf[466] = dbuf[466]-16;
结果 = _bios_disk(_DISK_WRITE, &dinfo);
休息;
/*取消隐藏分区表的第三个分区*/
案例 ‘3’:
dbuf[482] = dbuf[482] -16;
结果 = _bios_disk(_DISK_WRITE, &dinfo);
休息;
/*取消隐藏分区表的第四个分区*/
案例 ‘4’:
dbuf[498] = dbuf[498]-16;
结果 = _bios_disk(_DISK_WRITE, &dinfo);
休息;
默认:
退出(0);
}
如果 ((结果 & 0xff00) == 0)
{
printf("\n\n四分区新分区码
条目为 0x%02x、0x%02x、0x%02x 和 0x%02x。\n",
dbuf[450] & 0xff, dbuf[466] & 0xff,
dbuf[482] & 0xff, dbuf[498] & 0xff);
获取();
}
别的
{
printf("无法更改字节,状态 = 0x%02x\n",
结果);
获取();
}
}
返回0;
}
对计划的评论
输入要取消隐藏的分区号时要小心。如果错误地输入了分区号,该分区的文件系统信息将被更改,分区可能无法访问。但是,前面讨论的隐藏分区的程序可能会帮助您修复该分区的文件系统指示字节。
编写程序删除分区
删除分区的程序用于故障排除。例如,假设您的磁盘中有 FAT32 文件系统分区。现在您决定同时在磁盘上安装 LINUX 操作系统。
无论如何,操作系统的安装在修改 MBR 分区表的阶段会中断。在这种情况下,很有可能您原本要安装其他操作系统的分区变得无法访问。
在这种情况下,丢失分区的磁盘空间因无法访问而变得毫无用处。但是,如果我们从分区表中删除该分区的分区信息,我们可以使用 DOS 的 FDISK 命令再次使该空间可用。
接下来给出了从 MBR 分区表中删除分区条目的程序:
/*程序从 MBR 的分区表中删除第二个分区条目*/
# 包括 <bios.h>
/* 从分区表中读取分区条目的结构 */
结构分区
{
/* 活动分区字节 */
无符号字符可启动;
/* 起始头 */
无符号字符起始点;
/* 起始扇区和磁柱号的组合 */
无符号整数 start_sec_cyl;
/* 文件系统指示字节 */
无符号字符部分类型;
/* 结尾头 */
无符号字符 end_side;
/* 起始扇区和磁柱号的组合 */
无符号整数 end_sec_cyl;
/* 相对扇区号 */
无符号长整型 part_beg;
/* 分区长度(以扇区为单位) */
无符号长整型;
};
/* 读写 MBR 的结构 */
结构部分
{
/* IPL(初始程序加载器) */
无符号字符 master_boot[446];
/* 分区表 */
结构分区 pt[4];
/* 魔法数字 */
int 最后两个;
};
结构部分 p;
空主()
{
无符号整数t1,t2;
clrscr();
biosdisk ( 2, 0x80, 0, 0, 1, 1, &p ) ;
display(); /*显示信息
分区表 */
获取();
p.pt[1].可启动 = 0;
p.pt[1].起始侧 = 0 ;
起始安全圆柱体 = 0 ;
p.pt[1].零件类型 = 0;
p.pt[1].结束点 = 0;
pt[1].结束_sec_cyl = 0;
p.pt[1].part_beg = 0;
p.pt[1].plen = 0;
printf("\n\n\n 删除第二个分区后
来自 MBR 分区表的条目,”);
printf("\n 分区表将更改为
如下:”);
/* 从分区中删除第二个分区信息
MBR 表删除
biosdisk() 函数。不要随意使用,分区
分区表第二个分区的信息将
被彻底删除。 */
////// biosdisk ( 3, 0x80, 0, 0, 1, 1, &p ) ;
display(); /*显示分区信息
修改后的表格*/
获取();
}
对程序的评论:
取消注释 biosdisk ( 3, 0x80, 0, 0, 1, 1, &p ) 函数以从 MBR 的分区表中删除第二个分区。
要删除分区,该分区的所有参数在 MBR 中的分区表条目中均被设置为 0。请始终记住,如果删除扩展分区,则该扩展分区的所有逻辑分区也将变得无法访问。
函数display()用于显示MBR的分区表。该函数的代码如下:
/* 显示MBR分区表的函数*/
展示()
{
无符号整数 s_sec,s_trk,e_sec,e_trk,i,t1,t2;
char 类型[20], boot[5] ;
printf("\n\nPart.引导起始位置结束位置
相对数量”);
printf("\n类型 侧圆柱 扇形 侧圆柱
部门部门部门\n");
对于(i = 0;i <= 3;i++)
{
如果(p.pt[i].bootable == 0x80)
strcpy ( boot, "是" ) ;
别的
strcpy (boot, "否" ) ;
开关(p.pt[i].parttype)
{
情况 0x00:
strcpy (类型,“未使用”); 中断;
案例 0x1:
strcpy (类型,“FAT12”); 中断;
案例 0x2:
strcpy (类型,“Xenix”); 中断;
案例 0x3:
strcpy (类型,“Xenix:usr”); 中断;
案例 0x4:
strcpy (类型, "FAT16<32M" ); 中断;
案例 0x5:
strcpy (类型,“DOS-Ext。”);中断;
案例 0x6:
strcpy (类型,“FAT16>32M”); 中断;
案例 0x7:
strcpy (类型,“NTFS”); 中断;
案例 0x0b:
strcpy (类型,“FAT32”); 中断;
案例 0x0c:
strcpy (类型,“FAT32-LBA”); 中断;
情况 0x0d:
strcpy (类型,“VFAT16”); 中断;
情况 0x0e:
strcpy (类型,“VFAT16-LBA”); 中断;
情况 0x0f:
strcpy (类型,“FAT EXT”); 中断;
案例 0x17:
strcpy (类型,“HPFS”); 中断;
案例 0x81:
strcpy (类型,“Old LINUX”); 中断;
案例 0x82:
strcpy (类型,“LinuxSwap”); 中断;
案例 0x83:
strcpy (类型,“LinuxNative”); 中断;
案例 0x85:
strcpy (类型,“Linux Ext.”);中断;
默认 :
strcpy (类型,“未知”); 中断;
}
s_sec = ( p.pt[i].start_sec_cyl & 0x3f ) ;
t1 = ( p.pt[i].start_sec_cyl & 0xff00 ) >> 8 ;
t2 = ( p.pt[i].start_sec_cyl & 0x00c0 ) << 2 ;
s_trk = t1 | t2;
复制代码
t1 = ( p.pt[i].end_sec_cyl & 0xff00 ) >> 8 ;
t2 = ( p.pt[i].end_sec_cyl & 0x00c0 ) << 2 ;
e_trk = t1 | t2;
printf ( "\n%6s %3s", 类型, 引导 );
printf ("%4d %6d %8d", p.pt[i].start_side,
s_trk,s_sec );
printf ("%7d %6u %8u", p.pt[i].end_side, e_trk,
e_秒 );
printf (" %10lu %10lu", p.pt[i].part_beg,
pt[i].plen );
}
返回0;
}
格式化“0 轨坏”软盘
此程序用于格式化那些在 0 号磁道上有坏扇区的软盘,当使用 DOS 或 Windows 格式化时,会显示“0 号磁道坏”之类的错误消息。但是,您也可以使用它来格式化普通软盘。
该程序的编码已在本书附带的磁盘中给出,名为“TTFORMAT.C”。该程序的工作逻辑与 2003 年 2 月版 PCQUEST 计算机杂志上发表的程序相同。
在这个程序中,我们尝试通过格式化使这种类型的软盘可重复使用。该程序听起来即使软盘上有一些坏扇区,您也可以处理它。但是,如果磁盘的第一个扇区坏了,软盘就无法格式化。
程序会重写所有 DBR、FAT 和根目录信息。如果磁盘表面有坏扇区,则会在 FAT 中将其标记为坏扇区。
在程序的编码中,结构BPB用于写入DBR的BIOS参数块,结构boot_sector用于写入磁盘的DBR,结构address_field用于与每个磁道的柱面数、磁头数、扇区数以及扇区大小进行交互。
下表给出了程序编码中使用的不同功能及其描述。

软盘的卷序列号是由 DOS 根据系统时钟的当前日期和时间计算出来的。
序列号的第一部分由时间(秒和百分之一秒)与日期(月和日)之和计算得出。序列号的第二部分等于时间(小时和分钟)与日期(年份)之和。
所有计算均以十六进制进行。例如,假设您在 2003 年 10 月 23 日 11:16:28:65 在 DOS 环境中格式化了软盘。现在让我们计算磁盘的序列号。
(秒和百分之一秒)格式的时间是
=(28 和 65)
=(1CH 和 41H)
写为 1C41
类似地,(月和日)格式的日期是
=(10和23)
=(0AH 和 17H)
写为0A17
类似地,(小时和分钟)格式的时间是,
=(11和16)
=(0BH 和 10H)
写为 0B10
今年将是
= 2003
= 07D3
现在,让我们根据前面的描述来计算软盘的序列号。序列号的第一部分将是 (1C41 + 0A17) = 2658,而序列号的第二部分将是 (0B10 + 07D3) = 12E3。
编写磁盘编辑工具
本书附带的磁盘上给出了磁盘编辑程序的编码,文件名为“TTEDITOR.C”。您可以使用该程序来分析硬盘或软盘的表面。甚至在写这本书的大部分时间里我都使用 TTEDITOR 来分析磁盘表面或执行磁盘修改。
以下是该编辑程序可以执行的一些重要任务:
- 从硬盘和软盘表面读取逐个扇区的信息。
- 将任意扇区的备份副本写入文件。
- 从文件中恢复扇区数据。
- 改变一个字节。
- 用于将十六进制数转换为十进制和二进制的计算器。
该程序使用 biosdisk() 和 _bios_disk() 函数来访问磁盘。如果要分析超过 8.4GB 的磁盘,请修改程序以使用 INT 13H 扩展。程序中用到的函数说明如下表:
功能 |
描述 |
背景() |
创建第一个屏幕的背景和框架 |
clsline() 函数 |
用于从屏幕上清除行号指定的整行。 |
更新( ) |
调用屏幕上所有显示功能的函数 |
写入文件() |
将扇区数据写入用户文件的函数。 |
写入扇区() |
从指定文件恢复某个扇区的功能。 |
msgdisp() |
在屏幕上显示消息的功能。 |
改变( ) |
该函数用于更改用户指定的任意扇区的一个字节。 |
框架( ) |
扇形显示框架结构绘制功能 |
dispmax() 函数 |
显示磁盘的最大 CHS 数(对最大 8.4 GB 的磁盘有效) |
展示( ) |
在屏幕上显示扇区和信息。 |
十六进制数 ( ) |
函数将十六进制数转换为其对应的十进制数和二进制数。 |