30
Aug
2012

Función de Prólogo y Epílogo (lenguaje assembler)

Las funciones de Prólogo y el Epílogo son una convención (vigente para muchos lenguajes de alto nivel, como por ejemplo C) que los compiladores aplican al generar el código assembler correspondiente a una función. El objetivo es establecer una protección contra los buffer overflows.

Supongamos el siguiente código en C:

int obtener_numero_fijo();
 
int main(void) {
	return 1;
}
 
int obtener_numero_fijo(){
	return 2;
}

Luego de compilarlo (con GCC), analizamos el código objeto (con objdump) y observamos el siguiente código assembler:

00000000 
: 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: b8 01 00 00 00 mov $0x1,%eax 8: 5d pop %ebp 9: c3 ret 0000000a : a: 55 push %ebp b: 89 e5 mov %esp,%ebp d: b8 02 00 00 00 mov $0x2,%eax 12: 5d pop %ebp 13: c3 ret

Podemos observar que ambas funciones comienzan poniendo el valor del registro ebp en el stack. Luego mueven el stack pointer al registro ebp. Al finalizar la función, se retira el último valor del stack y se guarda en el registro ebp.


Leer el resto del artículo »

28
Aug
2012

Desensambladores de Barrido Lineal vs Recursivos Descendentes

Los Desensambladores de Barrido Lineal (Linear Sweep Disassemblers) son de diseño más simple. Funcionan en base al algoritmo básico descripto en el artículo Los 4 pasos básicos del desensamblado. El desensamblador determina en primer lugar dónde comienza el código de ejecución de un binario, según las cabezeras del archivo. Una vez encontrado este punto, lee de forma lineal instrucción a instrucción bajo el supuesto de que donde termina una instrucción, comienza la siguiente. El desensamblador “no tiene idea” de los flujos de ejecución del programa.

El problema con esto es que los programas pueden tener embebidos datos entre medio de sus instrucciones. Si el desensamblador no diferencia datos de instrucciones, los datos serán interpretados falsamente como instrucciones.

Los Desensambladores Recursivos Descendentes (Recursive Descent Disassemblers) son otra aproximación al diseño de un algoritmo de desensamblado. A diferencia de los primeros, estos algoritmos se enfocan en entender el flujo de control de un programa: qué es lo que está ocurriendo.

Leer el resto del artículo »

25
Aug
2012

Los 4 pasos básicos del desensamblado

Los lenguajes de programación pueden ser categorizados en 4 niveles:

  • 1a generación: código de máquina. Este código son números (ceros y unos, que generalmente se visualizan en sistema hexa) que representan instrucciones correspondientes a la arquitectura de procesador. Este código es lo único que un CPU puede entender para ejecutar sus tareas. Por ejemplo, los procesadores con arquitectura x86 tienen un set de estas instrucciones definido. Las instrucciones son de largo variable y cada una de ellas consiste de un “código de operación” (número) y, opcionalmente, datos de entrada.
  • 2da generación: traducción del código de máquina a un lenguaje nemotécnico, llamado assembler. Desensamblar un código es el proceso de traducir el código de máquina en código assembler. Para esto se requiere un conocimiento de la arquitectura para la cuál fue compilado el programa: no es lo mismo un código de máquina “90” para el set de instrucciones x86 que para ARM. Una vez identificada la arquitectura, la traducción implica principalmente buscar en una tabla de instrucciones assemblercódigo de máquina. Algunos ejemplos de instrucciones en assembler son NOOP, JMP, ADD, etc.
  • 3ra generación: aquí están los lenguajes de programación de alto nivel como C, C++, Java, etc. Lo que los programadores generalmente escriben, y envían al compilador para que se encargue del pasaje a código de máquina. Decompilar es el proceso de traducir código assembler en lenguaje de 3ra generación (revertir lo que hace un compilador). Este proceso es sucio dado que la decompilación “pierde información” (nombres de variables, nombres de funciones, etc.) y no existe un único camino hacia atrás (un programa puede ser traducido a diferentes códigos assembler y un código assembler a diferentes código de alto nivel).
  • 4ta generación: no interesan en este caso.


Leer el resto del artículo »