¿Por qué este código da la salida C++Sucks
? ¿Cuál es el concepto detrás de esto?
#include <stdio.h>
double m[] = {7709179928849219.0, 771};
int main() {
m[1]--?m[0]*=2,main():printf((char*)m);
}
Pruébalo aquí .
c
deobfuscation
codelayer1
fuente
fuente
skcuS++C
.Respuestas:
El número
7709179928849219.0
tiene la siguiente representación binaria como 64 bitsdouble
:+
muestra la posición del signo;^
del exponente y-
de la mantisa (es decir, el valor sin exponente).Como la representación usa exponente binario y mantisa, duplicar el número incrementa el exponente en uno. Su programa lo hace con precisión 771 veces, por lo que el exponente que comenzó en 1075 (representación decimal de
10000110011
) se convierte en 1075 + 771 = 1846 al final; La representación binaria de 1846 es11100110110
. El patrón resultante se ve así:Este patrón corresponde a la cadena que ves impresa, solo al revés. Al mismo tiempo, el segundo elemento de la matriz se convierte en cero, proporcionando un terminador nulo, haciendo que la cadena sea adecuada para pasar a
printf()
.fuente
7709179928849219
valor y recuperé la representación binaria.Versión más legible:
Llama recursivamente
main()
771 veces.En el principio,
m[0] = 7709179928849219.0
que se destaca porC++Suc;C
. En cada llamada,m[0]
se duplica, para "reparar" las dos últimas letras. En la última llamada,m[0]
contiene la representación de caracteres ASCIIC++Sucks
ym[1]
solo contiene ceros, por lo que tiene un terminador nulo para laC++Sucks
cadena. Todo bajo el supuesto de quem[0]
se almacena en 8 bytes, por lo que cada carácter toma 1 byte.Sin recurrencia y
main()
llamadas ilegales se verá así:fuente
Descargo de responsabilidad: esta respuesta se publicó en la forma original de la pregunta, que solo mencionaba C ++ e incluía un encabezado de C ++. La conversión de la pregunta a C pura fue realizada por la comunidad, sin aportes del autor de la pregunta original.
Hablando formalmente, es imposible razonar sobre este programa porque está mal formado (es decir, no es legal C ++). Viola C ++ 11 [basic.start.main] p3:
Aparte de esto, se basa en el hecho de que en una computadora de consumo típica, a
double
tiene una longitud de 8 bytes y utiliza una cierta representación interna bien conocida. Los valores iniciales de la matriz se calculan de modo que cuando se realiza el "algoritmo", el valor final del primerodouble
será tal que la representación interna (8 bytes) serán los códigos ASCII de los 8 caracteresC++Sucks
. El segundo elemento en la matriz es entonces0.0
, cuyo primer byte está0
en la representación interna, lo que lo convierte en una cadena de estilo C válida. Esto se envía a la salida usandoprintf()
.Ejecutar esto en HW donde algo de lo anterior no se cumple daría como resultado un texto basura (o tal vez incluso un acceso fuera de los límites).
fuente
basic.start.main
3.6.1 / 3 con la misma redacción.main()
, o reemplazarla con una llamada API para formatear el disco duro, o lo que sea.Quizás la forma más fácil de entender el código es trabajar a la inversa. Comenzaremos con una cadena para imprimir; para equilibrar, usaremos "C ++ Rocks". Punto crucial: al igual que el original, tiene exactamente ocho caracteres de longitud. Como vamos a hacer (aproximadamente) como el original, e imprimirlo en orden inverso, comenzaremos poniéndolo en orden inverso. Para nuestro primer paso, solo veremos ese patrón de bits como a
double
e imprimiremos el resultado:Esto produce
3823728713643449.5
. Entonces, queremos manipular eso de alguna manera que no sea obvia, pero que sea fácil de revertir. Elegiré semi-arbitrariamente la multiplicación por 256, lo que nos da978874550692723072
. Ahora, solo necesitamos escribir un código ofuscado para dividirlo entre 256, luego imprimir los bytes individuales de eso en orden inverso:Ahora tenemos muchos lanzamientos, pasando argumentos a (recursivos)
main
que se ignoran por completo (pero la evaluación para obtener el incremento y la disminución son absolutamente cruciales) y, por supuesto, ese número completamente arbitrario para encubrir el hecho de que lo que estamos haciendo es realmente bastante sencilloPor supuesto, dado que el punto es la ofuscación, si lo deseamos, también podemos tomar más medidas. Solo por ejemplo, podemos aprovechar la evaluación de cortocircuito para convertir nuestra
if
declaración en una sola expresión, de modo que el cuerpo de main se vea así:Para cualquiera que no esté acostumbrado al código ofuscado (y / o al código de golf), esto comienza a parecer bastante extraño: computar y descartar la lógica
and
de algún número de punto flotante sin sentido y el valor de retornomain
, que ni siquiera devuelve un valor. Peor aún, sin darse cuenta (y pensar) cómo funciona la evaluación de cortocircuito, puede que ni siquiera sea inmediatamente obvio cómo evita la recursión infinita.Nuestro próximo paso probablemente sería separar la impresión de cada personaje de encontrar ese personaje. Podemos hacerlo fácilmente generando el carácter correcto como valor de retorno
main
e imprimiendo lo quemain
devuelve:Al menos para mí, eso parece lo suficientemente ofuscado, así que lo dejaré así.
fuente
Simplemente está creando una matriz doble (16 bytes) que, si se interpreta como una matriz de caracteres, crea los códigos ASCII para la cadena "C ++ Sucks"
Sin embargo, el código no funciona en cada sistema, se basa en algunos de los siguientes hechos no definidos:
fuente
Se imprime el siguiente código
C++Suc;C
, por lo que toda la multiplicación es solo para las dos últimas letrasfuente
Los demás han explicado la pregunta bastante a fondo, me gustaría agregar una nota de que este es un comportamiento indefinido de acuerdo con el estándar.
C ++ 11 3.6.1 / 3 Función principal
fuente
El código podría reescribirse así:
Lo que está haciendo es producir un conjunto de bytes en la
double
matrizm
que corresponde a los caracteres 'C ++ Sucks' seguido de un terminador nulo. Han ofuscado el código eligiendo un valor doble que cuando se duplica 771 veces produce, en la representación estándar, ese conjunto de bytes con el terminador nulo proporcionado por el segundo miembro de la matriz.Tenga en cuenta que este código no funcionaría bajo una representación endian diferente. Además, las llamadas
main()
no están estrictamente permitidas.fuente
f
devoluciónint
?int
la respuesta en la pregunta. Déjame arreglar eso.Primero debemos recordar que los números de doble precisión se almacenan en la memoria en formato binario de la siguiente manera:
(i) 1 bit para el signo
(ii) 11 bits para el exponente
(iii) 52 bits para la magnitud
El orden de los bits disminuye de (i) a (iii).
Primero, el número fraccionario decimal se convierte en un número binario fraccionario equivalente y luego se expresa en forma de orden de magnitud en binario.
Entonces el número 7709179928849219.0 se convierte en
Ahora, considerando los bits de magnitud 1. se descuida, ya que todo el método de orden de magnitud comenzará con 1.
Entonces la parte de magnitud se convierte en:
Ahora la potencia de 2 es 52 , debemos agregarle un número de polarización como 2 ^ (bits para el exponente -1) -1, es decir, 2 ^ (11 -1) -1 = 1023 , por lo que nuestro exponente se convierte en 52 + 1023 = 1075
Ahora nuestro código multiplica el número con 2 , 771 veces, lo que hace que el exponente aumente en 771
Entonces nuestro exponente es (1075 + 771) = 1846 cuyo equivalente binario es (11100110110)
Ahora nuestro número es positivo, por lo que nuestro bit de signo es 0 .
Entonces nuestro número modificado se convierte en:
bit de signo + exponente + magnitud (concatenación simple de los bits)
como m se convierte en puntero de caracteres, dividiremos el patrón de bits en fragmentos de 8 del LSD
(cuyo equivalente hexadecimal es :)
Cuál del mapa de caracteres como se muestra es:
Ahora, una vez hecho esto, m [1] es 0, lo que significa un carácter NULO
Ahora, suponiendo que ejecute este programa en una máquina little-endian (el bit de orden inferior se almacena en la dirección inferior), de modo que el puntero m apunte al bit de dirección más bajo y luego continúe tomando bits en mandriles de 8 (como tipo insertado en char * ) y printf () se detiene cuando se encuentra 00000000 en el último fragmento ...
Sin embargo, este código no es portátil.
fuente