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.
1 comments :
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.
Publicar un comentario