¡El número
de serie de nuestra BIOS!
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é...
Enviado por : Ernesto Sequeira,
El truco me lo envio un profesor universitario
de España. |