web-dev-qa-db-fra.com

Comment écrire du code auto-modifiant en C?

Je veux écrire un morceau de code qui se modifie continuellement, même si le changement est insignifiant. 

Par exemple, peut-être quelque chose comme

for i in 1 to  100, do 
begin
   x := 200
   for j in 200 downto 1, do
    begin
       do something
    end
end

Supposons que je veuille que mon code, après la première itération, change la ligne x := 200 en une autre ligne x := 199 et que, après la prochaine itération, la change en x := 198 et ainsi de suite. 

L'écriture d'un tel code est-elle possible? Aurais-je besoin d'utiliser inline Assembly pour cela? 

EDIT: Voici pourquoi je veux le faire en C:

Ce programme sera exécuté sur un système d'exploitation expérimental et je ne peux pas/ne sais pas utiliser les programmes compilés à partir d'autres langages. La vraie raison pour laquelle j'ai besoin d'un tel code est parce que ce code est exécuté sur un système d'exploitation invité sur une machine virtuelle. L'hyperviseur est un traducteur binaire qui traduit des fragments de code. Le traducteur effectue des optimisations. Il ne traduit qu'une fois les morceaux de code. Lors de la prochaine utilisation du même bloc dans l'invité, le traducteur utilisera le résultat précédemment traduit. Maintenant, si le code est modifié à la volée, le traducteur le remarquera et marquera sa traduction précédente comme périmée. Forçant ainsi une re-traduction du même code. C’est ce que je veux réaliser, obliger le traducteur à faire de nombreuses traductions. Généralement, ces morceaux sont des instructions entre des instructions de branchement (telles que des instructions de saut). Je pense juste que le code auto-modifiable serait un moyen fantastique pour y parvenir.

26
AnkurVj

Vous voudrez peut-être envisager d'écrire une machine virtuelle en C, où vous pourrez créer votre propre code à modification automatique.

Si vous souhaitez écrire des exécutables à modification automatique, cela dépend en grande partie du système d'exploitation que vous ciblez. Vous pouvez approcher la solution souhaitée en modifiant l’image de programme en mémoire. Pour ce faire, vous obtiendrez l'adresse en mémoire des octets de code de votre programme. Ensuite, vous pouvez manipuler la protection du système d’exploitation sur cette plage de mémoire, ce qui vous permet de modifier les octets sans rencontrer de violation d’accès ou '' 'SIG_SEGV' ''. Enfin, vous utiliseriez des pointeurs (peut-être des pointeurs '' 'unsigned char *' '', éventuellement '' 'unsigned long *' '' comme sur les machines RISC) pour modifier les codes opération du programme compilé.

Un point clé est que vous allez modifier le code machine de l’architecture cible. Il n'y a pas de format canonique pour le code C pendant son exécution - C est la spécification d'un fichier d'entrée textuel destiné à un compilateur.

12
Heath Hunnicutt

C'est possible, mais ce n'est probablement pas possible et vous devrez peut-être vous battre avec des segments de mémoire en lecture seule pour le code en cours d'exécution et d'autres obstacles mis en place par votre système d'exploitation.

9
Vatine

Désolé, je réponds un peu tard, mais je pense avoir trouvé exactement ce que vous cherchez: https://shanetully.com/2013/12/writing-a-self-mutating-x86_64-c-program/

Dans cet article, ils changent la valeur d'une constante en injectant Assembly dans la pile. Ensuite, ils exécutent un shellcode en modifiant la mémoire d’une fonction de la pile.

Ci-dessous le premier code:

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>

void foo(void);
int change_page_permissions_of_address(void *addr);

int main(void) {
    void *foo_addr = (void*)foo;

    // Change the permissions of the page that contains foo() to read, write, and execute
    // This assumes that foo() is fully contained by a single page
    if(change_page_permissions_of_address(foo_addr) == -1) {
        fprintf(stderr, "Error while changing page permissions of foo(): %s\n", strerror(errno));
        return 1;
    }

    // Call the unmodified foo()
    puts("Calling foo...");
    foo();

    // Change the immediate value in the addl instruction in foo() to 42
    unsigned char *instruction = (unsigned char*)foo_addr + 18;
    *instruction = 0x2A;

    // Call the modified foo()
    puts("Calling foo...");
    foo();

    return 0;
}

void foo(void) {
    int i=0;
    i++;
    printf("i: %d\n", i);
}

int change_page_permissions_of_address(void *addr) {
    // Move the pointer to the page boundary
    int page_size = getpagesize();
    addr -= (unsigned long)addr % page_size;

    if(mprotect(addr, page_size, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) {
        return -1;
    }

    return 0;
}
6
Labo

Ce serait un bon début. Essentiellement la fonctionnalité LISP en C:

http://nakkaya.com/2010/08/24/a-micro-manual-for-LISP-implemented-in-c/

5
CreativeJourney

Selon le degré de liberté dont vous avez besoin, vous pourrez peut-être accomplir ce que vous voulez en utilisant des pointeurs de fonction. En utilisant votre pseudo-code comme point de départ, considérons le cas où nous souhaitons modifier cette variable x de différentes manières à mesure que l'index de boucle i change. Nous pourrions faire quelque chose comme ça:

#include <stdio.h>

void multiply_x (int * x, int multiplier)
{
    *x *= multiplier;
}

void add_to_x (int * x, int increment)
{
    *x += increment;
}

int main (void)
{
    int x = 0;
    int i;

    void (*fp)(int *, int);

    for (i = 1; i < 6; ++i) {
            fp = (i % 2) ? add_to_x : multiply_x;

            fp(&x, i);

            printf("%d\n", x);
    }

    return 0;
}

Le résultat, lorsque nous compilons et exécutons le programme, est le suivant: 

1
2
5
20
25

Évidemment, cela ne fonctionnera que si vous avez un nombre fini de choses que vous voulez faire avec x à chaque passage. Afin de rendre les modifications persistantes (ce qui fait partie de ce que vous voulez de "l'auto-modification"), vous voudriez que la variable fonction-pointeur soit globale ou statique. Je ne suis pas sûr de pouvoir vraiment recommander cette approche, car il existe souvent des moyens plus simples et plus clairs d'accomplir ce genre de choses.

5
Pillsy

La suggestion de mettre en œuvre LISP en C, puis de l'utiliser, est solide, en raison de problèmes de portabilité. Mais si vous le vouliez vraiment, cela pourrait également être implémenté dans le sens opposé sur de nombreux systèmes, en chargeant le code binaire de votre programme dans la mémoire, puis en y retournant.

Vous pouvez essayer de le faire de différentes manières. Une façon est via un exploit de débordement de tampon. Une autre solution consisterait à utiliser mprotect () pour rendre la section de code accessible en écriture, puis à modifier les fonctions créées par le compilateur.

Des techniques comme celle-ci sont amusantes pour les défis de programmation et les compétitions obscurcies, mais étant donné que votre code est illisible et que vous exploitez ce que C considère comme un comportement indéfini, il vaut mieux les éviter dans les environnements de production.

3
Daniel Papasian

Un langage qui s'auto-interprète (non pas compilé et lié comme C) pourrait être meilleur pour cela. Perl, javascript, PHP ont la fonction evil eval() qui pourrait convenir à votre objectif. Grâce à cela, vous pourriez avoir une chaîne de code que vous modifiez constamment, puis exécutez via eval().

1
Jonathan M

Mon ami et moi avons rencontré ce problème alors que nous travaillions sur un jeu qui modifie lui-même son code. Nous permettons à l'utilisateur de réécrire des extraits de code dans x86 Assembly.

Cela nécessite simplement de tirer parti de deux bibliothèques - un assembleur et un désassembleur:

Assembleur FASM https://github.com/ZenLulz/Fasm.NET

Désassembleur UDIS86: https://github.com/vmt/udis86

Nous lisons les instructions à l'aide du désassembleur, nous laissons l'utilisateur les éditer, convertissons les nouvelles instructions en octets avec l'assembleur et nous les écrivons en mémoire. L'écriture nécessite l'utilisation de VirtualProtect sur Windows pour modifier les autorisations de page afin de permettre la modification du code. Sous Unix, vous devez utiliser mprotect à la place.

J'ai posté un article ici sur la façon dont nous l'avons fait:

https://medium.com/squallygame/how-we-wrote-a-self-hacking-game-in-c-d8b9f97bfa99

Ainsi que l'exemple de code ici:

https://github.com/Squalr/SelfHackingApp

Ces exemples concernent Windows utilisant C++, mais il devrait être très facile de faire du multi-plateforme et du C uniquement.

0
Zachary Canann

En C11 standard (read n1570 ), vous ne pouvez pas écrire de code à modification automatique (au moins sans comportement non défini ). Sur le plan conceptuel au moins, le segment de code est en lecture seule.

Vous pouvez envisager d'étendre le code de votre programme avec plugins en utilisant votre éditeur de liens dynamique . Cela nécessite des fonctions spécifiques au système d'exploitation. Sur POSIX, utilisez dlopen (et probablement dlsym pour obtenir les pointeurs de fonction récemment chargés). Vous pouvez ensuite écraser les pointeurs de fonction avec l'adresse des nouveaux.

Vous pourriez peut-être utiliser une bibliothèque JIT-compiler (comme libgccjit ou asmjit ) pour atteindre vos objectifs. Vous obtiendrez de nouvelles adresses de fonction et les placerez dans vos pointeurs de fonction.

Rappelez-vous qu'un compilateur C peut générer un code de différentes tailles pour un appel de fonction ou un saut donné, ainsi, même le remplacement d'une manière spécifique par une machine est fragile.

0