Sunteți pe pagina 1din 14

El Baúl

Actualizado el Oct 8, 2017


Desafío de ingeniería inversa en C. Soluciones
Tiempo aprox ~ 12 m. (2355 palabras.)

Autor

Alejandro Alcalde

Graduado en Ingeniería Informática en la ETSIIT, Granada. Creador de El


Baúl del Programador

Más artículos de Alejandro Alcalde

Índice

Primera Bomba

Segunda Bomba

El código de mi programa Bomba

Referencias
El Baúl

Hace unas semanas hablé de un desafío propuesto por los profesores de Estructura de
computadores de mi facultad. Ahora que ha finalizado el plazo de entrega de la
práctica, escribo este artículo con los resultados que obtuve.

Como comenté, la practica consiste en averiguar dos contraseñas de un programa


escrito en C compilado para 32 bits y sin opciones de depuración. El guión está
escrito por los profesores Javier Fernandez y Mancia anguita bajo licencia creative
commons BY-NC-SA y disponible para descargar más abajo en las referencias.

Todos los programas escritos por los alumnos estan basados en este:

#include <stdio.h> // para printf()


#include <stdlib.h> // para exit()
#include <string.h> // para strncmp()/strlen()
#include <sys/time.h> // para gettimeofday(), struct timeval

char password[]="abracadabran";
int passcode = 7777;

void boom(){
printf("***************n");
printf("*** BOOM!!! ***n");
printf("***************n");
exit(-1);
}

void defused(){
printf("·························n");
printf("··· bomba desactivada ···n");
printf("·························n");
exit(0);
}

int main(){
#define SIZE 100
char pass[SIZE];
int pasv;
#define TLIM 5
struct timeval tv1,tv2; // gettimeofday() secs-usecs
El Baúl
gettimeofday(&tv1;,NULL);

printf("Introduce la contraseña: ");


fgets(pass,SIZE,stdin);
if (strncmp(pass,password,strlen(password)))
boom();

gettimeofday(&tv2;,NULL);
if (tv2.tv_sec - tv1.tv_sec > TLIM)
boom();

printf("Introduce el código: ");


scanf("%i",&pasv;);
if (pasv!=passcode) boom();

gettimeofday(&tv1;,NULL);
if (tv1.tv_sec - tv2.tv_sec > TLIM)
boom();

defused();
}

¿Te gusta el blog? Ayúdame a seguir escribiendo

Añádeme a la “Lista blanca” de tu bloqueador de anuncios

Por qué renuncio a Google Analytics

Usa mi enlace de Ámazon

Descarga una guía gratuita

Más formas de apoyarme

Al cual se le aplican los métodos deseados por el alumno para cifrar la contraseña
que elija.
Conseguí descifrar 2 programas en total, aunque de unos de ellos solo la contraseña
Elalfanumérica,
Baúl luego veremos la razón. Empecemos con el primer programa:

Primera Bomba
Puedes descargar el programa desde este enlace:

Lo primero que hay que hacer es un estudio del comportamiento del programa
usando gdb y objdump -d. Tras ejecutar el programa paso a paso con gdb y predecir
cuales son los puntos críticos creé un archivo de configuración para gdb que me
ayudara a facilitar el depurado:

b main
b *0x804a034
b *0x8048706
b *0x80486ec
b *0x80486e7
b *0x80486c9
b *0x80486c4
b *0x804871e
b *0x804872c
b *0x804874e
display /wx $eip
display /32xw $esp
display /s $eax
display /w (char*)$eax
display (char*)0x804a034
display /wx 0x804a03

Mediante el proceso de depurado, me dí cuenta que las instrucciones clave eran las
siguientes; para la contraseña alfanumérica:

La dirección 0x804a034 es un puntero a la cadena de texto “aeiouuuu”, con un


espacio al final, esta es la contraseña codificada.

En algún punto del programa, se deben comparar la contraseña elegida con la


introducida por el usuario con strncmp, en dicho punto, la contraseña debe estar
decodificada. A base de depurar se observa que ese proceso comienza aquí:

80486c9: 0f b6 05 34 a0 04 08 movzbl 0x804a034,%eax


80486d0: 83 c0 01 add $0x1,%eax
80486d3: a2 34 a0 04 08 mov %al,0x804a034

Estas tres instrucciones extraen la primera letra de la contraseña; la a, le suman 1 y


la vuelven a colocar en la cadena de texto, quedando “beiouuuu”.
Luego en este fragmento:
El Baúl
80486d8: 0f b6 05 36 a0 04 08 movzbl 0x804a036,%eax
80486df: 83 e8 02 sub $0x2,%eax
80486e2: a2 36 a0 04 08 mov %al,0x804a036

Se extrae la i y resta dos, quedando g, la tercera instrucción sustituye la i en la


cadena, obteniendo como resultado “begouuuu”.

No se realizan más modificaciones a la contraseña almacenada en el programa.


Ahora toca averiguar qué operaciones se realizan sobre la contraseña introducida por
el usuario:

La contraseña introducida por el usuario se encuentra en 0x28(%esp), el programa


resta uno al segundo caracter de la contraseña introducida y lo sustituye:

80486e7: 0f b6 44 24 29 movzbl 0x29(%esp),%eax ; con 0x29(%esp) obt


80486ec: 83 e8 01 sub $0x1,%eax
80486ef: 88 44 24 29 mov %al,0x29(%esp)

Luego, hace algo parecido con la cuarta letra de la contraseña introducida por el
usuario:

80486f3: 0f b6 44 24 2b movzbl 0x2b(%esp),%eax


80486f8: 83 c0 01 add $0x1,%eax
80486fb: 88 44 24 2b mov %al,0x2b(%esp)

La diferencia es que esta vez suma uno, en lugar de restar.

Llegados al punto donde se llama a strncmp, vemos claramente en la pila qué


parámetros se le están pasando:

2: x/32xw $esp
0xffffd2f0: 0xffffd318 0x0804a034 0x0000000a 0xffffd3a4
1: x/xw $eip
0x804871e <strncmp>: 0xfffe01e8

(%esp) [0xffffd318] -> puntero a la contraseña introducida por el usuario


0x4(%esp) [0x0804a034] -> puntero a la contraseña del programa
0x8(%esp) [0x0000000a] -> longitud que se quiere comparar (10).
Teniendo en cuenta las transformaciones hechas, la contraseña a introducir es
Elbfgnuuuu
Baúl . ya que ‘f’-1 = ‘e’ y ‘n’+1=’o’, resultando begouuuu

Una vez descubierta la contraseña alfanumérica, pasé a la numérica, que resultó ser
bastante sencilla. Esta vez la clave de todo está en estas instrucciones:

804877a: a1 40 a0 04 08 mov 0x804a040,%eax


804877f: 05 f4 01 00 00 add $0x1f4,%eax
8048784: a3 40 a0 04 08 mov %eax,0x804a040
8048789: 8b 44 24 24 mov 0x24(%esp),%eax
804878d: 05 c8 00 00 00 add $0xc8,%eax

Se suma 500 (0x1f4) a la contraseña original, resultando 1500, y 200 (0xc8) a la


contraseña que introduzca el usuario. Haciendo la comparación de 1000+500 con
PASSINTRODUCIDA+200, se deduce que la contraseña que se debe introducir es
1300.

Segunda Bomba
Puedes descargar el programa desde este enlace.

Como en la bomba anterior, despues de investigar un poco, creé un archivo de sesión


para gdb con los puntos críticos:

b main
b cambio
b *0x804882a
b begin
b change
b code
b *0x804880e
b *0x804888d
display /wx $eip
display /d $ecx
display /d $eax
display /d $edx
display /32xw $esp
display /s $eax
display /w (char*)$eax
display (char*)0x804a034
display /wx 0x804a034

Llegué a la siguiente conclusión:


La contraseña codificada es C4b3Z0n, pero únicamente se comprueba la primera
Elletra.
Baúl Si el usuario introduce como contraseña C4b3Z0n, la bomba explota, ya que
en la función cambio, se comprueba que la primera letra de la contraseña no sea
igual a C, de ser igual, se llama a la bomba:

movzbl (%eax),%eax
cmp $0x43,%al
jne 80486a1 <cambio>
call 804860c <boomb>

Llegados a este punto, si no ha explotado, se compara la primera letra con “Q”, si la


primera letra de la contraseña introducida es “Q” también, se cambia por “c”. Todo
lo mencionado anteriormente se hace dentro de la función cambio. En resumen, la
función cambio comprueba que la primera letra no sea “C”, si no es “C”, compara
con “Q”, de ser “Q”, la cambia por una “c”, dicha “c”, la cambiará la función
change por una “C”, concretamente aquí:

80486be: 0f b6 00 movzbl (%eax),%eax ; Extrae la pri


80486c1: 3c 63 cmp $0x63,%al ; La compara co
80486c3: 75 06 jne 80486cb <change> ; Si no son igu
80486c5: 8b 45 08 mov 0x8(%ebp),%eax ; si son iguale
80486c8: c6 00 43 movb $0x43,(%eax) ; y sustituye l

Por tanto, la contraseña que el usuario debe introducir es c4b3Z0n, para que al
realizar el cambio quede como C4b3Z0n. Si se introduce por contraseña Q4b3Z0n,
se reemplaza por c4b3Z0n que es distinto de C4b3Z0n, detonando la bomba.

En este ejecutable no conseguí descubrir la contraseña numérica por la siguiente


razón, el código que realiza las operaciones es:

080486cd <code>:
80486cd: 55 push %ebp
80486ce: 89 e5 mov %esp,%ebp
80486d0: 83 ec 18 sub $0x18,%esp
80486d3: c7 45 e8 00 00 00 00 movl $0x0,-0x18(%ebp)
80486da: c7 45 f4 00 00 00 00 movl $0x0,-0xc(%ebp)
80486e1: c7 45 ec 00 00 00 00 movl $0x0,-0x14(%ebp)
80486e8: 81 45 08 1a 27 00 00 addl $0x271a,0x8(%ebp)
80486ef: 81 45 08 e8 03 00 00 addl $0x3e8,0x8(%ebp)
80486f6: 83 45 08 02 addl $0x2,0x8(%ebp)
80486fa: 8b 45 08 mov 0x8(%ebp),%eax
80486fd: 89 45 f0 mov %eax,-0x10(%ebp)
El8048700:
Baúl eb 4f jmp 8048751 <code>
8048702: 8b 4d f0 mov -0x10(%ebp),%ecx
8048705: ba 67 66 66 66 mov $0x66666667,%edx
804870a: 89 c8 mov %ecx,%eax
804870c: f7 ea imul %edx
804870e: c1 fa 02 sar $0x2,%edx
8048711: 89 c8 mov %ecx,%eax
8048713: c1 f8 1f sar $0x1f,%eax
8048716: 29 c2 sub %eax,%edx
8048718: 89 d0 mov %edx,%eax
804871a: c1 e0 02 shl $0x2,%eax
804871d: 01 d0 add %edx,%eax
804871f: 01 c0 add %eax,%eax
8048721: 89 ca mov %ecx,%edx
8048723: 29 c2 sub %eax,%edx
8048725: 89 d0 mov %edx,%eax
8048727: 89 45 f4 mov %eax,-0xc(%ebp)
804872a: 8b 45 f4 mov -0xc(%ebp),%eax
804872d: 01 45 e8 add %eax,-0x18(%ebp)
8048730: 8b 4d f0 mov -0x10(%ebp),%ecx
8048733: ba 67 66 66 66 mov $0x66666667,%edx
8048738: 89 c8 mov %ecx,%eax
804873a: f7 ea imul %edx
804873c: c1 fa 02 sar $0x2,%edx
804873f: 89 c8 mov %ecx,%eax
8048741: c1 f8 1f sar $0x1f,%eax
8048744: 89 d1 mov %edx,%ecx
8048746: 29 c1 sub %eax,%ecx
8048748: 89 c8 mov %ecx,%eax
804874a: 89 45 f0 mov %eax,-0x10(%ebp)
804874d: 83 45 ec 01 addl $0x1,-0x14(%ebp)
8048751: 83 7d ec 04 cmpl $0x4,-0x14(%ebp)
8048755: 7e ab jle 8048702 <code>
8048757: 83 7d e8 17 cmpl $0x17,-0x18(%ebp)
804875b: 74 07 je 8048764 <code>
804875d: e8 aa fe ff ff call 804860c <boomb>
8048762: eb 05 jmp 8048769 <code>
8048764: e8 d9 fe ff ff call 8048642 <bomb>
8048769: 8b 45 08 mov 0x8(%ebp),%eax
804876c: c9 leave
804876d: c3 ret
Gran parte de este código se usa para calcular un simple módulo, la razón; gcc
Elrealiza
Baúl esta optimización porque la instrucción div a pesar de ser una sola, es más
lenta que todo este código. Si quieres profundizar más en este tema, en las
referencias hay un enlace a stackoverflow que explica qué método se sigue para
calcular el módulo.

El código de mi programa Bomba


Para escribir mi programa me basé en técnicas de ofuscación que encontré por
internet, con un poco de imaginación se puede hacer muy dificil la lectura de un
programa:

/*
==========================================================================
Name : Boom.c
Author : Alejandro Alcalde
Version : 0.1
Description : Práctica sobre ingeniería inversa
==========================================================================
*/

#include <stdio.h> // para printf()


#include <stdlib.h> // para exit()
#include <string.h> // para strncmp()/strlen()
#include <sys/time.h> // para gettimeofday(), struct timeval

#define SIZE 15
#define ESTO printf(
#define ES "%sn"
#define OFUSCACION ,(char *) v);

//char password[] = "abracadabran";

void boom() {
printf("***************n");
printf("*** BOOM!!! ***n");
printf("***************n");
exit(-1);
}

void defused() {
printf("·························n");
printf("··· bomba desactivada ···n");
El Baúl
printf("·························n");
exit(0);
}

double password[3];

char* decode(char* p){


int a = 0;
char* f;
f = (char*) malloc(sizeof(char)*SIZE);

for (a = 0; a < 10; a++)


f[a] = p[a * 2 + 1];
return f;
}

void confuse2(int* pw){


int i = 0;
char* salt = "wE9mg9pu2KSmp5lh";
for (i = 0; i < 16; i++)
*pw ^= salt[i];
*pw+=7777;
}

int main(_, v) double *v; int _;{


int pasv;
#define TLIM 60
struct timeval tv1, tv2; // gettimeofday() secs-usecs
double h[3];
switch (_) {
case 0:
// ESTO ES OFUSCACION
break;
case 45681:
strcpy((char*) password, (char*)v);
confuse2(&passcode;);
main(0, v);
break;
default:
h[0] = 1302734077532073284113083965463480854832287808184119994524488
h[1] = 3870500591494514751058285253136238534286695148502666756138516
h[2] = 0;
main(45681, h);

//main(45681, "@M?eg \PoiRlLldo! ");
break;
El Baúl
}

//Pedimos datos al usuario


char f[SIZE];
gettimeofday(&tv1;,NULL);

printf("Introduce la contraseña: ");


fgets(f,SIZE,stdin);

if (strncmp(f,decode((char*)password),strlen(decode((char*)password))))
boom();

gettimeofday(&tv2;,NULL);
if (tv2.tv_sec - tv1.tv_sec > TLIM)
boom();

printf("Introduce el código: ");


scanf("%i",&pasv;);
confuse2(&pasv;);
if (pasv!=passcode)
boom();

gettimeofday(&tv1;,NULL);
if (tv1.tv_sec - tv2.tv_sec > TLIM)
boom();

defused();

return 0;
}

Explicaré por encima su funcionamiento. En la función main se evalua el primer


argumento, que contiene el número de parámetros pasados a la función, contanto
también con el nombre del programa, como no pasamos ningún argumento el valor
por defecto será 1, y como este valor no se encuentra en el switch, entra en el
default, Los números tan largos que ves son la contraseña representada en formato
double, en concreto esta cadena @M?eg \PoiRlLldo!�. Tras asignar la contraseña
a un array de doubles, llamo recursivamente a la función main, pasando como
argumento el número 65381 y el array con la contraseña. De modo que esta vez el
switch entra en la segunda sentencia (case 45681:), en la que se copia el array
con la contraseña en double a una cadena de caracteres.
Por muy complicado que parezca, el código ensamblador solo tiene un punto clave
Elpara
Baúldescubrir la contraseña:

En cualquier optimización, ya sea 0, 1 o 2, para averiguar la contraseña basta con


poner un punto de ruptura en la función decode:

08048870 <decode>:
8048870: 53 push %ebx
8048871: 83 ec 18 sub $0x18,%esp
8048874: c7 04 24 0f 00 00 00 movl $0xf,(%esp)
804887b: 8b 5c 24 20 mov 0x20(%esp),%ebx
804887f: e8 4c fc ff ff call 80484d0 <malloc>
8048884: 31 d2 xor %edx,%edx
8048886: 66 90 xchg %ax,%ax
8048888: 0f b6 4c 53 01 movzbl 0x1(%ebx,%edx,2),%ecx
804888d: 88 0c 10 mov %cl,(%eax,%edx,1)
8048890: 83 c2 01 add $0x1,%edx
8048893: 83 fa 0a cmp $0xa,%edx
8048896: 75 f0 jne 8048888 <decode>
8048898: 83 c4 18 add $0x18,%esp
804889b: 5b pop %ebx
804889c: c3 ret
804889d: 8d 76 00 lea 0x0(%esi),%esi

En la que se va extrayendo de la cadena @M?eg \PoiRlLldo!� los caracteres


impares, mediante un for:

8048888: 0f b6 4c 53 01 movzbl 0x1(%ebx,%edx,2),%ecx


804888d: 88 0c 10 mov %cl,(%eax,%edx,1)
8048890: 83 c2 01 add $0x1,%edx
8048893: 83 fa 0a cmp $0xa,%edx
8048896: 75 f0 jne 8048888 <decode>

Con lo cual, antes de retornar de la función, si miramos el valor de EBX


encontramos la contraseña a usar para desactivar la bomba.

La clave está en cómo interpretar en el main, concretamente en la línea

80486d5: 89 54 24 04 mov %edx,0x4(%esp)

el valor de EDX, pues es un double, que oculta la cadena cifrada @M?eg


\PoiRlLldo!�.
Valor que veremos si examinamos en gdb con x /gs $edx
El Baúl
También podemos deducir el tamaño de la contraseña mirando la pila antes de llamar
a strncmp, pues el tercer valor de la pila 0x8(%esp), es la longitud. En cuanto al
pin, la clave está en mirar este trozo de código:

80486b0: 0f be 08 movsbl (%eax),%ecx


80486b3: 83 c0 01 add $0x1,%eax
80486b6: 31 ca xor %ecx,%edx
80486b8: 3d 90 89 04 08 cmp $0x8048990,%eax
80486bd: 75 f1 jne 80486b0 <main>
80486bf: 81 c2 61 1e 00 00 add $0x1e61,%edx

Que va sumando al pin el resultado de hacer un XOR de su valor con el valor de una
letra almacenada en %ecx y al resultado le suma 7777. Con lo cual tenemos que
conseguir un número al que al hacerle todas estas operaciones de 8305, que resulta
ser 555.

Referencias

Calculating modulo in assembly | Visitar sitio

Obfuscation | Visitar sitio

¿Has visto algún error?: Por favor, ayúdame a corregirlo contactando conmigo
o comentando abajo.

Compártelo Suscríbete

Categorías:

dev

Etiquetas:
asm c hacking Ingenieria inversa
El Baúl
Quizá también te interese leer...

Desafío de ingeniería inversa en C

Hacking y Seguridad en Comunicaciones Móviles GSM / GPRS / UMTS / LTE

Jugando con las secciones .dtors y .ctors de la tabla de secciones en C

Cómo compilar todos los archivos CPP de una vez usando MakeFile

Optimizar sumas en paralelo

«« Desarrollo android. ¿Qué es Android?. Traducción de


Desafío de ingeniería inversa en C »»
developer.android.com

0 Comentarios El baúl del Programador 


1 Acceder

Ordenar por los mejores


 Recomendar ⤤ Compartir

Sé el primero en comentar...

INICIAR SESIÓN CON


O REGISTRARSE CON DISQUS ?

Nombre

Sé el primero en comentar.

✉ Suscríbete d Añade Disqus a tu sitio webAñade Disqus Añadir 🔒 Privacidad

License: CC BY-SA 4.0


An adaptation for Hugo of Cards's Willian Justen Theme for Jekyll

S-ar putea să vă placă și