FLUJO INCONDICIONAL DE UN PROGRAMA EN ASSEMBLY

Las instrucciones que se encuentran en esta categoría corresponden a aquellas que no se encuentran limitadas por instrucciones condicionales para que se su ejecución se lleve a cabo.

El siguiente es el programa de ejemplo que se usará para la demostración de los tópicos relacionados con el flujo incondicional de un programa en assembly, que como en casos anteriores se depurará paso a paso para su correcto entendimiento.

#Ejemplo de un programa usando instrucciones de flujo incondicional.

.data

HelloWorld:

.asciz «Hello Assembly»

CallDemo:

.asciz «Call Demo»

.text

.globl _start

nop

_start:

#Escribir contenido de HelloWorld usando la funcion write

movl $4, %eax

movl $1, %ebx

movl $HelloWorld, %ecx

movl $14, %edx

int $0x80

call Calling

jmp Exit

Calling:

movl $4, %eax

movl $1, %ebx

movl $CallDemo, %ecx

movl $9, %edx

int $0x80

ret

Exit:

#Instruccion para salir del programa.

movl $1, %eax

movl $0, %ebx

int $0x80

INSTRUCCIÓN JUMP (JMP):

Esta instrucción se encarga de transferir el flujo del control del programa a un segmento del mismo identificado con una etiqueta que lo declara de forma única, de este modo el register EIP (que como se ha indicado anteriormente es el register que controla la ejecución de un programa en Assembly) apunta a la dirección de memoria del segmento invocado, esta instrucción es equivalente a la instrucción GOTO de C/C++

En un proceso de depuración del programa anteriormente listado, se procede a poner un punto de interrupción en la linea jump Exit, el resultado de la ejecución es el siguiente, utilizando GDB para la depuración del programa:

#Plataforma 64 bits.

(gdb) print $rip

$4 = (void (*)()) 0x4000cc <_start+27>

(gdb) disas _start

Dump of assembler code for function _start:

0x00000000004000b1 <+0>: mov $0x4,%eax

0x00000000004000b6 <+5>: mov $0x1,%ebx

0x00000000004000bb <+10>: mov $0x6000f4,%ecx

0x00000000004000c0 <+15>: mov $0xe,%edx

0x00000000004000c5 <+20>: int $0x80

0x00000000004000c7 <+22>: callq 0x4000ce <Calling>

=> 0x00000000004000cc <+27>: jmp 0x4000e5 <Exit>

Como puede apreciarse, después de ejecutar el print $rip, la ejecución actual del programa se encuentra en la instrucción jmp, la cual apunta a la posición de memoria 0x4000cc, este a su vez apunta al segmento “Exit” (0x4000e5) el cual será el objetivo para la transferencia de control del programa por parte de la instrucción jmp y cuya posición en memoria es 0x4000e5

Al continuar con la ejecución del programa podemos apreciar que la instrucción jmp hace justo lo que se espera que haga, es decir, transferir el control del programa al segmento Exit

(gdb) print $rip

$4 = (void (*)()) 0x4000e5 <Exit>

(gdb) disas Exit

Dump of assembler code for function Exit:

=> 0x00000000004000e5 <+0>: mov $0x1,%eax

0x00000000004000ea <+5>: mov $0x0,%ebx

0x00000000004000ef <+10>: int $0x80

End of assembler dump.

instrucción CALL

Esta instrucción es similar a lo que en lenguajes como C/C++ se conoce como la llamada a una función o procedimiento. A diferencia de la instrucción jmp, una vez el segmento de memoria ha finalizado su ejecución, este retorna el control a la posición de memoria desde donde se ha invocado. Por otro lado, el segmento que es invocado por la instrucción CALL, siempre tiene asociado una instrucción RET, que tiene un efecto similar es similar a la palabra reservada “return” en lenguajes como C/C++

Para probar el funcionamiento de esta característica, se implementa un punto de ruptura en la linea call Calling .

(gdb) disas _start

Dump of assembler code for function _start:

0x00000000004000b1 <+0>: mov $0x4,%eax

0x00000000004000b6 <+5>: mov $0x1,%ebx

0x00000000004000bb <+10>: mov $0x6000f4,%ecx

0x00000000004000c0 <+15>: mov $0xe,%edx

0x00000000004000c5 <+20>: int $0x80

=> 0x00000000004000c7 <+22>: callq 0x4000ce <Calling>

0x00000000004000cc <+27>: jmp 0x4000e5 <Exit>

End of assembler dump.

Como puede apreciarse, callq, efectuará la transferencia de control a la posición de memoria 0x4000ce que se encuentra etiquetada con <Calling>

(gdb) disas Calling

Dump of assembler code for function Calling:

0x00000000004000ce <+0>: mov $0x4,%eax

0x00000000004000d3 <+5>: mov $0x1,%ebx

0x00000000004000d8 <+10>: mov $0x600103,%ecx

0x00000000004000dd <+15>: mov $0x9,%edx

0x00000000004000e2 <+20>: int $0x80

0x00000000004000e4 <+22>: retq

End of assembler dump.

(gdb) print /x $esp

$1 = 0xffffe250

(gdb) print /x $rip

$3 = 0x4000c7

(gdb) s

Call DemoCalling () at FlujoIncondicional.s:28

28 ret

Llegados hasta este punto, se ha ejecutado la función write del sistema operativo con los argumentos especificados y se ha impreso en pantalla el valor de la variable, antes de ejecutar la instrucción RET, examinamos la memoria:

(gdb) print $rip

$5 = (void (*)()) 0x4000e4 <Calling+22>

Despues de ejecutar la instrucción RET:

(gdb) s

_start () at FlujoIncondicional.s:20

20 jmp Exit

(gdb) print $rip

$7 = (void (*)()) 0x4000cc <_start+27>

Ha retornado al punto de llamada, y ahora el register EIP estará ubicado justo en la siguiente posición de memoria, es decir en la linea jmp Exit, que corresponde a la posición de memoria 0x4000cc