Ce code C obscurci prétend s’exécuter sans main (), mais que fait-il vraiment?

#include  #define decode(s,t,u,m,p,e,d) m##s##u##t #define begin decode(a,n,i,m,a,t,e) int begin() { printf("Ha HA see how it is?? "); } 

Est-ce que cela appelle indirectement main ? Comment?

Le langage C définit l’environnement d’exécution en deux catégories: autonome et hébergé . Dans les deux environnements d’exécution, une fonction est appelée par l’environnement pour le démarrage du programme.
Dans une fonction de démarrage d’un programme d’environnement autonome peut être définie par l’implémentation alors qu’en environnement hébergé , elle doit être main . Aucun programme en C ne peut s’exécuter sans la fonction de démarrage du programme dans les environnements définis.

Dans votre cas, main est caché par les définitions de préprocesseur. begin() se développera pour decode(a,n,i,m,a,t,e) qui sera ensuite étendu à main .

 int begin() -> int decode(a,n,i,m,a,t,e)() -> int m##a##i##n() -> int main() 

decode(s,t,u,m,p,e,d) est une macro paramétrée avec 7 parameters. La liste de remplacement pour cette macro est m##s##u##t . m, s, u et t sont les 4 ème , 1 er , 3 ème et 2 ème parameters utilisés dans la liste de remplacement.

 s, t, u, m, p, e, d 1 2 3 4 5 6 7 

Le repos ne sert à rien ( juste pour obscurcir ). L’argument passé au decode est ” a , n , i , m , a, t, e”, les identificateurs m, s, u et t sont remplacés respectivement par les arguments m, a, i et n .

  m --> ms --> au --> it --> n 

Essayez d’utiliser gcc -E source.c , la sortie se termine par:

 int main() { printf("Ha HA see how it is?? "); } 

Une fonction main() est donc générée par le préprocesseur.

Le programme en question appelle main() raison de l’expansion de la macro, mais votre hypothèse est erronée – elle n’a pas besoin d’appeler main() du tout!

Ssortingctement parlant, vous pouvez avoir un programme C et pouvoir le comstackr sans avoir un symbole main . main c library accéder à mainte main après avoir terminé sa propre initialisation. Habituellement, vous sautez dans main partir du symbole de la libc appelé _start . Il est toujours possible d’avoir un programme très valide, qui exécute simplement l’assemblage, sans avoir de main. Regarde ça:

 /* This must be comstackd with the flag -nostdlib because otherwise the * linker will complain about multiple definitions of the symbol _start * (one here and one in glibc) and a missing reference to symbol main * (that the libc expects to be linked against). */ void _start () { /* calling the write system call, with the arguments in this order: * 1. the stdout file descriptor * 2. the buffer we want to print (Here it's just a ssortingng literal). * 3. the amount of bytes we want to write. */ asm ("int $0x80"::"a"(4), "b"(1), "c"("Hello world!\n"), "d"(13)); asm ("int $0x80"::"a"(1), "b"(0)); /* calling exit syscall, with the argument to be 0 */ } 

Comstackz ce qui précède avec gcc -nostdlib without_main.c , et voyez-le imprimer Hello World! sur l’écran simplement en émettant des appels système (interruptions) dans l’assemblage en ligne.

Pour plus d’informations sur ce problème particulier, consultez le blog de ksplice

Un autre problème intéressant est que vous pouvez également avoir un programme qui comstack sans que le symbole main corresponde à une fonction C. Par exemple, vous pouvez avoir ce qui suit comme un programme C très valide, qui ne fait que rendre le compilateur lorsque vous montez le niveau des avertissements.

 /* These values are extracted from the decimal representation of the instructions * of a hello world program written in asm, that gdb provides. */ const int main[] = { -443987883, 440, 113408, -1922629632, 4149, 899584, 84869120, 15544, 266023168, 1818576901, 1461743468, 1684828783, -1017312735 }; 

Les valeurs du tableau sont des octets correspondant aux instructions nécessaires pour imprimer Hello World à l’écran. Pour un compte rendu plus détaillé du fonctionnement de ce programme spécifique, consultez ce billet de blog , où je le lis également en premier.

Je veux faire un dernier avis concernant ces programmes. Je ne sais pas s’ils s’inscrivent en tant que programmes C valides conformément à la spécification du langage C, mais leur compilation et leur exécution est certainement possible, même s’ils violent la spécification elle-même.

Quelqu’un essaie d’agir comme un magicien. Il pense qu’il peut nous piéger. Mais nous soaps tous que c l’exécution du programme commence par main() .

Le int begin() sera remplacé par un decode(a,n,i,m,a,t,e) par une étape de préprocesseur. Ensuite, le decode(a,n,i,m,a,t,e) sera remplacé par m ## a ## i ## n. Comme par association de position de la macro-appel, s will a une valeur de caractère a . De même, u sera remplacé par “i” et t sera remplacé par “n”. Et c’est comme ça que m##s##u##t deviendra le main

En ce qui concerne le symbole ## dans l’extension de la macro, c’est l’opérateur de prétraitement qui effectue le collage des jetons. Lorsqu’une macro est développée, les deux jetons de chaque côté de chaque opérateur “##” sont combinés en un seul jeton, qui remplace alors le “##” et les deux jetons d’origine dans l’extension de la macro.

Si vous ne me croyez pas, vous pouvez comstackr votre code avec l’ -E . Il arrête le processus de compilation après le prétraitement et vous pouvez voir le résultat du collage de jetons.

 gcc -E FILENAME.c 

decode(a,b,c,d,[...]) mélange les quatre premiers arguments et les joint pour obtenir un nouvel identifiant, dans l’ordre dacb . (Les trois arguments restants sont ignorés.) Par exemple, decode(a,n,i,m,[...]) donne l’identifiant main . Notez que c’est ce que la macro de begin est définie comme.

Par conséquent, la macro de begin est simplement définie comme main .

Dans votre exemple, main() fonction main() est réellement présente, car begin est une macro que le compilateur remplace par une macro de decode qui à son tour est remplacée par l’expression m ## s ## u ## t. En utilisant l’extension de macro ## , vous atteindrez le mot main from decode . Ceci est une trace:

 begin --> decode(a,n,i,m,a,t,e) --> m##parameter1##parameter3##parameter2 ---> main 

C’est juste un truc pour avoir main() , mais utiliser le nom main() pour la fonction de saisie du programme n’est pas nécessaire en langage de programmation C. Cela dépend de vos systèmes d’exploitation et de l’éditeur de liens comme l’un de ses outils.

Sous Windows, vous n’utilisez pas toujours main() , mais plutôt WinMain ou wWinMain , bien que vous puissiez utiliser main() , même avec la chaîne d’outils de Microsoft . Sous Linux, on peut utiliser _start .

C’est l’éditeur de liens en tant qu’outil du système d’exploitation pour définir le point d’entrée, et non le langage lui-même. Vous pouvez même définir notre propre point d’entrée, et vous pouvez créer une bibliothèque également exécutable !