Unidad 2.- Programación básica
2.1. Ensamblador (y ligador) a utilizar.
Ensamblador. El lenguaje ensamblador es un lenguaje de bajo nivel que se comunica directamente con el hardware de la máquina.El termino ensamblador se refiere a un tipo de programa informático que se encarga de traducir un fichero fuente escrito en un lenguaje ensamblador, a un fichero objeto que contiene código máquina, ejecutable directamente por el microprocesador.El programa lee el fichero escrito en lenguaje ensamblador y sustituye cada uno de los códigos nemotécnicos que aparecen por su código de operación correspondiente en sistema binario para la plataforma que se eligió como destino en las opciones especificas del ensamblador.
Características:
La razón de ser de estos segmentos es que, considerando que el tamaño máximo de un número que puede manejar el procesador esta dado por una palabra de 16 bits o registro, no sería posible accesar a más de 65536 localidades de memoria utilizando uno solo de estos registros, ahora, si se divide la memoria de la pc en grupos o segmentos, cada uno de 65536 localidades, y utilizamos una dirección en un registro exclusivo para localizar cada segmento, y entonces cada dirección de una casilla específica la formamos con dos registros, nos es posible accesar a una cantidad de 4294967296 bytes de memoria, lo cual es, en la actualidad, más memoria de la que veremos instalada en una PC.
Para que el ensamblador pueda manejar los datos es necesario que cada dato o instrucción se encuentren localizados en el área que corresponde a sus respectivos segmentos. El ensamblador accesa a esta información tomando en cuenta la localización del segmento, dada por los registros DS, ES, SS y CS, y dentro de dicho registro la dirección del dato específico.
- El código escrito en lenguaje ensamblador posee una cierta dificultad de ser entendido ya que su estructura se acerca al lenguaje máquina, es decir, es un lenguaje de bajo nivel.
- El lenguaje ensamblador es difícilmente portable, es decir, un código escrito para un microprocesador, puede necesitar ser modificado, para poder ser usado en otra máquina distinta. Al cambiar a una máquina con arquitectura diferente,generalmente es necesario reescribirlo completamente.
- Con el lenguaje ensamblador se tiene un control muy preciso de las tareas realizadas por un microprocesador por lo que se pueden crear segmentos de código difíciles y/o muy ineficientes de programar en un lenguaje de alto nivel, ya que, entre otras cosas, en el lenguaje ensamblador se dispone de instrucciones del CPU que generalmente no están disponibles en los lenguajes de alto nivel.
- Ensambladores modulares 32 bits o de alto nivel, son ensambladores que aparecieron como respuesta a una nueva arquitectura de procesadores de 32 bits, muchos de ellos teniendo compatibilidad hacia atrás pudiendo trabajar con programas con estructuras d 16 bits.
- Ensambladores básicos. Son de muy bajo nivel, y su tarea consiste básicamente en ofrecer nombres simbólicos a las distintas instrucciones, parámetros y cosas
2.2 Ciclos Numéricos
La arquitectura de los procesadores x86 obliga al uso de segmentos de memoria para manejar la información, el tamaño de estos segmentos es de 64kb.La razón de ser de estos segmentos es que, considerando que el tamaño máximo de un número que puede manejar el procesador esta dado por una palabra de 16 bits o registro, no sería posible accesar a más de 65536 localidades de memoria utilizando uno solo de estos registros, ahora, si se divide la memoria de la pc en grupos o segmentos, cada uno de 65536 localidades, y utilizamos una dirección en un registro exclusivo para localizar cada segmento, y entonces cada dirección de una casilla específica la formamos con dos registros, nos es posible accesar a una cantidad de 4294967296 bytes de memoria, lo cual es, en la actualidad, más memoria de la que veremos instalada en una PC.
Para que el ensamblador pueda manejar los datos es necesario que cada dato o instrucción se encuentren localizados en el área que corresponde a sus respectivos segmentos. El ensamblador accesa a esta información tomando en cuenta la localización del segmento, dada por los registros DS, ES, SS y CS, y dentro de dicho registro la dirección del dato específico.
Saltos, ciclos y procedimientos.
Los saltos incondicionales en un programa escrito en lenguaje ensamblador están dados por la instrucción jmp, un salto es alterar el flujo de la ejecución de un programa enviando el control a la dirección indicada.
Un ciclo, conocido también como iteración, es la repetición de un proceso un cierto número de veces hasta que alguna condición se cumpla. En estos ciclos se utilizan los brincos "condicionales" basados en el estado de las banderas. Por ejemplo la instrucción jnz que salta solamente si el resultado de una operación es diferente de cero y la instrucción jz que salta si el resultado de la operación es cero.
Por último tenemos los procedimientos o rutinas, que son una serie de pasos que se usarán repetidamente en el programa y en lugar de escribir todo el conjunto de pasos únicamente se les llama por medio de la instrucción call.
Un procedimiento en ensamblador es aquel que inicie con la palabra Proc y termine con la palabra ret. Realmente lo que sucede con el uso de la instrucción call es que se guarda en la pila el registro IP y se carga la dirección del procedimiento en el mismo registro, conociendo que IP contiene la localización de la siguiente instrucción que ejecutara la UCP, entonces podemos darnos cuenta que se desvía el flujo del programa hacia la dirección especificada en este registro. Al momento en que se llega a la palabra ret se saca de la pila el valor de IP con lo que se devuelve el control al punto del programa donde se invocó al procedimiento. Es posible llamar a un procedimiento que se encuentre ubicado en otro segmento, para ésto el contenido de CS (que nos indica que segmento se está utilizando) es empujado también en la pila.
2.3.- Captura Básica de Cadenas
Criptografía simétricaLa criptografía simétrica solo utiliza una clave para cifrar y descifrar el mensaje, que tiene que conocer el emisor y el receptor previamente y este es el punto débil del sistema, la comunicación de las claves entre ambos sujetos, ya que resulta más fácil interceptar una clave que se ha transmitido sin seguridad (diciéndola en alto, mandándola por correo electrónico u ordinario o haciendo una llamada telefónica).
Teóricamente debería de ser más fácil conocer la clave interceptándola que probándola una por una por fuerza bruta, teniendo en cuenta que la seguridad de un mensaje cifrado debe recaer sobre la clave y nunca sobre el algoritmo (por lo que sería una tarea eterna reventar la clave, como comenté en un ejemplo de ataque por fuerza bruta).
Para poner un ejemplo la máquina Enigma (que era una maquina de cifrado electromecánica que generaba abecedarios según la posición de unos rodillos que podrían tener distintas ordenes y posiciones) usaba un método simétrico con un algoritmo que dependía de una clave (que más que clave parece un ritual) que está formada por: los rotores o rodillos que usaba, su orden y la posición de cada anillo, siendo esto lo más básico.
La máquina Enigma contaba también con un libro de claves que contenía la clave del día y hacia un poco más difícil encontrar la clave, pero no es una clave lo suficientemente segura como para que no se pudiese reventar, sobretodo cuando los ingleses gracias a los polacos consiguieron el algoritmo, por este motivo la mayoría de los días conseguían la clave.
Y otro inconveniente que tiene este sistema es que si quieres tener un contenido totalmente confidencial con 10 personas tienes que aprenderte o apuntarte (siendo esta forma menos segura) las 10 claves para cada persona.
Criptografía asimétrica
La criptografía asimétrica se basa en el uso de dos claves: la pública (que se podrá difundir sin ningún problema a todas las personas que necesiten mandarte algo cifrado) y la privada (que no debe de ser revelada nunca).
Sabiendo lo anterior, si queremos que tres compañeros de trabajo nos manden un archivo cifrado debemos de mandarle nuestra clave pública (que está vinculada a la privada) y nos podrán mandar de forma confidencial ese archivo que solo nosotros podremos descifrar con la clave privada.
Puede parecer a simple vista un sistema un poco cojo ya que podríamos pensar que sabiendo la clave pública podríamos deducir la privada, pero este tipo de sistemas criptográficos usa algoritmos bastante complejos que generan a partir de la frase de paso (la contraseña) la clave privada y pública que pueden tener perfectamente un tamaño de 2048bits (probablemente imposible de reventar).
Como nos hemos dado cuenta solo cifra una persona (con la clave pública) y la otra se limita a mirar el contenido, por lo que la forma correcta de tener una comunicación bidireccional sería realizando este mismo proceso con dos pares de claves, o una por cada comunicador. Otro propósito de este sistema es también el de poder firmar documentos, certificando que el emisor es quien dice ser, firmando con la clave privada y verificando la identidad con la pública.
Diferencias entre criptografía simétrica y asimétrica
Para empezar, la criptografía simétrica es más insegura ya que el hecho de pasar la clave es una gran vulnerabilidad, pero se puede cifrar y descifrar en menor tiempo del que tarda la criptografía asimétrica, que es el principal inconveniente y es la razón por la que existe la criptografía híbrida.
Criptografía híbrida
Este sistema es la unión de las ventajas de los dos anteriores, debemos de partir que el problema de ambos sistemas criptográficos es que el simétrico es inseguro y el asimétrico es lento.
El proceso para usar un sistema criptográfico híbrido es el siguiente (para enviar un archivo):
Diferencias entre criptografía simétrica y asimétrica
Para empezar, la criptografía simétrica es más insegura ya que el hecho de pasar la clave es una gran vulnerabilidad, pero se puede cifrar y descifrar en menor tiempo del que tarda la criptografía asimétrica, que es el principal inconveniente y es la razón por la que existe la criptografía híbrida.
Criptografía híbrida
Este sistema es la unión de las ventajas de los dos anteriores, debemos de partir que el problema de ambos sistemas criptográficos es que el simétrico es inseguro y el asimétrico es lento.
El proceso para usar un sistema criptográfico híbrido es el siguiente (para enviar un archivo):
- Generar una clave pública y otra privada (en el receptor).
- Cifrar un archivo de forma síncrona.
- El receptor nos envía su clave pública.
- Ciframos la clave que hemos usado para encriptar el archivo con la clave pública del receptor.
- Enviamos el archivo cifrado (síncronamente) y la clave del archivo cifrada (asíncronamente y solo puede ver el receptor).
2.4 Comparación y prueba
La instrucción CMP pro lo común es utilizada para comparar dos campos de datos, uno de los cuales están contenidos en un registro. El formato general para CMP es:| [etiqueta:] | CMP | {registro/memoria}, {registro/memoria/inmediato} | Observe que la operación compara el primer operando con el segundo.
Por ejemplo, el valor del primer operando es mayor que, igual o menor que el valor del segundo operando? La instrucción CMPS compara el contenido de una localidad de memoria (direccionada por DS:SI). Dependiendo de la bandera de dirección, CMPS incrementa o disminuye también losregistros SI y DI en 1 para bytes, en 2 para palabras y en 4 para palabras dobles.
La operación establece las banderas AF, CF, OF, PF, SF y ZF. Cuando se combinan con un prefijo REP y una longitud en el CX, de manera sucesiva CMPS puede comparar cadenas de cualquier longitud. Pero observe que CMPS proporciona una comparación alfanumérica, esto es, una comparación de acuerdo a con los valores ASCII.
Considere la comparación de dos cadenas que contienen JEAN y JOAN Algunas derivaciones de CMPS son las siguientes:
- CMPSB. Compara bytes
- CMPSD. Compara palabras dobles
- CMPSW. Compara palabras
2.6 Saltos
Los saltos son instrucciones que permiten al programador cambiar el orden de ejecución del programa según sea necesario, dentro de ensamblador existen dos tipos de salto principales: condicionales e incondicionales.
Saltos Incondicionales:
Los saltos incondicionales se utilizan mediante la instrucción JMP, la cual transfiere el control a la línea especificada después de la palabra JMP, la cual puede ser un valor directo o una etiqueta.
También se puede contar como un salto incondicional la instrucción CALL, la cual llama una procedimiento y al terminarla devuelve el control a la línea siguiente de donde se inicio la llamada a procedimiento.
Ejemplo:
- Salto: Etiqueta a la que se hará referencia para el salto incondicional.
- JMP Salto: Instrucción que indica que el flujo del programa continuara desde la ubicación de la etiqueta Salto.
Saltos Condicionales:
Los saltos condicionales transfieren el control del programa a la ubicación que se les dé como parámetro si al hacer una comparación se cumple la condición establecida en el salto, los saltos condicionales son los siguientes:
- JA (Jump if Above): Salta cuando el valor es superior, su condición es equivalente al salto JNBE (Jump if Not Below or Equal).
- JAE (Jump if Above or Equal): Salta cuando el valor es superior o igual, su condición es equivalente al salto JNB (Jump if Not Below).
- JB (Jump if Below): Salta cuando el valor es menor, su condición es equivalente al salto JNAE (Jump if Not Above or Equal).
- JBE (Jump if Below or Equal): Salta cuando el valor es menor o igual, su condición es equivalente al salto JNA (Jump if Not Above).
- JE (Jump if Equal): Salta cuando el valor es igual.
- JZ (Jump if Zero): Salta cuando el valor es cero.
- JNE (Jump if Not Equal): Salta cuando el valor no es igual.
- JNZ (Jump if Not Zero): Salta cuando el valor no es cero.
- JG (Jump if Greater): Salta cuando el valor es mayor, su condición es equivalente al salto JNLE (Jump if Not Less or Equal).
- JGE (Jump if Greater or Equal): Salta cuando el valor es mayor o igual, su condición es equivalente al salto JNL (Jump if Not Less).
- JL (Jump if Less): Salta cuando el valor es menor, su condición es equivalente al salto JNGE (Jump if Not Greater or Equal).
- JLE (Jump if Less or Equal): Salta cuando el valor es menor o igual, su condición es equivalente al salto JNG (Jump if Not Greater).
Ejemplo:
- Salto: Etiqueta a la que se hará referencia para el salto condicional.
- CMP al, bl: Comparación entre el valor almacenado en al y el almacenado en bl.
- JG Salto: Instrucción que indica que el flujo del programa continuara desde la ubicación de la etiqueta Salto si el valor de al es mayor al de bl.
2.6 Ciclos condicionales
LOOP etiqueta: La instrucción loop decrementa CX en 1, y transfiere el flujo del programa a la etiqueta dada como operando si CX es diferente a 1.
LOOPE etiqueta: Esta instrucción decrementa CX en 1. Si CX es diferente a cero y ZF es igual a 1, entonces el flujo del programa se transfiere a la etiqueta indicada como operando.
LOOPNE etiqueta: Esta instrucción decrementa en uno a CX y transfiere el flujo del programa solo si ZF es diferente a 0.
2.7 Incremento y decremento
Son las instrucciones más básicas a la hora de hacer operaciones con registros: INC incrementa el valor de un registro, o de cualquier posición en memoria, en una unidad, y DEC lo decrementa.Instrucción INC
- INC AX: Incrementa en uno el valor de AX
- IN WORD PTR: Incrementa la palabra situada en CS.
Instrucción DEC
- DEC AX: Decremento AX, le resta uno.
- DEC WORD PTR: Decrementa la palabra situada en CS
2.8 Captura de cadenas con formato
El capturar cadenas con formato permite el movimiento, comparación o búsqueda rápida entre bloques de datos, las instrucciones son las siguientes:- MOVC: Esta instrucción permite transferir un carácter de una cadena.
- MOVW: Esta instrucción permite transferir una palabra de una cadena.
- CMPC: Este comando es utilizado para comparar un carácter de una cadena.
- CMPW: Esta instrucción es utilizada para comparar una palabra de una cadena.
- SCAC: Esta instrucción permite buscar un carácter de una cadena.
- SCAW: Esta instrucción se utiliza para buscar una palabra de una cadena.
- LODC: Esta instrucción permite cargar un carácter de una cadena.
- LODW: Esta instrucción es utilizada para cargar una palabra de una cadena.
- STOC: Esta instrucción permite guardar un carácter de una cadena.
- STOW: Esta instrucción es utilizada para guardar una palabra de una cadena.
2.9 Instrucciones aritméticas
Existen 8 instrucciones aritméticas básicas:- ADD (Suma)
- SUB (Resta)
- MUL (Multiplicación sin signo)
- DIV (División sin signo)
- IMUL (Multiplicación con signo)
- IDIV (División con signo)
- INC (Incremento unitario)
- DEC (Decremento unitario).
ADD Destino, Fuente
SUB Destino, Fuente
- ADD AX,BX ;AX=AX+BX
- ADD AX,10 ;AX=AX+10
- SUB AX,BX ;AX=AX-BX
- SUB AX,10 ;AX=AX-10
2.10 Manipulación de la pila
La pila es un grupo de localidades de memoria que se reservan con la finalidad de proporcionar un espacio para el almacenamiento temporal de información.La pila de los programas es del tipo LIFO (Last In First Out, Ultimo en entrar, Primero en salir).
Para controlar la pila el microprocesador cuenta con dos instrucciones básicas:
Push (Meter) y Pop (sacar).
El formato de estas instrucciones es el siguiente:
- Push operando
- Pop operando
Cuando se ejecuta la instrucción Push, el contenido del operando se almacena en la ultima posición de la pila. Por ejemplo, si AX se carga previamente con el valor 5, una instrucción Push AX almacenaría el valor 5 en la ultima posición de la pila.
Por otro lado la instrucción Pop saca el último dato almacenado en la pila y lo coloca en el operando.
Siguiendo el ejemplo anterior, la instrucción Pop BX obtendría el número 5 y lo almacenaría en el registro BX. El siguiente ejemplo muestra como implementar la instrucción XCHG por medio de las instrucciones Push y Pop. Recuerde que la instrucción XCHG intercambia el contenido de sus dos operandos.
.COMMENT
Programa: PushPop.ASM
Descripción: Este programa demuestra el uso de las instrucciones para el manejo de la pila, implementando la instrucción XCHG con Push y Pop
MODEL tiny
.CODE
Inicio: Punto de entrada al programa
Mov AX,5 ;AX=5
Mov BX,10 ;BX=10
Push AX ;Pila=5
Mov AX,BX ;AX=10
Pop BX ;BX=5
Mov AX,4C00h ;Terminar programa y salir al DOS
Int 21h ;
END Inicio
END
- ROT (a b c – c a b) Rota hacia atrás.
- 2ROT (ab cd ef – ef ab cd) Rota hacia atrás.
- NIP ( a b – b) Quita a de la pila.
- OUTK (... n -- ..) Elimina el elemento n.
- TUCK (a b -- b a b) Inserta una copia de b.
- 2?DUP (ab – ab ab) Duplica si ab <> 0.
- 2DROP (ab -- ) Elimina 2 de encima.
- 2DUP ( ab – ab ab) Duplica los elementos.
- 2NIP (ab cd – cd) Elimina elementos.
- 2OUTK (::: n -- ::) Elimina el elemento n
- 2OVER (ab cd – ab cd ab) Inserta una copia de ab.
- 2PICK (:: n -- :::) Copia el elemento n encima de la pila.
- 2ROLL (::: n -- :::) Quita el elemento n y lo deja arriba de la pila.
- 2ROT (ab cd ef – cd ef ab) Rota los elementos
- 2TUCK (ab cd – cd ab cd) Inserta una copia de cd.
- 2SWAP (ab cd – cd ab) Rota los elementos
2.12 Instrucciones lógicas.
Ensamblador cuenta con un grupo de cuatro instrucciones lógicas a nivel de bit, las cuales con excepción de la primera requieren de dos operandos, estas son las siguientes:NOT: La instrucción NOT o negación requiere un solo operando y su función es cambiar el estado de los bits del mismo, es decir, cambiar los ceros por unos y los unos por ceros.
Ejemplo:
NOT ax Se aplica la negación al valor del registro ax.
AND: Esta instrucción también conocida como producto lógico requiere de dos operandos y su valor será igual a uno cuando los bits que se comparen ambos sean uno.
Ejemplo:
AND ax,bx Se aplica el producto lógico a los valores de ax y bx.
OR: La instrucción OR también conocida como suma lógica requiere de dos operandos y su valor será uno si alguno de los bits que compara es uno.
Ejemplo:
OR ax,bx Se aplica la suma lógica a los valores de ax y bx.
XOR: La instrucción XOR o suma lógica exclusiva requiere dos operandos, los cuales se comparan y el resultado obtenido es uno cuando uno de los bits es uno y el otro cero.
Ejemplo:
XOR ax,bx Se aplica la instrucción XOR a los valores de ax y bx.
2.13 Desplazamiento y rotación.
Las instrucciones de desplazamiento son cuatro: shl, shr, sar y sal; y su objetivo es desplazar los bits de un operando un determinado número de posiciones a la izquierda o a la derecha. La estructura de los operandos manejados por estas instrucciones y su significado es idéntico para las cuatro instrucciones.SHL (Shift Left = desplazamiento a la izquierda)
Se desplazan a la izquierda los bits del operando destino tantas posiciones como indique el operando fuente. El desplazamiento de una posición se realiza de la siguiente forma: el bit de mayor peso del operando se desplaza al bit CF del registro de estado, el resto de los bits se desplazan una posición hacia la izquierda, y la posición de menor peso se rellena con un 0
SAL (Shift Arithmetic Left = desplazamiento aritmético a la izquierda)
El objetivo de un desplazamiento aritmético a la izquierda es multiplicar un operando, interpretado con signo, por una potencia de 2.
Para llevar a cabo este tipo de desplazamiento, hay que desplazar los bits del operando hacia la izquierda introduciendo ceros por su derecha.
Nota: En realidad, este tipo de desplazamiento es idéntico al llevado a cabo por la instrucción shl; por tanto, sal y shl son de hecho la misma instrucción y se codifican con el mismo código máquina.
· SHR (Shift Right = desplazamiento a la derecha)
· SAR (Shift Arithmetic Right = desplazamiento aritmético a la derecha)
2.14 Obtención de una cadena con la representación hexadecimal
La conversión entre numeración binaria y hexadecimal es sencilla. Lo primero que se hace para una conversión de un número binario a hexadecimal es dividirlo en grupos de 4 bits, empezando de derecha a izquierda. En caso de que el último grupo (el que quede más a la izquierda) sea menor de 4 bits se rellenan los faltantes con ceros.Tomando como ejemplo el número binario 101011 lo dividimos en grupos de 4 bits y nos queda:
10; 1011
Rellenando con ceros el último grupo (el de la izquierda):
0010; 1011
después tomamos cada grupo como un número independiente y consideramos su valor en decimal:
0010 = 2; 1011 = 11
Pero como no podemos representar este número hexadecimal como 211 porqué sería un error, tenemos que sustituir todos los valores mayores a 9 por su respectiva representación en hexadecimal, con lo que obtenemos:
2BH (Donde la H representa la base hexadecimal)
Para convertir un número de hexadecimal a binario solo es necesario invertir estos pasos: se toma el primer dígito hexadecimal y se convierte a binario, y luego el segundo, y así sucesivamente hasta completar el número.
2.16 Captura y almacenamiento de datos numéricos.
Las variables numéricas son muy útiles en ensamblador de la misma forma que en otros lenguajes de programación, ya que permiten al programador hacer operaciones aritméticas con datos que se desconocen al momento de la compilación.La utilización de datos numéricos es similar a la de cadenas, con la diferencia de que en vez de declarar las variables como db, se declaran como dw, lo cual significa que son variables numéricas.
Comentarios
Publicar un comentario