2021/10/13

Teensy al rescate

Me había contado una persona, obviamente no puedo decir cuál, que cuando entró hace ya muchos años en una importante empresa informática de alcance mundial, precursora por ese entonces en tener la modalidad de algunos días remotos, que en la laptop que le habían dado había un agente que reportaba a un sistema de monitoreo y si no interactuabas con la máquina te escrachaba. No recuerdo que recurso utilizó para engañarlo, si fué físico o por software.

Como te había contado trabajo con dos máquinas físicas, la power del trabajo llena de virtuales y la de medio pelo mía, llena de pantallas y con un buen teclado. Normalmente no uso de modo directo la del trabajo, pero a veces me viene bien poner algo en las pantallas, por ejemplo durante un reciente ejercicio de ciberseguridad mostrar un mapa de la red.

El problema es que se bloquea por falta de interacción y tiene una política que me impide cambiar el tiempo de activación del bloqueo, que debe andar por unos diez minutos. Encima está lejos y me tengo que desplazar...

Aunque a mi me moleste, es una política correcta, pues la idea es que el equipo no debe quedar expuesto si uno se distancia.

Me encuentro en una situación parecida, pero no para evitar ser controlado. En el caso de la otra persona, había un escenario adversarial entre la empresa y la persona. En mi caso, estoy pagando el efecto colateral del escenario de un tercero, adversario, afecta la usabilidad.

Necesito entonces algún método para evitar el bloqueo. Entonces recuerdo que tengo algún teensy libre, esto es un microcontrolador que se puede comportar como un dispositivo usb, en este caso en particular me interesa como teclado o mouse. 

El teensy viene a ser como un arduino, de hecho arduino ide con la extensión apropiada se usa para programarlo. No voy a repetir los detalles de la instalación de arduino ide más teensy, están en Rescatando un touchpad y Usando un touchpad con usb, donde tambien hay más detalles respecto a los botones y a comportarse como un teclado.

Lo primero que pensé es que envíe un evento de teclado, pero ¿cuál? No tiene que escribir. Usar algún modificador tambien es un problema si justo estoy escribiendo que me cambie minúscula por mayúscula y no me de cuenta.

Lo mejorcito será entonces mover apenas el mouse para un lado, luego revertir.

Necesito tambien algún feedback de que está activo, de que está actuando y poder desactivarlo sin desconectarlo, pero esto último me obligaría a armarle un gabinete, pues no tiene botón, sólo un led.

El programa es increiblemente sencillo, sólo hay que configurar apropiadamente el entorno.

 

const int ledPin = 6;

void setup() {
   pinMode(ledPin, OUTPUT);
}
void loop()
{
   Mouse.move(1, 1);
   digitalWrite(ledPin, HIGH);
   delay(1000);
   Mouse.move(-1, -1);
   digitalWrite(ledPin, LOW);
   delay(10000);   
}

 

Me lleva más tiempo mirar fijo la máquina a ver si esta funcionando que

sudo modprobe usbmon

sudo wireshark

 

Selección de usb en wireshark
Selección de usb en wireshark

 

Elegir el monitor correcto al inicio, atinarle al filtro, esperar dos mensajes y mirarlos fijos y ver que uno corresponde a Mouse.move(1, 1) y otro a Mouse.move(-1, -1).



Mouse.move(-1,-1)
Mouse.move(-1,-1)



Mouse.move(1,1)
Mouse.move(1,1)


De todos modos, mientras escribo esto, lo dejé conectado y ya pasaron 29 minutos sin bloquearse, debe andar bien.

2021/10/11

H4CK3D 2021: mejor resolución de dependencias en Makefile

Te recuerdo que todo esto tiene el objetivo de armar un ataque al comportamiento de un programa desde el hardware para mostrar en H4CK3D 2021. Como productos colaterales, una mejor adaptación de grahamedgecombe/icicle a ciaa/icicle, en parte aceptada como PR, en parte quedará en cpantel/evilCodeSequence y un montón de aprendizaje de mi parte, que te comparto como siempre, más si venís de leer lo anterior.

El problema que vengo arrastrando es el tiempo:
  • yosys: 1:10
  • nextpnr-ice40: 1:45
  • iceprog: 2:00

 

No puedo evitarlos, pero como habías visto en la entrada anterior, hay dos ramas, una para el hardware y otra para el software. Convergen en la que llamo "system", que es al resultado del proceso de hardware pegotearle la imagen obtenida por el proceso de software.

Makefile detallado original
Makefile detallado original


Tal como está el Makefile, como que no está fácil, así que lo desarmé para terminar de entenderlo y lo volví a armar. De paso, veamos el proceso pero un poco menos detallado:


Makefile con reglas sencillas
Makefile con reglas sencillas, diagrama simplificado

Eliminé algunas partes del diagrama para recalcar las cuatro reglas y lo de la memoria. 

Arriba a la derecha, se arma la imagen, lo cual incluye tanto nuestro programa como otro código, de esto aún no sé mucho, parece haber algo de inicialización y el de arranque, ya seá desde la RAM o desde flash, este es un punto interesante a explorar, cargar el programa desde memoria SD, agilizaría aún más el proceso.

Arriba a la izquierda, como que estaba confundida la construcción de la imagen con la inclusión de RAM. De hecho, en el Makefile original si modificás el programa regenera sin necesidad el hardware. El primer icebram lo que hace es sólo reservar el espacio de memoria, luego, en system, nuevamente icebram, pone el programa en esa área reservada.

Las nuevas reglas las construí apuntando al final de reglas existentes, los cambios son en el Makefile:


software: BUILD/progmem.hex

Sencillo, pedís "software", hace lo necesario para construir progmem.hex.


hardware: $(ASC_SYN)

según ASC_SYN  = BUILD/$(TOP)_syn.asc viene a ser top_syn.asc en el diagrama.

 

system: $(BIN)

según BIN      = BUILD/$(TOP).bin es top.bin en el diagrama

 

Para romper el encadenamiento erróneo que había, convertí:

$(BLIF) $(JSON): $(YS) $(SRC) BUILD/progmem_syn.hex BUILD/progmem.hex BUILD/defines.sv

en

$(BLIF) $(JSON): $(YS) $(SRC) BUILD/progmem_syn.hex BUILD/defines.sv

Ahora, tal como es de esperar, puedo reemplazar el programa en sólo dos minutos en lugar de los cinco originales.


Respecto a utilizar la flash como arranque, mmh, estuve mirando con más detalle y dice ser SPI, lo cual implica cuatro señales: SCLK (el clock), SS (Slave Select para elegir el chip), MOSI (datos del master al slave) y MISO (datos del slave al master).

Para comenzar, en top.sv parecen estar los dos primeros, pero los últimos están como inout

Me da olor a Dual SPI. Más que luego se instancian uno tales "flash_io" del tipo SB_IO y TRELLIS_IO que según veo por encima rapidito tienen que ver con tristate.

 Luego, en flash.sv, dice

     assign clk_out = clk;

y no me suena que SPI soporte 36Mhz.

¡Qué lástima! Me había hecho ilusiones, incluso le había soldado los pines al sdcard reader que tengo. Esto está muy encima de mis habilidades y no es bloqueante ni aporta mucho al malware que tengo que hacer funcionar...



2021/10/10

H4CK3D 2021: cambios al Makefile

Esquivándole el bulto al problema principal, que es implementar el ataque, sigo introduciendo cambios y mejorías, en este caso al Makefile.

 

Relacionado al Makefile, ya había puesto:

BOARD    ?= edufpga
PROGRAM  ?= hello

Lo primero debido a que desde ciaa/icicle para acá, es la placa que vamos a usar, no quiero estar poniendo BOARD=edufpga en cada llamada a make. Lo segundo es que el foco ha pasado de elegir el BOARD a elegir el PROGRAM.

Tambien había modificado la regla para defines.sv para que tome la configuración de puertos y dispositivos de la carpeta del programa. Esto implica que al hacer un nuevo programa, convienen hacer un make clean para que se reescriba con la nueva configuración.

 

Optimización del programa

 

Una mejoría está relacionada a la optimización del código del programa. Por default grahamedgecombe/icicle lo puso en -Os, que es razonable, hacer el programa lo más chico posible, menos RAM, menos ocupación de la FPGA. Pero para un inexperto como yo en assembly en general y de risc-v en particular, se me hace bastante complicado analizar el código para implementar el ataque, así que ahora existe la opción:

OPTLEVEL ?= -Os

si en la invocación agregás

OPTLEVEL=

como en 

make PROGRAM=access_control OPTLEVEL=""

no optimiza.

 

Uso de azar en PNR


Apenas complicado fué el asunto del SEED. ¿Qué es el SEED? Pues que para el place and route funcione bien, el algoritmo utiliza pseudoazar.

¿Qué es place and route? Es el proceso de ubicar y conectar los componentes en un circuito electrónico en este caso en la FPGA.

¿Qué es pseudoazar? Bueno, primero veamos que es azar. Azar es... tirar los dados. El problema con el azar es que no podés repetirlo y si usás azar para algo, si tenés una falla y no lo podés repetir, se complica diagnosticar la cause. Pero necesitás el azar, ¿en qué quedamos?

El pseudoazar viene a ser tirar los dados un montón de veces y guardar lo que te salió en una lista. Luego, cuando lo necesitás tirás una sola vez los dados para elegir donde empezar en esa lista. Entonces tenés azar y repetición a la vez si necesitás, eligiendo la posición en la lista. Es un azar apto para estas cosas, pero no ante un adversario, pues no bien reusás la lista o comparando ejecuciones, se puede obtener la lista.

La gente que realmente sabe criptografía sabrá ser generosa con mi explicación.

Como el archivo generado depende entonces del pseudoazar, si necesito que dado el mismo diseño obtener el mismo placement and routing, necesito el control de SEED, que en nextprn-ice40 se determina con el parámetro --seed.

Me interesa que genere siempre lo mismo debido a que quizás para el ataque pueda, no creo, hacer la modificación no sobre el código HDL sino en una etapa posterior, veremos.

En el Makefile primero hay de definirlo con vacío como default:

SEED     ?=

y luego en arch/ice40.mk, si SEED está vacío, sigue vacío, pero si hay algo, agregarle la key --speed.

ifeq ($(SEED),)
SEEDVAL =
else
SEEDVAL = --seed $(SEED)
endif

SEEDVAL luego es reemplazado en la línea:

nextpnr-ice40 $(QUIET) --$(SPEED)$(DEVICE) --package $(PACKAGE) $(SEEDVAL) --json $< --pcf $(PCF) --freq $(FREQ_PLL) --asc $@

 

¿Por qué no hice la misma lógica con OPTLEVEL? Buena pregunta... debe ser por que es fácil escribir "" y no "--seed 3".

 

Dependencias

 

El diagrama de Pablo, redibujado así, no me alcanza, necesito más detalle. Para ello necesito corregir un poco el Makefile.

 

icicle flow high level
icicle flow high level


Para comenzar, mover todo objeto temporario o construido a una carpeta, BUILD es un buen lugar, luego, mover los .ys a arch.

De tanto revisar como le fuí tomando la mano y generé un nuevo diagrama:

 

ICE40 flow low level
ICE40 flow low level


 

El problema restante que me queda es el tiempo:

  • yosys: 1:10
  • nextpnr-ice40: 1:45
  • iceprog: 2:00

Tengo que armar reglas de Makefile tal que pueda hacer tener estas cuatro:

  • software: genera progmem.hex.
  • hardware: genera top_syn.asc.
  • system: genera top.bin.
  • all: que haga todo sin tanta sutileza, lo que está ahora.

Será otro día...


 

 



H4CK3D 2021: sigue el desvío, para bien

Esto más que H4CK3D debería llamarse... no sé, queda H4CK3D pues todas estas entradas son lo que no me alcanzaría jamás el tiempo para mostrar en una charla de una conferencia, es toda la investigación previa, la familiarización con el entorno y las herramientas.


Además, se viene la charla y quizás esté un poquito retrasado, eso me pone en modo procrastinador productivo, no hago lo que debería pero si algo útil en su lugar.

 

La excusa de hoy es haber agregado un módulo de servo. ¿Qué es un servo? Sos una de las pocas personas que lee este blog y calculo que deberías estar en sintonía con estos temas, así que si no sabés, podés buscar mejores detalles en Internet, pero te hago un resumen.

 

Es un motorcito con unos engranajes que dependiendo de una señal se posiciona en un ángulo. Esa señal es un pulso que debe cumplir con que el período sea de 20ms y el pulso mida entre 1 y 2 ms.

 

Servo al máximo
Servo al máximo

Servo al mínimo
Servo al mínimo

Esa forma se puede obtener en un microcontrolador con este sencillo programa,


#define PERIOD    0.020
#define MIN_WIDTH 0.001
#define MAX_WIDTH 0.002
float period = PERIOD;
float width = MIN_WIDTH;
int main() {
    DigitalOut servo1(PC_8);   
    while(1) {
       for(int i=0; i< 200; ++i) {
          servo1=1;
          wait(MIN_WIDTH);
          servo1=0;
          wait(PERIOD - MIN_WIDTH);
       }    
       for(int i=0; i< 200; ++i) {
          servo1=1;
          wait(MAX_WIDTH);
          servo1=0;
          wait(PERIOD - MAX_WIDTH);
       }    
    }


La versión completa en  https://os.mbed.com/users/cpantel/code/servo_software/

 

Si ya programaste alguna vez un microcontrolador o si pensás un rato, notarás que no bien quieras hacer algo más, vas a tener tocar los wait()s para compensar, una pesadilla inmanejable. Podrías entonces usar un timer que genere excepciones pero la verdad es que buena parte del los microcontroladores, al menos todos los que me he cruzado, que no son muchos, tiene un dispositivo llamado PWM al que le decís generame pulsos de tales características y lo hacen, aunque hayas logrado colgar al micro.

 

#define PERIOD    0.020
#define MIN_WIDTH 0.001
DigitalIn openButton(D4);
DigitalIn moveUpButton(D5);
DigitalIn moveDownButton(D6);
DigitalIn closeButton(D7);
PwmOut led1(LED1);
PwmOut servo1(PC_8);
PwmOut servo2(PC_9);
float period = PERIOD;
float width = MIN_WIDTH;
int main() {
    servo1.period(PERIOD);
    servo1.pulsewidth(width);
    while (1) {
// hacé lo que quieras acá
    }
}


Listo,un ejemplo más completo que según aprietes los botones mueve el servo en

https://os.mbed.com/users/cpantel/code/servo_PWM/

Volviendo a la cpu risc v en la edu-ciaa-fpga, agregué un PWM restringido, que sólo sirve para manejar un servo. Esto es, con un PWM normal puedo darle por software cualquier frecuencia y duración de pulso, lo cual no necesariamente es correcto. Con el módulo servo, no importa lo que hagas en software, la salida va a ser correcta.


Tuve algunas dificultades, por ejemplo, tengo módulos en los que puedo leer (rtc), en este escribir, pero no pude leer y escribir, ya le dedicaré un rato a ello.

 

La otra, es que tuve que implementar con una condición lógica bastante larga en lugar de unos case que más adelante me hubiesen permitido usar funciones más avanzadas de (system)verilog para generar los casos, ya le dedicaré un rato a ello.

Ya que estaba, aproveché para mejorar los ifdef que habilitan los puertos y los módulos. Antes hubiese sido así:

En top.sv:


`ifdef ARDUINO
    output logic [31:0] arduino,
`endif

y en la instanciación de icicle, en la interfaz:

`ifdef ARDUINO
        .arduino(arduino),
`endif

 

luego en icicle.sv:

 

`ifdef ARDUINO
    //código...

 

El problema es que al hacer el servo, que usa un pin del arduino, tenía que medio hackearle ese pin pues al habilitar ARDUINO, los pines los usaba el módulo arduino. Lo que hice fue separar en _CONN y _DEV, lo cual me permite habilitar los pines del arduino y luego conectarlos al módulo arduino o no instanciar ese módulo y usarlos desde otro.

Esto nos lleva a que cada programa puede usar distintas configuraciones de hardware, asi que además agregué que a los defines.sv que se le copia la base de la placa, se le agreguen lo que el programa pida, entonces en boards/edufpga-defines.sv tengo


// Defines for EDU-FPGA
`define noPMOD0_DEV
`define noPMOD1_DEV
`define noARDUINO_DEV
`define noRTC_DEV
`define noSERVO_DEV
`define noUART_DEV

`define noPMOD0_CONN
`define noPMOD1_CONN
`define noARDUINO_CONN

que cumple la función de documentar que hay disponible, luego en programs/servo_device/edufpga-defines.sv:

// Defines for servo_device running in EDU-FPGA
`define SERVO_DEV
`define UART_DEV

`define ARDUINO_CONN

y esto se arma en el Makefile:

cat boards/$(BOARD)-defines.sv programs/$(PROGRAM)/$(BOARD)-defines.sv > defines.sv

 

En el mismo camino de facilitar cambiar el programa, pasé a un .h el mapa de memoria, actualmente:


#define LEDS        *((volatile uint32_t *) 0x00010000)
#define BUTTONS     *((volatile uint32_t *) 0x00010004)
#define PMOD0       *((volatile uint32_t *) 0x00010008)
#define PMOD1       *((volatile uint32_t *) 0x0001000c)
#define ARDUINO     *((volatile uint32_t *) 0x00010010)
#define RTC         *((volatile uint32_t *) 0x00010014)
#define SERVO       *((volatile uint32_t *) 0x00010018)
#define UART_BAUD   *((volatile uint32_t *) 0x00020000)
#define UART_STATUS *((volatile uint32_t *) 0x00020004)
#define UART_DATA   *((volatile  int32_t *) 0x00020008)
#define MTIME       *((volatile uint64_t *) 0x00030000)
#define MTIMECMP    *((volatile uint64_t *) 0x00030008)

Con todos estos cambios, si algún día alguien quiere portar el programa a otra placa, le va a resultar más fácil.

Esto me llevó, por completitud, a adaptar todo el "legacy", comprobar que todos los programas funcionen, probablemente, con un poco de suerte... https://github.com/cpantel/evilCodeSequence/tree/v0.2.0


Me faltaría documentar un poco en el readme, uno de estos dias...