zoukankan      html  css  js  c++  java
  • 实战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 15 Bit 14 Bit 13 Description
    0 0 0 Other/Unknown.
    0 0 1 FAT data.
    0 1 0 Directory data.
    0 1 1 Normal file data.
    1 0 0 Reserved.


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

     

    [相关资源]
    bhw98的专栏:http://www.csdn.net/develop/author/netauthor/bhw98/

  • 相关阅读:
    abap 调用Http --“Get”
    SAP-PI接口创建中的ABAP处理(不含PI配置)
    ALV 监听事件
    ABAP动态创建内表并展示--自撸版
    ABAP动态生成内表的三种方法
    SAP BP字段增强--付款条件检查
    SAP RFC上传接口(包含发布Webservice地址)
    C# 时间函数
    配置Excel的DCOM权限
    ORA-12571 : TNS : 包写入程序失败
  • 原文地址:https://www.cnblogs.com/lzjsky/p/1947882.html
Copyright © 2011-2022 走看看