31 julio 2009

El componente mxProtector (1)

Un asunto importante que no debemos descuidar en la distribución de nuestros programas es la protección de los mismos frente a las copias piratas. Para ello hay infinidad de herramientas que permiten proteger el software por número de ejecuciones, por fecha límite, activación por número de serie, etc.

Si no queremos complicarnos mucho la vida hay herramientas que permiten proteger un ejecutable una vez compilado como pueden ser Armadillo, ASProtect, ExeCryptor, Enigma Protector, IntelliProtector, etc. Pero casi todas ellas (sobre todo las más buenas) son comerciales y ninguna es perfecta. Un buen crackeador puede reventar cualquiera de ellas utilizando ingeniería inversa mediante desensambladores y dumpeadores de memoria avanzados.

Si os interesa investigar sobre seguridad informática hay una página web en la que también tiene código fuente de Delphi dedicada a este tema:

http://indetectables.net

Hablan sobre todo de cómo crear troyanos, virus, protectores de archivos EXE o como protegerse contra los mismos. Sobre todo hay que fijarse en el foro.

DESCARGANDO EL COMPONENTE

Lo que si podemos hacer es crear una protección a nuestro programa que sin ser muy compleja por lo menos evite que cualquier principiante pueda copiarlo. Para ello vamos a ver el componente mxProtect que cumple este cometido a la perfección. La versión actual es la 1.32 (a la fecha de escribir este artículo).

Este componente lo podemos encontrar en esta página web:

http://www.maxcomponents.net/

Tiene algunos componentes comerciales y otros con licencia freeware. Es este caso, el componente mxProtect es freeware y lo podemos descargar seleccionando en su página web el apartado Downloads -> Freeware Components:


El archivo que nos bajamos es la instalación comprimida con zip que tiene un tamaño de 355 KB. Es la típica instalación de siempre donde nos pedirá donde instalar el componente (por defecto en Archivos de programa):

Yo tengo por costumbre instalar los componentes en una carpeta debajo de cada versión de Delphi, por ejemplo:

De este modo, si vamos a utilizar el componente en dos versiones distintas de Delphi (en mi caso Delphi 7 y Delphi 2007) lo mejor es que cada una tenga su copia del componente para que se compilen por separado, es decir, instalamos por ejemplo el componente mxProtector dentro de la carpeta:

D:\RadStudio2007\Componentes\mxProtector\

Y luego le sacamos una copia a mano a la carpeta:

D:\Delphi7\Componentes\mxProtector\

De este modo, cada versión del compilador no estropeará el componente de la otra.

INSTALANDO EL COMPONENTE

Una vez que lo tenemos instalado debemos añadirlo a Delphi 2007 abriendo el paquete mxProtector_11.dpk. En caso de que sea Delphi 2009 seria mxProtector_12.dpk. En la ventana del Project Manager pinchamos el archivo mxProtector_11.bpl con el botón derecho del ratón y seleccionamos Compile:


Después seleccionamos Install:


y nos aparecerá el mensaje de que acaba de instalar el componente:

Al abrir o crear un nuevo proyecto veremos ese componente en la paleta:


Para Delphi 7 sería prácticamente lo mismo.

PROTEGER UN PROGRAMA POR EL NÚMERO DE EJECUCIONES

Cada vez que se ejecute el programa restará una vez al contador de número de ejecuciones en ese PC. Vamos a ver un ejemplo de cómo crear un programa que permita ejecutarse 3 veces.

Antes tengo que aclarar una cosa. Este componente tiene dos modos de ejecución: o él se encarga de guardar (dios sabe donde) el número de ejecuciones o nosotros nos encargamos de decirle donde tiene que guardar estos datos (en un archivo INI, en un archivo binario, en el registro del sistema, etc.).

Yo prefiero esta segunda opción, ya que aunque es más pesada de implementar nos da más libertad a la hora de proteger nuestro software. Este será el método que yo voy a utilizar, concretamente lo voy a guardar en el registro del sistema dentro de la clave:

\HKEY_LOCAL_MACHINE\MiEmpresa\MiPrograma\

Por lo demás le dejamos al componente mxProtector que guarde en esa clave lo que tenga que guardar.

Para probar este ejemplo voy a comenzar un nuevo proyecto que tenga este sencillo formulario:

Al componente de la clase TMXProtector lo he llamado mxProtector y a la etiqueta que va a mostrar el número de ejecuciones que nos quedan la he llamado ENumEje.

Para ello vamos a realizar estas acciones:

1º Ponemos la propiedad MaxStartNumber del componente mxProtector a 3.

2º En el mismo componente activamos sólo el valor stStartTrial de su propiedad ProtectionType. Esto hará que se al arrancar nuestro programa se ejecute el evento OnStartTrial.

2º En el evento OnStartTrial podemos el código que se va a ejecutar por primera vez:

procedure TFPrincipal.mxProtectorStartTrial(Sender: TObject;
StartsRemained: Integer);
begin
ENumEje.Caption := Format('Te quedan %d ejecuciones de %d',
[StartsRemained, mxProtector.MaxStartNumber]);
end;

Este código se ejecutará automáticamente al inicio del programa, ya que está activada la opción poAutoInit dentro de la propiedad Options.

2º Al pulsar el botón Reiniciar ponemos a cero en número de veces que hemos ejecutado el programa:

mxProtector.Reset;

Podemos hacer que el componente se encargue de guardar ese número por si mismo o bien nos encargamos nosotros de todo el proceso (lo mejor). Como vamos a guardar en número de ejecuciones en el registro el sistema entonces haremos que en el evento OnReset elimine la clave del registro:

procedure TFPrincipal.mxProtectorReset(Sender: TObject; var Handled: Boolean);
var
Reg: TRegistry;
begin
Handled := True;
Reg := TRegistry.Create;
Reg.RootKey := HKEY_LOCAL_MACHINE;
Reg.DeleteKey('\Software\MiEmpresa\MiPrograma');
Reg.Free;
end;

Si ponemos a True la variable Handled le estamos diciendo al componente mxProtector que nosotros nos encargamos de guardar el contador de ejecuciones. Si la ponemos a false se encarga él. Lo que no sé es donde la guarda. Lo he estado monitorizando con el programa RegMon en el registro del sistema y no he encontrado donde lo mete.

3º En el evento OnExpiration debemos añadir el código de lo que queramos que haga cuando se finalicen el número de ejecuciones (se acabe la versión trial):

procedure TFPrincipal.mxProtectorExpiration(Sender: TObject);
begin
ENumEje.Caption := 'Ya no te quedan más ejecuciones';
end;

Si por ejemplo estamos haciendo un programa de facturación podíamos cambiar o eliminar la contraseña de acceso al motor de bases de datos para que no pueda volver a funcionar el programa.

Este tipo de protección hace que este componente cargue y grabe una variable tipo booleana y otra de texto. Nosotros nos vamos a encargar de guardar y recoger estas variables. Así que vamos a reprogramar los eventos que necesitamos:

4º En el evento OnGetBoolean leemos la variable booleana que mxProtector nos pida:

procedure TFPrincipal.mxProtectorGetBoolean(Sender: TObject; var APath,
AKey: string; var AResult, Handled: Boolean);
var
Reg: TRegistry;
begin
Reg := TRegistry.Create;
Reg.RootKey := HKEY_LOCAL_MACHINE;
try
if Reg.OpenKey('\Software\MiEmpresa\MiPrograma', True) then
if Reg.ValueExists(AKey) then
AResult := Reg.ReadBool(AKey);

Handled := True;
Reg.CloseKey;
finally
Reg.Free;
end;
end;

Al igual que antes, le ponemos la variable Handled a True para decirle al componente que nos encargamos del asunto.

5º Lo mismo hacemos para cargar una variable de tipo string en el evento OnGetString:

procedure TFPrincipal.mxProtectorGetString(Sender: TObject; var APath, AKey,
AResult: string; var Handled: Boolean);
var
Reg: TRegistry;
begin
Reg := TRegistry.Create;
Reg.RootKey := HKEY_LOCAL_MACHINE;
try
if Reg.OpenKey('\Software\MiEmpresa\MiPrograma', True) then
if Reg.ValueExists(AKey) then
AResult := Reg.ReadString(AKey);

Handled := True;
Reg.CloseKey;
finally
Reg.Free;
end;
end;

6º Ahora hacemos lo mismo para guardar una variable booleana en el evento OnPutBoolean:

procedure TFPrincipal.mxProtectorPutBoolean(Sender: TObject; var APath,
AKey: string; var ASavedData, Handled: Boolean);
var
Reg: TRegistry;
begin
Handled := True;
Reg := TRegistry.Create;
Reg.RootKey := HKEY_LOCAL_MACHINE;
if Reg.OpenKey('\Software\MiEmpresa\MiPrograma', True) then
begin
Reg.WriteBool(AKey, ASavedData);
Reg.CloseKey;
end;
Reg.Free;
end;

7º Y los mismo para una variable string en el evento OnPutString:

procedure TFPrincipal.mxProtectorPutString(Sender: TObject; var APath, AKey,
ASavedData: string; var Handled: Boolean);
var
Reg: TRegistry;
begin
Handled := True;
Reg := TRegistry.Create;
Reg.RootKey := HKEY_LOCAL_MACHINE;
if Reg.OpenKey('\Software\MiEmpresa\MiPrograma', True) then
begin
Reg.WriteString(AKey, ASavedData);
Reg.CloseKey;
end;
Reg.Free;
end;

8º Por último sólo nos queda reprogramar los eventos OnCodeData y OnDecodeData:

procedure TFPrincipal.mxProtectorCodeData(Sender: TObject; var ACode: string);
begin
ACode := ACode;
end;

procedure TFPrincipal.mxProtectorDeCodeData(Sender: TObject; var ACode: string);
begin
ACode := ACode;
end;

Ahora mismo no tienen ningún tipo de codificación. Lo mismo que leen o cargan del registro del sistema es lo que va a parar al componente. Aquí podíamos ampliar la funcionalidad utilizando alguna función de encriptación y desencriptación como suele hacerse comúnmente con las funciones booleanas XOR, aunque esto se sale de los objetivos de este artículo.

Después de todo este rollo que os he metido, vamos a ejecutar el programa para ver que guarda en el registro:

Cuando se ejecuta el programa automáticamente nos resta el número de ejecuciones (eso lo hace sólo el componente) y nos guarda esto en el registro (hacer clic para ampliar):

Cerramos el programa y volvemos a abrirlo y nos queda una ejecución:

Y en el registro vemos que sólo cambia el valor S2:

Ejecutamos por última vez el programa y se acaban el nº de ejecuciones:

Y el estado del valor S2 vuelve a cambiar:


El contenido del registro puede cambiar dependiendo del PC, de la fecha y hora del sistema o de algún patrón interno del componente mxProtector. Nosotros no tenemos que preocuparnos por eso. Lo más que podemos hacer es modificar los eventos OnCodeDate y OnDecodeDate para despistar aun más a los crackers.

También podíamos encriptar el ejecutable con algún compresor de archivos EXE tipo UPX para evitar la ingenieria inversa. Aún así, ningún sistema de protección es perfecto, pero por lo menos da algo más de seguridad frente a los crackeadores novatos.

En el siguiente artículo seguiremos viendo los otros métodos de protección que incorpora este componente.

Pruebas realizadas en RAD Studio 2007.

Publicidad