Mais um blog inútil.

Coding

Junho 5, 2009

Threefish - Part II

Arquivado em: assembly, coding, serious-business, useless — dcoder @ 22:28

Como prometido aos meus fiéis leitores ontem, hoje vamos ver como optimizar a cifra Threefish de 250 ciclos por byte (em 32 bit) para algo mais útil na prática.

Em primeiro lugar, e de longe mais relevante, vamos eliminar todos os loops e branches da função. Estes não apenas ocupam recursos que podiam estar a ser usados na cifração em si, mas impedem o uso de todas as unidades de execução. Em segundo lugar, queremos eliminar os acessos à memória desnecessários e manter o máximo de dados possível em registos: substituímos os arrays por variáveis para facilitar esta tarefa ao compilador. Infelizmente, não existem registos para toda a gente (em x86) e vamos sempre ter de aceder à stack para guardar alguns dados. Mas não faz mal. Assim sendo, ficamos com uma função do género:

void ThreefishEncrypt(const ThreefishKey ks, u8 *in, u8 *out)
{
	u64 *p = (u64*)in;
	u64 v0, v1, v2, v3;
	u64 f0, f1, f2, f3;
	int i, d;
		
	v0 = p[0];
	v1 = p[1];
	v2 = p[2];
	v3 = p[3];
	
	/* start of round 0 */
	v0 += ks->subkey[0][0];
	v1 += ks->subkey[0][1];
	v2 += ks->subkey[0][2];
	v3 += ks->subkey[0][3];
	
	MIX(f1, f0, v1, v0, 5);
	MIX(f3, f2, v3, v2, 56);
	
	v0 = f0; v1 = f3; v2 = f2; v3 = f1;
	
	/* start of round 1 */
	MIX(f1, f0, v1, v0, 36);
	MIX(f3, f2, v3, v2, 28);
	
	v0 = f0; v1 = f3; v2 = f2; v3 = f1;
	
	/* start of round 2 */
	MIX(f1, f0, v1, v0, 13);
	MIX(f3, f2, v3, v2, 46);

	v0 = f0; v1 = f3; v2 = f2; v3 = f1;
	......

	((u64*)out)[0] = v0 + ks->subkey[ROUNDS/4][0];		
	((u64*)out)[1] = v1 + ks->subkey[ROUNDS/4][1];	
	((u64*)out)[2] = v2 + ks->subkey[ROUNDS/4][2];	
	((u64*)out)[3] = v3 + ks->subkey[ROUNDS/4][3];	
}

Esta versão já é substancialmente mais rápida - passamos de 250 para 80 ciclos por byte. Em 64 bit passamos de 50 para 11!

Será que ainda podemos acelerar o processo?

A resposta é sim.

Recorrendo às instruções SSE2 presentes em essencialmente todos os CPUs x86 actuais, podemos cifrar 2 blocos simultaneamente por essencialmente o custo de um. Como é que isto é conseguido?

void ThreefishEncryptSSE2(const ThreefishKey ks, u8 *in, u8 *out)
{
	__m128i *p = (__m128i*)in;
	__m128i v0, v1, v2, v3;
	__m128i f0, f1, f2, f3;
	__m128i t0, t1, t2, t3;
	__m128i k0, k1, k2, k3;
		
	t0 = _mm_load_si128(&p[0]);
	t1 = _mm_load_si128(&p[1]);
	t2 = _mm_load_si128(&p[2]);
	t3 = _mm_load_si128(&p[3]);
	
	v0 = _mm_unpacklo_epi64(t0, t2);
	v1 = _mm_unpackhi_epi64(t0, t2);
	v2 = _mm_unpacklo_epi64(t1, t3);
	v3 = _mm_unpackhi_epi64(t1, t3);
	
	t0 = _mm_load_si128((__m128i*)&(ks->subkey[0][0]));
	t1 = _mm_load_si128((__m128i*)&(ks->subkey[0][2]));
	
	k0 = _mm_shuffle_epi32(t0, 0x44);
	k1 = _mm_shuffle_epi32(t0, 0xEE);
	k2 = _mm_shuffle_epi32(t1, 0x44);
	k3 = _mm_shuffle_epi32(t1, 0xEE);
	
	/* Round 0 */
	v0 = _mm_add_epi64(v0, k0);
	v1 = _mm_add_epi64(v1, k1);
	v2 = _mm_add_epi64(v2, k2);
	v3 = _mm_add_epi64(v3, k3);
	
	/* load next subkey */
	t0 = _mm_load_si128((__m128i*)&(ks->subkey[1][0]));
	t1 = _mm_load_si128((__m128i*)&(ks->subkey[1][2]));
	k0 = _mm_shuffle_epi32(t0, 0x44);
	k1 = _mm_shuffle_epi32(t0, 0xEE);
	k2 = _mm_shuffle_epi32(t1, 0x44);
	k3 = _mm_shuffle_epi32(t1, 0xEE);
	
	/* mix */
	f0 = _mm_add_epi64(v0, v1);
	f1 = _mm_xor_si128(_mm_xor_si128(_mm_slli_epi64(v1, 5),_mm_srli_epi64(v1, 64-5)), f0);
	f2 = _mm_add_epi64(v2, v3);
	f3 = _mm_xor_si128(_mm_xor_si128(_mm_slli_epi64(v3, 56),_mm_srli_epi64(v3, 64-56)), f2);
	
	/* permute */
	v0 = f0;
	v1 = f3;
	v2 = f2;
	v3 = f1;
	
	....

	/* Round 71 */
	/* mix */
	f0 = _mm_add_epi64(v0, v1);
	f1 = _mm_xor_si128(_mm_xor_si128(_mm_slli_epi64(v1, 59),_mm_srli_epi64(v1, 64-59)), f0);
	f2 = _mm_add_epi64(v2, v3);
	f3 = _mm_xor_si128(_mm_xor_si128(_mm_slli_epi64(v3, 50),_mm_srli_epi64(v3, 64-50)), f2);
	
	/* permute */
	v0 = _mm_add_epi64(f0, k0);
	v1 = _mm_add_epi64(f3, k1);
	v2 = _mm_add_epi64(f2, k2);
	v3 = _mm_add_epi64(f1, k3);
	
	// unpack
	t0 = _mm_unpacklo_epi64(v0, v1);
	t1 = _mm_unpacklo_epi64(v2, v3);
	t2 = _mm_unpackhi_epi64(v0, v1);
	t3 = _mm_unpackhi_epi64(v2, v3);
	
	// place holder
	p = (__m128i*)out;
	_mm_store_si128(&p[0], t0);
	_mm_store_si128(&p[1], t1);
	_mm_store_si128(&p[2], t2);
	_mm_store_si128(&p[3], t3);
}

Em primeiro lugar, carregamos os dois blocos para registos XMM. No entanto, quando fazemos isto, os inteiros de 64 bits dentro dos blocos ficam na ordem errada. Através da instrução PUNPCKLQDQ e PUNPCKHQDQ alteramos a ordem dos blocos de a0a1 a2a3 b0b1 b2b3 para a0b0 a1b1 a2b2 a3b3. Assim já podemos facilmente processar os dois blocos como se o estivessemos a fazer no caso acima, sem SSE2.

Ainda de notar é também o truque necessário para suportar o mesmo formato das subkeys: através de 2 loads e 4 shuffles, essencialmente extendemos a subchave a0 a1 a2 a3 para a0a0 a1a1 a2a2 a3a3.

Após as 72 iterações, temos de retornar os inteiros à ordem original, novamente com as instruções PUNPCKLQDQ e PUNPCKHQDQ. Após isto só nos resta armazenar as variáveis no buffer de saída.

Será que todo este trabalho vale a pena? Mais uma vez, sim. No modo CTR, passamos de 80 ciclos por byte (acima) para 26. Podemos ainda melhorar este número? Quebrando a compatibilidade com o formato das subchaves acima, podemos. Trocamos então 2 loads + 4 shuffles por 4 loads. Isto leva-nos ao resultado final de 19 ciclos por byte em 32 bit e 10 ciclos por byte em 64 bit. Isto não é óptimo, mas está bastante perto do óptimo num Core 2.

Para obter melhores desempenhos ainda, particularmente no modo CTR, poderíamos utilizar as variantes com blocos maiores do Threefish, e.g. 512 ou 1024 bits. Estas facilmente iriam parar aos 10 ou menos ciclos por byte em 32 bits. No entanto, blocos deste tamanho são incómodos para os tamanhos típicos de payloads cifradas - só para mensagens relativamente grandes é que faz sentido utilizar estas variantes.

O código utilizado para estas experiências está aqui. Não está bem comentado nem bem organizado - é apenas o resultado de experimentação. Divirtam-se.

Threefish

Arquivado em: coding, serious-business, useless — dcoder @ 03:54

Uma das formas de criar hash functions seguras passa por reutilizar uma cifra num ou noutro modo de operação especial. Entre estes encontram-se o modo Davies-Meyer, UBI, Miyaguchi-Preneel, etc.

Um dos candidatos ao SHA-3, o Skein, utiliza uma cifra nova em modo UBI — Threefish. Dada esta conversa toda sobre o AES-256 ser inseguro, e como não temos já cifras seguras suficientes, implementei esta cifra em C a partir das especificações acessíveis aqui. Existem vários tamanhos para o bloco e chave do Threefish — implementei apenas o mais realista para cifração de dados: bloco de 256 bits, chave de 256 bits.

Além do bloco e chave de 256 bits, esta cifra aceita um parâmetro adicional, chamado “tweak”. Tipicamente para cifração de dados não é muito útil, mas isto muda de figura quando queremos criar modos de operação para cifração sector a sector, como os utilizados pelo TrueCrypt e companhia. A cifra é bastante simples: 72 iterações de adições, xor e rotações em inteiros de 64 bits. A cada 4 iterações é misturada uma subchave derivada da chave com o bloco. No fim de cada iteração a ordem dos inteiros é alterada através de uma permutação. Se acederem às especificações da cifra, irão notar que esta não segue a estrutura típica de uma Feistel Network — trata-se de uma Substitution-Permutation Network. As vantagens deste design serão abordadas noutra altura mais conveniente.

Uma implementação canónica que aparenta respeitar as especificações encontra-se aqui. O desempenho desta implementação é miserável — ~250 cpb em 32 bits, ~50 cpb em 64 bit quando a cifra é usada em CTR mode.

O próximo post abordará como optimizar esta cifra para níveis usáveis de desempenho.

Maio 28, 2009

Lucifer

Arquivado em: coding, serious-business — dcoder @ 23:58

Lucifer foi uma cifra desenvolvida pela IBM durante os anos 70 que definiu a estrutura de todas as cifras nas décadas seguintes. A cifra DES foi directamente derivada da Lucifer; ambas eram Feistel networks.

Ao contrário do DES, no entanto, a Lucifer era mais vulnerável a ataques diferenciais (um exemplo das fraquezas da cifra mais adiante). Tinha também um bloco e chave maiores (ambos de 128 bits). O código que se segue tem apenas valor histórico, dado que a cifra é bastante insegura para os standards actuais.

Lucifer C Source code.

O exemplo fornecido cifra um bloco de 0s com uma chave composta também inteiramente de 0s. O que acontece é evidente:

53 53 53 53 53 53 53 53 F2 F2 F2 F2 F2 F2 F2 F2

Além de se notarem claramente as delimitações dos dois blocos da Feistel net, é tremendamente evidente que isto não é uma permutação aleatória de bits, como uma boa cifra devia ser.

Esta implementação foi derivada do artigo “LUCIFER: a cryptographic algorithm.” de Arthur Sorkin, 1984. Aqui.

Maio 26, 2009

Leitor de SMS - Modem Huawei E220

Arquivado em: coding, drama, openbsd, osx, serious-business — falso @ 23:46

Ora viva!

No natal passado a minha fc ofereceu-me um “Vodafone Vita Net”. Que inclui este modem USB. A malandrice é que em OSX (onde costumo usar) e OpenBSD não há maneira de ler as sms que nos são enviadas pela Vodafone a dizer ate quando dura o serviço, quantos megabytes temos, etc. Já andava com esta fisgada há muito tempo então decidi fazer um programinha que se ligasse ao modem por serie, mandasse os comandos AT que listam as sms, e mostrasse duma forma pipi.

Decidi usar perl, porque já vem por default no OSX e no OpenBSD. Tive foi de usar um modulo extra do perl que não vem na base, o Device::Modem para conseguir comunicar com o modem.

Para instalar este modulo em OpenBSD basta instalar o comms/p5-Device-Modem. E no OSX o mais facil é chamar a shell do CPAN “perl -MCPAN -e shell” e depois lá “install Device::Modem” e responder que sim ao que ele pergunta.

Também uso o comando “stty”, mas existe em OpenBSD como em OSX portanto não há dramas. Uso-o para apanhar o numero de colunas do terminal, para o output se adaptar à largura.

No inicio do ficheiro estão duas variáveis que devem ser preenchidas correctamente, o $pin e o $device. Ah… e tem que ser corrido como root, ou então dar permissão ao device de alguma forma, eu não sei como.

Futuramente, talvez adicione opções para eliminar sms e também para enviar.

Sem mais demoras, aqui está o sms.pl - Licença BSD.

Aqui vão os screenshots obrigatórios.

osxopenbsd

Maio 14, 2009

Arvorezinha - ML (versão alternativa)

Arquivado em: arvorezinha, coding — falso @ 00:22

Ora viva de novo!
Mais uma vez venho blogar por causa de novas submissões de arvorezinhas, desta vez do ardoRic da PTnet! Ele diz o seguinte:

nao gostei da outra versão (está nos comentários) … estava pouco ML like.
Espero que gostes desta :)

let rec branch n = 
   match n with 
     0 -> [] 
   | n -> "*":: branch (n-1)

let rec nat n = 
  match n with
     0 -> []
   | n -> n::(nat (n-1))
;;

List.iter (fun x -> 
  List.iter print_string x ;
  print_string "\n"
) (List.rev_map branch (nat 5))

Maio 13, 2009

Arvorezinha - COBOL

Arquivado em: arvorezinha, coding, useless — falso @ 16:07

Outra submissão para a saga da arvorezinha, pelo meu colega João Saramago, desta vez em COBOL. Aqui vai:

IDENTIFICATION DIVISION.
PROGRAM-ID.  Arvorezinha.
AUTHOR.  João Saramago.

DATA DIVISION.

WORKING-STORAGE SECTION.
01  Linhas                                PIC 99  VALUE 5.
01  AUX                                   PIC 99 VALUE ZEROS.
01  AUX2                                  PIC 99 VALUE ZEROS.

PROCEDURE DIVISION.
Main.
    DISPLAY "Número de Linhas: " WITH NO ADVANCING
    ACCEPT Linhas.
    PERFORM VARYING AUX FROM 1 BY 1 UNTIL AUX GREATER THAN Linhas
       PERFORM VARYING AUX2  FROM 0 BY 1 UNTIL AUX2 EQUAL TO AUX 
          DISPLAY "*" WITH NO ADVANCING
       END-PERFORM
       DISPLAY ""
    END-PERFORM
    STOP RUN.

Para compilar usei o OpenCobol e para compilar é este comando:

cobc -free -x arvorezinha.cbl 

Maio 10, 2009

Arvorezinha - ARM Assembly

Arquivado em: arvorezinha, assembly, coding, useless — falso @ 23:18

Boas noites!

Venho de novo dar continuação à saga da arvorezinha, desta vez em ARM assembly para Linux. Esta foi relativamente simples, mas também depois de se ter feito em tantos tipos de arches já não há muito a diferir :-P

Aqui segue o código:

.equ SYSCALL_BASE,	0x900000
.equ SYSCALL_EXIT,	1
.equ SYSCALL_WRITE,	4

.text
.align 2
.globl _start

_start:
	mov r0,#5				@ total = 5
	mov r1,#0				@ counter1 = 0

_ciclo1:
	cmp r0,r1				@ compara c1 e tot
	beq _exit				@ se for igual sai
	mov r2,#0				@ counter2 = 0

_ciclo2:
	cmp r2,r1				@ compara c1 e c2 	
	beq _estrela			@ se for igual _estrela
	blt _estrela			@ se for menor _estrela

	stmfd sp!, {r0-r2}		@ guarda r0 a r2 na stack
	adr r1,newline			@ le \n
	bl _print				@ imprime
	ldmfd sp!, {r0-r2}		@ le da stack r0 a r2
	
	add r1,r1,#1			@ incrementa c1
	b _ciclo1				@ salta _ciclo1

_estrela:
	stmfd sp!, {r0-r2}		@ guarda r0 a r2 na stack
	adr r1,estrela			@ le *
	bl _print				@ imprime
	ldmfd sp!, {r0-r2}		@ le da stack r0 a r2

	add r2,r2,#1			@ incrementa c2
	b _ciclo2				@ salta _ciclo2

_exit:
	mov	r0,#0				@ resultado e' 0
	swi	SYSCALL_BASE+SYSCALL_EXIT	@ sai

_print:
	mov	r0,#1				@ 1 == stadout
	mov	r2,#1				@ tamanho da string
	swi	SYSCALL_BASE+SYSCALL_WRITE	@ imprime
	bx	r14					@ return

estrela:.string "*"
newline:.string "\n"

E aqui a prova-dos-nove:

:~/arvorezinha-arm$ cat /proc/cpuinfo
Processor       : XScale-IXP42x Family rev 1 (v5b)
BogoMIPS        : 266.24
Features        : swp half thumb fastmult edsp
CPU implementer : 0x69
CPU architecture: 5TE
CPU variant     : 0x0
CPU part        : 0x41f
CPU revision    : 1
Cache type      : undefined 5
Cache clean     : undefined 5
Cache lockdown  : undefined 5
Cache format    : Harvard
I size          : 32768
I assoc         : 32
I line length   : 32
I sets          : 32
D size          : 32768
D assoc         : 32
D line length   : 32
D sets          : 32

Hardware        : Linksys NSLU2
Revision        : 0000
Serial          : 0000000000000000
:~/arvorezinha-arm$ make
rm -f arvorezinha.o arvorezinha *~
Using fallback suid method
as -o arvorezinha.o arvorezinha.arm.s
ld -o arvorezinha arvorezinha.o
:~/arvorezinha-arm$ ./arvorezinha
*
**
***
****
*****
:~/arvorezinha-arm$

Download do projecto: arvorezinha-arm.tar.

kryptwit 0.1

Arquivado em: coding, fail, useless — dcoder @ 05:41

Bom dia. Tenho, com alguma tristeza, assistido ao flagelo que o Twitter se tornou nos últimos tempos. Esta actividade inútil, ’twitar’, fomentou a minha última criação: o kryptwit. A prova viva que inutilidade gera inutilidade.

Esta biblioteca tem como função cifrar updates do twitter, por forma a poderem apenas ser lidos pelos possuidores de uma password adequada. O RFC que proponho é o seguinte:

Mensagens de entrada - Não podem ultrapassar os 100 bytes (isto inclui caracteres unicode que podem ocupar mais de 1 byte) de tamanho.

Mensagem de saída - Tem exactamente 139 caracteres ASCII de tamanho. Tem como prefixo “;-) “. Os seguintes 10 caracteres (em Base 85) correspondem ao IV utilizado. Os restantes caracteres, também em Base85, correspodem à mensagem propriamente dita.

Algoritmo para cifração - AES no modo CTR (RFC3686), chave de 256 bits.

Derivação de chave - Password de tamanho arbitrário (até 2^32 bytes), ‘salt’ consiste no nome do utilizador que envia a mensagem. O algoritmo utilizado é o PBKDF2, 1000 iterações, SHA-256.

O IV tem apenas 64 bits. Esta escolha foi feita para contrabalançar a probabilidade de colisões (2^(n/2)) com o tamanho roubado à mensagem. 64 bits dá-nos um bom balanço entre 10 chars e 2^32 mensagens por colisão, em média.

A implementação foi feita em C, recorrendo às implementações excelentes do Brian Gladman do AES, SHA-2 e companhia. O resultado é algo pouco retocado, mas parece funcionar.

Download.

A aplicação de exemplo, sample.c, pode ser utilizada para cifrar e/ou decifrar mensagens. A utilização é simples:

sample e/d username password mensagem

Apresento agora um exemplo de funcionamento:

C:\kryptwit>sample.exe e dongs morte "ola sirs"
;-) lyqf(mXkLw&xU[53F8fP#Owitr}o6VVMAsf[C,h$POG=(M{u9sDT9t68Tfgj{RM}k6sGG.[n#V4i(jbdmp-FLxr#ENS`+Djv
(zV+t*imiCz=Aj]H39iRKcImO&DcSsmqZu-.f#F

C:\kryptwit>sample.exe d dongs morte ";-) lyqf(mXkLw&xU[53F8fP#Owitr}o6VVMAsf[C,h$POG=(M{
u9sDT9t68Tfgj{RM}k6sGG.[n#V4i(jbdmp-FLxr#ENS`+Djv(zV+t*imiCz=Aj]H39iRKcImO&DcSsmqZu-.f#F"
ola sirs

Divirtam-se!

EDIT: Disponibilizei uma nova versão (no mesmo link), com 1 bugfix menor e um Makefile e binario para Unix (amd64).

Maio 5, 2009

Oldies

Arquivado em: coding, fail, useless — dcoder @ 21:14

Em conversa há dias com o falso, mostrei-lhe uma calculadora de bitrate que tinha feito há (N) anos, toda em Win32 assembly. Escusado será dizer o desperdício de tempo que foi. Fiquei, no entanto, triste por ter perdido o código original da mesma; aquilo era até bastante complexo, para o efeito que era.

Para não perder mais lixo que fiz no passado, sempre que o encontrar por aqui vou postando. Neste post coloco o CryptoFrame, uma aplicação modular que fiz para servir de workbench para as várias experiências que ia fazendo. Como sempre, nunca foi acabado nem passou da primeira versão, 0.1. Mesmo assim, há quem tenha achado isto útil por alguma razão que me ultrapassa.

A versão inicial continha um plugin para brincar com curvas elípticas e assinaturas digitais com as mesmas, um plugin para calcular hashes de strings ou ficheiros e uma calculadora de precisão arbitrária com base na biblioteca GNU MP.

Download.

Maio 4, 2009

Malbolge desmistificado

Arquivado em: arvorezinha, coding, lulz, useless — mirage @ 21:42

Bem-vindos. Espicaçado pelas inutilidades que o falso e o Dcoder fizeram em malbolge, e ainda mais depois de o falso ter afirmado peremptoriamente que era impossível fazer uma arvorezinha à mão nesta linguagem diabólica, decidi, fatalmente, jogar mãos à obra.

O segredo do falso em relação ao malbolge é que todo o código dele é gerado por bruteforce. As optimizações do Dcoder limitaram-se a correr o bruteforce durante mais tempo para procurar soluções mais curtas. Neste post proponho um método real, analítico e bastante simples para criar um programa de malbolge que imprima qualquer sequência de caracteres, com a limitação de ser curta. E sim, a arvorezinha é suficientemente curta. ;-) Para facilitar a programação, criei vários utilitários, baseados no malbolge.c original, que constituem o malbolge SDK disponível aqui. Eis uma curta explicação de cada programa:

  • malbolgec: Compila uma source não obfuscada de malbolge (extensão .mbs), transformando-a na versão obfuscada executável (extensão .mb). São admitidos comentários na source, começando com “;”.
  • malbolged: Uma variação do malbolge.c standard que imprime o estado da VM à medida que executa o código. Muito útil para debugging.
  • malbolgedis: O inverso do malbolgec. Dado um programa executável de malbolge, mostra o seu código desobfuscado.
  • malbolgeconstant: Descobre a sequência mais curta para calcular o valor desejado. Mais abaixo será mostrado como o utilizar, já que é uma peça fulcral no método apresentado.
  • malbolgevalid: Mostra os valores válidos num programa de malbolge no intervalo de memória especificado. Útil para procurar valores directos para usar em jumps.
  • malbolgestring: Um proof-of-concept que gera um programa de malbolge que imprima a string especificada. Apenas aceita strings muito curtas (uma arvorezinha completa não cabe, por exemplo, mas “lol jews” sim).

Um dos problemas da programação em malbolge é que que o code pointer e o data pointer começam ambos a 0 no início do programa, ou seja, estão sobrepostos. Por isso, o meu plano começa por separar claramente a zona de código da zona de dados. Assim sendo, a primeira coisa que faço é avançar o D para qualquer sítio mais à frente, com a instrução “j”. Devido à obfuscação que a o malbolge obriga no input, esta instrução na posição 0 de memória corresponde a um salto do D para o valor 41. Temos agora 40 bytes de código disponíveis pela frente, sem mais complicações, antes de voltar a tropeçar nos dados.

Outro dos problemas é a alteração sofrida nas instruções depois de serem executadas, e também dos dados depois de serem usados pelos operadores. Para minimizar estes problemas optei por uma abordagem naïve mas suficientemente eficaz para o problema em questão, que consiste em ter todo o código e dados a correr linearmente, excepto a configuração inicial da área de dados descrita anteriormente.

Talvez a limitação mais severa seja a dos caracteres que podemos usar como input inicial no malbolge, o que dificulta tremendamente o uso de valores pré-definidos, como sejam os caracteres da nossa string. Isto obriga-nos a calcular praticamente todos os valores desejados e imprimi-los, um por um. O malbolge apenas oferece duas instruções de cálculo, o trinary rotate e a trinary op a.k.a. crazy operation. Felizmente para nós, qualquer valor ASCII é calculável usando estas operações com um máximo 3 operações seguidas com valores específicos nos dados (por vezes antecedidas de nops, é certo, para aceder aos dados necessários que, como é sabido, variam consoante a sua posição em memória). Para ajudar, não precisamos de ter o valor exacto no A, basta um A tal que A & 0xFF seja o caracter que queremos (os registos são unsigned short e o print só usa os primeiros 8 bits). Para descobrir a menor sequência de operações necessárias e respectivos valores nos dados fiz o malbolgeconstant (tal como referi acima, pertence ao SDK).

Para exemplificar, vamos criar um programa que imprima “Oi”. Usaremos os símbolos reais para as instruções e não a versão executável, que depois compilaremos com o malbolgec. Primeiro avançamos o D para a posição 41:

jv

Agora calculamos o caracter “O” (ASCII 79) com o malbolgeconstant (tem como input o valor do D, o valor que procuramos e o valor actual do A, que no início do programa é zero):

./malbolgeconstant 41 79 0
EUREKA! code=p pos=42 data=p (r)
EUREKA! code=* pos=41 data=p (s)

OK, esta efusão de felicidade conta-nos que devemos usar as operações “*” e “p”, com o input “p” e “p” respectivamente (que correspondem a “r” e “s” em código obfuscado, mas vamos aqui trabalhar com código normal). De notar que o pos (a posição de memória onde devem estar os dados) começa a 41, que foi a posição inicial que especificámos. Poderia ser 42 ou mais, o que significaria que precisávamos de inserir nops no código (e nos dados, mas aqui não é obrigatório serem nops) antes de executar as instruções especificadas. Por vezes os valores necessários às operações só estão disponíveis mais à frente na memória, devido à forma como o malbolge obriga a codificar as sores. Adiante, o nosso programa tem agora este aspecto:

j*p<voooooooooooooooooooooooooooooooooooopp

Recapitulando: avançamos o D, calculamos “O” (o seu valor fica no registo A) e imprimimo-lo com “< “, terminando o programa com o “v”. A sequência de “o” (nops) no meio é apenas para encher choriços, já que não chegam a ser executados. Estão cá apenas para chegar à posição 41 e 42, onde temos o input necessário às nossas instruções. Vamos compilar este programa e corre-lo no malbolged para ver a evolução dos registos:

./malbolgec example.mbs example.mb && ./malbolged example.mb
	CODE: j	MEM: 040 (() 	A: 00000	C: 00000	D: 00000
	CODE: *	MEM: 115 (s) 	A: 00000	C: 00001	D: 00041
	CODE: p	MEM: 114 (r) 	A: 19721	C: 00002	D: 00042
	CODE: < MEM: 29416 (è) 	A: 09807	C: 00003	D: 00043
O	CODE: v	MEM: 114 (r) 	A: 09807	C: 00004	D: 00044

Como podem ver, quando o programa termina, o registo A tem o valor 9807 e o D tem 44. Vamos usar estes valores como base para calcular a próxima constante de que necessitamos, que é a letra “i” (ASCII 105):

./malbolgeconstant 44 105 9807
EUREKA! code=p pos=45 data=j (Y)
EUREKA! code=* pos=44 data=/ (I)

Mais uma vez só precisamos de duas operações. Temos assim o seguinte:

j*p<*p<voooooooooooooooooooooooooooooooooppo/j

Notem o “o” entre os dois grupos de dados: é para compensar o avanço do D gerado pela instrução que imprime o caracter. Poderia ser qualquer outro símbolo, já que não está a ser usado.

Vamos terminar com um \n, para ficar com um output decente:

./malbolgec example.mbs example.mb && ./malbolged example.mb
	CODE: j	MEM: 040 (() 	A: 00000	C: 00000	D: 00000
	CODE: *	MEM: 115 (s) 	A: 00000	C: 00001	D: 00041
	CODE: p	MEM: 114 (r) 	A: 19721	C: 00002	D: 00042
	CODE: < MEM: 119 (w) 	A: 09807	C: 00003	D: 00043
O	CODE: *	MEM: 073 (I) 	A: 09807	C: 00004	D: 00044
	CODE: p	MEM: 089 (Y) 	A: 19707	C: 00005	D: 00045
	CODE: <	MEM: 29477 (%) 	A: 09833	C: 00006	D: 00046
i	CODE: v	MEM: 088 (X) 	A: 09833	C: 00007	D: 00047

./malbolgeconstant 47 10 9833
EUREKA! code=p pos=49 data=* (T)
EUREKA! code=p pos=48 data=v (!)
EUREKA! code=* pos=47 data=i (3)

Chegamos por fim ao programa desejado:

mirage@arda ~/malbolge-sdk-1.0 $ cat example.mbs
j*p<*p<*pp<voooooooooooooooooooooooooooooppo/joiv*
mirage@arda ~/malbolge-sdk-1.0 $ ./malbolgec example.mbs example.mb
mirage@arda ~/malbolge-sdk-1.0 $ cat example.mb
(&<`#9]~65YF876543210/.-,+*)('&%$#"!~}|{zsrwIYt3!T
mirage@arda ~/malbolge-sdk-1.0 $ ./malbolge example.mb
Oi

Vamos agora à prova dos nove: a arvorezinha! Note-se que esta arvorezinha tem \n na última linha, como manda o RFC. Tal como as arvorezinhas anteriores em malbolge, não usa ciclos. Ocupa 99 caracteres (incluí no SDK uma versão sem a última newline com apenas 95 caracteres). Como verão, usei dois incrementos ao D, porque a arvorezinha não coube em 40 caracteres de código. Ainda assim, foi necessário usar o próprio valor em 44, que é um < , para imprimir um caracter da árvore, e simultaneamente servir de input para o segundo “j”. Reparem:

./malbolgevalid 44 44
44:	037 (v)	054 (i)	055 (<)	073 (/)	089 (*)	090 (j)	112 (p)	118 (o)

O “< “, por coincidência a instrução de print, resulta no valor 55, bastante simpático para o novo início dos dados. Atenção que, na listagem que se segue, o nº de linha N corresponde à posição de memória N-1. Sem mais paleio, aqui vai:

j ; avançar dados para o 41
o ; estes nops são para diminuir o resultado do próximo j
o
o
j ; avançar ainda mais, para o 56
p ; calcular "*" com "pp" com os dados 56 e 57
p
< ; imprime-o
* ; agora calcula o "\n" com dados do 59 ao 61
p
p
< ; imprime-o
* ; calcula novamente "*"
p
p
< ; desta vez imprime dois
<
p ; calcula "\n" de novo e assim sucessivamente até completar a arvorezinha
p
<
p
p
<
<
<
p
p
<
*
p
p
<
<
<
<
p
p
<
p
p
< ; print dos últimos 5 asteriscos
< ; posição de dados usado no D=56, e simultaneamente faz um print ;-)
<
<
<
*
p
p
< ; printa "\n" no fim, como manda o RFC, seus batoteiros
v ; fim do código
o ; nops a encher choriço até começarem os dados úteis
o
o
o
o
o
j ; dados do cálculo do primeiro "*"
*
o ; nop para compensar o "<" desse "*"
i ; dados do cálculo do primeiro "\n"
p
i
o ; nop idem
i ; etc, dados dos cálculos até ao final
<
*
o
o
o
p
o
*
<
o
o
o
o
i
o
/
p
j
o
o
o
o
*
o
o
p
j
o
o
o
o
o
p
/
j

Rapidamente, a versão prêt-à-porter:

(CBA$98}54Xy10TS-,P*)MLK%$Hi!~DCBAyx>vu;:987Xnm3~ponmlkNLh'`%d##D`_^W>yYXWVsT&L5PONM/KJC,GFEDC<r$

E assim chegamos ao fim do post. Espero que tenham perdido tanto tempo a lê-lo como eu perdi a escrevê-lo. ;-) Feliz programação em malbolge!

Este post é dedicado à memória do fravia, o Deus do cracking, e do Vasco Granja, o Rei do lulz importado da antiga checoslováquia.