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 syscall write com fd = 1 (stdout), buffer = endereço do caractere atual e count = 1, escrevendo um byte por vez,
  • o loop no main controla 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.

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 #define pode 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”.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.