實戰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數據備份、驅動器數據壓縮等方面的問題。

0

收藏

hello_world

99篇文章,146W+人氣,0粉絲

Ctrl+Enter 發佈

發佈

取消

推薦專欄更多

基於Python的DevOps實戰

自動化運維開發新概念

共20章 | 撫琴煮酒

¥51.00 445人訂閱
網工2.0晉級攻略 ——零基礎入門Python/Ansible

網絡工程師2.0進階指南

共30章 | 薑汁啤酒

¥51.00 1621人訂閱
全局視角看大型園區網

路由交換+安全+無線+優化+運維

共40章 | 51CTO夏傑

¥51.00 1843人訂閱
負載均衡高手煉成記

高併發架構之路

共15章 | sery

¥51.00 516人訂閱
帶你玩轉高可用

前百度高級工程師的架構高可用實戰

共15章 | 曹林華

¥51.00 471人訂閱

掃一掃,領取大禮包

0

分享
hello_world
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章