Set of number which **not** change after format computer

Hi Experts!

I'm writing application which is shareware (30 days trial) and have a registration system,
in this system, at first I will get volume information (GetVolumeInformation) and use this
set of number to calculate (with my own equation) an Activation Code to register

Problem is, after user format their haddisk, and re-install my program again. This Volume Number changed.
That means user must register again (pay for licence again). I don't want to do this, so i try to find some
set of number which NOT change when user format their drive...

The last one, if you have my answer, will your answer compatible with ALL Windows Version?

Please, someone tell me some direction?

Thanks!
xtrueAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

2266180Commented:
well .. one way to go that really doesn't change too often is using some otehr programs serial or activation key (like windows ;) ). because hardware changes occure generally once 1-2 years. anotehr way to go is using the MAC of the network interface. this again is only changing when the network card dies (very very rare) or the user spoofs it (in which case it's his fault)
you could of course use multiple such numbers (MAC, windows activation key (it's in the registry by the way) and some otehr hardware serial number or ID (like the CPU)) and compute 3 -4 activation codes and the final activation code will be the 3-4 codes put one after another. then, if 1 changes, you can see which one changed and if the other ones are the same, you update the on ethat changed (it's very rare when the person is changing the whole PC)
of course, there is no perfect way to achieve what you want, you will have to leave a small niche.

an idea would be to send the user a cd with the software, and on the cd try to hide the key in such a manner that it will not be copied over anotehr cd when doing an image (like simulating "bad sectors" or othervise exploit some weaknesses in the cd filesystem: never looked at it so I don't know if it can be done, but it looks like a good idea). in this case, the user will have to install always from it's original disk. And, if you keep a list with your customers, then you can give them the possibility of replacing the original cd with a new one in case they damage it in some way (and of course you can control and see if indeed it's the original cd or not)

Thelast way would be the one that I would choose if being in your situation :)
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
AmigoJackCommented:
one system device which changes rarely is the processor. the advantage is: the user can reformat anything and also install every windows-version. you could get a string of infos out of this:

var
  s: _system_info;
  s1: string;
begin
  getsysteminfo(s);
 
  s1:= inttohex(s.wProcessorArchitecture, 4)
//     + inttohex(s.wProcessorLevel, 4)
//     + inttohex(s.wProcessorRevision, 4)
     + inttohex(s.wReserved, 4)
     + inttohex(s.dwPageSize, 8)
     + inttohex(s.dwProcessorType, 8);
end;

the lines which are commented out will give different values based on the windows type (95 versus NT). but the info above is very little and i think many users will come up with the same string.

you could combine it with GetVolumeInformation() - but dont get the serial of the formatted drive - get instead its capacity (because that will not change - got it?).

other methods would be to get the vendor description of one harddisk or the processor, that wont change too.

a classic (but also not perfect) way is to simply compute a value out of the users name and its birth date. that requires interaction of course, like writing a mail to register
0
DragonSlayerCommented:
Hi xtrue, I know this is already a PAQ, but if you really need the serial number of the hard disk which *doesn't* change even after a formatting, try this code:

unit HDD_Serial;
interface
uses Windows, SysUtils;
function GetHddSerial: String;
implementation
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 for future use.  Must be zero.
end;
IDEREGS   = TIDERegs;
PIDERegs  = ^TIDERegs;
TSendCmdInParams = packed record
  cBufferSize  : DWORD;                // Buffer size in bytes
  irDriveRegs  : TIDERegs;             // Structure with drive register values.
  bDriveNumber : Byte;                 // Physical drive number to send command to (0,1,2,3).
  bReserved    : Array[0..2] of Byte;  // Reserved for future expansion.
  dwReserved   : Array[0..3] of DWORD; // For future use.
  bBuffer      : Array[0..0] of Byte;  // Input buffer.
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; // PSendCmdInParams;
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 := PChar(@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;
function GetDeviceHandle( sDeviceName : String ) : THandle;
begin
 Result := CreateFile( PChar('\\.\'+sDeviceName), GENERIC_READ or GENERIC_WRITE,
    FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0 );
end;
function ScsiHddSerialNumber( DeviceHandle : THandle ) : String;
{$ALIGN ON}
type
 TScsiPassThrough = record
    Length             : Word;
    ScsiStatus         : Byte;
    PathId             : Byte;
    TargetId           : Byte;
    Lun                : Byte;
    CdbLength          : Byte;
    SenseInfoLength    : Byte;
    DataIn             : Byte;
    DataTransferLength : ULONG;
    TimeOutValue       : ULONG;
    DataBufferOffset   : DWORD;
    SenseInfoOffset    : ULONG;
    Cdb                : Array[0..15] of Byte;
 end;
 TScsiPassThroughWithBuffers = record
    spt : TScsiPassThrough;
    bSenseBuf : Array[0..31] of Byte;
    bDataBuf : Array[0..191] of Byte;
 end;
{ALIGN OFF}
var
 dwReturned : DWORD;
 len : DWORD;
 Buffer : Array[0..SizeOf(TScsiPassThroughWithBuffers)+SizeOf(TScsiPassThrough)-1] of Byte;
 sptwb : TScsiPassThroughWithBuffers absolute Buffer;
begin
 Result := '';
 FillChar(Buffer,SizeOf(Buffer),#0);
 with sptwb.spt do begin
    Length   := SizeOf(TScsiPassThrough);
    CdbLength := 6; // CDB6GENERIC_LENGTH
    SenseInfoLength := 24;
    DataIn := 1; // SCSI_IOCTL_DATA_IN
    DataTransferLength := 192;
    TimeOutValue := 2;
    DataBufferOffset := PChar(@sptwb.bDataBuf)-PChar(@sptwb);
    SenseInfoOffset := PChar(@sptwb.bSenseBuf)-PChar(@sptwb);
    Cdb[0] := $12; // OperationCode := SCSIOP_INQUIRY;
    Cdb[1] := $01; // Flags := CDB_INQUIRY_EVPD;  Vital product data
    Cdb[2] := $80; // PageCode            Unit serial number
    Cdb[4] := 192; // AllocationLength
 end;
 len := sptwb.spt.DataBufferOffset+sptwb.spt.DataTransferLength;
 if DeviceIoControl( DeviceHandle, $0004d004, @sptwb, SizeOf(TScsiPassThrough), @sptwb, len, dwReturned, nil ) and ((PChar(@sptwb.bDataBuf)+1)^=#$80) then
    SetString( Result, PChar(@sptwb.bDataBuf)+4, Ord((PChar(@sptwb.bDataBuf)+3)^) );
end;
function GetHddSerial: String;
var
 NumTry: Byte;
 FinalStr: String;
 hDevice: THandle;
 sDeviceName: String;
begin
 NumTry := 1;
 Repeat
    Case NumTry Of
       1:
          Begin
             FinalStr := Trim(GetIdeDiskSerialNumber);
          End;
       2:
          Begin
             sDeviceName := 'C:';
             hDevice := GetDeviceHandle(sDeviceName);
             If hDevice <> INVALID_HANDLE_VALUE Then Begin
                Try
                   FinalStr := Trim(ScsiHddSerialNumber(hDevice));
                Finally
                   CloseHandle(hDevice);
                End;
             End;
          End;
       3: Begin
             FinalStr := 'Error!';
          End;
    End;
    Inc(NumTry);
 Until (FinalStr <> '') Or (NumTry > 3);
 Result := FinalStr;
end;



Regards,
DragonSlayer.
0
Cloud Class® Course: Certified Penetration Testing

This CPTE Certified Penetration Testing Engineer course covers everything you need to know about becoming a Certified Penetration Testing Engineer. Career Path: Professional roles include Ethical Hackers, Security Consultants, System Administrators, and Chief Security Officers.

xtrueAuthor Commented:
Hi DragonSlayer!

Can this code compat with all Windows Version?

By the way, thanks for your help!
0
DragonSlayerCommented:
Yup :-)
0
xtrueAuthor Commented:
Again, DragonSlaver!

In your code i got both number and character (14 lengths), is that right ?

So i have to convert character to number because in my case i want this set of number
to calculate (+,-,*,/)


Thanks,



0
DragonSlayerCommented:
Yup. You can always treat it as a sequence of 7 hex pairs, or do your own conversion to numbers.

Regardless, it will always be able to correctly and uniquely identify the same hard disk :-)



DragonSlayer.
0
xtrueAuthor Commented:
thanks!
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.