Ofuscado C Código De Concurso De 2006. Por favor, explique sykes2.c


¿Cómo funciona este programa C?

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

Se compila tal como es (probado en gcc 4.6.3). Imprime la hora en que se compila. En mi sistema:

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

Fuente: sykes2-Un reloj en una línea, sykes2 consejos de autor

Algunas sugerencias: No hay advertencias de compilación por defecto. Compilado con -Wall, se emiten las siguientes advertencias:

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]
Author: iCodez, 2013-03-13

4 answers

Vamos a des-ofuscarlo.

Sangría:

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

Introduciendo variables para desenredar este lío:

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
    }
}

Tenga en cuenta que -~i == i+1 debido a doses-complemento. Por lo tanto, tenemos

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));
    }
}

Ahora, tenga en cuenta que a[b] es lo mismo que b[a], y aplicar de nuevo el cambio -~ == 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));
    }
}

Convirtiendo la recursión en un bucle y simplificando un poco más:

// 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));
        }
    }
}

Esto produce un carácter por iteración. Cada personaje 64, genera una nueva línea. De lo contrario, utiliza un par de tablas de datos para averiguar qué generar, y coloca el carácter 32 (un espacio) o el carácter 33 (a !). La primera tabla (">'txiZ^(~z?") es un conjunto de 10 mapas de bits que describen la apariencia de cada carácter, y la segunda tabla (";;;====~$::199") selecciona el bit apropiado para mostrar del mapa de bits.

La segunda tabla

Comencemos examinando la segunda tabla, int shift = ";;;====~$::199"[(i*2&8) | (i/64)];. i/64 es el número de línea (6 a 0) y i*2&8 es 8 iff i es 4, 5, 6 o 7 mod 8.

if((i & 2) == 0) shift /= 8; shift = shift % 8 selecciona el dígito octal alto (para i%8 = 0,1,4,5) o el dígito octal bajo (para i%8 = 2,3,6,7) del valor de la tabla. La tabla shift termina así:

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

O en forma tabular

00005577
11775577
11775577
11665577
22773377
22773377
44443377

Tenga en cuenta que el autor utilizó el terminador nulo para las dos primeras entradas de la tabla (sneaky!).

Esto está diseñado después de una pantalla de siete segmentos, con 7 s como espacios en blanco. Por lo tanto, las entradas en la primera tabla deben definir la segmentos que se iluminan.

La primera tabla

__TIME__ es una macro especial definida por el preprocesador. Se expande a una constante de cadena que contiene la hora en la que se ejecutó el preprocesador, en la forma "HH:MM:SS". Observe que contiene exactamente 8 caracteres. Tenga en cuenta que 0-9 tiene valores ASCII 48 a 57 y : tiene valor ASCII 58. La salida es de 64 caracteres por línea, lo que deja 8 caracteres por carácter de __TIME__.

7 - i/8%8 es así el índice de __TIME__ que se está produciendo actualmente (el 7- es necesario porque estamos iterando i hacia abajo). Entonces, t es el carácter de __TIME__ siendo salida.

a termina igualando lo siguiente en binario, dependiendo de la entrada t:

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

Cada número es un mapa de bits que describe los segmentos que se iluminan en nuestra pantalla de siete segmentos. Dado que los caracteres son todos ASCII de 7 bits, el bit alto siempre se borra. Por lo tanto, 7 en la tabla de segmentos siempre se imprime como un espacio en blanco. La segunda tabla se ve así con las 7s como espacios en blanco:

000055  
11  55  
11  55  
116655  
22  33  
22  33  
444433  

Así, por ejemplo, 4 es 01101010 (conjunto de bits 1, 3, 5 y 6), que se imprime como

----!!--
!!--!!--
!!--!!--
!!!!!!--
----!!--
----!!--
----!!--

Para mostrar que realmente entendemos el código, ajustemos un poco la salida con esta tabla:

  00  
11  55
11  55
  66  
22  33
22  33
  44

Esto está codificado como "?;;?==? '::799\x07". Para fines artísticos, agregaremos 64 a algunos de los caracteres (ya que solo se usan los 6 bits bajos, esto no afectará la salida); esto da "?{{?}}?gg::799G" (tenga en cuenta que el 8º el carácter no se usa, por lo que en realidad podemos hacer lo que queramos). Poniendo nuestra nueva tabla en el código 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);}

Tenemos{[47]]}

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

Tal como esperábamos. No es tan sólido como el original, lo que explica por qué el autor eligió usar la tabla que hizo.

 1772
Author: nneonneo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:50

Formateemos esto para una lectura más fácil:

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

Entonces, ejecutándolo sin argumentos, _ (argc convencionalmente) es 1. main() se llamará recursivamente a sí mismo, pasando el resultado de -(~_) (negativo bit a bit NO de _), por lo que realmente irá 448 recursiones (Solo condición donde _^448 == 0).

Tomando eso, imprimirá 7 líneas anchas de 64 caracteres (la condición ternaria exterior, y 448/64 == 7). Así que vamos a reescribirlo un poco más limpio:

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');
}

Ahora, {[10] } es decimal para ASCII espacio. O imprime un espacio o un '!'(33 es'!", de ahí el "&1 " al final). Vamos a centrarnos en la mancha en el medio:

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

Como dijo otro poster, __TIME__ es el tiempo de compilación para el programa, y es una cadena, por lo que hay algo de aritmética de cadenas, además de aprovechar que un subíndice de matriz es bidireccional: a[b] es lo mismo que b[a] para matrices de caracteres.

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

Esto seleccionará uno de los primeros 8 caracteres en __TIME__. Esto se indexa en [">'txiZ^(~z?"-48] (0-9 caracteres son 48-57 decimales). Los caracteres de esta cadena deben haber sido elegidos por sus valores ASCII. Este mismo carácter de manipulación de código ASCII continúa a través de la expresión, para dar lugar a la impresión de un '' o '! dependiendo de la ubicación dentro del glifo del personaje.

 98
Author: chmeee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-13 21:11:25

Sumando a las otras soluciones, -~x es igual a x+1 porque ~x es equivalente a (0xffffffff-x). Esto es igual a (-1-x) en el complemento 2s, por lo que -~x es -(-1-x) = x+1.

 46
Author: Thomas Song,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-21 21:44:31

I de-ofuscado el modulo aritmética de tanto como pude y me quita el reccursion

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');
}

Expandiéndolo un poco más:

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
Author: Lefteris E,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-21 16:34:56