Black Hole organizacion
CostaRica  
  Bienvenida
  Hackers
  Foro ingresa
  Noticias
  Contacto
  Imagenes
  Programas
  Visitantes
  Chat
  Libros
  => hacker
  => Virus
  => Cracker
  => Lecciones hackin
  => Programacion
  => Diseños web
  => Guia de hackin
  => Privasidad
  => Guia → 1
  => Guia → 2
  => Guia → 3
  => Guia → 4
  => Guia → 5
  => Guia → 6
  => Guia → 7
  => Guia → 8
  => Guia → 9
  => Guia → 10
  => Guia → 11
  => Como intrudusirse aun sistema
  => shellcodes_linux-1
  => shellcodes_linux -2
  => UN-scodes.
  => Guia version Deluxe
  => Ser Hacker dentro de Términos Legales
  => Como hackear una paginaweb
  => Comoprogramar un Virus
  => Como Crear un Virus
  => Cuantos tipos de Virus existen
  => Programaciones de un virus
  => Estructura de computadores
  => Fundamentos de SSOO
  => Sistemas de numeración
  => Ensamblador I: Conceptos básicos
  => Ensamblador II
  => Utilidades para la programación
  => Infección bajo Windows
  => Infección bajo Linux
  => Técnicas avanzadas
  => Apéndices
  => CONOCIENDO LA MAQUINA
  => DIRECCIONAMIENTO DE MEMORIA EN EL 8086
  => CHIPS DE APOYO (Ampliación de la lección 1)
  => LA PILA DEL 8086
  => CODIFICACIÓN DE LAS INSTRUCCIONES EN EL 8086
  => Manual HTML
  => Ataques basados en Desbordamiento de Buffer (Buffer Overflow)
  => Privasidad
  => Escaneo
  => anti Escaneo y Escaneo
  => Malianom
  Triang
  Tersirve esta paguina?
  -
  juegos
  Vagos
  mapa
  Mapa del sitio
  546
CODIFICACIÓN DE LAS INSTRUCCIONES EN EL 8086

Atras


Hola de nuevo, aplicados alumnos:-). En esta lección vamos a tratar conceptos muy técnicos acerca del formato de las instrucciones en código máquina. Veremos como se codifican las instrucciones en el 8086.

CODIFICACIÓN DE LAS INSTRUCCIONES EN EL 8086

(Este apartado es muy técnico. Aunque no es imprescindible comprender lo que se expone a continuación para programar en ensamblador, es muy útil conocer como el procesador interpreta lo que le 'pedimos'.

Esto nos da un mayor conocimiento acerca de la máquina en cuestión.

Y de esta forma entendemos el porqué de ciertas sintaxis de instrucciones. Y resolveremos más fácilmente los errores una vez que se nos presenten).

Cada procesador tiene un conjunto de instrucciones para manejarlo, as¡ como para manejar la máquina por medio de él.

Indistintamente del lenguaje de programación que estemos utilizando, cuando obtenemos el ejecutable, éste está compuesto únicamente por ese tipo de instrucciones básicas (instrucciones de código máquina). Dependiendo de la calidad y prestaciones de ese lenguaje de programación, el código resultante, necesita más instrucciones del procesador o menos. De todos es conocido, que hay lenguajes de alto o medio nivel (como C, pascal, basic, etc.) en los que para una misma tarea, uno dar un ejecutable más grande que otro. Velocidad, aparte. Esto no sucede as¡ con ensamblador, en el que para cada instrucción, existe una y sólo una instrucción en código máquina.

Pues bien, ahora vamos a ver la estructura de esas instrucciones básicas o de código máquina.

Las instrucciones del 8086 se codifican sobre 4 campos como máximo, y tienen un tamaño de 1 a 6 bytes. Es decir, dependiendo de la instrucción de que se trate, necesita más o menos bytes para su codificación, as¡ como más o menos campos.

Los cuatro campos en una instrucción código máquina son:

1)Código de operación: Este campo siempre aparece (obviamente). Una vez que el procesador descifra el significado de este campo, sabe si la instrucción consta de más campos o si se trata de una instrucción de un sólo campo.

2) Modo de direccionamiento (byte EA): Le indica al procesador el número de operándos que acompañan al código de operación, as¡ como el tipo de estos operándos (registros, memoria, valor inmediato).

3)Desplazamiento del dato (sobre 8 o 16 bits): En caso de existir éste campo, supone un desplazamiento sobre la dirección dada por un registro índice o base (especificado este registro mediante el byte EA).

4)Valor inmediato (sobre 8 o 16 bits): Almacena un valor numérico de 8 o 16 bits, que va a ser utilizado para una transferencia, una operación aritmética, etc.

El código de operación está  codificado sobre 8 bits. Por medio de este campo se sabe si va a ser necesario cualquier otro de los tres restantes. También el código de operación contiene información acerca de si se va a trabajar con palabras o con bytes. Byte EA. Modo de direccionamiento: Contiene 3 campos. Los campos MOD y R/M especifican el modo de direccionamiento, y el campo REG especifica el registro de que se trata en la instrucción.

El campo MOD que es de 2 bits puede tener 4 valores diferentes: Los 3 primeros seleccionan el desplazamiento en los modos de direccionamiento de memoria. El cuarto selecciona un registro. Detallemos la función de estos bits en cada una de las 4 posibilidades:

00----> No hay desplazamiento.

01----> Se usa un byte para codificar el desplazamiento.

10----> Se usan 2 bytes (una palabra) para codificar el desplazamiento.

11----> Hace que R/M seleccione un registro usando la misma codificación

           de los registros que para REG (ver más abajo), en lugar de un

           modo de direccionamiento de la memoria.

           Es decir, que se produce una transferencia de un registro a otro.

 

El campo REG que es de 3 bits codifica el registro empleado. Por tanto es posible especificar hasta 8 registros diferentes por medio de este campo. Dependiendo de que se trate de acceso a palabras o a octetos, se seleccionar  un registro de entre un grupo de 8, o de un segundo grupo de 8 registros.

Para cuando se accede a registros de 16 bits, el campo REG codifica los registros de palabra de la siguiente manera:

AX (000), CX (001), DX (010), BX (011)

SP (100), BP (101), SI (110), DI (111)

 

Cuando se accede a registros de 8 bits, la codificación de los registros de tamaño byte queda como sigue:

AL (000), CL (001), DL (010), BL (011)

AH (100), CH (101), DH (110), BH (111)

 

El campo R/M indica el segundo registro (si lo hay) o el tipo de direccionamiento a memoria. En caso de que haya segundo registro, éste se codifica de la misma forma que para el campo REG.

En caso de que se trate de un modo de direccionamiento de memoria, estos tres bits seleccionan uno de los modos de direccionamiento posibles de acuerdo con la siguiente tabla:

000  desplazamiento final = [BX] + [SI] + desplazamiento

001  desplazamiento final = [BX] + [DI] + desplazamiento

010  desplazamiento final = [BP] + [SI] + desplazamiento

011  desplazamiento final = [BP] + [DI] + desplazamiento

100  desplazamiento final = [SI] + desplazamiento

101  desplazamiento final = [DI] + desplazamiento

110  desplazamiento final = [BP] + desplazamiento

111  desplazamiento final = [BX] + desplazamiento

 

 

El desplazamiento en caso de existir, supone un incremento en la dirección dada por un registro índice o base, dando lugar as¡ a un desplazamiento final, dentro de un segmento dado. Es decir, como se ve en la tabla superior, podemos acceder a memoria a través de un registro base (BX) o un registro índice (SI, DI), etc, o bien hacerlo a través de uno de esos registros, pero ayudándonos de un desplazamiento que se suma a la dirección que tienen establecida esos registros. Veremos más adelante la utilidad de utilizar desplazamientos sobre un registro base o índice.

Como ejemplo: Tenemos el registro DI apuntando a (con valor igual a) la dirección 3000h (direcciones siempre en hexadecimal). En esa dirección tenemos el comienzo de una cadena de caracteres que queremos convertir a mayúsculas. Y una vez que los hemos convertido, los queremos copiar a la memoria de pantalla. Pues bien, podemos ir incrementando DI para tratar cada uno de estos caracteres, o bien podemos utilizar DI junto con un desplazamiento para acceder a cada uno de los caracteres. Es decir, para acceder al primer elemento sería DI+0, para el segundo, sería DI+1, etc. De esta forma, al terminar la tarea, DI seguir  apuntando al principio de la cadena, y podremos copiar la cadena desde el principio a donde corresponda.

Si no utilizáramos desplazamiento, tendríamos que tener una variable apuntando al inicio de la cadena, para tenerlo luego localizable. Bueno... Esto es un simple ejemplo. Las posibilidades que nos ofrece el utilizar desplazamientos acompañando al registro base o índice son mucho más interesantes que lo que acabamos de ver en el ejemplo.

El valor inmediato se utiliza cuando hacemos movimientos de datos a registros o a memoria. Por ejemplo queremos introducir en el registro AX la cantidad 37867 (93EBH), pues ese 37867 sería el valor inmediato.

En ensamblador la instrucción sería:

MOV AX,37867

Simple, no?  Mover (MOV) la cantidad 37867 al registro AX. Próximamente se verá el resto de instrucciones en ensamblador, mientras tanto, y por ser necesario ahora, aprenderemos el uso de la instrucción MOV.

La instrucción como hemos podido ver, se utiliza para movimientos o transferencias de datos: de registro a registro, de registro a memoria, y de memoria a registro. Pero nunca de memoria a memoria, ya que la arquitectura del procesador y bus no lo permiten.

La sintaxis básica de la instrucción es la siguiente:

MOV destino, fuente.

El destino siempre a la izquierda, y la fuente a la derecha.

Ejemplos:

* MOV AX,5-----> mueve el valor inmediato (o dato) 5 al registro AX. Examinemos esta instrucción. Alguien podría pensar que como el valor 5 cabe en un sólo registro de 8 bits (AL en este caso), el registro AH quedaría como estaba antes de la instrucción. Pues no es as¡. Si le decimos al procesador que introduzca un 5 en AX, as¡ se hará. Poniendo a cero el registro AH, para que AX tenga el valor 5.

Veamos cómo se codifica esta instrucción:

MOV AX,5--------->  B8 05 00  (Código máquina, siempre en hexadecimal).

En primer lugar tenemos el primer byte que contiene el código de operación (B8).

Debido a que este código de operación (B8) tiene implícita la utilización del registro AX como destino, no es necesario el byte EA o byte de direccionamiento, que s¡ sería necesario para transferencias con otros registros. Como vimos en la primera lección al hablar de registros, el registro AX (AH, AL) se utiliza normalmente como acumulador, de tal manera que existen operaciones especiales para trabajar con él, como la instrucción B8 y otras muchas de movimiento de datos, en las que no se especifica el registro mediante el byte EA, ya que está implícito en el código de operación. De esta manera se gana velocidad en la ejecución del programa utilizando los registros para lo que han sido creados. AX acumulador, CX contador, etc.

Después del código de operación tenemos dos bytes (1 palabra). Estos dos bytes forman el campo Valor Inmediato, que como vemos aquí es de 16 bits.

Como os habréis dado cuenta, de los 4 campos que puede tener una instrucción código máquina, ésta sólo tiene dos:

El primero (código de operación), y el último (valor inmediato). Y volviendo de nuevo al campo Valor inmediato y a su tamaño en esta instrucción (2 bytes):

El orden de estos bytes es muy significativo. Veamos...

Tenemos el valor 5 para introducir en una palabra. Lo normal sería que en el código se almacenara este cinco como (00 05), pues en el 8086 esto no es así. Como siempre, para acelerar el programa cuando se manejan transferencias de datos, se llegó a la conclusión de que si se almacenan los bytes que componen una palabra en orden inverso al normal, luego es mucho más rápido recuperarlos. Y es as¡ como se hace en la práctica. Cada vez que almacenamos una palabra en memoria, el byte de mayor peso queda a la derecha del byte de menor peso. De lo anterior se desprende que el número 5 al introducirlo en una palabra de memoria, quedaría como (05 00).

Otro ejemplo: Una vez que almacenamos el número 8BC3H en memoria, si hacemos un volcado de memoria para ver qué tenemos, veremos que en memoria no está el número como 8BC3H, sino que nos encontramos con C38BH.

* MOV AL,5------> Introduce el valor 5 en el registro AL. En este caso, s¡ que AH queda como estaba antes de la instrucción, ya que en la misma no interviene tal registro de ninguna forma (ni implícita al referirse a AX, ni explícita al referirnos a él en concreto).

La instrucción se codifica como:

MOV AL,5--------> B0 05

Este ejemplo es prácticamente como el anterior, excepto que el código de operación en vez de ser B8 es B0, y además ya no hay 2 bytes en el campo valor inmediato, sino que hay uno sólo, ya que vamos a introducir el dato en un registro de tamaño byte.

Ejemplo cuando se trata de transferencias entre registros:

* MOV CX,SI---------> Introduce el valor del registro SI en el registro CX.

La instrucción se codifica como:

MOV CX,SI---------> 8B CE

En esta instrucción tenemos un código de operando y el byte EA. Mediante este byte EA el procesador sabe qué registros intervienen en la transferencia.

Descomponiendo el byte EA en sus dígitos binarios, tenemos:

CE-----> 11001110

El campo MOD con valor 11, hace que R/M seleccione un registro como fuente. El campo REG con valor 001, indica que el registro destino es CX. El campo R/M con valor 110, indica que el registro fuente es SI.

Hemos visto la manera de introducir un dato en un registro. Pero ¿cómo hacemos para introducir un dato en memoria?. Bien, para esto se utilizan las variables (que también existen en ensamblador) o bien, se indica una posición de memoria concreta, pasando de variables. Hay una tercera manera que es utilizar registros índice o base. En el primer caso, es muy simple. Si queremos introducir el valor 70h en la variable X, basta con escribir MOV X,70h. Previamente la variable X la hemos definido y hemos definido también su tamaño: byte, palabra, doble palabra.

Una vez que el compilador de el código ejecutable, lo que antes era la variable X, ahora será la posición de memoria ocupada por la variable. Es decir, que el usar variables es para darnos una gran comodidad a los programadores. Podríamos hacer un programa sin usar variables, indicando posiciones de memoria directamente, pero eso es ya más parecido a código máquina puro que a ensamblador. En el segundo caso, el de indicar la posición de memoria concreta, hay que tener en cuenta si esa posición de memoria la utilizamos como un byte o como una palabra. Esto es as¡ ya que si por medio del programa queremos guardar un 5 en la posición de memoria 7654h (por ejemplo), el procesador no sabe si queremos guardar un byte o una palabra.

Para que no surja ningún tipo de líos, el lenguaje ensamblador cuenta con ciertos convencionalismos para tratar estas transferencias a memoria. Cuando queremos introducir un byte en una posición dada de memoria lo hacemos con el siguiente formato:

MOV BYTE PTR DS:[7654H],5

BYTE PTR indica que vamos a acceder a una posición de memoria de tipo BYTE.

Cuando queremos introducir una palabra a partir de una posición de memoria el formato queda como sigue:

MOV WORD PTR DS:[7654H],5

WORD PTR indica que vamos a acceder a una posición de memoria de tipo WORD.

Tened en cuenta también que cuando se quiere acceder a una posición concreta de memoria sin pasar por una variable, se debe indicar entre corchetes, como en los ejemplos de arriba. Pero eso no es todo, se debe indicar un segmento, para que el procesador sepa a qué zona de 64 ks de la memoria pertenece la posición dada entre los corchetes.

En este caso indicamos el segmento DS (segmento de datos), que es lo usual. Aunque también podríamos haber seleccionado el segmento ES (segmento extra de datos) para así poder transferir algo fuera de nuestra zona de datos.

Obsérvese la manera de indicar una dirección en dirección segmentada, no real. Primero se indica el segmento, luego dos puntos para separar, y luego entre corchetes el offset o desplazamiento dentro de ese segmento.

Segmento:[desplazamiento]

DS:[2626h], ES:[FFFFh], etc.

En el tercer caso nos valemos de un registro índice o base, el cual contiene la dirección de la posición de memoria que nos interesa, para acceder a dicha posición de memoria.

Un ejemplo: MOV BYTE PTR [DI],5

Obsérvese que aquí no es necesario indicar el segmento al que nos referimos. Se coge por defecto el segmento DS. En definitiva, cuando accedemos a memoria a través de registros índice o base, no es necesario indicar el segmento. Mientras que si lo hacemos en forma directa, indicando la posición de memoria tal que [2635h], debemos indicar el segmento con el que vamos a tratar.

Que‚ lío... verdad?

He intentado ponerlo lo más claro posible, con muchos ejemplos, y como se lo explicaría a una persona que tuviera a mi lado. Pasando de rollos teóricos de libros y demás complicaciones, pero si aún as¡ os resulta un lío o complicado, no os preocupéis. Estoy aquí para reexplicar lo que haga falta. Y además cuando empecemos a hacer programillas, todo esto se verá muy claro en la práctica.

Sigamos:

Veamos ahora como se codifica una instrucción en la que se hace acceso a memoria.

* MOV WORD PTR DS:[7654H],5-----> Esta instrucción introduce el valor 5 a partir de la posición de memoria 7654h. Y digo a partir, ya que necesita dos posiciones de memoria para almacenarlo, ya que se trata de un valor inmediato de 16 bits (esto se determina al poner lo del WORD PTR). Con lo cual, la palabra con valor 5, queda almacenada en dos posiciones de memoria, la indicada [7654h] y la contigua [7655h]. Si tenemos en cuenta lo que hemos comentado antes acerca de cómo el 8086 almacena los datos de tipo palabra en memoria, sabremos de antemano que la posición [7654h] contener  el valor 05, y la posición [7655h] contener el valor 00.Veamos cómo se codifica esta instrucción:

MOV WORD PTR [7654H],5------> C7 06 54 76 05 00

Vemos que esta instrucción ha ocupado el máximo posible (6 bytes). De tal forma que los 4 campos de instrucción están presentes. Vamos a estudiarla detenidamente:

Lo primero que tenemos es el código de operación: C7.

Este código indica una operación MOV sobre una dirección concreta o desplazamiento, y con un valor numérico de tipo palabra.

El 06 y 54 byte juntos forman el desplazamiento (tener en cuenta lo del tema del orden inverso en los bytes), y los bytes 76 y 05 juntos forman el valor inmediato a introducir (tener en cuenta de nuevo lo del orden inverso).

Y nos queda el 00 byte, que es el byte EA o de direccionamiento. Que por qué lo he dejado para el final? jeje. Porque llevo 2 o 3 horas intentando descubrir el por qué de que sea 06. No me cuadra por ningún sitio, ya que este 6 indica que no hay desplazamiento, cuando s¡ lo hay.

A ver si para la próxima lección, consigo descifrar el misterio.

Un saludo.

AESOFT....

Para cualquier duda o consulta, deja un mensaje a:

- Francisco Jesús Riquelme

- AESOFT (pseudónimo dentro del BBS club).

 

Podrás encontrarme en:

BBS Club:  (968) 201819/201262  14k4 x2

FidoNet 2:341/43.9   MasterNet 17:3468/301.3
Black Hole  
   
Facebook botón-like  
 
 
Hoy hay 18 visitantes¡Aqui en esta página!
Este sitio web fue creado de forma gratuita con PaginaWebGratis.es. ¿Quieres también tu sitio web propio?
Registrarse gratis