Instrucciones indocumentadas: todos los secretos

0

 ¿Qué es una instrucción?

Una instrucción, código de operación u op_code, como tantas veces he explicado, es un código binario que cuando llega a la CPU, la unidad de control mediante el decodificador la decodificará para determinar qué operación tiene implícita para operar con los datos que la acompañan. Por ejemplo, una instrucción puede ser ADD (suma), SUB (resta), MUL (multiplicación), DIV (división), AND (Y lógico), OR (O lógico), etc., es decir, operaciones aritmeticológicas aplicadas sobre datos enteros o de coma flotante, que son los operandos.

Como debes saber, un programa no es más que una consecución de instrucciones de este tipo. Por ejemplo, veamos un código fuente en C para un programa simple que muestra en pantalla un Hola mundo:

Cuando este código se pasa a lenguaje ensamblador, por ejemplo el de la ISA x86-64, obtenemos estas instrucciones que la CPU deberá procesar para que este programa se pueda ejecutar:

Como ves, lo que tendría que ejecutar la CPU para que este programa funcione es una serie de instrucciones MOV, que son movimientos de datos a registros, y la instrucción syscall que en x86-64 corresponde a la llamada al sistema, aunque en x86 más antiguos puede usarse la instrucción int. Por otro lado están los datos, que en este programa son una serie de constantes y el mensaje Hola mundo…

Una vez que este código se pasa a binario, es decir, se convierte en código máquina o unos y ceros, ya es comprensible por la CPU y la electrónica.

El sistema operativo puede cargarlo en la memoria RAM como un proceso cuando se ejecuta el binario ejecutable y desde allí la CPU comenzará a acceder a las instrucciones y datos que componente este programa. Lo hará de forma secuencial, es decir, en orden, instrucción por instrucción.

Cuando, por ejemplo, llegue la primera instrucción MOV a la CPU, primero pasará a la unidad de control, concretamente al decodificador. Éste, gracias al microcódigo, sabrá decodificar la instrucción, que representa un movimiento de datos al registro en este caso.

Una vez decodificada, la unidad de control sabrá que es una instrucción de movimiento, por lo que generará unas señales de control que indicarán a las unidades de ejecución lo que deben hacer. En este caso el movimiento. Pero igual si fuese una suma, una multiplicación, etc. Así es como funciona la base de la informática.

Es decir, la instrucción MOV, es un op_code (tipo: b8 01 00 00 00) que corresponde en x86 a una serie de códigos binarios, y MOV RAX, 1, lo que hará es grabar una constante, en este caso 1, al registro RAX de la CPU. Cuando llegue este código, el decodificador buscará en la ROM del microcódigo para obtener la interpretación.

Y ¿por qué te cuento esto? Muy sencillo, quería que entendieses esto para saber qué es un op_code o instrucción, y cómo funciona.

Tipos de instrucciones

Como puedes deducir, existen op_codes o instrucciones de varios tipos, para que la CPU pueda ejecutar cualquier programa. En el ejemplo anterior hemos usado solamente un hola mucho, para lo cual con instrucciones MOV y syscall es suficiente. Por ejemplo, veamos este otro código en C para sumar dos números enteros:

El ensamblador para este sería este otro código:

Como ves, en este otro programa también se usan otras instrucciones, como es el caso de ADD EAX, EDX, es decir, los valores de num1 y num2 que se han cargado previamente en estos registros.

Lo que pretendo decirte con esto es que las instrucciones no solo son de movimiento de datos, también podemos encontrar tipos como:

  • De transferencia de datos: permiten mover datos de un registro a otro, de una dirección a otra, cargar una constante o variable en un registro, etc. Es el caso de la MOV que hemos visto anteriormente.
  • Aritméticas: son instrucciones u op_codes que, una vez decodificadas, se traducen en señales que le dirá a la ALU que tiene que realizar una operación aritmética con los operandos. Por ejemplo, de este tipo serían las ADD, SUB, MUL, DIV, etc. ADD EAX, EDX en el ejemplo anterior, sumará el contenido del registro EAX con el de EDX y guardará el resultado en EAX nuevamente.
  • Lógicas: por supuesto, también existen instrucciones para realizar operaciones lógicas sobre bits, como puede ser AND, OR, NOT, XOR, etc.
  • Control: otro tipo de instrucciones importantes son las que pueden alterar el registro PC, es decir, el registro Program Counter de la CPU. Antes he dicho que la CPU procesará las instrucciones el programa en orden, se forma secuencial. Para ello, tiene un registro PC en el que se va sumando 1 a la dirección de la instrucción actual para que apunte a la siguiente. Alterar este registro puede ser necesario cuando el programa necesita que no se siga ese orden secuencial, como cuando se producen saltos condicionales, bucles, llamadas al sistema, etc. Por ejemplo, algunos casos de este tipo de instrucciones serían SYSCALL, JMP, RET, etc.
  • De E/S: para finalizar, también tenemos las instrucciones de operaciones de entrada y salida, que actuarán como si de direcciones de memoria se tratase para leer o escribir en ellas, pero que irán destinadas a los periféricos mapeados. Por ejemplo, IN, OUT,… seguidas del número de puerto.
Instrucciones indocumentadas (illegal op_code)


Un illegal op_code, o instrucción no documentada, o instrucción no deseada, es aquella instrucción que el fabricante de la CPU no ha documentado para que sea usada por los programadores. Y el motivo para que esto ocurra pueden ser varios:
  • Que el diseñador de la CPU desconozca también su existencia y que simplemente haya sido un código implícito en la ISA por error. En estos casos podría generar un funcionamiento errático o no deseado si se usase. Si la CPU ha sido bien diseñada, debería generar una excepción o condición de falla si se trata de ejecutar una de estas instrucciones no documentadas.
  • En algunos otros casos puede ser que el diseñador las haya incluido de forma consciente para ciertas tareas específicas que no pueden ser explotadas directamente por los programadores, es decir, son instrucciones opacas para los desarrolladores, y que pueden servir, por ejemplo, para acelerar ciertas tareas.
  • Otro caso podría ser que sean instrucciones que no se usarán durante el uso habitual de la CPU, pero que se integran para hacer ciertas pruebas durante las etapas de verificación de los sample engineering, etc.
  • En algunos casos podría ser para evitar o dificultar la ingeniería inversa.
  • E incluso podría ser por motivos mucho más oscuros, como poderlas ejecutar con ciertos códigos para poder realizar tareas maliciosas.
Historia

Por ejemplo, uno de los primeros casos de illegal op_codes detectados fue el Intel 8086, el Zilog Z80, el Texas Instruments TM9900, o el MOS Technology 6502 de la década de los 1970. Pero no solo estaban presentes en estas CPUs antiguas, también en otras actuales.

Muchos usuarios han descubierto este tipo de códigos realizando técnicas de fuzzing, viendo que existen algunas instrucciones no documentadas en multitud de modelos de CPU. 
Tags

Publicar un comentario

0 Comentarios
* Por favor, no envíe spam aquí. Todos los comentarios son revisados ​​por el administrador.
Publicar un comentario (0)

#buttons=(Accept !) #days=(20)

Our website uses cookies to enhance your experience. Learn More
Accept !
Subir