Ir al contenido principal

Un bot IRC

Aprovechando el PyIC escribí un bot que se une a un canal y responde a los típicos mensajes de "@loquesea" y "!loquesea", que están definidos en una base de datos SQLite3, la idea es algo así: Se crea un archivo SQLite3 (por defecto el nombre es "dbase.db", si quieres puedes cambiarlo en la línea 12) con 2 tablas, "talks" y "commands". La tabla "talks" tendría como mínimo 2 columnas, la primera es la palabra a la que responde, y la segunda, con lo que responde, por ejemplo, si la base de datos tiene esta fila:

=============================================================================== busca | http://www.lmgtfy.com/?q= ===============================================================================

El chat sería algo así

=============================================================================== : @busca loquesea < bot >: humano: http://www.lmgtfy.com/?q=loquesea ===============================================================================

La tabla "commands" es mas o menos igual, dos columnas, la primera el comando, y la segunda lo que se ejecuta, por ejemplo...

=============================================================================== info | uname ===============================================================================

=============================================================================== : !info -a < bot >: humano: Linux blablabla 2.6.32-22-generic #36trisquel1 SMP Sat Jun 5 02:09:43 UTC 2010 i686 GNU/Linux ===============================================================================

Solo decir que si despues del simbolo ('@'/'!') se escribe un nick seguido de ':', este será al que se hable...

=============================================================================== : @humano2:busca loquesea < bot >: humano2: http://www.lmgtfy.com/?q=loquesea ===============================================================================

Y no se me ocurre que más comentar :P, aqui está el código [yabot.py], o coloreado...

===============================================================================

!/usr/bin/env python

Ejemplo de cliente IRC con pyIC

from pyic import * import sqlite3, os

botName = "vagoBot" # Nombre del bot server = "irc.freenode.net" # Adivina xD channel = "#bot_testing" # Canal al que se une el Bot

global db_file db_file = "dbase.db" # Archivo sqlite3

def sayIt(irc, msg, ex):     to = msg.by     via = msg.to     mmsg = msg.msg     if ( via == irc.nick ):         via = msg.by     cc = 0     try:         cc = mmsg.index(":")         try:             sc = mmsg.index(" ")             if (sc > cc):                 to = mmsg[ 0 : cc ]         except:             to = mmsg[ 0 : cc ]         cc += 1         mmsg = mmsg[ cc : ]     except:         pass

try:         i = mmsg.index(" ")         command = mmsg[ : i ]         args = mmsg[ i + 1 : ]     except:         command = mmsg         args = ""

conn = sqlite3.connect(db_file)     cur = conn.cursor()     if (not ex):         cur.execute("select * from talks")     else:         cur.execute("select * from commands")     for line in cur:         if (line[ 0 ] == command):             if ( not ex):                 irc.notice( via, to + ": " + line[ 1 ] + args )             else:                 p = os.popen( line[ 1 ] + args , "r" )                 l = p.readline()                 while ( len( l ) > 0 ):                     while (l [ -1 ] == "\r" ) or (l [ -1 ] == "\n"):                         l = l [ : -1 ]                     irc.notice(via, to + ": " + l )                     l = p.readline()

irc = irc_client(botName,server)

Esperar por que acabe de "presentarse" el servidor

print "Esperando a que acabe de presentarse..." while ( True ):     msg = irc.getmsg()     if ( msg.type == RPL_ENDOFMOTD ): # Fin del mensaje del dia         break     elif (msg.type == ERR_NOMOTD): # No hay mensaje del dia         break

print "Entrando a",channel irc.join( channel ) # Se une al canal

Saluda a todos los que hay en el canal

while ( True ):     msg = irc.getmsg()     if (msg.type.upper() == "PRIVMSG") or (msg.type.upper() == "NOTICE"):         msg.msg = msg.msg.strip()         if (msg.msg[ 0 ] == "!"):             msg.msg = msg.msg [ 1 : ]             sayIt(irc, msg, True)         elif (msg.msg[ 0 ] == "@" ):             msg.msg = msg.msg [ 1 : ]             sayIt(irc, msg, False) ===============================================================================

Hasta otra!

IRC con Python (PyIC)

Si quieres programar algo que use IRC en Python puedes hacerlo tu mismo con sockets, puedes usar una librería y programarlo_por_eventos_con_pyirclib , PyIRC ...

A la hora de ver este tema, buscaba algo que se encargase de las cosas que no son interesantes del IRC (PING-PONG's, parseado de mensajes) y que no sea orientado a eventos, pero que siguiese permitiendo hacer cosas "poco comunes", como hacer que un archivo que se nos ofreció por esta red lo descargue directamente un servidor FTP (para eso hace falta algo mas, que la librería que trae python no lo permite), pero...

A falta de algo ya escrito, se programa, y eso es lo que traigo, otra librería de IRC más [ pyic.zip ]

El nombre... PyIRC ya estaba cogido xD, asi que supongo que valdrá PyIC (Python IRC Client)

Por si acaso lo dejo claro, la librería no descarga bien, así que no es recomendable usarlo para eso, ademas esta pensado para ser asíncrono, por ejemplo, no se pide la lista de canales y la propia librería la devuelve, sino que hace la peticion al servidor y es cosa del script capturar la lista (aunque la librería ya parsea los mensajes para hacer esta parte mas fácil), repito, no esta hecho para ser la forma mas sencilla, sino para permitir hacer cosas que otras no permiten.

Ahora explicaré las funciones que trae, pero para verlo mejor, aqui hay un pequeño bot que entra a un canal y saluda a los que llegan

===============================================================================

!/usr/bin/env python

Ejemplo de cliente IRC con pyIC

from  pyic import *

botName = "HelloBot"

server = "irc.freenode.net" channel = "#bot_testing" saludo = "Hola"

irc = irc_client(botName,server)

Esperar por que acabe de "presentarse" el servidor

print "Esperando a que acabe de presentarse..."

while (True):     msg = irc.getmsg()     if ( msg.type == RPL_ENDOFMOTD ): # Fin del mensaje del dia         break     elif (msg.type == ERR_NOMOTD): # No hay mensaje del dia         break

print "Entrando a",channel irc.join(channel) # Se une al canal

print "Saludando :)}" irc.notice(channel,saludo+" "+channel) # Saluda al canal

no usa irc.msg() para evitar que los bot's respondan

Saluda a todos los que hay en el canal

while (True):     msg = irc.getmsg()

if (msg.type.upper()=="JOIN"): # Alguien mas entro al canal         if (msg.by != botName):

print "Llego",msg.by             irc.notice(channel,saludo+" "+msg.by) ===============================================================================

No hace gran cosa, pero sirve como "Hola mundo" :)

Nota: Normalmente para hablar por el chat se utilizaria irc.sendmsg(), pero este protocolo definió especificamente un tipo de mensaje para que utilizaran los bots automáticos, a los cuales no se debería responder para evitar que varios robots entren en un bucle entre ellos... algo_como_esto_(por_decirlo_de alguna_forma)

Como se puede ver, todo se hace desde un objeto irc_client, que se inicializa con estos elementos:
* nick: nick del usuario (elemental... xD)
* server: servidor IRC
* port: puerto del servidor (6667 por defecto)
* username: nombre de usuario ("user" por defecto)
* username2: segundo nombre del usuario ("user" por defecto)
* fullname: nombre completo del usuario ("user Name"  by default)
* serverpasswd: contraseña para el servidor (si la tiene)
* passwd: contraseña para el nick (si la tiene)

A partir de esto, las funciones disponibles son:
* sendmsg(to,msg): envía un mensaje "msg" para "to"
* notice(to,msg): como sendmsg, pero este tipo de mensajes no se debe
responder
* chng_nick(nick): cambia de nick
* join(channel,passwd=None): se une al canal "channel", usando una
contraseña, si es necesaria (contraseña desactivada por defecto)
* quit(msg = "Client quit"): sale del servidor, con un mensaje "msg"
* quit_channel(channel): sale del canal "channel"
* kick(channel,nick,comment=None): echa a un usuario "nick" de un canal
"channel", el comentario es opcional
* invite(nick,channel): invita a un usuario "nick" al canal "channel"
* get_channels(): pide la lista de canales
* get_names(channel): pide la lista de nicks en un canal "channel"
* get_topic(channel): pregunta al servidor el tema del canal "channel"
* set_topic(channel,topic): cambia el tema del canal "channel"
* whois(user): pide informacion acerca del usuario "user"
* whowas(user): pide informacion acerca del usuario "user" que ya no esta
en el servidor
* set_mode(mode): cambia el modo del propio usuario
* set_chanmode(channel,mode,data = None): cambia al modo "mode" el canal
"channel", si el modo requiere informacion adicional, se pasa a traves de
"data"

La funcion getmsg() requiere una explicación aparte, pues es la que se utiliza para recuperar las comunicaciones del servidor al cliente, devuelve una variable de tipo irc_msg , con las siguientes propiedades:
* by: nick del emisor del mensaje
* sender: informacion del emisor
* to: a donde se envia (el usuario o un canal)
* type: tipo de mensaje: "PRIVMSG", "NOTICE", explico después un poco mas
que es un poco largo
* msg: el mensaje
* multiline: si es un tipo de mensaje multilinea
* multiline_end: si es el fin de un mensaje multilinea
* ctcp: si contiene un mensaje CTPC
* ctcp_msg: mensaje ctcp

Los nombres de los tipos de mensaje son los especificados por el RFC de IRC, ademas de "NOTICE" y "PRIVMSG", hay una lista en  [  http://irchelp.org/ irchelp/rfc/chapter6.html] (en inglés), aunque se añadio el "DCC_SEND_OFFER" para identificar las ofertas de envio de archivos

Si el mensaje es una oferta de DCC SEND (msg.type == DCC_SEND_OFFER ), además tiene las siguientes propiedades:
* ip:  IP de la conexión
* port: puerto de la conexión
* file: nombre del archivo
* turbo: si se usa el modo turbo
* size: tamaño del archivo (o -1 si no se especifica)

Queda por hacer (hoy no... mañaaaaana ):

IRC sobre SSL Mucho de DCC: enviar, conexiones inversas,transmisiones cifradas... que funcione :P

[Referencias] http://www.kvirc.de/docu/doc_dcc_connection.html http://en.wikipedia.org/wiki/Direct_Client-to-Client http://irchelp.org/irchelp/rfc/index.html RFC_1459_(en_español,_version_texto)

Descargando una lista de webproxys

No ando con mucho tiempo para programar, asi que se me ocurrio hacer un pequeño script en python aprovechando el pycrawler para que descargue una lista de webproxies, el codigo no es gran cosa, pero funciona :)

Nota: la lista se descarga de proxy.org

Este es el codigo (o para descargar [proxylist.py])

!/usr/bin/env python

import pycrawler import sys if (len(sys.argv) > 1):     f = open(sys.argv[1],'w') else:     f = open(raw_input('Se guardara en: '),'w')

print "Descargando lista de proxy's..." c = pycrawler.crawler() site = c.crawlsite('proxy.org')

Se buscan los proxys en la pagina

for e in site.element_list:     if (e.tag_type == "option") and ('value' in e.property):         if ("." in e.property['value']):             print "->",e.property['value']             f.write(e.property['value']+"\n")

f.close()

===============================================================================

Hasta otra

pyCrawler 0.2

Pues hay actualizacion del pyCrawler, los cambios son bastante significativos... y lo que es mas importante, ahora hace algo util! :D

El motivo fue que ultimamente aprovechaba el codigo del primer_pyCrawler para los scripts que usasen la web, pero no es que sea un codigo muy pythonico (de hecho es bastante malo y tiene algunos bugs), asi que aproveche para reescribirlo desde cero... no le doy mas vueltas:

  • Pasa de ser un script a ser mas bien una libreria
  • Pasa de administrar las conexiones a mano (con sockets) a hacerlo con urllib, que es estandar, y lo hace mucho mejor
  • Se limpio el codigo para hacerlo mas comprensible

Se puede descargar aqui [pycrawler.zip]

El uso es bastante sencillo (o eso se intento), solo hay que importarlo

from pycrawler import *

y crear un objeto (solo es necesario uno), el unico argumento (opcional) es el de proxies, si no se especifica se usara el del sistema, la forma de definirlo es como los de urllib (que es la libreria que se ocupa de esa parte)

c = crawler()

Despues, solo hay que llamar al la funcion crawlsite, con una url

site = c.crawlsite(url)

la variable que se devuelve tiene una sola propiedad (en posteriores versiones habra mas), element_list, que como su nombre indica es una lista de elementos. Cada uno es un elemento de la web, las propiedades que tienen son

  • tag (booleano), indica si es una etiqueta o si es solo texto
  • tag_type (string), el tipo de etiqueta que es ("body", "head", "a",... )
  • property (diccionario), las propiedades de la etiqueta (si tiene), se
    accede a ellas a traves de su nombre
  • closing (booleano), si es una etiqueta de cierre
  • closed (booleano), si es una etiqueta que se cierra (como
    )
  • commented (booleano), si esta comentado

Ademas los enlaces ( tag_type == "a" ), tienen las propiedades:

  • absolute (string), url absoluta a donde enlazan
  • range (string), si es local ("local"), externa ("external") o si es
    javascript ("javascript")

Eso es todo lo que tiene. Asi por ejemplo, este codigo exploraria un website completo:

===============================================================================

!/usr/bin/env python

-- coding: utf-8 --

from pycrawler import * c = crawler()

newlist = [] inicio = raw_input('Introduce la url por la que se comenzara: ') newlist.append(inicio)

mylist = []

Mientras queden paginas por explorar

while len(newlist) > 0:

# La nueva pagina se mueve a la lista de las ya exploradas     this = newlist.pop(0)     mylist.append(this)

print this,"..."

# Se explora     site = c.crawlsite(this)

# Se lee la lista de links locales     for e in site.element_list:

if (e.tag_type == "a"):             if (e.range == "local"):

# Sino se vio antes, se añade a la lista                 if not(e.absolute in mylist) and not(e.absolute in newlist):

newlist.append(e.absolute)

print "Mostrando lista (",len(mylist) ,"):"

print mylist

===============================================================================

Hasta otra

Actualizacion: acabo de subir una version con los fallos que encontre corregidos (no cai en los "./" y "../" de las url, mal empezamos xD), el enlace es el mismo, [pycrawler.zip]

Trasteando con Gambas

Ando trasteando con Gambas, que (a parte del marisco) como sabreis es un IDE/ compilador/lenguaje para programacion "visual" bajo GNU/linux, y que al parecer es bastante similar a Basic, y su nombre ya avisa de que "Gambas Almost Means Basic" (Gambas casi significa Basic)

Asi que para probar, monte un programilla que mostrara las temperaturas del pc [http://1.bp.blogspot.com/_26RJWnubh-w/TBa0bEdgr2I/AAAAAAAAAGM/DorL90LAL8c/ s320/Pantallazo-1.png]

El programilla no tiene mucha cosa (solo lee en /proc/acpi/thermal_zone/*/ temperature), asi que no me liare con eso... De la prueba saque algunas conclusiones sobre Gambas:
* Es sencillo si el objetivo es hacer un programa que se base mucho en la
interfaz grafica
* Sino, sigue siendo sencillo, pero el codigo no me parece tan limpio como
podria ser con otros lenguajes (como Python o C)
* La documentacion es poca, o eso, o simplemente no supe buscarla... el
detalle es que la version actual de Gambas es la 2 y los libros que se
encuentran por ahi son para la primera version Y nada mas... por ultimo dejo el codigo del programilla por si resulta util [ Temp-Gambas.tar.gz] (exportado con "Crear archivo fuente")

Hasta otra! [Referencias] http://www.gambas-es.org http://gambasdoc.org/help/lang http://www.youtube.com/watch?v=0aOkiEUn3xE http://sites.google.com/site/codigogambas/

Clon de LOIC en python

Cierto anónimo_sugirió_hacer_un_clon_de_LOIC_en_python, aquí está un posible código, dividido en 5 archivos, no tiene interfaz gráfica, pero implementa el flooder de los 3 protocolos de LOIC.

Actualización2: eliminado de github en un impulso, queda en el de Mephiston

(Los argumentos son)

Uso: ./pyloic.py {http | tcp | udp} -h [-p ] [-t ] [-m Message] [-k]

===============================================================================

http, tcp o udp: El protocolo escogido. -h : La dirección (IP o hostname). -p : El puerto al que se dirigirán las conexiones (por defecto el 80). -t : Número de hilos que se ejecutan a la vez (por defecto 10). -m Message: El mensaje que se enviará. -k: En TCP y HTTP no espera a que el otro lado de la conexión la cierre, en UDP detiene el script si el puerto no es accesible.

El script se finaliza si se pulsa [Enter] o se produce algún otro tipo de Excepción en el hilo principal (no los flooders), como pulsar Ctrl-C. pyloic.py: Archivo principal, lee los argumentos y llama al flooder adecuado

flooder.py: La base de cada clase de flooder:

udp_flood.py: El flooder de UDP, hereda el init y el ser un hilo de flooder.py

tcp_flood.py: Flooder de TCP

Y http_flood.py: Flooder de HTTP

Eso es todo, un clon de LOIC en casi 200 líneas (199 =P )

Introduccion a la criptografia, con Python: ElGamal (V -1 )

Y vamos por la quinta parte de Introduccion a la criptografia, se dividira en dos porque ya que hablamos de cifrado asimetrico vale la pena ver un cifrado/ descifrado y un algoritmo de firma/comprobacion, el problema es que si bien ElGamal cumple perfectamente su parte en lo primero, no tiene un esquema de firmado con el que comparta la generacion de claves (tambien hay un algoritmo de firma ElGamal, pero no es el mismo, y ademas no se considera del todo seguro), asi que ademas veremos un esquema de solo firma ( DSA ) en la segunda parte. Sin mas espera...

¿Que es ElGamal?

El procedimiento de cifrado/descifrado ElGamal se refiere a un  
esquema de cifrado basado en problemas matemáticos de algoritmos  
discretos. Es un algoritmo de criptografía_asimétrica basado en la  
idea de Diffie-Hellman y que funciona de una forma parecida a este  
algoritmo discreto.  
El algoritmo de ElGamal puede ser utilizado tanto para generar firmas  
digitales como para cifrar_o_descifrar.  
Fue descrito por Taher_Elgamal en 1984 y se usa en software GNU  
Privacy_Guard, versiones recientes de PGP, y otros sistemas  
criptográficos. Este algoritmo no esta bajo ninguna patente lo que  
lo hace de uso libre. [wikipedia]

Ademas ElGamal comparte con RSA el uso de operaciones matematicas como el calculo de la exponenciacion modular y la generacion de numeros primos, que ya estan posteadas en la entrada_correspondiente asi que ya no las incluire aqui para evitar saturar el post con informacion repetida  (aunque si en el codigo fuente al final y para descargar, que ahi no molesta)

Aviso: como sabreis, esta implementacion (como toda esta serie), no se deberia usar para nada real, no solo porque es mas lento que un i286 (lo dicho, esta en python solo para que sea mas facil de entender), sino porque ademas seguro que los programas y librerias que existen cumplen las funciones que necesarias mejor y con mas seguridad

Generando claves

Para generar las claves se realizan las siguientes operaciones (he intentado mantener el mismo nombre de las variables que la Wikipedia para simplificar)

  1. Se genera un numero primo (p), a ser posible con un (p-1) que al factorizarlo se obtengan numeros grandes (aqui simplemente se espera que con un p grande p-1 cumpla su condicion )

=============================================================================== def genkeys(bitn):

p = genprime(bitn)

  1. Se obtienen dos numeros aleatorios (g y a), g es el generador, parte de la clave publica, mientras que a es la clave privada, g puede ser cualquier numero (aunque se recomienda que se tomen algunas_consideraciones_de_seguridad), mientras que a es un numero entre 0 y p-2 (aunque por seguridad se recomienda usar un numero de 2 a p-2):

===============================================================================     g = random.randint(2(bitn-1),2bitn)#Esto se puede sustituir por una lectura a /dev/random, por ejemplo

a = random.randint(2,p-2)

  1. La ultima variable a generar (a2 , A en la Wikipedia) es el resultado de elevar g a la potencia a, y obtener el resultado modulo p

===============================================================================     a2 = modex(g,a,p) ===============================================================================

Asi, la clave publica esta formada por las variables p , g y a2 , mientras que la clave privada es a Por comodidad, la clave publica se carga en un diccionario:

===============================================================================     public = {'p':p,'g':g,'a2':a2}

private = a

return public, private

Cifrado y descifrado

De nuevo, el cifrado y descifrado necesitaran convertir el mensaje en un numero, como las funciones ya estan en el post_de_RSA, no tiene sentido repetirlas

Cifrando...

  1. Para cifrar, se convierte el mensaje en un numero n

=============================================================================== def cipher(s, public):     n = msg2num(s) ===============================================================================

  1. Despues se obtiene un numero pseudo-aleatorio b entre 2 y p-2 (recordemos que p forma parte de la clave publica)

===============================================================================     b = random.randint(2, (public ['p'] -2) ) ===============================================================================

  1. Se eleva g a b modulo p, para conseguir la variable y1

===============================================================================     y1 = modex(public ['g'], b, public['p']) ===============================================================================

  1. Lo que falta, y2 se obtiene al elevar a2 a b y multiplicarlo por n , el resultado se obtiene modulo p ... o, para evitar dejarse el procesador en la operacion, se eleva a2 a b modulo p, el resultado se multiplica por n y se hace modulo p ( esto aprovecha que hacer la operacion de exponenciacion modular es mas rapida que una exponenciacion normal )

===============================================================================     y2 = modex(public ['a2'], b, public['p'])     y2 = ( y2 * n ) % public ['p'] ===============================================================================

El mensaje cifrado son las variables y1 y y2

===============================================================================     return (y1,y2) ===============================================================================

Descifrando...

Para descifrar necesitaremos el mensaje cifrado, la clave privada y la publica (al contrario de RSA, que no necesita la clave publica)

  1. Recuperamos el numero que corresponde al texto elevando y1 a p - 1 - a , multiplicando el resultado por y2 y obteniendo todo modulo p ... o con exponenciacion modular haciendo y1 elevado a p - 1 - a modulo p, multiplicado por y2, y con el resultado modulo p

=============================================================================== def decipher(n, public, private):

d = modex(n[0], public['p'] -1 -private, public['p'])     d = (d * n[1]) % public ['p'] ===============================================================================

  1. Solo queda volver a convertir el numero a texto

===============================================================================     s = num2msg(d)     return s ===============================================================================

Y ya esta :D, el codigo fuente esta aqui [ elgamal.py ]

[Referencias] http://es.wikipedia.org/wiki/ElGamal http://en.wikipedia.org/wiki/ElGamal

Probando shellcodes

Pues hoy vengo con poca cosa, andaba escribiendo algo para pasar el rato... un programilla para probar shellcodes ¿porque no?

Y este es el resultado [ shellcode_tester.c ] o al final coloreado con Pygments

La compilacion es simple, solo hay que hacer gcc shellcode_tester.c -o shellcode_tester

Las opciones al lanzarlo son: ./shellcode_tester [-nv] [-nw] [-nr] [-f ] -nv: No verbose (no se imprimira nada por pantalla)[--no-verbose] -nw: No write (no se permitira escribir en la memoria del shellcode)[--no- write] -nr: No read (no se permitira leer la memoria del shellcode)[--no-read] -f: Introduce el shellcode a traves de un archivo

Mas o menos, usarlo seria algo asi: kenkeiras@viaxante:~/%%%%%$ ./shellcode_tester Shellcode Tester

Introduce el shellcode: \x31\xdb\x8d\x43\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80\x31\xc0\x40\xcd\x80

Ejecutando Shellcode... [36] $ echo "Esto es otra shell :D}" Esto es otra shell :D} $ kenkeiras@viaxante:~/%%%%%$

/*

  • Shellcode Tester (Yet Another Shellcode Tester)

  • Copyright (c) 2010 Kenkeiras

*

  • DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE

  • Version 2, December 2004

*

*

  • Everyone is permitted to copy and distribute verbatim or modified

  • copies of this license document, and changing it is allowed as long

  • as the name is changed.

*

  • DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE

  • TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

*

    1. You just DO WHAT THE FUCK YOU WANT TO.

*

*

*/

include

include

include

include

define max_size 1024 //Caracteres maximos para el shellcode

// Como (sh en Gnu/Linux de 32 bits)

/ / \x31\xdb\x8d\x43\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80\x31\xc0\x40\xcd\x80

char *scs = NULL;

char *sc = NULL;

char a2h(char c){

char r;

if (c>'9'){

   if (c>'Z')



       r=c-0x57;

   else

       r=c-0x37;

}

else

   r=c-0x30;

return r;

}

// Formas de leer los shellcode

// \x99\xAA\xaa (\x)

int bar_hexa(char in,char out){

char curr;

int i,r=-1,len=0;

for (i=0; (i < max_size) && in[i] != '\0'; i++){

   if((r == 2) || ((in[i] == '\\') && (r > 0))){



       r =- 1;

       out[len] = curr;



       len++;



   }

   if(in[i] == '\n'){



       break;

   }

   else if (in[i] == '\r'){



       continue;

   }

   else if (in[i] == 'x'){



       r = 0;

   }

   else if(r >- 1){



       if (r == 0){

           curr = a2h(in[i])*16;



       }

       else{

           curr = a2h(in[i]) + curr;



       }

       r++;

   }

}

return len;

}

// Escrito directamente

int raw_bin(char in,char out){

int i,len=strlen(in);

for (i = 0;i < len;i++){

   out[i] = in[i];

}

return len;

}

int main(int argc,char **argv){

FILE *f = stdin;

char verbose = 1;

char stack_write = 1;

char stack_read = 1;

int i,len,r;

sc=malloc(max_size+1);

if (argc>1){

   for (i=1;i<argc;i++){



       if ((strcmp(argv[i],"-nv") == 0) && (strcmp(argv[i],"--no-

verbose") == 0)){

           verbose = 0;

       }

       else if ((strcmp(argv[i],"-f") == 0) && ((i+1)<argc) ){



           f = fopen(argv[i+1],"r");

           i++;



       }

       else if ((strcmp(argv[i],"-nw") == 0) && (strcmp(argv

[i],"--no-write") == 0)){

           stack_write = 0;

       }

       else if ((strcmp(argv[i],"-nr") == 0) && (strcmp(argv

[i],"--no-read") == 0)){

           stack_read = 0;

       }

       else{

           printf("Uso: ./shellcode_tester [-nv] [-nw] [-nr] [-

f ]\n");

           printf("-nv: No verbose (no se imprimira nada por pantalla)[--

no-verbose]\n");

           printf("-nw: No write (no se permitira escribir en la memoria

del shellcode)[--no-write]\n");

           printf("-nr: No read (no se permitira leer la memoria del

shellcode)[--no-read]\n");

           printf("-f: Introduce el shellcode a traves de un archivo\n");

       }

   }

}

int PROT_MODE = PROT_EXEC|PROT_NONE ;

if (stack_write){

   PROT_MODE |= PROT_WRITE;

}

if (stack_read){

   PROT_MODE |= PROT_READ;

}

if (verbose){

   printf("\tShellcode Tester\n\n");

   printf("Introduce el shellcode: ");

}

char s = malloc((max_size4)+1);

  fgets(s,max_size*4,f);

r=-1;

len = bar_hexa(s,sc);

if (len < (strlen(s)/4)){

   len = raw_bin(s,sc);

}

free(s);

scs=mmap(0,len+1,PROT_MODE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);

for (i=0;i<len;i++){

   scs[i]=sc[i];

}

free(sc);

if (verbose){

   printf("\nEjecutando Shellcode… [%i]\n",len);

}

((void()()) scs)();

if (verbose){

    printf("Fin del Shellcode\n");

}

return 0;

}

Hasta otra

Introduccion a la criptografia, con Python: AES (IV)

Cuarte parte de la serie... uffff, esto debía haberse posteado hace un mes xD

¿Qué es AES?
Advanced Encryption Standard (AES), también conocido como Rijndael
(pronunciado "Rain Doll" en inglés), es un esquema de cifrado_por
bloques adoptado como un estándar de cifrado por el gobierno_de_los
Estados_Unidos. Se espera que sea usado en el mundo entero y
analizado exhaustivamente, como fue el caso de su predecesor, el Data
Encryption_Standard (DES). El AES fue anunciado por el Instituto
Nacional_de_Estándares_y_Tecnología (NIST) como FIPS PUB 197 de los
Estados Unidos (FIPS 197) el 26_de_noviembre de 2001 después de un
proceso de estandarización que duró 5 años (véase proceso_de
Advanced_Encryption_Standard para más detalles). Se transformó en
un estándar efectivo el 26_de_mayo de 2002. Desde 2006, el AES es
uno de los algoritmos más populares usados en criptografía
simétrica. [ http://es.wikipedia.org/wiki/
Advanced_Encryption_Standard ]

Así, AES es uno de los algoritmos de cifrado más importantes de la actualidad, por lo que es imposible hablar de criptografía y no hablar de el.

AES es un cifrado por bloques, esto significa que no maneja cada byte independientemente, sino que los agrupa en bloques de 16 bytes, ademas soporta 3 longitudes de clave, con cada uno el proceso de cifrado (las rondas) se repite un numero dado de veces 128 bits - 10 rondas 192 bits - 12 rondas 256 bits - 14 rondas

Nota: Por supuesto, esta implementación (como cualquiera de esta serie) no se debe usar para cosas reales, por que es mas lento que un dialup (está en python para que sea más comprensible, si_quieres_velocidad_busca_algo_en_C, por_ejemplo ) y porque no tiene en cuenta ataques por canal auxiliar [ AES_- Ataques_de_canal_auxiliar ]

Otra cosa, algunas implementaciones representan el "estado" del cifrador como un array de 16 elementos, pero dadas las operaciones, me parecio más gráfico utilizar un array de 4 * 4 (bidimensional)

Antes de entrar en materia, vamos a definir unas cuantas funciones para manejar los datos más cómodamente:

Word to array conversion

def word2array(word):

arr = [0,0,0,0]

arr[3] = word&((1<<8)-1) 

arr[2] = (word/(1<<8))&((1<<8)-1)

arr[1] = (word/(1<<16))&((1<<8)-1)

arr[0] = (word/(1<<24))&((1<<8)-1)

return arr

Array to word conversion

def array2word(arr):

word = 0

arr.reverse()

for i in range(0,len(arr)):

word+=(arr[i]<<(i*8))

return word

String to array conversion

def str2arr(s):

arr = []

for c in s:

arr.append(ord(c))

return arr

Array to string conversion

def arr2str(arr):

s = ""

for c in arr:

s+=chr(c)

return s

Y una función que se encargue de "las matematicas":

Galois Multiplication

def galoisMult(a, b):

p = 0

for counter in range(0,8):

if((b & 1) == 1):

p ^= a

hi_bit_set = (a & 0x80)

a = (a<<1) & 0xFF

if(hi_bit_set == 0x80):

a ^= 0x1b

b >>= 1

return p

Bien, una vez hecho esto, decir que a diferencia de otros cifrados simétricos (como ARC4), no se usa el mismo proceso para cifrar y para descifrar, pero es muy similar, por comodidad empezaremos la parte del cifrado:

Para el cifrado, ademas hay que definir dos estructuras, lo que se conoce como una S-box (caja-S), que se utiliza (veremos después como) en el cifrado y lo que se suele llamar RCon (que en el fondo es una tabla precomputa de las exponenciaciones de 2 en el campo finito de Rinjdael) ,que se utiliza al manejar la clave del cifrado(esto es igual para cifrado y descifrado):

global sbox

global rcon

AES S-Box

sbox = [

0     1    2      3

4    5     6     7      8    9     A      B    C     D     E     F

0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,  #0

0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,  #1

0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,  #2

0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,  #3

0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,  #4

0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,  #5

0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,  #6

0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,  #7

0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,  #8

0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,  #9

0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,  #A

0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,  #B

0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,  #C

0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,  #D

0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,  #E

0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ] #F

AES Rcon

rcon = [

0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,

0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,

0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,

0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d,

0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab,

0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d,

0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25,

0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01,

0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d,

0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa,

0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a,

0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02,

0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,

0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,

0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,

0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,

0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f,

0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5,

0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33,

0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb ]

Ahora veremos las funciones básicas que componen el cifrado... recordemos que el "estado" del cifrador es un array de cuatro por cuatro bytes, y que solo se cifra un bloque (en caso de este algoritmo), de 16 bytes (ya se hablara de como solucionar este problema mas tarde):

La primera funcion se denomina Add Round Key (añadir clave de ronda), y consiste en hacer XOR de el estado del cifrador y de la clave de la ronda (Round Key, de esto se hablara después tambien):

def aes_AddRoundKey(data, key): 

for i in range(0,4):

for j in range(0,4):

data[i][j]^=key[i][j]

return data

Sencillo, no? Seguimos...

La segunda operación se llama Shift Rows (desplazar filas), y consiste en desplazar la primera fila del "estado" del cifrador (la de posición 0), 0 bytes a la izquierda (vamos, no hacer nada), desplazar la segunda (la de la posición 1), 1 byte a la izquerda, y asi hasta la cuarta (posición 3, 3 a la izquierda)

def aes_ShiftRows(data):

new_data=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]

for i in range(0,4):

for j in range (0,4):

new_data[i] [j] = data[i] [ (j+i) % 4]

return new_data

Bien, vamos con la tercera, Sub Bytes (substituir bytes), consiste en substituir cada byte del estado, por el que ocupa el de la S-box en la posicion igual al numero que tiene, es decir: def aes_SubBytes(data):

for i in range(0,4):

for j in range(0,4):

data[i][j]=sbox[data[i][j]]

return data La última de las funciones de cifrado (y me temo que la mas difícil con diferencia) es Mix Columns (mezclar columnas), y esto es basicamente lo que se hace, por comodidad, dividiremos esto en dos partes, la division del "estado" del cifrador en columnas, y su tratamiento, la primera parte es: def aes_MixColumns(data):

for i in range(0,4):

column = []

for j in range(0,4):

column.append(data[j][i])

column = aes_MixColumn(column)

for j in range(0,4):

data[j][i] = column[j]

return data La operación que se aplica sobre cada columna es esta, básicamente lo que hace es hacer el XOR de determinadas multiplicaciones en conjuntos finitos (ehh... da igual, el código esta al principio del post ;)

def aes_MixColumn(data):

tmp = []

for i in data:

tmp.append(i)

for i in range(0,4):

data[i]=galoisMult(tmp[i],2) ^ galoisMult(tmp[ (i+3)%4],1) ^ \

galoisMult(tmp[ (i+2)%4],1) ^ galoisMult(tmp[(i+1)%4],3)

return data Una vez explicadas las funciones básicas de cifrado, se hace necesario hablar de las claves... las claves no se utilizan directamente, primero se expanden, y despúes se obtienen subclaves (las claves de ronda que se mencionaron anteriormente). La expansion sigue el siguiente patrón:

Tamaño de clave expandida = (número de rondas +1) * 16

O... si se prefiere: 128 bits -> 176 bytes 192 bits -> 208 bytes 256 bits -> 240 bytes

Para expandir una clave se utilizan la siguientes funciónes (solo se utilizaria directamente aes_expandKey ):

AES rotate Operation

def aes_rotate(data):

aux=data[0]

for i in range(0,3):

data[i]=data[i+1]

data[3]=aux

return data

AES Key Schedule Core Operation

def aes_KS_core(word,it):

word =  aes_rotate(word)

newWord = []

for i in word:

newWord.append(sbox[i])

newWord[0]^=rcon[it]

return newWord

AES expandKey Operation

def aes_expandKey( key, expandedKeySize):

keyLength=len(key)

rconIteration = 1

tmp = [0,0,0,0]

expandedKey = []

for i in range(0,keyLength):

expandedKey.append(ord(key[i]))

while (len(expandedKey) < expandedKeySize):

for i in range(0,4):

tmp[i] = expandedKey[(len (expandedKey) - 4) + i]

if(len(expandedKey) % keyLength == 0):

tmp=aes_KS_core(tmp, rconIteration)

rconIteration+=1

if(keyLength == 32 and ((len(expandedKey) % keyLength) == 16)):

for i in range(0,4):

tmp[i] = sbox [tmp[i]]

for i in range(0,4):

expandedKey.append(expandedKey [len(expandedKey) - keyLength] ^ tmp[i] )

return expandedKey

Como se puede ver, la función aes_rotate mueve los 4 bytes (un word) hacia la izquierda. aes_KS_core rota el word y compone un nuevo word a partir de el S-box (de forma similar a como lo hace Sub Bytes), por último hace XOR del primer byte y una posicion en el RCon.

Sabiendo esto, se entiende moderadamente bien la función aes_expandKey

Las claves de ronda (Round Keys) son claves de 16 bytes extraidas de la clave expandida, no tiene mucha complicación: def aes_CreateRoundKey(expandedKey,start):

roundKey = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]

for i in range(0,4):

for j in range(0,4):

roundKey[i][j] = expandedKey[ (i*4)+j+start]

return roundKey Ok, ahora hay que juntarlo todo: Si recordais, antes se hablo de rondas... pues bien, en ellas se ejecutan cada una de las funciones de cifrado: def aes_Round(data,key):

data = aes_SubBytes (data)

data = aes_ShiftRows (data)

data = aes_MixColumns (data)

data = aes_AddRoundKey (data,key)

return data Pero se utilizan mas funciones a parte de las rondas la estructura seria mas o menos asi:

def aes_Main(data,expandedKey, nbrRounds):

block = []

for i in range(0,4):

block.append(data[i4:(i+1)4])

roundKey = aes_CreateRoundKey(expandedKey, 0)

block = aes_AddRoundKey(block, roundKey)

for i in range(1,nbrRounds):

roundKey = aes_CreateRoundKey(expandedKey, 16*i)

block = aes_Round(block, roundKey)

roundKey = aes_CreateRoundKey(expandedKey, 16*nbrRounds)

block = aes_SubBytes(block)

block = aes_ShiftRows(block)

block = aes_AddRoundKey(block, roundKey)

data = ""

for b in block:

data+=(arr2str(b))

return data Por ultimo, si añadimos la parte donde se comprueba que la longitud de clave sea correcta y se genera la clave expandida (y de paso añadimos "\0"'s para rellenar los bloques y las contraseñas)...

def aes_Encrypt(data,passwd,size=256):

if (size not in [128,192,256]):

raise Exception('Invalid key length')

while (len(passwd)<(size/8)):

passwd+="\0"

while ((len(data)%0x10)!=0):

data+="\0"

data = str2arr(data)

nbrRounds = {

128: 10,

192: 12,

256: 14

} [size]

expandedKeySize = (nbrRounds+1) * 16

expandedKey = aes_expandKey(passwd, expandedKeySize)

data = aes_Main(data, expandedKey,nbrRounds)

return data El cifrado ya esta :D El descifrado es bastante similar (de hecho al ser igual la parte de las claves, se ahorra mucho codigo :)

Se utiliza una S-box inversa (por decirlo de alguna forma, si teniendo un numero (n) y la posicion n de la S-box es s , la posicion s en la S-box inversa nos devolvera el numero original (n) )

AES RS-Box (reverse S-Box)

rsbox = [

0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,

0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,

0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,

0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,

0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,

0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,

0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,

0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,

0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,

0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,

0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,

0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,

0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,

0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,

0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,

0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ]

Así, la función inversa (de descifrado) de Sub Bytes es igual, pero utilizando la S-box inversa: pre class="brush:python">def aes_invSubBytes(data):

for i in range(0,4):         for j in range(0,4):             data[i][j]=rsbox[data[i][j]]

return data El inverso de Shift Rows consiste en mover las filas igual, pero en sentido contrario: def aes_invShiftRows(data):

new_data=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]

for i in range(0,4):

for j in range(0,4):

new_data[i][j] = data[i] [ (j- i) % 4]

return new_data  La parte de Mix Columns solo cambia en cuanto a las constantes por las que se multiplica:

AES Reverse MixColumns Operation

def aes_invMixColumns(data):

for i in range(0,4):

column = []

for j in range(0,4):

column.append(data[j][i])

column = aes_invMixColumn(column)

for j in range(0,4):

data[j][i] = column[j]

return data

AES Reverse MixColumn Operation

def aes_invMixColumn(data):

tmp = []

for i in data:

tmp.append(i)

for i in range(0,4):

data[i]=galoisMult(tmp[i],14) ^ galoisMult(tmp[ (i+3)%4],9) ^ \

galoisMult(tmp[ (i+2)%4],13) ^ galoisMult(tmp[(i+1)%4],11)

return data Si estas esperando una función inversa a Add Round Key, que sepas que no la hay, está basada en XOR, asi que no necesita una version inversa Pero si que existe una ronda inversa (que consiste en utilizar el inverso de las operaciones, pero tambien en un orden distinto): def aes_invRound(data,key):

data = aes_invShiftRows (data)

data = aes_invSubBytes (data)

data = aes_AddRoundKey (data,key)

data = aes_invMixColumns (data)

return data Por supuesto, las rondas no van en el mismo orden (en vez de ir de 0 al maximo de rondas, van en "direccion contraria", decreciendo), y tambien hay otros pequeños cambios de orden: def aes_invMain(data,expandedKey, nbrRounds):

block = []

for i in range(0,4):

block.append(data[i4:(i+1)4])

roundKey = aes_CreateRoundKey(expandedKey, 16*nbrRounds)

block = aes_AddRoundKey(block, roundKey)

for i in range(nbrRounds-1,0,-1):

roundKey = aes_CreateRoundKey(expandedKey, 16*i)

block = aes_invRound(block, roundKey)

roundKey = aes_CreateRoundKey(expandedKey, 0)

block = aes_invShiftRows(block)

block = aes_invSubBytes(block)

block = aes_AddRoundKey(block, roundKey)

data = ""

for b in block:

data+=(arr2str(b))

return data Por ultimo, queda hacer la función que se encarga de todo el proceso de expansion de clave y descifrado (la diferencia con la de cifrado se reduce a ejecutar la funcion principal inversa, en vez de la normal): def aes_Decrypt(data,passwd,size=256):

if (size not in [128,192,256]):

raise Exception('Invalid key length')

while (len(passwd)<(size/8)):

passwd+="\0"

while ((len(data)&0xFF)<0xF):

data+="\0"

data = str2arr(data)

nbrRounds = {

128: 10,

192: 12,

256: 14

} [size]

expandedKeySize = (nbrRounds+1) * 16

expandedKey = aes_expandKey(passwd, expandedKeySize)

data = aes_invMain(data, expandedKey,nbrRounds)

return data Y ya esta listo :D, podemos cifrar cualquier cosa de 16 bytes... pero eso seguramente se quede corto, no pretendo hablar aqui de modos de operacion de cifrado de bloques (como solucionar esta problema), pero dire donde podeis encontrar mas informacion:

http://www.progressive-coding.com/tutorial.php?id=3 (en inglés) http://es.wikipedia.org/wiki/ Modos_de_operaci%C3%B3n_de_una_unidad_de_cifrado_por_bloques Como ejemplo muuuuy sencillo (y para nada seguro), se podrian escribir estas funciones:

AES sample multi-byte encryption

def aes_Long_Encrypt(text,key):

out=""

while (len(text)>15):

out+=aes_Encrypt(text[0:16],key)

text=text[16:]

if (len(text)>0):

out+=aes_Encrypt(text,key)

return out

AES sample multi-byte decryption

def aes_Long_Decrypt(text,key):

out=""

while (len(text)>15):

out+=aes_Decrypt(text[0:16],key)

text=text[16:]

if (len(text)>0):

out+=aes_Decrypt(text,key)

return out Aqui teneis el codigo fuente completo [ aes.py ]

Y eso es todo! [Referencias] (casi todo en inglés) http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf http://brandon.sternefamily.net/posts/2007/06/aes-tutorial-python- implementation/ http://www.moserware.com/2009/09/stick-figure-guide-to-advanced.html http://www.progressive-coding.com/tutorial.php http://es.wikipedia.org/wiki/Advanced_Encryption_Standard

VirtualSlug un bot de Real Time Battle

Pues por aqui, en la lanparty FicOnLan, se organizo un torneo de Real Time Battle, que es un juego que consiste en programar un robot (en cualquier lenguaje, la informacion se pasa por la entrada y salida estándar) para que luche contra otros, esto fue lo que presente:

[ VirtualSlug.tar.gz ]

Por supuesto, quedo de ultimo, funciono incluso peor que los robots que se quedaban quietos xD... parece ser que ir de frente al medio de la batalla no es tan buena idea cuando no es un uno contra uno :)

Teneis mucha informacion del tema en [ http://rtb.belay.es/ ] o en [ http:// realtimebattle.sourceforge.net/ ]

Tambien hubo un concurso de programacion... pero la verdad es que no me gusto lo que consegui, cuando lo pula un poco (cuando consiga hacer una deteccion de colisiones decente) lo subo

Hasta ahora!