|
Getting the BIOS serial number
Copyright © 2000 Ernesto
De Spirito
For a simple copy-protection scheme we need to know whether the machine
that is executing our application is the one where it was installed. We
can save the machine data in the Windows Registry when the application
is installed or executed for the first time, and then every time the
application gets executed we compare the machine data with the one we
saved to see if they are the same or not.
But, what machine data should we use and how do we get it? In a past
issue we showed how to get the volume serial number of a logical disk
drive, but normally this is not satisfying for a software developer
since this number can be changed.
A better solution could be using the BIOS serial number. BIOS stands for
Basic Input/Output System and basically is a chip on the motherboard of
the PC that contains the initialization program of the PC (everything
until the load of the boot sector of the hard disk or other boot device)
and some basic device-access routines. Unfortunately, different BIOS
manufacturers have placed the serial numbers and other BIOS information
in different memory locations, so the code you can usually find in the
net to get this information might work with some machines but not with
others. However, most (if not all) BIOS manufacturers have placed the
information somewhere in the last 8 Kb of the first Mb of memory, i.e.
in the address space from $000FE000 to $000FFFFF.
Assuming that "s" is a string variable, the following code
would store these 8 Kb in it:
SetString(s, PChar(Ptr($FE000)), $2000); // $2000 = 8196
We can take the last 64 Kb to be sure we are not missing anything:
SetString(s, PChar(Ptr($F0000)), $10000); // $10000 = 65536
The problem is that it's ill-advised to store "large volumes" of data
in the Windows Registry. It would be better if we could restrict to 256
bytes or less using some hashing/checksum technique. For example we can
use the SHA1 unit (and optionally the Base64 unit)
introduced in the Pascal Newsletter #17.
The code could look like the following:
uses SHA1, Base64;
function GetHashedBiosInfo: string;
var
SHA1Context: TSHA1Context;
SHA1Digest: TSHA1Digest;
begin
// Get the BIOS data
SetString(Result, PChar(Ptr($F0000)), $10000);
// Hash the string
SHA1Init(SHA1Context);
SHA1Update(SHA1Context, PChar(Result), Length(Result));
SHA1Final(SHA1Context, SHA1Digest);
SetString(Result, PChar(@SHA1Digest), sizeof(SHA1Digest));
// Return the hash string encoded in printable characters
Result := B64Encode(Result);
end;
This way we get a short string that we can save in the Windows Registry
without any problems. You can take it as a sort of "BIOS serial number".
As an alternative to using SHA1 and Base64, you
can use any checksum algorithm and binary-to-string conversion function of
your liking. In the example below we use a simple algorithm that gets a
64-bit checksum and finally we convert it to a 16-chars string of
hexadecimal digits:
function GetBiosCheckSum: string;
var
s: int64;
i: longword;
p: PChar;
begin
i := 0;
s := 0;
p := PChar($F0000);
repeat
inc(s, Int64(Ord(p^)) shl i);
if i < 64 then inc(i) else i := 0;
inc(p);
until p > PChar($FFFFF);
Result := IntToHex(s,16);
end;
Displaying the BIOS information
If we wanted to display the BIOS information we should parse the bytes
to extract all null-terminated strings with ASCII printable characters
at least 8-characters length, as it is done in the following function:
function GetBiosInfoAsText: string;
var
p, q: pchar;
begin
q := nil;
p := PChar(Ptr($FE000));
repeat
if q <> nil then begin
if not (p^ in [#10, #13, #32..#126, #169, #184]) then begin
if (p^ = #0) and (p - q >= 8) then begin
Result := Result + TrimRight(String(q)) + #13#10;
end;
q := nil;
end;
end else
if p^ in [#33..#126, #169, #184] then
q := p;
inc(p);
until p > PChar(Ptr($FFFFF));
Result := TrimRight(Result);
end;
Then we can use the return value for example to display it in a memo:
procedure TForm1.FormCreate(Sender: TObject);
begin
Memo1.Lines.Text := GetBiosInfoAsText;
end;
WARNING: The code presented in this
article won't work on Windows NT/2000, although some information about the
BIOS and the system hardware can be found in the Windows Registry under
the key HKEY_LOCAL_MACHINE\Hardware\Description\System, but
not enough to identify a machine as far as I know...
You can find the full source code of this article in the archive that accompanies the Pascal Newsletter #20
|