天天看點

實戰DeviceIoControl 之七:在Windows 9X中讀寫磁盤扇區

實戰DeviceIoControl 共7篇,這是我在兩年前看到的文章,很不錯,現在想把它轉載過來。

原作者和其部落格的位址是:http://blog.csdn.net/bhw98

他部落格裡的這些序列文章的格式更好看,如果你覺得這些文章好,不妨去他的部落格看,格式比我的随便粘貼好多了 :)

在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
Other/Unknown.
1 FAT data.
1 Directory data.
1 1 Normal file data.
1 Reserved.

如果不按照上述值操作,盡管能夠寫成功,但系統無法自動完成相關功能,可能會導緻FAT資料備份、驅動器資料壓縮等方面的問題。

[相關資源]

  • bhw98的專欄:http://www.csdn.net/develop/author/netauthor/bhw98/