La herramienta akheron
está escrita en Python y diseñada para ayudar a dar visibilidad a las comunicaciones entre chips UART y ayudar a comprender, revertir y manipular esas comunicaciones para discernir la funcionalidad del dispositivo (tanto intencionada como no intencionada). Para ello, admite varios modos de funcionamiento:
La comunicación entre chips a través de UART sigue siendo una opción de diseño común que se encuentra en muchos dispositivos y se parece a esto:
+------------+ +------------+
| TXD +-------------------------->+ RXD |
| CHIP A | | CHIP B |
| RXD +<--------------------------+ TXD |
+------------+ +------------+
En este ejemplo, el chip A envía datos al chip B enviando datos fuera del pin de transmisión (TX) del chip A y el chip B recibe los datos entrantes en su pin de recepción (RX). El chip B envía datos a través de su pin de transmisión (TX) para que el chip A los reciba en el pin de recepción (RX) del chip A.
Pero, ¿qué se envían exactamente entre ellos?
Si nos convertimos en una máquina física intermedia, ¡podremos descubrirlo!
La herramienta akheron
está diseñada para ejecutarse en un sistema con dos puertos serie (los adaptadores USB a serie están bien, siempre que el sistema operativo del sistema los admita), actuando como un proxy para enviar tráfico entre esos dos puertos. Algo como esto:
^ + ^ +
| v | v
+---------------------------------+
| |TXD1 RXD1| |TXD2 RXD2| |
| +---------+ +---------+ |
| UART 1 UART 2 |
| |
| Machine-in-the-Middle |
| (running Akheron Proxy) |
| |
+---------------------------------+
Si cortamos físicamente los rastros de comunicación entre el chip A y el chip B y luego los enrutamos a los puertos serie de nuestra máquina intermedia, podremos ver qué se envían esos chips entre sí a través de sus comunicaciones UART con akheron
:
+------------+ +------------+
| TXD +--------+ +------->+ RXD |
| CHIP A | | | | CHIP B |
| RXD +<-+ | | +--+ TXD |
+------------+ | | | | +------------+
| | | |
| v | v
+---------------------------------+
| |TXD1 RXD1| |TXD2 RXD2| |
| +---------+ +---------+ |
| UART 1 UART 2 |
| |
| Machine-in-the-Middle |
| (running Akheron Proxy) |
| |
+---------------------------------+
¡Con una configuración como tal, akheron
está listo para usar!
La versión 0.1 akheron
es la primera iteración de este esfuerzo y nace de un código de prueba de concepto. Es una herramienta de línea de comandos que utiliza un REPL estándar para la interacción.
La herramienta akheron
requiere Python 3.6 o posterior y utiliza la biblioteca pyserial
para interactuar con los puertos serie del sistema. Fue probado tanto en macOS 10.15 como en Ubuntu 18.04.
pip install -r requirements.txt
Puede iniciar akheron
desde una ventana de terminal en el directorio de nivel superior del repositorio:
./akheron.py
En muchos sistemas, el acceso a los dispositivos serie está restringido. Para evitar ejecutar akheron
con privilegios elevados, asegúrese de que su cuenta de usuario pertenezca al mismo grupo que el dispositivo que desea utilizar. En Linux, es probable que el dispositivo serie sea miembro del grupo dialout
. Agregar su cuenta de usuario a ese grupo (por ejemplo, sudo usermod -a -G dialout $USER
) debería permitirle acceder al dispositivo. Para que pueda ver los cambios, es posible que deba cerrar sesión y volver a iniciar sesión en su cuenta, o posiblemente reiniciar el sistema.
Una vez que se ejecute, verá un banner y un mensaje >
:
$ ./akheron.py
######################################################
Akheron Proxy, UART proxy tool for inter-chip analysis
version 0.1
######################################################
>
Hay varios comandos disponibles en este mensaje, a los que puede acceder escribiendo help
:
> help
Documented commands (type help <topic>):
========================================
capturedump checksumget delimset portget replaceset stop
capturestart checksumset help portset replay watch
capturestop delimget list replaceget start
Undocumented commands:
======================
exit quit version
Puede obtener más ayuda sobre un comando específico escribiendo help <command>
.
> list
/dev/ttyS0
/dev/ttyUSB0
/dev/ttyUSB1
/dev/ttyUSB2
/dev/ttyUSB3
También puede obtener una lista detallada:
> list -v
/dev/ttyS0
desc: ttyS0
hwid: PNP0501
/dev/ttyUSB0
desc: Quad RS232-HS
hwid: USB VID:PID=0403:6011 LOCATION=1-1:1.0
/dev/ttyUSB1
desc: Quad RS232-HS
hwid: USB VID:PID=0403:6011 LOCATION=1-1:1.1
/dev/ttyUSB2
desc: Quad RS232-HS
hwid: USB VID:PID=0403:6011 LOCATION=1-1:1.2
/dev/ttyUSB3
desc: Quad RS232-HS
hwid: USB VID:PID=0403:6011 LOCATION=1-1:1.3
Los siguientes pasos reenviarán el tráfico entre puertos, lo 'vigilarán' y luego detendrán el reenvío de tráfico:
> portset A /dev/ttyUSB1 115200
> portset B /dev/ttyUSB2 115200
> start
Data now PASSING between ports "/dev/ttyUSB1" <-> "/dev/ttyUSB2"...
> watch
Watching data passed between ports. Press CTRL-C to stop...
A -> B: 0x61 0x61 0x73 0x73 0x64 0x64
B -> A: 0x31 0x32 0x33 ^C
Watch mode exited.
> stop
Data now BLOCKED between ports "/dev/ttyUSB1" <-> "/dev/ttyUSB2".
>
Este es el mismo flujo, pero con un delimitador de inicio de mensaje de 0x37
establecido:
> portset A /dev/ttyUSB1 115200
> portset B /dev/ttyUSB2 115200
> delimset start 0x37
> start
Data now PASSING between ports "/dev/ttyUSB1" <-> "/dev/ttyUSB2"...
> watch
Watching data passed between ports. Press CTRL-C to stop...
A -> B: 0x37 0x71 0x77 0x65 0x65 0x72
0x37 0x64 0x66 0x61 0x64
0x37 0x73
0x37 0x68 0x68
B -> A: 0x37 0x6e 0x6d 0x62
0x37 0x69 0x69
A -> B: 0x37 0x61 0x73 0x64 ^C
Watch mode exited.
> stop
Data now BLOCKED between ports "/dev/ttyUSB1" <-> "/dev/ttyUSB2".
>
Los siguientes pasos reenviarán el tráfico entre puertos, lo capturarán en un archivo, lo observarán, luego detendrán y volcarán el contenido de la captura y luego detendrán el reenvío de tráfico:
> portset A /dev/ttyUSB1 115200
> portset B /dev/ttyUSB2 115200
> start
Data now PASSING between ports "/dev/ttyUSB1" <-> "/dev/ttyUSB2"...
> capturestart mycap.out
Saving captured traffic to "mycap.out"...
> watch
Watching data passed between ports. Press CTRL-C to stop...
A -> B: 0x31 0x32 0x33
B -> A: 0x33 0x32 0x31
A -> B: 0x20 0x20 0x20
B -> A: 0x36 0x36 0x37 0x38 0x39 ^C
Watch mode exited.
> capturestop
Capture stopped
> capturedump mycap.out
1: A -> B: 0x31 0x32 0x33
2: B -> A: 0x33 0x32 0x31
3: A -> B: 0x20 0x20 0x20
4: B -> A: 0x36 0x36 0x37 0x38 0x39
> stop
Data now BLOCKED between ports "/dev/ttyUSB1" <-> "/dev/ttyUSB2".
>
Los siguientes pasos muestran el reenvío de tráfico entre puertos con reproducción de datos (en este ejemplo, un archivo de captura que contiene una sola línea) en esta secuencia:
0x64 0x61
con 0x99 0x91
y reproducir (tenga en cuenta los datos sustituidos en la salida)Checksum8Modulo256Plus1
de los bytes anteriores y reproduzca > portset A /dev/ttyUSB1 115200
> portset B /dev/ttyUSB2 115200
> start
Data now PASSING between ports "/dev/ttyUSB1" <-> "/dev/ttyUSB2"...
> replay /tmp/aaa
Replaying data from A -> B, press CTRL-C to exit watch mode...
A -> B: 0x61 0x73 0x64 0x61 0x73 0x64 ^C
Watch mode exited.
> replaceset A 0x64 0x61 -> 0x99 0x91
> replaceget
Replace port A pattern X -> pattern Y:
0x64 0x61 -> 0x99 0x91
Replace port B pattern X -> pattern Y:
> replay /tmp/aaa
Replaying data from A -> B, press CTRL-C to exit watch mode...
A -> B: 0x61 0x73 0x99 0x91 0x73 0x64 ^C
Watch mode exited.
> checksumset A 3
> checksumget
Replace on port A using checksum 'Checksum8Modulo256Plus1'
No replace checksum specified on port B
> replay /tmp/aaa
Replaying data from A -> B, press CTRL-C to exit watch mode...
A -> B: 0x61 0x73 0x99 0x91 0x73 0x72 ^C
Watch mode exited.
> stop
Data now BLOCKED between ports "/dev/ttyUSB1" <-> "/dev/ttyUSB2".
>