¿Puede pensar en algún uso legítimo (inteligente) para la modificación del código en tiempo de ejecución (programa que modifica su propio código en tiempo de ejecución)?
Los sistemas operativos modernos parecen desaprobar los programas que hacen esto, ya que los virus han utilizado esta técnica para evitar la detección.
Todo lo que puedo pensar es algún tipo de optimización en tiempo de ejecución que eliminaría o agregaría código al saber algo en tiempo de ejecución que no se puede conocer en tiempo de compilación.
Respuestas:
Hay muchos casos válidos para la modificación de código. La generación de código en tiempo de ejecución puede resultar útil para:
A veces, el código se traduce en código en tiempo de ejecución (esto se denomina traducción binaria dinámica ):
La modificación de código se puede utilizar para solucionar las limitaciones del conjunto de instrucciones:
Más casos de modificación de código:
fuente
Esto se ha hecho en gráficos por computadora, específicamente en renderizadores de software con fines de optimización. En tiempo de ejecución, se examina el estado de muchos parámetros y se genera una versión optimizada del código rasterizador (eliminando potencialmente muchos condicionales) que permite renderizar primitivas gráficas, por ejemplo, triángulos mucho más rápido.
fuente
Una razón válida es porque el conjunto de instrucciones ASM carece de algunas instrucciones necesarias, que podría construir usted mismo. Ejemplo: en x86 no hay forma de crear una interrupción a una variable en un registro (por ejemplo, hacer interrupción con el número de interrupción en ax). Solo se permitían números constantes codificados en el código de operación. Con código automodificable se podría emular este comportamiento.
fuente
Algunos compiladores solían usarlo para la inicialización de variables estáticas, evitando el costo de un condicional para accesos posteriores. En otras palabras, implementan "ejecutar este código solo una vez" sobrescribiendo ese código con no-ops la primera vez que se ejecuta.
fuente
Hay muchos casos:
Los modelos de seguridad de algunos sistemas operativos significan que el código que se modifica automáticamente no se puede ejecutar sin privilegios de administrador o root, lo que lo hace poco práctico para uso general.
De Wikipedia:
En tales sistemas operativos, incluso programas como Java VM necesitan privilegios de administrador / root para ejecutar su código JIT. (Consulte http://en.wikipedia.org/wiki/W%5EX para obtener más detalles)
fuente
El sistema operativo Synthesis básicamente evaluó parcialmente su programa con respecto a las llamadas a la API y reemplazó el código del sistema operativo con los resultados. El principal beneficio es que desaparecieron muchas comprobaciones de errores (porque si su programa no va a pedirle al sistema operativo que haga algo estúpido, no necesita comprobarlo).
Sí, ese es un ejemplo de optimización del tiempo de ejecución.
fuente
Hace muchos años, pasé una mañana tratando de depurar algún código auto-modificable, una instrucción cambió la dirección de destino de la siguiente instrucción, es decir, estaba calculando una dirección de sucursal. Estaba escrito en lenguaje ensamblador y funcionó perfectamente cuando revisé el programa una instrucción a la vez. Pero cuando ejecuté el programa falló. Finalmente, me di cuenta de que la máquina estaba obteniendo 2 instrucciones de la memoria y (como las instrucciones estaban colocadas en la memoria) la instrucción que estaba modificando ya había sido recuperada y, por lo tanto, la máquina estaba ejecutando la versión no modificada (incorrecta) de la instrucción. Por supuesto, cuando estaba depurando, solo estaba haciendo una instrucción a la vez.
Mi punto, el código auto-modificable puede ser extremadamente desagradable de probar / depurar y, a menudo, tiene suposiciones ocultas sobre el comportamiento de la máquina (ya sea hardware o virtual). Además, el sistema nunca podría compartir páginas de códigos entre los diversos subprocesos / procesos que se ejecutan en las (ahora) máquinas de múltiples núcleos. Esto anula muchos de los beneficios de la memoria virtual, etc. También invalidaría las optimizaciones de rama realizadas a nivel de hardware.
(Nota: no incluyo JIT en la categoría de código que se modifica automáticamente. JIT está traduciendo de una representación del código a una representación alternativa, no está modificando el código)
En general, es solo una mala idea, realmente ordenada, realmente oscura, pero realmente mala.
por supuesto, si todo lo que tiene son 8080 y ~ 512 bytes de memoria, es posible que tenga que recurrir a tales prácticas.
fuente
Desde el punto de vista del núcleo de un sistema operativo, cada Just In Time Compiler y Linker Runtime realiza la auto modificación del texto del programa. Un ejemplo destacado sería el intérprete de secuencias de comandos ECMA V8 de Google.
fuente
Otra razón por la que el código se modifica automáticamente (en realidad, un código "autogenerado") es implementar un mecanismo de compilación Just-In-time para el rendimiento. Por ejemplo, un programa que lee una expresión algebric y la calcula en un rango de parámetros de entrada puede convertir la expresión en código de máquina antes de establecer el cálculo.
fuente
Conoces la vieja casta de que no hay una diferencia lógica entre hardware y software ... también se puede decir que no hay una diferencia lógica entre código y datos.
¿Qué es el código auto modificable? Código que coloca valores en el flujo de ejecución para que se pueda interpretar no como datos sino como un comando. Seguro que existe el punto de vista teórico en los lenguajes funcionales de que realmente no hay diferencia. Estoy diciendo que podemos hacer esto de una manera sencilla en lenguajes imperativos y compiladores / intérpretes sin la presunción de igualdad de estatus.
A lo que me refiero es en el sentido práctico de que los datos pueden alterar las rutas de ejecución del programa (en cierto sentido, esto es extremadamente obvio). Estoy pensando en algo así como un compilador-compilador que crea una tabla (una matriz de datos) que uno atraviesa al analizar, moviéndose de un estado a otro (y también modificando otras variables), al igual que cómo se mueve un programa de un comando a otro. , modificando variables en el proceso.
Entonces, incluso en el caso habitual en el que un compilador crea un espacio de código y se refiere a un espacio de datos completamente separado (el montón), aún se pueden modificar los datos para cambiar explícitamente la ruta de ejecución.
fuente
Implementé un programa usando la evolución para crear el mejor algoritmo. Usó código de modificación automática para modificar el plano de ADN.
fuente
Un caso de uso es el archivo de prueba EICAR, que es un archivo COM ejecutable legítimo de DOS para probar programas antivirus.
Tiene que utilizar la modificación del código propio porque el archivo ejecutable debe contener solo caracteres ASCII imprimibles / mecanografiables en el rango [21h-60h, 7Bh-7Dh], lo que limita significativamente el número de instrucciones codificables
Los detalles se explican aquí.
También se usa para el despacho de operaciones de punto flotante en DOS
Algunos compiladores emitirán
CD xx
con xx que van desde 0x34-0x3B en lugar de instrucciones de coma flotante x87. Dado queCD
es el código de operación para laint
instrucción, saltará a la interrupción 34h-3Bh y emulará esa instrucción en el software si el coprocesador x87 no está disponible. De lo contrario, el manejador de interrupciones reemplazará esos 2 bytes con9B Dx
para que las ejecuciones posteriores sean manejadas directamente por x87 sin emulación.¿Cuál es el protocolo para la emulación de punto flotante x87 en MS-DOS?
fuente
El kernel de Linux tiene módulos de kernel cargables que hacen precisamente eso.
Emacs también tiene esta habilidad y la uso todo el tiempo.
Todo lo que admita una arquitectura de complemento dinámico es esencialmente modificar su código en tiempo de ejecución.
fuente
Realizo análisis estadísticos contra una base de datos continuamente actualizada. Mi modelo estadístico se escribe y se reescribe cada vez que se ejecuta el código para adaptarse a los nuevos datos que están disponibles.
fuente
El escenario en el que se puede utilizar es un programa de aprendizaje. En respuesta a la entrada del usuario, el programa aprende un nuevo algoritmo:
Hay una pregunta sobre cómo hacer eso en Java: ¿Cuáles son las posibilidades de auto-modificación del código Java?
fuente
La mejor versión de esto puede ser Lisp Macros. A diferencia de las macros de C, que son solo un preprocesador, Lisp le permite tener acceso a todo el lenguaje de programación en todo momento. Esta es la característica más poderosa de lisp y no existe en ningún otro idioma.
¡De ninguna manera soy un experto, pero haz que uno de los chicos ceceo hable de ello! Hay una razón por la que dicen que Lisp es el lenguaje más poderoso que existe y la gente inteligente no es que probablemente tengan razón.
fuente