实战DeviceIoControl 之七:在Windows 9X中读写磁盘扇区

在Windows NT/2K/XP中,直接用CreateFile打开名称类似于"//./A:"的”文件”,就可以与设备驱动打交道,通过 ReadFile/WriteFile以绝对地址方式访问磁盘了。但Windows 9X不支持这样的简单方法。本文介绍一种在Windows 9X中实现磁盘直接访问的方法:利用系统的vwin32.vxd,通过DeviceIoControl调用DOS INT21 7305H与440DH功能来完成。该调用支持FAT12、FAT16和FAT32,适用于Windows 95 SR2以及更高版本。

先来了解一下DOS INT21 7305H功能的入口参数:

AX -- 功能号7305H
DS:BX -- 读写扇区的信息结构
CX -- 必须为-1
DL -- 驱动器号: 1=A:, 2=B:, 3=C:, ...
SI -- 读写标志: 最低位0=读, 1=写

若调用成功,清除C标志;否则设置C标志。

DS:BX指向一个结构,此结构定义如下:

DISKIO STRUC
dwStartSector dd ? ; 起始扇区
wSector dw ? ; 扇区数
lpBuffer dd ? ; 数据缓冲区地址
DISKIO ENDS

在写操作下,需要“锁定”驱动器。DOS INT21 440DH的4AH/6AH功能可实现逻辑驱动器的加锁/解锁。其入口参数为:

AX -- 功能号440DH
BH -- 锁的级别,0-3级,直接写扇区用1
BL -- 驱动器号: 1=A:, 2=B:, 3=C:, ...
CH -- 0x08
CL -- 0x4A
DX -- 0
AX -- 功能号440DH
BL -- 驱动器号: 1=A:, 2=B:, 3=C:, ...
CH -- 0x08
CL -- 0x6A

以上两个调用,若调用成功,清除C标志;否则设置C标志。

通过IOCTL码VWIN32_DIOC_DOS_DRIVEINFO等调用上述中断。实现绝对磁盘读写的关键代码如下:

// INT21的IOCTL码
#define VWIN32_DIOC_DOS_IOCTL 1
#define VWIN32_DIOC_DOS_DRIVEINFO 6

// 寄存器组
typedef struct _DIOC_REGISTERS {
DWORD reg_EBX;
DWORD reg_EDX;
DWORD reg_ECX;
DWORD reg_EAX;
DWORD reg_EDI;
DWORD reg_ESI;
DWORD reg_Flags;
} DIOC_REGISTERS, *PDIOC_REGISTERS;

// IO参数(注意字节对齐方式)
#pragma pack(1)
typedef struct _DISKIO {
DWORD dwStartSector; // 起始扇区
WORD wSectors; // 扇区数
void* pBuffer; // 缓冲区指针
} DISKIO, *PDISKIO;
#pragma pack()

BOOL AbsDiskRead(
BYTE nDiskNumber, // 盘号, 1=A:, 2=B:, 3= C:, ...
DWORD dwStartSector, // 起始扇区
WORD wSectors, // 扇区数
void* pBuffer) // 数据缓冲区指针
{
HANDLE hDevice;
DIOC_REGISTERS regs;
DISKIO dio;
DWORD dwOutBytes;
BOOL bResult;

// 打开设备,获得VxD句柄
hDevice = CreateFile(".//vwin32", // 设备路径
GENERIC_READ | GENERIC_WRITE, // 读写方式
FILE_SHARE_READ | FILE_SHARE_WRITE, // 共享方式
NULL, // 默认的安全描述符
OPEN_EXISTING, // 创建方式
FILE_ATTRIBUTE_NORMAL, // 文件属性
NULL); // 不需参照模板文件

if(hDevice == INVALID_HANDLE_VALUE)
{
return FALSE;
}

// 填充DISKIO参数结构
dio.dwStartSector = dwStartSector;
dio.wSectors = wSectors;
dio.pBuffer = pBuffer;

// 填充寄存器组--中断入口参数
memset(&regs, 0, sizeof(DIOC_REGISTERS));
regs.reg_EAX = 0x7305; // AX=0x7305
regs.reg_EBX = (DWORD)&dio; // EBX=DS:BX=参数指针
regs.reg_ECX = 0xffff; // CX=-1
regs.reg_EDX = nDiskNumber; // DL=盘号
regs.reg_ESI = 0; // SI=0 -- 读操作

// 用VWIN32_DIOC_DOS_DRIVEINFO读磁盘
dwOutBytes = 0;
bResult = DeviceIoControl(hDevice, // 设备句柄
VWIN32_DIOC_DOS_DRIVEINFO, // INT21
&regs, sizeof(regs), // 输出数据缓冲区与长度
&regs, sizeof(regs), // 输出数据缓冲区与长度
&dwOutBytes, // 输出数据长度
NULL); // 用同步I/O

// 确定DeviceIoControl与INT21都无错误
bResult = bResult && !(regs.reg_Flags & 1);

CloseHandle(hDevice);

return bResult;
}

BOOL AbsDiskWrite(
BYTE nDiskNumber, // 盘号, 1=A:, 2=B:, 3= C:, ...
DWORD dwStartSector, // 起始扇区
WORD wSectors, // 扇区数
void* pBuffer) // 数据缓冲区指针
{
HANDLE hDevice;
DIOC_REGISTERS regs;
DISKIO dio;
DWORD dwOutBytes;
BOOL bResult;

// 打开设备,获得VxD句柄
hDevice = CreateFile(".//vwin32", // 设备路径
GENERIC_READ | GENERIC_WRITE, // 读写方式
FILE_SHARE_READ | FILE_SHARE_WRITE, // 共享方式
NULL, // 默认的安全描述符
OPEN_EXISTING, // 创建方式
FILE_ATTRIBUTE_NORMAL, // 文件属性
NULL); // 不需参照模板文件

if(hDevice == INVALID_HANDLE_VALUE)
{
return FALSE;
}

// 填充DISKIO参数结构
dio.dwStartSector = dwStartSector;
dio.wSectors = wSectors;
dio.pBuffer = pBuffer;

// 填充寄存器组--中断入口参数
memset(&regs, 0, sizeof(DIOC_REGISTERS));
regs.reg_EAX = 0x7305; // AX=0x7305
regs.reg_EBX = (DWORD)&dio; // EBX=DS:BX=参数指针
regs.reg_ECX = 0xffff; // CX=-1
regs.reg_EDX = nDiskNumber; // DL=盘号
regs.reg_ESI = 0x6001; // SI=0x6001 -- 普通写操作

// 用VWIN32_DIOC_DOS_DRIVEINFO写磁盘
dwOutBytes = 0;
bResult = DeviceIoControl(hDevice, // 设备句柄
VWIN32_DIOC_DOS_DRIVEINFO, // INT21
&regs, sizeof(regs), // 输出数据缓冲区与长度
&regs, sizeof(regs), // 输出数据缓冲区与长度
&dwOutBytes, // 输出数据长度
NULL); // 用同步I/O

// 确定DeviceIoControl与INT21都无错误
bResult = bResult && !(regs.reg_Flags & 1);

CloseHandle(hDevice);

return bResult;
}

BOOL LockVolume(
BYTE nDiskNumber) // 盘号, 1=A:, 2=B:, 3=C:, ...
{
HANDLE hDevice;
DIOC_REGISTERS regs;
DWORD dwOutBytes;
BOOL bResult;

// 打开设备,获得VxD句柄
hDevice = CreateFile(".//vwin32", // 设备路径
GENERIC_READ | GENERIC_WRITE, // 读写方式
FILE_SHARE_READ | FILE_SHARE_WRITE, // 共享方式
NULL, // 默认的安全描述符
OPEN_EXISTING, // 创建方式
FILE_ATTRIBUTE_NORMAL, // 文件属性
NULL); // 不需参照模板文件

if(hDevice == INVALID_HANDLE_VALUE)
{
return FALSE;
}

// 填充寄存器组--中断入口参数
memset(&regs, 0, sizeof(DIOC_REGISTERS));
regs.reg_EAX = 0x440D; // AX=0x440D
regs.reg_EBX = 0x0100 | nDiskNumber; // BH=锁的级别,BL=盘号
regs.reg_ECX = 0x084A;
regs.reg_EDX = 0;

// 用VWIN32_DIOC_DOS_DRIVEINFO读磁盘
dwOutBytes = 0;
bResult = DeviceIoControl(hDevice, // 设备句柄
VWIN32_DIOC_DOS_IOCTL, // INT21
&regs, sizeof(regs), // 输入数据缓冲区与长度
&regs, sizeof(regs), // 输出数据缓冲区与长度
&dwOutBytes, // 输出数据长度
NULL); // 用同步I/O

// 确定DeviceIoControl与INT21都无错误
bResult = bResult && !(regs.reg_Flags & 1);

CloseHandle(hDevice);

return bResult;
}

BOOL UnlockVolume(
BYTE nDiskNumber) // 盘号, 1=A:, 2=B:, 3=C:, ...
{
HANDLE hDevice;
DIOC_REGISTERS regs;
DWORD dwOutBytes;
BOOL bResult;

// 打开设备,获得VxD句柄
hDevice = CreateFile(".//vwin32", // 设备路径
GENERIC_READ | GENERIC_WRITE, // 读写方式
FILE_SHARE_READ | FILE_SHARE_WRITE, // 共享方式
NULL, // 默认的安全描述符
OPEN_EXISTING, // 创建方式
FILE_ATTRIBUTE_NORMAL, // 文件属性
NULL); // 不需参照模板文件

if(hDevice == INVALID_HANDLE_VALUE)
{
return FALSE;
}

// 填充寄存器组--中断入口参数
memset(&regs, 0, sizeof(DIOC_REGISTERS));
regs.reg_EAX = 0x440D; // AX=0x440D
regs.reg_EBX = nDiskNumber; // BL=盘号
regs.reg_ECX = 0x086A;

// 用VWIN32_DIOC_DOS_DRIVEINFO读磁盘
dwOutBytes = 0;
bResult = DeviceIoControl(hDevice, // 设备句柄
VWIN32_DIOC_DOS_IOCTL, // INT21
&regs, sizeof(regs), // 输入数据缓冲区与长度
&regs, sizeof(regs), // 输出数据缓冲区与长度
&dwOutBytes, // 输出数据长度
NULL); // 用同步I/O

// 确定DeviceIoControl与INT21都无错误
bResult = bResult && !(regs.reg_Flags & 1);

CloseHandle(hDevice);

return bResult;
}

下面的例子,从A盘的0扇区开始,读取10个扇区的数据,并保存在文件中:

    unsigned char buf[512 * 10];

if (AbsDiskRead(1, 0, 10, buf))
{
FILE* fp = fopen("a.dat", "w+b");
fwrite(buf, 512, 10, fp);
fclose(fp);
}

下面的例子,读取D驱动器的第8888扇区,然后写回去:

    unsigned char buf[512];

LockVolume(4);
if (AbsDiskRead(4, 8888, 1, buf))
{
... ...
if (AbsDiskWrite(4, 8888, 1, buf))
{
... ...
}
}
UnlockVolume(4);

在写方式下,SI寄存器的位0设置为1,位15-13在磁盘的不同区域需要有不同的值:

Bit 15Bit 14Bit 13Description
000Other/Unknown.
001FAT data.
010Directory data.
011Normal file data.
100Reserved.

如果不按照上述值操作,尽管能够写成功,但系统无法自动完成相关功能,可能会导致FAT数据备份、驱动器数据压缩等方面的问题。

 

[相关资源]

bhw98的专栏: http://www.csdn.net/develop/author/netauthor/bhw98/

From: http://blog.csdn.net/bhw98/archive/2003/09/12/19674.aspx

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值