Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Software activation management / registration 1

Status
Not open for further replies.

djjd47130

Programmer
Nov 1, 2010
480
US
I'm trying to come up with a good way to keep computers registered on a server. I have a SQL database where all the registrations will be saved. This is a server/client system of mine, and I want to make very sure that each client is uniquely registered on the server, they don't get duplicated, and so on. Basically, it's similar to a software activation system, but not quite so secure and strict. However, it does need to be strict enough to make sure every one computer has one registration.

I was just wondering if anyone had any input on the subject.

Now in the database, I have 1 table which holds all the clients. The way I plan on going about this is when the client connects to the server, the first thing the client does is sends its own registration ID to the server. I cannot rely on IP address or host name because those are subject to change. Therefore, I need my own unique ID. So if the client its self knows it's registered, and it knows its unique code, it will send along this code to the server. The server will recognize this code and validate it. If the client is not registered, it will send along a null value to the server, which the server will interpret as needing to register. So at that moment, the server will automatically create a new record in the database, along with a new unique code, and send that code back to the client. The client will from then on use that code when connecting to the server.

This can be interpreted as a Cookie - only cookies can change, they're unique to a session, not to the client. This is also not on the same level of login either - there's a separate login validation process. A different user can login through the same client, but the client always has this same code.

What I need help with is trying to figure out a nifty way to keep track of these clients - as Microsoft keeps track of their products (Windows, Office, SQL, etc.). I just don't need it as complex as a full fledged activation system.

JD Solutions
 
Among other things, this link shows how to get the CPU ID, which should be a unique value for each computer.


Strangely, it paints the results onto a TImage component named img_info. So, the only thing you need to do to test it, is set up a form, place a TImage on it, rename it to img_info, and copy the code.

Another possibility is to use the network card MAC address, but those can be spoofed, not sure if that's the case with CPU ID's...
 
Very clever, thank you. The CPU actually is in fact how most of such systems work, don't know why I didn't think of it before. It's a serial number, and the only way it can change is if someone gets a new processor.

JD Solutions
 
The only thing I'd research is how it works if a computer with more than one CPU, which is getting more and more common, especially for servers.
 
That did work like a charm, except, I find it very hard to believe that my CPU's serial number includes 4 sets of 0's:

0001-XXXX-0000-0000-0000-0000
(XXXX = Hidden)

And that's it. I highly doubt I'm the only person in the world with this serial number - it's either a bug somewhere or somehow ironically just by chance my cpu's serial number just so happens to be that.

That is in fact the answer I was looking for, thanks, just this particular code may not be up to date. Like you mentioned, I'd want to make sure I take all cpu's into account here.

JD Solutions
 
Actually, I realized something else. There's a property just before this serial number string which shows "Serial Number: Disabled". Does this mean it does not have one, or it cannot be properly accessed?

By the way, I wrapped this into a component. Install into any package, it will be in a component tab 'JD Custom' called 'TJDCPUInfo'. Once perfected, I will be posting it in the FAQ...

Code:
unit JDCPUInfo;

interface

uses
  Classes, Windows, SysUtils, StrUtils;

type
  TJDCPUInfo = class(TComponent)
  private
    fMMXSupp: Bool;
    fExtendedCPUID: Bool;
    fSSE2Supp: Bool;
    fFXSupp: Bool;
    fSSESupp: Bool;
    fSerialNumber: Bool;
    fLargestFunction: Integer;
    fExtendedFamily: Integer;
    fProcessorType: Integer;
    fCount: Integer;
    fChunk: Integer;
    fAPICID: Integer;
    fModelNumber: Integer;
    fExtendedModel: Integer;
    fFamilyCode: Integer;
    fBrandID: Integer;
    fSteppingID: Integer;
    fSerialNumberStr: String;
    fBrandString: String;
    fVendorID: String;
    procedure SetAPICID(const Value: Integer);
    procedure SetBrandID(const Value: Integer);
    procedure SetBrandString(const Value: String);
    procedure SetChunk(const Value: Integer);
    procedure SetCount(const Value: Integer);
    procedure SetExtendedCPUID(const Value: Bool);
    procedure SetExtendedFamily(const Value: Integer);
    procedure SetExtendedModel(const Value: Integer);
    procedure SetFamilyCode(const Value: Integer);
    procedure SetFXSupp(const Value: Bool);
    procedure SetLargestFunction(const Value: Integer);
    procedure SetMMXSupp(const Value: Bool);
    procedure SetModelNumber(const Value: Integer);
    procedure SetProcessorType(const Value: Integer);
    procedure SetSerialNumber(const Value: Bool);
    procedure SetSerialNumberStr(const Value: String);
    procedure SetSSE2Supp(const Value: Bool);
    procedure SetSSESupp(const Value: Bool);
    procedure SetSteppingID(const Value: Integer);
    procedure SetVendorID(const Value: String);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property VendorID: String read fVendorID write SetVendorID;
    property SteppingID: Integer read fSteppingID write SetSteppingID;
    property ModelNumber: Integer read fModelNumber write SetModelNumber;
    property FamilyCode: Integer read fFamilyCode write SetFamilyCode;
    property ProcessorType: Integer read fProcessorType write SetProcessorType;
    property ExtendedModel: Integer read fExtendedModel write SetExtendedModel;
    property ExtendedFamily: Integer read fExtendedFamily write SetExtendedFamily;
    property BrandID: Integer read fBrandID write SetBrandID;
    property Chunk: Integer read fChunk write SetChunk;
    property Count: Integer read fCount write SetCount;
    property APICID: Integer read fAPICID write SetAPICID;
    property SerialNumber: Bool read fSerialNumber write SetSerialNumber;
    property SerialNumberStr: String read fSerialNumberStr write SetSerialNumberStr;
    property MMXSupport: Bool read fMMXSupp write SetMMXSupp;
    property FXSupport: Bool read fFXSupp write SetFXSupp;
    property SSESupport: Bool read fSSESupp write SetSSESupp;
    property SSE2Support: Bool read fSSE2Supp write SetSSE2Supp;
    property ExtendedCPUID: Bool read fExtendedCPUID write SetExtendedCPUID;
    property LargestFunction: Integer read fLargestFunction write SetLargestFunction;
    property BrandString: String read fBrandString write SetBrandString;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('JD Custom', [TJDCPUInfo]);
end;

{ TJDCPUInfo }

constructor TJDCPUInfo.Create(AOwner: TComponent);   
var
  _eax, _ebx, _ecx, _edx: Longword;
  i: Integer;
  b: Byte;
  b1: Word;
  s, s1, s2, s3, s_all: string;
begin
  inherited;
  asm
    mov eax,0
    db $0F,$A2
    mov _ebx,ebx
    mov _ecx,ecx
    mov _edx,edx
  end;
  for i := 0 to 3 do begin
    b := lo(_ebx);
    s := s + chr(b);
    b := lo(_ecx);
    s1:= s1 + chr(b);
    b := lo(_edx);
    s2:= s2 + chr(b);
    _ebx := _ebx shr 8;
    _ecx := _ecx shr 8;
    _edx := _edx shr 8;
  end;
  Self.fVendorID:= s + s2 + s1;
  asm
    mov eax,1
    db $0F,$A2
    mov _eax,eax
    mov _ebx,ebx
    mov _ecx,ecx
    mov _edx,edx
  end;
  b := lo(_eax) and 15;
  Self.fSteppingID:= B;
  b := lo(_eax) shr 4;
  Self.fModelNumber:= B;
  b := hi(_eax) and 15;
  Self.fFamilyCode:= B;
  b := hi(_eax) shr 4;
  Self.fProcessorType:= B;
  b := lo((_eax shr 16)) and 15;
  Self.fExtendedModel:= B;
  b := lo((_eax shr 20));
  Self.fExtendedFamily:= B;
  b := lo(_ebx);
  Self.fBrandID:= B;
  b := hi(_ebx);
  Self.fChunk:= B;
  b := lo(_ebx shr 16);
  Self.fCount:= B;
  b := hi(_ebx shr 16);
  Self.fAPICID:= B;
  Self.fSerialNumber:= ((_edx and $40000) = $40000);
  s := IntToHex(_eax, 8);
  asm
    mov eax,3
    db $0F,$A2
    mov _ecx,ecx
    mov _edx,edx
  end;
  s1 := IntToHex(_edx, 8);
  s2 := IntToHex(_ecx, 8);
  Insert('-', s, 5);
  Insert('-', s1, 5);
  Insert('-', s2, 5);
  Self.fSerialNumberStr:= s + '-' + s1 + '-' + s2;
  asm
    mov eax,1
    db $0F,$A2
    mov _edx,edx
  end;
  Self.fMMXSupp:= ((_edx and $800000) = $800000);
  Self.fFXSupp:= ((_edx and $01000000) = $01000000);
  Self.fSSESupp:= ((_edx and $02000000) = $02000000);
  Self.fSSE2Supp:= ((_edx and $04000000) = $04000000);
  asm
    mov eax,$80000000
    db $0F,$A2
    mov _eax,eax
  end;
  Self.fExtendedCPUID:= (_eax > $80000000);
  if fExtendedCPUID then begin
    Self.fLargestFunction:= _eax - $80000000;
    asm
      mov eax,$80000002
      db $0F
      db $A2
      mov _eax,eax
      mov _ebx,ebx
      mov _ecx,ecx
      mov _edx,edx
    end;
    s  := '';
    s1 := '';
    s2 := '';
    s3 := '';
    for i := 0 to 3 do begin
      b := lo(_eax);
      s3:= s3 + chr(b);
      b := lo(_ebx);
      s := s + chr(b);
      b := lo(_ecx);
      s1 := s1 + chr(b);
      b := lo(_edx);
      s2 := s2 + chr(b);
      _eax := _eax shr 8;
      _ebx := _ebx shr 8;
      _ecx := _ecx shr 8;
      _edx := _edx shr 8;
    end;
    s_all := s3 + s + s1 + s2;
    asm
      mov eax,$80000003
      db $0F
      db $A2
      mov _eax,eax
      mov _ebx,ebx
      mov _ecx,ecx
      mov _edx,edx
    end;
    s  := '';
    s1 := '';
    s2 := '';
    s3 := '';
    for i := 0 to 3 do begin
      b := lo(_eax);
      s3 := s3 + chr(b);
      b := lo(_ebx);
      s := s + chr(b);
      b := lo(_ecx);
      s1 := s1 + chr(b);
      b := lo(_edx);
      s2 := s2 + chr(b);
      _eax := _eax shr 8;
      _ebx := _ebx shr 8;
      _ecx := _ecx shr 8;
      _edx := _edx shr 8;
    end;
    s_all := s_all + s3 + s + s1 + s2;
    asm
      mov eax,$80000004
      db $0F
      db $A2
      mov _eax,eax
      mov _ebx,ebx
      mov _ecx,ecx
      mov _edx,edx
    end;
    s  := '';
    s1 := '';
    s2 := '';
    s3 := '';
    for i := 0 to 3 do begin
      b  := lo(_eax);
      s3 := s3 + chr(b);
      b := lo(_ebx);
      s := s + chr(b);
      b := lo(_ecx);
      s1 := s1 + chr(b);
      b  := lo(_edx);
      s2 := s2 + chr(b);
      _eax := _eax shr 8;
      _ebx := _ebx shr 8;
      _ecx := _ecx shr 8;
      _edx := _edx shr 8;
    end;
    if s2[Length(s2)] = #0 then setlength(s2, Length(s2) - 1);
    Self.fBrandString:= s_all + s3 + s + s1 + s2;
  end;
end;

destructor TJDCPUInfo.Destroy;
begin

  inherited;
end;

//IMPORTANT: Do not actually set the values of these properties.
//  It is to display the values in design time in the object inspector.

procedure TJDCPUInfo.SetAPICID(const Value: Integer);
begin

end;

procedure TJDCPUInfo.SetBrandID(const Value: Integer);
begin

end;

procedure TJDCPUInfo.SetBrandString(const Value: String);
begin

end;

procedure TJDCPUInfo.SetChunk(const Value: Integer);
begin

end;

procedure TJDCPUInfo.SetCount(const Value: Integer);
begin

end;

procedure TJDCPUInfo.SetExtendedCPUID(const Value: Bool);
begin

end;

procedure TJDCPUInfo.SetExtendedFamily(const Value: Integer);
begin

end;

procedure TJDCPUInfo.SetExtendedModel(const Value: Integer);
begin

end;

procedure TJDCPUInfo.SetFamilyCode(const Value: Integer);
begin

end;

procedure TJDCPUInfo.SetFXSupp(const Value: Bool);
begin

end;

procedure TJDCPUInfo.SetLargestFunction(const Value: Integer);
begin

end;

procedure TJDCPUInfo.SetMMXSupp(const Value: Bool);
begin

end;

procedure TJDCPUInfo.SetModelNumber(const Value: Integer);
begin

end;

procedure TJDCPUInfo.SetProcessorType(const Value: Integer);
begin

end;

procedure TJDCPUInfo.SetSerialNumber(const Value: Bool);
begin

end;

procedure TJDCPUInfo.SetSerialNumberStr(const Value: String);
begin

end;

procedure TJDCPUInfo.SetSSE2Supp(const Value: Bool);
begin

end;

procedure TJDCPUInfo.SetSSESupp(const Value: Bool);
begin

end;

procedure TJDCPUInfo.SetSteppingID(const Value: Integer);
begin

end;

procedure TJDCPUInfo.SetVendorID(const Value: String);
begin

end;

end.


JD Solutions
 
just by chance my cpu's serial number just so happens to be that.

There's a property just before this serial number string which shows "Serial Number: Disabled". Does this mean it does not have one, or it cannot be properly accessed?

I wouldn't rely on the existence of a CPU serial number at all. Only Pentium IIIs had this feature, which was removed in later processors for privacy reasons within the market. I just noticed as well that the CPU identifier software I have here doesn't report it at all, so it would probably be very smart to forget this.

As one may very well realize, CPU instructions can't be changed around incredibly much or a good amount of software will be broken. So that disabled flag becomes almost the only relevant part of this, if that is even relevant.

It is not possible for anyone to acknowledge truth when their salary depends on them not doing it.
 
Actually, both Intel and AMD have this feature, but doing a bit of research, they are disabled by default.

You can try to get the mac address, which is unique to the network card, but it's not unusual for a computer to have more than one network card, and it can be spoofed.

To get the MAC address,


(line 50, lana[0] indicates to get the MAC address of the first network card, you can change it to get subsequent cards.
 
Makes sense. The MAC address + IP address + computer name + OS version = as unique as I can think, but there's still no guarantee that there won't be any duplication, even though it's highly doubtful and unlikely there will be two matches.

Considering this system of mine is designed for an internal network (not for the world to connect to), I think I can settle with a combination of the MAC and IP, minus the . in the IP and the : in the MAC.

So, if my network were:
- IP: 192.168.4.100
- MAC: 08-00-27-00-10-2C
Then, the code could be:
- 08002700102C1921684100

OR, what about reserving a GUID? I've never ever worked with them, but that's also a large unique string.

It can be randomly generated too, as long as it can be stored somewhere in the registry on the client machine, so when the client connects to the server, it sends this string so the server can validate it.


JD Solutions
 
How are IP addresses assigned to a computer? If it's dynamically, then you won't be able to rely on it being the same every time.
 
Computer name should help as well. That should be unique within a network.
 
OR, what about reserving a GUID? I've never ever worked with them, but that's also a large unique string.

You'd have to read up on them more, but from the short bits that I just read it seems that might be all you need if you're looking just for uniqueness.


Generate one per session/connection and then continue to deal with only those that are "authenticated".

But using ComputerName would probably be easiest for what you are wanting in the long run.

It is not possible for anyone to acknowledge truth when their salary depends on them not doing it.
 
Thank you all for the input. In the end, the client won't be repeatedly reading this value, it's for a one time registration. After registration, the code is saved in the client computer's registry and read from there from then on. In this case, it's more-so that I'm trying to find a scrambled method of creating a randomized string. I could just as easily do randomized numbers, but I'd rather spice things up by randomizing something that's already very unique.

Actually, one extremely unique number would be the precise date/time stamp down to the millisecond, combined with the MAC address.

The goal is that IF a generated code already exists, I want to minimize the number of attempts before finding a non-existent code. In other words, suppose the code generated happens to already exist. It just simply generates some new code and checks that one. Assume it attempts 5 codes before it finally gets a unique one. That's 5 round-trip packets to be passing. I want to minimize that to 1 round-trip. Therefore, if I nail a unique code the first time, I won't have to keep trying new codes.

JD Solutions
 
Actually in my last idea, it would be even better if the Server could generate this code by its self and send it to the client for reference. I may just have to settle with the Date/Time stamp + Client database ID.

By the way, the database ID of the Clients table would be just as sufficient, except it's starting from 1, and I want this code to be long, like 15 characters.

So: It will be the Date/Time stamp plus the Client ID...

Code:
function GetNewCode: String;
begin
  Repeat
    Result:= FormatDateTime('mmddyy-hhnnsszzz-', Now)+IntToStr(ClientID);
  until not CodeExists(Result);
end;


JD Solutions
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top