Newsgroup: it.comp.lang.delphi
Titolo: Controllo HW

Ciao ragazzi,
Vorrei utilizzare una api che non mi dia troppi problemi sulle diverse
versioni di Windows (XP, VISTA, 64 etc.) mi serve solo per trovare
qualcosa che sia legato al hardware per fare una piccola protezione.
Es.
GetSystemFirmwareTable
Con il unique id del hard disk e cpu id ho trovato qualche problema.
Unique id dell'hard disk è ok per XP ma non riesco a utilizzarlo per
VISTA.
Mi era poi venuta in mente la data del bios, ma difficile trovare
qualcosa di sicuro e stabile.
Qualche idea ?
Non mi interessa usare complessi programmi di protezione :-)
Ciao

 
ciao io uso da anni i maxcomponents (free) mxprotector e mi trovo bene 
relegando l'uso di quella applicazione a quel computer.
pagina: 
http://www.maxcomponents.net/index.php?id=2&page=1
saluti :)

 qualcosa che sia legato al hardware per fare una piccola protezione.
Alche le JEDI lib hanno dei componenti che ti danno queste info.
Ciao,
Gp


Grazie per le segnalazioni,
Sto un po' navigando tra le API, in quanto preferirei una chiamata ad
API semplice senza usare componenti di terze parti...
Non capisco perché in VISTA 32 bit non funziona la chiamata:
CreateFile(PChar(Format('\\.\PhysicalDrive%d',
[bDriveNum])),dwDesiredAccess,FILE_SHARE_READ or
FILE_SHARE_WRITE,nil,OPEN_EXISTING,0,0);
Anche mettendo dwDesiredAccess=GENERIC_ALL e eseguendo il file come
amministratore mi dà picche, mentre con XP è tutto ok !
Mi basterebbe leggere la data del bios.
Se qualcuno ha un pochino di pappa pronta preconfezionata la posti
pure :-)
Ciao


Per la data del bios puoi seguire il consiglio di gpcaretti ed usare
le jedi, c'è un componente system che fa quello che ti serve. Io
personalmente però eviterei di usare la dta del bios, in primis perchè
i bios possono essere aggiornati (ed oggi è una pratica piuttosto
frequente) e a quel punto la data cambia e il cliente sarebbe cotretto
a contattarti niovamente x una nuova chiave di sblocco. In più la data
non è univoca, ossia potresti avere + pc col medesimo bios/data dove
il tuo software girerebbe indisturbato...
Ciao
Andrea

 saluti :)
L'ho provato tempo fa, non è affatto male.
Ricordo però che nel mio caso avendo bisogno di un ID hardware univoco
e costante non andava bene. In caso di formattazione del pc l'ID
hardware cambiava, guardando il sorgente infatti l'ID è generato da
alcuni parametri tra cui l'id utente e il seriale di partizione che
sono due dati che come ho detto cambiano se si formatta il pc...
Andrea

 Andrea
si � vero allora l'unica cosa � una key usb. te ne spiego subito i motivi:
1) pc rotto.
2) sostituzione dell'hardware che tu chiami per la procedura (es. CPU)
3) come hai fatto notare formattazione hd.
ci sono mille altri casi ovviamente quindi l'unica � una key usb con una 
ricerca su google ne trovi a bizeffe :)
saluti

 saluti
Si � vero, hai pienamente ragione.
Avevo provato per un p� le chiavi hw della EutronSec. Davvero ottime.
E' l'unico vero modo per non avere *rogne*.
L'inconveniente � il costo delle chiavi (ricordo si partiva da 40 ?), che in 
molti casi supera quello del software stesso. E allora che fare? Si ovvia 
con la protezione software che ha le sue lacune...
Ciao
Andrea 

un informazione univoca per una protezione software economica potresti
ricavarla dal mac address della scheda di rete, non e' il massimo ma
e' meglio di niente, ciao Brunello

 e' meglio di niente, ciao Brunello
si ï¿½ vero ma ci sono alcuni dettagli...
scheda bruciata e quindi niente piï¿½ mac univoco.
software (a centinaia) che modificano il detto mac...
etc... etc...
per le chiavette le eutron se non ricordo male si aggirano intorno ai 
25ï¿½ + iva...
quindi si dipende dal costo del software ma quanto incidono le rogne nel 
tuo budget?
mi spiego meglio meglio vendere un soft a 50ï¿½ o a 80ï¿½ ed essere cosï¿½ 
tranquilli che anche se il cliente ha il pc in volo dal 5ï¿½ piano con la 
chiavetta risolve e quindi non ti preoccupi di copie abusive?
saluti :)


Il Mac address delle schede di rete è facilmente modificabile, non è
come il seriale fisico degli hard disk che non può essere
riprogrammato.
Tempo fa ebbi a tal proposito un inconveniente davvero curioso.
In un software eseguivo delle operazioni con il Mac address ed da un
cliente (a distanza) avevo problemi. Dopo varie prove e telefonate si
capì l'inghippo.
Il cliente utilizzava una lan USB che non sempre aveva collegata...
Come detto, la protezione software ha delle lacune, compresa quella
che si basa sul MAC
Andrea

 saluti :)
Quoto.
- rogne = + tempo per altro
+ tempo = soldi risparmiati

 + tempo = soldi risparmiati
Forse sono vicino alla soluzione che posto qui perché magari qualcuno
può risolvere la parte mancante o trarre ispirazione.
Il SN del hard disk credo sia una buonissima soluzione. Vendi una
licenza per HD, quindi se a uno interessa su due postazione ne prende
due... :-)
Inoltre se formatta è tutto ok.
Ok adesso restituisce l'handle con MAXIMUM_ALLOWED per non dover
entrare come admin e non GENERIC_READ or GENERIC_WRITE
L'struzione va:
CreateFile
   (
     PChar(Format('\\.\PhysicalDrive%d',
[bDriveNum])),MAXIMUM_ALLOWED,FILE_SHARE_READ or
FILE_SHARE_WRITE,nil,OPEN_EXISTING,0,0);
riesco a ottenere l'handle.
L'istruzione che non va:
DeviceIoControl( hDevice, DFP_RECEIVE_DRIVE_DATA, @SCIP, SizeOf
(TSendCmdInParams)-1, @SCOP, nBufferSz, dwBytesRet, nil );
unit SerialeHD;
interface
uses
  Windows, SysUtils, dialogs;
const
      IDENTIFY_BUFFER_SIZE   = 512;
      DFP_GET_VERSION        = $00074080;
      DFP_SEND_DRIVE_COMMAND = $0007c084;
      DFP_RECEIVE_DRIVE_DATA = $0007c088;
      IDE_ID_FUNCTION        = $EC;
type
   USHORT = Word;
   TIdSector = packed record
   wGenConfig                : USHORT;
   wNumCyls                  : USHORT;
   wReserved                 : USHORT;
   wNumHeads                 : USHORT;
   wBytesPerTrack            : USHORT;
   wBytesPerSector           : USHORT;
   wSectorsPerTrack          : USHORT;
   wVendorUnique             : array[0..2] of USHORT;
   sSerialNumber             : array[0..19] of Char;
   wBufferType               : USHORT;
   wBufferSize               : USHORT;
   wECCSize                  : USHORT;
   sFirmwareRev              : array[0..7] of Char;
   sModelNumber              : array[0..39] of Char;
   wMoreVendorUnique         : USHORT;
   wDoubleWordIO             : USHORT;
   wCapabilities             : USHORT;
   wReserved1                : USHORT;
   wPIOTiming                : USHORT;
   wDMATiming                : USHORT;
   wBS                       : USHORT;
   wNumCurrentCyls           : USHORT;
   wNumCurrentHeads          : USHORT;
   wNumCurrentSectorsPerTrack: USHORT;
   ulCurrentSectorCapacity   : ULONG;
   wMultSectorStuff          : USHORT;
   ulTotalAddressableSectors : ULONG;
   wSingleWordDMA            : USHORT;
   wMultiWordDMA             : USHORT;
   bReserved                 : array[0..127] of Byte;
   end;
   TIDERegs = packed record
   bFeaturesReg    : Byte;
   bSectorCountReg : Byte;
   bSectorNumberReg: Byte;
   bCylLowReg      : Byte;
   bCylHighReg     : Byte;
   bDriveHeadReg   : Byte;
   bCommandReg     : Byte;
   bReserved       : Byte;
   end;
   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;
   TDriverStatus = packed record
   bDriverError: Byte;
   bIDEStatus  : Byte;
   bReserved   : array[0..1] of Byte;
   dwReserved  : array[0..1] of DWord;
   end;
   TSendCmdOutParams = packed record
   cBufferSize : DWord;
   DriverStatus: TDriverStatus;
   bBuffer     : array[0..0] of Byte;
   end;
procedure valore;
var     tagOSInfo: TOSVersionInfo;
        varInfo : array [0..50] of String;
        varY    : array [0..50] of String;
implementation
{$R-}
function GetPhysicalDriveHandle( bDriveNum: Byte; dwDesiredAccess:
ACCESS_MASK ): THandle;
begin
dwDesiredAccess:=MAXIMUM_ALLOWED; // NECESSARIO PER VISTA
 if( tagOSInfo.dwPlatformId=VER_PLATFORM_WIN32_NT ) then begin
   // Windows NT, Windows 2000, Windows XP, (anche Vista ma attenzione
a dwDesiredAccess)
   Result:=CreateFile
   (
     PChar(Format('\\.\PhysicalDrive%d',
[bDriveNum])),dwDesiredAccess,FILE_SHARE_READ or
FILE_SHARE_WRITE,nil,OPEN_EXISTING,0,0);
 end else begin
   // Windows 95 OSR2, Windows 98
   Result:=CreateFile( '\\.\SMARTVSD', 0, 0, nil, CREATE_NEW, 0, 0 );
 end;
end;
function SmartIdentifyDirect( hDevice: THandle; bDriveNum, bIDCmd:
Byte; var IdSector: TIdSector; var IdSectorSize: Longint ): Boolean;
const nBufferSz = SizeOf(TSendCmdOutParams)+IDENTIFY_BUFFER_SIZE-1;
var
 SCIP      : TSendCmdInParams;
 abBuffer  : array [0..nBufferSz-1] of Byte;
 SCOP      : TSendCmdOutParams absolute abBuffer;
 dwBytesRet: DWord;
 (* variabili fittizie *) A,B,C     : Integer;
begin
 ZeroMemory( @SCIP, SizeOf(TSendCmdInParams) );
 ZeroMemory( @abBuffer, nBufferSz );
 dwBytesRet  :=0;
 IdSectorSize:=0;
 with SCIP do begin
   cBufferSize :=IDENTIFY_BUFFER_SIZE;
   bDriveNumber:=bDriveNum;
   with irDriveRegs do begin
     bFeaturesReg    :=0;
     bSectorCountReg :=1;
     bSectorNumberReg:=1;
     bCylLowReg      :=0;
     bCylHighReg     :=0;
     bDriveHeadReg   :=$A0 or ((bDriveNum and 1) shl 4);
     bCommandReg     :=bIDCmd;
   end;
 end;
 // QUI PROBLEMI CON VISTA !!!
 Result:=DeviceIoControl( hDevice, DFP_RECEIVE_DRIVE_DATA, @SCIP,
SizeOf(TSendCmdInParams)-1, @SCOP, nBufferSz, dwBytesRet, nil );
 if( Result ) then begin
   IdSectorSize:=dwBytesRet-SizeOf(TSendCmdOutParams)+1;
   if( IdSectorSize<0 ) then
     IdSectorSize:=0
   else System.Move( SCOP.bBuffer, IdSector, IdSectorSize );
 end;
 end;
procedure ChangeByteOrder( var Data; Size: Longint );
var
 lpTmp: PChar;
 ii   : Longint;
 cTmp : Char;
 (* variabili fittizie per confondere il listato esadecimale *)A,B,C :
Integer;
begin
 lpTmp:=@Data;
 for ii:=0 to (Size shr 1)-1 do begin
   cTmp:=lpTmp^;
   lpTmp^:=(lpTmp+1)^;
   (lpTmp+1)^:=cTmp;
    Inc(lpTmp,2);
 end;
end;
function PrintIdSectorInfo(quale:integer; IdSector : TIdSector ):
string;
var acOutBuffer: array [0..40] of Char;
    Inverti : String;
    ii : Integer;
   begin
 with IdSector do begin
   ChangeByteOrder( sModelNumber, SizeOf(sModelNumber) );
   acOutBuffer[SizeOf(sModelNumber)] := #0;
   Result:='Model: '+Trim(sModelNumber);
   (*
   ChangeByteOrder( sFirmwareRev, SizeOf(sFirmwareRev) );
   acOutBuffer[SizeOf(sFirmwareRev)] := #0;
   Result:=Result+', Firmware: '+Trim(sFirmwareRev);
   *)
   ChangeByteOrder( sSerialNumber, SizeOf(sSerialNumber) );
   acOutBuffer[SizeOf(sSerialNumber)] := #0;
   varY[quale]:=Trim(sSerialNumber);
   Inverti:='';
   for ii:=length(VarY[quale]) downto 1 do Inverti:=Inverti+varY[quale]
[ii];
   varY[quale]:=Inverti;
   Result:=Result+', Serial: '+Trim(sSerialNumber);
 end;
end;
procedure valore;
var
 ii         : Longint;
 hDevice    : THandle;
 nIdSectorSz: LongInt;
 aIdBuffer  : array [0..IDENTIFY_BUFFER_SIZE-1] of Byte;
 IdSector   : TIdSector absolute aIdBuffer;
 acOutBuffer: array [0..40] of Char;
 (* variabili fittizie per confondere il listato esadecimale *)
A,B,C,D    : Integer;
begin
 // Versione OS
 tagOSInfo.dwOSVersionInfoSize:=SizeOf(TOSVersionInfo);
 GetVersionEx(tagOSInfo);
 // Azzera gli Array
 for ii:=0 to 50 do varInfo[ii]:='';
 for ii:=0 to 50 do varY[ii]:='';
 // Max 500 HardDisk
 ii:=-1; A:=400; B:=500; C:=220; D:=150;
 Repeat
 ii:=ii+1; A:=B-C;
 ZeroMemory( @aIdBuffer, SizeOf(aIdBuffer) );
 hDevice:=GetPhysicalDriveHandle( ii, GENERIC_READ or GENERIC_WRITE );
  if(hDevice=INVALID_HANDLE_VALUE) then
    Break
   else
   begin
     try
       if( SmartIdentifyDirect( hDevice, 0, IDE_ID_FUNCTION, IdSector,
nIdSectorSz ) ) then begin
          // Fai un confronto con il SN dichiarato dall'utente
         varInfo[ii]:=Chr(67+ii)+':\ '+PrintIdSectorInfo(ii,IdSector);
         end;
     finally
      (* vedi per VISTA se per cancellare buffer
       PurgeComm(hdevice , PURGE_RXABORT or PURGE_RXCLEAR or
       PURGE_TXABORT or PURGE_TXCLEAR); CancelIo(hDevice); *)
       CloseHandle(hDevice);
     end;
   end;
 Until (ii=50);
end;
end.

 Inoltre se formatta ï¿œ tutto ok.
Anni fa ho implementato una soluzione con mxprotector 
(
http://groups.google.it/group/it.comp.lang.delphi/msg/c73e1ff21f57127f?hl=it
)
si basava sul codice di sblocco e per generare il pezzo di codice che 
il cliente mi leggeva la telefono o mi faxava, usavo l'hardware del pc 
ospite.
Ogni volta che il cliente mi chiamava chiedendomi un altro codice di 
sblocco, chiedevo per che motivo, e cosi' avevo un controllo sulle sue 
attivitï¿œ.
Il problema non e' come rendere uncrackabile un software, ma quanto 
avere un controllo delle installazioni pirata.
Se fai nel mio modo, il cliente dovrï¿œ chiedere un codice di sblocco, e 
quindi dovrï¿œ pur telefonarti e parlare con te, e quindi venire allo 
scoperto.
Nei dettagli ecco la mia soluzione: in origine il software esce come 
installazione completa ma modalitï¿œ DEMO, che limita un certo numero di 
record massimi per alcune tabelle chiave e su tutte le stampe mette una 
bitmap grande con scritto "demo". Dopo 30 giorni dall'installazione, il 
software si blocca e chiede la chiave di sblocco per funzionare in 
maniera FULL. E' li' che il cliente chiama per farsi dare il codice, 
comperare il programma e iniziare a usare il software in modo pulito.
-- 
Morde


E' il metodo che grosso modo utilizzo anche io. Il cliente mi contatta
per la chiave di sblocco, così tengo traccia di tutte le attivazioni.
Se poi becchi l'hacker avanzato potrebbe risalire alla funzione di
generazione della chiave di sblocco, ma vorrei trovarmi volentieri in
questa situazione xchè significherebbe che il software andrebbe a
ruba ;-)
Io ho ampliato implementando una procedura per cui all'avvio il
software manda una querystring ad una pagina web comunicando l'ID
hardware e lo memorizzo in db.
Così anche eventuali ID non sbloccati da me possono essere tracciati e
nelle release successive bannati.
Ciao
Andrea

 nelle release successive bannati.
Totally agreed with you. ;-)
-- 
Morde

Molto interessante ma non stiamo facendo filosofia del codice ? :-)
Intendo dire come trovare un sistema per la lettura di un ID univoco
senza grane ?
Le API ci sono possibile che sia così difficile implementare del
codice del genere, senza menose librerie esterne ?

 
Si potrebbe avere l' algoritmo o qualche informazione piu` dettagliata 
sul tuo metodo?
Sto per finire un programmma e appunto cerco come difendermi da copie 
illegali.
Non mi piace l'idea di usare ID hardware ne chiavette usb o LPT che ho 
usato fino a qualche anno fa ma di cui non ero troppo contento.
Un buon metodo software sarebbe l'ideale. E se si trovera` un hacker 
disposto a perdere tempo per crackare il mio programma... vorra` dire 
che ho avuto successo :)
Enrico

 codice del genere, senza menose librerie esterne ?
Il problema non e' trovare un id univoco, quanto impedire il cracking, 
che di fatto ï¿œ impossibile: alla fine ci sarï¿œ sempre un jump nel codice 
assembler che, se cambiato, inverte la logica di registrazione e ti 
valida codici invalidi.
Ciao
-- 
Morde


Niente di complicato.
Ricavo l'id hardware tramite WMI (
http://www.magsys.co.uk/delphi/
magwmi.asp)
Al passaggio da demo a full lancio la procedura di attivazione. A
video visualizzo l'ID hw ricavato che il cliente mi comunica e per il
qualche è necessaria una corrispondente chiave di sblocco per
procedere. la chiave viene calcolata con funzioni crypt/decrypt (su
torry ce ne sono a bizzeffe). Così sarebbe sufficiente di già.
Per di più all'avvio del software lancio tramite Indy ftp un head
verso una pagina asp a cui passo qualche parametro (ID hardware, data
e ora, un bool se demo o licenziato, ecc..). Così sò effettivamente i
software in uso sia demo che licenziati e le rispettive chiavi hw. Se
una chiave non mi risulta, nelle release successive la aggiungo ad un
array di ID bannati e per i quali il software non funge.
La cosa è grossolana ma funziona, potrebbe essere migliorata di molto,
ma di tempo per queste *finezze* non ce n'è.
Saluti
Andrea

Morde ha scritto:

l'hack ï¿œ impossibile da evitare in ogni caso...
ma secondo me il problema nasce quando il cliente cerca di fregarti...
tipico caso:
	ho rotto il pc mi serve un nuovo codice.
quanti gliene darai? perciï¿œ secondo me il sistema migliore ï¿œ la 
chiavetta le soluzioni software fanno perdere solo un mucchio di tempo, 
ovviamente parlo secondo le mie esperienze ;)
saluti

 saluti
Non ho molta esperienza con le chiavette USB ma quando usavo le LPT 
c'erano un sacco di problemi di compatibilita`.
Inoltre i clienti non erano molto contenti di dover aggiungere al costo 
del programma quasi il 50% per una cosa che a loro non serve (per motivi 
fiscali dovevo evidenziare la voce chiavetta nella fattura).
Quindi anche la chiavetta ha i suoi svantaggi, almeno nel marketing :)
Enrico

 vinto !
Avendo sprotetto parecchia roba, so che l'hacker che mette un jmp e
inizia a usare il programma non controlla gli errori sotterranei
dell'applicazione.
Quando trova questi errori pensa di aver sbagliato il jmp, in pratica
è saltato sopra qualche costante o assegnazione.
Se poi una costante è calcolata come risultato del ID univoco le cose
divengono + complesse.
Ovvio se un bravo hacker guadagna 1 milione di euro allora te lo
sprotegge. Ma mettersi li dei giorni per un programmino da 50 euro chi
lo farebbe ?

vinto !
Ciao
dici delle cose interessanti ma ... non ci ho capito molto....
sottointendi molte cose o forse ti sei mangiato qualche parola ?
Potresti spiegare meglio ?
Grazie
C. 

 Dicesi sprotezione sporca, la peggiore per qualsiasi hacker.
il tutto ovviamente dipende dalla bravura del programmatore ma 
sopratuttto dall'hacker.
il mio esempio era un altro:
c_thelegend chiama nibiru e chiede: "salve ho bruciato l'hd e devo 
reinstallare il programma, mi da il codice?"
nibiru dall'altro lato si chiede pensoso:"sar� vero? o semplicemente lo 
sta installando su un altro pc?"
ed � questo il problema pi� grande con le chiavette nessuno ti frega, se 
la perdono cavoli loro, se la bruciano te la reinviano e al costo la 
sostituisci, problemi zero, tanti pensieri risparmiati e sopratutto 
tempo guadagnato (quello dei controlli non fatti).
saluti ;)

 saluti ;)
Si ma la licenza parla chiaro. Io do la licenza per l'hard disk in
questione, se lo brucia sono cavoli suoi.
Non cedo la licenza alla persona ma è da utilizzarsi solo su quel
particolare HD. :-)
Il codice che ho postato funziona anche per VISTA, ma si deve lanciare
il programma con diritti di amministratore.
DeviceIoControl dà infatti false senza tali diritti.
Per harddisk scasi il discorso è analogo,
program IdeSN;
// PURPOSE: Simple console application that extract first IDE disk
serial number.
{$APPTYPE CONSOLE}
uses
  Windows,
  SysUtils; // only for Win32Platform and SysErrorMessage
//-------------------------------------------------------------
function GetIdeDiskSerialNumber : String;
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:', // Note: '\\.\C:' requires administrative
permissions.
        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;
//=============================================================
var s : String;
    rc : DWORD;
begin
  s := GetIdeDiskSerialNumber;
  if s='' then
    begin
      rc := GetLastError;
      if rc=0 then WriteLn('IDE drive is not support SMART feature')
      else WriteLn(SysErrorMessage(rc));
    end
  else WriteLn('Disk serial number: ''', s,'''');
end.
Qualcuno sa come bypassare il problema ?

 particolare HD. :-)
mi sa che viviamo realt� diverse allora...
qui se uno compra una cosa la vuole usare anche sul nuovo pc, se ne 
frega che la licenza � per hd, e come discorso � anche giusto se compro 
una autoradio voglio usarla su qualunque auto mica solo su quella che ho 
al momento...

 al momento...
Tieni presente che la protezione HD legata alla macchina è molto
diffusa.
Es. SQL server se non erro usa la CPUID e vende la licenza per la
determinata macchina.
Io in questo caso mi rivolgo a vendite di programmi online da 20 euro.
Se includi una usb key il gioco non vale + la candela.
Se a qualcuno viene in mente come bypassare il problema di
DeviceIoControl senza dover effettuare il run come admin in
vista .... :-)
E' possibile compilare un eseguibile ini modo che venga lanciato in
Delphi come admin rights ? Ad es. se lo chiamo setup viene lanciato
così, quindi magari esiste qualche flag o controllo sull'exe.
Ciao e grazie anche per le librerie che mi sto studiando.

Ma se il cliente dice di aver perso la USBkey cosa fai ? :-)