stringtranslate.com

Codigo de maquina

Monitor de lenguaje de máquina en una computadora de placa única W65C816S , que muestra el desmontaje del código , así como el registro del procesador y los volcados de memoria.

En programación de computadoras , el código de máquina es un código de computadora que consta de instrucciones en lenguaje de máquina , que se utilizan para controlar la unidad central de procesamiento (CPU) de una computadora. Aunque las computadoras decimales alguna vez fueron comunes, el mercado contemporáneo está dominado por las computadoras binarias ; para esas computadoras, el código de máquina es "la representación binaria de un programa de computadora que en realidad es leído e interpretado por la computadora. Un programa en código de máquina consiste en una secuencia de instrucciones de máquina (posiblemente intercaladas con datos)". [1]

Cada instrucción hace que la CPU realice una tarea muy específica, como una carga, un almacenamiento, un salto o una operación de unidad aritmética lógica (ALU) en una o más unidades de datos en los registros o la memoria de la CPU .

Las primeras CPU tenían un código de máquina específico que podía romper la compatibilidad con cada nueva CPU lanzada. La noción de arquitectura de conjunto de instrucciones (ISA) define y especifica el comportamiento y la codificación en memoria del conjunto de instrucciones del sistema, sin especificar su implementación exacta. Esto actúa como una capa de abstracción, permitiendo la compatibilidad dentro de la misma familia de CPU, de modo que el código de máquina escrito o generado según ISA para la familia se ejecutará en todas las CPU de la familia, incluidas las CPU futuras.

En general, cada familia de arquitectura (por ejemplo, x86 , ARM ) tiene su propio ISA y, por tanto, su propio lenguaje de código de máquina específico. Hay excepciones, como la arquitectura VAX , que incluía soporte opcional del conjunto de instrucciones PDP-11 y el IA-64 , que incluía soporte opcional del conjunto de instrucciones IA-32 . Otro ejemplo es el PowerPC 615 , un procesador diseñado para procesar de forma nativa instrucciones tanto PowerPC como x86.

El código de máquina es un lenguaje estrictamente numérico y es la interfaz de nivel más bajo para la CPU destinada a un programador. El lenguaje ensamblador  proporciona un mapeo directo entre el código de máquina numérico y una versión legible por humanos donde los códigos de operación y operandos numéricos se reemplazan por cadenas legibles (por ejemplo, 0x90 es la instrucción NOP en x86 ). Si bien es posible escribir programas directamente en código de máquina, administrar bits individuales y calcular direcciones numéricas y constantes manualmente es tedioso y propenso a errores. Por esta razón, los programas rara vez se escriben directamente en código de máquina en contextos modernos, pero se pueden hacer para depuración de bajo nivel , parcheo de programas (especialmente cuando la fuente ensambladora no está disponible) y desensamblaje del lenguaje ensamblador .

La mayoría de los programas prácticos actuales están escritos en lenguajes de nivel superior . Esos programas son traducidos a código de máquina por un compilador o interpretados por un intérprete , generalmente después de ser traducidos a un código intermedio, como un código de bytes , que luego se interpreta. [nota 1]

El código de máquina es, por definición, el nivel más bajo de detalle de programación visible para el programador, pero internamente muchos procesadores utilizan microcódigo u optimizan y transforman instrucciones de código de máquina en secuencias de microoperaciones . El microcódigo y las microoperaciones generalmente no se consideran código de máquina; Excepto en algunas máquinas, el usuario no puede escribir microcódigo o microoperaciones, y la operación del microcódigo y la transformación de las instrucciones del código de máquina en microoperaciones ocurre de forma transparente para el programador, excepto por los efectos secundarios relacionados con el rendimiento.

Conjunto de instrucciones

Cada procesador o familia de procesadores tiene su propio conjunto de instrucciones . Las instrucciones son patrones de bits , dígitos o caracteres que corresponden a comandos de una máquina. Por tanto, el conjunto de instrucciones es específico de una clase de procesadores que utilizan (principalmente) la misma arquitectura . Los diseños de procesadores sucesores o derivados a menudo incluyen instrucciones de un predecesor y pueden agregar nuevas instrucciones adicionales. Ocasionalmente, un diseño sucesor descontinuará o alterará el significado de algún código de instrucción (generalmente porque es necesario para nuevos propósitos), lo que afectará la compatibilidad del código hasta cierto punto; Incluso los procesadores compatibles pueden mostrar un comportamiento ligeramente diferente en algunas instrucciones, pero esto rara vez supone un problema. Los sistemas también pueden diferir en otros detalles, como la disposición de la memoria, los sistemas operativos o los dispositivos periféricos . Debido a que un programa normalmente depende de tales factores, diferentes sistemas normalmente no ejecutarán el mismo código de máquina, incluso cuando se utilice el mismo tipo de procesador.

El conjunto de instrucciones de un procesador puede tener instrucciones de longitud fija o variable. La forma en que se organizan los patrones varía según la arquitectura particular y el tipo de instrucción. La mayoría de las instrucciones tienen uno o más campos de código de operación que especifican el tipo de instrucción básica (como aritmética, lógica, salto , etc.), la operación (como agregar o comparar) y otros campos que pueden proporcionar el tipo de operando (s) . ), los modos de direccionamiento , los desplazamientos o índices de direccionamiento, o el valor del operando en sí (tales operandos constantes contenidos en una instrucción se denominan inmediatos ). [2]

No todas las máquinas o instrucciones individuales tienen operandos explícitos. En una máquina con un solo acumulador , el acumulador es implícitamente tanto el operando izquierdo como el resultado de la mayoría de las instrucciones aritméticas. Algunas otras arquitecturas, como la arquitectura x86 , tienen versiones acumulativas de instrucciones comunes, y el acumulador se considera uno de los registros generales de instrucciones más largas. Una máquina de pila tiene la mayoría o todos sus operandos en una pila implícita. Las instrucciones de propósito especial también suelen carecer de operandos explícitos; por ejemplo, CPUID en la arquitectura x86 escribe valores en cuatro registros de destino implícitos. Esta distinción entre operandos explícitos e implícitos es importante en los generadores de códigos, especialmente en las partes de asignación de registros y seguimiento de rango en vivo. Un buen optimizador de código puede rastrear operandos implícitos y explícitos, lo que puede permitir una propagación constante más frecuente , un plegado constante de registros (un registro asignado al resultado de una expresión constante liberada al reemplazarlo por esa constante) y otras mejoras de código.

Programas

Un programa de computadora es una lista de instrucciones que pueden ser ejecutadas por una unidad central de procesamiento (CPU). La ejecución de un programa se realiza para que la CPU que lo ejecuta resuelva un problema y así lograr un resultado. Mientras que los procesadores simples pueden ejecutar instrucciones una tras otra, los procesadores superescalares pueden, en determinadas circunstancias (cuando la canalización está llena), ejecutar dos o más instrucciones simultáneamente. Por ejemplo, el Intel Pentium original de 1993 puede ejecutar como máximo dos instrucciones por ciclo de reloj cuando su canalización está llena.

El flujo del programa puede verse influenciado por instrucciones especiales de "salto" que transfieren la ejecución a una dirección (y por tanto a la instrucción) distinta de la siguiente dirección numéricamente secuencial. Que se produzcan estos saltos condicionales depende de una condición como que un valor sea mayor, menor o igual a otro valor.

Lenguajes ensambladores

Una interpretación mucho más amigable para los humanos del lenguaje de máquina, llamada lenguaje ensamblador , usa códigos mnemotécnicos para referirse a instrucciones de código de máquina, en lugar de usar los valores numéricos de las instrucciones directamente, y usa nombres simbólicos para referirse a ubicaciones de almacenamiento y, a veces, registros . [3] Por ejemplo, en el procesador Zilog Z8000000101 , el código de máquina , que hace que la CPU disminuya el B registro de propósito general , se representaría en lenguaje ensamblador como DEC B. [4]

Ejemplo

La arquitectura MIPS proporciona un ejemplo específico para un código de máquina cuyas instrucciones tienen siempre 32 bits de longitud. [5] : 299  El tipo general de instrucción viene dado por el campo op (operación), los 6 bits más altos. Las instrucciones tipo J (salto) y tipo I (inmediata) están completamente especificadas en op . Las instrucciones de tipo R (registro) incluyen una función de campo adicional para determinar la operación exacta. Los campos utilizados en estos tipos son:

 6 5 5 5 5 6 bits[ op | rs | rt | rd |falso| función] tipo R[ op | rs | rt | dirección/inmediato] tipo I[ op | dirección de destino] tipo J

rs , rt y rd indican operandos de registro; shamt da una cantidad de turno; y la dirección o los campos inmediatos contienen un operando directamente. [5] : 299–301 

Por ejemplo, sumando los registros 1 y 2 y colocando el resultado en el registro 6 se codifica: [5] : 554 

[ op | rs | rt | rd |falso| función] 0 1 2 6 0 32 decimales 000000 00001 00010 00110 00000 100000 binario

Cargue un valor en el registro 8, tomado de la celda de memoria 68 celdas después de la ubicación indicada en el registro 3: [5] : 552 

[ op | rs | rt | dirección/inmediato] 35 3 8 68 decimales 100011 00011 01000 00000 00001 000100 binario

Saltando a la dirección 1024: [5] : 552 

[ op | dirección de destino] 2 1024 decimales 000010 00000 00000 00000 10000 000000 binario

Instrucciones superpuestas

En arquitecturas de procesador con conjuntos de instrucciones de longitud variable [6] (como la familia de procesadores x86 de Intel ), dentro de los límites del fenómeno de resincronización del flujo de control conocido como recuento de Kruskal , [7] [6] [8] [9] [10] a veces es posible mediante programación a nivel de código de operación organizar deliberadamente el código resultante de modo que dos rutas de código compartan un fragmento común de secuencias de código de operación. [nb 2] Estas se denominan instrucciones superpuestas , códigos de operación superpuestos , código superpuesto , código superpuesto , escisión de instrucciones o salto al medio de una instrucción , y representan una forma de superposición . [11] [12] [13]

En las décadas de 1970 y 1980, a veces se utilizaban instrucciones superpuestas para preservar el espacio de la memoria. Un ejemplo fue la implementación de tablas de error en Altair BASIC de Microsoft , donde las instrucciones entrelazadas compartían mutuamente sus bytes de instrucción. [14] [6] [11] La técnica rara vez se utiliza hoy en día, pero aún podría ser necesario recurrir a ella en áreas donde es necesaria una optimización extrema del tamaño a nivel de bytes, como en la implementación de cargadores de arranque que tienen que encajar en sectores de arranque . [nota 3]

A veces también se utiliza como técnica de ofuscación de código como medida contra el desmontaje y la manipulación. [6] [9]

El principio también se utiliza en secuencias de código compartido de binarios pesados ​​que deben ejecutarse en múltiples plataformas de procesadores incompatibles con conjuntos de instrucciones. [nota 2]

Esta propiedad también se utiliza para encontrar instrucciones no deseadas llamadas gadgets en repositorios de código existentes y se utiliza en programación orientada al retorno como alternativa a la inyección de código para vulnerabilidades como los ataques de retorno a libc . [15] [6]

Relación con el microcódigo

En algunas computadoras, el código de máquina de la arquitectura se implementa mediante una capa subyacente aún más fundamental llamada microcódigo , que proporciona una interfaz de lenguaje de máquina común a través de una línea o familia de diferentes modelos de computadora con flujos de datos subyacentes muy diferentes . Esto se hace para facilitar la portabilidad de programas en lenguaje de máquina entre diferentes modelos. Un ejemplo de este uso es la familia de computadoras IBM System/360 y sus sucesores. Con anchos de ruta de flujo de datos de 8 bits a 64 bits y más, presentan sin embargo una arquitectura común a nivel de lenguaje de máquina en toda la línea.

El uso de microcódigo para implementar un emulador permite que la computadora presente la arquitectura de una computadora completamente diferente. La línea System/360 usó esto para permitir portar programas de máquinas IBM anteriores a la nueva familia de computadoras, por ejemplo, un emulador IBM 1401/1440/1460 en el IBM S/360 modelo 40.

Relación con el código de bytes

El código de máquina es generalmente diferente del código de bytes (también conocido como código p), que es ejecutado por un intérprete o compilado en código de máquina para una ejecución más rápida (directa). Una excepción es cuando un procesador está diseñado para utilizar un código de bytes particular directamente como código de máquina, como es el caso de los procesadores Java .

El código de máquina y el código ensamblador a veces se denominan código nativo cuando se hace referencia a partes de funciones o bibliotecas del lenguaje que dependen de la plataforma. [dieciséis]

Almacenamiento en la memoria

Desde el punto de vista de la CPU, el código de máquina se almacena en la RAM, pero normalmente también se guarda en un conjunto de cachés por motivos de rendimiento. Puede haber diferentes cachés para instrucciones y datos, según la arquitectura.

La CPU sabe qué código de máquina ejecutar, basándose en su contador de programa interno. El contador del programa apunta a una dirección de memoria y se cambia según instrucciones especiales que pueden provocar ramas programáticas. El contador del programa generalmente se establece en un valor codificado cuando la CPU se enciende por primera vez y, por lo tanto, ejecutará cualquier código de máquina que se encuentre en esta dirección.

De manera similar, el contador del programa se puede configurar para ejecutar cualquier código de máquina que se encuentre en alguna dirección arbitraria, incluso si no es un código de máquina válido. Normalmente, esto desencadenará un fallo de protección específico de la arquitectura.

A menudo, los permisos de página en un sistema basado en paginación le dicen a la CPU si la página actual realmente contiene código de máquina mediante un bit de ejecución; las páginas tienen múltiples bits de permiso (legibles, escribibles, etc.) para diversas funciones de mantenimiento. Por ejemplo, en sistemas tipo Unix, las páginas de memoria se pueden alternar para que sean ejecutables con la mprotect()llamada al sistema, y ​​en Windows, VirtualProtect()se pueden usar para lograr un resultado similar. Si se intenta ejecutar código de máquina en una página no ejecutable, normalmente se producirá una falla específica de la arquitectura. Tratar los datos como código de máquina , o encontrar nuevas formas de utilizar el código de máquina existente, mediante diversas técnicas, es la base de algunas vulnerabilidades de seguridad.

De manera similar, en un sistema basado en segmentos, los descriptores de segmentos pueden indicar si un segmento puede contener código ejecutable y en qué anillos se puede ejecutar ese código.

Desde el punto de vista de un proceso , el espacio de código es la parte de su espacio de direcciones donde se almacena el código en ejecución. En los sistemas multitarea , esto comprende el segmento de código del programa y, por lo general, bibliotecas compartidas . En un entorno de subprocesos múltiples , diferentes subprocesos de un proceso comparten espacio de código junto con espacio de datos, lo que reduce considerablemente la sobrecarga del cambio de contexto en comparación con el cambio de procesos.

Legibilidad por humanos

Pamela Samuelson escribió que el código de máquina es tan ilegible que la Oficina de Derechos de Autor de los Estados Unidos no puede identificar si un programa codificado en particular es una obra de autoría original; [17] sin embargo, la Oficina de Derechos de Autor de EE. UU. permite el registro de derechos de autor de programas de computadora [18] y el código de máquina de un programa a veces se puede descompilar para que su funcionamiento sea más fácilmente comprensible para los humanos. [19] Sin embargo, al resultado de un descompilador o desensamblador le faltarán los comentarios y las referencias simbólicas, por lo que si bien el resultado puede ser más fácil de leer que el código objeto, seguirá siendo más difícil que el código fuente original. Este problema no existe para formatos de código objeto como SQUOZE , donde el código fuente se incluye en el archivo.

El profesor de ciencias cognitivas Douglas Hofstadter ha comparado el código de máquina con el código genético , diciendo que "mirar un programa escrito en lenguaje de máquina es vagamente comparable a mirar una molécula de ADN átomo por átomo". [20]

Ver también

Notas

  1. ^ Como muchas versiones de BASIC , especialmente las primeras, así como Smalltalk , MATLAB , Perl , Python , Ruby y otros lenguajes de programación o de propósito especial .
  2. ^ ab Si bien a veces se pueden organizar instrucciones superpuestas en arquitecturas de procesador con conjuntos de instrucciones de longitud variable para fusionar diferentes rutas de código en una sola mediante la resincronización del flujo de control , a veces también se puede diseñar código superpuesto para diferentes arquitecturas de procesador para provocar que las rutas de ejecución se bifurquen en diferentes direcciones dependiendo del procesador subyacente, como a veces se utiliza en binarios gordos .
  3. ^ Como ejemplo, los MBR de DR-DOS y los sectores de arranque (que también contienen la tabla de particiones y el bloque de parámetros del BIOS , dejando menos de 446, respectivamente, 423 bytes para el código) tradicionalmente podían ubicar el archivo de arranque en el archivo FAT12 o FAT16 . sistema por sí mismos y cargarlo en la memoria como un todo, en contraste con sus contrapartes en MS-DOS / PC DOS , que en cambio dependían de los archivos del sistema para ocupar las dos primeras entradas del directorio en el sistema de archivos y los primeros tres sectores de IBMBIO. .COM se almacenará al inicio del área de datos en sectores contiguos que contienen un cargador secundario para cargar el resto del archivo en la memoria (lo que requiere que SYS se encargue de todas estas condiciones). Cuando se agregó soporte para FAT32 y LBA , Microsoft incluso pasó a requerir instrucciones 386 y dividió el código de arranque en dos sectores por razones de tamaño del código, lo cual no era una opción a seguir para DR-DOS ya que se habría roto hacia atrás y la compatibilidad cruzada con otros sistemas operativos en escenarios de arranque múltiple y carga en cadena , así como con PC más antiguas . En cambio, los sectores de arranque de DR-DOS 7.07 recurrieron a código automodificable , programación a nivel de código de operación en lenguaje de máquina, utilización controlada de efectos secundarios (documentados) , superposición de datos/código multinivel y técnicas de plegado algorítmico para aun así encajar todo en un sector físico de sólo 512 bytes sin renunciar a ninguna de sus funciones extendidas.

Referencias

  1. ^ Puestos, William. Organización y Arquitectura de Computadores 10ª edición . pag. 776.ISBN _ 9789332570405.
  2. ^ Kjell, Bradley. "Operando inmediato".
  3. ^ Dourish, Paul (2004). Dónde está la acción: los fundamentos de la interacción encarnada. Prensa del MIT . pag. 7.ISBN _ 0-262-54178-5. Consultado el 5 de marzo de 2023 .
  4. ^ Zaks, Rodnay (1982). Programación del Z80 (Tercera edición revisada). Sybex . págs.67, 120, 609. ISBN 0-89588-094-6. Consultado el 5 de marzo de 2023 .
  5. ^ abcdeHarris , David; Harris, Sarah L. (2007). Diseño Digital y Arquitectura de Computadores. Editores Morgan Kaufmann . ISBN 978-0-12-370497-9. Consultado el 5 de marzo de 2023 .
  6. ^ abcde Jacob, Matías; Jakubowski, Mariusz H.; Venkatesan, Ramarathnam [en Wikidata] (20-21 de septiembre de 2007). Hacia la ejecución binaria integral: implementación de hash ajeno mediante codificaciones de instrucciones superpuestas (PDF) . Actas del noveno taller sobre Multimedia y Seguridad (MM&Sec '07). Dallas, Texas, EE.UU.: Asociación de Maquinaria de Computación . págs. 129-140. CiteSeerX 10.1.1.69.5258 . doi :10.1145/1288869.1288887. ISBN  978-1-59593-857-2. S2CID  14174680. Archivado (PDF) desde el original el 4 de septiembre de 2018 . Consultado el 25 de diciembre de 2021 .(12 páginas)
  7. ^ Lagarias, Jeffrey "Jeff" Clark ; Lluvias, Eric Michael ; Vanderbei, Robert J. (2009) [13 de octubre de 2001]. Brams, Stephen; Gehrlein, William V.; Roberts, Fred S. (eds.). "El Conde Kruskal". Las matemáticas de la preferencia, la elección y el orden. Ensayos en honor a Peter J. Fishburn . Berlín / Heidelberg, Alemania: Springer-Verlag : 371–391. arXiv : matemáticas/0110143 . ISBN 978-3-540-79127-0.(22 páginas)
  8. ^ Andriesse, Dennis; Bos, Herbert [en Wikidata] (10 de julio de 2014). Escrito en Vrije Universiteit Amsterdam, Amsterdam, Países Bajos. Dietrich, Sven (ed.). Esteganografía a nivel de instrucción para malware encubierto basado en activadores (PDF) . XI Congreso Internacional sobre Detección de Intrusiones y Malware, y Evaluación de Vulnerabilidades (DIMVA). Apuntes de conferencias sobre informática . Egham, Reino Unido; Suiza: Springer International Publishing . págs. 41–50 [45]. doi :10.1007/978-3-319-08509-8_3. eISSN  1611-3349. ISBN 978-3-31908508-1. ISSN  0302-9743. S2CID  4634611. LNCS 8550. Archivado (PDF) desde el original el 26 de agosto de 2023 . Consultado el 26 de agosto de 2023 .(10 páginas)
  9. ^ ab Jakubowski, Mariusz H. (febrero de 2016). "Modelo basado en gráficos para protección contra manipulaciones de software". Microsoft . Archivado desde el original el 31 de octubre de 2019 . Consultado el 19 de agosto de 2023 .
  10. ^ Jämthagen, Christopher (noviembre de 2016). "Sobre métodos ofensivos y defensivos en la seguridad del software" (PDF) (Tesis). Lund, Suecia: Departamento de Tecnología de la Información y Eléctrica, Universidad de Lund . pag. 96.ISBN _ 978-91-7623-942-1. ISSN  1654-790X. Archivado (PDF) desde el original el 26 de agosto de 2023 . Consultado el 26 de agosto de 2023 .(1+xvii+1+152 páginas)
  11. ^ ab "Instrucciones no deseadas en x86". Noticias de piratas informáticos . 2021. Archivado desde el original el 25 de diciembre de 2021 . Consultado el 24 de diciembre de 2021 .
  12. ^ Más amable, Johannes (24 de septiembre de 2010). Análisis estático de ejecutables x86 [ Statische Analyse von Programmen in x86 Maschinensprache ] (PDF) (Disertación). Múnich, Alemania: Technische Universität Darmstadt . D17. Archivado desde el original el 12 de noviembre de 2020 . Consultado el 25 de diciembre de 2021 .(199 páginas)
  13. ^ "¿Qué es la ofuscación de" instrucciones superpuestas "?". Intercambio de pila de ingeniería inversa . 2013-04-07. Archivado desde el original el 25 de diciembre de 2021 . Consultado el 25 de diciembre de 2021 .
  14. ^ Gates, William "Bill" Henry , comunicación personal(NB. Según Jacob et al.)
  15. ^ Shacham, Hovav (2007). La geometría de la carne inocente sobre los huesos: regreso a libc sin llamadas a funciones (en x86) (PDF) . Actas de la ACM, CCS 2007. Prensa ACM . Archivado (PDF) desde el original el 15 de diciembre de 2021 . Consultado el 24 de diciembre de 2021 .
  16. ^ "Administrado, no administrado, nativo: ¿qué tipo de código es este?". desarrollador.com . 2003-04-28 . Consultado el 2 de septiembre de 2008 .
  17. ^ Samuelson, Pamela (septiembre de 1984). "CONTU revisitado: el caso contra la protección de derechos de autor para programas informáticos en formato legible por máquina". Revista de derecho de Duke . 1984 (4): 663–769. doi :10.2307/1372418. JSTOR  1372418. PMID  10268940.
  18. ^ "Registro de derechos de autor para programas informáticos" (PDF) . Oficina de derechos de autor de EE. UU . Agosto de 2008 . Consultado el 23 de febrero de 2014 .
  19. ^ "¿Qué es descompilar? - Definición de WhatIs.com". WhatIs.com . Consultado el 26 de diciembre de 2016 .
  20. ^ Hofstadter, Douglas R. (1980). Gödel, Escher, Bach: Una eterna trenza dorada . pag. 290.

Otras lecturas