Cracking 101 - Capítulo 0

Por Cesar.

A petición popular me dispuse a crear otra serie de escritos dedicados al cracking, no como actividad ilegal, sino como el arte que es. ¿Porqué lo hago? la respuesta en sí es sencilla: Por que me gusta crackear. Pero hay otras razones de peso:

  • Muchos de los tutoriales en español disponibles son muy antigüos, y aunque la información sigue siendo válida, a veces es difícil conseguir las herramientas de las que hacen uso, o la versión exacta del programa víctima que usaron
  • Hay un interés por mejores tutoriales, mejor escritos y más accesibles a los usuarios novatos
  • En su tiempo recibí muy buenos comentarios sobre mis propios tutoriales, lo que me lleva a pensar que tal vez tengo algo de talento en eso

Antes de que se quieran poner a crackear sus programas favoritos, hay que estudiar lo que hay detrás de un programa. Es muy similar a tocar la guitarra: uno quiere agarrar la guitarra y tocar Stairway to Heaven, y con la suficiente práctica sucederá, pero tenemos que empezar por unos sencillos acordes primero. Todo es parte del proceso. Aún así, en este tutorial #0 voy a ilustrar la teoría utilizando un programa real, ejecutándose bajo un debugger.

Hay algunos conceptos que tenemos que revisar antes de comenzar a crackear. Algunos parecerán obvios, otros no tanto. Lo importante es tener la cabeza despejada, abrir la mente y comprender cada uno de los detalles. Uno de los conceptos clave es saber cómo se ejecuta un programa en la computadora. Esto nos ayuda a saber cómo atacar al programa. Un programa típicamente se guarda en memoria no volatil, es decir, memoria que se mantiene aún cuando se apaga la computadora (como el disco duro, memoria flash). Cuando el programa se ejecuta, este se copia a la memoria RAM, ya que de ahí el CPU puede accesarlo de manera directa. El CPU ejecuta el programa instrucción por instrucción. Como podemos ver, aún cuando la computadora se compone de múchos subsistemas, el que nos interesa para crackear es el CPU, ahí es donde ocurre la magia.

Para crackear, necesitamos entender cómo es que el CPU ejecuta el programa, el problema es que el CPU entiende instrucciones binarias solamente. Binario es muy conveniente para una máquina ya que es muy fácil de representar por hardware los dos estados (0 y 1), pero no lo es para nosotros y por eso necesitamos lenguajes de más alto nivel. El lenguaje que le sigue al lenguaje máquina es el llamado “ensamblador”. Cada familia de procesadores tiene su propio lenguaje ensamblador. En nuestro caso, necesitamos entender lenguaje ensamblador para el x86.

Algo que sería buena señal es que en estos momentos ya tienen en sus manos un manual para ensamblador x86, o mejor aún, ya lo estudiaron de alguna manera. Como recomendación personal está el libro “The Art of Assembly“. No es específico de cracking, e introduce un concepto de ensamblador de “alto nivel” que podría confundir a algunos, pero tiene una sección que deben leer titulada “Una introducción a la familia de CPUs 80×86 de Intel“. Del lenguaje ensamblador no necesitamos todo, aunque entre más sepas del lenguaje, más se te facilita tu tarea. Lo básico son algunas instrucciones con las que se van a topar al inicio: instrucciones de salto, comparaciones, movimiento de datos, llamadas a funciones y lo referente a la pila (es decir, stack, no pila de batería). Como primer ejercicio, vamos a revisar algunas de estas instrucciones:

Ojo: en este punto ya estoy suponiendo que están familiarizados con los conceptos explicados en la introducción a la familia 80×86 de Intel. Saben lo que son los principales registros del CPU, saben lo que son números binarios y hexadecimal, saben como se ve un programa en ensamblador..

  • MOV: MOVer datos de un lado a otro
  • ADD: Suma
  • SUB: Resta
  • CMP: Compara. Hace lo mismo que SUB pero solamente afecta a las banderas de estado (más de esto próximamente)
  • Saltos: En ensamblador existen varias instrucciones de salto, la más básica es el salto incondicional JMP. La instrucción JMP provoca que la siguiente línea a ejecutar cambie, es decir, la ejecución ‘brinca’ de un lugar a otro. Hay otros saltos condicionales que, basados en el estado de las banderas, deciden si saltar o no saltar. La lista completa de instrucciones de salto la pueden encontrar aquí

Antes de seguir a explicar qué es lo que hacen, y cómo es que se usan las instrucciones anteriores, tenemos que aprender cómo es que funcionan. Lo anterior nos lleva a explorar la arquitectura del CPU: los registros, las banderas (flags) y las interrupciones.

Los registros pueden ser vistos como memoria que va dentro del CPU, y pueden ser usados por las aplicaciones. Existen registros de propósito general, registros especiales, registros de segmento y registros especiales para el modo kernel. Los más utilizados son los de propósito general, estos son:

EAX, EBX, ECX, EDX, ESI, EDI, EBP y ESP

La ‘E’ significa que son ‘E’xtendidos, es decir, de 32 bits. Cuando se nombra al registro sin la ‘E’ (AX, BX, CX, etc.) se refiere a la parte baja del registro. La parte baja significan los primeros 16 bits del registro. Para complicar aún más las cosas, los registros de 16 bits pueden ser separados en 2 registros de 8 bits, por ejemplo el registro AX puede separarse en AH y AL (A High y A Low), de nuevo refiriéndose a los bits más significativos y a los menos significativos, respectivamente.

Para ilustrar el concepto de “parte alta” y “parte baja”, supongamos que tenemos el registro EAX con su parte alta llena de 0’s y su parte baja llena de 1’s. El registro se vería así:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

Decir “la parte baja de EAX” es igual a decir “el registro AX”:

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

Y si ahora modificamos al registro AX para que se vea asi:

0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1

RegistrosNos quedamos con puros 0’s en la parte alta de AX (AH) y con puros 1’s en la parte baja de AX (AL). Lo anterior se entiende mejor con una imagen, sacada del libro “Art of Assembly” que explica esto de los registros, la parte alta y baja, y la relación que existe entre ellos.

Existe un registro especial , el registro de banderas (EFLAGS). Este registro de 32 bits es especial ya que cada uno de sus bits representa un estado del procesador que se encuentra encendido (1) o apagado (0). De los 32 estados o “banderas” que contiene el registro EFLAGS, los más utilizados son:

  • Overflow (sobreflujo): Indica que el resultado de una operación excedió el máximo tamaño posible y por lo tanto el resultado es inválido
  • Carry (acarreo): Indica que el resultado de una operación produjo un acarreo. Un acarreo se produce cuando el resultado de una operación aritmética es más grande que sus operandos. Un ejemplo de esto sería la suma 5 + 5 = 10. El número 5 se representa con un sólo dígito, sin embargo, el resultado de sumar 5 + 5 necesita dos dígitos para ser representado. Lo mismo sucede en binario: si sumamos 1 1 0 0 + 0 0 1 0 = 1 1 1 0 y ahí no se produce acarreo. Pero si sumamos 1 1 0 0 + 1 0 0 0 = 1 0 1 0 0 el resultado tiene 5 dígitos mientras que sus dos operandos son de 4, en este caso la bandera de acarreo tendrá el último digito, esto es bien importante porque si no, el resultado de la suma sería 0 1 0 0 lo que es incorrecto.
  • Sign (signo): Si esta bandera se activa, indica que el resultado de la operación es un número negativo
  • Zero (cero): Indica que el resultado de la operación fue cero. Esta bandera es utilizada para hacer comparaciones. En ensamblador no hay instrucción ‘if’ como en lenguajes de alto nivel, por lo tanto para saber si un dato es igual a otro, generalmente se hace una resta y si la bandera Zero se activa, son iguales. Las instrucciones de salto JZ, JNZ, también usan esta bandera

¿Cómo se ve un programa en ensamblador? veamos:

  1. section .data
  2.         hello:    db ‘Hello world!’,10
  3.         helloLen:       equ $-hello
  4.  
  5. section .text
  6.         global    _start
  7.  
  8. _start:
  9.         mov eax,4
  10.         mov ebx,1
  11.         mov ecx,hello
  12.         mov edx,helloLen
  13.  
  14.         int 80h
  15.  
  16.         mov eax,1
  17.         mov ebx,0
  18.         int 80h

Eso es el equivalente a lo siguiente (en Java):

  1. class HelloWorld{
  2.     public static void main(String args[]){
  3.         System.out.println(“Hello World!”);
  4.     }
  5. }

Si se fijan, en el programa en ensamblador la mayoría de las instrucciones son instrucciones MOV, que meten datos a los registros y preparan el llamado a la interrupción 80h , que su servicio 4 es escribir datos y su servicio 1 es terminar la ejecución del programa.

Pero no hemos mencionado las interrupciones, que como su nombre lo indican, interrumpen al procesador para que este atienda nuestra petición. En este caso le pedimos al procesador que nos atienda nuestra petición de escribir a la salida estándar (la consola) y después le pedimos que nos atienda nuestra petición de terminar el programa. Existen otras interrupciones, para leer la entrada del teclado, timers, cualquier evento de entrada / salida, señales para apagar el sistema, uso de la tarjeta ethernet, entre otras.

Al estar analizando un programa tenemos varias opciones: analizar usando listado muerto, o analizar utilizando un debugger. Listado muerto es básicamente desensamblar (es decir, convertir el ejecutable al código ensamblador) y utilizar este listado. A veces lo anterior es suficiente para determinar qué es lo que hace el programa, especialmente con protecciones no muy complicadas. Sin embargo, en mi experiencia utilizar un buen debugger facilita mucho las cosas.

En un debugger podemos ver la ejecución del programa en cuestión, paso a paso. Nosotros controlamos la ejecución del programa, vemos que instrucción sigue, podemos alterar el contenido del registro de banderas, entre muchas otras cosas.

Para cerrar con broche de oro, vamos a correr un pequeño programa bajo el debugger OllyDBG. El programa en cuestión es el crackme PushPushMe, que lo pueden obtener del sitio crackmes.de. El objetivo del crackme es hacer que se despliegue la cadena “Well d0ne!” en el messagebox que sale cuando se ejecuta el programa. Por ahora más que resolver el cracke (es decir, crackear) nos interesa ver cómo es que funciona, Una vez entendido el funcionamiento crackearlo será fácil.

Ventana de OllyDBG Lo primero que tenemos que hacer es abrir OllyDBG y cargar dentro de éste, el ejecutable que queremos analizar (PushPushMe.exe). Olly no tardará mucho en analizarlo, ya que es un ejecutable pequeño. Como podemos ver en la imagen, Olly nos presenta una ventana con 4 secciones a las que he llamado:

  1. Ventana de código: muestra el código desensamblado de la aplicación
  2. CPU: muestra los registros del CPU y su contenido
  3. Dump de memoria: muestra el contenido de una sección de memoria en representación hexadecimal y ASCII
  4. Stack: muestra el contenido del stack

Podemos observar en la ventana de CPU que el registro EIP contiene el valor 00401000. El registro EIP es el apuntador de instrucción. Este registro siempre contiene la dirección que contiene la próxima instrucción a ser ejecutada por el procesador. Si vemos el dump, inmediatamente observamos lo siguiente:

  1. 00403000  50 75 73 68 50 75 73 68        PushPush
  2. 00403008  4D 65 00 57 65 6C 6C 20        Me.Well
  3. 00403010  64 30 6E 65 21 00 00 00        d0ne!…

La primera columna contiene la dirección en donde se encuentran los datos. El siguiente bloque de 8 columnas son los bytes contenidos en las siguientes 8 direcciones. Al final se nos presenta el caracter ASCII representado por el valor, 1 caracter por byte. Si consultamos una tabla ascii nos dirá que 50 (en hex) corresponde a la letra ‘P’, 75 a la ‘u’ y así sucesivamente.

También podemos observar que ahí se encuentra la cadena que estábamos buscando (Well d0ne!) en la dirección 00403008 + 4 = 0040300B. ¿Por qué más 4? El renglón comienza en 00403008 y los primeros 3 caracteres son ‘M’, ‘e’, ‘.’. El siguiente ‘W’ ya pertenece a la cadena buscada.

Para iniciar el análisis, presionamos F8. El procesador ejecutó la instrucción contenida en la dirección 00401000 tal y como se lo indicaba EIP. Ahora EIP apunta a 00401005 y efectivamente, esa es la siguiente instrucción a ejecutar. Presionamos F8 5 veces más, observando cómo el contenido de los registros del CPU cambia en cada ocasión.

En 0040101E nos encontramos con la instrucción JMP ESP. Ya dijimos que la instrucción JMP es un salto incondicional, pase lo que pase, vamos a saltar a la dirección apuntada por ESP. El registro ESP contiene el valor 0012FFAC que Olly nos lo pone convenientemente en la parte de abajo de la ventana de código. Si volvemos a presionar F8, la ventana de código cambia para mostrarnos la siguiente instrucción a ejecutar: 0012FFAC como se muestra en la siguiente imagen:

Después del salto

Continuamos presionando F8 y observamos que en 0012FFAE está la instrucción PUSH 403000 que contiene la cadena “PushPushMe” (el título del messagebox). Si cambiamos PUSH 403000 por PUSH 40300B ¿que creen que pasará? En este punto presionamos F9 para continuar con la ejecución normal del programa y nos aparecerá el messagebox.

Eso es todo por este tutorial introductorio. Hay material de sobra para mantener entretenido a cualquier novato. Lean las ligas que presento, especialmente la sección de introducción al procesador x86. También es recomendable aprender sobre las bases numéricas (hex, bin) y operaciones básicas con ellas si es que nunca han visto algo de eso. Gracias a todos los que mantienen la escena del cracking un lugar tan interesante.

Hasta la próxima. Espero su retroalimentación.

5 Comentarios

  • Gustavo:

    Mayo 7th, 2008

    Que bueno que aproveche su tiempo libre pa’ ilustrar un poco y compartir con los mortales sus conocimientos en el arte de crackear.

    Muy digerible el contenido, esperemos que el próximo tambien lo sea.

    Y también esperemos que se moche con las Koyotitas pues!!!!

    Una sudada a su salud!

  • Aluziner:

    Mayo 8th, 2008

    Felicitaciones, excelente material sobre cracking… a finales de los 90’s tuve una epoca oscura en la cual estube estudiando un poco de este mundo del cracking y hasta llegué a realizar algunos cracks de programas de la época. Que tiempos aquellos! desafortunadamente (como dijo alquien por ahi), tuve que ir a la escuela y dejar de aprender.

    Salu2

  • Pablo:

    Mayo 8th, 2008

    Oh, los recuerdos!! =P

    The Art of Assembly, en su version original (antes de que empezara con esa onda del HLA) me enseño casi todo lo que se de arquitectura de computadoras… :)

    Buen comienzo con este tutorial… esperemos que asi sigan los demas ;)

  • César:

    Mayo 9th, 2008

    Gracias! trataré de mantener la calidad… del ritmo de publicación no prometo nada.

  • m.jorge:

    Mayo 19th, 2008

    hola!
    como estas cesar pues espero q bien, oye gracias por mandarme el mensaje esto me servira de mucho tratare de practicar los numeros binarios por que influye mucho en este aprendizaje bueno ensero gracias

Haz un comentario:

Este post fué publicado por Cesar en Mayo 7th, 2008, bajo la(s) categoría(s): cracking.

Hasta el momento hay 5 comentarios.

¿Quieres más? Todos nuestros posts están en la sección de Archivos.

Creative Commons

Paskola Estudio Creativo
Help the authors