Como esse programa C funciona?
main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}
Compila como está (testado em gcc 4.6.3
). Imprime o horário em que é compilado. No meu sistema:
!! !!!!!! !! !!!!!! !! !!!!!!
!! !! !! !! !! !! !! !!
!! !! !! !! !! !! !! !!
!! !!!!!! !! !! !! !! !! !!!!!!
!! !! !! !! !! !! !!
!! !! !! !! !! !! !!
!! !!!!!! !! !! !! !!!!!!
Fonte: sykes2 - Um relógio em uma linha , sugere o autor do sykes2
Algumas dicas: Não há avisos de compilação por padrão. Compilado com -Wall
, os seguintes avisos são emitidos:
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]
c
obfuscation
deobfuscation
brega
fonte
fonte
printf("%d", _);
ao início dasmain
impressões: pastebin.com/HHhXAYdJint
./a.out $(seq 0 447)
Respostas:
Vamos ofuscar isso.
Recuo:
Introduzindo variáveis para desembaraçar essa bagunça:
Observe que,
-~i == i+1
devido ao complemento de dois. Portanto, temosAgora, observe que
a[b]
é o mesmo queb[a]
e aplique a-~ == 1+
alteração novamente:Convertendo a recursão em um loop e esgueirando-se um pouco mais de simplificação:
Isso gera um caractere por iteração. Cada 64º caractere gera uma nova linha. Caso contrário, ele usa um par de tabelas de dados para descobrir o que produzir e coloca o caractere 32 (um espaço) ou o caractere 33 (a
!
). A primeira tabela (">'txiZ^(~z?"
) é um conjunto de 10 bitmaps que descrevem a aparência de cada caractere, e a segunda tabela (";;;====~$::199"
) seleciona o bit apropriado a ser exibido no bitmap.A segunda mesa
Vamos começar examinando a segunda tabela
int shift = ";;;====~$::199"[(i*2&8) | (i/64)];
,.i/64
é o número da linha (6 a 0) ei*2&8
é 8 sei
4, 5, 6 ou 7 mod 8.if((i & 2) == 0) shift /= 8; shift = shift % 8
seleciona o dígito octal alto (parai%8
= 0,1,4,5) ou o dígito octal baixo (parai%8
= 2,3,6,7) do valor da tabela. A tabela de turnos acaba assim:ou em forma de tabela
Observe que o autor usou o terminador nulo para as duas primeiras entradas da tabela (sorrateira!).
Isso foi desenvolvido após uma exibição de sete segmentos, com
7
s como espaços em branco. Portanto, as entradas na primeira tabela devem definir os segmentos que serão iluminados.A primeira mesa
__TIME__
é uma macro especial definida pelo pré-processador. Ele se expande para uma constante de cadeia que contém o tempo em que o pré-processador foi executado, no formulário"HH:MM:SS"
. Observe que ele contém exatamente 8 caracteres. Observe que 0-9 tem valores ASCII 48 a 57 e:
tem valor ASCII 58. A saída é de 64 caracteres por linha, de modo que deixa 8 caracteres por caractere__TIME__
.7 - i/8%8
é, portanto, o índice do__TIME__
que está sendo produzido atualmente (7-
é necessário porque estamos iterandoi
para baixo). Então,t
é o caráter de__TIME__
ser produzido.a
acaba igualando o seguinte em binário, dependendo da entradat
:Cada número é um bitmap que descreve os segmentos iluminados em nossa exibição de sete segmentos. Como os caracteres são todos ASCII de 7 bits, o bit alto é sempre limpo. Assim,
7
na tabela de segmentos sempre imprime em branco. A segunda tabela fica assim com7
s como espaços em branco:Assim, por exemplo,
4
é01101010
(bits 1, 3, 5 e 6 definidos), que imprime comoPara mostrar que realmente entendemos o código, vamos ajustar um pouco a saída com esta tabela:
Isso é codificado como
"?;;?==? '::799\x07"
. Para fins artísticos, adicionaremos 64 a alguns dos caracteres (como apenas os 6 bits baixos são usados, isso não afetará a saída); isso dá"?{{?}}?gg::799G"
(observe que o 8º caractere não é utilizado, para que possamos fazer o que quisermos). Colocando nossa nova tabela no código original:Nós temos
assim como esperávamos. Não é tão sólido quanto o original, o que explica por que o autor optou por usar a tabela que usou.
fonte
*
(desreferência) quanto a+
: PVamos formatar isso para facilitar a leitura:
Portanto, executando-o sem argumentos, _ (argc convencionalmente) é
1
.main()
chamará recursivamente a si mesmo, passando o resultado de-(~_)
(NOT bit a bit negativo_
), então, na verdade, ocorrerá 448 recursões (apenas condição onde_^448 == 0
).Levando isso, ele imprimirá 7 linhas largas de 64 caracteres (a condição externa ternária e
448/64 == 7
). Então, vamos reescrevê-lo um pouco mais limpo:Agora,
32
é decimal para o espaço ASCII. Imprime um espaço ou um '!' (33 é '!', Daí o '&1
' no final). Vamos nos concentrar no blob no meio:Como outro pôster disse,
__TIME__
é o tempo de compilação do programa e é uma string, portanto há alguma aritmética em andamento, além de tirar proveito de um índice de matriz sendo bidirecional: a [b] é o mesmo que b [a] para matrizes de caracteres.Isso selecionará um dos 8 primeiros caracteres em
__TIME__
. Isso é indexado em[">'txiZ^(~z?"-48]
(0-9 caracteres são 48-57 decimais). Os caracteres nessa sequência devem ter sido escolhidos para seus valores ASCII. A manipulação de código ASCII com o mesmo caractere continua através da expressão, resultando na impressão de um '' ou '!' dependendo da localização no glifo do personagem.fonte
Adicionando às outras soluções,
-~x
é igual ax+1
porque~x
é equivalente a(0xffffffff-x)
. Isso é igual ao(-1-x)
complemento 2s, assim-~x
é-(-1-x) = x+1
.fonte
Eu ofusquei a aritmética do módulo o máximo que pude e removi a reccursão
Expandindo um pouco mais:
fonte