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.
|