winner obfuscated code c obfuscation

obfuscated - c obfuscation winner



Entrevista Hola explicación mundial (5)

Esta entrada clásica de ioccc es un programa Hello World escrito en C. ¿Alguien puede proporcionar una explicación de cómo funciona?

Código original (resaltado de sintaxis que falta intencionalmente):

int i;main(){for(;i["]<i;++i){--i;}"];read(''-''-''-'',i+++"hell/ o, world!/n",''/''/''/''));}read(j,i,p){write(j/p+p,i---j,i/i);}

Ligeramente más limpio:

int i; main() { for ( ; i["]<i;++i){--i;}"]; read(''-'' - ''-'', i++ + "hello, world!/n", ''/'' / ''/'')); } read(j, i, p) { write(j / p + p, i-- - j, i / i); }


para condición de bucle

i["]<i;++i){--i;}"]

Esta expresión aprovecha el hecho de que la indexación de matrices es conmutativa en C. Es equivalente a.

"]<i;++i){--i;}"[i]

Por lo tanto, el bucle terminará cuando el carácter en la posición i sea /0 , es decir, al final de la cadena, que tiene una longitud de 14 caracteres (que es la misma longitud que "hola, mundo! / N"). Por lo tanto, la condición de bucle for se puede reescribir como:

i != 14

aritmética de caracteres

read(''-'' - ''-'', i++ + "hello, world!/n", ''/'' / ''/'')

char es un tipo entero, y por lo tanto:

  • ''-'' - ''-'' es 0
  • ''/'' / ''/'' es 1

    lee (0, i ++ + "hola, mundo! / n", 1)

Después de corregir todas las advertencias del compilador (como int implícito a la conversión del puntero) y simplificar las cosas mencionadas anteriormente, el código se convierte en:

#include <unistd.h> int i = 0; void read2(int, char*, int); int main() { while (i != 14) { read2(0, i++ + "hello, world!/n", 1); } return 0; } void read2(int j, char* i, int p) { write(j / p + p, i-- - j, 1); }

( read2 nombre de read a read2 para evitar conflictos con la función de read Unix).

Tenga en cuenta que los argumentos j y p para read2 son innecesarios, ya que la función siempre se llama con j = 0 y p = 1.

#include <unistd.h> int i = 0; void read2(char*); int main() { while (i != 14) { read2(i++ + "hello, world!/n"); } return 0; } void read2(char* i) { write(1, i--, 1); }

La write(1, i--, 1) llamada write(1, i--, 1) escribe 1 carácter de i en el descriptor de archivo 1 (stdout). Y el dato posterior es superfluo porque esta i es una variable local a la que nunca se hace referencia nuevamente. Entonces esta función es equivalente a putchar(*i) .

Alinear la función read2 dentro del bucle principal da

#include <stdio.h> int i = 0; int main() { while (i != 14) { putchar(*(i++ + "hello, world!/n")); } return 0; }

Para lo cual el significado es obvio.


El índice de cadena de i ["..."] hace que el bucle se ejecute para la longitud de la cadena. i ++ + la cadena hola mundo significa que cada iteración pasa la cadena que comienza una letra más a la función de lectura local.

primera iteración = "hola ..." segunda iteración = "ello .."

El primer parámetro de la función de lectura se simplifica a 0, el descriptor de archivo stdout. El último parámetro se simplifica a 1, por lo que solo se escribe un carácter por llamada. Esto significa que cada iteración escribirá el primer carácter de la cadena que se pasa a la salida estándar. Así que siguiendo las cadenas de ejemplo de arriba:

primera iteración = "h" segunda iteración = "e"

El índice de cadena en el bucle for tiene la misma longitud que la cadena hello world, y como [] es conmutativo, y las cadenas terminan en nulo, la última iteración devolverá el carácter nulo y saldrá del bucle.


Lejos de C experto, pero lo intentaré:

i["]<i;++i){--i;}"] // is actually char* x = "]<i;++i){--i;}" *(i + x) // and will turn to zero when i == 14 (will point to string''s ending zero) // ''-'' - ''-'' and ''/'' / ''/'' are always 0 and 1 // int i is initiated to zero // i++ will return original value, so main turns to main() { char* hello = "hello, world!/n"; for (i = 0 ; i != 14; i++) read(0, hello[i], 1); } // read becomes // i-- does nothing here, i is in read''s scope, not the global one read(j, i, p) { write(1, i, 1); } // and at last main() { char* hello = "hello, world!/n"; for (i = 0 ; i<14; i++) write(1, hello[i], 1); }


No estoy pensando en desarmar esto completamente, pero hay algunos consejos:

  • ''-'' - ''-'' está ofuscado para 0
  • ''/'' / ''/'' y el i / i en read () están ofuscados por 1

Recuerde que [] es conmutativo, es decir, i["]<i;++i){--i;}"] es lo mismo que "]<i;++i){--i;}"[i] (tener una matriz de caracteres y apuntar al elemento i''th). El contenido de la cadena no importa de ninguna manera, solo su longitud se utiliza para definir el número de iteraciones del bucle. Cualquier otra cadena de la misma longitud (incidencialmente, la misma longitud que la salida ...) funcionaría. Después de ese número de iteraciones, "cadena" [i] devuelve el carácter nulo que termina la cadena. Cero == falso, el bucle termina.

Con eso, debería ser comparativamente fácil averiguar el resto.

Edit: los votos positivos me interesaron lo suficiente como para ver esto un poco más.

Por supuesto, i++ + "Hello, world!/n" es lo mismo que "Hello, world!/n"[ i++ ] .

Codelark ya señaló que 0 es el fid para la salida estándar. (Dale +1 por eso, no a mí. Solo mencionándolo para completar).

El i en read() es, por supuesto, local, es decir, el i-- en read() no afecta al i en main() . Como la siguiente i / i es siempre 1 todos modos, el operador -- no hace nada en absoluto.

Ah, y dile al entrevistador que despida a quien haya escrito este código. No realiza comprobación de errores en el valor de retorno de write() . Puedo vivir con un código notoriamente mal escrito (y tengo, durante muchos años), pero no verificar errores es peor que malo, eso es defectuoso . :-)

El resto son solo algunas matemáticas de tercer grado. Se lo pasaría a un interno. ;-)


Otra pregunta de la entrevista que parece reflejar más en el entrevistador que en el entrevistado. Problemas clave aquí: * j siempre es 0 ((''-'' - ''-'') == 0) * p siempre es 1 ((''/'' / ''/'') == 1) * i (en la función de lectura) es (i ++ + "hola mundo") == el i''th carácter en hola mundo (luego incrementa i)

Así, la función de lectura se convierte en

read(0, NextChar, 1) { write(1, NextChar , 1); }

El otro bit es la conmutatividad del operador [] en el bucle for.

Comprender y analizar este tipo de código no tiene valor alguno.