Mais um blog inútil.

Janeiro 4, 2012

Arvorezinha em perl

Filed under: Arvorezinha,Serious Business,Useless — korn @ 16:54

Como a ultima arvorezinha era muito grande, vai aqui uma versão reduzida da v2!

#!/usr/bin/perl
$i=$ARGV[0];map{$a.=" "x($i-$_-1)."*"x($_*2+1)."\n";$b.=" "x($i/2)."#"x($i%2?$i:$i-1)."\n"if($i-$_+1)%2}(0..$i-1);print$a.$b;

Dezembro 26, 2011

Como não ganhar um iPad 2

Filed under: Coding,Cracking,Drama,Fail,Useless — falso @ 3:09

Boas noites,

Venho hoje aqui blogar a minha tristeza ao saber que a password do concurso para ganhar um iPad 2 no Pplware é alta string manhosa.

Vou começar então a falar da minha jornada para tentar ganhar isto...

Comecei por andar a procurar qual era o software que usava a extensão "xcon", um amigo meu descobriu entretanto que era o Conceal, um software todo manhoso em .NET.
Experimentei umas passwords básicas no programa só para ver se advinhava antes de fazer alguma coisa mais complexa, mas o programa era tão manhoso que se usasse uma password invalida ele crashava, então desisti dessa aproximação.

Falei com um amigo meu todo cromo da criptografia e ele automagicamente disse-me que aquilo eram blocos de TripleDES CBC.

Caso os senhores do Pplware não saibam, bruteforcar TripleDes não é assim pêra doce, citando a Wikipedia (está sempre correcta :-P):

"Deep Crack was designed by Cryptography Research, Inc., Advanced Wireless Technologies and the EFF. ...  Advanced Wireless Technologies built 1856 custom ASICDES chips housed on 29 circuit boards of 64 chips each. The boards were then fitted in six cabinets and mounted in a Sun-4/470 chassis. ... The entire machine was capable of testing over 90 billion keys per second. It would take about 9 days to test every possible key at that rate. On average, the correct key would be found in half that time."

Entretanto, eu sem sabendo que era alta string manhosa, e pensando que a password poderia ser o titulo de algum dos produtos entre 50 e 100€, fiz um scriptzinho que ia la parsar o site do OfficeLan e sacar de la os títulos todos (faz uso do phpQuery):

<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
</head>
<?php

require('phpQuery.php');

// batota, tem o total hardcoded
for($start = 0; $start <= 120; $start = $start + 20) {
	$url = 'http://shop.officelan.pt/pesquisa.html?start='.$start;
	$fields = array(
		'yagendoo_price_min'=>urlencode('50'),
		'yagendoo_price_max'=>urlencode('100'),
		'option'=>urlencode('com_yagendoo_vmsearch'),
	);
	$fields_string = "";
	foreach($fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; }
	rtrim($fields_string,'&');

	$ch = curl_init();
	curl_setopt($ch,CURLOPT_URL,$url);
	curl_setopt($ch,CURLOPT_POST,count($fields));
	curl_setopt($ch,CURLOPT_POSTFIELDS,$fields_string);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

	$result = curl_exec($ch);

	curl_close($ch);

	$doc = phpQuery::newDocument($result);
	phpQuery::selectDocument($doc);

	foreach(pq('span.yagendoo_searchresult_title a') as $product) {
			echo $product->nodeValue;
			echo '<br/>';
	}
}

E com o resultado fui experimentar o seguinte:

<?php

$produtos = array(
  'MikroTik RouterBOARD 411U (Level 4) - RB411U - RB/411U',
  'MikroTik RouterBOARD 411AR (Level 4) - RB411AR - RB/411AR',
  ...
  'ANTENA INTERLINE SECTORIAL 12dBi/2.4GHz Mimo',
  'ANTENA INTERLINE SECTORIAL 12dBi/2.4GHz',
  'Presente', 'Presente_Natal', 'Presente_Natal.zip',
  'OfficeLan', 'pplware', 'natal',
);

$string = file_get_contents('nome_produto_offiLan.xcon');
foreach($produtos as $key) {
	$output = mcrypt_decrypt( MCRYPT_3DES , $key , $string , 'cbc');
	echo '<h4>'.$key.'</h4>';
	echo '<pre>'.$output.'</pre>';
}

Mas sem grandes resultados... Então foi que me disseram que o TripleDES CBC alem de uma key na cifra usa também outra variável, que é o IV (Initialization vector), que pelo que entendi, são dados usados para "inicializar" a cifra.
Então o tal programa tinha de usar algum algoritmo para "gerar" um IV a partir da nossa password ou então usava algum valor fixo, decidi tentar descobrir.

Andei a procura de técnicas sobre reversing a cenas .NET e achei alta programinha hacker, .NET Reflector.
Neste belo software, abre-se um executável .NET e ele escreve mais ou menos o codigo desse executável em belíssimo código C#, só os nomes de algumas funções e variáveis é que se perdem...

Então abri o executável do Conceal, e andei la a vasculhar, e no Form2 achei lá uma função chamada tdes_decrypt() e vi que era a função desejada, mas que a key e o IV eram calculados noutro lado, então com outra feature bonita deste programa, fiz Analyze nessa função e vi que era chamada pela func2(), e BINGO nessa achei o algoritmo que era usado para a key e o IV.

reflector 300x239 Como não ganhar um iPad 2

Decidi então criar um novo projecto C# no Visual Studio, onde iria utilizar o código do programa gerado pelo Reflector, mas podia-lhe alimentar um array gigante de passwords (a lista de produtos anterior). E com poucas dificuldades consegui mete-lo a funcionar, mas rapidamente vi que nenhuma das passwords que estava tentar usar era a correcta.
Sem saber mais o que tentar, rapidamente desisti...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace unconceal
{
    class Program
    {
        public static void tdes_decrypt(String inputfile, String outputfile, byte[] key, byte[] IV)
        {
            long bytecount = 0L;
            long fread_len = 0L;
            int numbytes = 0;
            byte[] mybuffer = new byte[0xf4241];
            TripleDESCryptoServiceProvider tdesProvider;
            CryptoStream cryptStream;

            FileStream freader = new FileStream(inputfile, FileMode.Open, FileAccess.ReadWrite);
            FileStream fwriter = new FileStream(outputfile, FileMode.OpenOrCreate, FileAccess.Write);

            fread_len = freader.Length;

            tdesProvider = new TripleDESCryptoServiceProvider();
            tdesProvider.Key = key;
            tdesProvider.IV = IV;
            tdesProvider.Padding = PaddingMode.Zeros;

            cryptStream = new CryptoStream(fwriter, tdesProvider.CreateDecryptor(), CryptoStreamMode.Write);
            int num = 0;
            num = inputfile.Length - 1;

            while (bytecount < fread_len)
            {
                numbytes = freader.Read(mybuffer, 0, 0xf4240);
                cryptStream.Write(mybuffer, 0, numbytes);
                bytecount += numbytes;
            }

            while (bytecount % 8 != 0)
            {
                cryptStream.WriteByte(0);
                bytecount++;
            }

            freader.Close();
            cryptStream.Close();
        }

        static void Main(string[] args)
        {
            List<string> passwords = new List<string>(new string[]
	        {
                "MikroTik RouterBOARD 411U (Level 4) - RB411U - RB/411U",
                "MikroTik RouterBOARD 411AR (Level 4) - RB411AR - RB/411AR",
                ...
                "ANTENA INTERLINE SECTORIAL 12dBi/2.4GHz Mimo",
                "ANTENA INTERLINE SECTORIAL 12dBi/2.4GHz",
                "Presente",
                "Presente_Natal",
                "Presente_Natal.zip",
                "OfficeLan",
                "pplware",
                "natal",
                "Natal",
                "iPad2",
                "Natal 2011",
                "PPLWARE.COM",
                "EBA428ECA16691133FA946FED56AF824E8527BB6",
                "_F2Liz12!" // password correct, mas só agora :(
        	});

            int x = 0;
            foreach (String for_keyf1 in passwords)
            {
                String password = for_keyf1.ToString();

                byte[] bytes = new byte[0x2710];
                int charIndex = 0;
                int length = for_keyf1.Length;
                byte[] buffer2 = new byte[length + 1];
                int index = 0;
                int num4 = 0;
                int num7 = length - 1;
                for (index = 0; index <= num7; index++)
                {
                    num4 += 3;
                    buffer2[index] = (byte)(Convert.ToInt64(for_keyf1[index]) + (index + num4));
                    num4--;
                }
                new ASCIIEncoding().GetBytes(Encoding.ASCII.GetString(buffer2), charIndex, Encoding.ASCII.GetString(buffer2).Length, bytes, charIndex);
                byte[] buffer3 = new SHA1CryptoServiceProvider().ComputeHash(bytes);

                byte[] key = new byte[24];
                key[0] = buffer3[2];
                key[1] = buffer3[6];
                key[2] = buffer3[0x12];
                key[3] = buffer3[0x10];
                key[4] = buffer3[0x13];
                key[5] = buffer3[1];
                key[6] = buffer3[9];
                key[7] = buffer3[7];
                key[8] = buffer3[14];
                key[9] = buffer3[3];
                key[10] = buffer3[8];
                key[11] = buffer3[0x11];
                key[12] = buffer3[10];
                key[13] = buffer3[15];
                key[14] = buffer3[0];
                key[15] = buffer3[11];
                key[0x10] = buffer3[9];
                key[0x11] = buffer3[4];
                key[0x12] = buffer3[0x12];
                key[0x13] = buffer3[5];
                key[20] = buffer3[11];
                key[0x15] = buffer3[2];
                key[0x16] = buffer3[0x13];
                key[0x17] = buffer3[0];

                byte[] iv = new byte[8];
                iv[0] = buffer3[2];
                iv[1] = buffer3[6];
                iv[2] = buffer3[0x12];
                iv[3] = buffer3[0x10];
                iv[4] = buffer3[0x13];
                iv[5] = buffer3[1];
                iv[6] = buffer3[9];
                iv[7] = buffer3[7];

                tdes_decrypt(@"C:\Users\falso\Documents\Visual Studio 2010\Projects\unconceal\unconceal\nome_produto_offiLan.xcon",
                    @"C:\Users\falso\Documents\Visual Studio 2010\Projects\unconceal\unconceal\" + x.ToString() + ".txt",
                    key, iv);

                string text = System.IO.File.ReadAllText(@"C:\Users\falso\Documents\Visual Studio 2010\Projects\unconceal\unconceal\" + x.ToString() + ".txt");
                System.Console.WriteLine("String = {0}", text);
                x++;
            }
        }
    }
}

Hoje dia 26 de Dezembro, lembrei-me de ir ver qual era afinal a password do concurso, e foi então que descobri que era "_F2Liz12!", adicionei essa string ao meu programinha em C# e não é que funcionou?

unconceal Como não ganhar um iPad 2

Download do projecto unconceal (belo nome :-P) para Visual Studio, para caso alguém esteja interessado em brincar mais com isto. Não esquecer os caminhos que estão hardcoded ao chamar a função tdes_decrypt().

Acho que para a próxima os senhores do Pplware deviam fazer concursos mais bem pensados, onde ganhe quem acha o resultado final, e não quem escolhe um produto à sorte que esteja no intervalo de preços dito inicialmente (dor de corno).

Um abraço e até à próxima!

Dezembro 22, 2011

Ano do Linux no Desktop

Filed under: Useless — madinfo @ 15:33

Faltam apenas 10 dias!!!

Novembro 30, 2011

Quake 1

Filed under: Uncategorized — madinfo @ 14:17

Tenho que blolar sobre isto... Encontra-se um server de Quake 1 Shareware a correr com CRMOD (bons velhos tempos do quake.telepac.pt em que tínhamos que fazer reconnect 50 mil vezes pois o server só tinha 16 slots), para quem quiser jogar pode sacar 17 magnificas megas de jogo em cadaval.net/quake e entrar no quake.cadaval.net para jogar...

Outubro 20, 2011

Novas arvorezinhas (1.0 e 2.0) em Perl

Filed under: Arvorezinha,Coding,Serious Business,Useless — falco @ 16:05

A arvorezinha em Perl que existe, não desenha a arvorezinha toda, com código escrito por quem faz o código da arvorezinha. Na minha opinião isso não respeitava o standard. Como tal achei que devia fazer uma arvorezinha como deve ser em Perl:

A versão 1.0:

print "*"x$_,"\n" for(1 .. 5);

Para este não ser um post demasiado parvo. Resolvi fazer uma arvorezinha em versão 2.0. em Perl. Inspirei-me no código do falso, mas não consegui resolver o bug do tronco (também não tentei muito):

use 5.010;
use strict;
use warnings;

# I'm not playing Perl Golf!

our $RAMO = "*";
our $ESPACO = qq/ /;
our $TRONCO = "#";

run();

sub run {
    my $altura = $ARGV[0] || 0;

    desenha_ramos($altura);
    desenha_tronco($altura);
}

sub desenha_ramos {
    my ($altura) = @_;

    for(1 .. $altura) {
        my $linha = desenha_elemento(1, ($altura - $_), $ESPACO);
        $linha .= $RAMO for( 1 .. (($_ * 2) - 1) );
        say $linha;
    }
}

sub desenha_tronco {
    my ($altura) = @_;

    my $largura = ($altura * 2) - 1;
    $altura = ($altura / 2);

    for(1 .. $altura) {
        if($altura % 2) {
            my $sombra = desenha_elemento(0, (($largura / 4) - 1), $ESPACO);
            my $tronco = desenha_elemento(0, (($largura / 2) - 1), $TRONCO);

            say $sombra.$tronco;
        }
        else {
            my $sombra = desenha_elemento(0, ($largura / 4), $ESPACO);
            my $tronco = desenha_elemento(1, (($largura / 2) - 1), $TRONCO);

            say $sombra.$tronco;
        }
    }
}

sub desenha_elemento {
    my ($min, $max, $tipo_elemento) = @_;

    my $elemento = $tipo_elemento;
    $elemento = sprintf("%s%s", $elemento, $tipo_elemento) for($min .. $max);

    return $elemento;
}

Julho 6, 2011

Ementalicious

Filed under: Coding,Serious Business — falco @ 22:22

Quando revi o post do falso sobre as ementas das cantinas da UNL, fiquei aborrecido com o entusiasmo do falso em relação ao BeautifulSoup.

É um facto que o Python é gay. E por isso resolvi mostrar ao falso, como é que se programa com linguagens de homem. Mas entretanto, resolvi que em vez de fazer o mesmo que o falso, iria antes criar um saite, que para além de disponibilizar as ementas em HTML, também iria disponibilizar em RSS e JSON.

O saite vai ser desenvolvido por fases... Nesta fase o saite apenas está a mostrar numa página o mesmo conteúdo que a página das cantinas da UNL.

O site está a ser feito em Perl com Mojo::Lite, que é uma versão aligeirada e simplificada da framework para web MojoLicious

O site são apenas dois ficheiros, como eu não tenho skills de Word Press e não consigo colocar aqui o código todo bonitinho meti tudo numa tarball, que vocês podem obter aqui.

Notas importantes:

  • O código está licenciado com a GNU General Public License version 3.
  • Para correr a aplicação é necessário ter Perl e Mojolicious instalado.
  • Ao contrário do que o falso pensa, usar expressões regulares, não é mau e não devemos ter problemas em utilizar quando úteis.

Querido falsinho se quiseres dar-te ao trabalho de meter aqui o código todo bonitinho, eu dava-te dois beijinhos e um abraço.

Eis o screenshot da página:

CapturaEcra 1 300x187 Ementalicious

Imagem da página com os menus das várias faculdades

Julho 4, 2011

BrainFucker

Filed under: Arvorezinha,Coding,OSX,Useless — falso @ 23:54

Ora viva!!!

Há uns tempos atrás vi nas internets um projecto verdadeiramente inutil, do calibre das coisas que vão aparecendo por aqui, chamado Brainfuck Developer, que é um IDE para programar e debugar Brainfuck.
Senti-me um pouco cabisbaixo ao ver que alguém tinha tentado chegar aos meus calcanhares em nível de inutilidade, portanto decidi por mãos à obra para me redimir. E então à boa maneira dos projectos open sores, decidi ripar a ideia, e comecei a desenvolver o BrainFucker, um IDE de Brainfuck para OSX!
Claro que isto não foi assim feito do pé-pra-mão, até porque não sabia nada de Objective-C nem de Cocoa, mas com o tempo, e pachorra aos fins de semana e tal, finalmente está disponível a versão 0.1.

BrainFuckerScreenSnapz001 BrainFucker

Suporta correr programas de Brainfuck que só usem OUTPUT (.), o INPUT (,) ainda não está implementado, mas provavelmente vai ser uma DialogBox a pedir o valor.
Possibilidade de correr programas STEP-BY-STEP para ver as alterações dos valores e posição do apontador de memoria.

BrainFuckerScreenSnapz002 BrainFucker
BrainFuckerScreenSnapz003 BrainFucker

Espero que isto sirva para alguém aprender Brainfuck, ou mesmo para aprender como não programar em Objective-C.
O codigo está disponivel no github. E um build experimental está disponível: BrainFucker.zip - Apenas testado em OSX 10.4 e 10.5 POWERPC (só para quem ama). Agradeço a alguém que reporte se também funciona em Intels.

Cumprimentos, e um bem haja!

Fevereiro 22, 2011

Adeus AquaPC, olá PCDiga

Filed under: Serious Business — mirage @ 19:07

Carta aberta à AquaPC

Resumindo a história, encomendei uma placa gráfica em Novembro, que ficou em espera sem darem qualquer acompanhamento, e foi cancelada a 31 de Janeiro por alegada falta de stock. Dias depois, o stock apareceu, 40 euros mais caro do que o valor quando fiz a encomenda. Pedi para reabrirem a encomenda. Não obtive resposta durante uma semana e eis que a placa deixou de estar em stock.

De notar que o stock terminou no dia seguinte a eu vos ter telefonado, como último recurso, e a única resposta que obtive foi "tem de ser o chefe a decidir". E decidiu muitíssimo bem. Isto soma-se a mails anteriores que vos enviei a pedir informações (não relativas a esta encomenda) que também não foram respondidos.

É incrível como a AquaPC antigamente era uma referência na qualidade do serviço e atendimento, e hoje parece ir no caminho duma Chip7 ou semelhante em que os clientes deixam de ser pessoas e passam a ser números de encomenda.

É com tristeza que vos informo que perderam um cliente fiel desde 2005, que não tinha problemas em pagar um extra (sim, nunca tiveram os preços mais competitivos) para ter garantia de qualidade.

Adeus AquaPC, olá PCDiga.

--
Tiago Sousa

Dezembro 14, 2010

Árvorezinha 2.0

Filed under: Arvorezinha,Assembly,Coding,Drama,Useless — falso @ 1:07

Ora viva amigos!

Há uns tempos no trabalho um colega meu começou a fazer pouco das minhas árvorezinhas, a dizer que só eram meia árvore, e que eu devia era de fazer uma árvore completa. Eu fiquei SENTIDO com tal AFRONTA, e fiquei a MATUTAR sobre isso, até que decidi por mãos à obra, e criar a Árvorezinha 2.0.

Primeiro decidi faze-la em C porque é a linguagem STANDARD!

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
        int altura;
        int i,x;
        int lul;

        if (argc < 2)
        {
                return(0);
        }
        altura = atoi(argv[1]);

        /* Ciclo da altura da Arvore */
        for(i = 1; i <= altura; i++)
        {

                /*
                        Numero de Espaços
                        Começa em 0 porque existem linhas com 0 espaços
                        Algoritmo: altura - linha
                */
                for(x = 0 ; x < (altura-i); x++)
                {
                        putchar(' ');
                }

                /*
                        Numero de Asteriscos
                        Começa em 1 porque não existem linhas sem *
                        Algoritmo: (2 * linha) - 1
                */
                for (x = 1; x <= (2*i)-1; x++)
                {
                        putchar('*');
                }

                putchar('\n');
        }

        /* Largura ultima linha */
        lul = (altura * 2) - 1;

        /*
                Ciclo da altura do tronco
                Algoritmo: altura / 2
        */
        for(i = 1; i <= (altura/2); i++)
        {
                /*
                        Por causa do ASCII nao permitir meio char
                        tive de fazer duas implementacoes diferentes,
                        uma para quando o  valor da altura do tronco
                        e' par, e outra para quando e' impar.
                */
                if (altura % 2)
                {
                        /* Impar */

                        /*
                                Numero de Espaços
                                Algoritmo: (lul / 4) - 1
                        */
                        for(x = 0; x <= (lul/4)-1; x++)
                        {
                                putchar(' ');
                        }

                        /*
                                Numero de # (tronco)
                                Algoritmo: lul / 2
                        */
                        for(x = 0; x <= (lul/2); x++)
                        {
                                putchar('#');
                        }

                        putchar('\n');
                }
                else
                {
                        /* Par */

                        /*
                                Numero de Espaços
                                Algoritmo: lul / 4
                        */
                        for(x = 0; x <= (lul/4); x++)
                        {
                                putchar(' ');
                        }

                        /*
                                Numero de # (tronco)
                                Algoritmo: (lul / 2) - 1
                        */
                        for(x = 0; x <= (lul/2)-1; x++)
                        {
                                putchar('#');
                        }

                        putchar('\n');
                }
        }

}

A pedido de muitas famílias, foi me imposta a tarefa de fazer um RFC da nova árvorezinha, mas não tenho muito jeito para escrever algoritmos em pseudo-código. Então tal obra heróica fica para o caro leitor, façam me um baseado no código em C e enviem-me!

Segundo as próprias leis já pré-estabelecidas da Árvorezinha, tem de existir uma implementação em Assembly! Então não podia cá faltar a minha versão em x86 Assembly.


;
; Arvorezinha 2.0
; x86 Assembly
; Copyright (C) 2010 Pedro de Oliveira
; http://blol.org
;
; this assembly can never fail
;
        BITS            32
        GLOBAL          main

; Vou usar duas funcoes da libc para o codigo nao crescer gigantescamente
; com rotinas que nao interessam nada para aqui.
        EXTERN          atoi
        EXTERN          printf

; Definicao das Variaveis
SECTION         .data

        argc    dd      0
        argv    dd      0

        erro    db      "ERRO! Executar: %s <altura da arvore>",10,0

        card    db      '#'
        newl    db      0xa
        aste    db      '*'
        espa    db      ' '

        altura  dd      0
        i       dd      1
        x       dd      0
        lul     dd      0

; He cometh!
SECTION         .text

main:
        pop     eax                     ; Ignorar...

        pop     eax                     ; Saca o argc da Stack
        mov     dword [argc], eax       ; Guarda o valor na variavel argc

        pop     ebx                     ; Saca a posicao de memoria do
                                        ; argv[0] da Stack
        mov     eax, dword [ebx]        ; Mete a posicao em EAX
        mov     [argv], eax             ; Guarda-a em argv

        add     ebx,0x4                 ; Salta 4 bytes para a frente
                                        ; para o argv[1] ficar em EBX

        mov     eax, [argc]             ; Mete o argc em EAX
        cmp     eax, 0x2                ; Verifica se e' diferente de 2
        jne     jafoste                 ; Se for sai com erro

        push    dword [ebx]             ; Mete o valor de argv[1] na Stack
        call    atoi                    ; Corre o atoi com esse valor
        mov     [altura], eax           ; O resultado fica em EAX, guarda
                                        ; na variavel altura

ciclo_linhas:
        ; INICIO - CICLO DAS LINHAS DA ARVORE
        mov     eax, [i]                ; i em EAX
        mov     ebx, [altura]           ; altura em EBX

        cmp     ebx, eax                ; Compara
        jb      prepara_tronco          ; i > altura ? proximo passo
        mov     dword [x], 0            ; Mete x a 0

ciclo_espacos:
        ; INICIO - CICLO DE ESPAÇOS ANTES DOS ASTERISCOS
        mov     eax, [x]                ; x em EAX

        ; pretende-se (altura - i) em EBX
        mov     ebx, [altura]           ; altura em EBX
        mov     ecx, [i]                ; i em ECX
        sub     ebx, ecx                ; EBX - ECX

        cmp     ebx, eax                ; Compara
        jbe     prepara_asteriscos      ; x >= (altura - i) ? proximo passo

        push    espa                    ; Espaço
        call    print                   ; write()

        call    incrementa_x
        jmp     ciclo_espacos           ; Volta para o inicio do ciclo
        ; FIM - CICLO DE ESPAÇOS ANTES DOS ASTERISCOS

prepara_asteriscos:
        mov     dword [x], 1            ; Mete x a 1

ciclo_asteriscos:
        ; INICIO - CICLO DE ASTERISCOS (ARVORE)
        mov     ebx, [x]                ; x em EBX

        ; pretende-se (2 * i) - 1 em EAX
        mov     eax, 2                  ; 2 em EAX
        mov     ecx, [i]                ; i em ECX
        mul     ecx                     ; Multiplica EAX por ECX
        dec     eax                     ; Subtrai 1 a EAX

        cmp     eax, ebx                ; Compara
        jb      fim_ciclo_linhas        ; x > (2*i)-1 ? proximo passo

        push    aste                    ; Asterisco
        call    print                   ; write()

        call    incrementa_x
        jmp     ciclo_asteriscos        ; Volta para o inicio do ciclo
        ; FIM - CICLO DE ASTERISCOS (ARVORE)

fim_ciclo_linhas:
        push    newl                    ; Newline
        call    print                   ; write()

        call    incrementa_i
        jmp     ciclo_linhas            ; Volta para o inicio
        ; FIM - CICLO DAS LINHAS DA ARVORE

prepara_tronco:
        ; pretende-se (altura * 2) - 1 em EAX
        mov     eax, [altura]           ; altura em EAX
        mov     ecx, 2                  ; 2 em ECX
        mul     ecx                     ; Multiplica EAX por ECX
        dec     eax                     ; Subtrai 1 a EAX
        mov     dword [lul], eax        ; Guarda a largura da ultima linha
                                        ; em lul

        mov     dword [i], 1            ; Mete o i a 1

ciclo_linhas_tronco:
        ; BEGIN - CICLO DAS LINHAS DO TRONCO
        mov     ecx, [i]                ; i em ECX

        ; pretende-se (altura / 2)
        mov     eax, [altura]           ; altura em EAX
        shr     eax, 1                  ; divide por 2

        cmp     ecx, eax                ; Compara ECX com EAX
        jg      sair                    ; i > (altura / 2) ? Adeus!

        mov     eax, [altura]           ; altura em EAX
        test    eax, 1
        je      pc_tronco_par_espacos   ; e' par?

; --------------------------- IMPAR -------------------------------
pc_tronco_impar_espacos:
        mov     dword [x], 0            ; Mete-se x a 0

c_tronco_impar_espacos:
        ; INICIO - CICLO DOS ESPAÇOS ANTES DO TRONCO (IMPAR)
        mov     ecx, [x]                ; x em EAX

        ; pretende-se (lul / 4) - 1 em EAX
        mov     eax, [lul]              ; lul em EAX
        shr     eax, 2                  ; Divide por 4
        dec     eax                     ; Subtrai 1

        cmp     eax, ecx                ; Compara
        jb      pc_tronco_impar_cardinal; x > (lul/4)-1 ? Next!

        push    espa                    ; Espaço
        call    print                   ; write()

        call    incrementa_x
        jmp     c_tronco_impar_espacos  ; Volta para o inicio
        ; FIM - CICLO DOS ESPAÇOS ANTES DO TRONCO (IMPAR)

pc_tronco_impar_cardinal:
        mov     dword [x], 0            ; Mete x a 0

c_tronco_impar_cardinal:
        ; INICIO - CICLO DOS CARDINAIS DO TRONCO (IMPAR)
        mov     ecx, [x]                ; x em ECX

        ; pretende-se (lul / 2) em EAX
        mov     eax, [lul]              ; lul em EAX
        shr     eax, 1                  ; Divide por 2

        cmp     eax, ecx                ; Compara
        jb      fim_ciclo_linhas_tronco ; x > (lul/2) ? Next!

        push    card                    ; Cardinal
        call    print                   ; write()

        call    incrementa_x

        jmp     c_tronco_impar_cardinal ; Volta para o inico
        ; FIM - CICLO DOS CARDINAIS DO TRONCO (IMPAR)

; --------------------- FIM IMPAR --------------------------------

; ----------------------------- PAR ------------------------------
pc_tronco_par_espacos:
        mov     dword [x], 0            ; Mete x a 0

c_tronco_par_espacos:
        ; INICIO - CICLO DOS ESPAÇOS ANTES DO TRONCO (PAR)
        mov     ecx, [x]                ; x em ECX

        ; pretende-se (lul / 4) em EAX
        mov     eax, [lul]              ; lul em EAX
        shr     eax, 2                  ; divide por 4

        cmp     eax, ecx                ; Compara
        jb      pc_tronco_par_cardinal  ; x > (lul / 4) ? Next!

        push    espa                    ; Espaço
        call    print                   ; write()

        call    incrementa_x
        jmp     c_tronco_par_espacos    ; Volto para o inico do ciclo
        ; FIM - CICLO DOS ESPAÇOS ANTES DO TRONCO (PAR)

pc_tronco_par_cardinal:
        mov     dword [x], 0            ; Mete x a 0

c_tronco_par_cardinal:
        ; INICIO - CICLO DOS CARDINAIS DO TRONCO (PAR)
        mov     ecx, [x]                ; x em ECX

        ; pretende-se (lul / 2) - 1 em EAX
        mov     eax, [lul]              ; lul em EAX
        shr     eax, 1                  ; Divide por 2
        dec     eax                     ; Subtrai 1

        cmp     eax, ecx                ; Compara
        jb      fim_ciclo_linhas_tronco ; x > (lul / 2) - 1 ? uhuhuh

        push    card                    ; Cardinal
        call    print                   ; write()

        call    incrementa_x

        jmp     c_tronco_par_cardinal   ; Volta para o inicio do ciclo
        ; FIM - CICLO DOS CARDINAIS DO TRONCO (PAR)
; ----------------------- FIM PAR ----------------------------------

fim_ciclo_linhas_tronco:
        push    newl
        call    print

        call    incrementa_i
        jmp     ciclo_linhas_tronco     ; Volta para o inico do ciclo
        ; FIM - CICLO DAS LINHAS DO TRONCO

jafoste:
        mov     eax, [argv]             ; Mete o apontador de argv em EAX
        push    dword eax               ; Mete o endereço de argv na Stack
        push    dword erro              ; Mete o endereço da String na Stack
        call    printf                  ; Escreve no ecra!

sair:
        mov     ebx,0x0                 ; valor de saida
        mov     eax,0x1                 ; sys_exit
        int     0x80

print:
        mov     ecx,[esp+4]             ; Mete o argumento em ECX
        mov     edx,1                   ; Length
        mov     ebx,1                   ; stdout
        mov     eax,4                   ; sys_write
        int     0x80
        ret

incrementa_x:
        mov     eax, [x]
        inc     eax
        mov     dword [x], eax
        ret

incrementa_i:
        mov     eax, [i]
        inc     eax
        mov     dword [i], eax
        ret

E aqui vai a prova dos nove:

falso@lemonparty:~/src/zbr$ make
rm -f arvore2 arvore.o
nasm -f elf arvore.asm -o arvore.o
gcc -g -o arvore2 arvore.o
falso@lemonparty:~/src/zbr$ ./arvore2 4
   *
  ***
 *****
*******
  ###
  ###
falso@lemonparty:~/src/zbr$ ./arvore2 5
    *
   ***
  *****
 *******
*********
  #####
  #####
falso@lemonparty:~/src/zbr$

Espero lançar futuramente um género de Unit Tests, para testar as varias implementações da Arvorezinha 2.0 que possam surgir, para ver se cumprem o standard ou não.

Espero que tenham gostado do post, até à proxima, fiquem bem e joguem muito! Chuuuuuuack!

Dezembro 11, 2010

Programação Funcional com Perl (parte I, revista e aumentada)

Filed under: Uncategorized — falco @ 22:23

Começo com este artigo uma série de artigos relacionados com a Programação Funcional com Perl.

Com estes artigos pretendo demonstrar algumas das capacidades do Perl para utilização com o paradigma de Programação Funcional. Não pretendo ensinar nem as bases da Programação Funcional, nem do Perl. Vou abrir excepções quando se tratarem de aspectos mais avançados e/ou muito relacionados com a própria Programação Funcional.
O objectivo destes artigos também não passa por colocar os leitores a fazer Programação Funcional pura, mas sim demonstrar as funcionalidades de Programação Funcional que o Perl dispõe.
Isto pode ser utilizado para praticar um estilo de programação com muitas semelhanças ao do paradigma da Programação Funcional, ou então simplesmente utilizar alguns "truques" para melhorar os programas que o leitor desenvolve.
A compreensão elementar do Perl e da Programação Funcional é um requisito para a compreensão destes artigos contudo tudo será explicado de forma a permitir que um principiante possa compreender tudo e quem tiver dúvidas poderá contactar-me para as esclarecer (falco @ Portugal a Programar).

Uma das principais características do Perl, em seguimento do mantra da sua comunidade («There's more than one way to do it»), é não obrigar o programador a utilizar. Nem escolher um paradigma de programação, mas sim permitir ao programador utilizar o que quiser, quando quiser. Fica ao cuidado dele ter os cuidados necessários para que o código seja útil e siga boas práticas de programação. Há quem concorde com esta filosofia, há quem não concorde, mas a filosofia do Perl e da sua comunidade não é o âmbito deste artigo.
Um dos muitos paradigmas de programação que são possíveis utilizar em Perl é a Programação Funcional. Contudo não cabe a este artigo explicar a Programção Funcional. Por isso sugiro que antes de prosseguirem na leitura deste artigo leiam pelo menos uma curta explicação do que é Programação Funcional (os primeiros parágrafos do artigo da Wikipedia sobre Programação Funcional por exemplo). Como já há vários artigos sobre Programação Funcional em diversas linguagem como Python, Scheme, Haskell (recomendo que os leiam), deixo aqui a sugestão a alguém, para redigir um artigo teórico e agnóstico (em relação a linguagens de programação) sobre Programação Funcional.

Algumas das funcionalidades elementares das linguagens funcionais são asfunções anónimas e as closures (mas não só). O Perl tem ambas as features e é por aí que vamos começar esta série de artigos.

Começamos pelas funções anónimas.
Quando declaramos uma função em Perl, a prática mais comum é atribuir-lhe um nome na sua declaração. As funções anónimas não têm esse nome. Por isso, quando as queremos invocar, temos que utilizar uma referência para essa função dita anónima.

Em Perl a declaração de funções anónimas é simples. Para declarar uma função utiliza-se uma outra função, a função sub e para declarar uma função anónima também.
Para criar uma função anónima invoca-se a função sub sem o parâmetro correspondente ao nome da função. E como retorno obtém-se um código de referência para a função declarada.

Uma das alterações que o Perl 5.12 trouxe foi uma função chamada say.
O say é basicamente um print que adiciona um newline à string que queremos imprimir (poupa-nos pelo menos 4 caracteres).

Qualquer uma das seguintes linhas de código vai imprimir isolado numa linha a string: «Hello world!»:

print "Hello world!\n";
print "Hello world!"."\n";
print "Hello world!", "\n";
say "Hello world!";

Para demonstrar a utilização de funções anónimas decidi criar uma implementação da função say, que antes da sua utilização permite definir como queremos que a string seja formatada:

sub say {   #definição da função say
    my ($string, $format) = @_;

    my $str = $format->($string);
    print $str;
}

my $format = sub {  #função anónima de formatação da string
        my $str = shift;
        return $str."\n";
    };

say("Hello world!", $format);

Explicação do código antecedente:

Primeiro é definida a função say, como definiríamos qualquer outra função em Perl.

Depois é definida a função anónima de formatação e atribui-se essa função a uma variável chamada format e que guarda uma referência para a função. A variável permite-nos ter uma forma de passar a função anónima para a função say.

Quais as utilidades das funções anónimas?

São diversas. E vão desde a modificação do comportamento de uma função, como no exemplo anterior, ou como nos mecânismos de callback, ou a criação de "dispach tables", como no exemplo que se segue:

my %lingua = (  #dispach table
        "pt" => sub { return "Olá mundo!" },
        "es" => sub { return "Hola mundo!" },
        "en" => sub { return "Hello world!" },
        "fr" => sub { return "Bonjour monde!" },
    );

sub dispach {
    my $l = shift;

    if(defined $lingua{$l} && exists $lingua{$l}) {
        my $str = $lingua{$l}->();
        print $str."\n";
    }
    else {
        print "Erro: lingua desconhecida!\n";
    }
}

dispach("fr");

Explicação do código antecedente:

Começou por ser definida uma hash table. Essa hash contém como chaves as opções válidas de línguas que queremos utilizar na impressão de uma saudação. E como valores, tem funções anónimas que imprimem uma saudação, na língua representada pela respectiva chave na hash table.

Em Perl há poderia ter sida implementada esta solução recorrendo a funções que não fossem anónimas. Mas isso traria imediatamente dois problemas: aumentaria a quantidade de código necessário e este artigo tem um limite de caracteres; não utilizaria funções anónimas, que é o objectivo deste exemplo ;). Para além de isso, o que é pretendido neste exemplo, é algo tremendamente simples que pode perfeitamente ser feito utilizando funções anónimas, sem se aumentar a dificuldade relevante da sua compreensão, quer como um todo, quer isoladamente.

A solução do exemplo, em vez de a utilização de um encadeamento maior de if-elsif, ou de um switch-case (também maior). Permite que a solução escale para mais opções de línguas, sem qualquer alteração e atinge melhor vários dos objectivos da utilização de funções na programação: conter/isolar os problemas de forma a simplificar a sua resolução, facilitar a compreensão do código.

No próximo, artigo vou falar da segunda das principais capacidades do Perl para a Programação Funcional de que falei antes, as closures.

(c) 2010 falco
Este artigo está licenciado de acordo com os termos da licença:
Atribuição - Uso Não-Comercial - Partilha nos Termos da Mesma Licença 2.5 que pode ser encontrada em: http://creativecommons.org/licenses/by-nc-sa/2.5/pt/legalcode
Em caso de dúvida, indisponibilidade de acesso à licença, ou se prentender algo que não seja permitido com esta licença, deverá contactar o autor no sentido de pedir exclarecimento, ou permissão para fazer o que pretende.

« Posts anteriores