|
|
 |
ESTUDIO
COLECTIVO DE DESPROTECCIONES
Última Actualizacion:
25/10/2001
|
 |
|
Programa |
Cuentapasos
3.75 |
W95 / W98 / NT |
Descripción |
Control del gasto telefónico |
Tipo |
Shareware |
Tipo de Tutorial |
[X]Original, []Adaptación,
[]Aplicación, []Traducción |
Url |
http://www.cuentapasos.com |
Protección |
Nag Screen.
Periodo evaluación de 30 dias |
Dificultad |
1) Principiante, 2) Amateur,
3) Aficionado, 4) Profesional, 5) Especialista |
Herramientas |
SoftIce v4.0, SmartCheck 6.0, Spy++ |
Objetivo |
Simular estar registrados. |
Cracker |
Mr. Blue |
Grupo |
Whiskey
Kon Tekila |
Fecha |
18 de Octubre de 1999 |
INTRODUCCION |
Quién no haya intentado crackear este clásico,
o alguna de sus versiones predecesoras, que me tire la primera
piedra...
Todos hemos oído hablar del Cuentapasos que, a pesar
de existir otros programas equivalentes de distribución
gratuita, sigue siendo una codiciada presa. Nos encontramos
ante una aplicación escrita en Visual Basic, en la
que el autor ha intentado protegerla por todos lados, tapando
entradas y huecos, cerrándonos puertas de formas
más o menos inteligentes. Y sabemos que cuanto más
interés pone el autor en proteger una aplicación,
más interesante hace a la presa, desde el punto de
vista didáctico que es el que nos mueve a los miembros
de WKT!.
Sirva el presente tutorial para demostrar la gran dificultad
que supone para cualquier programador realizar una protección
eficiente de sus aplicaciones cuando estas están
escritas en Visual Basic. La nueva forma de compilación
en p-code no hace más que facilitar las cosas
a los amantes de la ingeniería inversa. De hecho,
durante el tutorial se mostrará que desproteger un
programa de Visual Basic p-compilado es mucho más
sencillo e inmediato que el mismo compilado en código
nativo.
Aprenderemos, además de cómo pelearnos con
éxito contra el p-code, otras técnicas
exóticas. Reconozco que podría haberse
hecho de otra forma menos elaborada, pero nuestra meta no
es crackear; es aprender, enseñar
y mostrar nuevas técnicas crackeando.
|
RECONOCIMIENTO
Y EXPLORACION |
Veamos a que nos enfrentamos. Al ejecutar el programa,
nos aparece una pantalla de bienvenida, en la que posteriormente
aparece que es una versión de evaluación.
Sobre esta pantalla aparece otra que nos muestra los datos
de la aplicación, dirección de correo electrónico
del autor, etc... También aparece un código
de compra, tanto en el título como en el botón
de la derecha.
Lo que nos interesa es que aparecen los días transcurridos
del periodo de evaluación y los días que nos
restan que no es más que treinta menos los días
transcurridos. Además aparecen dos botones, Aceptar
y Salir. Estos serán nuestros enemigos,
la nag-screen y las rutinas que calculan los días
restantes de evaluación. Nuestros objetivos serán
que el usuario no se vea obligado a pulsar Aceptar
para arrancar la aplicación (es irritante) y por
supuesto, "ampliar" el periodo de evaluación
(que alguien puede pensar que es insuficiente).
Es una buena costumbre, para empezar a tomar contacto con
el programa, y sobre todo en esta fase inicial en la que
tenemos que planear nuestra estrategia, pasar a la víctima
por un detector de formatos como el GetTyp.
La salida del GetTyp nos muestra que el ejecutable se encuentra
comprimido. Más explicaciones en el tutorial
de Mr.Orange sobre descompresión
manual con ProcDump. Al encontrarse comprimido, no podremos
realizar un parcheador que actúe sobre el fichero
en disco, tendremos que descomprimirlo previamente.
|
EL
RATON VIRTUAL |
Para saltar la nagscreen vamos a utilizar un método
clásico, que muchos pueden desconocer. Vamos a simular
un click del ratón sobre el botón Aceptar,
sin intervención del usuario. Es un método
en desuso pero elegante y limpio, que nos permite comprender
un poco mejor el funcionamiento del Windows y aprender muchas
cosas, que es para lo que estamos aquí.
¿Cómo se hace? Lo primero que debemos hacer
es obtener el identificador del proceso del cual queremos
depende el botón que queremos pulsar. Esto puede
hacerse de dos maneras, o ejecutamos nosotros el proceso
vícitma mediante CreateProcess o nos enganchamos
al proceso que ya está en ejecución. Lógicamente,
lo que a nosotros nos interesa es lo primero, para lo que
crearemos un cargador del programa que será el que
cargue a la aplicación víctima, esperará
a que se cargue y buscará una referencia o handle
de la ventana en la que se encuentra el botón que
queremos pulsar. Después, buscaremos en esa ventana
el botón que nos interesa y finalmente simularemos
un click sobre él.
Para buscar la ventana se utiliza la función EnumThreadWindows.
Esta función sirve para enumerar todas las ventanas
padre asociadas al thread que le pasamos como parámetro.
Para ello, utiliza una función de tipo Callback
que no es más que una función escrita por
nosotros, que es ejecutada por EnumThreadWindows
cada vez que encuentra una ventana. EnumThreadWindows
le pasa a nuestra función de Callback el puntero
a la ventana que ha encontrado.Nuestra función ,
a su salida, devolverá un booleano: False
si ya se ha encontrado la ventana o True en caso
contrario. EnumThreadWindows no devolverá
el control a nuestro cargador hasta que la función
de Callback haya devuelto un False o bien
se hayan enumerado ya todas las ventanas.
¿Y cómo sabemos si una ventana es la que
buscamos o no? Para eso, nuestra función de Callback
puede utilizar varias funciones:
- GetClassName. Devuleve el nombre de la clase
de la ventana que le pasamos como parámetro.
- GetWindowText. Devuelve el texto o título
de la ventana.
- GetWindowRect. Devuelve las coordenadas de la
esquina superior izquierda y esquina inferior derecha
de la ventana.
Una vez que hayamos encontrado la ventana que contiene
el botón que queremos pulsar, debemos encontrar un
puntero a este botón. En este caso, la función
es EnumChildWindows, a la que le pasamos el puntero
a una ventana padre, y nos enumera todas la ventanas hijas
de ella (botones, cuadros de texto,...). Su funcionamiento
es idéntico al de EnumThreadWindows.
Ya tenemos el botón, ahora, a pulsarlo. Esto se
realiza mediante PostMessage, que manda a la ventana
que queramos, el mensaje que queramos. En este caso, enviaremos
a un botón el mensaje BM_CLICK, que simula pulsar
y soltar el botón del ratón.
Más información y ejemplos sobre
este tema en las páginas de Fravia,
en la sección '+HCU's Papers': Simulating
User Input to Eliminate Nag Screens por bb.
|
ATRAPEN
A ESE BOTÓN, ATRÁPENLO! |
Botones al
borde de un ataque de nervios
Antes de nada tenemos que recabar información sobre
el botón que queremos "pulsar". Volvemos
a arrancar el Cuentapasos pero dejamos sin pulsar el botón
Aceptar. Arrancamos el Spy++ (o similar) para
que nos muestre los parámetros de los componentes
de la nagscreen, en especial los nombres de clase de los
objetos de interes, el texto que aparece y las coordenadas.
Pulsamos en la opción 'Find' (Alt+F3) y pasamos el
punto de mira por todos los componentes de interés.
Por si no os habeis dado cuenta todavía, de ejecución
en ejecución, los botones Aceptar y Salir
se intercambian de manera aleatoria. Así, el autor
evita una de las posibles formas de distinguirlos, mediante
su situación en pantalla que nos da la función
GetWindowRect.
Tanto la nagscreen como la ventana de presentación
son de la clase ThunderRT5Form,
para poderlas distinguir nos tendremos que fijar en el título,
ya que la pantalla de presentación no tiene título
y la nagscreen tiene "Versión de
Evaluación P...". Por lo que parece,
no vamos a tener problemas para encontrar la ventana padre
de nuestro botón.
Vamos a los botones. Podemos olvidarnos
del botón "Comprar &Programa
P...", ya que es facilmente identificable por
tamaño, situación y texto. Tanto el botón
Aceptar como Salir, tienen exactamente el
mismo tamaño, evitando así el autor del programa
que podamos distinguirlos mediante la función GetWindowRect
que también permite conocer el tamaño de una
ventana. Los dos son de la clase
ThunderRT5CommandButton (tampoco podremos distinguirlos
por el nombre de la clase), y el texto... oh, oh. Ninguno
de los dos tienen texto, así que tampoco nos sirve
GetWindowText. Y algunos se preguntarán, "Entonces
¿Aceptar y Salir qué son?" Pues está
claro, si no son cadenas de texto serán ... imágenes.
Para probarlo, cambiad el color de las ventanas de Windows
y veréis que, aunque el botón cambia de color,
las palabras Aceptar y Salir están
enmarcadas por el fondo gris que trae por defecto el Windows.
Como podeis ver, el autor ha obrado inteligentemente y se
ha informado convenientemente sobre técnicas de ingeniería
inversa (a lo mejor hasta nos lee a nosotros).
Bien, no nos pongamos nerviosos.
Tenemos dos botones que al parecer, cambian de sitio de
manera aleatoria pero ... el orden de creación de
los botones siempre será el mismo, independientemente
de donde se situen. Si excarvamos más profundamente
en la ayuda de la API nos encontramos la función
GetNextWindow, que dado el puntero a una ventana
hija, nos da el puntero a la ventana hija siguiente o a
la posterior. Ya tan solo queda por descubrir que posición
ocupa el botón Aceptar en el orden de creación.
De hecho, la función EnumChildWindows enumera
las ventanas hijas en el orden de creación, bastaría
con coger, por ejemplo, la cuarta ventana que nos enumere
suponiendo que el botón Aceptar sea la cuarta.
Bueno, vamos a ver. Volvemos al Spy++
con esperanzas renovadas y pulsamos las propiedades del
botón Aceptar de la nagscreen. En mi caso
es el botón de la izquierda y es el último
que aparece en la lista, si pinchamos en la etiqueta 'Windows'
de la ventana de propiedades del botón podemos recorrenos
sus predecesores, y al ser el último, no tiene sucesores.
Veamos si esto es cierto cuando Aceptar cambia de
sitio. Cerramos el Cuentapasos y lo ejecutamos hasta que
el botón Aceptar sea el del centro. Volvemos
a ir al Spy++ y el botón Aceptar..... joderl,
ahora es el penúltimo. Será "joío".
Parece que el autor se ha informado "demasiado bien".
Esto significa, que los botones no
cambian de sitio, lo que cambia es la funcionalidad de cada
uno de ellos, y la imagen que tienen incrustrada. No podemos
distinguirlos por la posición, ni por el tamaño,
ni por el texto, ni por la clase, ....
Soluciones
- Registrarnos. :-(
- Aplicar la solución propuesta por bb
en Simulating User Input to Eliminate
Nag Screens.
- "Amarrar" a ese travieso
botoncito en un sitio fijo.
La solución propuesta por
bb, para el WinZip (un
problema idéntico al nuestro) es utilizar la función
GetPixel para poder distinguir qué imagen
tiene cada botón. Para unas coordenadas dadas, esta
función devuelve el color del pixel correspondiente.
Conocemos las coordenadas de los dos botones, tan solo nos
queda buscar un pixel en de los botones Aceptar y
Salir que sean disitntos. Esto, con cualquier programa
gráfico esta "chupao". Sería la
solución ideal, ya que siempre debemos intentar modificar
los menos posible el ejecutable de nuestra víctima.
Como somos más chulos que
un ocho, y lo que queremos es mostrar la facilidades que
nos ofrece Visual Basic, vamos a "pegar" el botón
Aceptar a un sitio fijo, para posteriormente, ametrallarlo
a placer con PostMessage. Para hacer esto, tendremos
que bucear en el código con ayuda del SoftIce y del
SmartCheck, y, lógicamente, cambiar algunas "cosillas"
en las rutinas que deciden dónde se pone cada botón.
Descubriremos la impactante sensación de "no
me entero de ná" que se experimenta inicialmente
al sumergirnos en el p-code. Agarraros al ratón
que el viaje va a ser movidito.
|
P-CODE:
DESCENSO A LOS INFIERNOS DE MICRO$OFT |
Número
aleatorios
Antes de entrar en faena, es casi obligado leerse el fantástico
tutorial de Esiel2,
CRACKEANDO EN VISUAL BASIC. Aprenderéis
todo lo básico para para crackear programas de Visual
Basic, y cómo configurar nuestras herramientas adecuadamente
para sacarles el máximo partido al atacar programas
escritos en este lenguaje.
Tal y como hacemos siempre que tenemos una pieza realizada
en Visual Basic, recurrimos al alucinante SmartCheck. Arrancamos
el SmartCheck, cargamos el programa y, lo primero que nos
aparece, a modo de advertencia es lo siguiente:
cpasos32.exe is compiled
to p-code...etc...
Se podría decir que los amigos
de NuMega nos quieren advertir que bajo esas condiciones,
nuestra ya esacasa salud mental puede correr peligro. Tenemos
la oportunidad de arrepentirnos, como en toda aplicación
de Windows que se precie, pero "semos" valientes
y elegimos continuar.
Inicialmente no vamos a necesitar las llamadas
a la API, por lo que podemos habilitar la opción
de suprimir las llamadas a la API. Ejecutamos el Cuentapasos
desde el SmartCheck, habilitando desde el principio la captura
de eventos, y nos acomodamos en la silla, para ver pasar
la película. Cuando aparezca la nagscreen, nos fijamos
que posición ocupa el botón Aceptar
y pulsamos Salir . Como vamos a intentar localizar
donde pueden estar las rutinas que deciden donde se situa
cada botón con llegar a la nagscreen tenemos de sobra.
Nos vamos a la zona en la que se carga la nagscreen, al
final. Buscamos el evento frmRegistro_Load:

Como puede verse, lo último que hace
la aplicación al cargar la nagscreen es inicializar
las propiedades de los botones. En esta ocasión,
el botón Aceptar me ha salido a la izquierda,
por lo que tus resultados serán distintos si te ha
salido en el centro. Así, en el evento 15242, se
asigna la propiedad Caption del botón que
muestra la ayuda de como registrarse. A partir del evento
15244, se asignan las propiedades a otros dos botones que
no pueden ser otros que lo que a nosotros nos traen de cabeza.
Se cargan las imágenes 'imgAcepto' y 'imgSalir',
y se asignan a la propiedad Picture de cada uno de
los botones. Al botón 'cmdAceptar(1)' se le activa
la propiedad Cancel. Si desempolvamos la ayuda de
Visual Basic, encontramos que si esta propiedad es TRUE,
la tecla ESC activará al botón. Si volvemos
a cargar el Cuentapasos (sin el SmartCheck) y pulsamos ESC
en la nagscreen, la aplicación termina. Esto significa
que en este caso el botón 'cmdAceptar(1)' está
funcionando como Salir. Conclusión, 'cmdAceptar(0)'
es el botón de la izquierda y el otro el del centro.
Esto podemos probarlo ejecutando varias veces el Cuentapasos,
y viendo como al cambiar Aceptar al centro, la propiedad
Cancel se la activa a 'cmdAceptar(0)'.
Veis lo que hay en el evento 15243. Justo
después de inicializar el botón de Comprar
y justo antes de los de Aceptar y Salir, se
genera un número aleatorio mediante la función
Rnd(). Por su delatadora
situación, creo que todos apostaríamos el
brazo a que la decisión de dónde se colocan
los botones, tiene mucho que ver con el numerito obtenido.
Si nos las amañamos para que dicho numerito tenga
siempre un valor fijo, el botón Aceptar se
quedará fijo.
Vemos desde donde se invoca la función
Rnd(),
¿desde "MSVBVM50.DLL!000FE7BD"? Arrea,
¿qué es esto?. Comenzamos a mirar el resto
de funciones y TODAS se ejecutan desde las direcciones 0FE7BD
y 0FE7ED de MSVBVM50.DLL, exceptuando aquellas que tengan
que ver con objetos visuales (forms, botones,...)
¿Se nos vuelve loco el SmartCheck con programas
p-compilados?
La puerta
del sótano
Vamos a ver si el SoftIce nos aclara algo. Lo primero que
debemos hacer, como con todos los programas de Visual Basic,
es cargar los símbolos de las librerías de
VB. En este caso, como nos lo ha "chivado" el
SmartCheck, es la librería MSVBVM50.DLL. Lo de "MSVBVM"
debe ser algo así como MicroSoft Visual Basic
Virtual Machine, o máquina virtual de Visual
Basic (toda una máquina de torturas, ya vereis...)
Consultamos cuál de las funciones que exporta, puede
ser Rnd(). Tenéis
un listado de todas las funciones que se exportan en el
tutorial de Mr.Brown
para VB5. Nos saltan a los ojos dos de ellas:
Addr:0F004A95 |
Ord: 593 (0251h) |
Name: rtcRandomNext |
Addr:0F03AE08 |
Ord: 594 (0252h) |
Name: rtcRandomize |
Visual Basic dispone de dos funciones para generar números
aleatorios:
- Randomize.
Se utiliza para inicializar el generador de números
aletorios. El Cuentapasos la utiliza, por ejemplo, al
inicio del frmRegistro_Load.
- Rnd().
Devuelve un número aleatorio entre 0 y 1.
La función Rnd()
se corresponderá con la rtcRandomNext
exportada por la Dll. Lo primero que debemos ver es el número
de veces que el Cuentapasos llama a la función Rnd()
antes de la llamada que nos interesa. Nos colocamos en el
SmartCheck y buscamos las llamadas a Rnd().
Se producen dos llamadas, la nuestra es la segunda.
Bajada a
los infiernos
Nos vamos al SoftIce, y colocamos
un bpx rtcRandomNext.
Volvemos al Windows y ejecutamos el Cuentapasos. Vemos como
se carga el splash y ... BOOM... salta el SoftIce. Como
la que nos interesa es la segunda llamada, pulsamos Ctrl+D
y enseguida vuelve a saltar el punto de ruptura. No nos
importa que es lo que hace la función rtcRandomNext
para generar el número aleatorio, solo nos interesa
saber que es lo que el Cuentapasos se propone hacer con
él. Pulsamos F11 y salimos justo destrás del
call que llamó a la función, en la
dirección 0F0FE7C0h de la Dll del VB.
La función Rnd()
devuelve un número entre cero y uno, por lo que debe
devolverlo en los registros de punto flotante. Para ver
el contenido de esos registros desde SoftIce escribimos
wf. Efectivamente, el
número aleatorio generado se encuentra en el registro
ST0.
Empezamos a ejecutar paso a paso
pulsando F8, y en seguida encontramos algo interesante:
0F0FE7CC |
mov al, [esi] |
0F0FE7CE |
inc esi |
0F0FE7CF |
jmp ds:[eax*4+0F0FED94] |
Cargamos en AL el contenido de la dirección apuntada
por ESI (0F4h), incrementamos ESI y saltamos a una dirección
que depende de lo que hemos leído con ESI. Si vemos
a dónde esta apuntado ESI, descubriremos que está
apuntando al código de nuestro querido Cuentapasos.
Tras el salto, aterrizamos en 0F0FDE15. Seguimos pulsando
F8 y vemos como se carga en la pila un byte de valor 0Ah
que está en el código del Cuentapasos (siempre
apuntado por ESI). Nuevamente, uno de los bytes del código
del ejecutable (0EBh) es utilizado para efectuar un salto
exactamente igual que antes, al contenido de la dirección
0EBh*4+0F0FED94=0F0FF140
Esta vez vamos a parar a 0F0FD5E5, en donde el 0Ah cargado
anteriormente en la pila es volcado en el registro ST0,
desplazando el número aleatorio al ST1. Se elimina
el 0Ah de la pila y se vuelve a realizar otro salto igual
que el anterior.
Caemos en 0F0FDFCB. Vamos a parar un momento a ver si nos
aclaramos la cabeza antes de que cometamos una locura con
el monitor y la silla ...
Una luz al
final del pasillo...
Nos desplazamos por la ventana de código y le echamos
una mirada a los alrededores de donde estamos situados.
Nos encontramos porciones de código de 5-10 instrucciones,
todas terminadas por el mismo código que cité
antes; carga de un byte apuntado por ESI en AL, incremento
ESI y salto al contenido de la dirección calculada
como EAX*4+0F0FED94h.
Vamos a echarle un vistazo a esa especie de tabla de direcciones
que parecen usar todas las porciones de código al
terminar para proseguir con la ejecución. Miremos,
por ejemplo, en la dirección 0F0FF140h, que se cálculo
al final de la parte de código que cargo el 0Ah del
código del Cuentapasos en la pila. Encontramos en
dicha dirección, 0F0FD5E5h, que es la dirección
de la porción de código en la que se carga
el contenido de la pila en ST0. Las direcciones que se encuentran
alrededor, apuntan igualmente a porciones de código
que al finalizar, vuelven a recurrir a esta tabla para ver
a donde saltan.
Del infierno
al cielo
Dicho en otras palabras, el ejecutable
(cpasos32.exe) nunca es ejecutado. Su código es leído
e interpretado completamente por la Dll. Por lo tanto,
todas y cada una de las instrucciones en ensamblador que
conocemos, tendrán que tener una porción de
código en la Dll que las sustituya.
Volvamos al inicio, a la dirección 0F0FE7CCh, justo
después de volver de la función Rnd().
Allí, cargamos del ejecutable un byte, 0F4h, que
nos hizo saltar a una dirección en la que se cargo
el siguiente byte del ejecutable, 0Ah, en la pila. Por tanto,
nuestro "push 0Ah" del ensamblador que en código
máquina se codifica como "6A 0A", en p-code
es "F4 0A". El F4h será interpretado como
"introducir el siguiente byte en la pila" por
la tabla situada en 0F0FED94h, al ejecutarse con la Dll.
No es más que la filosofía de Visual Basic,
en la que las funciones más generales no se implementan
en los ejecutables, si no en unas librerías comunes
a todas las aplicaciones de VB, pero llevada a sus últimas
consecuencias. No solo se implementan en las librerías
las funciones más usuales si no que además
se meten TODAS las instrucciones que puede ejecutar la aplicación.
Algunos pensarán, "Pues bien, es tan solo una
especie de traducción rara que lo que hace es complicar
las cosas. Es un lenguaje completamente interpretado. ¿Dónde
está esa ventaja del p-code?". Si alguien no
lo ve claro ahora, cuando ataquemos a las rutinas de cálculo
del periodo restante de evaluación, se le abrirán
los ojos.
Clavando
un botón
Recordamos por donde íbamos. Se había generado
un número aleatorio entre 0 y 1 que almacenamos en
el registro ST0. Posteriormente, cargamos un 0Ah del código
del Cuentapasos en el registro ST0, desplazando el número
aleatorio a ST1.
Nos situamos en 0F0FDFCB, en donde multiplicamos el contenido
de los registros ST0 y ST1 almacenando el resultado en ST0.
Nuevo acceso a la tabla de interpretación y aparecemos
en 0F0FD5B5, en donde el contenido del registro ST0 es redondeado
al entero más cercano y volcado a la pila. Se almacenan
los flags FPU (que son como los flags de toda la vida pero
exclusivos de los registros de coma flotante) en el registro
EAX.
Una nueva visita a la tabla y saltamos a 0F0FDE28h para
cargar un valor de 4 bytes del ejecutable (2) en la pila.
En este caso es un "push" de una doble palabra,
no de un byte. Otro paseo por la tabla y caemos en 0F0FE013.
Recuperamos de la pila el 2 anterior en ECX, y lo que volcamos
anteriormente del registro ST0 en EAX. Dividimos este último
por el 2, almacenándose el cociente en EAX y el resto
en EDX. Se vuelca el resto a la pila y nos disponemos a
saltar a otro sitio.
Del número aleatorio generado, tan solo nos queda
el valor volcado a la pila. Al haberse dividido por dos,
este valor será 0 o 1. La aplicación decidirá
las posiciones de los botones según este valor. Nos
hemos quedado con el resto de la división:
round(x*10) / 2
donde 'x' es el número aleatorio generado,
entre cero y uno.
Pulsamos Ctrl+D para ejecutar completamente
el Cuentapasos. Los que tuvierais como resto de la división
un cero, ¿a qué tenéis el botón
Aceptar a la izquierda? ¿A que los que lo
tenéis en centro, el resto os daba uno?
Podemos probar nuestra teoría tanto
con el SmartCheck como con el SoftIce (más rápido).
Ejecutamos varias veces el Cuentapasos, capturando el valor
aleatorio devuelto. Calculamos metalmente cual sería
el resto y vemos si se corresponde con la ubicación
del botón Aceptar.
Todavía podemos ir más lejos.
¿Cómo influye el resto de la división
en la elección de qué hace un botón
y qué hace el otro? Si recordamos lo que vimos con
el SmartCheck, 'cmdAceptar' es un array de dos botones.
'cmdAceptar(0)' es el botón de la izquierda y 'cmdAceptar(1)'
el del centro. Lo que la aplicación parece hacer,
es asignar la función de Aceptar a 'cmdAceptar(resto)'
y Salir a 'cmdAceptar(resto xor 1)'.
Vamos a inmovilizar el puto botoncito de los....
. Tenemos que obligar a que el resto de la división
tome siempre el mismo valor, cero o uno, independientemente
del número aleatorio generado. Se nos pueden ocurrir
dos formas:
- Cambiar las instrucciones. Después del
ejercicio que acabamos de hacer, conocemos los códigos
que corresponden a varias instrucciones (push, división,
multiplicación,..).Así, por ejemplo, podríamos
sustituir las instrucciones que multiplican, redondean
y almacenan en pila el aleatorio generado por un "push
00000002" o un "push 00000001", que ya
sabemos como se codifican en p-code. Así, en lugar
de meter en la pila el aleatorio multiplicado por 10 y
redondeado, meteríamos un entero fijo. Debemos
tener la precaución de que el número de
bytes de la instrucción falsa, sobreescriba completamente
las instrucciones a las que sustituye, por que todavía
no conocemos como es el "nop" ;-)
- Cambiar los operandos. Está es la solución
más fácil y segura.
¿Qué operandos podemos cambiar? Pues tenemos
el 0Ah (de la multiplicación) y el 0000002 (de la
división). ¿Formas de obligar a que el resto
de la división tome un valor fijo? Por lo menos dos:
- Multiplicar el aleatorio por cero en lugar de diez.
Resto de la división cero, Aceptar siempre
a la izquierda.
- Dividir por uno en lugar de dos. Resto de la división
cero, Aceptar siempre a la izquierda.
Si hemos descomprimido el ejecutable, podemos
ir realizando los parches sobre disco para ir comprobando
que efectivamente funcionan. Repetimos los pasos anteriores
para ir anotando las direcciones en memoria en las que se
encuentra el operando que deseamos cambiar, las cadenas
que deberemos buscar para localizar estos operandos en el
ejecutable descomprimido con un editor hexadecimal, etc..
Ya sabeis como se hace esto, ¿verdad?
|
REVENTAMOS
EL P-CODE |
Ahora, nos olvidamos del Cuentapasos, del SoftIce, de la
vecinita esa que está tan buena.... y nos concentramos
en lo que hemos descubierto sobre el p-code.
Para enfrentarse contra programas en Visual Basic normales,
muchos habréis leído tutoriales información
sobre funciones clave de Visual Basic que en un momento
dado nos pueden ser muy útiles. En casi todos, se
recomienda la función __vbastrcomp, utilizada
por Visual Basic para comparar cadenas de texto. Así,
en programas en donde debes introducir una clave de registro
alfanumérica, la aplicación, en algún
sitio, deberá comparar la cadena introducida con
la que la aplicación considera correcta.
En programas escritos en otros lenguajes no interpretados,
la rutina de comparación de las cadenas se incluye
en el ejecutable. Sabemos que está ahí, pero
no sabemos dónde. En Visual Basic, esa función
es una de las que no se incluyen en el ejecutable, si no
que se encuentra en las librerías compartidas con
el resto de aplicaciones de VB. En el caso de __vbastrcomp,
nos basta con colocar un punto de ruptura en esta función.
Como puede ser llamada muchas veces, podemos limitar el
punto de ruptura a que la cadena a comparar sea la que introducimos.
La facilidad de desproteger los programas en Visual Basic,
radica en esta compartición de funciones, que nos
permite conocer, para algunas tareas específicas,
donde colocar nuestro punto de ruptura.
En el caso del p-code, siguen utilizándose estas
rutinas compartidas, pero la novedad es que ahora, no solo
se comparten una serie limitada de funciones, sino que todas
las instrucciones que puede ejecutar un programa se han
portado a una serie de pequeñas "funciones"
en las librerías del VB. Examinando la librería
del VB, podremos identificar gran cantidad de instrucciones,
algunas muy importantes.
El Cuentapasos me permitirá ilustrar la importancia
de este hecho con más claridad.
30-6 = 36
Nos centramos en el cálculo de los
días que nos restan para acabar el periodo de evaluación.
La nagscreen y el 'acerca de...' del Cuentapasos nos muestran
los días transcurridos y los días que nos
restan.
Ahora bien, ¿me puede decir alguien
cómo puede cualquier aplicación (no solo el
Cuentapasos), sabiendo los días transcurridos y el
número de días máximo (30), calcular
los días que nos quedan de evaluación?.....
Llegados a este punto algunos pensareis que
Mr.Blue está completamente "grillao". La
respuesta es clara, restando. La resta, en ensamblador,
se realiza mediante la instrucción "SUB".
¿Y cómo se hace en p-code? Pues como hemos
visto, tiene que exisitir una porción de código
en MSVBVM50.DLL que se utilice para hacer la resta. ¿Vais
cogiendo la onda...? ¿Sentís el cosquilleo
por la espalda ....?
Si localizamos dónde se encuentra esa
porción de código, podemos colocar un punto
de ruptura en ella. Puesto que cualquier programa realiza
un montón de restas, tendremos que limitar este punto
de ruptura a que los operandos tomen el valor que nos interesa.
En el caso de los periodos de evaluación, limitaremos
a que el operando del que se sustrae, tenga como valor el
número de días de evaluación, o el
número máximo de ejecuciones, o... Las posibilidades
son enormes.
¿Y dóde está esa función?
Para encontrarla, podemos seguir traceando el programa,
tarde o temprano hará una resta. O también
podemos, si tenemos el VB, generarnos un p-code que haga
restas, sumas y todas las funciones que queramos descubrir.
No tenemos más que pasarlo por el SoftIce.
La resta nos la encontramos en 0F0FDF78h,
y el resto de funciones aritméticas se encuentran
a su alrededor. Encontramos multiplicaciones de enteros,
de flotantes, operaciones lógicas,... todo un surtido.
La función de resta de enteros, al
igual que la de suma de enteros que está más
arriba, se encuentran para operadores de 16 bits y para
operadores de 32 bits. Si queremos colocar puntos de ruptura,
los tendremos que colocar por duplicado:
0F0FDF78 |
pop eax |
0F0FDF79 |
sub [esp], eax |
0F0FDF7C |
jo 0F0FDAC4 |
0F0FDF82 |
xor eax, eax |
0F0FDF84 |
mov al, [esi] |
0F0FDF86 |
inc esi |
0F0FDF87 |
jmp ds:[eax*4+0F0FED94] |
Esta función, saca un operador de la
pila y se lo resta al que queda en la misma. El resultado
queda almacenado en la pila, saltando a 0F0FDAC4h en caso
de overflow (EAX>(ESP)).
En mi Cuentapasos, los días transcurridos
son seis así que coloco un punto de ruptura en 0F0FDF79
de la forma bpx 0F0FDF79
if (*(esp)==1E)&(eax==6) .
Lo colocamos también en la versión
de 16 bits, por si las moscas: bpx
0F0FDF62 if (*(esp)==1E)&(ax==6) .
Pulsamos Ctrl+D, ejecutamos el Cuentapasos
y aparecemos en 0F0FDF79. Efectivamente, la aplicación
está intentando hacer 30-6. Examinamos a donde apunta
ESI. A la dirección 4ADF48h, del ejecutable del Cuentapasos.
En realidad, ESI está ya apuntando a la siguiente
instrucción a ejecutar (hace las veces de EIP). La
instrucción que nos trajo aquí es el byte
'0AEh' de 4ADF47h.
Aquí, al igual que en el caso anterior
podemos cambiar dos cosas:
- Los operandos. Para cambiarlos tendríamos
que retroceder, poner un punto de ruptura antes y ver
de donde saca el Cuentapasos los valores de los operandos.
Para ello, lo mejor es colocar un punto de ruptura de
acceso a memoria en direcciones anteriores a 4ADF47h (nuestros
antiguos bpx, se convierten en bpm's). Después
traceamos y vamos viendo con atención de donde
van saliendo los operandos (ingeniería inversa
hacia atrás).
- La instrucción. Podemos cambiar la resta
por otra instrucción, o mejor, por una suma ;-)
¿Como hacemos esto? ¿Cuál es el código
de la suma? Veamos, el código de la resta (32 bits)
como hemos visto es 0AEh. La función de suma, nos
la encontramos algo más arriba, en 0F0FDF3Dh. Para
saber su código, nos vamos a la tabla de 0F0FED94h.
El puntero a la función resta, debe encontrarse en:
4*0AEh + 0F0FED94h
= 0F0FF04Ch
Nos vamos allí, y efectivamente encontramos
la dirección de la función resta de 32 bits.
Vamos a ver donde está el puntero a la suma, no debe
estar muy lejos. La encontramos cuatro palabras más
arriba, en 0F0FF03Ch. El código de la suma será
por tanto:
(0F0FF03Ch - 0F0FED94h)
/ 4 = 0AAh
El crack consitirá en sustituir en
aquellos lugares en donde se realice la resta, 30-6 (6 en
mi caso), el código de la operación resta
(0AEh) por el de la suma (0AAh).
Pulsamos Ctrl+D y vuelve a saltar enseguida
el SoftIce. En esta ocasión, el culpable es el 0AEh
situado en 4ADFA9h. Lo anotamos y volvemos a pulsar Ctrl+D.
Nos aparece la nagscreen. Vamos a por el 'acerca de...'.
Pulsamos Aceptar y abrimos el 'acerca de...' .. otra
vez nos salta el SoftIce, otro código de resta en
04A4716h. Anotamos y Ctrl+D... otro más en 04A4769h.
Este es el último.
Repetimos la prueba, pero ahora, en el primer
punto de ruptura, sustituímos los bytes 0AEh de 4ADF47h,
4ADFA9h, 4A4716h y 4A4769h por 0AAh. Realizar un cargador
que, además de pulsar el botón de la nagscreen
tal y como se ha explicado, realice los parches en memoria
necesarios es sencillo. Tenéis un ejemplo en otro
tutorial de Esiel2,
que casualmente versa sobre el antecesor del actual Cuentapasos.
Quitamos los puntos de ruptura, para probar,
Ctrl+D y ......
..... ;-) p-code rulezzz!!
|
CONCLUSIONES
|
Hemos mostrado como una protección como la de la
nagscreen, correctamente implementada por el autor de la
aplicación, puede ser fácilmente burlada porque
el autor cometió el "error" de programar
en Visual Basic. De paso, hemos sacado del baúl una
técnica casi olvidada pero de una gran potencia,
mediante la cual podríamos entre otras cosas, automatizar
tareas, rellenar formularios automáticamente, gastar
bromas, etc...
Hasta aquí, nada nuevo.
La novedad es el p-code. Inicialmente, en
una primera toma de contacto, los programas p-code confunden
y pueden terminar por aburrir al más intrépido
ingeniero inverso. Está invención de Microsoft,
desde el punto de vista teórico, puede parecer un
gran avance ya que está a un paso de la creación
de programas que puedan ejecutarse en cualquier máquina,
independientemente del procesador o sistema operativo, siempre
y cuando existan unas librerías diseñadas
para dicha máquina o sistema operativo que interpreten
el código del ejecutable.
Nada más lejos de la realidad. Lo que
en código nativo es una simple instrucción
como la resta, se convierte en los programas p-compilados
en varias instrucciones. Igual ocurre con el resto: push,
add,... Esto, inevitablemente conduce a que las aplicaciones
se enlentezcan o, lo que es lo mismo, que necesitemos más
recursos (procesadores más rápidos) para poder
mantener las mismas prestaciones. Esta política de
Microsoft, por supuesto favorece a los fabricantes de hardware
como Intel.
Desde el punto de vista de la ingeniería
inversa, supone una metedura de pata más de Microsoft
(y van ...). Identificando convenientemente las funciones
de interés, podremos facilmente desentrañar
las protecciones de las aplicaciones. En este tutorial se
ha expuesto un método que puede, sistemáticamente,
echar abajo casi cualquier sistema de protección
de aplicaciones p-compiladas, basado en limitaciones en
el número de ejecuciones, periodos de pruebas, etc...
Es cuestión de acostumbrarse, donde poníamos
antes breakpoints de ejecución ahora ponemos breakpoints
de acceso a memoria, donde el EIP decía ahora dice
el ESI,...
En cierto modo es triste ver como los desvelos
de un programador por proteger su aplicación, colocando
protecciones a diestro y siniestro, y creando un sistema
de protección en conjunto bastante robusto, salta
en mil pedazos por el hecho de haber utilizado Visual Basic
y su "p-compilación". Si el futuro del
Visual Basic pasa por el p-code ... o le abrimos los ojos
a los programadores y reaccionan o creo que la ingeniería
inversa de VB se va a volver muy aburrida.
Para ilustrar dicho método, lo hemos
utilizado contra una de las pocas aplicaciones p-compiladas
que he podido encontrar (por ahora): nuestro entrañable
Cuentapasos. Pero no creais que sus protecciones se limitan
tan solo a la nagscreen y al periodo de evaluación.
¡Que va! El Cuentapasos no se acaba ahí, esconde
muchos más secretos. Desviaros de la senda seguida
en este tutorial para deshacer su protección, buscad
caminos alternativos. Quedan muchas preguntas por responder:
- Dónde se guardan los días transcurridos?
O se guarda la fecha de instalación?
- Dónde se guarda el otro protagonista de la resta,
el límite de 30 días? ¿Y si lo cambiamos?
- Cómo se registra el programa? De dónde
sale el código de compra?
- Por qué no cambia el número de días
transcurridos si cambio la fecha? Cuándo se actualiza?
- El cálculo de los días transcurridos,
es igual al ejecutar las primeras veces el Cuentapasos
que después?
- Por qué en algunos ordenadores se cierra el programa
a los pocos minutos de conexión?
Investigad, y os encontrareis destellos de calidad del
programador, de los que da gusto encontrar en un producto
nacional, y alguna que otra metedura de pata, de las que
hacen que te caigas al suelo de risa ;-D
Como veis, se podría escribir un libro
sobre el Cuentapasos (este tutorial ya es casi un libro).
En cierta manera somos nosotros los que obligamos
a los programadores a profundizar en el arte de la programación.
Y gran parte de lo que aprenden en el sano intento de "jodernos",
lo aplican en otras facetas de sus aplicaciones, mejorando
sus prestaciones. Aunque la mayoría no lo quieran
reconocer, parte de lo que saben nos lo deben a nosotros,
los "crackers".
Y por supuestísimo, parte de lo que
sabemos nosotros se lo debemos a las "comeduras de
coco" de ellos a la hora de implementar una protección.
Agradecimientos:
Esiel2/TNT |
.... ves lo que te has perdido? ... ;-P |
Mr.BlacK/WkT! |
.... por sus orientaciones en esta jungla
.... |
bb |
.... no te conozco, no te he visto, pero
tu tutorial es una gozada ..... |
Fravia |
.... La Biblia On-Line .... |
WkT! |
.... por creer en un novato .... |
|
|
Toby |
.... ha sido todo un placer "pelearme"
contigo (y olvídate del VB) .... |
Mr. Blue
.... era una raza muy atrasada .... |
.... al borde del siglo XXI aún utilizaban
aplicaciones Micro$oft ... |
|
[
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 |
|
|
|
|
|