Inicio > Hacking, Programacion > Assembly en GNU/Linux para Hackers Newbies, Parte VII

Assembly en GNU/Linux para Hackers Newbies, Parte VII

FLUJO CONDICIONAL DE UN PROGRAMA EN ASSEMBLY

Existen una serie de instrucciones que consisten en la ejecución de una determinada rutina dependiendo de que se cumplan ciertas condiciones para su ejecución. En Assembly, algunas de estas instrucciones están relacionadas con las Flags contenidas en el register EFLAGS, de este modo cada una de estas instrucciones verifican si dicho register tiene establecida una determinada bandera y en función de dicha verificación, ejecutar o no una rutina de código, lo que en lenguajes como Pascal, C/C++ o Java se conoce como una instrucción IF, pero aquí es un poco mas compleja.

Las instrucciones de flujo condicional basadas en las banderas del register EFLAGS son:

JA: Jumping Above

JAE: Jumping Above Equal

JE: Jumping Equal

JZ: Jumping Zero

JNZ: Jumping No Zero

Estas instrucciones, como se ha indicado anteriormente dependen directamente de las banderas contenidas en el register EFLAGS, tales como:

ZF: Zero Flag

PF: Parity Flag

OF: Overflow Flag

SF: Sign Flag

CF: Carry Flag

En orden de usar un salto condicional es necesario tener una operación que establezca adecuadamente las banderas del register EFLAGS apropiadamente.

Como en las entradas anteriores, utilizamos el siguiente programa de ejemplo para comprender de un modo mas detallado

#Ejemplo de instrucciones para el Flujo condicional.dataHelloWorld:

.asciz “Hello Assembly”

ZeroFlagSet:

.asciz “Zero Flag Set”

ZeroFlagNotSet:

.asciz “Zero Flag Not Set”

.text

.globl _start

_start:

nop

movl $10, %eax

jz FlagSetPrint

FlagNotSetPrint:

movl $4, %eax

movl $1, %ebx

leal ZeroFlagNotSet, %ecx

movl $17, %edx

int $0x80

jmp ExitCall

FlagSetPrint:

movl $4, %eax

movl $1, %ebx

leal ZeroFlagSet, %ecx

movl $13, %edx

int $0x80

jmp ExitCall

ExitCall:

call PrintHelloWorldTenTimes

movl $1, %eax

movl $0, %ebx

int $0x80

PrintHelloWorldTenTimes:

movl $10, %ecx

PrintTenTimes:

pushl %ecx

movl $4, %eax

movl $1, %ebx

leal HelloWorld, %ecx

movl $14, %edx

int $0x80

popl %ecx

LOOP PrintTenTimes

Depurando el programa anterior, se establece un punto de interrupción en la linea movl $10, %eax se obtiene lo siguiente:

(gdb) info registers

rax 0x0 0

rbx 0x0 0

………..

rip 0x4000b1 0x4000b1 <_start+1>

eflags 0x202 [ IF ]

Como se aprecia en la información de los registers anteriores, el register EFLAGS no tiene establecida la bandera correspondiente ZF, por este motivo, la instrucción siguiente, no ejecutará el código correspondiente al segmento denominado FlagSetPrint dado que la condición no se cumple

(gdb) s

16 jz FlagSetPrint

(gdb) s

FlagNotSetPrint () at FlujoCondicional.s:19

19 movl $4, %eax

Ahora, dado que el flujo del programa, no ha entrado por el segmento FlagSetPrint, continua hacia el siguiente segmento del programa que es: FlagNotSetPrint.

Ahora el programa anterior será un poco modificado con el fin de establecer el ZF, de este modo, se adiciona el uso de la instrucción XORL como se detalla en el listado del programa:

#Ejemplo de instrucciones para el Flujo condicional.dataHelloWorld:

.asciz “Hello Assembly”

ZeroFlagSet:

.asciz “Zero Flag Set”

ZeroFlagNotSet:

.asciz “Zero Flag Not Set”

.text

.globl _start

_start:

nop

movl $10, %eax

xorl %eax,%eax #Esta comparacion entre registers siempre será verdadera, por este motivo, se establecerá automáticamente la bandera ZF al register EFLAGS.

jz FlagSetPrint

FlagNotSetPrint:

movl $4, %eax

movl $1, %ebx

leal ZeroFlagNotSet, %ecx

movl $17, %edx

int $0x80

jmp ExitCall

FlagSetPrint:

movl $4, %eax

movl $1, %ebx

leal ZeroFlagSet, %ecx

movl $13, %edx

int $0x80

jmp ExitCall

ExitCall:

movl $1, %eax

movl $0, %ebx

int $0x80

En la depuración del programa, se establece un punto de interrupción en la linea xorl %eax,%eax y el resultado es el siguiente

(gdb) s

17 jz FlagSetPrint

(gdb) info registers

rax 0x0 0

rbx 0x0 0

…………….

rip 0x4000b8 0x4000b8 <_start+8>

eflags 0x246 [ PF ZF IF ]

(gdb) s

FlagSetPrint () at FlujoCondicional.s:28

28 movl $4, %eax
Como puede apreciarse, el programa ha entrado en el segmento correspondiente a FlagSetPrint, dado que la bandera ZF ha sido establecida en el register EFLAGS.

Instrucción LOOP

Esta instrucción permite la ejecución cíclica de un un conjunto de instrucciones determinado, lo que en lenguajes como C/C++, Pascal, Java, entre otros, es conocido como un bucle, normalmente una sentencia DO WHILE.

La instrucción LOOP utiliza el register ECX para almacenar el numero de ejecuciones del ciclo y se encarga de disminuir el valor almacenado en dicho register cada vez que se ejecuta una iteración de dicho ciclo, el uso de esta instrucción tiene la siguiente sintaxis:

<codigo>

movl $NUMERE, %ecx #Numero de repeticiones del ciclo.

EtiquetaCiclo:

<codigo>

<codigo>

LOOP EtiquetaCiclo

El siguiente fragmento de código expresa el uso de la instrucción LOOP

#Ejemplo de instrucciones para el Flujo condicional.dataHelloWorld:

.asciz “Hello Assembly”

ZeroFlagSet:

.asciz “Zero Flag Set”

ZeroFlagNotSet:

.asciz “Zero Flag Not Set”

.text

.globl _start

_start:

nop

movl $10, %eax

xorl %eax,%eax #Esta comparacion entre registers siempre sera verdadera, por este motivo, se establecera automaticamente la bandera ZF al register EFLAGS.

jz PrintHelloWorldTenTimes

FlagNotSetPrint:

movl $4, %eax

movl $1, %ebx

leal ZeroFlagNotSet, %ecx

movl $17, %edx

int $0x80

PrintHelloWorldTenTimes:

movl $10, %ecx

PrintTenTimes:

pushq %rcx

movl $4, %eax

movl $1, %ebx

leal HelloWorld, %ecx

movl $14, %edx

int $0x80

popq %rcx

LOOP PrintTenTimes

jmp ExitCall

NOTA IMPORTANTE: El código anterior depende de una plataforma de 64 bits, en el caso de que se ejecute en una arquitectura de 32 bits, es necesario cambiar la instrucción pushq y popq por pushl y popl respectivamente.
En la depuración del programa, el resultado es el siguiente, una vez establecido el punto de interrupción en la linea movl $10, %ecx

(gdb) print $ecx

$1 = 10

(gdb) s

44 movl $1, %ebx

(gdb) s

45 leal HelloWorld, %ecx

(gdb) s

46 movl $14, %edx

(gdb) s

47 int $0x80

(gdb) s

Hello Assembly48 popq %rcx

(gdb) s

PrintTenTimes () at FlujoCondicional.s:49

49 LOOP PrintTenTimes

(gdb) print $ecx

$2 = 10

(gdb) s

42 pushq %rcx

(gdb) s

PrintTenTimes () at FlujoCondicional.s:43

43 movl $4, %eax

(gdb) print $rcx

$3 = 9

………….

El ciclo continua con su marcha hasta finalizar, como ha podido verse, se ha utilizado las instrucciones pushq y popq, el motivo de esto es que dado que la linea leal HelloWorld, %ecx sobre-escribe constantemente la posición de memoria del register ECX, los valores de este permanecen invariables durante cada iteración del ciclo, lo que conlleva a un bucle infinito, por esta razón se emplean las instrucciones pushq y popq, donde la instrucción pushq almacena el valor actual del register ECX, y posteriormente la instrucción popq, reestablece el contenido de dicho register en función de la ultima ejecución de la instrucción pushq. Estas instrucciones serán explicadas con una mayor profundidad en próximas entradas.
Loops Condicionales:

Existen dos instrucciones adicionales relacionadas con la instrucción LOOP, se trata de:

LOOPZ: Ejecuta un ciclo mientras que el register ECX sea mayor que cero y la bandera ZF se encuentre establecida en el register EFLAGS

LOOPNZ: Ejecuta un ciclo mientras que el register ECX sea mayor que cero y la bandera ZF no se encuentre establecida en el register EFLAGS

Para probar su funcionamiento en el programa anterior basta con reemplazar la instrucción LOOP por LOOPZ y LOOPNZ para ver su funcionamiento.

En el primer caso, LOOPZ se ejecuta de la misma forma que con LOOP dado que la instrucción que ha invocado este segmento ha sido jz PrintHelloWorldTenTimes lo que indica que la bandera ZF ha sido establecida en el register EFLAGS.

En el segundo caso, el comportamiento es diferente, dado que la condición de que la bandera ZF no este establecida, no se cumple, el ciclo solamente se ejecuta una sola vez, y finalmente finaliza su ejecución, por la tanto las invocación a la función write solamente se invoca una vez.

  1. Aún no hay comentarios.
  1. No trackbacks yet.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: