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

來源:互聯網
上載者:User

在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 -- 功能號7305HDS:BX -- 讀寫扇區的資訊結構CX -- 必須為-1DL -- 磁碟機代號: 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 -- 功能號440DHBH -- 鎖的層級,0-3級,直接寫扇區用1BL -- 磁碟機代號: 1=A:, 2=B:, 3=C:, ...CH -- 0x08CL -- 0x4ADX -- 0
AX -- 功能號440DHBL -- 磁碟機代號: 1=A:, 2=B:, 3=C:, ...CH -- 0x08CL -- 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/
  • 相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.