web-dev-qa-db-fra.com

Obfuscated C Code Contest 2006. Veuillez expliquer sykes2.c

Comment fonctionne ce programme C?

main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}

Il compile tel quel (testé sur gcc 4.6.3). Il imprime le temps une fois compilé. Sur mon système:

    !!  !!!!!!              !!  !!!!!!              !!  !!!!!! 
    !!  !!  !!              !!      !!              !!  !!  !! 
    !!  !!  !!              !!      !!              !!  !!  !! 
    !!  !!!!!!    !!        !!      !!    !!        !!  !!!!!! 
    !!      !!              !!      !!              !!  !!  !! 
    !!      !!              !!      !!              !!  !!  !! 
    !!  !!!!!!              !!      !!              !!  !!!!!!

Source: sykes2 - une horloge sur une ligne , conseils de l'auteur sykes2

Quelques astuces: Pas de compilation des avertissements par défaut. Compilé avec -Wall, les avertissements suivants sont émis:

sykes2.c:1:1: warning: return type defaults to ‘int’ [-Wreturn-type]
sykes2.c: In function ‘main’:
sykes2.c:1:14: warning: value computed is not used [-Wunused-value]
sykes2.c:1:1: warning: implicit declaration of function ‘putchar’ [-Wimplicit-function-declaration]
sykes2.c:1:1: warning: suggest parentheses around arithmetic in operand of ‘|’ [-Wparentheses]
sykes2.c:1:1: warning: suggest parentheses around arithmetic in operand of ‘|’ [-Wparentheses]
sykes2.c:1:1: warning: control reaches end of non-void function [-Wreturn-type]
951
corny

Dé-obscurcissons-le.

Mise en retrait:

_main(_) {
    _^448 && main(-~_);
    putchar(--_%64
        ? 32 | -~7[__TIME__-_/8%8][">'txiZ^(~z?"-48] >> ";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1
        : 10);
}
_

Introduire des variables pour démêler ce gâchis:

_main(int i) {
    if(i^448)
        main(-~i);
    if(--i % 64) {
        char a = -~7[__TIME__-i/8%8][">'txiZ^(~z?"-48];
        char b = a >> ";;;====~$::199"[i*2&8|i/64]/(i&2?1:8)%8;
        putchar(32 | (b & 1));
    } else {
        putchar(10); // newline
    }
}
_

Notez que _-~i == i+1_ en raison de complément à deux. Par conséquent, nous avons

_main(int i) {
    if(i != 448)
        main(i+1);
    i--;
    if(i % 64 == 0) {
        putchar('\n');
    } else {
        char a = -~7[__TIME__-i/8%8][">'txiZ^(~z?"-48];
        char b = a >> ";;;====~$::199"[i*2&8|i/64]/(i&2?1:8)%8;
        putchar(32 | (b & 1));
    }
}
_

Notez maintenant que a[b]_ EST IDENTIQUE À _b[a] et appliquez à nouveau le changement _-~ == 1+_:

_main(int i) {
    if(i != 448)
        main(i+1);
    i--;
    if(i % 64 == 0) {
        putchar('\n');
    } else {
        char a = (">'txiZ^(~z?"-48)[(__TIME__-i/8%8)[7]] + 1;
        char b = a >> ";;;====~$::199"[(i*2&8)|i/64]/(i&2?1:8)%8;
        putchar(32 | (b & 1));
    }
}
_

Convertir la récursivité en boucle et simplifier un peu plus:

_// please don't pass any command-line arguments
main() {
    int i;
    for(i=447; i>=0; i--) {
        if(i % 64 == 0) {
            putchar('\n');
        } else {
            char t = __TIME__[7 - i/8%8];
            char a = ">'txiZ^(~z?"[t - 48] + 1;
            int shift = ";;;====~$::199"[(i*2&8) | (i/64)];
            if((i & 2) == 0)
                shift /= 8;
            shift = shift % 8;
            char b = a >> shift;
            putchar(32 | (b & 1));
        }
    }
}
_

Cela génère un caractère par itération. Chaque 64ème caractère, il génère une nouvelle ligne. Sinon, il utilise une paire de tables de données pour déterminer le contenu à afficher et met le caractère 32 (un espace) ou le caractère 33 (un _!_). La première table (_">'txiZ^(~z?"_) est un ensemble de 10 bitmaps décrivant l'apparence de chaque caractère, et la seconde table (_";;;====~$::199"_) sélectionne le bit approprié à afficher à partir de la bitmap.

La deuxième table

Commençons par examiner le second tableau, int shift = ";;;====~$::199"[(i*2&8) | (i/64)];. _i/64_ est le numéro de ligne (6 à 0) et _i*2&8_ est 8 iff i est 4, 5, 6 ou 7 mod 8.

if((i & 2) == 0) shift /= 8; shift = shift % 8 sélectionne le chiffre octal supérieur (pour _i%8_ = 0,1,4,5) ou le chiffre octal inférieur (pour _i%8_ = 2,3,6,7) du valeur de la table. La table de décalage finit par ressembler à ceci:

_row col val
6   6-7 0
6   4-5 0
6   2-3 5
6   0-1 7
5   6-7 1
5   4-5 7
5   2-3 5
5   0-1 7
4   6-7 1
4   4-5 7
4   2-3 5
4   0-1 7
3   6-7 1
3   4-5 6
3   2-3 5
3   0-1 7
2   6-7 2
2   4-5 7
2   2-3 3
2   0-1 7
1   6-7 2
1   4-5 7
1   2-3 3
1   0-1 7
0   6-7 4
0   4-5 4
0   2-3 3
0   0-1 7
_

ou sous forme de tableau

_00005577
11775577
11775577
11665577
22773377
22773377
44443377
_

Notez que l'auteur a utilisé le terminateur null pour les deux premières entrées de la table (sournois!).

Ceci est conçu après un affichage à sept segments, avec _7_ s en tant que blancs. Les entrées du premier tableau doivent donc définir les segments qui s’allument.

La première table

__TIME__ est une macro spéciale définie par le préprocesseur. Il se développe en une constante de chaîne contenant l'heure à laquelle le préprocesseur a été exécuté, sous la forme _"HH:MM:SS"_. Observez qu'il contient exactement 8 caractères. Notez que 0 à 9 ont ASCII valeurs 48 à 57 et _:_ a ASCII valeur 58. La sortie contient 64 caractères par ligne, ce qui laisse 8 caractères par caractère de ___TIME___.

_7 - i/8%8_ est donc l'index de ___TIME___ en cours de sortie (le _7-_ est nécessaire car nous effectuons une itération de i vers le bas). Donc, t est le caractère de ___TIME___ en sortie.

a finit par égaler le suivant en binaire, en fonction de l'entrée t:

_0 00111111
1 00101000
2 01110101
3 01111001
4 01101010
5 01011011
6 01011111
7 00101001
8 01111111
9 01111011
: 01000000
_

Chaque numéro est un bitmap décrivant les segments allumés dans notre affichage à sept segments. Les caractères étant tous des caractères ASCII 7 bits, le bit haut est toujours effacé. Ainsi, _7_ dans la table de segments s'imprime toujours comme un blanc. La deuxième table ressemble à ceci avec le _7_ s en blanc:

_000055  
11  55  
11  55  
116655  
22  33  
22  33  
444433  
_

Ainsi, par exemple, _4_ est _01101010_ (bits 1, 3, 5 et 6 définis), qui affiche

_----!!--
!!--!!--
!!--!!--
!!!!!!--
----!!--
----!!--
----!!--
_

Pour montrer que nous comprenons vraiment le code, ajustons un peu la sortie avec ce tableau:

_  00  
11  55
11  55
  66  
22  33
22  33
  44
_

Ceci est codé en tant que _"?;;?==? '::799\x07"_. Pour des raisons artistiques, nous ajouterons 64 à quelques caractères (puisque seuls les 6 bits les plus faibles sont utilisés, cela n’affectera pas la sortie); cela donne _"?{{?}}?gg::799G"_ (notez que le 8ème caractère est inutilisé, nous pouvons donc le créer comme nous le voulons). Mettre notre nouvelle table dans le code original:

_main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>"?{{?}}?gg::799G"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}
_

on a

_          !!              !!                              !!   
    !!  !!              !!  !!  !!  !!              !!  !!  !! 
    !!  !!              !!  !!  !!  !!              !!  !!  !! 
          !!      !!              !!      !!                   
    !!  !!  !!          !!  !!      !!              !!  !!  !! 
    !!  !!  !!          !!  !!      !!              !!  !!  !! 
          !!              !!                              !!   
_

comme nous nous y attendions. Ce n'est pas aussi solide que l'original, ce qui explique pourquoi l'auteur a choisi d'utiliser le tableau qu'il a utilisé.

1796
nneonneo

Formons ceci pour en faciliter la lecture:

main(_){
  _^448&&main(-~_);
  putchar((--_%64) ? (32|-(~7[__TIME__-_/8%8])[">'txiZ^(~z?"-48]>>(";;;====~$::199")[_*2&8|_/64]/(_&2?1:8)%8&1):10);
}

Donc, le lancer sans argument, _ (argc de manière conventionnelle) est 1. main() s'appellera de manière récursive en transmettant le résultat de -(~_) (PAS au niveau du bit négatif de _), si bien qu'il y aura 448 récursions (condition unique où _^448 == 0).

En prenant cela, cela affichera 7 lignes larges de 64 caractères (la condition ternaire extérieure et 448/64 == 7). Alors réécrivons-le un peu plus propre:

main(int argc) {
  if (argc^448) main(-(~argc));
  if (argc % 64) {
    putchar((32|-(~7[__TIME__-argc/8%8])[">'txiZ^(~z?"-48]>>(";;;====~$::199")[argc*2&8|argc/64]/(argc&2?1:8)%8&1));
  } else putchar('\n');
}

Maintenant, 32 est un nombre décimal pour ASCII espace. Il imprime un espace ou un '!' (33 est '!', D'où le '&1' à ​​la fin). Concentrons-nous sur la goutte au milieu:

-(~(7[__TIME__-argc/8%8][">'txiZ^(~z?"-48]) >>
     (";;;====~$::199"[argc*2&8|argc/64]) / (argc&2?1:8) % 8

Comme l'a dit une autre affiche, __TIME__ est le moment de la compilation du programme. Il s'agit d'une chaîne. Il y a donc une arithmétique de chaîne en cours, ainsi que l'avantage d'un indice de tableau bidirectionnel: a [b] est identique comme b [a] pour les tableaux de caractères.

7[__TIME__ - (argc/8)%8]

Ceci sélectionnera l'un des 8 premiers caractères de __TIME__. Ceci est ensuite indexé dans [">'txiZ^(~z?"-48] (0-9 caractères sont 48-57 décimaux). Les caractères de cette chaîne doivent avoir été choisis pour leurs valeurs ASCII. Cette même manipulation de code ASCII _ du caractère se poursuit tout au long de l’expression, afin d’imprimer un '' ou un '!' en fonction de l'emplacement dans le glyphe du personnage.

98
chmeee

En ajoutant aux autres solutions, -~x est égal à x+1 car ~x est équivalent à (0xffffffff-x). Ceci est égal à (-1-x) en complément à 2, donc -~x est -(-1-x) = x+1.

47
Thomas Song

J'ai dés-obscurci autant que possible l'arithmétique modulo et enlevé la récursion

int pixelX, line, digit ;
for(line=6; line >= 0; line--){
  for (digit =0; digit<8; digit++){
    for(pixelX=7;pixelX > 0; pixelX--){ 
        putchar(' '| 1 + ">'txiZ^(~z?"["12:34:56"[digit]-'0'] >> 
          (";;;====~$::199"[pixel*2 & 8  | line] / (pixelX&2 ? 1 : 8) ) % 8 & 1);               
    }
  }
  putchar('\n');
}

Développer un peu plus:

int pixelX, line, digit, shift;
char shiftChar;
for(line=6; line >= 0; line--){
    for (digit =0; digit<8; digit++){
        for(pixelX=7;pixelX >= 0; pixelX--){ 
            shiftChar = ";;;====~$::199"[pixelX*2 & 8 | line];
            if (pixelX & 2)
                shift = shiftChar & 7;
            else
                shift = shiftChar >> 3;     
            putchar(' '| (">'txiZ^(~z?"["12:34:56"[digit]-'0'] + 1) >> shift & 1 );
        }

    }
    putchar('\n');
}
3
Lefteris E