#include <WinIOCtl.h> #include <stdio.h>
#pragma inline //--------------------------------------------------------------------------- // IDE NT/2000/XP专用变量 #define GETVERSIONOUTPARAMS GETVERSIONINPARAMS #define DFP_GET_VERSION SMART_GET_VERSION #define DFP_SEND_DRIVE_COMMAND SMART_SEND_DRIVE_COMMAND #define DFP_RCV_DRIVE_DATA SMART_RCV_DRIVE_DATA
const WORD IDE_ATAPI_IDENTIFY = 0xA1; // 读取ATAPI设备的命令 const WORD IDE_ATA_IDENTIFY = 0xEC; // 读取ATA设备的命令
const int MAX_IDE_DRIVES = 4;
// SCSI专用变量 const DWORD FILE_DEVICE_SCSI = 0x0000001B; const DWORD IOCTL_SCSI_MINIPORT_IDENTIFY = ((FILE_DEVICE_SCSI << 16) + 0x0501); const DWORD IOCTL_SCSI_MINIPORT = 0x0004D008; // see NTDDSCSI.H for definition const DWORD SENDIDLENGTH = sizeof(SENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE;
typedef struct _SRB_IO_CONTROL { ULONG HeaderLength; UCHAR Signature[8]; ULONG Timeout; ULONG ControlCode; ULONG ReturnCode; ULONG Length; }SRB_IO_CONTROL, *PSRB_IO_CONTROL;
// 读取的主函数 void __fastcall ReadPhysicalDrive(TStrings *pSerList, TStrings *pModeList);
// 辅助函数 char *__fastcall ConvertToString(DWORD dwDiskData[256], int nFirstIndex, int nLastIndex); // NT/2000/XP函数 void __fastcall ReadPhysicalDriveOnNT(TStrings *pSerList, TStrings *pModeList); bool __fastcall DoIdentify(HANDLE hPhysicalDriveIOCTL, PSENDCMDINPARAMS pSCIP, PSENDCMDOUTPARAMS pSCOP, BYTE btIDCmd, BYTE btDriveNum, PDWORD lpcbBYTEsReturned); // Windows 9X函数 void __fastcall ReadPhysicalDriveOnW9X(TStrings *pSerList, TStrings *pModeList); void __fastcall ReadPhysicalDriveOnW9X_Ring0(bool IsFirst, WORD BaseAddress, BYTE MoS, bool &IsIDEExist, bool &IsDiskExist, WORD *OutData);
// SCSI读取函数(for NT/2000/XP) String __fastcall ReadIDEDriveAsScsiDriveOnNT(); //--------------------------------------------------------------------------- // ReadPhysicalDrive void __fastcall ReadPhysicalDrive(TStrings *pSerList, TStrings *pModeList) { switch(Win32Platform) { case VER_PLATFORM_WIN32_WINDOWS: ReadPhysicalDriveOnW9X(pSerList, pModeList); break; case VER_PLATFORM_WIN32_NT: ReadPhysicalDriveOnNT(pSerList, pModeList); break; default: break; } } //--------------------------------------------------------------------------- // ConvertToString char *__fastcall ConvertToString(DWORD dwDiskData[256], int nFirstIndex, int nLastIndex) { static char szResBuf[1024]; int nIndex = 0; int nPosition = 0;
// Each integer has two characters stored in it backwards for(nIndex = nFirstIndex; nIndex <= nLastIndex; nIndex++) { // Get high BYTE for 1st character szResBuf[nPosition] = (char)(dwDiskData[nIndex] / 256); nPosition++;
// Get low BYTE for 2nd character szResBuf[nPosition] = (char)(dwDiskData[nIndex] % 256); nPosition++; }
// End the string szResBuf[nPosition] = '';
// Cut off the trailing blanks for(nIndex = nPosition - 1; nIndex > 0 && ' ' == szResBuf[nIndex]; nIndex--) szResBuf[nIndex] = '';
return szResBuf; } //--------------------------------------------------------------------------- // Winndows NT4/2000/XP 代码 //--------------------------------------------------------------------------- // ReadPhysicalDriveOnNT void __fastcall ReadPhysicalDriveOnNT(TStrings *pSerList, TStrings *pModeList) { // 输出参数 BYTE btIDOutCmd[sizeof(SENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE - 1];
for(int nDrive=0; nDrive < MAX_IDE_DRIVES; nDrive++) { HANDLE hPhysicalDriveIOCTL; char szDriveName[32];
sprintf(szDriveName, "\\.\PhysicalDrive%d", nDrive); hPhysicalDriveIOCTL = CreateFile(szDriveName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if(hPhysicalDriveIOCTL != INVALID_HANDLE_VALUE) { DWORD dwBytesReturned = 0; GETVERSIONOUTPARAMS gvopVersionParams;
// Get the version, etc of PhysicalDrive IOCTL ZeroMemory(&gvopVersionParams, sizeof(GETVERSIONOUTPARAMS));
if(!DeviceIoControl(hPhysicalDriveIOCTL, DFP_GET_VERSION, NULL, 0, &gvopVersionParams, sizeof(gvopVersionParams), &dwBytesReturned, NULL)) { continue; }
if(gvopVersionParams.bIDEDeviceMap > 0) { // IDE or ATAPI IDENTIFY cmd BYTE btIDCmd = 0; SENDCMDINPARAMS InParams; // Now, get the ID sector for all IDE devices in the system. // If the device is ATAPI use the IDE_ATAPI_IDENTIFY command, // otherwise use the IDE_ATA_IDENTIFY command // 具体所得结果请参考头文件中的说明 btIDCmd = (gvopVersionParams.bIDEDeviceMap >> nDrive & 0x10) ? IDE_ATAPI_IDENTIFY : IDE_ATA_IDENTIFY; ZeroMemory(&InParams, sizeof(SENDCMDINPARAMS)); ZeroMemory(btIDOutCmd, sizeof(btIDOutCmd));
if(DoIdentify(hPhysicalDriveIOCTL, &InParams, (PSENDCMDOUTPARAMS)btIDOutCmd, (BYTE)btIDCmd, (BYTE)nDrive, &dwBytesReturned)) { DWORD dwDiskData[256]; USHORT *pIDSector; // 对应结构IDSECTOR,见头文件 char szSerialNumber[21]; char szModelNumber[41];
pIDSector = (USHORT*)((SENDCMDOUTPARAMS*)btIDOutCmd)->bBuffer; for(int i=0; i < 256; i++) dwDiskData[i] = pIDSector[i]; // 取系列号 ZeroMemory(szSerialNumber, sizeof(szSerialNumber)); strcpy(szSerialNumber, ConvertToString(dwDiskData, 10, 19));
// 取模型号 ZeroMemory(szModelNumber, sizeof(szModelNumber)); strcpy(szModelNumber, ConvertToString(dwDiskData, 27, 46));
pSerList->Add(szSerialNumber); pModeList->Add(szModelNumber); } } CloseHandle (hPhysicalDriveIOCTL); } } } //--------------------------------------------------------------------------- // DoIdentify bool __fastcall DoIdentify(HANDLE hPhysicalDriveIOCTL, PSENDCMDINPARAMS pSCIP, PSENDCMDOUTPARAMS pSCOP, BYTE btIDCmd, BYTE btDriveNum, PDWORD pdwBytesReturned) { // Set up data structures for IDENTIFY command. pSCIP->cBufferSize = IDENTIFY_BUFFER_SIZE; pSCIP->irDriveRegs.bFeaturesReg = 0; pSCIP->irDriveRegs.bSectorCountReg = 1; pSCIP->irDriveRegs.bSectorNumberReg = 1; pSCIP->irDriveRegs.bCylLowReg = 0; pSCIP->irDriveRegs.bCylHighReg = 0;
// Compute the drive number.(主盘和从盘所对应的值是不一样的) pSCIP->irDriveRegs.bDriveHeadReg = (btDriveNum & 1) ? 0xB0 : 0xA0;
// The command can either be IDE identify or ATAPI identify. pSCIP->irDriveRegs.bCommandReg = btIDCmd; pSCIP->bDriveNumber = btDriveNum; pSCIP->cBufferSize = IDENTIFY_BUFFER_SIZE;
return DeviceIoControl(hPhysicalDriveIOCTL, DFP_RCV_DRIVE_DATA, (LPVOID)pSCIP, sizeof(SENDCMDINPARAMS) - 1, (LPVOID)pSCOP, sizeof(SENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE - 1, pdwBytesReturned, NULL); } //--------------------------------------------------------------------------- // Windows 95/98/ME 代码 //--------------------------------------------------------------------------- // ReadPhysicalDriveOnW9X void __fastcall ReadPhysicalDriveOnW9X(TStrings *pSerList, TStrings *pModeList) { WORD wOutData[256]; SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
// 经过测试,发现第一次调用而且Drive >= 2时会在Ring0代码中出现错误,导致蓝屏。 // 经过N(N > 15)次的蓝屏后仍找不到原因:(,不得不在这里增加一段无用代码以 // 避免蓝屏的出现。(期待高人能指出原因) for(int nDrive = 0; nDrive < 8; nDrive++) { WORD dwBaseAddress; BYTE btMasterSlave; // Master Or Slave bool bIsIDEExist; bool IsDiskExist;
switch(nDrive / 2) { case 0: dwBaseAddress = 0x01F0; break; case 1: dwBaseAddress = 0x0170; break; case 2: dwBaseAddress = 0x01E8; break; case 3: dwBaseAddress = 0x0168; break; }
btMasterSlave = (BYTE)(((nDrive % 2) == 0) ? 0xA0 : 0xB0);
// 进入Ring0 ReadPhysicalDriveOnW9X_Ring0(true, dwBaseAddress, btMasterSlave, bIsIDEExist, IsDiskExist, wOutData); }
// 开始读取 for(int nDrive = 0; nDrive < 8; nDrive++) { WORD dwBaseAddress; BYTE btMasterSlave; // Master Or Slave bool bIsIDEExist; bool bIsDiskExist; switch(nDrive / 2) { case 0: dwBaseAddress = 0x01F0; break; case 1: dwBaseAddress = 0x0170; break; case 2: dwBaseAddress = 0x01E8; break; case 3: dwBaseAddress = 0x0168; break; }
btMasterSlave = (BYTE)(((nDrive % 2) == 0) ? 0xA0 : 0xB0);
// 进入Ring0 bIsIDEExist = false; bIsDiskExist = false; ZeroMemory(wOutData, sizeof(wOutData));
ReadPhysicalDriveOnW9X_Ring0(false, dwBaseAddress, btMasterSlave, bIsIDEExist, bIsDiskExist, wOutData);
if(bIsIDEExist && bIsDiskExist) { DWORD dwDiskData[256]; char szSerialNumber[21]; char szModelNumber[41];
for(int k=0; k < 256; k++) dwDiskData[k] = wOutData[k];
// 取系列号 ZeroMemory(szSerialNumber, sizeof(szSerialNumber)); strcpy(szSerialNumber, ConvertToString(dwDiskData, 10, 19));
// 取模型号 ZeroMemory(szModelNumber, sizeof(szModelNumber)); strcpy(szModelNumber, ConvertToString(dwDiskData, 27, 46));
pSerList->Add(szSerialNumber); pModeList->Add(szModelNumber); } } SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); } //--------------------------------------------------------------------------- // 为防止不负责任的转载者,在此注明原出处信息,请见谅。 // 资料收集整理:ccrun(老妖),欢迎光临C++Builder研究: http://www.ccrun.com //--------------------------------------------------------------------------- // ReadPhysicalDriveOnW9X_Ring0() // // dwBaseAddress = IDE(0,1,2,3) : 1F0h, 170h, 1E8h, 168h // btMasterSlave = Master(0xA0) Or Slave(0xB0) //--------------------------------------------------------------------------- void __fastcall ReadPhysicalDriveOnW9X_Ring0(bool bIsFirst, WORD dwBaseAddress, BYTE btMasterSlave, bool &bIsIDEExist, bool &bIsDiskExist, WORD *pOutData) { BYTE btIDTR1[6]; DWORD dwOldExceptionHook; const int nHookExceptionNo = 5;
BYTE btIsIDEExist = 0; BYTE btIsDiskExist = 0; WORD wOutDataBuf[256];
BYTE btIsFirst = (BYTE)bIsFirst;
const BYTE btBit00 = 0x01; // const BYTE btBit02 = 0x04; const BYTE btBit06 = 0x40; const BYTE btBit07 = 0x80; // const BYTE btERR = btBit00; const BYTE btBusy = btBit07; const BYTE btAtaCmd = 0xEC; const BYTE btAtapiCmd = 0xA1;
__asm { // 必须先执行这条语句 JMP EnterRing0
// 定义过程 // 等待IDE设备直到其不为忙为止 WaitWhileBusy proc
MOV EBX, 100000 MOV DX, dwBaseAddress ADD DX, 7
LoopWhileBusy:
DEC EBX CMP EBX, 0 JZ Timeout in AL, DX TEST AL, btBusy JNZ LoopWhileBusy JMP DriveReady
// 超时,直接退出 Timeout: JMP LeaveRing0 DriveReady: RET ENDP // End of WaitWhileBusy Procedure
// 设置主盘和从盘标志 SelectDevice proc
MOV DX, dwBaseAddress ADD DX, 6 MOV AL, btMasterSlave
out DX, AL RET
ENDP // End of SelectDevice Procedure
// 向IDE设备发送存取指令 SendCmd proc
MOV DX, dwBaseAddress ADD DX, 7 MOV AL, BL // BL是主从盘标识,在过程外设置 out DX, AL RET ENDP // End of SendCmd Procedure
// Ring0代码 Ring0Proc: PUSHAD // 查询IDE设备是否存在 MOV DX, dwBaseAddress ADD DX, 7 in AL,DX
// 当AL的值是0xFF或者0x7F时,IDE设备不存在,这时候直接返回 CMP AL,0xFF JZ LeaveRing0 CMP AL, 0x7F JZ LeaveRing0
// 设置IDE设备存在标志 MOV btIsIDEExist, 1
// 查询IDE设备上的驱动器是否存在(有IDE插槽在主板上,但是却不一定有硬盘插在上面) CALL WaitWhileBusy CALL SelectDevice
// 如果是第一次调用,则直接返回,否则执行下行语句时会出现蓝屏 CMP btIsFirst, 1 JZ LeaveRing0
// 第一次调用时,如果执行这行语句会导致蓝屏,Why??? CALL WaitWhileBusy
// AL的值等于cBit06时,不存在驱动器,直接返回 TEST AL, btBit06 JZ LeaveRing0
// 设置驱动器存在标志 MOV btIsDiskExist, 1
// 发送存取端口命令 // 无法像NT/2000/XP那样可以通过查询VERSION的值得到驱动器的类型, // 所以只能一步一步地测试,如果不是ATA设备,再尝试使用ATAPI设备命令 CALL WaitWhileBusy CALL SelectDevice // 设置主从盘标识 MOV BL, btAtaCmd // 发送读取命令 CALL SendCmd CALL WaitWhileBusy
// 检查是否出错 MOV DX, dwBaseAddress ADD DX, 7
in AL, DX
TEST AL, btBit00 JZ RetrieveInfo // 没有错误时则读数据
// 如果出错,则进一步尝试使用ATAPI设备命令 CALL WaitWhileBusy CALL SelectDevice MOV BL, btAtapiCmd CALL SendCmd CALL WaitWhileBusy
// 检查是否还出错 MOV DX, dwBaseAddress ADD DX, 7 in AL, DX TEST AL, btBit00 JZ RetrieveInfo // 没有错误时则读数据 JMP LeaveRing0 // 如果还是出错,直接返回
// 读取数据 RetrieveInfo:
LEA EDI, wOutDataBuf MOV ECX, 256 MOV DX, dwBaseAddress CLD
REP INSW
// 退出Ring0代码 LeaveRing0:
POPAD IRETD
// 激活Ring0代码 EnterRing0:
// 修改中断门 SIDT FWORD PTR btIDTR1 MOV EAX, DWORD PTR btIDTR1 + 02h ADD EAX, nHookExceptionNo * 08h + 04h CLI
// 保存原异常处理例程入口 MOV ECX, DWORD PTR [EAX] MOV CX, WORD PTR [EAX-04h] MOV dwOldExceptionHook, ECX
// 指定新入口 LEA EBX, Ring0Proc MOV WORD PTR [EAX-04h],BX SHR EBX, 10h MOV WORD PTR[EAX+02h], BX
// 激活Ring0代码 INT nHookExceptionNo
// 复原入口 MOV ECX,dwOldExceptionHook MOV WORD PTR[EAX-04h], CX SHR ECX,10h MOV WORD PTR[EAX+02h], CX STI } if(!bIsFirst) { bIsIDEExist = (bool)btIsIDEExist; bIsDiskExist = (bool)btIsDiskExist; CopyMemory(pOutData, wOutDataBuf, sizeof(wOutDataBuf)); } } //--------------------------------------------------------------------------- // 调用方法: void __fastcall TForm1::Button1Click(TObject *Sender) { ReadPhysicalDrive(Memo1->Lines, Memo2->Lines); } |