Solved

Machine unique ID - how to?

Posted on 2004-09-17
14
600 Views
Last Modified: 2010-05-18
Hi...
Please, how can i generate machine unique ID?
I can't use processor serial because some machines don't have it.

I wanna unique ID for a lot of machines on my network (including Pentium (100Mhz), Pentium 2, Athlon, P4, etc etc...)
0
Comment
Question by:Jmaurin
  • 3
  • 3
  • 2
  • +3
14 Comments
 
LVL 12

Accepted Solution

by:
esoftbg earned 32 total points
ID: 12084176
Get MAC unique address:

//........

unit Unit_MAC;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Buttons, StdCtrls;

type
  TForm1 = class(TForm)
    SpeedButton1: TSpeedButton;
    Memo: TMemo;
    procedure SpeedButton1Click(Sender: TObject);
  private{ Private declarations }
  public { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses
  NB30;


{$R *.dfm}

function GetAdapterInfo(Lana: Char): String;
var
  Adapter: TAdapterStatus;
  NCB: TNCB;
begin
  FillChar(NCB, SizeOf(NCB), 0);
  NCB.ncb_command := Char(NCBRESET);
  NCB.ncb_lana_num := Lana;
  if Netbios(@NCB) <> Char(NRC_GOODRET) then
  begin
    Result := 'MAC not found';
  end;

  FillChar(NCB, SizeOf(NCB), 0);
  NCB.ncb_command := Char(NCBASTAT);
  NCB.ncb_lana_num := Lana;
  NCB.ncb_callname := '*';

  FillChar(Adapter, SizeOf(Adapter), 0);
  NCB.ncb_buffer := @Adapter;
  NCB.ncb_length := SizeOf(Adapter);
  if Netbios(@NCB) <> Char(NRC_GOODRET) then
  begin
    Result := 'MAC not found';
  end;
  Result := IntToHex(Byte(Adapter.adapter_address[0]), 2) + '-' +
            IntToHex(Byte(Adapter.adapter_address[1]), 2) + '-' +
            IntToHex(Byte(Adapter.adapter_address[2]), 2) + '-' +
            IntToHex(Byte(Adapter.adapter_address[3]), 2) + '-' +
            IntToHex(Byte(Adapter.adapter_address[4]), 2) + '-' +
            IntToHex(Byte(Adapter.adapter_address[5]), 2);
end;

function GetMACAddress: string;
var
  AdapterList: TLanaEnum;
  NCB: TNCB;
begin
  FillChar(NCB, SizeOf(NCB), 0);
  NCB.ncb_command := Char(NCBENUM);
  NCB.ncb_buffer := @AdapterList;
  NCB.ncb_length := SizeOf(AdapterList);
  Netbios(@NCB);
  if (Byte(AdapterList.length) > 0) then
    Result := GetAdapterInfo(AdapterList.lana[0])
  else
    Result := 'MAC not found';
end;

procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
  Memo.Lines.Add(GetMACAddress);
end;

end.

//........

object Form1: TForm1
  Left = 224
  Top = 128
  BorderIcons = [biSystemMenu, biMinimize]
  BorderStyle = bsSingle
  Caption = 'MAC address'
  ClientHeight = 197
  ClientWidth = 344
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  Position = poDesktopCenter
  PixelsPerInch = 96
  TextHeight = 13
  object SpeedButton1: TSpeedButton
    Left = 8
    Top = 8
    Width = 128
    Height = 22
    Caption = 'Get MAC address'
    OnClick = SpeedButton1Click
  end
  object Memo: TMemo
    Left = 8
    Top = 32
    Width = 256
    Height = 160
    ReadOnly = True
    ScrollBars = ssVertical
    TabOrder = 0
  end
end
0
 

Author Comment

by:Jmaurin
ID: 12084248
Humm....the problem is: some lan cards enable user to change MAC.
Another problem: MAC is too big...
Another problem: if machine doesnt have lan card, just dial up connection (rare, i'm on a network!) but is possible...

another idea?
0
 
LVL 12

Expert Comment

by:esoftbg
ID: 12084322
I can get HDD serial number under Windows 2000 / XP ...., (can't get it under Win 95 / 98 / Millenium)
0
 
LVL 12

Expert Comment

by:Ivanov_G
ID: 12084469
esoftbg, you mean about the logical volume HDD ... you have serials for C:, D:, etc...
after format of this logical drives, this numbers are changed...

If you mean physical HDD serial:
http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_20490497.html

Another tips is to get CPU or BIOS serials, there are such examples/components on the internet, but I don't think you can use it user Windows 2003 Server for example...
0
 
LVL 12

Assisted Solution

by:Ivanov_G
Ivanov_G earned 31 total points
ID: 12084480
0
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
LVL 17

Assisted Solution

by:Wim ten Brink
Wim ten Brink earned 31 total points
ID: 12084669
You can easily use WMI to retrieve system information through multiple WMI queries. However, people should be allowed to change some hardware, so using serial numbers of harddisks or the MAC address of a network card is a bit useless. Even the BIOS version is unreliable since it can be upgraded. Still, most people just change one or two things at the same time, thus you should check for several things at once, and allow a few mismatches.
Thus, a combination of MAC address, HDD serial number of each harddisk, version of the BIOS, list of other connected devices and whatever else would provide you a good way to identify a machine and allow you to identify it even after some changes to the hardware. (You could consider it the same system as long as 50% is still identical.)

Also, add a weight to everything you use as identification. A serial number would be more important than the number of devices. A MAC address would also be heavyweight factor. The BIOS name more important than the BIOS version.
0
 
LVL 12

Expert Comment

by:esoftbg
ID: 12084718
This code extracts the physical serial number from HDDs:

unit Unit1_Q_21134979;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Buttons, DBT, StdCtrls;

const
      IDE_ID_FUNCTION            = $EC; // Returns ID sector for ATA.
      CAP_IDE_ID_FUNCTION        = 1;   // ATAPI ID command supported
      IDENTIFY_BUFFER_SIZE       = 512;
      DFP_RECEIVE_DRIVE_DATA     = $0007c088;

type
  TForm1 = class(TForm)
    SpeedButton1: TSpeedButton;
    ListBox: TListBox;
    procedure SpeedButton1Click(Sender: TObject);
  private   { Private declarations }
  public    { Public declarations }
    function  Physical_SN(Id: Integer): Int64;
  end;

      USHORT = Word;
  TIdBuffer    = array [0..IDENTIFY_BUFFER_SIZE-1] of Byte;
      TGetVersionOutParams = packed record
            bVersion      : BYTE;                 // Binary driver version.
            bRevision     : BYTE;                 // Binary driver revision.
            bReserved     : BYTE;                 // Not used.
            bIDEDeviceMap : BYTE;                 // Bit map of IDE devices.
            fCapabilities : DWORD;                // Bit mask of driver capabilities.
            dwReserved    : Array[0..3] of DWORD; // For future use.
      end;

      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;

      TDriverStatus = packed record
            bDriverError : Byte;                 // Error code from driver, or 0 if no error.
            bIDEStatus   : Byte;                 // Contents of IDE Error register. Only valid when bDriverError is SMART_IDE_ERROR.
            bReserved    : Array[0..1] of Byte;  // Reserved for future expansion.
            dwReserved   : Array[0..1] of DWORD; // Reserved for future expansion.
      end;

      TSendCmdOutParams = packed record
            cBufferSize  : DWORD;               // Size of bBuffer in bytes
            DriverStatus : TDriverStatus;       // Driver status structure.
            bBuffer      : Array[0..0] of BYTE; // Buffer of arbitrary length in which to store the data read from the drive.
      end;

      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
            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;

var
  Form1: TForm1;

var
  aIdBuffer: TIdBuffer;
  Id_Sector: TIdSector absolute aIdBuffer;
function  DirectIdentify(I: DWORD): Boolean;

implementation

{$R *.dfm}

var
  OSVersionInfo: TOSVersionInfo;

function GetPhysicalDriveHandle(DriveNum: Byte; DesireAccess: ACCESS_MASK): THandle;
var
  S:     string;
begin
  OSVersionInfo.dwOSVersionInfoSize := SizeOf(OSVersionInfo);
  GetVersionEx(OSVersionInfo);
  if OSVersionInfo.dwPlatformId=VER_PLATFORM_WIN32_NT then // Windows NT, Windows 2000
  begin
    Str(DriveNum,s); // avoid SysUtils
    Result := CreateFile( PChar('\\.\PhysicalDrive'+S), DesireAccess, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0 );
  end
  else // Windows 95 OSR2, Windows 98
    Result := CreateFile( '\\.\SMARTVSD', 0, 0, nil, CREATE_NEW, 0, 0 );
  {$ifdef debug}
  if Result=INVALID_HANDLE_VALUE then
    OutputDebugString(PChar('Error on CreateFile: '+SysErrorMessage(GetLastError)));
  {$endif}
end;

function SmartIdentifyDirect( hDevice : THandle; bDriveNum : Byte; bIDCmd : Byte; var IdSector : TIdSector; var IdSectorSize : LongInt ) : BOOL;
const
  BufferSize = SizeOf(TSendCmdOutParams)+IDENTIFY_BUFFER_SIZE-1;
var
  SCIP:        TSendCmdInParams;
             Buffer : Array [0..BufferSize-1] of Byte;
             SCOP : TSendCmdOutParams absolute Buffer;
             dwBytesReturned : DWORD;
begin
  FillChar(SCIP,SizeOf(TSendCmdInParams)-1,#0);
  FillChar(Buffer,BufferSize,#0);
  dwBytesReturned := 0;
  IdSectorSize := 0;
  // Set up data structures for IDENTIFY command.
  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;      // The command can either be IDE identify or ATAPI identify.
    end;
  end;
  Result := DeviceIoControl( hDevice, DFP_RECEIVE_DRIVE_DATA, @SCIP, SizeOf(TSendCmdInParams)-1, @SCOP, BufferSize, dwBytesReturned, nil );
  if Result then
  begin
    IdSectorSize := dwBytesReturned-SizeOf(TSendCmdOutParams)+1;
    if IdSectorSize<=0 then IdSectorSize := 0 else System.Move(SCOP.bBuffer,IdSector,IdSectorSize);
  end;
end;

function  DirectIdentify(I: DWORD): Boolean;
var
  B:               Boolean;
  hDevice:         THandle;
  nIdSectorSize:   LongInt;
begin
  B := False;
  FillChar(aIdBuffer, SizeOf(aIdBuffer), #0);
  try
    hDevice := GetPhysicalDriveHandle(I, GENERIC_READ or GENERIC_WRITE);
    try
      {$ifdef debug}
      OutputDebugString(PChar('GetPhysicalDriveHandle return '+IntToHex(hDevice,8)));
      {$endif}
      if (hDevice<>INVALID_HANDLE_VALUE) then
      try
        B := SmartIdentifyDirect(hDevice, 0, IDE_ID_FUNCTION, Id_Sector, nIdSectorSize);
      finally
        {$ifdef debug}
        OutputDebugString('PrintIdSectorInfo end');
        {$endif}
      end;
    finally
      Result := B;
      CloseHandle(hDevice);
    end;
  except
    Result := False;
  end;
end;

function  Hex_To_Dec(S: string): Int64;
var
  C:      Char;
  J:      Integer;
  II:     Int64;
  IH:     Int64;
begin
  IH := 0;
  try
    II := 1;
    while (S<>'') do
    begin
      C := S[Length(S)];
      if (C in ['0'..'9']) then
        J := StrToInt(C)
      else
        J := Ord(C) - 55;
      IH := IH + II * J;
      Delete(S,Length(S),1);
      II := II * 16;
    end;
  finally
    Result := IH;
  end;
end;

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;

function  TForm1.Physical_SN(Id: Integer): Int64;
type
  TacOutBuffer  =  array[0..40] of Char;
var
  I:               Integer;
  IH:              Int64;
  acOutBuffer:     TacOutBuffer;
  T:               string;
begin
  IH := 0;
  try
    DirectIdentify(Id);
    with Id_Sector do
    begin
      ChangeByteOrder(sSerialNumber, SizeOf(sSerialNumber));
      acOutBuffer[SizeOf(sSerialNumber)] := #0;
      StrLCopy(acOutBuffer, sSerialNumber, SizeOf(sSerialNumber));
      I := 0;
      T := '';
      while (acOutBuffer[I] in [#32..#127]) do
      begin
        T := T + acOutBuffer[I];
        Inc(I);
      end;
      T := Trim(T);
      ListBox.Items.Add(Char(Id+67) + ':');
      ListBox.Items.Add('Hex. Serial num:' + T);
      IH := Hex_To_Dec(T);
      ListBox.Items.Add('Dec. Serial num:' + IntToStr(IH));
    end;
  finally
    Result := IH;
  end;
end;

procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
  ListBox.Clear;
  Physical_SN(0); // C:
  Physical_SN(1); // D:
  Physical_SN(2); // E:
end;

end.
0
 
LVL 7

Assisted Solution

by:LRHGuy
LRHGuy earned 31 total points
ID: 12085067
Check out Turbopowers' OnGuard package at sourceforge, free and with source:

http://sourceforge.net/projects/tponguard/

It has a routine "CreateMachineID" which should suit your needs. It should be in OnGuard.pas.
0
 
LVL 12

Expert Comment

by:Ivanov_G
ID: 12085114
Just another idea.... something I practise...

the serial numbers used for activation of my software are encrypted strings, which contains the customer name. and there is a point in the contract, which doesn't allow them to publish this information and penalty about this...

The other way is to activate your software via web site. Thus you can track when someone wants to active his software with wrong serial or serial, that was alredy used before...
0
 
LVL 5

Expert Comment

by:Voodooman
ID: 12087658
Comment only

Hi

I see that we are once again looking at unique PC identification - often for registering software.

If you are looking at a unique PC ID on the Network - all PC's on a Network (the original question) have a unique ID - its the PC Name!!!!!!

This is 100% true - however the really, really, unique ID under windows is the SID - the Security Identifier.

All PC's have a SID - its what identifies them under Windows.  This is why when you change a PC on a network and give it the same name as the old one, things dont work out - because Windows uses the SID - not the Name.

Voodooman
0
 
LVL 5

Expert Comment

by:Voodooman
ID: 13434203
The SID is still the only unique ID on a Windows network - the rest is rubbish

Voodooman
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
This Micro Tutorial will teach you how to censor certain areas of your screen. The example in this video will show a little boy's face being blurred. This will be demonstrated using Adobe Premiere Pro CS6.
Video by: Mark
This lesson goes over how to construct ordered and unordered lists and how to create hyperlinks.

910 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now