📄 cipancaozuoziliao.txt
字号:
事实上,Windows 2000的引导过程是从安装时候就已经开始的。
那我们首先从windows 2000的安装说起。
当windows 2000 setup运行时,它向硬盘上写入MBR(主引导记录),同时在这个磁盘驱动器的第一个可引导分区(就是我们在fdisk后激活的分区)写入引导扇区,引导扇区的内容根据不同的文件系统格式而变化(FAT或者是NTFS)。如果你的机器上曾装有MS操作系统并建立了引导扇区的话,windows 2000 setup将检测它要覆盖的引导扇区是否有效,如果有效的话,windows 2000 setup安装程序将把引导扇区的内容复制到这个分区的根目录中的文件bootsect.dos中。Setup程序在写完引导扇区后,将把windows 2000所用的文件拷贝到硬盘,包括两个引导文件Ntldr和Ntdetect.com。另外,setup还会在引导分区的根目录中建立引导菜单文件boot.ini。
例:
[boot loader]
timeout=3
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows "
multi(0)disk(0)rdisk(0)partition(2)\WINDOWS="Windows Server 2000" /fastdetect
这是我的机器上的boot.ini文件,该内容显示装了两个操作系统,win98和win2000,后面的那个参数/fastdetect最常见,是安装系统时默认的,它的作用是使ntdetect忽略秉性和串行设备的枚举。Boot.ini文件中的相关参数还有很多,各有不同的功能,因为与本文没太大关系,所以不作具体介绍,有兴趣的朋友可以到网上找找有关资料。
Windows 2000的启动:
当你按下机器上的power键,计算机就开始启动了,首先是上电自检,通过后bios引导计算机去读取硬盘上的MBR,根据MBR中的信息,找到引导分区,将引导分区内的引导扇区的代码读入内存并把控制权交给该代码。引导扇区代码的作用是向Windows 2000提供磁盘驱动器(硬盘)的结构和格式信息并且从磁盘根目录中读取Ntldr文件,在引导扇区代码将Ntldr加载到内存后,它把控制权交给Ntldr的入口点。如果引导扇区代码在根目录中没有找到Ntldr文件的话,若文件系统为FAT格式,则显示:“Boot:无法找到Ntldr”,若引导文件系统是NTFS格式,则显示:“NTLDR丢失”。然后,Ntldr使用内建的文件系统代码从根目录读取boot.ini文件(Ntldr内建代码与引导扇区文件系统代码不同的是,Ntldr文件系统代码可以读取子目录)。此时,Ntldr清除屏幕,如果boot.ini中存在不止一种引导选项,则显示引导选择菜单,如果在boot.ini制定的超时范围内未有任何动作的话,Ntldr会选择默认的选项。引导选项确定后,Ntldr加载和执行Ntdetect.com(这是一个使用系统bios进行查询计算机基本设备和设置信息的16位实模式程序)。然后,Ntldr开始清除屏幕并显示:“Starting Windows……”进度栏。这个进度栏保持空白,直到Ntldr开始加载引导驱动程序(假如有100个引导驱动程序,则每加载一个文件,进度条增加1%)。在进度条的下面是信息:“For troubleshooting and advanced startup options for windows 2000 , press F8 .”如果此时按下F8键,会出现高级启动菜单,包括:已知的最近正确模式(last known good),安全模式(safe mode),调试模式(debug mode)等等等等。
此后,Ntldr加载合适的内核和HAL映像文件(缺省为Ntoskrnl.exe和HAL.dll),读入SYSTEM注册表hive文件(hive文件是一种包含注册表子树的文件)以确定该加载哪些引导驱动程序,加载引导驱动程序,为Ntoskrnl.exe的执行准备CPU寄存器。之后,Ntldr调用Ntoskrnl.exe并由它开始初始化执行程序子系统并引导系统-启动(system-start)设备驱动程序,在一系列的初始化工作完成后Ntoskrnl.exe为系统本机应用程序作准备并运行smss.exe。
smss的主要任务是:初始化注册表,创建系统环境变量,加载Win32子系统(Win32k.sys)的内核模式部分,启动子系统进程Crss,启动登陆进程winlogon。然后,winlogon开始执行其启动步骤,如创建初始的窗口和桌面对象等等。然后它创建服务控制管理器(SCM)进程(Winnt\System32\Services.exe),它加载所有的标记为自动启动(auto-start)的服务程序和设备驱动程序和本机安全验证子系统(Lsass)进程(Winnt\system32\Lsass.exe)。当一切加载成功且用户在控制台成功登陆后,SCM则认为系统引导成功,注册表中 已知最近正确配置(HKLM\SYSTEM\select\LastKnownGood)由\CurrentControlSet替代。反之,如果用户在引导的时候选择高级菜单中的已知最近正确模式(LastKnownGood)或者加载时驱动程序返回一个严重的或者关键的错误,系统会以LastKnownGood的值作为CurrentControlSet 的值。
之后,我们便看到了熟悉的桌面。至此,windows 2000的引导过程结束。
限于篇幅,本文只简单的讲述一下windows 2000操作系统引导的大体过程,一些细节方面的东东请看我整理的其他windows 2000操作系统方面的文章。
不同WINDOWS平台下磁盘逻辑扇区的直接读写
一、概述
在DOS操作系统下,通过BIOS的INT13、DOS的INT25(绝对读)、INT26(绝对写)等功能调用实现对磁盘逻辑扇区或物理扇区的读写是很方便的,C语言中还有对应上述功能调用的函数:biosdisk、absread和abswrite等。但在WINDOWS操作系统下编写WIN32应用程序时却再也不能直接使用上述的中断调用或函数了。那么,在WINDOWS操作系统下能不能实现磁盘扇区的直接读写呢?如何实现磁盘扇区的读写呢?为了解决这些问题,笔者查阅了一些相关资料后发现,WINDOWS操作系统也提供了读写磁盘扇区的方法,只是在不同的版本中有着不同的方式和使用限制。最后,笔者编写了一个磁盘扇区直接读写类,不敢独专,特提供出来,希望能对大家有所帮助。
注:这里INT13表示INT 13H,其它类同。
二、一个读取软盘扇区的例子
WINDOWS操作系统对所有的存储设备实行了统一管理,而且为了安全起见,操作系统还不允许在WIN32应用程序(工作在Ring3级)中直接调用中断功能,如INT13、INT21、INT25、INT26等。但它同时也提供了一些服务来弥补这种缺憾,在WIN95/98中,VWIN32服务就是其中一种。VWIN32服务是通过一个VXD来实现的,它提供了设备IO功能,通过它,使用API函数DeviceIoControl便可以实现WIN32应用程序和磁盘设备驱动程序间的通信,从而实现对磁盘的存取。VWIN32提供的服务是一系列的控制命令字,它们实现诸如DOS操作系统下的INT13、INT25、INT26和INT21等功能调用。下面是它定义的一些控制命令字:
VWIN32_DIOC_DOS_IOCTL (1) 实现INT21 功能
VWIN32_DIOC_DOS_INT25 (2) 实现INT25 功能
VWIN32_DIOC_DOS_INT26 (3) 实现INT26 功能
VWIN32_DIOC_DOS_INT13 (4) 实现INT13 功能
VWIN32_DIOC_DOS_DRIVEINFO (6) 实现INT21 730x 功能
如果要对磁盘进行读写,只要使用DeviceIoControl执行相应命令即可,下面的例子用来读取软盘的一个扇区(使用INT13):
第一步:打开VWIN32服务,HANDLE hDev=CreateFile("\\\\.\\VWIN32",0,0,0,0,FILE_FLAG_DELETE_ON_CLOSE,NULL);
第二步:填充中断所用到的相关寄存器。这里将寄存器放在一个结构中,结构定义如下(有关INT13使用的寄存器情况,请参阅相关资料):
typedef struct INT13Regs{
PVOID buffer; // ebx 寄存器
BYTE Drive; // 磁盘号 dl
BYTE Head; //磁头号 dh
WORD EDX_High; // edx 寄存器
BYTE Sector; //起始扇区 cl
BYTE Track; //磁道号 ch
WORD ECX_High; //ecx 寄存器
BYTE Number; //要读写的扇取数 al
BYTE CMD; //命令:2--读,3--写,5--格式化 ah
WORD EAX_High; //eax 寄存器
DWORD EDI; // edi 寄存器
DWORD ESI; // esi
DWORD EFLAG; // flags
}INT13_REGISTERS;
unsigned char Buffer[512];//定义缓冲区,放置读取扇区数据
INT13_REGISTERS reg={0};//定义寄存器结构变量
reg.buffer =(void *)Buffer;
reg.Drive =0;//0-软盘A 1-软盘B 0x80-硬盘c
reg.Head =0;
reg.Track=0;
reg.Sector=1;
reg.Number=1;
reg.CMD=2; //读取
第三步:调用设备IO API函数DeviceIoControl执行4号命令(即VWIN32_DIOC_DOS_INT13), BOOL b_ret=DeviceIoControl(hDev,4,®,sizeof(INT13_REGISTERS),®,sizeof(INT13_REGISTERS),&lpRet,0);
如果其返回值不等于零,调用成功,进一步处理....否则调用失败。
第四步:关闭服务,CloseHandle(hDev);
三、限制或局限
上面是使用INT13读取软盘扇区的完整步骤,在WIN95/98下它是可以工作的。那么,是否将上面的寄存器结构中的Drive置为0x80就可以读取逻辑硬盘C盘的扇区了呢?回答是否定的。INT13用来存取硬盘的功能在WINDOWS中被忽略了。另外,INT25、INT26虽然可以存取硬盘,但是它们不能工作在FAT32格式的硬盘上。下面的列表将详细列举与磁盘操作相关的中断调用的限制情况(不特殊说明,指的是在WIN95/98操作系统下):
中断功能 限制及使用情况
INT13 不可以读写硬盘,仅支持软盘
INT25/INT26 不可以读/写FAT32硬盘,支持FAT12、FAT16
INT21(440DH-41H/61H) 不可用(文档资料中说支持FAT12、FAT16、FAT32,实际上没有实现)
INT21(7305H) 可以读写软盘、硬盘,支持FAT12、FAT16、FAT32,但要求WIN95OSR2及以后版本
值得一提的是上表中的INT21--7305H功能是专门提供用来支持FAT32的,并且用来替换INT25/INT26,对应的控制命令字是6(即VWIN32_DIOC_DOS_DRIVEINFO),它和INT13、INT25、INT26等中断功能的一个显著区别是:它不使用寄存器来传递参数(INT21--440DH-41H/61H类同),而是使用一个称为DISKIO的结构,寄存器EBX用来保存指向该结构的地址。DISKIO的定义如下:
typedef struct _DISKIO {
DWORD dwStartSector; // 要读写的起始扇区号
WORD wSectors; // 要读写的扇区数
DWORD dwBuffer; // 用来保存读/写数据的缓冲区
}DISKIO, * PDISKIO;
另外,在使用该功能时还需要特别设置一些寄存器,如ECX必须为-1,用ESI来表示读写。下面的例子是使用该功能来实现上面的例子功能,即读软盘A的一个扇区。首先定义一个新的寄存器结构供本例使用:
typedef struct _DIOC_REGISTERS{
DWORD EBX;
DWORD EDX;
DWORD ECX;
DWORD EAX;
DWORD EDI;
DWORD ESI;
DWORD Flags;
}DIOC_REGISTERS;
其实该结构和上面的INT13_REGISTERS是一样的,只不过INT13_REGISTERS将寄存器细分开了,可读性更强些。本例从步骤上说和上面的例子相同,只有寄存器设置一步在内容上有差异。
第一步:打开VWIN32服务。
第二步:设置寄存器。
DIOC_REGISTERS reg = {0};
DISKIO dio;
unsigned char Buffer[512];
//设置参数结构
dio.dwStartSector = 0;//注意:和上例不同,不是1,从0开始编号
dio.wSectors = 1;
dio.dwBuffer = (DWORD)Buffer;
//设置寄存器
reg.EAX = 0x7305; //功能上类似于INT25,绝对读
reg.EBX = (DWORD)&dio;//参数结构的地址
reg.ECX = -1;//必须是-1
reg.EDX = 1; //注意:和上例不同,驱动器编号变了,0--缺省 1--A、2--B、3--C
reg.ESI = 0; //ESI的bit0表示读写,0--读、1--写
在写状态时SI的bit1--bit12,bit15必须是0,bit13、bit14、bit15共同来表示所写数据的类型,具体见下表:
15 14 13 类型描述
0 0 0 其它或不知道.
0 0 1 FAT数据
0 1 0 目录数据
0 1 1 一般数据
1 x x 保留。bit15必须是0
第三步:调用API。BOOL b_ret=DeviceIoControl(hDev,6,®, sizeof(DIOC_REGISTERS),®,sizeof(DIOC_REGISTERS),&cb,0);
第四步:关闭服务。
可以发现,两种方法读到的数据完全一致。
四、WIN2000中的磁盘扇区读写
在WINNT和WIN2000中磁盘被看做一种标准设备,可以使用CreateFile象打开文件一样打开并存取。CreateFile支持两种方式的磁盘设备--逻辑磁盘(格式为"\\.\C:")和物理磁盘(格式为"\\.\PHYSICALDRIVEx",其中x为数字),例如打开A:盘进行读取操作,只要这样:
HANDLE hDev=CreateFile("\\\\.\\A:",GENERIC_READ,FILE_SHARE_WRITE,0,OPEN_EXISTING,0,0);
如果得到的句柄有效,就可以使用ReadFile来读取了,
ReadFile(hDev,Buffer,512,&dwRet,0);
读取结束要关闭该句柄,
CloseHandle(hDev);
这比WIN95/98下的磁盘扇区读取方便多了。
另外,上面的例子是操作逻辑磁盘的,它包括软驱、硬盘分区等;物理磁盘指的是实际的硬盘,它不关心该硬盘被分成几个区,硬盘的编号是从0开始的,"\\.\PHYSICALDRIVE0"表示第一块硬盘,其它依此类推。大家可能马上会想起,利用这种机制可以对硬盘的分区表进行存取了。确实如此,此时便可以对硬盘的主引导扇区(独立存在的一个扇区,包含分区表信息,不同于磁盘分区的BOOT区)进行操作了。
unsigned char Buffer[512]={0};
HANDLE hDev=CreateFile("\\\\.\\PHYSICALDRIVE0",GENERIC_WRITE,FILE_SHARE_WRITE,0,OPEN_EXISTING,0,0);
WriteFile(hDev,Buffer,512,&dwRet,0);
CloseHandle(hDev);
危险!!!千万别这么做!!!
五、一个自适应的磁盘读写类
由上面的例子可以看出,不同的操作系统下对磁盘扇区的读写有不同的方式,为了能够在各类操作系统下能够使用统一的方法读写磁盘扇区,特设计了一个通用类。该类的设计思想如下:首先编写各类操作系统下的磁盘扇区存取函数,然后通过GetVersionEx来判断操作系统,进而选取对应的函数来实现磁盘扇区的读写。由上面的分析可知,WINDOWS操作系统对INT13的支持是最差的,所以在这里只使用INT25、INT26、INT21--7305等中断调用来实现。类的定义如下:
class CDiskInfo{
public:
CDiskInfo();
~CDiskInfo();
private:
HANDLE hDev;
DWORD dwCurrentPlatform;
void GetPlatform(); //取得操作系统,并存入变量dwCurrentPlatform
BOOL Win2000_AccessSectors(WORD CMD,BYTE bDrive,DWORD dwStartSector,WORD wSectors,LPBYTE lpSectBuff);//用于WIN2000、WINNT等操作系统,
BOOL Int25_ReadSectors(BYTE bDrive,DWORD dwStartSector,WORD wSectors,LPBYTE lpSectBuff);
BOOL Int26_WriteSectors(BYTE bDrive,DWORD dwStartSector,WORD wSectors,LPBYTE lpSectBuff);//用于WIN95以前的操作系统
BOOL Int21_AccessSectors(WORD CMD,BYTE bDrive,DWORD dwStartSector,WORD wSectors,LPBYTE lpSectBuff);//7305功能实现,用于WIN95OSR2、WIN98等操作系统
public:
//对外统一提供Read和Write操作,类内部根据平台选用适合的函数调用
BOOL ReadSectors(BYTE bDrive,DWORD dwStartSector,WORD wSectors,LPBYTE lpSectBuff);
BOOL WriteSectors(BYTE bDrive,DWORD dwStartSector,WORD wSectors,LPBYTE lpSectBuff);
};
该类对外提供了两个接口,即ReadSectors和WriteSectors,其参数是一样的,分别是要读写的磁盘编号bDrive,要存取磁盘的开始扇区号dwStartSector,要读取的扇区数wSectors和读写扇区数据的缓冲区lpSectBuff。这里磁盘编号是从1开始的,即1代表A:,2代表B:,3代表C:,依此类推。扇区的编号从0开始。使用时也很简单,只要作如下声明即可:
BYTE Buffer[1024];
CDiskInfo A;
BOOL bRet=A.ReadSectors(1,0,2,Buffer);
详细情况见附带的类文件及测试程序。
六、补充说明
严格来说,在对磁盘进行读写时,应该遵循以下顺序:打开设备(WIN95/98下为VWIN32服务,WIN2000下为磁盘设备)、锁卷、验证卷的有效性、读/写、开锁卷、关闭设备。这里为了描述上的简洁,忽略了锁卷/开锁卷及验证有效性等操作。有兴趣的朋友可以自行添加。
另外,该类仅实现了逻辑驱动器的读写,要想实现诸如对物理硬盘的主引导扇区的读写,还需要其它技术,如thunk技术,即编写两个动态库,一个是WIN32动态库,一个是WIN16动态库(thunk技术只可以用动态库实现),其中WIN16动态库转到DPMI模式,调用INT13(或者扩展INT13)来实现物理磁盘扇区的读写。有关thunk技术请参阅相关文档资料。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -