参考了多种方案,最终觉得采用硬盘序列号来给软件加密。
求:
  1.取硬盘序列号的方法。网上很多,我不要那种垃圾代码,烦请回贴代码本人    测试过再发!    要求在win2000/98/95/xp等不同操作系统下均能使用;
      在奔2、奔3、奔4、AMD等主流CPU上均能使用;  2.好的加密算法  3.有好的控件也行,但只要不需要注册的那种!(有源码更好)

解决方案 »

  1.   

    function GetHDSerialNumber: LongInt;    //得到硬盘序列号
    {$IFDEF WIN32}
    var 
      pdw : pDWord; 
      mc, fl : dword; 
    {$ENDIF} 
    begin 
      {$IfDef WIN32} 
      New(pdw); 
      GetVolumeInformation('c:\',nil,0,pdw,mc,fl,nil,0); 
      Result := pdw^;
      dispose(pdw); 
      {$ELSE}
      Result := GetWinFlags;
      {$ENDIF} 
    end;
      

  2.   

    function GetHdID : String;
    //获取Ide硬盘序列号
    type
      TSrbIoControl = packed record
        HeaderLength : ULONG;
        Signature : Array[0..7] of Char;
        Timeout : ULONG;
        ControlCode : ULONG;
        ReturnCode : ULONG;
        Length : ULONG;
      end;
      SRB_IO_CONTROL = TSrbIoControl;
      PSrbIoControl = ^TSrbIoControl;
      TIDERegs = packed record
        bFeaturesReg : Byte;     // Used for specifying SMART "commands".
        bSectorCountReg : Byte;  // IDE sector count register
        bSectorNumberReg : Byte; // IDE sector number register
        bCylLowReg : Byte;       // IDE low order cylinder value
        bCylHighReg : Byte;      // IDE high order cylinder value
        bDriveHeadReg : Byte;    // IDE drive/head register
        bCommandReg : Byte;      // Actual IDE command.
        bReserved : Byte;        // reserved. Must be zero.
      end;
      IDEREGS = TIDERegs;
      PIDERegs = ^TIDERegs;
      TSendCmdInParams = packed record
        cBufferSize : DWORD;
        irDriveRegs : TIDERegs;
        bDriveNumber : Byte;
        bReserved : Array[0..2] of Byte;
        dwReserved : Array[0..3] of DWORD;
        bBuffer : Array[0..0] of Byte;
      end;
      SENDCMDINPARAMS = TSendCmdInParams;
      PSendCmdInParams = ^TSendCmdInParams;
      TIdSector = packed record
        wGenConfig : Word;
        wNumCyls : Word;
        wReserved : Word;
        wNumHeads : Word;
        wBytesPerTrack : Word;
        wBytesPerSector : Word;
        wSectorsPerTrack : Word;
        wVendorUnique : Array[0..2] of Word;
        sSerialNumber : Array[0..19] of Char; 
        wBufferType : Word;
        wBufferSize : Word; 
        wECCSize : Word; 
        sFirmwareRev : Array[0..7] of Char;
        sModelNumber : Array[0..39] of Char; 
        wMoreVendorUnique : Word; 
        wDoubleWordIO : Word; 
        wCapabilities : Word;
        wReserved1 : Word;
        wPIOTiming : Word; 
        wDMATiming : Word; 
        wBS : Word; 
        wNumCurrentCyls : Word;
        wNumCurrentHeads : Word;
        wNumCurrentSectorsPerTrack : Word;
        ulCurrentSectorCapacity : ULONG;
        wMultSectorStuff : Word;
        ulTotalAddressableSectors : ULONG;
        wSingleWordDMA : Word;
        wMultiWordDMA : Word;
        bReserved : Array[0..127] of Byte;
      end;
      PIdSector = ^TIdSector;
    const
      IDE_ID_FUNCTION = $EC;
      IDENTIFY_BUFFER_SIZE = 512;
      DFP_RECEIVE_DRIVE_DATA = $0007c088;
      IOCTL_SCSI_MINIPORT = $0004d008;
      IOCTL_SCSI_MINIPORT_IDENTIFY = $001b0501;
      DataSize = sizeof(TSendCmdInParams)-1+IDENTIFY_BUFFER_SIZE;
      BufferSize = SizeOf(SRB_IO_CONTROL)+DataSize;
      W9xBufferSize = IDENTIFY_BUFFER_SIZE+16;
    var 
      hDevice : THandle;
      cbBytesReturned : DWORD;
      pInData : PSendCmdInParams;
      pOutData : Pointer; // PSendCmdOutParams
      Buffer : Array[0..BufferSize-1] of Byte;
      srbControl : TSrbIoControl absolute Buffer;procedure ChangeByteOrder( var Data; Size : Integer ); 
    var
      ptr : PChar;
      i : Integer;
      c : Char;
    begin
      ptr := @Data;
      for i := 0 to (Size shr 1)-1 do
      begin
        c := ptr^;
        ptr^ := (ptr+1)^;
        (ptr+1)^ := c;
        Inc(ptr,2);
      end;
    end; begin 
      Result := '';
      FillChar(Buffer,BufferSize,#0);
      if Win32Platform=VER_PLATFORM_WIN32_NT then
      begin // Windows NT, Windows 2000
        // Get SCSI port handle
        hDevice := CreateFile( '\\.\Scsi0:',
        GENERIC_READ or GENERIC_WRITE,
        FILE_SHARE_READ or FILE_SHARE_WRITE,
        nil, OPEN_EXISTING, 0, 0 );
        if hDevice=INVALID_HANDLE_VALUE then Exit;
        try
          srbControl.HeaderLength := SizeOf(SRB_IO_CONTROL);
          System.Move('SCSIDISK',srbControl.Signature,8);
          srbControl.Timeout := 2;
          srbControl.Length := DataSize;
          srbControl.ControlCode := IOCTL_SCSI_MINIPORT_IDENTIFY;
          pInData := PSendCmdInParams(PChar(@Buffer)
          +SizeOf(SRB_IO_CONTROL));
          pOutData := pInData;
          with pInData^ do
          begin
            cBufferSize := IDENTIFY_BUFFER_SIZE;
            bDriveNumber := 0;
            with irDriveRegs do
            begin
              bFeaturesReg := 0;
              bSectorCountReg := 1;
              bSectorNumberReg := 1;
              bCylLowReg := 0;
              bCylHighReg := 0;
              bDriveHeadReg := $A0;
              bCommandReg := IDE_ID_FUNCTION;
            end;
          end;
          if not DeviceIoControl( hDevice, IOCTL_SCSI_MINIPORT,
          @Buffer, BufferSize, @Buffer, BufferSize,
          cbBytesReturned, nil ) then Exit;
        finally
          CloseHandle(hDevice);
        end;
      end else
      begin // Windows 95 OSR2, Windows 98
        hDevice := CreateFile( '\\.\SMARTVSD', 0, 0, nil,
        CREATE_NEW, 0, 0 );
        if hDevice=INVALID_HANDLE_VALUE then Exit;
        try
          pInData := PSendCmdInParams(@Buffer);
          pOutData := @pInData^.bBuffer;
          with pInData^ do
          begin
            cBufferSize := IDENTIFY_BUFFER_SIZE;
            bDriveNumber := 0;
            with irDriveRegs do
            begin
              bFeaturesReg := 0;
              bSectorCountReg := 1;
              bSectorNumberReg := 1;
              bCylLowReg := 0;
              bCylHighReg := 0;
              bDriveHeadReg := $A0;
              bCommandReg := IDE_ID_FUNCTION;
            end;
          end;
          if not DeviceIoControl( hDevice, DFP_RECEIVE_DRIVE_DATA,
          pInData, SizeOf(TSendCmdInParams)-1, pOutData,
          W9xBufferSize, cbBytesReturned, nil ) then Exit;
        finally
          CloseHandle(hDevice);
        end;
      end;
      with PIdSector(PChar(pOutData)+16)^ do
      begin
        ChangeByteOrder(sSerialNumber,SizeOf(sSerialNumber));
        SetString(Result,sSerialNumber,SizeOf(sSerialNumber));
      end;
    end;
      

  3.   

    又用的卷标好,Faint写驱动没这么简单吧,而且要Vxd, Wmd都精通...
      

  4.   

    yesxwl(吴宫幽径) :谢谢你。不过你的这个版本的代码我在网上找了很多。至少在98下是不行的。
    我跟了一下源码,98下主要有两个问题:
    1.if hDevice=INVALID_HANDLE_VALUE then Exit; 程序到此即退出;
    2.如果屏蔽上面代码,程序到此退出:
      if not DeviceIoControl( hDevice, DFP_RECEIVE_DRIVE_DATA,
          pInData, SizeOf(TSendCmdInParams)-1, pOutData,
          W9xBufferSize, cbBytesReturned, nil ) then Exit;所以返回值为空。你有那么多三角裤,搞定这个问题当然小菜一碟了,烦请帮我一把。郁闷好几天了。谢谢!
    如有好的加密算法顺便给我一个。
      

  5.   

    不太适用,首先你不知道是哪一个IDE, 是Master还是Slave另外某些磁盘读不出...用BiosID吧,简单一点。
      

  6.   

    hDevice := CreateFile( '\\.\SMARTVSD', 0, 0, nil,
        CREATE_NEW, 0, 0 );
    改为
    hDevice := CreateFile( '\\.\SMARTVSD', 0, 0, nil,
        CREATE_NEW, 0, nil );win95下必须如是如果失败用GetLastError 看看究竟创建时出了什么错,我用的是2000不好帮你测试
      

  7.   

    hDevice := CreateFile( '\\.\SMARTVSD', 0, 0, nil,
        CREATE_NEW, 0, nil );win95下必须如是编译都通不过!
      

  8.   

    yesxwl(吴宫幽径) 的方法是正确的,98/nt/2000/xp下都可以用
    只是在98下面用,需要将c:\windows\system\smartvsd.vxd拷贝到
    c:\windows\system\iosubsys目录下
    我现在用的就是这种加密算法
      

  9.   

    以上方法好像只能取主盘的ID,从盘的ID没法取,另有些硬盘根本没有ID号。
      

  10.   

    // WinNT/Win2000 - 你必须拥有对硬盘的读/写访问权限// Win98 
    // SMARTVSD.VXD 必须安装到 \windows\system\iosubsys 
    // (不要忘记在复制后重新启动系统) 
    //获取第一个IDE硬盘的序列号 
    function GetIdeSerialNumber : SerialNumber; 
    const IDENTIFY_BUFFER_SIZE = 512; 
    type 
    TIDERegs = packed record 
    bFeaturesReg : BYTE; // Used for specifying SMART "commands". 
    bSectorCountReg : BYTE; // IDE sector count register 
    bSectorNumberReg : BYTE; // IDE sector number register 
    bCylLowReg : BYTE; // IDE low order cylinder value 
    bCylHighReg : BYTE; // IDE high order cylinder value 
    bDriveHeadReg : BYTE; // IDE drive/head register 
    bCommandReg : BYTE; // Actual IDE command. 
    bReserved : BYTE; // reserved for future use. Must be zero. 
    end; 
    TSendCmdInParams = packed record 
    // Buffer size in bytes 
    cBufferSize : DWORD; 
    // Structure with drive register values. 
    irDriveRegs : TIDERegs; 
    // Physical drive number to send command to (0,1,2,3). 
    bDriveNumber : BYTE; 
    bReserved : Array[0..2] of Byte; 
    dwReserved : Array[0..3] of DWORD; 
    bBuffer : Array[0..0] of Byte; // Input buffer. 
    end; 
    TIdSector = packed record 
    wGenConfig : Word; 
    wNumCyls : Word; 
    wReserved : Word; 
    wNumHeads : Word; 
    wBytesPerTrack : Word; 
    wBytesPerSector : Word; 
    wSectorsPerTrack : Word; 
    wVendorUnique : Array[0..2] of Word; 
    sSerialNumber : Array[0..19] of CHAR; 
    wBufferType : Word; 
    wBufferSize : Word; 
    wECCSize : Word; 
    sFirmwareRev : Array[0..7] of Char; 
    sModelNumber : Array[0..39] of Char; 
    wMoreVendorUnique : Word; 
    wDoubleWordIO : Word; 
    wCapabilities : Word; 
    wReserved1 : Word; 
    wPIOTiming : Word; 
    wDMATiming : Word; 
    wBS : Word; 
    wNumCurrentCyls : Word; 
    wNumCurrentHeads : Word; 
    wNumCurrentSectorsPerTrack : Word; 
    ulCurrentSectorCapacity : DWORD; 
    wMultSectorStuff : Word; 
    ulTotalAddressableSectors : DWORD; 
    wSingleWordDMA : Word; 
    wMultiWordDMA : Word; 
    bReserved : Array[0..127] of BYTE; 
    end; 
    PIdSector = ^TIdSector; 
    TDriverStatus = packed record 
    // 驱动器返回的错误代码,无错则返回0
    bDriverError : Byte; 
    // IDE出错寄存器的内容,只有当bDriverError 为 SMART_IDE_ERROR 时有效
    bIDEStatus : Byte; 
    bReserved : Array[0..1] of Byte; 
    dwReserved : Array[0..1] of DWORD; 
    end; 
    TSendCmdOutParams = packed record 
    // bBuffer的大小
    cBufferSize : DWORD; 
    // 驱动器状态
    DriverStatus : TDriverStatus; 
    // 用于保存从驱动器读出的数据的缓冲区,实际长度由cBufferSize决定
    bBuffer : Array[0..0] of BYTE; 
    end; var hDevice : THandle; 
    cbBytesReturned : DWORD; 
    ptr : PChar; 
    SCIP : TSendCmdInParams; 
    aIdOutCmd : Array [0..(SizeOf(TSendCmdOutParams)+IDENTIFY_BUFFER_SIZE-1)-1] of Byte; 
    IdOutCmd : TSendCmdOutParams absolute aIdOutCmd; procedure ChangeByteOrder( var Data; Size : Integer ); 
    var ptr : PChar; 
    i : Integer; 
    c : Char; 
    begin 
    ptr := @Data; 
    for i := 0 to (Size shr 1)-1 do 
    begin 
    c := ptr^; 
    ptr^ := (ptr+1)^; 
    (ptr+1)^ := c; 
    Inc(ptr,2); 
    end; 
    end; begin 
    Result := ''; // 如果出错则返回空串
    if SysUtils.Win32Platform=VER_PLATFORM_WIN32_NT then // Windows NT, Windows 2000 
    begin 
    // 提示! 改变名称可适用于其它驱动器,如第二个驱动器: '\\.\PhysicalDrive1\'
    hDevice := CreateFile( '\\.\PhysicalDrive0', GENERIC_READ or GENERIC_WRITE, 
    FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0 ); 
    end 
    else // Version Windows 95 OSR2, Windows 98 
    hDevice := CreateFile( '\\.\SMARTVSD', 0, 0, nil, CREATE_NEW, 0, 0 ); 
    if hDevice=INVALID_HANDLE_VALUE then Exit; 
    try 
    FillChar(SCIP,SizeOf(TSendCmdInParams)-1,#0); 
    FillChar(aIdOutCmd,SizeOf(aIdOutCmd),#0); 
    cbBytesReturned := 0; 
    // Set up data structures for IDENTIFY command. 
    with SCIP do 
    begin 
    cBufferSize := IDENTIFY_BUFFER_SIZE; 
    // bDriveNumber := 0; 
    with irDriveRegs do 
    begin 
    bSectorCountReg := 1; 
    bSectorNumberReg := 1; 
    // if Win32Platform=VER_PLATFORM_WIN32_NT then bDriveHeadReg := $A0 
    // else bDriveHeadReg := $A0 or ((bDriveNum and 1) shl 4); 
    bDriveHeadReg := $A0; 
    bCommandReg := $EC; 
    end; 
    end; 
    if not DeviceIoControl( hDevice, $0007c088, @SCIP, SizeOf(TSendCmdInParams)-1, 
    @aIdOutCmd, SizeOf(aIdOutCmd), cbBytesReturned, nil ) then Exit; 
    finally 
    CloseHandle(hDevice); 
    end; 
    with PIdSector(@IdOutCmd.bBuffer)^ do 
    begin 
    ChangeByteOrder( sSerialNumber, SizeOf(sSerialNumber) ); 
    (PChar(@sSerialNumber)+SizeOf(sSerialNumber))^ := #0; 
    Result := PChar(@sSerialNumber); 
    end; 
    end; 
      

  11.   

    我没碰到你说的问题,IBM,迈拓,昆腾,西捷的硬盘我都用过,没你说的问题
      

  12.   

    或者你可以采用Onguard控件,我试过,只要是WIN操作系统,无论是否安装硬盘驱动,硬盘是否有ID号,这个控件取出的机器码都是不同的,而且这个控件已经公开了源码。
      

  13.   

    或者楼上的吧硬盘安在从盘的位置试试能否读出来。
    www.playicq.com有。
      

  14.   

    我也试过很多读硬盘ID的代码,目前还没有发现一个比效不错的。Onguard控件虽然可以取唯一的机器码,但不同的操作系统在同一台机器上的机器码也是不一样的。目前还没有更好的方法。
    to cnssk(小柯) :
    上面的方法我也试过,我还试用用C++写的DLL读硬盘ID的东东,同样不能读从盘的ID。
      

  15.   

    在98下读从盘可用以下代码:
    function  inp(rdx:word) : byte;
    asm
        mov dx, rdx
        in al, dx
    end;function inpw(rdx: word) : word;
    asm
        mov dx, rdx
        in  ax, dx
    end;procedure outp(ral : byte; rdx : word);
    asm
        mov dx, rdx
        mov al, ral
        out dx, al
    end;function WaitIde:byte;
    var
      al:byte;
    begin
      repeat
        al:=inp(IdeBase+7);
      until (al<$80) or (al=$a0); //$a0可能就没有硬盘
      WaitIde := al;
    end;procedure ReadIDE;
    var
      al : byte;
      i : integer;
    begin
      WaitIde;
      outp(SelectDisk,IdeBase+6);
      al := WaitIde;
      if ((al and $50) <>$50) then
      begin
        ErrNo:=1;
        exit;
      end;  outp(SelectDisk,IdeBase+6);
      outp($EC,IdeBase+7);
      al := WaitIde;
      if ((al and $58)<>$58) then
      begin
        ErrNo:=2;
        exit;
      end;  for i:=0 to 255 do
      begin
        pw[i] := inpw(IdeBase);
      end;
    end;// 新的中断处理程序
    procedure ReadIt; assembler;
    asm
        push eax
        push ebx
        push ecx
        push edx
        push esi
        push edi    // 在这里写读程序
        call ReadIDE    pop edi
        pop esi
        pop edx
        pop ecx
        pop ebx
        pop eax
        iretd
    end;procedure GetSerialNo; assembler;
    begin
      asm
        push eax    // 获取修改的中断的中断描述符(中断门)地址
        sidt idtr_1
        mov eax,dword ptr idtr_1+02h
        add eax,hookexceptionno*08h+04h    // 保存原先的中断入口地址
        cli
        push ecx
        mov ecx,dword ptr [eax]
        mov cx,word ptr [eax-04h]
        mov dword ptr oldexceptionhook,ecx
        pop ecx    // 设置修改的中断入口地址为新的中断处理程序入口地址
        push ebx
        lea ebx,ReadIt
        mov word ptr [eax-04h],bx
        shr ebx,10h
        mov word ptr [eax+02h],bx
        pop ebx    // 执行中断,转到ring 0(与cih 病毒原理相似!)
        push ebx
        int hookexceptionno
        pop ebx    // 恢复原先的中断入口地址
        push ecx
        mov ecx,dword ptr oldexceptionhook
        mov word ptr [eax-04h],cx
        shr ecx,10h
        mov word ptr [eax+02h],cx
        pop ecx    // 结束
        sti
        pop eax
        ret
      end;
    end;procedure GetPN(DriveNo: integer; var s:string);
    var
      i : integer;
    begin
    //  asm int 3 end;
      ErrNo:=0;
      fillchar(pw,sizeof(pw),0);
      s:='';  case DriveNo of       //设置基址
        0,1:IdeBase:= $1f0;
        2,3:IdeBase:= $170;
      end;
      case DriveNo of       //指定主从
        0,2:SelectDisk:=$A0;
        1,3:SelectDisk:=$B0;
      end;  GetSerialNo;
      if ErrNo<>0 then
        exit;       //读错误  if (pw[0]=0) then
        s := '没有序列号:('
      else
        for i:=10 to 19 do
        begin
          s := s+ char(pw[i] shr 8) + char(pw[i] and $ff);
        end;
    end;procedure TForm1.Button1Click(Sender: TObject);
    var
      s:string;
      i:integer;
    begin
       GetPN(0,Gs_HDID);
       If LowerCase(Trim(Gs_HDID))=LOwerCase(Trim(HDSERIAL)) Then Begin   End;
      for i:=0 to 3 do
      begin
        GetPN(i, s);
        if (s<>'') then
        case i of
          0:  Label1.Caption := 'IDE1 主盘' +s;
          1:  Label2.Caption := 'IDE1 从盘' +s;
          2:  Label3.Caption := 'IDE2 主盘' +s;
          3:  Label3.Caption := 'IDE3 从盘' +s;
        end;
      end;
    end;在nt/2000下读从盘可以
    if SysUtils.Win32Platform=VER_PLATFORM_WIN32_NT then // Windows NT, Windows 2000 
    begin 
    // 提示! 改变名称可适用于其它驱动器,如第二个驱动器: '\\.\PhysicalDrive1\'
    hDevice := CreateFile( '\\.\PhysicalDrive0', GENERIC_READ or GENERIC_WRITE, 
    FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0 ); 
      

  16.   

    to walkmangood(小小安) :
    同一批主板出厂时的序列号是相同的。所以取主板序列号没有意义。CPU ID号同一批也是相同的。也同样没有意义,只有硬盘的ID号是唯一的,但存在用户不安装硬盘驱动,或有些硬盘没有ID号的问题。所以最好的解决方法是把他们的号码都取出来再加上自己的算法做成一个唯一的机器码才行,还有一促方法是取网卡的MAC地址,这个地址是全球唯一的,不过这个地址用户可以更改。
      

  17.   

    上面第一段代码忘记定义变量了var
      pw : array [0..255] of WORD;//    pw[256];
      idtr_1 : array [0..5] of byte; //保存中断描述符表寄存器
      oldexceptionhook : dword;      //保存原先的中断入口地址
      IdeBase : word;
      SelectDisk: integer;
      

  18.   

    Onguard控件我已经下载了。谁有例程共享一个啊!
      

  19.   

    大概看了一下Onguard,功能很多啊,不错不错。就是没有中文教程。谁有这方面的使用经验,给大家指导一下啊!
      

  20.   

    谁有Onguard方面的中文使用资料,共享共享嘛!
      

  21.   

    大家写的程序是不是也在其它操作系统是进行测试呀,取BIOS序列号的程序在2000以上能通过吗?我写的程序都需要在98~XP是测试的。
      

  22.   

    本来我想让关心这方面的同志们多发表意见,既然
       cnssk(小柯) ( ) 信誉:101 
    同志对俺不揭帖很有意见,所以结之!但我仍然希望有这方面经验的继续发贴!大家交流嘛,不要把分看那么重!谢谢!
      

  23.   

    Onguard控件取的是硬盘卷标,不是id!
    她还有一个姊妹控件 LockBox ,做加密很好用,大家可以试试!