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