Pouvez-vous penser à des utilisations légitimes (intelligentes) pour la modification du code d'exécution (programme modifiant son propre code au moment de l'exécution)?
Les systèmes d'exploitation modernes semblent désapprouver les programmes qui font cela puisque cette technique a été utilisée par des virus pour éviter la détection.
Tout ce à quoi je peux penser, c'est une sorte d'optimisation d'exécution qui supprimerait ou ajouterait du code en connaissant quelque chose au moment de l'exécution qui ne peut pas être connu au moment de la compilation.
Réponses:
Il existe de nombreux cas valides pour la modification de code. La génération de code au moment de l'exécution peut être utile pour:
Parfois, le code est traduit en code au moment de l'exécution (c'est ce qu'on appelle la traduction binaire dynamique ):
La modification du code peut être utilisée pour contourner les limitations du jeu d'instructions:
Plus de cas de modification de code:
la source
Cela a été fait dans l'infographie, en particulier les moteurs de rendu logiciels à des fins d'optimisation. Lors de l'exécution, l'état de nombreux paramètres est examiné et une version optimisée du code du rastériseur est générée (éliminant potentiellement beaucoup de conditions), ce qui permet de rendre les primitives graphiques, par exemple les triangles, beaucoup plus rapidement.
la source
Une raison valable est que le jeu d'instructions asm ne dispose pas des instructions nécessaires, que vous pouvez créer vous-même. Exemple: Sur x86, il n'y a aucun moyen de créer une interruption vers une variable dans un registre (par exemple, faire une interruption avec un numéro d'interruption dans ax). Seuls les numéros const codés dans l'opcode étaient autorisés. Avec un code auto-modifiable, on pourrait émuler ce comportement.
la source
Certains compilateurs l'utilisaient pour l'initialisation de variables statiques, évitant ainsi le coût d'un conditionnel pour les accès ultérieurs. En d'autres termes, ils implémentent "n'exécuter ce code qu'une seule fois" en écrasant ce code par des no-ops la première fois qu'il est exécuté.
la source
Il existe de nombreux cas:
Les modèles de sécurité de certains systèmes d'exploitation signifient que le code auto-modifiable ne peut pas s'exécuter sans les privilèges root / admin, ce qui le rend impraticable pour une utilisation générale.
De Wikipedia:
Sur de tels systèmes d'exploitation, même des programmes tels que la machine virtuelle Java ont besoin des privilèges root / admin pour exécuter leur code JIT. (Voir http://en.wikipedia.org/wiki/W%5EX pour plus de détails)
la source
Le système d'exploitation Synthesis a essentiellement évalué partiellement votre programme par rapport aux appels d'API et a remplacé le code du système d'exploitation par les résultats. Le principal avantage est que de nombreuses vérifications d'erreurs ont disparu (car si votre programme ne demande pas au système d'exploitation de faire quelque chose de stupide, il n'a pas besoin de vérifier).
Oui, c'est un exemple d'optimisation d'exécution.
la source
Il y a de nombreuses années, j'ai passé une matinée à essayer de déboguer du code auto-modifiable, une instruction a changé l'adresse cible de l'instruction suivante, c'est-à-dire que je calculais une adresse de branche. Il a été écrit en langage d'assemblage et a parfaitement fonctionné lorsque j'ai parcouru le programme une instruction à la fois. Mais quand j'ai exécuté le programme, il a échoué. Finalement, j'ai réalisé que la machine récupérait 2 instructions de la mémoire et (comme les instructions étaient disposées en mémoire) l'instruction que je modifiais avait déjà été récupérée et que la machine exécutait donc la version non modifiée (incorrecte) de l'instruction. Bien sûr, lorsque je déboguais, je ne faisais qu'une instruction à la fois.
Mon point de vue, le code auto-modifiable peut être extrêmement désagréable à tester / déboguer et a souvent des hypothèses cachées quant au comportement de la machine (qu'elle soit matérielle ou virtuelle). De plus, le système ne pourrait jamais partager les pages de code entre les différents threads / processus s'exécutant sur les (maintenant) machines multicœurs. Cela annule de nombreux avantages de la mémoire virtuelle, etc. Cela invaliderait également les optimisations de branche effectuées au niveau matériel.
(Remarque - je n'inclus pas JIT dans la catégorie du code auto-modifiable. JIT traduit une représentation du code en une autre représentation, il ne modifie pas le code)
Dans l'ensemble, c'est juste une mauvaise idée - vraiment soignée, vraiment obscure, mais vraiment mauvaise.
bien sûr - si tout ce que vous avez est une mémoire de 8080 et ~ 512 octets, vous devrez peut-être recourir à de telles pratiques.
la source
Du point de vue du noyau d'un système d'exploitation, chaque Just In Time Compiler et Linker Runtime effectue une auto-modification du texte du programme. Un exemple frappant serait l'interpréteur de scripts V8 ECMA de Google.
la source
Une autre raison du code auto-modifiable (en fait un code "auto-générateur") est d'implémenter un mécanisme de compilation juste à temps pour les performances. Par exemple, un programme qui lit une expression algébrique et la calcule sur une plage de paramètres d'entrée peut convertir l'expression en code machine avant d'énoncer le calcul.
la source
Vous connaissez le vieux châtain qu'il n'y a pas de différence logique entre le matériel et le logiciel ... on peut aussi dire qu'il n'y a pas de différence logique entre le code et les données.
Qu'est-ce que le code auto-modifiable? Code qui place des valeurs dans le flux d'exécution afin qu'il puisse être interprété non pas en tant que données mais en tant que commande. Bien sûr, il y a le point de vue théorique dans les langages fonctionnels qu'il n'y a vraiment aucune différence. Je dis que sur e peut le faire d'une manière simple dans les langages impératifs et les compilateurs / interprètes sans présomption d'égalité de statut.
Ce à quoi je fais référence, c'est dans le sens pratique que les données peuvent modifier les chemins d'exécution du programme (dans un certain sens, c'est extrêmement évident). Je pense à quelque chose comme un compilateur-compilateur qui crée une table (un tableau de données) que l'on traverse en analysant, passant d'un état à l'autre (et modifiant également d'autres variables), tout comme la façon dont un programme passe d'une commande à l'autre , en modifiant les variables dans le processus.
Ainsi, même dans le cas habituel où un compilateur crée un espace de code et se réfère à un espace de données entièrement séparé (le tas), on peut toujours modifier les données pour changer explicitement le chemin d'exécution.
la source
J'ai implémenté un programme utilisant l'évolution pour créer le meilleur algorithme. Il a utilisé un code auto-modifiable pour modifier le plan d'ADN.
la source
Un cas d'utilisation est le fichier de test EICAR qui est un fichier COM exécutable DOS légitime pour tester les programmes antivirus.
Il doit utiliser la modification du code automatique car le fichier exécutable ne doit contenir que des caractères ASCII imprimables / typables dans la plage [21h-60h, 7Bh-7Dh], ce qui limite considérablement le nombre d'instructions encodables
Les détails sont expliqués ici
Il est également utilisé pour la distribution d'opérations en virgule flottante sous DOS
Certains compilateurs émettront
CD xx
avec xx allant de 0x34-0x3B à la place des instructions à virgule flottante x87. CommeCD
c'est l'opcode pour l'int
instruction, il sautera dans l'interruption 34h-3Bh et émulera cette instruction dans le logiciel si le coprocesseur x87 n'est pas disponible. Sinon, le gestionnaire d'interruption remplacera ces 2 octets par9B Dx
sorte que les exécutions ultérieures seront gérées directement par x87 sans émulation.Quel est le protocole pour l'émulation en virgule flottante x87 dans MS-DOS?
la source
Le noyau Linux a des modules de noyau chargeables qui font exactement cela.
Emacs a également cette capacité et je l'utilise tout le temps.
Tout ce qui prend en charge une architecture de plug-in dynamique modifie essentiellement son code au moment de l'exécution.
la source
J'exécute des analyses statistiques sur une base de données constamment mise à jour. Mon modèle statistique est écrit et réécrit chaque fois que le code est exécuté pour accueillir les nouvelles données qui deviennent disponibles.
la source
Le scénario dans lequel cela peut être utilisé est un programme d'apprentissage. En réponse à l'entrée de l'utilisateur, le programme apprend un nouvel algorithme:
Il y a une question comment faire cela en Java: Quelles sont les possibilités d'auto-modification du code Java?
la source
La meilleure version de ceci peut être les macros Lisp. Contrairement aux macros C qui ne sont qu'un préprocesseur Lisp vous permet d'avoir accès à tout le langage de programmation à tout moment. Il s'agit de la fonctionnalité la plus puissante de lisp et n'existe dans aucune autre langue.
Je ne suis en aucun cas un expert, mais faites en parler l'un des gars lisp! Il y a une raison pour laquelle ils disent que Lisp est le langage le plus puissant et les gens intelligents ne disent pas qu'ils ont probablement raison.
la source