Skip to main content

violeta

Home Blog RSS

Quine ofuscada

01-04-2025

Olá amigos! Novamente estou aqui, com algo que não é relacionado aos posts anteriores :D

O prazo para o projeto e esse post é de 24 horas.

Antes de dormir, estava pensando em Quines, que são códigos que retornam eles mesmos (sem ler o próprio arquivo), e como gostava muito desse conceito que quebrava minha cabecinha simples quando era jovem.

Aí pensei um pouco mais, e como seria uma quine ofuscada?

Recentemente tenho visto muitos videos do John Rammond, aonde ele desbrava no mundo de análise de malware, tenho que interpretar código ofuscado. Não sei nada real sobre ofuscação de código, mas vi bastante videos dele para entender que dá pra brincar bastante com isso!

O desafio de fazer uma Quine ofuscada envolve fazer algo que seja consistente e confuso.

A Quine

Peguei da wikipedia, será a base. Fazendo em python pra salvar tempo usando Jupyter no VSCode.

c = 'c = %r; print(c %% c)'; print(c % c)

A ideia inicial era pegar e criar métodos, então renomear tudo pra ficar confuso, pensando nisso, cheguei no seguinte código:

1def asd():
2    qwe = 'def asd()\n\tqwe = %r;\n\tprint(qwe %% qwe)\nasd()'
3    print(qwe % qwe)
4asd()

Não é muito inspirador, é só uma quine levemente mais longa. Então pensei em fazer uma função que substitui os \n:

1def barraN():
2    _temp = 55
3    _num = int(chr(_temp)) * int(chr(_temp))
4    _str = chr(_num) + chr(int(_num - 1))
5    return chr(int(_str))

Fazer uma Quine com isso é meio longo e trabalhoso, então simplifiquei um pouco, por agora:

1def barraN():
2    return '\n'
3a = "def barraN():{0}\treturn '\\n'{0}a = {1}{0}print(a.format(barraN(),repr(a)))"
4print(a.format(barraN(),repr(a)))

Cheatando levemente por usar repr(a), que retorna a propria string de forma pura, com aspas, por exemplo. Mas o ponto não é ser boa em quines, e sim ofuscar isso tudo.

Tentativa 1: Compressão

Vendo o código acima, dá pra brincar um pouco mais com a string final, usando a mesma lógica do barraN. Se fossemos comprimir a string a, daria pra, por exemplo, fazer algo assim:

 1def n():
 2    return '\n'
 3def t():
 4    return '\t'
 5def d():
 6    return 'def '
 7def r():
 8    return 'return '
 9a = "{3}n():{0}{1}{4}'\\n'{0}{3}t():{0}{1}{4}'\\t'{0}{3}d():{0}{1}{4}'{3}'{0}{3}r():{0}{1}{4}'{4}'{0}a = {2}{0}print(a.format(n(),t(),repr(a),d(),r()))"
10print(a.format(n(),t(),repr(a),d(),r()))

Todas as repetições de 3 ou mais caracteres foram comprimidas, e a string em si já está levemente confusa.

Indo por essa linha, podemos ver que existem duas repetições grandes na string a: {0}{3} e {0}{1}{4}

 1def n():
 2    return '\n'
 3def t():
 4    return '\t'
 5def d():
 6    return 'def '
 7def r():
 8    return 'return '
 9def z():
10    return '{0}{1}{4}'
11def y():
12    return '{0}{3}'
13a = "{{3}}n():{0}'\\n'{1}t():{0}'\\t'{1}d():{0}'{{3}}'{1}r():{0}'{{4}}'{1}z():{0}'{{5}}'{1}y():{0}'{{6}}'{{0}}a = {{2}}{{0}}print(a.format(z(),y(),repr(a)).format(n(),t(),repr(a),d(),r(),z(),y()))"
14print(a.format(z(),y(),repr(a)).format(n(),t(),repr(a),d(),r(),z(),y()))

Bom. Agora a string está gigantesca, e tudo está confuso, fiquei embasbacada em como prosseguir.

O problema é que para conseguir comprimir o z() e o y(), tivemos que criar um novo format, por cima do format antigo.

 1def n():
 2    return '\n'
 3def t():
 4    return '\t'
 5def d():
 6    return 'def '
 7def r():
 8    return 'return '
 9def z():
10    return '{0}{1}{4}'
11def y():
12    return '{0}{3}'
13def f():
14    return '.format('
15a = "{{3}}n():{0}'\\n'{1}t():{0}'\\t'{1}d():{0}'{{3}}'{1}r():{0}'{{4}}'{1}z():{0}'{{5}}'{1}y():{0}'{{6}}'{1}f():{0}'{{7}}'{{0}}a = {{2}}{{0}}print(a{{7}}z(),y(),repr(a)){{7}}n(),t(),repr(a),d(),r(),z(),y(),f()))"
16print(a.format(z(),y(),repr(a)).format(n(),t(),repr(a),d(),r(),z(),y(),f()))

Enquanto o que exatamente está sendo printado é confuso, não diria que o código está ofuscado, e os milhões de método entregam, mais ou menos.

Tentativa 2: Exec

Tentei, para fins de teste, fazer uma das versões simples, utilizando exec para criar funções em tempo real:

1def n():
2    return '\n'
3a = "def n():{0}\treturn '\\n'{0}a = {1}{0}print(a.format(n(),repr(a)))"
4print(a.format(n(),repr(a)))
1b = 'def a(x,y): exec(f\'def {x}(): return \"{y}\"\',globals())'
2exec(b)
3a('n','\\n')
4q = '{0}exec(b){0}a(\'n\',\'\\\\n\'){0}q = {1}{0}print(b + q.format(n(),repr(q)))'
5print(b + q.format(n(),repr(q)))

Não estou exatamente impressionada, mas dá para melhorar.

Comparando essa versão base, são 126 caracteres contra 205.

Uma coisa engraçadinha que eu percebi nisso tudo………é que uma função para imprimir \n vai sempre aumentar em 1 caractere. “NOSSAAAAAAAAA SÓ PERCEBEU AGORA??” Pergunta leitor frustrado. Sim, só agora. Sou besta.

Após ter oitenta bilhões de problemas com , " e ‘, decidi que vou me contentar com essa versão mesmo. Mas estou longe de terminar.

Ultima tentativa, juro

Peguei o algoritmo mais bestinha de criptografia que não usasse biblioteca:

1a = "def e(t): return ''.join(chr(ord(c)^456)for c in t)"
2exec(a)
3q = 'ƩǨǵǨƳǸƵǂƭưƭƫǠƩǡǂƹǨǵǨƳǹƵǂƸƺơƦƼǠƭǠƹǡǦƮƧƺƥƩƼǠƺƭƸƺǠƩǡǤƺƭƸƺǠƹǡǡǡ'
4print(e(q).format(repr(a),repr(q)))

Gostei bastante de ter feito essa quine, basicamente aquele bloco estranho é igual a 'a = {0}\nexec(a)\nq = {1}\nprint(e(q).format(repr(a),repr(q)))'

Está simples e engraçadinho, mas ainda dá pra ver que no final das contas estamos printando algo que veio de q.

1a = "def e(t): return ''.join(chr(ord(c)^456)for c in t)\nc = repr(a)"
2exec(a)
3a = 'ƭưƭƫǠǪƪǨǵǨǯƩǨǵǨƳǸƵƔƔƦƭưƭƫǠƩǡƔƔƦƩǨǵǨƳǹƵƔƔƦƭưƭƫǠƭǠƩǡǡǯƔƦƸƺơƦƼǠƪǦƮƧƺƥƩƼǠƫǤƺƭƸƺǠƩǡǡǡǪǤƯƤƧƪƩƤƻǠǡǡ'
4exec(e(a))

Levemente mais engraçado, agora não dá pra saber o que vai ser executado. Estou feliz, PUTA MERDA ESQUECI DO EPISÓDIO DE HOJE DA NOVELA.

Espero que tenham gostado fuiiiiiiii!

#python #code obfuscation #silly

|