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

Programa Paint Shop Pro v6.02 W95 / W98 / NT
Descripción Utilidad edición gráfica
Tipo Nag/Trial de 60 dias 
Tipo de Tutorial [X]Original, []Adaptación, []Aplicación, []Traducción
Url http://www.jasc.com
Protección Nag Screen. Time Limit 60 Dias
Dificultad 1) Principiante, 2) Amateur, 3) Aficionado, 4) Profesional, 5) Especialista
Herramientas SoftIce v4.05, Exescope v6.0
Objetivo Eliminar Nag Screen y límite de tiempo.
Cracker Mr.YelloW
Grupo Whiskey Kon Tekila
Fecha 30 de Julio de 2000 

INTRODUCCION
Paint Shop Pro es un conocido programa de edición gráfica, el cuál siempre se ha distinguido por utilizar en sus versiones de evaluación un Nag Screen. Este tutorial pretende explicar otra forma de eliminar este odioso Nag a través de los mensajes de windows.
AL ATAQUE
La principal peculiaridad de la protección de este programa es que no es tan sencillo como eliminar la llamada a la creación del dialogo y seguir "palante". Esto se deba pobablemente a que después de pulsar el botón OK del Nag, se ejecutan algunas determinadas funciones vitales para el buen funcionamiento del programa.

Sin embargo, hay un pequeño problema, dicho botón no existe, solo es un dibujo. Lo que el programa realmente hace es verificar que la pulsación del ratón se hizo en un area determida.

Entonces, sino tenemos más pelotas que pulsar el dichoso botoncito, dejemos que el programa lo haga por nosotros ;)

Os preguntareis como es posible esto. Pues muy sencillo, toda la comunicación entre el sistema operativo y los dialogos se realiza a través de mensajes. Cada dialogo tiene su propia pila de mensajes que atenderá a medida que los vaya recibiendo. De esto se encarga la función de dialogo que tiene asociada toda ventana.

Por ejemplo, cuando se pulsa una tecla, windows le comunica al dialogo que se ha pulsado una tecla, y el código del programa actuará en consecuencia.

De modo que basándonos en este funcionamiento, podemos generar un mensaje de pulsación del botón izquierdo del ratón sobre una determinada ventana en una determinada coordenada, sencillo verdad? ;)

Analicemos que funciones del núcleo nos permiten hacer esto. Por un lado tenemos la función SendMessage y por el otro PostMessage. La principal diferencia entre estas dos funciones es que la primera funciona de forma síncrona y la otra de forma asíncrona. Ahora en cristiano, la primera no devuelve el control hasta que no se tenga la certeza de que el mensaje ha llegado y se ha procesado correctamente. Sin embargo, PostMessage envía el mensaje, pero no hay ningun tipo de garantía de que la ventana lo reciba y lo procese. En este caso voy a utlizar la función PostMessage, pero mas adelante explicaré mis razones.

Los prototipos de ambas funciones son iguales, de modo que la siguiente explicación sirve para las dos.

LRESULT  WINAPI  SendMessageA (HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
BOOL  WINAPI  PostMessageA (HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

El primer parámetro corresponde al manejador de la ventana al que se quiere enviar el mensaje, en este caso sería el manejador de la ventana del Nag. El segundo parámetro se refiere al tipo de mensaje que se quiere enviar. Una muy pequeña lista de los mensajes mas usados sería : WM_CHAR, WM_COMMAND, WM_CREATE, WM_DESTROY, WM_LBUTTONDOWN, WM_PAINT, WM_QUIT, WM_SIZE, WM_TIMER, ...........

En nuestro caso, el mensaje que debemos enviar será el de WM_LBUTTONDOWN. El resto de parámetros son utilizados como parámetros, valga la redundancia, por los mensajes. Es decir, estas variables pueden contener los caracteres de la teclas pulsadas, las coordenadas del ratón, los keycodes, .... su contenido varía en función del tipo de mensaje. Aqui deberiamos poner las coordenadas de la posición del botón OK para que el programa reconozca que se ha pulsado sobre este botón.

El código del mensaje WM_LBUTTONDOWN es 201h. Esto se puede ver en cualquier compilador para windows, o incluso en el propio softice escribiendo: ?WM_LBUTTONDOWN . Las coordenadas se pasan a través del parámetro lParam, donde las coordenadas X e Y estan en la parte baja y alta respectivamente de la variable de 32 bits.

Bien, ahora que esta presentada la teoría, vayamos al tema. Hay tres grandes preguntas que deberiamos hacernos antes de empezar. ¿Cuando debemos mandar el mensaje? ¿Cual es el handle de la ventana? ¿Cuales son las coordenadas del botón?

A la primera pregunta podría contestar que inmediatamente despues de la creacion de la ventana. Pero aqui viene el dilema entre utilizar una llamada síncrona o asíncrona. Si enviamos un mensaje síncrono, recibirá el mensaje inmediatamente, pero tendrá el resultado que nosotros deseamos? Quizas deberiamos esperar un poco más antes de enviarlo para que pueda ejecutar otra serie funciones. Con las puebas que yo realicé llegué a la conclusión de que este sistema no funcionaba correctamente, de modo que opté por los mensajes asíncronos, dando así un margen para que atienda el mensaje.

Para buscar la función donde se crea el Nag podriamos empezar poniendo un breakpoint en la API SetTimer, ya que si nos fijamos, hay una temporización de unos segundos. Si hacemos esto, el programa se interrumpe justo depues de aparecer el Nag, en 770F1Fh.

Si continuamos la ejecucion paso a paso, y llegamos a una instruccion RET, salimos a la direccion 653814h.

* Reference To: MFC42.Ordinal:09BE, Ord:09BEh
                                  |
:00653802 E89B771E00             Call 0083AFA2
:00653807 8BCE                         mov ecx, esi
:00653809 E8E71EDBFF            call 004056F5
:0065380E 56                              push esi
:0065380F E87D32DBFF           call 00406A91
:00653814 83C404                     add esp, 00000004    <-- Estamos aqui

* Reference To: MFC42.Ordinal:1926, Ord:1926h
                                  |
:00653817 E8207A1E00              Call 0083B23C

Ahora hagamos la prueba de ejecutar los call de más arriba a ver que pasa. Y bingo!! hemos dado con la función que genera el Nag en la dirección 65380Fh. Ahora solo nos queda examinarla mas detenidamente.

Traceamos el interior del call hasta llegar a la función que carga la imagen y crea el Nag Screen. Esto se realiza en el siguiente codigo.

:00770EC8 E8A534C9FF               call 00404372     ;Aqui se carga la imagen y crea la ventana.
:00770ECD 85C0                            test eax, eax        ;Si es diferente de cero continua normalmente.
:00770ECF 7519                             jne 00770EEA
:00770ED1 8B0DD48C9300           mov ecx, dword ptr [00938CD4]
:00770ED7 3BCF                            cmp ecx, edi
:00770ED9 7407                             je 00770EE2
:00770EDB 8B11                            mov edx, dword ptr [ecx]
:00770EDD 6A01                           push 00000001
:00770EDF FF5204                       call [edx+04]

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00770ED9(C)
|
:00770EE2 893DD48C9300           mov dword ptr [00938CD4], edi
:00770EE8 EB0F                            jmp 00770EF9

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00770ECF(C)
|
:00770EEA A1D48C9300              mov eax, dword ptr [00938CD4]
:00770EEF 8B4820                        mov ecx, dword ptr [eax+20]
:00770EF2 51                                 push ecx           ;Aqui se obtiene el manejador de la ventana.

* Reference To: USER32.UpdateWindow, Ord:0291h
                                  |
:00770EF3 FF1524359900            Call dword ptr [00993524]

El mensaje se puede enviar al final del código de esta función, ya que precisamente hay un hueco vacio, en la dirección lineal 770F4Fh.

Ya sabemos cuando mandar el mensaje, y cual es el handle de la ventana. Ahora nos queda saber las coordenadas correctas del cursor del ratón dentro del dialogo para que la pulsación sea correcta. Para ello debemos encontrar el código que trata las pulsaciones.

Si nos fijamos en el cursor cuando se coloca sobre el botón, este se convierte en un reloj impidiendo que pulsemos sobre este. Esta rutina tiene que estar ligada de alguna manera con algun temporizador.

De modo que vamos a colocar un breakpoint sobre la función KillTimer. Esto nos lleva a este código:

:00772220 8B442404                mov eax, dword ptr [esp+04]
:00772224 56                            push esi
:00772225 83F807                    cmp eax, 00000007
:00772228 8BF1                       mov esi, ecx
:0077222A 0F8588000000       jne 007722B8
:00772230 A1D48C9300          mov eax, dword ptr [00938CD4]
:00772235 8B5620                    mov edx, dword ptr [esi+20]
:00772238 8B4874                    mov ecx, dword ptr [eax+74]
:0077223B 51                            push ecx
:0077223C 52                            push edx

* Reference To: USER32.KillTimer, Ord:0195h
                                  |
:0077223D FF15C8349900      Call dword ptr [009934C8]        ;Aqui se mata el temporizador.
:00772243 A1CC8C9300         mov eax, dword ptr [00938CCC]   ;Prestar atencion a esta variable.
:00772248 85C0                       test eax, eax
:0077224A 7553                       jne 0077229F            ; En caso de que valga cero se pone a 1.
:0077224C 57                           push edi

* Possible StringData Ref from Data Obj ->"5000"
                                  |
:0077224D 6800B79200                          push 0092B700
:00772252 C705CC8C930001000000    mov dword ptr [00938CCC], 00000001

La variable de la direccion 938CCCh indica si estamos dentro o no del tiempo de espera antes de poder pulsar sobre el botón. Para ver la relación entre esta variable y la rutina que verifica la posición del cursor dentro del dialogo, vamos a poner un breakpoint sobre esta variable.

:bpm 938CCC R

Ahora pulsamos sobre el dialogo. Existen dos rutinas. Una se ejecuta cada vez que se mueve el raton. La cual se utiliza para determinar que icono debe mostrar, por ejemplo durante el tiempo de espera.

* Reference To: USER32.ScreenToClient, Ord:020Ah
                                  |
:00772365 FF1508359900            Call dword ptr [00993508]
:0077236B A1CC8C9300             mov eax, dword ptr [00938CCC]    <------- Interrupcion
:00772370 5E                                pop esi
:00772371 85C0                           test eax, eax
:00772373 7525                            jne 0077239A
:00772375 8B542408                   mov edx, dword ptr [esp+08]
:00772379 8B442404                   mov eax, dword ptr [esp+04]
:0077237D 52                               push edx
:0077237E 8D4C2410                 lea ecx, dword ptr [esp+10]
:00772382 50                               push eax
:00772383 51                               push ecx

* Reference To: USER32.PtInRect, Ord:01EAh
                                  |
:00772384 FF15F8349900            Call dword ptr [009934F8]
...................................
...................................
...................................

La otra interrupcion solamente se produce cuando el cursor esta dentro del boton OK. Esto significa algo, vamos a analizar el codigo.

:0077129E 56                          push esi        ; Coordenada Y
:0077129F 8D54242C             lea edx, dword ptr [esp+2C]
:007712A3 57                         push edi        ; Coordenada X
:007712A4 52                         push edx
:007712A5 FFD5                    call ebp
:007712A7 85C0                     test eax, eax
:007712A9 0F84C0000000     je 0077136F    ;Este salto provoca la salida de la funcion
:007712AF A1CC8C9300       mov eax, dword ptr [00938CCC]    <------- Interrupcion
:007712B4 85C0                      test eax, eax
:007712B6 0F848B000000      je 00771347    ;Este salto provoca la salida de la funcion
:007712BC 8B35D48C9300    mov esi, dword ptr [00938CD4]

De modo que esto verifica nuestra teoría, una de las condiciones es que la variable 938CCCh tiene que valer uno. Ahora hay que analizar la otra condicion. Si colocamos un breakpoint en 77129Eh, el programa pasa por aqui cada vez que se pulsa el botón izquierdo en cualquier parte de la ventana. Pero el resultado de la llamada a la funcion en 7712A5h es uno unicamente cuando el cursor esta sobre el botón. Debemos analizar los parametros que recibe esta funcion. Los registros ESI y EDI contienen las coordenadas de la pulsación sobre el botón OK. Ahora todo lo que deberiamos hacer es quedarnos con unas coordenadas correctas.

Ya tenemos contestadas las TRES preguntas cruciales. Ahora solo queda meter el código que genera el mensaje. Como se ha dicho antes, esto lo vamos a hacer en la dirección 770F4Fh.

:00770F4F 56                              push esi
:00770F50 8B35D48C9300        mov esi, dword ptr [00938CD4]
:00770F56 8B7620                      mov esi, dword ptr [esi+20]        ;Cogemos el handle de la ventana.
:00770F59 68C8005C01            push 015C00C8                        ;Coordenadas Y | X (LPARAM)
:00770F5E 6AFF                        push FFFFFFFF                        ;Este dato no afecta (WPARAM)
:00770F60 6801020000              push 00000201                         ;Mensaje WM_LBUTTONDOWN
:00770F65 56                              push esi                                     ;Manejador ventana

* Reference To: USER32.PostMessageA, Ord:01DEh
                                  |
:00770F66 FF15DC349900                        Call dword ptr [009934DC]
:00770F6C 5E                                             pop esi
:00770F6D C705CC8C930001000000      mov dword ptr [00938CCC], 00000001    ;Anulamos el temporizador
:00770F77 C3                                             ret

Con esto seria suficiente para saltarnos el Nag. El mensaje es enviado, y el programa se lo traga inocentemente. }:-)

Queda explicar un pequeño detalle acerca de este código: La llamada a la función PostMessageA. El codigo de las dll's se ubica en diferentes zonas de memoria dependiendo del S.O. u otro tipo de causas. De modo que no podemos hacer una llamada directa a la función. Tenemos dos soluciones: La primera consiste en llamar a las funciones GetModuleHandle y GetProcAddress para obtener la dirección de la función PostMessageA. O por otro lado, si el programa tiene la función PostMessageA en la lista de importaciones, podemos utilizar la dirección de la función en esta tabla de importaciones. En este caso el programa utiliza la función, de modo que va a ser mas sencillo. 

Ahora de lo que se trataría es de averigüar el puntero donde se guarda la dirección de la función PostMessageA. Yo lo he hecho al método tradicional, cogiendo un editor hexadecimal y entrando en la estructura de los descriptores de importación. Pero esto lo dejo como ejecicio para el lector ;) , aunque para no dejarlo muy tirado, voy a decir las direcciones donde esta la información más relevante.

Sección .import: 98F000h
Descriptor USER32.dll: 98F0B4h
Puntero PostMessage: 9934DCh

Bueno, ahora queda la parte mas mundana, quitar la limitación de tiempo y cambiar el dibujo de ese feo Nag Screen.

Para quitar la limitación de tiempo colocamos un breakpoint sobre la función GetSystemTime. Esto nos lleva hasta la dirección 771CC0h donde se compara un número que presumiblemente podría ser el número de días que se esta utilizando el programa y el límite, que son 30 dias.

cmp     ESI,1E
jle        771D0Ch

Esto podria ser sustitudo por:

mov    ESI,1
jmp    771D0Ch

Ya nos hemos quitado la limitación de tiempo, ahora solo queda cambiar la pantallita inicial. Si recordaís, había un sitio donde se cargaba la imagen del Nag de los recursos del programa. El código es el siguiente:

:007714AA 68DC219200              push 009221DC
:007714AF 687F010000                push 00000181    ; Identificador del recurso a buscar
:007714B4 50                                 push eax

* Reference To: KERNEL32.FindResourceA, Ord:00A3h
                                  |
:007714B5 FF15341A9900            Call dword ptr [00991A34]
:007714BB 8BF0                            mov esi, eax
:007714BD 85F6                            test esi, esi
:007714BF 7507                             jne 007714C8
:007714C1 8B07                            mov eax, dword ptr [edi]
:007714C3 8BCF                           mov ecx, edi
:007714C5 FF5070                        call [eax+70]
:007714C8 55                                push ebp
:007714C9 E8A28D0C00             Call 0083A270
:007714CE 8B4008                       mov eax, dword ptr [eax+08]
:007714D1 56                                push esi
:007714D2 50                                push eax
:007714D3 FF152C1A9900          Call dword ptr [00991A2C]
:007714D9 8BF8                           mov edi, eax
:007714DB E8908D0C00             Call 0083A270
:007714E0 8B4008                       mov eax, dword ptr [eax+08]
:007714E3 56                                push esi
:007714E4 50                                push eax

* Reference To: KERNEL32.LoadResource, Ord:01C7h
                                  |
:007714E5 FF153C1A9900          Call dword ptr [00991A3C]    ; Carga del recurso

Si utilizamos el Exescope para hurgar dentro de las tripas del programa, y nos adentramos en la sección de recursos, llegamos hasta una serie de mapas de bits en formato DIB. Entre ellas hay perro muy majo deborando un gato (que sádicos), el Nag Screen (181h) y la presentación sin botones (179h). De lo que se trata es de cambiar el numerito 181h por 179h, y todo esta arreglado.

Como podeis comprobar no ha sido tan dificil. Simplemente es tener las ideas un poco claras, y yo no soy precisamente un buen ejemplo con mi lógica borrosa ;)

Bueno, pues esto ha sido todo. Hasta el próximo tutorial, que no tengo ni idea cuando será.

Gero arte.

[ 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