Voy a tratar de explicar como proteger una app encriptando una función vital para el programa. La clave de descifrado dependerá de la clave de la licencia.
Imaginemos un código como este en el que una función es vital porque importa dinámicamente un procedimiento esencial de una dll:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Edit1: TEdit; Button1: TButton; procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; type PLogueado = procedure; stdcall; implementation {$R *.dfm} var Logueado: PLogueado; procedure Vital; var hLib: HMODULE; begin hLib:= LoadLibrary('UnaDll.dll'); Logueado:= GetProcAddress(hLib, 'Logueado'); if @Logueado <> nil then Logueado; end;} procedure TForm1.FormCreate(Sender: TObject); begin Vital; end; end.
Para el ejemplo, esta será la dll:
library UnaDll; uses Windows; {$R *.res} procedure Logueado; begin MessageBox(0, 'El programa funcionará correctamente', 'Eureca', MB_OK); end; exports Logueado; begin end.
Ahora vamos a cambiar las cosas para encriptar el procedimiento Vital. Añadimos una función o procedimiento de cifrado, otro de descifrado y otro para guardar el binario cifrado de Vital. El procedimiento FinVital hay que dejarlo justo al final de Vital:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Edit1: TEdit; Button1: TButton; procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; type PLogueado = procedure; stdcall; implementation {$R *.dfm} var Logueado: PLogueado; // Una función de cifrado simétrico... procedure Crypt(Source: Pointer; Size: Cardinal; Password: PCHAR; _Mod: integer); var S: PCHAR; len, n: integer; begin S:= Source; len:= lstrlen(Password); for n:=0 to Size-1 do begin S[n]:= CHAR(integer(S[n]) xor integer(Password[_Mod mod len])); inc(_Mod); end; end; procedure Vital; var hLib: HMODULE; begin hLib:= LoadLibrary('UnaDll.dll'); Logueado:= GetProcAddress(hLib, 'Logueado'); if @Logueado <> nil then Logueado; end;} // Este procedimiento inservible nos ayudará a encontrar el tamaño asm de Vital procedure FinVital; begin end; // Este procedimiento copia Vital en un buffer, lo encripta y lo guarda en un archivo binario. procedure ExtraeVital; var hFile: THANDLE; Size: integer; Buffer: PBYTE; begin hFile:= CreateFile('Vital.bin', GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH, 0); if hFile <> INVALID_HANDLE_VALUE then begin //Calculamos el tamaño de Vital Size:= UINT(@FinVital) - UINT(@Vital); Buffer:= GetMemory(Size); CopyMemory(Buffer, @Vital, Size); Crypt(Buffer, Size, 'escafandra', 0); _lwrite(hFile, PAnsiChar(Buffer), Size); CloseHandle(hFile); FreeMemory(Buffer); end; end; // Descifra Vital en su propia localización procedure DescifraVital; var Size: integer; OldProtect: DWORD; begin //Calculamos el tamaño de Vital Size:= UINT(@FinVital) - UINT(@Vital); VirtualProtectEx(DWORD(-1), @Vital, Size, PAGE_EXECUTE_READWRITE, @OldProtect); Crypt(@Vital, Size, PCHAR(Form1.Edit1.Text), 0); VirtualProtectEx(DWORD(-1), @Vital, Size, OldProtect, nil); end; procedure TForm1.FormCreate(Sender: TObject); begin ExtraeVital; DescifraVital; Vital; end; end.
Ahora, con ayuda de alguna app que nos pase un binario a texto (FileToCode) hardcodeamos Vital con el resultado obtenido:
unit Vital_; interface uses Windows; var Vital_Size: integer = 80; Vital_Bytes: array [0..79] of BYTE = ( $30, $F8, $8F, $30, $0E, $65, $9F, $20, $72, $89, $2B, $1D, $98, $9E, $EF, $24, $92, $0C, $62, $90, $21, $73, $E8, $24, $9A, $31, $86, $C1, $1F, $9A, $9A, $D0, $BB, $4A, $23, $61, $ED, $59, $AA, $4A, $20, $73, $63, $15, $60, $9E, $7B, $BC, $59, $24, $65, $2A, $3E, $A2, $66, $61, $3B, $0A, $13, $25, $09, $1F, $4D, $05, $0A, $0D, $6E, $64, $3E, $0E, $02, $06, $06, $00, $02, $0E, $6E, $64, $72, $61 ); implementation end.
El procedimiento VItal quedará como sigue:
procedure Vital; asm db $30, $F8, $8F, $30, $0E, $65, $9F, $20, $72, $89, $2B, $1D, $98, $9E, $EF, $24; db $92, $0C, $62, $90, $21, $73, $E8, $24, $9A, $31, $86, $C1, $1F, $9A, $9A, $D0; db $BB, $4A, $23, $61, $ED, $59, $AA, $4A, $20, $73, $63, $15, $60, $9E, $7B, $BC; db $59, $24, $65, $2A, $3E, $A2, $66, $61, $3B, $0A, $13, $25, $09, $1F, $4D, $05; db $0A, $0D, $6E, $64, $3E, $0E, $02, $06, $06, $00, $02, $0E, $6E, $64, $72, $61; end;
Solo quede hacer tres cosas, sustituir el el código fuente Vital por su nueva implementación "justo en su mismo lugar", trucar ExtraeVital para que no guarde archivos y cambiar la cadena de cifrado por otra cosa de un carácter, ¡no debemos dejarla expuesta!. Este paso es crucial, no debemos alterar mucho el código porque hay que recordar los saltos relativos asm. Cuando recompilemos al gún salto o llamada call puede cambiar su dirección relativa, por lo tanto el código final no puede alterarse mucho.Hay que tener cuidado de dejar todos los procedimientos donde están:
Este es el resultado final:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Edit1: TEdit; Button1: TButton; procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; type PLogueado = procedure; stdcall; implementation {$R *.dfm} var Logueado: PLogueado; procedure Crypt(Source: Pointer; Size: Cardinal; Password: PCHAR; _Mod: integer); var S: PCHAR; len, n: integer; begin S:= Source; len:= lstrlen(Password); for n:=0 to Size-1 do begin S[n]:= CHAR(integer(S[n]) xor integer(Password[_Mod mod len])); inc(_Mod); end; end; procedure Vital; asm db $30, $F8, $8F, $30, $0E, $65, $9F, $20, $72, $89, $2B, $1D, $98, $9E, $EF, $24; db $92, $0C, $62, $90, $21, $73, $E8, $24, $9A, $31, $86, $C1, $1F, $9A, $9A, $D0; db $BB, $4A, $23, $61, $ED, $59, $AA, $4A, $20, $73, $63, $15, $60, $9E, $7B, $BC; db $59, $24, $65, $2A, $3E, $A2, $66, $61, $3B, $0A, $13, $25, $09, $1F, $4D, $05; db $0A, $0D, $6E, $64, $3E, $0E, $02, $06, $06, $00, $02, $0E, $6E, $64, $72, $61; end; { procedure Vital; var hLib: HMODULE; begin hLib:= LoadLibrary('UnaDll.dll'); Logueado:= GetProcAddress(hLib, 'Logueado'); if @Logueado <> nil then Logueado; end; } procedure FinVital; begin end; procedure ExtraeVital; var hFile: THANDLE; Size: integer; Buffer: PBYTE; begin hFile:= 0; // Un Handle de archivo nulo evita que se ejecute este procedimiento CreateFile('Vital.bin', GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH, 0); if hFile <> INVALID_HANDLE_VALUE then begin //Calculamos el tamaño de Vital Size:= UINT(@FinVital) - UINT(@Vital); Buffer:= GetMemory(Size); CopyMemory(Buffer, @Vital, Size); Crypt(Buffer, Size, '?', 0); _lwrite(hFile, PAnsiChar(Buffer), Size); CloseHandle(hFile); FreeMemory(Buffer); end; end; procedure DescifraVital; var Size: integer; OldProtect: DWORD; begin //Calculamos el tamaño de Vital Size:= UINT(@FinVital) - UINT(@Vital); VirtualProtectEx(DWORD(-1), @Vital, Size, PAGE_EXECUTE_READWRITE, @OldProtect); Crypt(@Vital, Size, PCHAR(Form1.Edit1.Text), 0); VirtualProtectEx(DWORD(-1), @Vital, Size, OldProtect, nil); end; procedure TForm1.FormCreate(Sender: TObject); begin ExtraeVital; DescifraVital; Vital; end; end.
No hay condicionales, sólo una dependencia. Si Vital no es desencriptada correctamente la app dará una excepción o se colgará. Vital no se ejecutará alterando definitivamente el programa.
Para implementarlo en una app, habra que hacerlo en la versión liberada y probar que funciona.
Recordar que si falla es que no hemos tenido cuidado con los JMP y CALL relativos.
La fortaleza de la protección la dará el sistema de cifrado ejegido, yo he usado para el ejemplo un método simétrico XOR, no es el más apropiado pero para el ejemplo sirve.
Ahora os reto a cojer el ejecutable y crakearlo para que funcione la función Vital. No valen las trampas, sólo se puede usar el ejecutable.
Espero que os sirva u os de nuevas ideas para vuestras protecciones.
Subo el ejemplo que he escrito.
Saludos.