ESTUDIO COLECTIVO DE DESPROTECCIONES
Última Actualizacion: 25/10/2001

Programa 1stClass 1.01
Dream Collection 3.1
LMD-Tools 4.51
Multimedia Tools 1.7
OaAgent Component Set
SMExport Component Suite 3.50
SMS Driver 1.0
TUsersSTD 1.0
VCLZip 2.20
W95 / W98 / NT 
Descripción Componentes o colecciones de componentes VCL para Borland/Inprise Delphi.
Tipo Trial
Tipo de Tutorial

[X]Original, []Adaptación, []Aplicación, []Traducción

(de todo un poco ... lo dejamos en 3.5 ;-)

Url ftp://ftp.woll2woll.com/pub/wol2wol/1stclass/Trial1stclass_d5.exe
ftp://ftp.woll2woll.com/pub/wol2wol/1stclass/Demo1st.exe
http://www.dream-com.com/Download/trials/dccol_d5.exe
http://www.webwatson.de/download/files/lmdt4_d4.exe
http://www.swiftsoft.de/files/mmtool5.exe
http://www.swiftsoft.de/files/mmdemos.exe
http://www.swiftsoft.de/files/mmdocs.exe
http://www.o2a.com/downloads/agntd5e.exe
http://www.torry.css.pl/vcl/database/smetrial.zip
http://www.torry.css.pl/vcl/comms/vnsmsdriver.zip
http://www.toolsandcomps.com/download/usersstd.zip
http://vclzip.bizland.com/kp220_d5.exe
http://vclzip.bizland.com/kpdemosd.exe
http://vclzip.bizland.com/helpfile.exe
Protección Los programas que incluyan alguno de los componentes sólo funcionan cuando el IDE de Delphi está en ejecución.
Dificultad 1) Principiante, 2) Amateur, 3) Aficionado, 4) Profesional, 5) Especialista 
Herramientas Depurador: SoftICE 4.01 NT
Desensambladores: W32Dasm 8.93, IDA Pro 3.7
Editor hexadecimal: Cygnus binary file editor 1.02
Buscador multi-fichero de cadenas hexadecimales: LookDisk 1.51
Visor de procesos: ProcDump 1.6
Espía de ventanas: Spy32 2.70 for Win9x/NT
Memory dumper (aaargh, ¿cómo se traduce?): nticedump 1.6
Compiladores: Borland/Inrpise Delphi 4.03, Borland/Inprise Delphi 5
Música ambiente: My Dying Bride "The light at the end of the world", Children of Bodom "Hatebreeder", Dissection "Storm of the light's bane"
Estimulantes: yogur de frutas, zumo de naranja, zumo de melocotón ;-)
Y, como siempre ...  ilusión, paciencia y un poco de cerebro.
Objetivo Mostrar el funcionamiento de uno de los tipos de protección más utilizados por los programadores de componentes VCL:  "works only when IDE is running" (sólo funciona cuando el entorno de desarrollo está en ejecución).
Cracker +Aitor
Grupo Whiskey Kon Tekila
Fecha Febrero/Marzo 2000
 


INTRODUCCION
Son muchas, y cada vez más, las ocasiones en las que nos encontramos con desarrolladores de módulos para otros desarrolladores (léase librerías, controles, componentes, etc.) que,  para proteger su trabajo de un uso ilegal, recurren al sistema que hoy nos ocupa.  Dependiendo del entorno de desarrollo (IDE) en el que utilicemos dichos componentes, las técnicas varían, aunque en algunos casos son muy parecidas.  En esta ocasión, comentaremos la que con más frecuencia nos podemos encontrar cuando el protagonista sea un componente escrito para Delphi:  “works only when IDE is running” (sólo funciona cuando el IDE está en ejecución).

Para ilustrar el tema con ejemplos prácticos, nos hemos dado una vuelta por uno de los repositorios de mayor audiencia, la Torry's Page, y hemos seleccionado unos cuantos productos VCL (componentes o paquetes de componentes) shareware, compatibles con Delphi 4 o 5, que decían estar limitados con este tipo de protección.

Antes de continuar, recuerda en todo momento que este tutorial NO está pensado para enseñarte a robar, sino para ofrecerte otra cara más del mundo de las protecciones de software y, en el caso de que seas programador de este tipo de material, darte ideas para mejorar la codificación de tus protecciones en un futuro.

Los productos anteriormente citados han sido elegidos al azar, intentando coger un poco de todo para podernos hacer una idea más o menos fiable de cuál es el panorama actual en lo que se refiere a protecciones de este tipo.  Los hay buenos y los hay menos buenos, los hay muy caros y los hay muy baratos, y sus funciones varían desde la programación de aplicaciones multimedia hasta la transmisión de mensajes cortos.

Y un mensaje para los autores “afectados”.  Aunque haya algún que otro estúpido que opine lo contrario, soy de la opinión de que tutoriales como éste no sólo no perjudican a los autores de shareware, sino que en cualquier caso suponen una ayuda.  Si tu producto es bueno y competitivo no tienes nada que temer, aunque si no lo es, tal vez deberías estar ahora invvirtiendo el tiempo en mejorarlo en lugar de “perder” el tiempo leyendo este documento.

Y, por supuesto, otra nota para los lamers caza-cracks.  Aunque las protecciones de todos los productos mencionados están concienzudamente detalladas en este documento, no esperes encontrar ni un solo parche para ninguno de ellos, ni en un archivo adjunto a este texto ni en la sección de cracks de WKT.  Bastardos como vosotros son los que nos hacen la vida menos agradable a los +crackers honrados.

Sin más, empezamos.

LA BASE TEORICA. BUSCANDO VENTANAS
Es la base del tipo de protección más habitual, asegurarse de que el componente sólo funciona desde dentro del entorno de desarrollo (IDE) de Delphi.  Consiste en buscar mediante llamadas a FindWindow o FindWindowEx, ventanas características de presencia obligada cuando el IDE de Delphi está arrancado.  Arrancamos el IDE de Delphi 4, cerramos todos los proyectos y, con ayuda de nuestro espía favorito,  obtenemos lo siguiente:


Algo muy parecido obtenemos con el de Delphi 5:


Lo que aparece en estas capturas es, de izquierda a derecha, el icono, el handle, el título y el nombre de la clase, de cada ventana presente, visible o no visible, cuando el IDE está funcionado.  Sin fijarnos mucho, nos damos cuenta de que hay ventanas significativas comunes, y es precisamente su presencia la que utiliza el programador para saber si su componente está siendo utilizado desde dentro de Delphi o no, aunque no sea esta la manera más exacta de hacerlo, veremos más adelante el porqué.

Ilustraremos lo dicho anteriormente con algunos ejemplos ...

LOS PEREZOSOS
De los 9 productos estudiados para la redacción de este tutorial, 6 caen en esta categoría.  En este caso, el programador se ha limitado a chequear, mediante llamadas consecutivas a FindWindow la presencia de una o más de las “ventanas obligadas”.  Son un ejemplo de cómo NO hay que proteger un programa.  Vamos con ellos ...
Dream Collection 3.1, © 1997-2000 Dream Company
Lo primero que hacemos, tras la instalación, es arrancar Delphi 5 y compilar el proyecto de ejemplo H:\Dream\demo\common\d5\project1.dpr.  Obtenemos un ejecutable, project1.exe, de 2.689.024 bytes.

Aunque no hay nada en la documentación que lo diga notamos que, cuando ejecutamos el programa con Delphi arrancado, todo va perfecto.  Sin embargo, cuando no está Delphi presente, el programa nos muestra la siguiente ventana al arrancar:


Un vistazo al código desensamblado nos da lo siguiente:
 

:4721C0 803D00BF5F0000  cmp byte ptr [5FBF00], 0  ; Flag = FALSE
:4721C7 7523            jne 4721EC                ; nos vamos
:4721C9 C60500BF5F0001  mov byte ptr [5FBF00], 1  ; Flag = TRUE
:4721D0 E80FFBFFFF      call 471CE4               ; ¿está Delphi presente?
:4721D5 84C0            test al, al
:4721D7 7513            jne 4721EC                ; sí, nos vamos
:4721D9 6A30            push 30                   ; no, mensaje de aviso
:4721DB 68F0214700      push 4721F0               ; "Warning"
:4721E0 68F8214700      push 4721F8               ; "This application uses ..."
:4721E5 6A00            push 0
:4721E7 E8CC6EF9FF      call 4090B8               ; MessageBoxA
:4721EC C3              ret

No hace falta mucha imaginación para darse cuenta del papel que juega el flag apuntado por [5FBF00], ¿verdad?  Pero vamos a lo que nos interesa, cómo se produce el chequeo de la presencia de Delphi:
 

:471CE4 53          push ebx
:471CE5 56          push esi
:471CE6 6A00        push 0
:471CE8 68241D4700  push 471D24     ; "TAlignPalette"
:471CED E84671F9FF  call 408E38     ; FindWindowA
:471CF2 8BD8        mov ebx, eax
:471CF4 6A00        push 0
:471CF6 68341D4700  push 471D34     ; "TPropertyInspector"
:471CFB E83871F9FF  call 408E38     ; FindWindowA
:471D00 8BF0        mov esi, eax
:471D02 6A00        push 0
:471D04 68481D4700  push 471D48     ; "TAppBuilder"
:471D09 E82A71F9FF  call 408E38     ; FindWindowA
:471D0E 85DB        test ebx, ebx
:471D10 7408        je 471D1A
:471D12 85F6        test esi, esi
:471D14 7404        je 471D1A
:471D16 85C0        test eax, eax
:471D18 7505        jne 471D1F
:471D1A 33C0        xor eax, eax
:471D1C 5E          pop esi
:471D1D 5B          pop ebx
:471D1E C3          ret

No nos podemos ir sin rematar la faena, y un parche elegante podría ser el siguiente:
 

:4721C0 C60500BF5F0000  mov byte ptr [5FBF00], 0  ; Flag = FALSE, por si acaso
:4721C7 EB23            jmp 4721EC                ; nos vamos siempre

Como siempre, el origen de la protección lo hallaremos en los archivos .DCU, localizándolo esta vez en el fichero dclib.dcu.

LMD-Tools 4.51, ©1995-1999 LMD Innovative
Lo primero que hacemos, tras la instalación, es arrancar Delphi 4 y compilar el ejemplo que acompaña al paquete.  Se trata del proyecto C:\LMD40\DEMOS\delphi\NewMegaDemo.dpr que,  una vez compilado, nos genera el ejecutable NewMegaDemo.exe, de 5.381.632 bytes, que será el ejecutable sobre el que trabajaremos.

Nada más ejecutarlo (una vez apagado Delphi) aparece la siguiente ventana:


Nos tiramos a por un “crackeo muerto” y,  tras unos minutos de paciente espera,  W32Dasm nos regala un fichero de 35 Mb del cual rápidamente extraemos esta hermosura:
 

:466CA0 55          push ebp
:466CA1 8BEC        mov ebp, esp
:466CA3 6A00        push 0
:466CA5 6A00        push 0
:466CA7 6A00        push 0
:466CA9 33C0        xor eax, eax
:466CAB 55          push ebp
:466CAC 68D46D4600  push 466DD4
:466CB1 64FF30      push dword ptr fs:[eax]
:466CB4 648920      mov dword ptr fs:[eax], esp
:466CB7 C645F700    mov [ebp-9], 0               ; contador de ventanas encontradas
:466CBB 33C0        xor eax, eax
:466CBD 55          push ebp
:466CBE 68B26D4600  push 466DB2
:466CC3 64FF30      push dword ptr fs:[eax]
:466CC6 648920      mov dword ptr fs:[eax], esp
:466CC9 8D45FC      lea eax, dword ptr [ebp-4]
:466CCC BAE86D4600  mov edx, 466DE8              ; "TAPPBUILDER"
:466CD1 E84AD2F9FF  call 403F20
:466CD6 6A00        push 0
:466CD8 8B45FC      mov eax, dword ptr [ebp-4]
:466CDB E8E8D5F9FF  call 4042C8
:466CE0 50          push eax
:466CE1 E8A20DFAFF  call 407A88                  ; FindWindowA
:466CE6 85C0        test eax, eax                ; ¿Primera ventana encontrada?
:466CE8 750A        jne 466CF4                   ; sí, vamos a por la segunda
:466CEA E8ADCCF9FF  call 40399C                  ; no, nag-screen y nos vamos
:466CEF E9C5000000  jmp 466DB9
:466CF4 FE45F7      inc [ebp-9]                  ; ya tenemos una en el bote
:466CF7 8D45FC      lea eax, dword ptr [ebp-4]
:466CFA BAFC6D4600  mov edx, 466DFC              ; "TAPPLICATION"
:466CFF E81CD2F9FF  call 403F20
:466D04 8D45F8      lea eax, dword ptr [ebp-8]
:466D07 BA146E4600  mov edx, 466E14              ; "DELPHI 4"
:466D0C E80FD2F9FF  call 403F20
:466D11 8B45F8      mov eax, dword ptr [ebp-8]
:466D14 E8AFD5F9FF  call 4042C8
:466D19 50          push eax
:466D1A 8B45FC      mov eax, dword ptr [ebp-4]
:466D1D E8A6D5F9FF  call 4042C8
:466D22 50          push eax
:466D23 E8600DFAFF  call 407A88                  ; FindWindowA
:466D28 85C0        test eax, eax                ; ¿Segunda ventana encontrada?
:466D2A 750A        jne 466D36                   ; sí, vamos a por la tercera
:466D2C E86BCCF9FF  call 40399C                  ; no, nag-screen y nos vamos
:466D31 E983000000  jmp 466DB9
:466D36 FE45F7      inc [ebp-9]                  ; ya tenemos 2
:466D39 8D45FC      lea eax, dword ptr [ebp-4]
:466D3C BA286E4600  mov edx, 466E28              ; "TPropertyInspector"
:466D41 E8DAD1F9FF  call 403F20
:466D46 6A00        push 0
:466D48 8B45FC      mov eax, dword ptr [ebp-4]
:466D4B E878D5F9FF  call 4042C8
:466D50 50          push eax
:466D51 E8320DFAFF  call 407A88                  ; FindWindowA
:466D56 85C0        test eax, eax                ; ¿Tercera ventana encontrada?
:466D58 7507        jne 466D61                   ; sí, vamos a por la última
:466D5A E83DCCF9FF  call 40399C                  ; no, nag-screen y nos vamos
:466D5F EB58        jmp 466DB9
:466D61 FE45F7      inc [ebp-9]                  ; ya tenemos 3
:466D64 8D45FC      lea eax, dword ptr [ebp-4]
:466D67 BA446E4600  mov edx, 466E44              ; "TAlignPalette"
:466D6C E8AFD1F9FF  call 403F20
:466D71 6A00        push 0
:466D73 8B45FC      mov eax, dword ptr [ebp-4]
:466D76 E84DD5F9FF  call 4042C8
:466D7B 50          push eax
:466D7C E8070DFAFF  call 407A88                  ; FindWindowA
:466D81 85C0        test eax, eax                ; ¿Cuarta ventana encontrada?
:466D83 7507        jne 466D8C                   ; sí, continuamos
:466D85 E812CCF9FF  call 40399C                  ; no, nag-screen y nos vamos
:466D8A EB2D        jmp 466DB9
:466D8C FE45F7      inc [ebp-9]                  ; ya tenemos 4
:466D8F 33C0        xor eax, eax
:466D91 5A          pop edx
:466D92 59          pop ecx
:466D93 59          pop ecx
:466D94 648910      mov dword ptr fs:[eax], edx
:466D97 68B96D4600  push 466DB9
:466D9C 807DF704    cmp byte ptr [ebp-9], 4      ; ¿Hemos encontrado las 4 ventanas?
:466DA0 740F        je 466DB1                    ; sí, seguimos como si tal
:466DA2 B85C6E4600  mov eax, 466E5C              ; no, nag-screen y nos vamos
:466DA7 E8404AFFFF  call 45B7EC 
:466DAC E823CFF9FF  call 403CD4
:466DB1 C3          ret

Bueno, sobran los comentarios, ¿no?   Busca cuatro de las ventanas de presencia obligada cuando el IDE de Delphi 4 está arrancado, y si no encuentra alguna de ellas, interrumpe la ejecución del programa.

Buscamos un parche elegante, por ejemplo:
 

:466CA0 C3          ret                          ; nos vamos nada más llegar

Lo probamos y la demo corre perfectamente.  Sin embargo, nuestro trabajo no termina aquí, debemos localizar el código de chequeo en el fichero *.DCU de turno, para que el compilador nos proporcione ejecutables parcheados cada vez que compilemos.

Tras buscar un poco lo encontramos en el interior del fichero lmdclass.dcu.  Lo parcheamos de la misma forma que antes, recompilamos NewMegaDemo.dpr y probamos el nuevo ejecutable.  Ni rastro de la protección.  Probamos compilando un par de ejemplos más, y todo parece correcto.

SMExport Component Suite 3.50, © 1998-1999 Mike Shkolnik
Lo primero que hacemos, tras la instalación, es arrancar Delphi y compilar el proyecto de ejemplo H:\Cracking\SME\EXAMPLE\SMEDemo.dpr.  Obtenemos un ejecutable, SMEDemo.exe, de 770.048 bytes.
 

Una vez fuera de Delphi, ejecutamos el ejemplo y obtenemos el siguiente mensaje:

(Sí, yo también me he fijado, se le habrá pasado actualizar aquí el número de versión.)
 

Echamos mano una vez más de nuestro inseparable W32Dasm para obtener rápidamente el siguiente trozo de código:
 

:495108 53          push ebx
:495109 56          push esi
:49510A 6A00        push 0
:49510C 68040D4A00  push 4A0D04      ; "TAlignPalette"
:495111 E8DA23F7FF  call 4074F0      ; FindWindowA
:495116 8BD8        mov ebx, eax     ; Guardamos el handle en EBX
:495118 6A00        push 0
:49511A 68140D4A00  push 4A0D14      ; "TPropertyInspector"
:49511F E8CC23F7FF  call 4074F0      ; FindWindowA
:495124 8BF0        mov esi, eax     ; Guardamos el handle en ESI
:495126 6A00        push 0
:495128 68280D4A00  push 4A0D28      ; "TAppBuilder"
:49512D E8BE23F7FF  call 4074F0      ; FindWindowA, handle devuelto en EAX
:495132 85DB        test ebx, ebx    ; ¿Encontrada la primera ventana?
:495134 7408        je 49513E        ; No, nos largamos
:495136 85F6        test esi, esi    ; ¿Encontrada la segunda ventana?
:495138 7404        je 49513E        ; No, nos largamos
:49513A 85C0        test eax, eax    ; ¿Y la tercera?
:49513C 7505        jne 495143       ; Sí, todas encontradas, todo bien
:49513E 33C0        xor eax, eax
:495140 5E          pop esi
:495141 5B          pop ebx
:495142 C3          ret
:495143 B001        mov al, 1
:495145 5E          pop esi
:495146 5B          pop ebx
:495147 C3          ret

Una vez más, pensar en un parche elegante es lo único que nos ofrece la protección, ¿qué tal éste?:
 

:49510A EB37        jmp 495143       ; Nada más llegar nos vamos por el camino correcto

¿Y el código original?  Pues esta vez le ha tocado comérselo al fichero ExportDS.dcu.

SMS Driver 1.0, © 1996-1999, Valentin Nagacevschi
Esta vez no hay ejemplos con los que “experimentar”, así que tendremos que fabricarnos uno. Uno sencillo nos vale.  Tiramos sobre un formulario vacío un componente TSMSDriver y un botón para activarlo:


El único código que necesitamos escribir es una línea en el evento OnClick del botón:
 

procedure TForm1.Button1Click(Sender: TObject);
begin
  SMSDriver1.Connect;
end;

Lo compilamos y ya estamos listos para “trabajar”.  Una vez Delphi terminado, ejecutamos nuestro ejemplo y obtenemos la siguiente ventana:


Siguiendo nuestro método habitual, echamos una ojeada al código desensamblado y obtenemos lo siguiente:
 

:44221C 55              push ebp
:44221D 8BEC            mov ebp, esp
:44221F 6A00            push 0
:442221 53              push ebx
:442222 33C0            xor eax, eax
:442224 55              push ebp
:442225 6884224400      push 442284
:44222A 64FF30          push dword ptr fs:[eax]
:44222D 648920          mov dword ptr fs:[eax], esp
:442230 8D45FC          lea eax, dword ptr [ebp-4]
:442233 BA9C224400      mov edx, 44229C              ; "TAppBuilder"
:442238 E81F16FCFF      call 40385C
:44223D 6A00            push 0
:44223F 8B45FC          mov eax, dword ptr [ebp-4]
:442242 E8BD19FCFF      call 403C04
:442247 50              push eax
:442248 E8233FFCFF      call 406170                  ; FindWindowA
:44224D 85C0            test eax, eax
:44224F 0F94C3          sete bl
:442252 80F301          xor bl, 1
:442255 84DB            test bl, bl                  ; ¿BL = 0? (IDE no encontrado)
:442257 7515            jne 44226E                   ; No, nos vamos
:442259 6A00            push 0                       ; Sí, mensaje de aviso
:44225B 668B0DA8224400  mov cx, word ptr [4422A8]
:442262 33D2            xor edx, edx
:442264 B8B4224400      mov eax, 4422B4              ; "Thank you for using ..."
:442269 E82EDFFFFF      call 44019C
:44226E 33C0            xor eax, eax
:442270 5A              pop edx
:442271 59              pop ecx
:442272 59              pop ecx
:442273 648910          mov dword ptr fs:[eax], edx
:442276 688B224400      push 44228B
:44227B 8D45FC          lea eax, dword ptr [ebp-4]
:44227E E84115FCFF      call 4037C4
:442283 C3              ret
:442284 E9FB0FFCFF      jmp 403284
:442289 EBF0            jmp 44227B
:44228B 8BC3            mov eax, ebx
:44228D 5B              pop ebx
:44228E 59              pop ecx
:44228F 5D              pop ebp
:442290 C3              ret

Supongo que, a estas alturas de la película sobra cualquier comentario.  Nuestro parche habitual para quedarnos satisfechos, por ejemplo, el siguiente:
 

:442252 32DB            xor bl, bl                ; BL = 0
:442254 43              inc ebx                   ; BL = 1, IDE siempre encontrado

Y, para terminar, esta vez no hay duda ninguna respecto a la localización del código original, ya que sólo existe un archivo DCU en donde buscar ;-).

TUsersSTD 1.0, © 1999-2000 ToolsAndComps
Tras la instalación, buscamos un ejemplo que, una vez compilado, nos sirva para encontrar la protección.  Esta vez el afortunado será

H:\Cracking\UsersSTD\ExamplesD4\ShowAcessLevels\SALevels.dpr

que tras la compilación, se convierte en una monstruosidad llamada SALevels.exe, de 756.736 bytes.

Tras la primera ejecución, nos encontramos con el siguiente mensaje:


Un par de minutos después, ya somos capaces de encontrar la siguiente preciosidad:
 

:499EC8 53          push ebx
:499EC9 56          push esi
:499ECA 57          push edi
:499ECB 6878DC4900  push 49DC78     ; "Delphi 4"
:499ED0 6838DC4900  push 49DC38     ; "TApplication"
:499ED5 E86AD3F6FF  call 407244     ; FindWindowA
:499EDA 8BD8        mov ebx, eax    ; Guardamos el handle en EBX
:499EDC 6A00        push 0
:499EDE 6848DC4900  push 49DC48     ; "TAlignPalette"
:499EE3 E85CD3F6FF  call 407244     ; FindWindowA
:499EE8 8BF0        mov esi, eax    ; Guardamos el handle en ESI
:499EEA 6A00        push 0
:499EEC 6858DC4900  push 49DC58     ; "TPropertyInspector"
:499EF1 E84ED3F6FF  call 407244     ; FindWindowA
:499EF6 8BF8        mov edi, eax    ; Guardamos el handle en EDI
:499EF8 6A00        push 0
:499EFA 686CDC4900  push 49DC6C     ; "TAppBuilder"
:499EFF E840D3F6FF  call 407244     ; FindWindowA, handle devuelto en EAX
:499F04 85DB        test ebx, ebx   ; ¿Hemos encontrado la primera ventana?
:499F06 740C        je 499F14       ; No, nos largamos
:499F08 85F6        test esi, esi   ; ¿Hemos encontrado la segunda ventana?
:499F0A 7408        je 499F14       ; No, nos largamos
:499F0C 85FF        test edi, edi   ; ¿Hemos encontrado la tercera ventana?
:499F0E 7404        je 499F14       ; No, nos largamos
:499F10 85C0        test eax, eax   ; ¿Y la cuarta?
:499F12 7504        jne 499F18      ; Sí, estaban todas, perfecto
:499F14 33C0        xor eax, eax
:499F16 EB02        jmp 499F1A
:499F18 B001        mov al, 1
:499F1A 5F          pop edi
:499F1B 5E          pop esi
:499F1C 5B          pop ebx
:499F1D C3          ret

Esto está empezando a resultar aburrido, ¿no?   Probamos un parche cualquiera (elegir el parche es lo más “excitante” que nos permiten protecciones como ésta),  por ejemplo:
 

:499F16 EB00        jmp 499F18      ; Pase lo que pase, siempre salimos bien

Y, por si alguien siente aún alguna curiosidad, el código de chequeo original se encuentra en el fichero usersstd.dcu.

VCLZip Native Delphi Zip/UnZip Component! 2.20 Beta 1, © 1997-1999 Kevin L. Boylan
Lo primero que hacemos, tras la instalación, es arrancar Delphi 5 y compilar el proyecto de ejemplo H:\VCLZip\Examples\ZipUtil\Proj_32\zipp.dpr.  Obtenemos un ejecutable, zipp.exe, de 623.616 bytes.

Salimos de Delphi y al ejecutar nuestro recién compilado ejemplo, obtenemos el siguiente mensaje:


y tras ésta, un mensaje de excepción del sistema que, cuando menos, nos causa una mala sensación.  Tras urgar entre el código desensamblado, obtenemos lo siguiente:
 

:463098 6830E74700  push 47E730       ; "Delphi 5"
:46309D 6804E74700  push 47E704
:4630A2 E89939FAFF  call 406A40       ; FindWindowA
:4630A7 85C0        test eax, eax
:4630A9 7420        je 4630CB
:4630AB 6A00        push 0
:4630AD 6814E74700  push 47E714       ; "TAlignPalette"
:4630B2 E88939FAFF  call 406A40       ; FindWindowA
:4630B7 85C0        test eax, eax
:4630B9 7410        je 4630CB
:4630BB 6A00        push 0
:4630BD 6824E74700  push 47E724       ; "TAppBuilder"
:4630C2 E87939FAFF  call 406A40       ; FindWindowA
:4630C7 85C0        test eax, eax
:4630C9 7503        jne 4630CE
:4630CB 33C0        xor eax, eax      ; IDE no encontrado
:4630CD C3          ret
:4630CE B001        mov al, 1         ; IDE encontrado
:4630D0 C3          ret

Sin comentarios, ¿no?  Intentemos uno de nuestro parches favoritos:
 

:4630CD 90          nop               ; pase lo que pase, devolvemos siempre AL = 1

Y, como siempre, el origen de la protección lo buscamos en el interior de los archivos .DCU, siendo esta vez dos los ficheros en los que encontramos idéntico código:  Kpzipobj.dcu y VCLUnZip.dcu.

Probamos el parche, todo perfecto, parece que nuestro trabajo ha terminado.

LOS DESPISTADOS
La verdad es que uno no sabe qué decir ante un caso así.  Lo cierto es que no recuerdo que me haya ocurrido nunca encontrarme algo parecido.  Un producto supuestamente protegido, que se distribuye desprotegido.  Es posible que haya truco, pero no he encontrado nada.  (Si tú, lector espabilado, tienes más noticias acerca de este caso, no lo dudes y escríbenos.)
1stClass Trial 1.01, ©1995-99 Woll2Woll Software
Lo primero que hacemos, tras la instalación, es arrancar Delphi 5 y compilar el proyecto de ejemplo C:\Archivos de programa\Woll2Woll\1stClassTrial_d5\demos\Demo1stClass.dpr.  Obtenemos un ejecutable, Demo1stClass.exe, de 1.850.368 bytes.

Tras cerrar Delphi, ejecutamos el ejemplo.  Sin embargo, al contrario de lo que esperábamos, nos encontramos que el programa arranca de forma normal, sin ventanas de mensajes ni ningún otro rastro de protección.  Tras “jugar” un rato con el ejemplo y probarlo de arriba abajo, nos invade el aburrimiento al no ver nada “raro”, y nos largamos.  Aquí ocurre algo extraño, y sólo se nos ocurren las siguientes explicaciones:

  1. Woll2Woll, en un alarde de valentía, se ha arriesgado con una estrategia peligrosa, la de meter miedo a clientes estúpidos con protecciones inexistentes.  (Y estúpidos, con todas las letras, tienen que ser si no prueban algo tan sencillo como ejecutar sus pruebas fuera de Delphi, ¿o me equivoco?).
  2. La protección está oculta, y no aparece así sin más en cuanto ejecutamos cualquier cosa.
  3. El equipo de Woll2Woll encargado de preparar este paquete en concreto se ha colado y ha metido una versión desprotegida.


Tras meditarlo medio segundo, descartamos desde ya la explicación (a).  Nadie (o casi nadie ;-) puede ser tan necio como para decir que su producto está protegido y luego venderlo desprotegido, y mucho menos “insultar” de esa forma a posibles futuros clientes llamándolos imbéciles a la cara.

La explicación (c) no es tan descabellada como pudiera parecer.  Sin embargo, no podemos descartar la opción (b) sin haber al menos indagado un poco, ¿verdad que no?

Estrategias a seguir, por orden según nos van viniendo a la cabeza:

  1. Desensamblar “Demo1stClass.exe” y buscar llamadas a FindWindow o FindWindowEx.
  2. Estudiar si el programa carga en algún momento DLL’s o módulos de algún tipo que no hayamos detectado a simple vista tras la instalación.
  3. Estudiar si en algún momento de su ejecución el programa crea timers o threads, en los que puedan camuflarse bombas de tiempo y/o chequeos de IDE.


Si tras estudiar todo esto, los ejemplos que compilemos siguen funcionando bien, consideraremos el tema cerrado hasta nuevo aviso.  Todo tiene un límite, y tenemos mucho trabajo que hacer aún ;-).

Intentamos el desensamblado con W32Dasm, pero algo extraño ocurre, ya que el desensamblador se queda colgado sin terminar siquiera de cargar el ejecutable.  ¿La configuración de mi sistema? ¿tal vez trucos anti-W32Dasm?  Probaremos con IDA antes de llegar a conclusiones precipitadas.

Tras un par de minutos, obtenemos un fichero de 43 Mb con nuestro protagonista desensamblado.  Ni una sóla llamada a “FindWindow”, al menos explícitas que podamos detectar a simple vista.  Tampoco hay rastro de cadenas del tipo “Delphi” o “C++Builder”.  Guardamos el listado por si lo necesitamos más tarde, pero no perdemos más tiempo y vamos a por nuestro siguiente paso, buscar DLL’s.

Arrancamos el ejemplo y con ayuda de nuestra herramienta favorita obtenemos un listado de las DLL’s que el ejecutable carga de forma estática junto con él.  Obtenemos lo siguiente:


Dejando aparte las librerías del sistema (*.drv, *.dll) detectamos módulos de extensión .BPL.  Los archivos BPL, también llamados “paquetes” o runtime packages, no son más que DLL’s en cuyo interior se encuentra encapsulado código compilado relativo a componentes VCL de Delphi/C++Builder.  En la mayor parte de los casos, los programadores de Delphi compilan sus programas con la opción por defecto de “compilar sin paquetes”.  De esta forma, el ejecutable generado contiene en su interior todo el código necesario para desarrollar las funcionalidades ofrecidas de cualquier componente VCL que utilice.  Sin embargo, hay ocasiones (más de las que algunos piensan) en las que es conveniente cambiar la opción por defecto por la de “compilar con paquetes”.  Así, obtenemos ejecutables de dimensiones menores, además de disfrutar de ventajas adicionales,  pero no es éste el lugar ni el momento de extendernos, continuemos ...

Todos los “paquetes” cargados son paquetes VCL de los que se incluyen con Delphi 5. ¿Todos? No, no todos, y me imagino que sabemos cuál no lo es:  FirstClassTrial_d5.bpl.  Pocas dudas nos quedan ya, puesto que este hecho nos confirma que la segunda limitación descrita en la documentación tampoco se cumple:
 

1stClass Trial version for Delphi 5

With the trial version you will only be able to run programs from within the Delphi IDE.  The trial version does not support runtime packages so you will need to make sure that the \1stclass\lib is in your component search path.

No sólo podemos ejecutar el ejemplo de forma, al menos aparentemente, normal, sino que además lo podemos hacer sea cual sea el modo de compilación (con o sin paquetes) seleccionado.  No vamos a perder más tiempo, ya que todo apunta a nuestra tercera posible explicación, se han colado y parece que disponemos de una versión sin restricciones.

LOS CURRANTES
Entre tanta mediocridad, de vez en cuando uno se encuentra con gente que, aunque no se coma mucho la cabeza para diseñar la protección, al menos, intenta llegar más allá de la implementación cutre que hemos visto en apartados anteriores.  Esta tampoco es la forma, pero sí el camino.
oaAgent Component Set, © 1999-2000 O&A Productions
Lo primero que hacemos, tras la instalación, es arrancar Delphi 5 y compilar el proyecto de ejemplo H:\Archivos de programa\O&A\oaAgent\Demos\Agent Test\AgentTest.dpr.  Obtenemos un ejecutable, AgentTest.exe, de 560.128 bytes.

Tras salir de Delphi, ejecutamos el ejemplo y obtenemos el siguiente mensaje:


Como es habitual, indagamos entre el código desensamblado buscando las clásicas llamadas a FindWindow/FindWindowEx, sin embargo, ¡no encontramos nada!  Esto se pone interesante (por fin ;-), aunque son ya casi las 3 de la mañana y hay que madrugar ...
 


[Unas horas de sueño y una jornada de  trabajo después ...]


Una mini-sesión de Soft-ICE y localizamos rápidamente el código de chequeo:
 

:460D78 55            push ebp
:460D79 8BEC          mov ebp, esp
:460D7B B904000000    mov ecx, 4
:460D80 6A00          push 0
:460D82 6A00          push 0
:460D84 49            dec ecx
:460D85 75F9          jne 460D80
:460D87 51            push ecx
:460D88 53            push ebx
:460D89 56            push esi
:460D8A 57            push edi
:460D8B 33C0          xor eax, eax
:460D8D 55            push ebp
:460D8E 68F40E4600    push 460EF4
:460D93 64FF30        push dword ptr fs:[eax]
:460D96 648920        mov dword ptr fs:[eax], esp
:460D99 33DB          xor ebx, ebx
:460D9B 8D55FC        lea edx, dword ptr [ebp-4]
:460D9E A16C3D4700    mov eax, dword ptr [473D6C]    ; "USER32.DLL" encriptado
:460DA3 E880FFFFFF    call 460D28                    ; lo desencriptamos
:460DA8 8B45FC        mov eax, dword ptr [ebp-4]
:460DAB E84831FAFF    call 403EF8
:460DB0 50            push eax                       ; Obtenemos el handle de USER32.DLL
:460DB1 E87A5AFAFF    call 406830                    ; (GetModuleHandleA)
:460DB6 8BF0          mov esi, eax
:460DB8 85F6          test esi, esi
:460DBA 0F8419010000  je 460ED9
:460DC0 8D55F8        lea edx, dword ptr [ebp-8]
:460DC3 A1703D4700    mov eax, dword ptr [473D70]    ; "KERNEL32.DLL" encriptado
:460DC8 E85BFFFFFF    call 460D28                    ; lo desencriptamos
:460DCD 8B45F8        mov eax, dword ptr [ebp-8]
:460DD0 E82331FAFF    call 403EF8
:460DD5 50            push eax                       ; Obtenemos el handle de
:460DD6 E8555AFAFF    call 406830                    ; KERNEL32.DLL (GetModuleHandleA)
:460DDB 8BF8          mov edi, eax
:460DDD 85FF          test edi, edi
:460DDF 0F84F4000000  je 460ED9
:460DE5 8D55F4        lea edx, dword ptr [ebp-0C]
:460DE8 A1743D4700    mov eax, dword ptr [473D74]    ; "GetProcAddress" encriptado
:460DED E836FFFFFF    call 460D28                    ; lo desencriptamos
:460DF2 8B45F4        mov eax, dword ptr [ebp-0C]
:460DF5 E8FE30FAFF    call 403EF8
:460DFA 50            push eax
:460DFB 57            push edi                       ; Obtenemos la dirección de la
:460DFC E8375AFAFF    call 406838                    ; función "GetProcAddress"
:460E01 8BF8          mov edi, eax                   ; y la guardamos en EDI
:460E03 85FF          test edi, edi
:460E05 0F84CE000000  je 460ED9
:460E0B 8D55F0        lea edx, dword ptr [ebp-10]
:460E0E A1783D4700    mov eax, dword ptr [473D78]    ; "FindWindowA" encriptado
:460E13 E810FFFFFF    call 460D28                    ; lo desencriptamos
:460E18 8B45F0        mov eax, dword ptr [ebp-10]
:460E1B E8D830FAFF    call 403EF8
:460E20 50            push eax
:460E21 56            push esi                       ; Buscamos la dirección de la
:460E22 FFD7          call edi                       ; función "FindWindowA"
:460E24 8BF0          mov esi, eax                   ; y la guardamos en ESI
:460E26 85F6          test esi, esi
:460E28 0F84AB000000  je 460ED9
:460E2E 6A00          push 0
:460E30 8D55EC        lea edx, dword ptr [ebp-14]
:460E33 A17C3D4700    mov eax, dword ptr [473D7C]    ; "TAppBuilder" encriptado
:460E38 E8EBFEFFFF    call 460D28                    ; lo desencriptamos
:460E3D 8B45EC        mov eax, dword ptr [ebp-14]
:460E40 E8B330FAFF    call 403EF8
:460E45 50            push eax                       ; ¿Hay presente alguna ventana de
:460E46 FFD6          call esi                       ; clase TAppBuilder?
:460E48 85C0          test eax, eax
:460E4A 0F95C3        setne bl
:460E4D 84DB          test bl, bl
:460E4F 741F          je 460E70                      ; No, nos vamos
:460E51 6A00          push 0
:460E53 8D55E8        lea edx, dword ptr [ebp-18]
:460E56 A1803D4700    mov eax, dword ptr [473D80]    ; "TApplication" encriptado
:460E5B E8C8FEFFFF    call 460D28                    ; lo desencriptamos
:460E60 8B45E8        mov eax, dword ptr [ebp-18]
:460E63 E89030FAFF    call 403EF8
:460E68 50            push eax                       ; ¿Hay presente alguna ventana de
:460E69 FFD6          call esi                       ; clase TApplication?
:460E6B 85C0          test eax, eax
:460E6D 0F95C3        setne bl
:460E70 84DB          test bl, bl
:460E72 741F          je 460E93                      ; No, nos vamos
:460E74 6A00          push 0
:460E76 8D55E4        lea edx, dword ptr [ebp-1C]
:460E79 A1843D4700    mov eax, dword ptr [473D84]    ; "TPropertyInspector"
:460E7E E8A5FEFFFF    call 460D28                    ; lo desencriptamos
:460E83 8B45E4        mov eax, dword ptr [ebp-1C]
:460E86 E86D30FAFF    call 403EF8
:460E8B 50            push eax                       ; ¿Hay presente alguna ventana de
:460E8C FFD6          call esi                       ; clase TPropertyInspector?
:460E8E 85C0          test eax, eax
:460E90 0F95C3        setne bl
:460E93 84DB          test bl, bl
:460E95 741F          je 460EB6                      ; No, nos vamos
:460E97 6A00          push 0
:460E99 8D55E0        lea edx, dword ptr [ebp-20]
:460E9C A1883D4700    mov eax, dword ptr [473D88]    ; "TAlignPalette" encriptado
:460EA1 E882FEFFFF    call 460D28                    ; lo desencriptamos
:460EA6 8B45E0        mov eax, dword ptr [ebp-20]
:460EA9 E84A30FAFF    call 403EF8
:460EAE 50            push eax                       ; ¿Hay presente alguna ventana de
:460EAF FFD6          call esi                       ; clase TAlignPalette?
:460EB1 85C0          test eax, eax
:460EB3 0F95C3        setne bl
:460EB6 84DB          test bl, bl
:460EB8 741F          je 460ED9                      ; No, nos vamos
:460EBA 6A00          push 0
:460EBC 8D55DC        lea edx, dword ptr [ebp-24]
:460EBF A18C3D4700    mov eax, dword ptr [473D8C]    ; "TMenuBuilder" encriptado
:460EC4 E85FFEFFFF    call 460D28                    ; lo desencriptamos
:460EC9 8B45DC        mov eax, dword ptr [ebp-24]
:460ECC E82730FAFF    call 403EF8
:460ED1 50            push eax                       ; ¿Hay presente alguna ventana de
:460ED2 FFD6          call esi                       ; clase TMenuBuilder?
:460ED4 85C0          test eax, eax
:460ED6 0F95C3        setne bl
:460ED9 33C0          xor eax, eax
:460EDB 5A            pop edx
:460EDC 59            pop ecx
:460EDD 59            pop ecx
:460EDE 648910        mov dword ptr fs:[eax], edx
:460EE1 68FB0E4600    push 460EFB
:460EE6 8D45DC        lea eax, dword ptr [ebp-24]
:460EE9 BA09000000    mov edx, 9
:460EEE E8E52BFAFF    call 403AD8
:460EF3 C3            ret
:460EF4 E99325FAFF    jmp 40348C
:460EF9 EBEB          jmp 460EE6
:460EFB 8BC3          mov eax, ebx                   ; EAX > 0, IDE presente
:460EFD 5F            pop edi
:460EFE 5E            pop esi
:460EFF 5B            pop ebx
:460F00 8BE5          mov esp, ebp
:460F02 5D            pop ebp
:460F03 C3            ret

Un poco largo, pero muy ameno ;-). 

Vamos a ver si analizamos de forma rápida lo que el programador ha hecho aquí, incluyendo alguna curiosidad:

  1. Todos los nombres de las clases de ventana a buscar están encriptados.
  2. La función FindWindowA no aparece de forma explícita, sino que es llamada calculando su dirección de rutina mediante llamada a la función del API GetProcAddress.
  3. Por supuesto, lo mismo ocurre con GetProcAddress.  Aquí está una de las curiosidades, ya que para obtener la dirección de GetProcAddress se ejecuta explícitamente la propia GetProcAddress, aunque el nombre de la rutina necesario como parámetro también esté encriptado.
  4. Para obtener las direcciones de GetProcAddress y FindWindowA, es necesario conocer los handles de las DLL’s a las que pertenecen, KERNEL32.DLL y USER32.DLL, respectivamente.  Una vez más, los nombres de estas DLL’s también están encriptados.
  5. Por lo demás, el sistema es el ya conocido, buscar una serie de ventanas determinadas para ver si el IDE de Delphi/C++Builder está presente.  Sin embargo, aquí va otra curiosidad, ya que una de las clases de ventana que chequea es TApplication, chequeo que devolverá siempre TRUE si el ejecutable ha sido creado con el IDE de Delphi/C++Builder.


Para terminar, un parche para quedarnos satisfechos e irnos contentos a dormir, éste por ejemplo:
 

:460EFB B001          mov al, 1                     ; Siempre devolvemos EAX > 0

Y, cómo no, no nos podemos ir sin localizar el código original, que encontraremos en esta ocasión dentro del fichero Agent.dcu.

ASÍ SI!
De vez en cuando (muy de vez en cuando), uno se encuentra con protecciones trabajadas, de esas que no ventilas en 5 o 10 minutos y que tardas menos en destripar que en preparar el crack.  Esta es una de esas.  No es la protección perfecta (ninguna lo es), pero al menos lo han intentado.  Mis felicitaciones.
Multimedia Tools 1.7, ©1995-1999 SwiftSoft
Tomamos uno cualquiera de los ejemplos incluídos, lo compilamos y obtenemos el siguiente ejecutable:

c:\MMTOOLS\demos\cdrom\cddacopy\mmdemo32.exe, 653.312 bytes


Al intentar ejecutarlo sin Delphi arrancado nos aparece el siguiente mensaje:


Con ayuda de nuestro depurador favorito, y un bonito punto de ruptura en FindWindowA, localizamos el chequeo en toda su plenitud:
 

CHEQUEO #1

:45DECF 6A00        push 0                       ; cualquier título nos vale
:45DED1 6828DF4500  push 45DF28                  ; clase de la ventana "TAppBuilder"
:45DED6 E8598DFAFF  call 406C34                  ; FindWindowA
:45DEDB 85C0        test eax, eax                ; EAX = handle de la ventana
:45DEDD 7430        je 45DF0F                    ; EAX = 0, no encontrada
:45DEDF 6A50        push 50                      ; ventana encontrada, nos preparamos
:45DEE1 8D55AB      lea edx, dword ptr [ebp-55]  ; para obtener su título
:45DEE4 52          push edx                     ; EDX = destino del título
:45DEE5 50          push eax                     ; handle de la ventana
:45DEE6 E8A98EFAFF  call 406D94                  ; GetWindowTextA
:45DEEB BA34DF4500  mov edx, 45DF34              ; "Delphi"
:45DEF0 8D45AB      lea eax, dword ptr [ebp-55]  ; el título obtenido antes
:45DEF3 E854AFFAFF  call 408E4C                  ; los comparamos
:45DEF8 85C0        test eax, eax                ; ¿son iguales?
:45DEFA 7511        jne 45DF0D                   ; por supuesto, seguimos adelante
:45DEFC BA3CDF4500  mov edx, 45DF3C              ; no hemos encontrado el IDE de Delphi
:45DF01 8D45AB      lea eax, dword ptr [ebp-55]  ; a lo mejor es que trabajamos con
:45DF04 E843AFFAFF  call 408E4C                  ; C++Builder ...  intentamos lo mismo
:45DF09 85C0        test eax, eax                ; ¿título = "C++Builder"?
:45DF0B 7402        je 45DF0F                    ; no, no hay ningún IDE válido
:45DF0D B301        mov bl, 1                    ; todo ha ido bien, algún IDE presente

Momento de parchear y probar.  Como casi siempre, se nos presentan múltiples soluciones, ahí va una de las posibles:
 

:45DECF EB3C        jmp 45DF0D                   ; saltamos hacia la salida correcta

Parcheamos adecuadamente MMDEMO32.EXE,  probamos una nueva ejecución y nos encontramos con la siguiente ventana:


que no es más que el cuadro de Acerca de habitual con un timer que retrasa la rehabilitación del botón de OK.  Al pulsar OK nos encontramos con una ventana de IDE not found como la del principio, y a continuación el programa termina.  Efectivamente, el chequeo anterior no era el único, y poco tardamos en encontrar el siguiente, muy parecido al anterior:
 
 

CHEQUEO #2

:2B 6A00            push 0                       ; cualquier título nos vale
:2D 8B5638          mov edx, dword ptr [esi+38]
:30 52              push edx                     ; clase de la ventana "TAppBuilder"
:31 FFD0            call eax                     ; FindWindowA
:33 85C0            test eax, eax                ; EAX = handle de la ventana
:35 0F8422020000    je 25D                       ; EAX = 0, no encontrada
:3B 8D5330          lea edx, dword ptr [ebx+30]  ; ventana encontrada, nos preparamos
:3E 8B12            mov edx, dword ptr [edx] 
:40 8B12            mov edx, dword ptr [edx]
:42 6A50            push 50
:44 8D8C2484030000  lea ecx, dword ptr [esp+384]
:4B 51              push ecx                     ; EDX = destino del título
:4C 50              push eax                     ; handle de la ventana
:4D FFD2            call edx                     ; GetWindowTextA
:4F 8B463C          mov eax, dword ptr [esi+3C]  ; Ahora comparamos carácter a carácter
:52 8A10            mov dl, byte ptr [eax]       ; el título obtenido con la cadena
:54 3A942480030000  cmp dl, byte ptr [esp+380]   ; "Delphi"
:5B 7544            jne A1                       ; ¿alguno es diferente?, nos vamos
:5D 8A5001          mov dl, byte ptr [eax+1]     ; "e"
:60 3A942481030000  cmp dl, byte ptr [esp+381]
:67 7538            jne A1
:69 8A5002          mov dl, byte ptr [eax+2]     ; "l"
:6C 3A942482030000  cmp dl, byte ptr [esp+382]
:73 752C            jne A1
:75 8A5003          mov dl, byte ptr [eax+3]     ; "p"
:78 3A942483030000  cmp dl, byte ptr [esp+383]
:7F 7520            jne A1
:81 8A5004          mov dl, byte ptr [eax+4]     ; "h"
:84 3A942484030000  cmp dl, byte ptr [esp+384]
:8B 7514            jne A1
:8D 8A4005          mov al, byte ptr [eax+5]     ; "i"
:90 3A842485030000  cmp al, byte ptr [esp+385]
:97 7508            jne A1
:99 893424          mov dword ptr [esp], esi     ; IDE de Delphi encontrado, nos vamos
:9C E980000000      jmp 121 
:A1 8B4640          mov eax, dword ptr [esi+40]  ; lo intentamos con el de C++Builder
...

Como casi siempre, se nos presentan mútliples soluciones, ahí va una de las posibles:
 

:2B EB6C            jmp 99                       ; saltamos hacia la salida correcta

Momento de parchear y probar, de nuevo.  Esta vez, el código no se encuentra en el ejecutable, ¿tal vez en alguna DLL utilizada por éste?  Buscamos en MMUTIL32.DLL, pero no hay rastro de nuestro código ...  ¿estamos tal vez ante código encriptado?

Colocamos puntos de ruptura para detectar en qué momento el programa escribe en la zona de memoria donde se encuentra nuestro código, y encontramos finalmente lo siguiente:
 

:06 55          push ebp
:07 8BEC        mov ebp, esp
:09 57          push edi
:0A 53          push ebx
:0B 89C3        mov ebx, eax
:0D 89D7        mov edi, edx
:0F F76D10      imul [ebp+10]
:12 03450C      add eax, dword ptr [ebp+0C]
:15 99          cdq
:16 F77D08      idiv [ebp+8]
:19 89D0        mov eax, edx
:1B 25FF000080  and eax, 800000FF
:20 7907        jns 29
:22 48          dec eax
:23 0D00FFFFFF  or eax, FFFFFF00
:28 40          inc eax
:29 8A27        mov ah, byte ptr [edi]     ; leemos el siguiente byte
:2B 001F        add byte ptr [edi], bl     ; lo desencriptamos
:2D 2807        sub byte ptr [edi], al
:2F 88E3        mov bl, ah
:31 89D0        mov eax, edx
:33 47          inc edi
:34 E2D9        loop 0F                    ; seguimos hasta terminar con todos
:36 5B          pop ebx
:37 5F          pop edi
:38 5D          pop ebp
:39 C20C00      ret 0C

La rutina desencripta una zona de memoria que va desde ES:FF9618 hasta ES:FF9C43.  Ante semejante panorama se nos presentan dos alternativas:

  1. Hacer un volcado de memoria del bloque de memoria desencriptado, sustituirlo por el original encriptado, y anular la llamada a la rutina de desencriptación anterior.
  2. Con ayuda de nuestro compilador favorito escribir un pequeño programa que encripte y desencripte un bloque de memoria siguiendo el algoritmo anterior, y generar un bloque encriptado que, al ser desencriptado por la rutina original nos genere el código de chequeo #2 perfectamente parcheado.


Si observamos con un poco de detenimiento la rutina anterior, nos daremos cuenta de que la desencriptación no se realiza contra una tabla byte a byte ni nada parecido (lo que nos facilitaría sustanciosamente la labor) sino que para la desencriptación de cada byte se utiliza el valor del byte anterior encriptado.  Teniendo esto en cuenta, y aunque más entretenida, olvidaremos para otro día la segunda opción, y nos tiraremos a por la primera.
 

Con ayuda de la excelente utilidad IceDump hacemos un volcado de la zona de memoria afectada tras la encriptación, con la intención de sustituirla físicamente por su equivalente encriptada.


Y hacemos lo propio tras la desencriptación:


Ahora buscamos el bloque encriptado (lo encontramos en MMUTIL32.DLL) y lo sustituimos por el desencriptado parcheado:


quedando la DLL así:


y, en concreto, la parte que ahora nos interesa, nuestro parche:


Ahora nos queda localizar la llamada a la rutina de desencriptación para anularla.  Una vez más, encontramos el código correspondiente en MMUTIL32.DLL:
 

:409C48 53              push ebx
:409C49 803DC0D2400000  cmp byte ptr [40D2C0], 0
:409C50 7441            je 409C93
:409C52 33C0            xor eax, eax
:409C54 A3D8E54000      mov dword ptr [40E5D8], eax
:409C59 33C0            xor eax, eax
:409C5B A3DCE54000      mov dword ptr [40E5DC], eax
:409C60 BB187C4000      mov ebx, 407C18
:409C65 BAC87E4000      mov edx, 407EC8
:409C6A B86C874000      mov eax, 40876C
:409C6F E8E8E1FFFF      call 407E5C
:409C74 8BC3            mov eax, ebx
:409C76 E8ADE2FFFF      call 407F28                     ; desencriptación
:409C7B C605C8D2400001  mov byte ptr [40D2C8], 1
:409C82 891DB8D24000    mov dword ptr [40D2B8], ebx
:409C88 E81FECFFFF      call 4088AC
:409C8D 8BC3            mov eax, ebx
:409C8F 03C0            add eax, eax
:409C91 5B              pop ebx
:409C92 C3              ret

Parcheamos adecuadamente MMUTIL32.DLL con una de las múltiples soluciones:
 

:409C74 EB05            jmp 409C7B                      ; saltamos la desencriptación

Ya estamos listos para probar nuestro segundo parche.  Una vez más, la misma ventana de About que antes, y es que, efectivamente, existe un tercer chequeo de existencia de IDE de Delphi.  Un nuevo punto de ruptura en FindWindowA y obtenemos el código del tercer chequeo:
 

CHEQUEO #3

:409E24 6A00            push 0                      ; cualquier título nos vale
:409E26 68649F4000      push 409F64                 ; clase de la ventana "TAppBuilder"
:409E2B E820ABFFFF      call 404950                 ; FindWindowA
:409E30 BF07000000      mov edi, 7
:409E35 85C0            test eax, eax
:409E37 0F8419010000    je 409F56                   ; ventana no encontrada, nos vamos
:409E3D 33F6            xor esi, esi                ; ventana encontrada, nos preparamos
:409E3F 6A50            push 50                     ; para obtener su título
:409E41 8D942458030000  lea edx, dword ptr [esp+358]
:409E48 52              push edx                    ; EDX = destino del título
:409E49 50              push eax                    ; EAX = handle de la ventana
:409E4A E811ABFFFF      call 404960                 ; GetWindowTextA
:409E4F BA709F4000      mov edx, 409F70             ; "Delphi"
:409E54 8D842454030000  lea eax, dword ptr [esp+354]
:409E5B E85CFEFFFF      call 409CBC                 ; comparamos con el título obtenido
:409E60 85C0            test eax, eax               ; ¿son iguales?
:409E62 7407            je 409E6B                   ; no, no está el IDE de Delphi
:409E64 BED4D24000      mov esi, 40D2D4             ; sí, IDE de Delphi detectado
:409E69 EB1A            jmp 409E85                  ; nos largamos
:409E6B BA789F4000      mov edx, 409F78             ; probamos con "C++Builder"
:409E70 8D842454030000  lea eax, dword ptr [esp+354]
:409E77 E840FEFFFF      call 409CBC                 ; comparamos con el título obtenido
:409E7C 85C0            test eax, eax               ; ¿título = "C++Builder"?
:409E7E 7405            je 409E85                   ; sí, IDE presente, nos largamos
...

Como casi siempre, se nos presentan mútliples soluciones, ahí va una de las posibles:
 

:409E24 EB3E            jmp 409E64                  ; saltamos hacia la salida correcta

Esta vez tenemos suerte, ya que, aunque el código de este tercer chequeo estaba también encriptado, los programadores han sido tan amables de desencriptarlo de una sóla vez junto con el chequeo #2, de forma que lo encontramos perfectamente legible en MMUTIL32.DLL.  No nos queda más que ejecutar nuevamente y probar nuestro tercer parche.

El programa funciona perfectamente.  Probamos con un par de ejemplos cualesquiera y vemos que, aunque hayamos susituído la MMUTIL32.DLL original por la nuestra, aún es necesario parchear cada ejecutable producido para saltar el chequeo #1.

Parémonos un poco a pensar, intentando ponernos en el lugar del programador, y preguntémonos ¿dónde podríamos incluir nuestro código de chequeo para que cualquier ejecutable compilado con alguno de mis componentes lo incluya al arrancar?  Las respuestas pueden ser variadas, dependiendo de los conocimientos que posea el programador, del tiempo de que disponga, de lo vago que sea o de su grado de implicación con el producto.  La primera solución que suele venir a la cabeza es la de incluir todo el chequeo en la sección de inicialización de una unidad compartida, algo así como:
 

unit ChequeoIDE;

interface
  [...]

implementation
  [...]

initialization
  [... código de chequeo ...]

end.

e incluir luego el uso de esta unidad en cada unidad correspondiente a un componente:
 

unit MiPrimerComponente;

uses
  ChequeoIDE, ...;

interface
  [...]

implementation
  [...]

end.

Con este uses garantizamos la ejecución del código de chequeo al comienzo de cada programa.

Nos bastan unos segundos para echar un vistazo a los ficheros *.DCU (pequeño recordatorio:  el compilador genera un fichero *.DCU con código compilado por cada fichero fuente *.PAS, el linkador se encargará de “copiar” de cada *.DCU el código necesario y ubicarlo en el ejecutable final) incluídos con el producto y nos encontramos con uno que, cuando menos, despierta nuestra sospechas:
 

[...]
30/01/00  21:54    2.877 MMTimer.dcu
12/06/00  22:33    4.281 MMTools.dcu
12/06/00  22:33   10.925 mmtools_d5.dcu
30/01/00  21:54   16.587 MMTrigg.dcu
30/01/00  21:54    7.031 MMTrnPrp.dcu
30/01/00  21:54   34.940 MMUtils.dcu      <----
30/01/00  21:54   61.123 MMVFW.dcu
12/06/00  22:33    2.041 MMVisReg.dcu
30/01/00  21:54    5.603 MMVolume.dcu
30/01/00  21:54   40.499 MMWave.dcu
[...]

Tentador, ¿no?.  Está pidiendo a gritos que le echemos una ojeada y busquemos en su interior el código del chequeo #1:
 

:7232 6A00        push 0
:7234 68A0010000  push 1A0
:7239 E800000000  call 723E
:723E 85C0        test eax, eax
:7240 7430        je 7272
:7242 6A50        push 50
:7244 8D55AB      lea edx, dword ptr [ebp-55]
:7247 52          push edx
:7248 50          push eax
:7249 E800000000  call 724E
:724E BAAC010000  mov edx, 1AC
:7253 8D45AB      lea eax, dword ptr [ebp-55]
:7256 E800000000  call 725B
:725B 85C0        test eax, eax
:725D 7511        jne 7270
:725F BAB4010000  mov edx, 1B4
:7264 8D45AB      lea eax, dword ptr [ebp-55]
:7267 E800000000  call 726C
:726C 85C0        test eax, eax
:726E 7402        je 7272
:7270 B301        mov bl, 1

Efectivamente, dentro de MMUtils.dcu hay un código de chequeo idéntico a nuestro chequeo #1.  Probamos el mismo parche, sólo que esta vez lo hacemos sobre el *.DCU y no sobre el ejecutable. Compilamos un par de ejemplos más cualesquiera y, tras ejecutarlos,  nos damos cuenta de que estábamos en lo cierto.
 

Sin embargo, ¿qué hay del chequeo de 30 días del que se habla en el acuerdo de licencia?
 

[...]

A. Registration: Registering MMTools gains you the Delphi installable VCL units or OCX controls with no runtime limitations.  Applications compiled with the evaluation version of MMTools execute for 30 days and only if a development system is running. Registration is valid for the current major version of MMTools.  As a registered user of MMTools you are entitled to all maintenance releases for the current version.

[...]

Un vistazo al código desensamblado de MMUTIL32.DLL nos proporciona esta hermosura:
 

Exported fn(): _CheckTime - Ord:0003h
:409F90 FF05B4D24000    inc dword ptr [40D2B4]
:409F96 833DB0D2400002  cmp dword ptr [40D2B0], 2
:409F9D 7C1B            jl 409FBA
:409F9F 833DB4D2400002  cmp dword ptr [40D2B4], 2
:409FA6 7C12            jl 409FBA
:409FA8 803DCCD2400000  cmp byte ptr [40D2CC], 0
:409FAF 750C            jne 409FBD
:409FB1 E852FDFFFF      call 409D08
:409FB6 84C0            test al, al
:409FB8 7503            jne 409FBD
:409FBA 33C0            xor eax, eax                ; período de evaluación terminado
:409FBC C3              ret
:409FBD B001            mov al, 1                   ; todo va bien
:409FBF C3              ret

Si la función devuelve 0, nuestro período de evaluación ha terminado, y los programas nos muestran una ventana como la siguiente al arrancar:


Lo cierto es que, aunque durante nuestras pruebas nunca hayamos conseguido hacer que esta función devuelva 0, más nos valdría parchearla, por si las moscas.  Una solución rápida y “económica” podría ser la siguiente:
 

:409FBC 90              nop              ; ocurra lo que ocurra siempre devolvemos "1"
Y EN EL PRÓXIMO CAPÍTULO:
Este tutorial empezó de una forma, y ha acabado de otra, así que mal podemos prever de qué podríamos tratar en una próxima entrega de esta serie.  Sin embargo ahí va alguna pista ...  ¿os habéis fijado en que ninguno de los 9 productos analizados se asegura de que el ejecutable esté realmente corriendo “desde dentro” del IDE?
 

Sin más, un saludo y hasta la próxima, agur.
 


______________________
 

Los saludos de rigor (en ningún orden especial) para ...

+ORC, +Fravia y la +HCU al completo, el Equipo RHF/ZHR al completo (Txente "Mr. 400 Mb", Soraya "Pikatxu", Ainhoa "La Bruja" y Tommy), Tron, Pipo, Itziar, Ibon, Fredo, Jon, Alazne, Gloom Lord, Nieves, Roberto y Mari Mar, Pin y Pon (donde quiera que estéis ...  ¡seguid allí!) y, por supuesto, a todos los colores de [WkT!], desde el Blanco hasta el Negro, incluyendo a todos los que se fueron y a los que aún quedan por llegar
(¿tal vez tú, estimado lector?).
 

______________________
 

Euskal Herria, 2000/03/12
© 2000 Aitor, +HCU & [WkT!]

[ Entrada | Documentos Genéricos | WkT! Web Site ]
[ Todo el ECD | x Tipo de Protección | x Fecha de Publicación | x Orden Alfabético ]
(c) Whiskey Kon Tekila [WkT!] - The Original Spanish Reversers. 
Si necesitas contactar con nosotros , lee esto antes e infórmate de cómo puedes ayudarnos