|
Obteniendo el número de serie del BIOS
Copyright © 2000 Ernesto
De Spirito
Para un esquema simple de protección de copia necesitamos saber si la
máquina en que está ejecutándose nuestra aplicación es la misma donde
fue instalada. Podemos guardar los datos de la máquina en el Registro de
Windows cuando la aplicación sea instalada o ejecutada por primera vez,
y luego cada vez que la aplicación se ejecute comparamos los datos de la
máquina con el que hemos guardado para ver si son iguales o no.
Pero, ¿qué datos de la máquina deberíamos usar y cómo los obtenemos? En
una edición pasada mostramos cómo obtener el número de serie del volumen
de una unidad de disco lógica, pero normalmente esto no es satisfaciente
para un desarrollador de software dado que este número puede ser
cambiado.
Una solución mejor podría ser utilizar el número de serie del BIOS. BIOS
significa Sistema Básico de Entrada/Salida (Basic Input/Output
System), y básicamente es un chip en la placa madre de la PC que contiene
el programa de inicialización de la PC (todo hasta la carga del sector de
arranque del disco u otro dispositivo de arranque) y algunas rutinas
básicas de acceso a dispositivos. Desafortunadamente, los distintos
fabricantes de BIOS han puesto los números de serie y otra información
del BIOS en diferentes posiciones de memoria, así que el código que uno
generalmente puede encontrar en la red para acceder a esta información
puede que funcione con algunas máquinas pero no con otras. Sin embargo,
la mayoría (si no todos) los fabricantes de BIOS han puesto la información
en alguna parte en los último 8 Kb del primer Mb de memoria, es decir en
el espacio de direcciones de $000FE000 a $000FFFFF.
Si asumimos que "s" es una variable string, el código siguiente
guardaría esos 8 Kb en ella:
SetString(s, PChar(Ptr($FE000)), $2000); // $2000 = 8196
Podemos tomar los últimos 64 Kb para estar seguros que no nos perdemos
de nada:
SetString(s, PChar(Ptr($F0000)), $10000); // $10000 = 65536
El problema es que no se recomienda guardar "grandes volúmenes" de datos
en el Registro de Windows. Sería mejor si pudiéramos restringirnos a 256
bytes o menos usando alguna técnica de hashing/checksum. Por ejemplo
podemos usar la unidad SHA1 y opcionalmente la
unidad Base64 introducidas en el Boletín Pascal #17. El código se podría
parecer al siguiente:
uses SHA1, Base64;
function GetHashedBiosInfo: string;
var
SHA1Context: TSHA1Context;
SHA1Digest: TSHA1Digest;
begin
// Obtener los datos del BIOS
SetString(Result, PChar(Ptr($F0000)), $10000);
// Obtener el hash
SHA1Init(SHA1Context);
SHA1Update(SHA1Context, PChar(Result), Length(Result));
SHA1Final(SHA1Context, SHA1Digest);
SetString(Result, PChar(@SHA1Digest), sizeof(SHA1Digest));
// Devolver el hash codificado en caracteres imprimibles
Result := B64Encode(Result);
end;
De esta forma obtenemos una cadena corta que podemos guardar en el
Registro de Windows sin problemas. La puede considerar como una especie
de "número de serie del BIOS".
Como alternativa al uso de SHA1 y Base64,
puede usar el algoritmo de suma de verificación y la función de
conversión de binario a cadena que más le guste. En el siguiente
ejemplo usamos un simple algoritmo que calcula una suma de
verificación de 64 bits y finalmente la convertimos en una cadena
de 16 caracteres con dígitos hexadecimales:
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;
Mostrando la información del BIOS
Si quisiéramos mostrar la información del BIOS debemos recorrer los
bytes para extraer todas las cadenas terminadas en nulo con caracteres
ASCII imprimibles de por lo menos 8 caracteres de longitud, como se hace
en la siguiente función:
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;
Luego podemos utilizar el valor de retorno para por ejemplo mostrarlo en
un memo:
procedure TForm1.FormCreate(Sender: TObject);
begin
Memo1.Lines.Text := GetBiosInfoAsText;
end;
ATENCION: El código presentado en
este artículo no funcionará en Windows NT/2000, aunque alguna información
sobre la BIOS y el hardware del sistema se puede encontrar en el Registro
de Windows bajo la clave HKEY_LOCAL_MACHINE\Hardware\Description\System,
pero no la suficiente como para identificar una máquina hasta donde yo sé...
Puede encontrar el código fuente completo de este artículo en el archivo que acompaña al Boletín Pascal #20.
|