Un programme C peut-il modifier son fichier exécutable?
j'ai eu un peu trop de temps sur mes mains et j'ai commencé à me demander si je pouvais écrire un programme d'auto-modification. À cette fin, j'ai écrit un "Hello World" en C, puis j'ai utilisé un éditeur hexadécimal pour trouver l'emplacement de la chaîne "Hello World" dans l'exécutable compilé. Est-il possible de modifier ce programme pour s'ouvrir et écraser la chaîne "Hello World"?
char* str = "Hello Worldn";
int main(int argc, char* argv) {
printf(str);
FILE * file = fopen(argv, "r+");
fseek(file, 0x1000, SEEK_SET);
fputs("Goodbyewrldn", file);
fclose(file);
return 0;
}
cela ne fonctionne pas, je suppose qu'il y a quelque chose qui l'empêche de s'ouvrir puisque je peux diviser ceci en deux programmes séparés (un" Hello World " et quelque chose pour le modifier) et il fonctionne très bien.
EDIT: ma compréhension est que lorsque le programme est lancé, il est chargé complètement en ram. Donc l'exécutable sur le disque dur est, à toutes fins pratiques, une copie. Pourquoi serait-ce un problème pour elle de se modifier?
Est-il une solution?
Merci
9 réponses
sous Windows, lorsqu'un programme est exécuté en entier *.exe
le fichier est mappé dans la mémoire en utilisant le memory-mapped-fonctions de fichier dans Windows. Cela signifie que le fichier n'est pas nécessairement chargé en une seule fois, mais que les pages du fichier sont chargées à la demande au fur et à mesure qu'elles sont consultées.
lorsque le fichier est mappé de cette façon, une autre application (y compris elle-même) ne peut pas écrire dans le même fichier pour le modifier pendant qu'il est en cours d'exécution. (Aussi, sur le fonctionnement de Windows exécutable ne peut pas être renommé non plus, mais il peut sur Linux et D'autres systèmes Unix avec des systèmes de fichiers basés sur inode).
il est possible de changer les bits mappés en mémoire, mais si vous le faites, le système d'exploitation le fait en utilisant la sémantique "copier-écrire", ce qui signifie que le fichier sous-jacent n'est pas modifié sur le disque, mais une copie de la(Des) page (s) en mémoire est faite avec vos modifications. Avant d'être autorisé à faire cela cependant, vous avez généralement à jouer avec des bits de protection sur la mémoire en question (par exemple VirtualProtect
).
à une certaine époque, il était courant pour les programmes d'assemblage de bas niveau qui se trouvaient dans des environnements de mémoire très limités d'utiliser du code d'auto-modification. Cependant, plus personne ne fait cela parce que nous ne fonctionnons pas dans les mêmes environnements limités, et les processeurs modernes ont de longs pipelines qui sont très perturbés si vous commencez à changer le code de dessous eux.
si vous utilisez Windows, vous pouvez faire ce qui suit:
exemple étape par étape:
- Appel
VirtualProtect()
sur les pages de code que vous souhaitez modifier, avec l'PAGE_WRITECOPY
protection. - Modifier les pages de code.
- Appel
VirtualProtect()
sur les pages de code modifiées, avec lePAGE_EXECUTE
protection. - Appel
FlushInstructionCache()
.
Pour plus d'informations, voir comment modifier le Code exécutable en mémoire (Archivée: Août. 2010)
il est très dépendant du système d'exploitation. Certains systèmes d'exploitation verrouiller le fichier, de sorte que vous pouvez essayer de tricher en faisant une nouvelle copie de quelque part, mais vous êtes juste un autre compy du programme.
D'autres systèmes d'exploitation font des contrôles de sécurité sur le fichier, par exemple iPhone, donc écrire sera beaucoup de travail, plus il réside comme un fichier en lecture seule.
Avec d'autres systèmes, vous pourriez même ne pas savoir où se trouve le fichier.
toutes les réponses actuelles tournent plus ou moins autour du fait qu'aujourd'hui vous ne pouvez plus facilement faire du code machine auto-modificateur. Je suis d'accord que c'est essentiellement vrai pour les Ordinateurs d'aujourd'hui.
cependant, si vous voulez vraiment voir votre propre code modificateur en action, vous avez quelques possibilités disponibles:
essayez les microcontrôleurs, les plus simples n'ont pas le pipelinage avancé. Le choix le moins cher et le plus rapide que j'ai trouvé est un MSP430 Clé USB
si une émulation est ok pour vous, vous pouvez lancer un émulateur pour une ancienne plate-forme non pipelinée.
si vous vouliez du code autodestructeur juste pour le plaisir, vous pouvez vous amuser encore plus avec du code autodestructeur (plus exactement ennemi-destructeur) à Corewars.
si vous êtes prêt à vous déplacer de C pour dire un dialecte Lisp, le code qui écrit du code est très naturel là-bas. Je voudrais suggérez Programme qui est intentionnellement petit.
si nous parlons de faire cela dans un environnement x86, cela ne devrait pas être impossible. Il doit être utilisé avec prudence cependant parce que les instructions x86 sont de longueur variable. Une instruction longue peut remplacer les instructions suivantes et une instruction plus courte laissera des données résiduelles de l'instruction écrasée qui devrait être dopée (instruction NOP).
lorsque le x86 est d'abord devenu protégé, les manuels de référence intel ont recommandé la méthode suivante pour déboguer l'accès à XO (exécuter uniquement):
- créer un nouveau, vide sélecteur ("grande" partie de beaucoup de pointeurs)
- définissez ses attributs à celui de la zone XO
- les propriétés d'accès du nouveau sélecteur doivent être des données RO si vous voulez seulement regarder ce qu'il y a dedans
- si vous voulez modifier les données, les propriétés d'accès doivent être définies à RW DATA
Donc la réponse au problème est dans la dernière étape. La RW est nécessaire si vous voulez être en mesure d'insérer l'instruction breakpoint qui est ce que font les débogueurs. Les processeurs plus modernes que le 80286 ont des registres de débogage internes pour permettre une fonctionnalité de contrôle non intrusive qui pourrait entraîner l'émission d'un point de rupture.
Windows a mis à disposition les blocs de construction pour ce faire à partir de Win16. Ils sont probablement toujours en place. Je pense que Microsoft appelle cette classe de manipulation de pointeur " thunking."
j'ai déjà écrit une base de données très rapide de 16 bits moteur En PL / M-86 Pour DOS. Quand Windows 3.1 est arrivé (tournant sur 80386s) Je l'ai porté dans L'environnement Win16. Je voulais utiliser la mémoire 32 bits disponible mais il n'y avait pas de PL/M-32 disponible (ou Win32 d'ailleurs).
pour résoudre le problème mon programme a utilisé le thunking de la manière suivante
- pointeurs de distance 32 bits définis (sel_16:offs_32) en utilisant des structures
- a alloué des zones de données de 32 bits (<=>>taille 64KB) en utilisant la mémoire globale et les a reçues en Pointeur de distance de 16 bits (sel_16:offs_16) format
- a rempli les données dans les structures en copiant le sélecteur, puis en calculant l'offset en utilisant une multiplication de 16 bits avec des résultats de 32 bits.
- charge le pointeur/structure dans es: ebx en utilisant le préfixe d'annulation de la taille de l'instruction
- accédé aux données en utilisant une combinaison de la taille de l'instruction et des préfixes de la taille de l'opérande
une fois le mécanisme libre de tout bug, il fonctionnait sans problème. Le plus grand les zones de mémoire que mon programme a utilisées étaient 2304*2304 double précision qui revient à environ 40 Mo. Même aujourd'hui, je dirais que c'est un "gros" bloc de mémoire. En 1995, il était de 30% d'un SDRAM typique bâton (128 MB PC100).
Il existe des moyens non-portatifs pour le faire sur de nombreuses plateformes. Dans Windows, vous pouvez le faire avec WriteProcessMemory()
, par exemple. Cependant, en 2010, il est généralement une très mauvaise idée de faire cela. Ce N'est pas le temps du DOS où vous codez en assemblée et faites ceci pour économiser de l'espace. C'est très difficile d'avoir raison, et vous demandez essentiellement des problèmes de stabilité et de sécurité. A moins que vous ne fassiez quelque chose de très bas niveau comme un débogueur, je dirais ne vous embêtez pas avec cela, les problèmes que vous allez introduire ne sont pas vaut quel que soit le gain que vous pourriez avoir.
le code d'auto-modification est utilisé pour les modifications en mémoire, pas dans le fichier (comme déballer les paquets de temps d'exécution comme UPX le fait). De plus, la représentation de fichier d'un programme est plus difficile à utiliser en raison des adresses virtuelles relatives, des réinstallations possibles et des modifications aux en-têtes nécessaires pour la plupart des mises à jour (p. ex. en changeant l' Hello world!
longer Hello World
vous aurez besoin d'étendre le segment de données dans le fichier).
je suggère que vous appreniez d'abord à le faire en mémoire. Pour les mises à jour de fichiers les plus simples et une approche plus générique serait d'exécuter une copie du programme afin qu'il modifie l'original.
EDIT: Et n'oubliez pas les principales raisons pour lesquelles le self-modifying code est:
1) Obfuscation, de sorte que le code qui est réellement exécuté n'est pas le code que vous verrez avec une simple analyse statique du fichier.
aucun d'eux ne profite de la modification de l'exécutable.
si vous travaillez sur Windows, je crois qu'il verrouille le fichier pour l'empêcher d'être modifié pendant son exécution. C'est pourquoi vous devez souvent quitter un programme pour installer une mise à jour. Ce n'est pas vrai sur un système linux.
sur les nouvelles versions de Windows CE (au moins 5.x un nouveau) où les applications tournent dans l'espace de l'utilisateur (par rapport aux versions précédentes où toutes les applications tournent en mode superviseur), les applications ne peuvent même pas lire son propre fichier exécutable.