标识机器唯一的方法讨论及得到硬盘序列号的方法.

一般来说标识机器的唯一ID会使用MAC地址.这个信息也很好取到.但考虑到目前的机器使用无线上网的很多,网卡的变动相对较为频繁.所以不太想用MAC地址作为标识.于是探索是否有其他唯一的标识可以使用.

首先考虑到硬盘,因为如果系统是安装在硬盘上的,如果系统换了,那应该可以视为另外一台机器.

那么硬盘中什么信息是唯一的呢.搜索到有人使用卷标.硬盘的卷标只有在格式化或分区的时候会被改变,而且重复的概率很低.那么使用此参数应该是可以.

于是啪啪啪的就实现了.结果发现公司好几台机器的系统盘卷标是一样的.大概使用同一镜像盘安装的系统卷标都是相同的.于是此方法作废.

那么硬盘是否有唯一标识呢?.

硬盘分为 ATA 和 SCSI 两种接口模式.当然,普通用户使用的 IDE 或 SATA 都是 ATA 的子集.

在ATA标准中有个序列号可以使用.在ATA7后似乎还有个world wide number.但由于没有细查并且低版本的硬盘不支持,所以还是选择序列号作为唯一标识.

ATA 和 SCSI 需要用不同的方式获得其信息.

原理介绍到这.代码如下.

[cpp] view plaincopy

  1. #include <Windows.h>  
  2. // IOCTL控制码 //   
  3. #define DFP_SEND_DRIVE_COMMAND CTL_CODE(IOCTL_DISK_BASE, 0x0021, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) //   
  4. #define DFP_RECEIVE_DRIVE_DATA CTL_CODE(IOCTL_DISK_BASE, 0x0022, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)   
  5. #define FILE_DEVICE_SCSI 0x0000001B   
  6. #define IOCTL_SCSI_MINIPORT_IDENTIFY ((FILE_DEVICE_SCSI << 16) + 0x0501)   
  7. #define IOCTL_SCSI_MINIPORT 0x0004D008   
  8. // see NTDDSCSI.H for definition   
  9. // ATA/ATAPI指令   
  10. #define IDE_ATA_IDENTIFY 0xEC   
  11.   
  12. typedef struct _IDINFO   
  13. {  
  14.     USHORT wGenConfig; // WORD 0: 基本信息字   
  15.     USHORT wNumCyls; // WORD 1: 柱面数   
  16.     USHORT wReserved2; // WORD 2: 保留   
  17.     USHORT wNumHeads; // WORD 3: 磁头数   
  18.     USHORT wReserved4; // WORD 4: 保留   
  19.     USHORT wReserved5; // WORD 5: 保留   
  20.     USHORT wNumSectorsPerTrack; // WORD 6: 每磁道扇区数   
  21.     USHORT wVendorUnique[3]; // WORD 7-9: 厂家设定值   
  22.     CHAR sSerialNumber[20]; // WORD 10-19:序列号   
  23.     USHORT wBufferType; // WORD 20: 缓冲类型   
  24.     USHORT wBufferSize; // WORD 21: 缓冲大小   
  25.     USHORT wECCSize; // WORD 22: ECC校验大小   
  26.     CHAR sFirmwareRev[8]; // WORD 23-26: 固件版本   
  27.     CHAR sModelNumber[40]; // WORD 27-46: 内部型号   
  28.     USHORT wMoreVendorUnique; // WORD 47: 厂家设定值   
  29.     USHORT wReserved48; // WORD 48: 保留   
  30.     struct { USHORT reserved1:8; USHORT DMA:1; // 1=支持DMA   
  31.     USHORT LBA:1; // 1=支持LBA   
  32.     USHORT DisIORDY:1; // 1=可不使用IORDY   
  33.     USHORT IORDY:1; // 1=支持IORDY   
  34.     USHORT SoftReset:1; // 1=需要ATA软启动   
  35.     USHORT Overlap:1; // 1=支持重叠操作   
  36.     USHORT Queue:1; // 1=支持命令队列   
  37.     USHORT InlDMA:1; // 1=支持交叉存取DMA   
  38.     } wCapabilities;   
  39.     // WORD 49: 一般能力   
  40.     USHORT wReserved1; // WORD 50: 保留   
  41.     USHORT wPIOTiming; // WORD 51: PIO时序  
  42.     USHORT wDMATiming; // WORD 52: DMA时序   
  43.     struct {   
  44.         USHORT CHSNumber:1; // 1=WORD 54-58有效   
  45.         USHORT CycleNumber:1; // 1=WORD 64-70有效   
  46.         USHORT UnltraDMA:1; // 1=WORD 88有效   
  47.         USHORT reserved:13;   
  48.     } wFieldValidity; // WORD 53: 后续字段有效性标志   
  49.     USHORT wNumCurCyls; // WORD 54: CHS可寻址的柱面数   
  50.     USHORT wNumCurHeads; // WORD 55: CHS可寻址的磁头数   
  51.     USHORT wNumCurSectorsPerTrack; // WORD 56: CHS可寻址每磁道扇区数   
  52.     USHORT wCurSectorsLow; // WORD 57: CHS可寻址的扇区数低位字   
  53.     USHORT wCurSectorsHigh; // WORD 58: CHS可寻址的扇区数高位字   
  54.     struct {   
  55.         USHORT CurNumber:8; // 当前一次性可读写扇区数   
  56.         USHORT Multi:1; // 1=已选择多扇区读写   
  57.         USHORT reserved1:7;   
  58.     } wMultSectorStuff;   
  59.   
  60.     // WORD 59: 多扇区读写设定   
  61.     ULONG dwTotalSectors; // WORD 60-61: LBA可寻址的扇区数   
  62.     USHORT wSingleWordDMA; // WORD 62: 单字节DMA支持能力   
  63.   
  64.     struct {   
  65.         USHORT Mode0:1; // 1=支持模式0 (4.17Mb/s)   
  66.         USHORT Mode1:1; // 1=支持模式1 (13.3Mb/s)   
  67.         USHORT Mode2:1; // 1=支持模式2 (16.7Mb/s)   
  68.         USHORT Reserved1:5; USHORT Mode0Sel:1; // 1=已选择模式0   
  69.         USHORT Mode1Sel:1; // 1=已选择模式1   
  70.         USHORT Mode2Sel:1; // 1=已选择模式2   
  71.         USHORT Reserved2:5;   
  72.     } wMultiWordDMA; // WORD 63: 多字节DMA支持能力   
  73.   
  74.     struct {   
  75.         USHORT AdvPOIModes:8; // 支持高级POI模式数   
  76.         USHORT reserved:8;   
  77.     } wPIOCapacity; // WORD 64: 高级PIO支持能力   
  78.   
  79.     USHORT wMinMultiWordDMACycle; // WORD 65: 多字节DMA传输周期的最小值   
  80.   
  81.     USHORT wRecMultiWordDMACycle; // WORD 66: 多字节DMA传输周期的建议值   
  82.     USHORT wMinPIONoFlowCycle; // WORD 67: 无流控制时PIO传输周期的最小值   
  83.     USHORT wMinPOIFlowCycle; // WORD 68: 有流控制时PIO传输周期的最小值   
  84.     USHORT wReserved69[11]; // WORD 69-79: 保留   
  85.   
  86.     struct   
  87.     {   
  88.         USHORT Reserved1:1;   
  89.         USHORT ATA1:1; // 1=支持ATA-1   
  90.         USHORT ATA2:1; // 1=支持ATA-2   
  91.         USHORT ATA3:1; // 1=支持ATA-3   
  92.         USHORT ATA4:1; // 1=支持ATA/ATAPI-4   
  93.         USHORT ATA5:1; // 1=支持ATA/ATAPI-5   
  94.         USHORT ATA6:1; // 1=支持ATA/ATAPI-6   
  95.         USHORT ATA7:1; // 1=支持ATA/ATAPI-7   
  96.         USHORT ATA8:1; // 1=支持ATA/ATAPI-8   
  97.         USHORT ATA9:1; // 1=支持ATA/ATAPI-9   
  98.         USHORT ATA10:1; // 1=支持ATA/ATAPI-10   
  99.         USHORT ATA11:1; // 1=支持ATA/ATAPI-11   
  100.         USHORT ATA12:1; // 1=支持ATA/ATAPI-12   
  101.         USHORT ATA13:1; // 1=支持ATA/ATAPI-13   
  102.         USHORT ATA14:1; // 1=支持ATA/ATAPI-14   
  103.         USHORT Reserved2:1;   
  104.     } wMajorVersion; // WORD 80: 主版本   
  105.   
  106.     USHORT wMinorVersion; // WORD 81: 副版本   
  107.     USHORT wReserved82[6]; // WORD 82-87: 保留   
  108.   
  109.     struct   
  110.     {   
  111.         USHORT Mode0:1; // 1=支持模式0 (16.7Mb/s)   
  112.         USHORT Mode1:1; // 1=支持模式1 (25Mb/s)   
  113.         USHORT Mode2:1; // 1=支持模式2 (33Mb/s)   
  114.         USHORT Mode3:1; // 1=支持模式3 (44Mb/s)   
  115.         USHORT Mode4:1; // 1=支持模式4 (66Mb/s)   
  116.         USHORT Mode5:1; // 1=支持模式5 (100Mb/s)   
  117.         USHORT Mode6:1; // 1=支持模式6 (133Mb/s)   
  118.         USHORT Mode7:1; // 1=支持模式7 (166Mb/s) ???   
  119.         USHORT Mode0Sel:1; // 1=已选择模式0   
  120.         USHORT Mode1Sel:1; // 1=已选择模式1   
  121.         USHORT Mode2Sel:1; // 1=已选择模式2   
  122.         USHORT Mode3Sel:1; // 1=已选择模式3   
  123.         USHORT Mode4Sel:1; // 1=已选择模式4   
  124.         USHORT Mode5Sel:1; // 1=已选择模式5   
  125.         USHORT Mode6Sel:1; // 1=已选择模式6   
  126.         USHORT Mode7Sel:1; // 1=已选择模式7   
  127.     } wUltraDMA;   
  128.   
  129.     // WORD 88: Ultra DMA支持能力   
  130.     USHORT wReserved89[167]; // WORD 89-255   
  131. } IDINFO, *PIDINFO;  
  132.   
  133. // SCSI驱动所需的输入输出共用的结构   
  134.   
  135. typedef struct _SRB_IO_CONTROL   
  136. {   
  137.     ULONG HeaderLength; // 头长度   
  138.     UCHAR Signature[8]; // 特征名称  
  139.     ULONG Timeout; // 超时时间   
  140.     ULONG ControlCode; // 控制码   
  141.     ULONG ReturnCode; // 返回码   
  142.     ULONG Length; // 缓冲区长度   
  143.   
  144. } SRB_IO_CONTROL, *PSRB_IO_CONTROL;   
  145.   
  146.   
  147. // 打开设备 // filename: 设备的“文件名”(设备路径)   
  148. HANDLE OpenDevice(LPCTSTR filename)   
  149. {   
  150.     HANDLE hDevice; // 打开设备   
  151.     hDevice = ::CreateFile(filename, // 文件名   
  152.         GENERIC_READ | GENERIC_WRITE, // 读写方式  
  153.         FILE_SHARE_READ | FILE_SHARE_WRITE, // 共享方式   
  154.         NULL, // 默认的安全描述符   
  155.         OPEN_EXISTING, // 创建方式   
  156.         0, // 不需设置文件属性   
  157.         NULL);  // 不需参照模板文件   
  158.     return hDevice;  
  159. }  
  160. // 向驱动发“IDENTIFY DEVICE”命令,获得设备信息 // hDevice: 设备句柄 // pIdInfo: 设备信息结构指针   
  161. BOOL IdentifyDevice(HANDLE hDevice, PIDINFO pIdInfo)   
  162. {   
  163.     PSENDCMDINPARAMS pSCIP; // 输入数据结构指针   
  164.     PSENDCMDOUTPARAMS pSCOP; // 输出数据结构指针   
  165.     DWORD dwOutBytes; // IOCTL输出数据长度   
  166.     BOOL bResult; // IOCTL返回值 // 申请输入/输出数据结构空间   
  167.     pSCIP = (PSENDCMDINPARAMS)::GlobalAlloc(LMEM_ZEROINIT, sizeof(SENDCMDINPARAMS) – 1);   
  168.     pSCOP = (PSENDCMDOUTPARAMS)::GlobalAlloc(LMEM_ZEROINIT, sizeof(SENDCMDOUTPARAMS) + sizeof(IDINFO) – 1); // 指定ATA/ATAPI命令的寄存器值 //   
  169.     pSCIP->irDriveRegs.bFeaturesReg = 0; //   
  170.     pSCIP->irDriveRegs.bSectorCountReg = 0; //   
  171.     pSCIP->irDriveRegs.bSectorNumberReg = 0; //   
  172.     pSCIP->irDriveRegs.bCylLowReg = 0; //   
  173.     pSCIP->irDriveRegs.bCylHighReg = 0; //   
  174.     pSCIP->irDriveRegs.bDriveHeadReg = 0; pSCIP->irDriveRegs.bCommandReg = IDE_ATA_IDENTIFY; // 指定输入/输出数据缓冲区大小 IDENTIFY DEVICE  
  175.     pSCIP->cBufferSize = 0; pSCOP->cBufferSize = sizeof(IDINFO); //   
  176.     bResult = ::DeviceIoControl(hDevice, // 设备句柄   
  177.         DFP_RECEIVE_DRIVE_DATA, // 指定IOCTL   
  178.         pSCIP, sizeof(SENDCMDINPARAMS) – 1, // 输入数据缓冲区   
  179.         pSCOP, sizeof(SENDCMDOUTPARAMS) + sizeof(IDINFO) – 1, // 输出数据缓冲区   
  180.         &dwOutBytes, // 输出数据长度   
  181.         (LPOVERLAPPED)NULL); // 用同步I/O // 复制设备参数结构   
  182.     ::memcpy(pIdInfo, pSCOP->bBuffer, sizeof(IDINFO)); // 释放输入/输出数据空间   
  183.     ::GlobalFree(pSCOP); ::GlobalFree(pSCIP); return bResult;   
  184. }   
  185.   
  186. // 向SCSI MINI-PORT驱动发“IDENTIFY DEVICE”命令,获得设备信息 // hDevice: 设备句柄 // pIdInfo: 设备信息结构指针   
  187. BOOL IdentifyDeviceAsScsi(HANDLE hDevice, int nDrive, PIDINFO pIdInfo)   
  188. {   
  189.     PSENDCMDINPARAMS pSCIP; // 输入数据结构指针   
  190.     PSENDCMDOUTPARAMS pSCOP; // 输出数据结构指针   
  191.     PSRB_IO_CONTROL pSRBIO; // SCSI输入输出数据结构指针   
  192.     DWORD dwOutBytes; // IOCTL输出数据长度   
  193.     BOOL bResult; // IOCTL返回值   
  194.     // 申请输入/输出数据结构空间   
  195.     pSRBIO = (PSRB_IO_CONTROL)::GlobalAlloc(LMEM_ZEROINIT, sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDOUTPARAMS) + sizeof(IDINFO) – 1);   
  196.     pSCIP = (PSENDCMDINPARAMS)((char *)pSRBIO + sizeof(SRB_IO_CONTROL));   
  197.     pSCOP = (PSENDCMDOUTPARAMS)((char *)pSRBIO + sizeof(SRB_IO_CONTROL));   
  198.     // 填充输入/输出数据   
  199.     pSRBIO->HeaderLength = sizeof(SRB_IO_CONTROL); pSRBIO->Timeout = 10000;  
  200.     pSRBIO->Length = sizeof(SENDCMDOUTPARAMS) + sizeof(IDINFO) – 1;   
  201.     pSRBIO->ControlCode = IOCTL_SCSI_MINIPORT_IDENTIFY; ::strncpy ((char *)pSRBIO->Signature, “SCSIDISK”, 8);   
  202.     // 指定ATA/ATAPI命令的寄存器值 //   
  203.     pSCIP->irDriveRegs.bFeaturesReg = 0; //   
  204.     pSCIP->irDriveRegs.bSectorCountReg = 0; //   
  205.     pSCIP->irDriveRegs.bSectorNumberReg = 0; //   
  206.     pSCIP->irDriveRegs.bCylLowReg = 0; //   
  207.     pSCIP->irDriveRegs.bCylHighReg = 0; //   
  208.     pSCIP->irDriveRegs.bDriveHeadReg = 0; pSCIP->irDriveRegs.bCommandReg = IDE_ATA_IDENTIFY; pSCIP->bDriveNumber = nDrive;   
  209.     //IDENTIFY DEVICE    
  210.     bResult = ::DeviceIoControl(hDevice, // 设备句柄   
  211.         IOCTL_SCSI_MINIPORT, // 指定IOCTL   
  212.         pSRBIO, sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) – 1, // 输入数据缓冲区   
  213.         pSRBIO, sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDOUTPARAMS) + sizeof(IDINFO) – 1, // 输出数据缓冲区   
  214.         &dwOutBytes, // 输出数据长度   
  215.         (LPOVERLAPPED)NULL); // 用同步I/O // 复制设备参数结构   
  216.     ::memcpy(pIdInfo, pSCOP->bBuffer, sizeof(IDINFO));   
  217.     // 释放输入/输出数据空间   
  218.     ::GlobalFree(pSRBIO); return bResult;   
  219. }   
  220. // 将串中的字符两两颠倒 // 原因是ATA/ATAPI中的WORD,与Windows采用的字节顺序相反 // 驱动程序中已经将收到的数据全部反过来,我们来个负负得正   
  221. void AdjustString(char* str, int len)   
  222. {   
  223.     char ch; int i;   
  224.     // 两两颠倒   
  225.     for (i = 0; i < len; i += 2)   
  226.     {  
  227.         ch = str[i];   
  228.         str[i] = str[i + 1];   
  229.         str[i + 1] = ch;   
  230.     }  
  231.   
  232.     // 若是右对齐的,调整为左对齐 (去掉左边的空格)   
  233.     i = 0;   
  234.     while ((i < len) && (str[i] == ‘ ‘))   
  235.         i++;   
  236.   
  237.     char    szBuff[MAX_PATH];  
  238.     ZeroMemory( szBuff, MAX_PATH );  
  239.     ::memmove(szBuff, &str[i], len – i);   
  240.     // 去掉右边的空格  
  241.     i = len – 1;   
  242.     while ((i >= 0) && (szBuff[i] == ‘ ‘))   
  243.     {   
  244.         szBuff[i] = ‘\0’;   
  245.         i–;   
  246.     }   
  247.   
  248.     ZeroMemory( str, len );  
  249.     memcpy( str, szBuff, len );  
  250.       
  251.   
  252. }   
  253. // 读取IDE硬盘的设备信息,必须有足够权限 // nDrive: 驱动器号(0=第一个硬盘,1=0=第二个硬盘,……) // pIdInfo: 设备信息结构指针   
  254. BOOL GetPhysicalDriveInfoInNT(int nDrive, PIDINFO pIdInfo)   
  255. {   
  256.     HANDLE hDevice; // 设备句柄   
  257.     BOOL bResult; // 返回结果   
  258.     char szFileName[20]; // 文件名   
  259.     ::sprintf(szFileName,“\\\\.\\PhysicalDrive%d”, nDrive);   
  260.     hDevice = ::OpenDevice(szFileName); if (hDevice == INVALID_HANDLE_VALUE) { return FALSE; }   
  261.     //IDENTIFY DEVICE    
  262.     bResult = ::IdentifyDevice(hDevice, pIdInfo); if (bResult)   
  263.     {   
  264.         // 调整字符串   
  265.         ::AdjustString(pIdInfo->sSerialNumber, 20);   
  266.         ::AdjustString(pIdInfo->sModelNumber, 40);   
  267.         ::AdjustString(pIdInfo->sFirmwareRev, 8);   
  268.     }   
  269.     ::CloseHandle (hDevice); return bResult;   
  270. }   
  271. // 用SCSI驱动读取IDE硬盘的设备信息,不受权限制约 // nDrive: 驱动器号(0=Primary Master, 1=Promary Slave, 2=Secondary master, 3=Secondary slave) // pIdInfo: 设备信息结构指针   
  272. BOOL GetIdeDriveAsScsiInfoInNT(int nDrive, PIDINFO pIdInfo)   
  273. {   
  274.     HANDLE hDevice; // 设备句柄   
  275.     BOOL bResult; // 返回结果   
  276.     char szFileName[20]; // 文件名   
  277.     ::sprintf(szFileName,“\\\\.\\Scsi%d:”, nDrive/2);   
  278.     hDevice = ::OpenDevice(szFileName); if (hDevice == INVALID_HANDLE_VALUE) { return FALSE; }   
  279.     // IDENTIFY DEVICE   
  280.     bResult = ::IdentifyDeviceAsScsi(hDevice, nDrive%2, pIdInfo); // 检查是不是空串   
  281.     if (pIdInfo->sModelNumber[0] == ‘\0’)   
  282.     {  
  283.         bResult = FALSE;   
  284.     }   
  285.     if (bResult)   
  286.     {   
  287.         // 调整字符串   
  288.         ::AdjustString(pIdInfo->sSerialNumber, 20);   
  289.         ::AdjustString(pIdInfo->sModelNumber, 40);   
  290.         ::AdjustString(pIdInfo->sFirmwareRev, 8);   
  291.     }   
  292.     return bResult;   
  293. }  
  294.   
  295. int main (int argc, char * argv [])  
  296. {  
  297.     char sz[255];  
  298.     IDINFO  kInfo;  
  299.     GetPhysicalDriveInfoInNT( 0, &kInfo );  
  300. }  

以上代码在win7上运行成功.其他系统未经试验.

来源URL:http://www.360doc.com/content/13/1206/08/9200790_334866433.shtml