08 abril 2014

3.5  Parchear el binario en tiempo de ejecución

Una vez que sabemos a dónde debemos saltar para pintar el cuadrado, modifiquemos la zona a la que salta el JMP que hemos comentado anteriormente y deberíamos finalizar el reto.



La zona de memoria donde está el salto es “4010BD” y debemos saltar a “4022E0”. Los saltos en x86 son relativos a la zona desde la que se llama. Para saber exactamente quée opcodes necesitamos podemos usar “rasm2” de radare2:



Con el parámetro  “-o” indicamos que el salto se debe hacer teniendo en cuenta que el JMP está en la dirección “4010BD”.
¡Ya tenemos todo! Veamos cómo queda el fichero “key.txt”:

  • Los primeros cuatro bytes deciden a donde se hace el primer salto CALL EAX (el encargado de crear la ventana).
  • Los siguientes cuatro bytes deciden dónde vamos a escribir (0D1BE014C + X = 4110BD => X = 2E830F71 (710F831E en Little Endian)).
  • Los últimos cuatro bytes deciden qué vamos a escribir (lo obtenido con rasm2).

Veamos que todo funciona correctamente:



De momento EDI apunta a la dirección del JMP que queremos modificar sin embargo  el JMP no apunta a la parte de código que pinta el cuadrado y que, por tanto, nos interesa (4022E0).
A pesar de ello  MOV [edi+1], eax:



El JMP ya está apuntando a donde nosotros queremos y si pulsamos F9 para dejar que el programa prosiga con su ejecución obtenemos:



¡Reto solucionado!



Resolviendo el reto “por las ramas”

Muchos pueden pensar que eesto ya es suficiente, sin embargo seguí dándole un par de vueltas y conseguí solucionar el reto de otra forma distinta la cual el programador del código no había considerado a la hora de codear el reto. En este momento tenemos dos cosas a favor:
  • Sabemos cuáles son las dos zonas a las que debemos saltar (la que crea la ventana y la que pinta el cuadrado dentro de ella).
  • En el primer CALL EAX podemos saltar a la dirección que queramos.

Por tanto… ¿Por qué no saltar a la zona donde se carga nuestro “key.txt” y hacer que dicho archivo contenga dos saltos: uno a la zona donde se crea la ventana y otro a donde se pinta el cuadrado?
Tras esto me planteé dos elementos en contra que debemos controlar para que funcione la idea:
  • ¿Se cargan suficientes bytes en memoria  del archivo “key.txt” para poder hacer esos dos saltos?
  • ¿La zona donde se mapea el contenido de “key.txt” es siempre la misma o varía?

Recordemos la primera parte del código donde se carga el contenido del fichero a memoria:



Como vemos el número de bytes que se leerán del archivo “key.txt”  será de 14h que equivale a 20 bytes. Por tanto tenemos 20 bytes para hacer los dos saltos a las zonas de crear ventana y pintar cuadrado (realmente solo tenemos 16, ya que los cuatro primeros son los que usarán para calcular a dónde se hará el salto del CALL EAX). Aunque no es mucho es suficiente para hacer dos saltos. Primero contra bypassed.
También podemos ver que la zona de memoria donde se guarda el contenido del fichero es siempre la misma ya que está hardcodeada (lpBuffer=4040E4). Por tanto, el contenido del fichero se va a cargar siempre en la misma zona de memoria. Segundo contra bypassed.

Debemos hacer que el “CALL EAX” sea a 4040E4+4=4040E8 (la zona donde se carga nuestro “key.txt” sin contar los 4 primeros bytes que son los que decidirán a donde saltamos sumándose al valor 7A40B660).

Tenemos: 7A40B660 + X = 4040E8 => X = 85FF8A88  (888AFF85 en Little Endian).

Con esto ya solo nos falta generar los opcodes para saltar a las dos zonas que crean la ventana y pintan el cuadrado respectivamente.  Para hacer esto yo he optado por usar esta fórmula:

MOV EAX, 402000 ; zona donde se crea la ventana
CALL EAX
MOV  EAX, 4022E0 ; zona donde se pinta el cuadrado
CALL EAX
Para saber cuáles son los opcodes yo he usado rasm2 de radare2:



Añadimos esos opcodes a los 4 que ya teníamos y todo debería de funcionar. Comprobémoslo:



¡Genial! Todo está como debería. Si pulsamos F9 para seguir a la ejecución del programa:


¡Reto conseguido!  Esta vez de otra forma distinta y, a mi gusto, más interesante ya que somos nosotros mismos los encargados de guiar de la mano por donde debe continuar el flujo de instrucciones sin depender de parte del código del programador.


Artículo cortesía de: Alberto García Illera

1 comments :

Henry Sanchez dijo...

La segunda forma si es muy interesante, pero depende de la configuración del SO y/o del ejecutable, ya que si se hace uso de DEP se produciría una excepción.