引言:笔者在工作过程中,用户上报某某市信息科技学业水平测试软件在云电脑上打开初始化的情况下出现了加载和绑定机器失败的问题。一般情况下,在实体机上用户进行登录后,用户的账号信息跟主机的机器码进行绑定然后保存到配置文件,等下次再次登录的时候就可以不用再次输入用户账户信息的情况下完成自动登录。但是用户反馈在我们的云电脑上出现了无法完成自动登录的情况。
解决过程:
用dbgx64.exe对该软件进行逆向调试分析(这里过程复杂,不具体罗列),发现该软件是用vb写的程序,并最后调试并发现该软件获取机器序列号的函数,该软件是通过DeviceIoControl这个Api来获取机器的序列号的,通过hook该Api发现该软件会调用三次DeviceIoControl这个api来获取机器码。到底要hook哪一次的调用呢。
通过在网上找到vb程序写的获取机器码的相关代码(代码放在后面的附录里面)。通过vb6.0对改代码进行调试,发现该代码基本就是该软件获取机器码的方式:
并发现,在云桌面上每次通过DeviceIoCtrol获取的机器码都是不一样的,而在实体机上发现,该Api调用会返回失败,但为什么在实体机软件能运行正常呢,这里猜测是软件在获取机器码失败的情况会通过固定算法自动生成一个ID的方式来替代本机的机器码,而在云主机上由于每次调用DeviceIoCtrol都能调用成功,但是获取的机器码不一样导致校验失败。
这里通过Hook DeviceIoCtrol并根据特定的ID进行返回失败操作来达到目的。可以通过CFF对软件进行修改导入表的方式来完成软件对自己插件的自动依赖和加载,但同时考虑到软件软件的升级,所以这里不建议直接给软件程序打补丁,而是通过对软件依赖的msvbvm60.dll进行打补丁,这样哪怕软件完成了升级也不会导致因为软件文件升级而导致补丁加载失效:
附录代码:
static BOOL(WINAPI* OrgDeviceIoControl)(
HANDLE hDevice,
DWORD dwIoControlCode,
LPVOID lpInBuffer,
DWORD nInBufferSize,
LPVOID lpOutBuffer,
DWORD nOutBufferSize,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped
) = DeviceIoControl;
BOOL WINAPI NewDeviceIoControl(
HANDLE hDevice,
DWORD dwIoControlCode,
LPVOID lpInBuffer,
DWORD nInBufferSize,
LPVOID lpOutBuffer,
DWORD nOutBufferSize,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped
)
{
BOOL bRet = OrgDeviceIoControl(hDevice,
dwIoControlCode,
lpInBuffer,
nInBufferSize,
lpOutBuffer,
nOutBufferSize,
lpBytesReturned,
lpOverlapped);
//IOCTL_DISK_GET_DRIVE_GEOMETRY;
/* if (0x74080 == dwIoControlCode || 0x7C084 == dwIoControlCode || dwIoControlCode == 0x7C088)
{
bRet = FALSE;
return FALSE;
} */
if (dwIoControlCode == 0x7C088)
{
bRet = FALSE;
OutputDebugString(L"DeviceIoCtrol Code is 0x7C088, process it!");
SENDCMDOUTPARAMS out;
int outparamsize = sizeof(out);
memcpy(&out, lpOutBuffer, sizeof(out));
IDSECTOR idsector;
memcpy(&idsector, &out.bBuffer[0], sizeof(idsector));
char* pcNumber = idsector.sModelNumber;
__int64 pNumber = (__int64)pcNumber;
__int64 pFirmware = (__int64)idsector.sFirmwareRev;
__int64 pSerNum = (__int64)idsector.sSerialNumber;
WCHAR szBuf[350] = { 0 };
wsprintf(szBuf, L" sModelNumber 1:0x%p 0x%02X %02X %02X %02X %02X %02X %02X %02X \n"
L"0x%02X %02X %02X %02X %02X %02X %02X %02X \n"
L"0x%02X %02X %02X %02X %02X %02X %02X %02X \n"
L"0x%02X %02X %02X %02X %02X %02X %02X %02X \n"
L"0x%02X %02X %02X %02X %02X %02X %02X %02X \n",
pcNumber, *(char*)(pNumber+0), *(char*)(pNumber + 1), *(char*)(pNumber + 2), *(char*)(pNumber + 3), *(char*)(pNumber + 4), *(char*)(pNumber + 5), *(char*)(pNumber + 6), *(char*)(pNumber + 7),
*(char*)(pNumber + 8), *(char*)(pNumber + 9), *(char*)(pNumber + 10), *(char*)(pNumber + 11), *(char*)(pNumber + 12), *(char*)(pNumber + 13), *(char*)(pNumber + 14), *(char*)(pNumber + 15),
*(char*)(pNumber + 16), *(char*)(pNumber + 17), *(char*)(pNumber + 18), *(char*)(pNumber + 19), *(char*)(pNumber + 20), *(char*)(pNumber + 21), *(char*)(pNumber + 22), *(char*)(pNumber + 23),
*(char*)(pNumber + 24), *(char*)(pNumber + 25), *(char*)(pNumber + 26), *(char*)(pNumber + 27), *(char*)(pNumber + 28), *(char*)(pNumber + 29), *(char*)(pNumber + 30), *(char*)(pNumber + 31),
*(char*)(pNumber + 32), *(char*)(pNumber + 33), *(char*)(pNumber + 34), *(char*)(pNumber + 35), *(char*)(pNumber + 36), *(char*)(pNumber + 37), *(char*)(pNumber + 38), *(char*)(pNumber + 39)
);
OutputDebugStringW(szBuf);
wsprintf(szBuf, L" sFirmwareRev 2:0x%p 0x%02X %02X %02X %02X %02X %02X %02X %02X \n",
pFirmware, *(char*)(pFirmware + 0), *(char*)(pFirmware + 1), *(char*)(pFirmware + 2), *(char*)(pFirmware + 3), *(char*)(pFirmware + 4), *(char*)(pFirmware + 5), *(char*)(pFirmware + 6), *(char*)(pFirmware + 7));
OutputDebugStringW(szBuf);
wsprintf(szBuf,
L"sSerialNumber 3:0x%p 0x%02X %02X %02X %02X %02X %02X %02X %02X \n"
L"0x%02X %02X %02X %02X %02X %02X %02X %02X \n"
L"0x%02X %02X %02X %02X \n",
pSerNum, *(char*)(pSerNum + 0), *(char*)(pSerNum + 1), *(char*)(pSerNum + 2), *(char*)(pSerNum + 3), *(char*)(pSerNum + 4), *(char*)(pSerNum + 5), *(char*)(pSerNum + 6), *(char*)(pSerNum + 7),
*(char*)(pSerNum + 8), *(char*)(pSerNum + 9), *(char*)(pSerNum + 10), *(char*)(pSerNum + 11), *(char*)(pSerNum + 12), *(char*)(pSerNum + 13), *(char*)(pSerNum + 14), *(char*)(pSerNum + 15),
*(char*)(pSerNum + 16), *(char*)(pSerNum + 17), *(char*)(pSerNum + 18), *(char*)(pSerNum + 19)
);
OutputDebugStringW(szBuf);
}
return bRet;
}
bool Hook()
{
// 相关的初始化信息
DetourTransactionBegin();
// 更新线程信息
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)OrgDeviceIoControl, NewDeviceIoControl);
// org_vbaStrCmp = (_vbaStrCmp)GetProcAddress(LoadLibraryA("msvbvm60.dll"), "__vbaStrCmp");
//int iRet2 = DetourAttach(&(PVOID&)org_vbaStrCmp, new_vbaStrCmp);
return NO_ERROR == DetourTransactionCommit();
}
// 卸载Hook
bool UnHoo()
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
return NO_ERROR == DetourTransactionCommit();
}
C#获取机器码的相关代码:
VERSION 5.00
Begin VB.Form Form1
Caption = "Form1"
ClientHeight = 3015
ClientLeft = 120
ClientTop = 465
ClientWidth = 4560
LinkTopic = "Form1"
ScaleHeight = 3015
ScaleWidth = 4560
StartUpPosition = 3 '窗口缺省
Begin VB.CommandButton Command1
Caption = "Command1"
Height = 855
Left = 1080
TabIndex = 0
Top = 600
Width = 1335
End
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
'============================================
'模块功能:取得硬盘的信息
'编 程:来自互联网,阿勇修改
'更新日期:2005/7/8
'调用方法:
' GetDiskVolume() 取得逻辑盘的序列号
' GetHardDiskInfo() 取得物理盘的型号或序列号
'============================================
Private Const MAX_IDE_DRIVES As Long = 4
Private Const READ_ATTRIBUTE_BUFFER_SIZE As Long = 512
Private Const IDENTIFY_BUFFER_SIZE As Long = 512
Private Const READ_THRESHOLD_BUFFER_SIZE As Long = 512
Private Const DFP_GET_VERSION As Long = &H74080
Private Const DFP_SEND_DRIVE_COMMAND As Long = &H7C084
Private Const DFP_RECEIVE_DRIVE_DATA As Long = &H7C088
Private Type GETVERSIONOUTPARAMS
bVersion As Byte ' Binary driver version.
bRevision As Byte ' Binary driver revision.
bReserved As Byte ' Not used.
bIDEDeviceMap As Byte ' Bit map of IDE devices.
fCapabilities As Long ' Bit mask of driver capabilities.
dwReserved(3) As Long ' For future use.
End Type
Private Const CAP_IDE_ID_FUNCTION As Long = 1 ' ATA ID command supported
Private Const CAP_IDE_ATAPI_ID As Long = 2 ' ATAPI ID command supported
Private Const CAP_IDE_EXECUTE_SMART_FUNCTION As Long = 4 ' SMART commannds supported
Private Type IDEREGS
bFeaturesReg As Byte ' Used for specifying SMART "commands".
bSectorCountReg As Byte ' IDE sector count register
bSectorNumberReg As Byte ' IDE sector number register
bCylLowReg As Byte ' IDE low order cylinder value
bCylHighReg As Byte ' IDE high order cylinder value
bDriveHeadReg As Byte ' IDE drive/head register
bCommandReg As Byte ' Actual IDE command.
bReserved As Byte ' reserved for future use. Must be zero.
End Type
Private Type SENDCMDINPARAMS
cBufferSize As Long ' Buffer size in bytes
irDriveRegs As IDEREGS ' Structure with drive register values.
bDriveNumber As Byte ' Physical drive number to send
' command to (0,1,2,3).
bReserved(2) As Byte ' Reserved for future expansion.
dwReserved(3) As Long ' For future use.
bBuffer(0) As Byte ' Input buffer.
End Type
Private Const IDE_ATAPI_ID As Long = &HA1 ' Returns ID sector for ATAPI.
Private Const IDE_ID_FUNCTION As Long = &HEC ' Returns ID sector for ATA.
Private Const IDE_EXECUTE_SMART_FUNCTION As Long = &HB0 ' Performs SMART cmd.
Private Const SMART_CYL_LOW As Long = &H4F
Private Const SMART_CYL_HI As Long = &HC2
Private Type DRIVERSTATUS
bDriverError As Byte ' Error code from driver,
bIDEStatus As Byte ' Contents of IDE Error register.
bReserved(1) As Byte ' Reserved for future expansion.
dwReserved(1) As Long ' Reserved for future expansion.
End Type
Private Const SMART_NO_ERROR As Long = 0 ' No error
Private Const SMART_IDE_ERROR As Long = 1 ' Error from IDE controller
Private Const SMART_INVALID_FLAG As Long = 2 ' Invalid command flag
Private Const SMART_INVALID_COMMAND As Long = 3 ' Invalid command byte
Private Const SMART_INVALID_BUFFER As Long = 4 ' Bad buffer (null, invalid addr..)
Private Const SMART_INVALID_DRIVE As Long = 5 ' Drive number not valid
Private Const SMART_INVALID_IOCTL As Long = 6 ' Invalid IOCTL
Private Const SMART_ERROR_NO_MEM As Long = 7 ' Could not lock user's buffer
Private Const SMART_INVALID_REGISTER As Long = 8 ' Some IDE Register not valid
Private Const SMART_NOT_SUPPORTED As Long = 9 ' Invalid cmd flag set
Private Const SMART_NO_IDE_DEVICE As Long = 10 ' Cmd issued to device not present
Private Type SENDCMDOUTPARAMS
cBufferSize As Long ' Size of bBuffer in bytes
drvStatus As DRIVERSTATUS ' Driver status structure.
bBuffer(0) As Byte ' Buffer of arbitrary length in which to store the data read from the ' drive.
End Type
Private Const SMART_READ_ATTRIBUTE_VALUES As Long = &HD0 ' ATA4: Renamed
Private Const SMART_READ_ATTRIBUTE_THRESHOLDS As Long = &HD1 ' Obsoleted in ATA4!
Private Const SMART_ENABLE_DISABLE_ATTRIBUTE_AUTOSAVE As Long = &HD2
Private Const SMART_SAVE_ATTRIBUTE_VALUES As Long = &HD3
Private Const SMART_EXECUTE_OFFLINE_IMMEDIATE As Long = &HD4 ' ATA4
Private Const SMART_ENABLE_SMART_OPERATIONS As Long = &HD8
Private Const SMART_DISABLE_SMART_OPERATIONS As Long = &HD9
Private Const SMART_RETURN_SMART_STATUS As Long = &HDA
Private Type DRIVEATTRIBUTE
bAttrID As Byte ' Identifies which attribute
wStatusFlags As Integer ' see bit definitions below
bAttrValue As Byte ' Current normalized value
bWorstValue As Byte ' How bad has it ever been?
bRawValue(5) As Byte ' Un-normalized value
bReserved As Byte ' ...
End Type
Private Type ATTRTHRESHOLD
bAttrID As Byte ' Identifies which attribute
bWarrantyThreshold As Byte ' Triggering value
bReserved(9) As Byte ' ...
End Type
Private Type IDSECTOR
wGenConfig As Integer
wNumCyls As Integer
wReserved As Integer
wNumHeads As Integer
wBytesPerTrack As Integer
wBytesPerSector As Integer
wSectorsPerTrack As Integer
wVendorUnique(2) As Integer
sSerialNumber(19) As Byte
wBufferType As Integer
wBufferSize As Integer
wECCSize As Integer
sFirmwareRev(7) As Byte
sModelNumber(39) As Byte
wMoreVendorUnique As Integer
wDoubleWordIO As Integer
wCapabilities As Integer
wReserved1 As Integer
wPIOTiming As Integer
wDMATiming As Integer
wBS As Integer
wNumCurrentCyls As Integer
wNumCurrentHeads As Integer
wNumCurrentSectorsPerTrack As Integer
ulCurrentSectorCapacity(3) As Byte '这里只能用byte,因为VB没有无符号的LONG型变量
wMultSectorStuff As Integer
ulTotalAddressableSectors(3) As Byte '这里只能用byte,因为VB没有无符号的LONG型变量
wSingleWordDMA As Integer
wMultiWordDMA As Integer
bReserved(127) As Byte
End Type
Private Const ATTR_INVALID As Long = 0
Private Const ATTR_READ_ERROR_RATE As Long = 1
Private Const ATTR_THROUGHPUT_PERF As Long = 2
Private Const ATTR_SPIN_UP_TIME As Long = 3
Private Const ATTR_START_STOP_COUNT As Long = 4
Private Const ATTR_REALLOC_SECTOR_COUNT As Long = 5
Private Const ATTR_READ_CHANNEL_MARGIN As Long = 6
Private Const ATTR_SEEK_ERROR_RATE As Long = 7
Private Const ATTR_SEEK_TIME_PERF As Long = 8
Private Const ATTR_POWER_ON_HRS_COUNT As Long = 9
Private Const ATTR_SPIN_RETRY_COUNT As Long = 10
Private Const ATTR_CALIBRATION_RETRY_COUNT As Long = 11
Private Const ATTR_POWER_CYCLE_COUNT As Long = 12
Private Const PRE_FAILURE_WARRANTY As Long = &H1
Private Const ON_LINE_COLLECTION As Long = &H2
Private Const PERFORMANCE_ATTRIBUTE As Long = &H4
Private Const ERROR_RATE_ATTRIBUTE As Long = &H8
Private Const EVENT_COUNT_ATTRIBUTE As Long = &H10
Private Const SELF_PRESERVING_ATTRIBUTE As Long = &H20
Private Const NUM_ATTRIBUTE_STRUCTS As Long = 30
Private Const INVALID_HANDLE_VALUE As Long = -1
Private Const VER_PLATFORM_WIN32s As Long = 0
Private Const VER_PLATFORM_WIN32_WINDOWS As Long = 1
Private Const VER_PLATFORM_WIN32_NT As Long = 2
Private Type OSVERSIONINFO
dwOSVersionInfoSize As Long
dwMajorVersion As Long
dwMinorVersion As Long
dwBuildNumber As Long
dwPlatformId As Long
szCSDVersion As String * 128 ' Maintenance string for PSS usage
End Type
Private Const CREATE_NEW As Long = 1
Private Const GENERIC_READ As Long = &H80000000
Private Const GENERIC_WRITE As Long = &H40000000
Private Const FILE_SHARE_READ As Long = &H1
Private Const FILE_SHARE_WRITE As Long = &H2
Private Const OPEN_EXISTING As Long = 3
Private m_DiskInfo As IDSECTOR
Private Declare Function GetVersionEx Lib "kernel32" Alias "GetVersionExA" (lpVersionInformation As OSVERSIONINFO) As Long
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function DeviceIoControl Lib "kernel32" (ByVal hDevice As Long, ByVal dwIoControlCode As Long, lpInBuffer As Any, ByVal nInBufferSize As Long, lpOutBuffer As Any, ByVal nOutBufferSize As Long, lpBytesReturned As Long, ByVal lpOverlapped As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Declare Function GetVolumeInformation Lib "kernel32" Alias "GetVolumeInformationA" (ByVal lpRootPathName As String, ByVal lpVolumeNameBuffer As String, ByVal nVolumeNameSize As Long, lpVolumeSerialNumber As Long, lpMaximumComponentLength As Long, lpFileSystemFlags As Long, ByVal lpFileSystemNameBuffer As String, ByVal nFileSystemNameSize As Long) As Long
'信息类型枚举
Enum eumInfoType
hdmodelsn = 0
hdOnlyModel = 1
hdOnlySN = 2
End Enum
'磁盘通道枚举
Enum eumDiskNo
hdPrimaryMaster = 0
hdPrimarySlave = 1
hdSecondaryMaster = 2
hdSecondarySlave = 3
End Enum
'取得逻辑盘序列号(非唯一)
Function GetDiskVolume(Optional ByVal strDiskName = "C") As String
Dim TempStr1 As String * 256, TempStr2 As String * 256
Dim TempLon1 As Long, TempLon2 As Long, GetVal As Long
Dim tmpVol As String
Call GetVolumeInformation(strDiskName & ":/", TempStr1, 256, GetVal, TempLon1, TempLon2, TempStr2, 256)
If GetVal = 0 Then
tmpVol = ""
Else
tmpVol = Hex(GetVal)
tmpVol = String(8 - Len(tmpVol), "0") & tmpVol
tmpVol = Left(tmpVol, 4) & "-" & Right(tmpVol, 4)
End If
GetDiskVolume = tmpVol
End Function
'取得硬盘信息:型号/物理系列号(唯一)
Function GetHardDiskInfo(Optional ByVal numDisk As eumDiskNo = hdPrimaryMaster, Optional ByVal numType As eumInfoType = hdOnlySN) As String
If GetDiskInfo(numDisk) = 1 Then
Dim pSerialNumber As String, pModelNumber As String
pSerialNumber = StrConv(m_DiskInfo.sSerialNumber, vbUnicode)
pModelNumber = StrConv(m_DiskInfo.sModelNumber, vbUnicode)
Select Case numType
Case hdOnlyModel '仅型号
GetHardDiskInfo = Trim(pModelNumber)
Case hdOnlySN '仅系列号
GetHardDiskInfo = Trim(pSerialNumber)
Case Else '型号,系列号
GetHardDiskInfo = Trim(pModelNumber) & "," & Trim(pSerialNumber)
End Select
End If
End Function
Private Function OpenSMART(ByVal nDrive As Byte) As Long
Dim hSMARTIOCTL As Long
Dim hd As String
Dim VersionInfo As OSVERSIONINFO
hSMARTIOCTL = INVALID_HANDLE_VALUE
VersionInfo.dwOSVersionInfoSize = Len(VersionInfo)
GetVersionEx VersionInfo
Select Case VersionInfo.dwPlatformId
Case VER_PLATFORM_WIN32s
OpenSMART = hSMARTIOCTL
Case VER_PLATFORM_WIN32_WINDOWS
hSMARTIOCTL = CreateFile("//./SMARTVSD", 0, 0, 0, CREATE_NEW, 0, 0)
Case VER_PLATFORM_WIN32_NT
If nDrive < MAX_IDE_DRIVES Then
hd = "//./PhysicalDrive" & nDrive
hSMARTIOCTL = CreateFile(hd, GENERIC_READ Or GENERIC_WRITE, FILE_SHARE_READ Or FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0)
End If
End Select
OpenSMART = hSMARTIOCTL
End Function
Private Function DoIDENTIFY(ByVal hSMARTIOCTL As Long, pSCIP As SENDCMDINPARAMS, pSCOP() As Byte, ByVal bIDCmd As Byte, ByVal bDriveNum As Byte, lpcbBytesReturned As Long) As Boolean
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
pSCIP.irDriveRegs.bDriveHeadReg = &HA0 Or ((bDriveNum And 1) * 2 ^ 4)
'
pSCIP.irDriveRegs.bCommandReg = bIDCmd
pSCIP.bDriveNumber = bDriveNum
pSCIP.cBufferSize = IDENTIFY_BUFFER_SIZE
DoIDENTIFY = CBool(DeviceIoControl(hSMARTIOCTL, DFP_RECEIVE_DRIVE_DATA, _
pSCIP, 32, _
pSCOP(0), 528, _
lpcbBytesReturned, 0))
End Function
Private Function DoEnableSMART(ByVal hSMARTIOCTL As Long, pSCIP As SENDCMDINPARAMS, pSCOP As SENDCMDOUTPARAMS, ByVal bDriveNum As Byte, lpcbBytesReturned As Long) As Boolean
pSCIP.cBufferSize = 0
pSCIP.irDriveRegs.bFeaturesReg = SMART_ENABLE_SMART_OPERATIONS
pSCIP.irDriveRegs.bSectorCountReg = 1
pSCIP.irDriveRegs.bSectorNumberReg = 1
pSCIP.irDriveRegs.bCylLowReg = SMART_CYL_LOW
pSCIP.irDriveRegs.bCylHighReg = SMART_CYL_HI
pSCIP.irDriveRegs.bDriveHeadReg = &HA0 Or ((bDriveNum And 1) * 2 ^ 4)
pSCIP.irDriveRegs.bCommandReg = IDE_EXECUTE_SMART_FUNCTION
pSCIP.bDriveNumber = bDriveNum
DoEnableSMART = CBool(DeviceIoControl(hSMARTIOCTL, DFP_SEND_DRIVE_COMMAND, _
pSCIP, LenB(pSCIP) - 1, _
pSCOP, LenB(pSCOP) - 1, _
lpcbBytesReturned, 0))
End Function
'---------------------------------------------------------------------
'---------------------------------------------------------------------
Private Sub ChangeByteOrder(szString() As Byte, ByVal uscStrSize As Integer)
Dim i As Integer
Dim bTemp As Byte
For i = 0 To uscStrSize - 1 Step 2
bTemp = szString(i)
szString(i) = szString(i + 1)
szString(i + 1) = bTemp
Next i
End Sub
Private Sub DisplayIdInfo(pids As IDSECTOR, pSCIP As SENDCMDINPARAMS, ByVal bIDCmd As Byte, ByVal bDfpDriveMap As Byte, ByVal bDriveNum As Byte)
ChangeByteOrder pids.sModelNumber, UBound(pids.sModelNumber) + 1
ChangeByteOrder pids.sFirmwareRev, UBound(pids.sFirmwareRev) + 1
ChangeByteOrder pids.sSerialNumber, UBound(pids.sSerialNumber) + 1
End Sub
Public Function GetDiskInfo(ByVal nDrive As Byte) As Long
Dim hSMARTIOCTL As Long
Dim cbBytesReturned As Long
Dim VersionParams As GETVERSIONOUTPARAMS
Dim scip As SENDCMDINPARAMS
Dim scop() As Byte
Dim OutCmd As SENDCMDOUTPARAMS
Dim bDfpDriveMap As Byte
Dim bIDCmd As Byte ' IDE or ATAPI IDENTIFY cmd
Dim uDisk As IDSECTOR
m_DiskInfo = uDisk
hSMARTIOCTL = OpenSMART(nDrive)
If hSMARTIOCTL <> INVALID_HANDLE_VALUE Then
Call DeviceIoControl(hSMARTIOCTL, DFP_GET_VERSION, ByVal 0, 0, VersionParams, Len(VersionParams), cbBytesReturned, 0)
If Not (VersionParams.bIDEDeviceMap / 2 ^ nDrive And &H10) Then
If DoEnableSMART(hSMARTIOCTL, scip, OutCmd, nDrive, cbBytesReturned) Then
bDfpDriveMap = bDfpDriveMap Or 2 ^ nDrive
End If
End If
bIDCmd = IIf((VersionParams.bIDEDeviceMap / 2 ^ nDrive And &H10), IDE_ATAPI_ID, IDE_ID_FUNCTION)
ReDim scop(LenB(OutCmd) + IDENTIFY_BUFFER_SIZE - 1) As Byte
If DoIDENTIFY(hSMARTIOCTL, scip, scop, bIDCmd, nDrive, cbBytesReturned) Then
CopyMemory m_DiskInfo, scop(LenB(OutCmd) - 4), LenB(m_DiskInfo)
Call DisplayIdInfo(m_DiskInfo, scip, bIDCmd, bDfpDriveMap, nDrive)
CloseHandle hSMARTIOCTL
GetDiskInfo = 1
Exit Function '>---> Bottom
End If
CloseHandle hSMARTIOCTL
GetDiskInfo = 0
Else 'NOT HSMARTIOCTL...
GetDiskInfo = -1
End If
End Function
Private Sub Command1_Click()
result = MsgBox("你喜欢蓝色吗?", 3, "选择一个选项")
GetDiskInfo (0)
End Sub