WordPress bugado no tablet e mais

Por alguma razão bizarra, posts feito pelo tablet são salvos mais não publicados. Não faço a menor ideia do porque, e já tinham 4 posts acomulados. Vou liberá-los todos e tentar descobrir o porque.

Outra coisa, depois de mais de 2 anos, o pessoal ainda brinca com o apk  de placas de carro decodado do SinespCidadão. Se tiverem outros aplicativos similares, comentem e mandem o apk pro meu email que vou aproveitar minhas férias para postar os scripts para utilizar seus serviços!

o/

Advertisements

Bottle.py – a solução de um problema que nao deveria existir

Bottle( http://bottlepy.org/docs/dev/index.html ) é um micro web framwork em python leve e muito simples. O mais louco é que ele é distribuido em apenas um arquivo e não tem nenhuma dependencia, alem do interpretador python, o que facilita absurdamente o deploy em qualquer ambiente!!!
Ok, e dai?
Bem, vi muita gente fazendo deploy no raspberryPi de aplicações com front end web, usando apache, php, mysql, postgres, redis, ou até mesmo obscenidades como o tomcat. Por enquanto, isto não é punido com a morte, mas deve ser evitado a todo custo e se buscar leveza e simplicidade. Quando conheci o bottle, tive a certeza que isto que procurava!

Com suporte a templates, roteamento simplificado, gerenciamento de dados e um servidor built-in, não fica faltando nada para sua aplicação subir.

para instalar (deixar o .py no diretorio ou instalar via pip, com # pip install bottle) e ter várias ideias de uso, é muito bom perder um tempinho na página oficial, mas uma aplicação que me deixou emocionado foi fazer uma pseudo-API do GPIO para web, simples assim:

cat > webgpio.py << _EOF_
from bottle import route
import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BOARD)

@route('/le/<pino:int>')
def readgpio(pino):
    GPIO.setup(pino, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
    saida = GPIO.input(pino)
    return '<pinos><%d>%s</%d></pinos>' % (pino,str(saida),pino)

@route('/escreve/<pino:int>/<val:int>')
def writegpio(pino,val):
    GPIO.setup(pino, GPIO.OUT)
    if val==0 or val ==1:
        GPIO.output(pino, (GPIO.LOW , GPIO.HIGH)[val])
        return 'escrito %d no pino %d' % (val, pino)
    else:
        return 'valores validos para escrita: 0 e 1'
_EOF_

Rodando…

$ python webgpio.py 
Bottle v0.13-dev server starting up (using WSGIRefServer())...
Listening on http://localhost:8080/
Hit Ctrl-C to quit.

127.0.0.1 - - [19/Nov/2015 02:20:48] "GET /le/13 HTTP/1.1" 200 26
127.0.0.1 - - [19/Nov/2015 02:21:04] "GET /escreve/15/0 HTTP/1.1" 200 20
127.0.0.1 - - [19/Nov/2015 02:21:14] "GET /escreve/15/1 HTTP/1.1" 200 20

E fazendo os requests…

$ curl http://localhost:8080/le/13
<pinos><13>12.5</13></pinos>
$ curl http://localhost:8080/escreve/15/1
escrito 1 no pino 15
$ curl http://localhost:8080/escreve/15/2
valores validos para escrita: 0 e 1

Obviamente este código não é seguro nem otimizado, é apena um PoC. Mas ainda assim, este treco é lindo, leve e tem suporte a muita coisa. Se a interface de template dele não agradar, ele é facilmente integravel com o jinja2( http://jinja.pocoo.org/docs/dev/ ) e, principalmente para linux embarcado é o framework web definitivo(ok, isso é minha opinião pessoal, mas é o que realmente parece haha).

Lua + C + Python – Mais que uma combinação macabra, uma gambiarra sinistra

Antes de mais nada: Por que?
Eu precisava fazer deploy de uma shared library em um ARM com linux(um raspberry Pi) extremamente capado, e não podia instalar todas as tools de dev, pois estava no limite de espaço do cartão, devido a aplicação dele. Mas precisava desta shared lib para carregar no python, via ctypes, pois o mesmo usaria as informações extraidas do hardware pelo .c, interpretaria e daria ordens ao resto da eletrônica. Acontece que os parametros do C variavam muito, precisavam ser tunados constantemente. Foi daí que rolou a ideia do LUA.
A ideia foi boa? Bom, funcionou. E até que faz um pouco de sentido, de alguma forma haha

vamos la, o loader, em python, é simplezão:

$ cat > loader.py << _EOF_
import ctypes
biblioteca=ctypes.CDLL('./zoalib.so')
biblioteca.zoeira.restype = ctypes.c_int
biblioteca.zoeira.argtypes = [ctypes.c_int]
biblioteca.zoeira(5)
_EOF_

O loader em C:

$ cat >zoalib.c << _EOF_
#include "lua.h"
#include "lauxlib.h"

int zoeira(int a)
{
    double z;
    lua_State *L = lua_open();
    luaL_openlibs(L);
    luaL_loadfile(L, "zoeira.lua");	/* arquivo lua */
    lua_pcall(L, 0, 0, 0);		/* limpa a pilha */
    lua_getglobal(L, "modelamotor");	/* nome da função do lua */
    lua_pushnumber(L, 3);		/* envia primeiro argumento */
    lua_pushnumber(L, 4);		/* envia segundo argumento */
    lua_pcall(L, 2, 1, 0);
    z = lua_tonumber(L, -1);		/* pega resultados */
    printf("Resultado: %f\n",z);
    lua_pop(L, 1);
    lua_close(L);
    return (a+1);
}
_EOF_

E o script em lua:

$ cat > zoeira.lua << _EOF_
function modelamotor (param1, param2)
    return math.sqrt(param1^2 + param^2)
end
_EOF_

Agora, vamos compilar com estes poucos parametros e rodar haha:

$ gcc -I/usr/include/lua5.1 zoalib.c -llua5.1 -lm -fPIC -shared -Wl,-soname,zoalib -o zoalib.so
$ python l.py
Resultado: 5.000000
6

Hell yeah!
Embora isso seja absurdamente mais util nos raspberryPi/beaglebone, talvez haja algum uso no desktop, vai saber haha.

nodeMCU: Mais que uma dev Board, um vício

NodeMCU( http://nodemcu.com/index_en.html ) é uma plataforma de desenvolvimento baseada no SoC ESP8266, com stack TCP/IP, eLua e vários extensões do lua embarcadas, voltadas especialmente para aplicações de IoT.
O SoC ESP8266 usa uma CPU RISC de 32bits Tensilica Xtensa LX106, rodando a 80MH. Tem uma RAM de instruções de 64 Kb, uma RAM de dados de 96Kb e uma flash interna de 4 fucking MBs! A estrela do bolo é o IEEE 802.11 b/g/n com suporte a WEP,WPA/WPA2 disponível, além dos 16 pinos de GPIO, SPI, I2C, UART e ADC de até 10bits.

nodeMCU

No xubuntu, eu tive problemas de conectar nela com o minicom. Mas estou usando lindamento o screen:

$ screen /dev/ttyUSB0 9600

Lua/eLua é linda. coisas extremamente complexas são executadas com uma simplicidade absurda, como listar as redes 802.11 disponiveis:

> function lswifi()
>>  function listap(t)
>>    for k,v in pairs(t) do
>>      print(k.." : "..v)
>>    end
>>  end
>>  wifi.sta.getap(listap)
>>end
>lswifi()

listar os arquivos, o conhecido ‘ls’, é simples:

> for k,v in pairs(file.list()) do print(k,v) end

logar na rede wifi, então, piece of cake:

> wifi.setmode(wifi.STATION);
> wifi.sta.config("SSID","senha")

e pronto! so checar seu ip com:

print(wifi.sta.getip())

Aproveitei e fiz um wget rudimentar hahaha

conn=net.createConnection(net.TCP, false) 
conn:on("receive", function(conn, pl) file.open("oi.lua","a"); file.write(pl); file.close() end)
conn:connect(80,"121.41.33.127")
conn:send("GET / HTTP/1.1\r\nHost: www.nodemcu.com\r\n"
    .."Connection: keep-alive\r\nAccept: */*\r\n\r\n")

Escrevi alguns métodos do GNU coreutils, como ls, cp, mv, df… para facilitar a vida. Em breve posto no github e ponho o link aqui.

O legal é que existem uma infinidade de exemplos e aplicações para esta plaquinha. E ela ganhou um fã haha.

Enjoy!

Lua + C – uma combinação perfeita

Lua é uma linguagem de script imperativa, procedural, pequena(sério, muito pequena), reflexiva e leve, projetada para expandir aplicações em geral, com o objetivo de agilizar a prototipagem e fr ser embarcada em softwares complexos.

Embarcar lua em C/C++ por ter sido o pensamento original da linguagem, é algo muito fácil e incrivelmente util.

antes vamos instalar a biblioteca de desenvolvimento do lua no ubuntu:

# apt-get install liblua5.1-0-dev

Agara vamos fazer algo simples: uma função em C que executa instruções em lua

$ cat > 1.c << _EOF_
#include "lua.h"
#include "lauxlib.h"
int main(int argc, char **argv)
{
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);
    luaL_dostring(L, "print('Hey hey hey, '.._VERSION)");
    return 0;
}
_EOF_

compilar, lembrando de linkar a biblioteca do lua e adicionar ao include:

$ gcc -I/usr/include/lua5.1 -o 1 1.c -llua5.1 -lm
$ ./1
Hey hey hey, Lua 5.1

Ok, funcionou. Fazer o C carregar um arquivo contendo o script em lua é igualmente trivial, muda apenas a instrução luaL_dostring() por luaL_dofile(), visto a seguir:

$ cat > 2.c << _EOF_
#include "lua.h"
#include "lauxlib.h"
int main(int argc, char **argv)
{
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);
    luaL_dofile(L, "dois.lua");
    return 0;
}
_EOF_

E criar o script “dois.lua” que será carregado. Neste caso, não estou gerenciando erros, mas é um código em C, todos os erros devem ser checados ou vai crashar tudo.

$ cat > dois.lua << _EOF_
print "go go go"
for i=1,5 do
   print(i) 
end
print "hell yeah!"
_EOF_

compilar igualmente e rodar:

$ gcc -I/usr/include/lua5.1 -o 2 2.c -llua5.1 -lm
$ ./2
go go go
1
2
3
4
5
hell yeah!

Lembrando que o script pode ser mudado depois da compilação, esta é a ideia, na verdade haha.

Até aqui, tudo bem. Agora vamos fazer o lua acessar uma função do C:

$ car > 3.c << _EOF_
#include "lua.h"
#include "lauxlib.h"
static int huehue(lua_State *L){ /* método que será chamado dentro do script */
  int n = lua_gettop(L); /* numero de argumentos */
  double sum = 0;
  int i;
  for (i = 1; i <= n; i++){
    sum += lua_tonumber(L, i);
  }
  lua_pushnumber(L, sum / n); /* envia primeira resposta */
  lua_pushnumber(L, sum);     /* envia segunda resposta */
  lua_pushnumber(L, 666);     /* envia terceira resposta */
  return 3;                   /* quantidade de respostas */
}
int main(int argc, char **argv)
{
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);
    lua_register(L, "huehue", huehue);
    luaL_dofile(L, "tres.lua");
    return 0;
}
_EOF_

e o arquivo lua:

$ cat > tres.lua << __EOF__
print("arquivo lua\n")
_r, _s, _t = huehue(1,2,3,5,8,9)
print(_r.." - ".._s.." - ".._t)
__EOF__

A compilação continua a mesma:

$ gcc -I/usr/include/lua5.1 -o 3 3.c -llua5.1 -lm
$ ./3
arquivo lua

4.6666666666667 - 28 - 666

Beleza, so lebrar de registrar a função que se quer exportar para o script lua e tudo funciona muito bem.
Agora a ultima fronteira, fazer o C carregar uma função do lua. Para que isso? bom, vamos dizer que está extraindo dados do hardware de alguma forma em C, mas lidar com isso é complexo ou exige várias mudanças. Re-escrever e re-compilar não é exatamente a saída mais agradavel. Lua pode ser a resposta.
O exemplo que estou colocando, é, entretanto, mais simples. O C para dois parametros para uma função continha, que na prática executa o teorema de pitagoras, mas pode ser mudado para qualquer coisa sem requerer recompilação

$ cat > 4.c << _EOF_
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
int main()
{
    double z;
    lua_State *L = lua_open();
    luaL_openlibs(L);
    luaL_loadfile(L, "quatro.lua");	/* arquivo lua */
    lua_pcall(L, 0, 0, 0);		/* limpa a pilha */
    lua_getglobal(L, "continha");	/* nome da função do lua */
    lua_pushnumber(L, 3);		/* envia primeiro argumento */
    lua_pushnumber(L, 4);		/* envia segundo argumento */
    lua_pcall(L, 2, 1, 0);
    z = lua_tonumber(L, -1);		/* pega resultados */
    printf("Resultado: %f\n",z);
    lua_pop(L, 1);
    lua_close(L);
    return 0;
}
_EOF_

e o arquivo lua:

$ cat > quatro.lua << _EOF_
function continha (x, y)
    return math.sqrt(x^2 + y^2)
end
_EOF_

Como sempre, a compilação é a mesma:

$ gcc -I/usr/include/lua5.1 -o 4 4.c -llua5.1 -lm
$ ./4
Resultado: 5.000000

Lindo!
Como podemos ver, embarcar lua em c não só faz sentido, como pode facilitar muito a extensão de um software mais complexos. E é por isto que é tão udada em jogos e em aplicações mobile(compiladas com c++).
Usem lua, é uma coisa linda!
haha

Acessando PCI Express com python

Usando lspci para retornar o valor do dispositivo:
01:00.0 Non-VGA unclassified device: Altera Corporation Device 0de4 (rev 01)
Encontra-o em /sys/devices/
/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0
Habilita-o com:

 echo -n 1 > /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/enable 

Depois, só rodar o script(salvar como placapci.py):

import mmap, sys
with open('/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/resource0', 'r+b') as f:
    mm = mmap.mmap(f.fileno(), 32)
    mm[:1] = chr(int(sys.argv[1], 16))

no caso, ativar os leds de uma altera lindona:
python placapci.py 0xAA
01
python placapci.py 0x55
02

É isso aí.

Minha primeira tentativa de decodificar um apk android e usar seu serviço

UPDATE 15/maio/2014 no final do arquivo!
UPDATE 20/novembro/2015 no final do arquivo!

Escolhi como meu primeiro alvo a aplicaçao:
https://play.google.com/store/apps/details?id=br.gov.sinesp.cidadao.android
que checa os dados das placas dos carros e diz qual carro é. Algo bem inocente ;D
Depois de baixar o br.gov.sinesp.cidadao.android.apk e checar sua estrutura interna, pude ver que apenas classes.dex, resources.arsc e AndroidManifest.xml eram interessantes.
Primeiro procurei uma ferramente para transformar o dex em jar. Pela ordem das pesquisas no google, tentei inicialmente o apktool( http://android-apktool.googlecode.com/files/apktool1.5.2.tar.bz2 ), mas nao funcionou, deu trocentos erros. Passei para o dex2jar( http://dex2jar.googlecode.com/files/dex2jar-0.0.9.15.zip )… esse funcionou como mágica e gerou o classes_dex2jar.jar, que abri com o jd-gui ( http://jd.benow.ca/jd-gui/downloads/jd-gui-0.3.5.linux.i686.tar.gz ). Comecei a explorar.
Iniciando em br.gov.sinesp.cidadao, vi que android.* nao tinha nada interessante. Mas tanto util.Hash quanto cordova.plugin.InfoAplicacaoPlugin pareciam promissores.
Comecei, lógico, pelo Hash.

public class Hash
{
public static String generateHash(String paramString1, String paramString2)
{
SecretKeySpec localSecretKeySpec = new SecretKeySpec(paramString1.getBytes(), "HmacSHA1");
try
{
Mac localMac = Mac.getInstance("HmacSHA1");
localMac.init(localSecretKeySpec);
byte[] arrayOfByte = localMac.doFinal(paramString2.getBytes());
new Hex();
String str = new String(Hex.encode(arrayOfByte), "UTF-8");
return str;
}
...

Um método bem simples que usa HMAC(Hash-based Message Authentication Code) para gerar o hash encodado em hexa vindo de dois parametros (paramString1 e paramString2). Implementar HMAC em python e hex encodar a saida é trivial:

from hashlib import sha1
from hmac import new as hmac
generateHash = lambda(placa): hmac(paramString1, paramString2, sha1).digest().encode('hex')

Agora é achar o diacho destes paramString1 e paramString2.
Através da documentação do PhoneGap( http://docs.phonegap.com/en/2.0.0/guide_plugin-development_android_index.md.html ), cheguei a classe Plugin, que deve ser chamado no javascript da view da aplicação. O formato do chamado ao plugin é:

exec(<successFunction>, <failFunction>, <service>, <action>, [<args>]);

Ou seja, a função chamada caso o request seja um sucesso, caso ele falhe, o serviço, ação e os parametros. Não encontrei no código original os parametros tais quais estão definidos, porem na classe InfoAplicacaoPlugin que extende CordovaPlugin e que está em /br/gov/sinesp/cidadao/cordova/plugin/, encontrei as definiçoes dos plugins, especialmente:

public final String ACTION_GET_APP_INFO = "getAppInfo";
public final String ACTION_GET_TOKEN = "getToken";
...
private String chave = "sheacsrhet";

E no método execute encontrei:

if ("getToken".equals(paramString))
{
String str1 = paramJSONArray.getString(0);
localJSONObject.put("token", Hash.generateHash(this.chave, str1));
}
paramCallbackContext.success(localJSONObject);

Huhuhu… já tenho a chave, so preciso descobrir o que raios é paramJSONArray.getString(0).
Dando mais uma passeada pelo javascript, encontrei em /assets/www/js/plugin/InfoApp.js o seguinte:
InfoApp.prototype.getToken = function(funcaoRetornoSucesso, funcaoRetornoErro, dados)
Ou seja, uma redefinição do exec original onde apenas 3 parametros eram necessarios! Procurando um pouco mais, achei em /assets/www/js/sinesp-cidadao.js o seguinte:

window.plugins.infoApp.getToken(
function(retorno){
sucessoGetToken(retorno);
var ws = new WebService(URL_SERVICO);
var parametrosHeader = getParametrosHeader();
var parametrosBody = getParametrosBody();
ws.call(METODO_SERVICO, parametrosHeader, parametrosBody, retornoServico, retornoServicoErro);
},
erroGetToken,
[placa]
);

Então, pude saber que paramJSONArray.getString(0) era a própria placa. Ufa.
Agora fica fácil reescrever a função de HASH em python:

from hashlib import sha1
from hmac import new as hmac
generateHash = lambda(placa): hmac("sheacsrhet",placa,sha1).digest().encode('hex')

Sniffei a saida do app e meu código. O hash bateu ;D
Porém, tambem tem como parametro IP e localizacao geografica(lat e long). Não sei se o sistema pode restringir a checagem por local/IP, então, porque nao randomizar tambem? ;D
Latitute e longitude é facil fazer um esquema para randomizar o valor dentro de um raio de cobertura, evitando de por um local inexistente ou, sei lá, no Japão.

rLat = lambda(raio): '%.7f' % (( raio/111000.0 * math.sqrt(random.random()) ) * math.cos(2 * 3.141592654 * random.random()) + (-3.7506985))

rLong = lambda(raio): '%.7f' % (( raio/111000.0 * math.sqrt(random.random()) ) * math.sin(2 * 3.141592654 * random.random()) + (-38.5290245))

Usei a latitude e longitude de Fortaleza/Ceará como referencia.
O próximo passo é montar tudo:

#coding: utf-8
import socket
import random, math
from hashlib import sha1
from hmac import new as hmac

generateHash = lambda(placa): hmac("sheacsrhet",placa,sha1).digest().encode('hex')

rLong = lambda(raio): '%.7f' % (( raio/111000.0 * math.sqrt(random.random()) ) * math.sin(2 * 3.141592654 * random.random()) + (-38.5290245))

rLat = lambda(raio): '%.7f' % (( raio/111000.0 * math.sqrt(random.random()) ) * math.cos(2 * 3.141592654 * random.random()) + (-3.7506985))

pacote = lambda(placa): 'POST /sinesp-cidadao/ConsultaPlaca HTTP/1.1\nHost: sinespcidadao.sinesp.gov.br\nContent-Length: %d\nOrigin: file://\nSOAPAction: \nContent-Type: application/x-www-form-urlencoded; charset=UTF-8\nAccept: text/plain, */*; q=0.01\nx-wap-profile: http://wap.samsungmobile.com/uaprof/GT-S7562.xml\nUser-Agent: Mozilla/5.0 (Linux; U; Android 4.1.4; pt-br; GT-S1162L Build/IMM76I) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30\nAccept-Encoding: gzip,deflate\nAccept-Language: pt-BR, en-US\nAccept-Charset: utf-8, iso-8859-1, utf-16, gb2312, gbk, *;q=0.7\n\n%s' % ( len(payload(placa)), payload(placa) )

payload = lambda(placa): '<?xml version="1.0" encoding="utf-8" standalone="yes" ?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ><soap:Header><dispositivo>GT-S1312L</dispositivo><nomeSO>Android</nomeSO><versaoAplicativo>1.1.1</versaoAplicativo><versaoSO>4.1.4</versaoSO><aplicativo>aplicativo</aplicativo><ip>177.206.169.90</ip><token>%s</token><latitude>%s</latitude><longitude>%s</longitude></soap:Header><soap:Body><webs:getStatus xmlns:webs="http://soap.ws.placa.service.sinesp.serpro.gov.br/"><placa>%s</placa></webs:getStatus></soap:Body></soap:Envelope>\n/sinesp-cidadao/ConsultaPlaca HTTP/1.1\r<br>\r\n' % (generateHash(placa),rLat(20000),rLong(20000), placa)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("sinespcidadao.sinesp.gov.br", 80))
s.send(pacote('XXX0000'))
print s.recv(1024)
s.close()

Pronto. Funciona ;D Para efeito de beleza, pode-se usar alguma lib de parsing de XML ou mete um belo e bruto regex da escuridão para parsear tudo com a sutileza de um macaco-ogro do pântano.

 

[UPDATE 15/maio/2014] Conforme foi percebido pelo Lúcio Corrêa( @luciofcorrea ), o sistema mudou e a sinesp tentou obfuscar o resultado. Mas não foi muito difícil gerar novamente o HASH e obter os novos parametros.
Primeiro, em /br/gov/sinesp/cidadao/android/f/k.class foi mantida a chave antiga, para enganar uma busca por strings, porem em /br/gov/sinesp/cidadao/android/f/j.class a localSecretKeySpec mudou, ao invés de usar o valor em k.java, ele seta a senha estáticamente:

SecretKeySpec localSecretKeySpec = new SecretKeySpec("shienshenlhq".getBytes(), "HmacSHA1");

Desta forma, a nova chave é “shienshenlhq”.
Em /br/gov/sinesp/cidadao/android/f/a.class temos

public static final String a = "http://sinespcidadao.sinesp.gov.br/sinesp-cidadao/ConsultaPlacaNovo27032014";

ou seja, muda a string a se fazer o POST.
Já em /br/gov/sinesp/cidadao/android/e/c.class, encontramos o novo campo a ser incluído: versaoAplicativo, com conteudo fixo “1.1.1” e o parametro aplicativo agora tem o valor fixo “aplicativo”.
Com tudo isto incluído, o script voltou a funcionar. e está postado no meu github: https://github.com/paoloo/servicos/blob/master/placa.py

[UPDATE 20/NOVEMBRO/2015] Como da ultima vez, a string de request estava obfuscada, mas foi encontrada pelo grande Junior Kaibro, que a postou no comentario. O novo POST é feito para /sinesp-cidadao/ConsultaPlacaNovo e só, nada mais mudou. Atualizei no github. Depois de tanto tempo é legal ver o pessoal mantendo a ferramenta viva. E se colocarem captcha, relaxem, eu quebro ;D