Hola Mundo ... en ensamblador ...

Posted by selmineos | Posted in | Posted on 6:00

Antes de comensar aclaro que este articulo fue extraido de nuetro blog aliado hacking-avanzado cualquier copia o edicion debera ser notificada a dicho blog.

En lugar de soltar un rollo, vamos a empezar viendo el más sencillito de todos los programas, que es el típico "Hola Mundo" que nos ponen de ejemplo en cualquier lenguaje de programación. Así aprovechamos a hacernos una idea genérica de cómo funciona esto de programar en asm.

Lo que vamos a hacer, esquemáticamente, es lo siguiente:

- echarle un ojo a "Hola Mundo" y comentar cómo está programado.
- Usaremos "Hola Mundo" para dar una rápida intro a cómo se depura un programa con OllyDbg (en el siguiente post).
- hablaremos de los registros, la memoria, el stack ...

Este es el programita en asm:



Como veis tiene una estructura muy simple. Veamos:

.Data

strHolaMundo DB 'hola mundo', 0


data declara el comienzo de los datos del programa. Los ejecutables en windows corresponden al formato PE (Portable Executable), que define exactamente dónde y cómo se encuentran los datos, las instrucciones, qué DLLs deberían cargarse cuando se llama al programa, etc ...

Dentro de un ejecutable (PE), tenemos distintas "secciones". Las dos principales son "data", que guarda los datos que el programa necesita (strings y cosas así), y "code", que es donde se meten las instrucciones. Estas secciones en realidad podemos renombrarlas o reordenarlas como queramos, pero de momento lo dejaremos así. En assembly se pueden hacer muchas cosas que en principio no tendrían cabida en otros lenguajes de programación. Ya os avisé ...

Volviendo al programa ... Los strings, al igual que ocurre en C, acaban con un cero. Y eso que veis arriba es la declaración de un string. "db" significa "declare byte", y esta misma instrucción nos permitiría meter bytes en hexadecimal "embedidos" donde nos de la real gana.

.Code

start:


Ya lo comentábamos antes ... "code" declara el comienzo del código y "start" corresponde al "main" del programa. El loader de windows, el programa encargado de subir a memoria un ejecutable y lanzarlo, llama al "main" tras cargar las DLLs, reservar memoria, etc ... y le cede control al programa. El main es lo que se llama "Entry Point", y es un dato que viene metido dentro de las cabeceras del formato PE.

El Entry Point puede ser cualquier cosa. De hecho, antes muchos virus modificaban el EntryPoint para engañar a las rutinas de los antivirus, pasando el control al inicio del programa a una sección "no esperada". Hoy en día esto está en desuso.

Invoke MessageBox, NULL, Addr strHolaMundo, Addr strHolaMundo, NULL

"Invoke" es una macro que se usa simplemente por simplicidad al escribir el código. Lo que hace es un "push" de los parámetros en orden inverso y luego llama a la función que queremos, en este caso MessageBox.

Si miramos la definición de MessageBox en msdn tenemos:

Displays a modal dialog box that contains a system icon, a set of buttons, and a brief application-specific message, such as status or error information. The message box returns an integer value that indicates which button the user clicked.
Syntax

int MessageBox(
__in HWND hWnd,
__in LPCTSTR lpText,
__in LPCTSTR lpCaption,
__in UINT uType
);

Hemos pasado como parámetros la dirección del string "Hola Mundo". Cuando veamos el resultado tras compilar el programa veremos que eso se traduce en una serie de instrucciones "push" y un call. "Push" lo que hace es guardar un dato en el stack, que es una región de memoria que se usa para pasar parámetros a/desde funciones.

Uno de los fallos típicos de programción, y cada vez más difíciles de encontrar, son los buffer overflows, que consisten en, dicho super simplificado, meter dentro de alguno de los parámetros de una función nuestro código en ensamblador, consiguiendo que el programa lo ejecute.

Invoke ExitProcess, 0

Como comentábamos antes, el sistema operativo cede el control al programa llamando a "main", y por lo tanto debemos devolver algún código de retorno. Lo normal es devolver SUCCESS, que es un cero.

Esto mismo podríamos hacerlo en lugar de con invoke con un par de instrucciones en asm:

push 0
ret

Aunque realmente cuando decompilemos veremos algo como esto, en lugar de un simple ret:

push 0
call ExitProcess

El ret ("ret" de return) volvería de la llamada a "main" y devolvería un cero. El valor cero, como veis, lo hemos metido en el stack, que es donde el sistema operativo espera leer el código de retorno.

Y éste es el resultado:

Comments (0)

Publicar un comentario

Archivo del blog