O que é obfuscated code, opções e exemplos [parte 2]
Continuo aqui a explicação iniciada em https://www.galirows.com.br/meublog/blog/o-que-e-obfuscated-code-parte1/.
Inicio falando sobre o International Obfuscated C Code Contest (IOCCC), que é uma competição de programação que premia os programadores que conseguem escrever os programas em C mais criativamente “confusos”, engenhosos e difíceis de entender. O IOCCC nasceu em 1984, criado por Landon Curt Noll e Larry Bassel, e é um dos concursos de programação mais antigos ainda em atividade. O evento desenvolve: O uso de truques de sintaxe, macros, e comportamento indefinido do C para criar arte técnica; A criatividade e o humor na programação; e o domínio da linguagem C.
O IOCCC concede prêmios em categorias como:
- Best one-liner (melhor código em uma linha);
- Most creative (mais criativo);
- Most confusing (mais confuso);
- Best visual effect;
- Best abuse of the C preprocessor;
- Best algorithmic trick.
Trago aqui dois códigos explicados para inspirar novos competidores 😀
Vencedor de 1984 – Anonymous 1984
O vencedor do IOCCC de 1984 é um código que imprime, byte a byte, a string "hello, world!\n" usando truques de sintaxe e sem usar explicitamente puts/printf.
int i;main(){for(;i["]<i;++i){--i;}"];read('-'-'-',i+++"hell\
o, world!\n",'/'/'/'));}read(j,i,p)void*i;{write(j/p+p,i---j,(int)i/(int)i);}
Código disponível em winner/1984/anonymous/anonymous.c at master · ioccc-src/winner · GitHub
Abaixo mostro o código melhor apresentado, em múltiplas linhas e endentações. A leitura do código melhora, mesmo assim ainda não é fácil entender.
int i;
main(){
for(;i["]<i;++i){--i;}"];
read('-'-'-',i+++"hell\
o, world!\n",'/'/'/'));
}
read(j,i,p)void*i;{
write(j/p+p,i---j,(int)i/(int)i);
}
O que o código faz é:
- constrói um ponteiro que aponta para as letras da string literal
"hello, world!\n", - chama uma função
read()(nome enganador) que, por sua vez, chama o syscallwritecomfd = 1(stdout), buffer = endereço do caractere atual e count = 1, escrevendo um byte por vez, - o loop no
maincontrola quantas vezes essa chamada acontece (termina quando a indexação numa certa string chega ao'\0').
Existe uma explicação mais detalhada do código disponível em Deobfuscating obfuscated code for fun and no profit | by Lain Iwakura | Medium
A seguir mostro o código em uma versão legível e moderna. Ele está com a mesma lógica, mas usando sintaxe ANSI C moderna e nomes descritivos.
#include <unistd.h> // para write()
int main(void) {
int i = 0; // contador/índice de caracteres
const char *msg = "hello, world!\n";
// laço que percorre cada caractere até o '\0' (fim da string)
while (msg[i] != '\0') {
write(1, &msg[i], 1); // escreve um caractere na saída padrão (stdout)
i++; // avança para o próximo caractere
}
return 0;
}
Por que foi premiado
- Essas construções são válidas no padrão C de 1983 (K&R C), mas hoje parecem heresias de engenharia. O programa demonstra como conhecer os cantos mais obscuros da linguagem pode gerar resultados surpreendentes. O autor explora regras pouco conhecidas, mas legítimas, da linguagem C:
a[b] == b[a](indexação simétrica)'/' / '/' == 1(divisão de caracteres)'-' - '-' == 0- Ponteiros e inteiros podem ser somados (
"hello" + i) - Conversões implícitas e pós-decrementos (
i---j) - Definições no estilo K&R (antes do ANSI C) permitem parâmetros sem tipos explícitos.
- Tamanho mínimo e ausência de dependências:
- não inclui
<stdio.h>, - não usa
printf, - tem apenas 2 funções e 1 variável global.
- não inclui
O que se aprende com ele
- Entender profundamente o C: Como o compilador trata strings e ponteiros.
- A limitação da clareza: este tipo de código mostra que, embora “funcione”, a legibilidade fica profundamente prejudicada — e em contextos de engenharia de software isso seria um problema.
- A brincadeira técnica: o IOCCC não é sobre “ser prático”, mas sobre “ser criativo” e explorar os limites da linguagem C. Ele mostra que domar C inclui entender seus cantos obscuros.
Vencedor de 1986 – Jim Hague
Este programa ganhou o IOCCC em 1986. Ele implementa o padrão Morse internacional de transmissão de mensagens — ou seja, converte texto em código Morse — e o faz de forma altamente ofuscada.
#define DIT (
#define DAH )
#define __DAH ++
#define DITDAH *
#define DAHDIT for
#define DIT_DAH malloc
#define DAH_DIT fgets
#define _DAHDIT char
_DAHDIT _DAH_[]="ETIANMSURWDKGOHVFaLaPJBXCYZQb54a3d2f16g7c8a90l?e'b.s;i,d:"
;main DIT DAH{_DAHDIT
DITDAH _DIT,DITDAH DAH_,DITDAH DIT_,
DITDAH _DIT_,DITDAH DIT_DAH DIT
DAH,DITDAH DAH_DIT DIT DAH;DAHDIT
DIT _DIT=DIT_DAH DIT 81 DAH,DIT_=_DIT
__DAH;_DIT==DAH_DIT DIT _DIT,81,stdin DAH&&(_DIT[strlen(_DIT)-1]='\0',_DIT);__DIT
DIT'\n'DAH DAH DAHDIT DIT DAH_=_DIT;DITDAH
DAH_;__DIT DIT DITDAH
_DIT_?_DAH DIT DITDAH DIT_ DAH:'?'DAH,__DIT
DIT' 'DAH,DAH_ __DAH DAH DAHDIT DIT
DITDAH DIT_=2,_DIT_=_DAH_; DITDAH _DIT_&&DIT
DITDAH _DIT_!=DIT DITDAH DAH_>='a'? DITDAH
DAH_&223:DITDAH DAH_ DAH DAH; DIT
DITDAH DIT_ DAH __DAH,_DIT_ __DAH DAH
DITDAH DIT_+= DIT DITDAH _DIT_>='a'? DITDAH _DIT_-'a':0
DAH;}_DAH DIT DIT_ DAH{ __DIT DIT
DIT_>3?_DAH DIT DIT_>>1 DAH:'\0'DAH;return
DIT_&1?'-':'.';}__DIT DIT DIT_ DAH _DAHDIT
DIT_;{DIT void DAH write DIT 1,&DIT_,1 DAH;}
Código disponível em winner/1986/hague/hague.c at master · ioccc-src/winner · GitHub
No início do código encontram-se várias macros definidas com nomes enganosos. O autor nomeia tokens como DIT e DAHDIT para símbolos de C, o que torna o código visualmente muito estranho, porque o leitor espera algo como for ou if, mas vê DAHDIT.
O corpo do programa em si mistura essas macros com os comandos usuais de C, combinando loops e condições com uma estrutura que visualmente não parece “texto de programa” convencional. Isso força o leitor a pensar “o que isso realmente faz?” e acompanhar o pré-processador para entender.
A lógica do programa realmente converte caracteres de entrada em sinais de código Morse (pontos e traços), imprimindo o resultado. A ofuscação está no modo como o autor esconde os laços de repetição, as verificações de caractere e a saída usando essas macros com nomes que desencadeiam “DIT” e “DAHDIT”, evocando Morse, mas trocando pela sintaxe de C.
Outro truque típico: o uso de operadores e expressões pouco comuns, como combinação de ponteiros, ternários, manipulação de bits, variáveis com nomes curtos ou simbólicos, para comprimir o código ou torná-lo menos legível.
Por que foi premiado
- Porque fez algo funcional — não era apenas “printar Hello, world!”, mas implementar um conversor de Morse.
- Porque usou ofuscação criativa — renomeando macros, alterando visual do código, tornando difícil de ler, mas ainda C correto.
- Porque tornou-se educativo: ao “decifrar” o código o leitor acaba aprendendo sobre o pré-processador, macros, substituição de tokens, e construção de laços menos triviais.
O que se aprende com ele
- A importância de macros: ver como
#definepode transformar radicalmente a leitura de um código, ao substituir tokens por outros símbolos ou palavras. - O mesmo já visto no exemplo anterior, sobre “a limitação da clareza” e “a brincadeira técnica”.