Skip to content
KitploitKITPLOIT
HerramientasExploitsBlog
Log in
Enviar
HerramientasExploitsBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
8cryptdo — Documentación y herramientas de ingeniería inversa para el cifrado de firmware de 8BitDo | Kitploit
Herramientas/GitHubGitHub/aeromodes/8cryptdo
Seguridad de Sistemas EmbebidosHerramientas de Cifrado/DescifradoSeguridad IoTIngeniería InversaCriptografíaSeguridad de Hardware e IoTAnálisis de BinariosPapers e InvestigaciónAnálisis de Firmware
GitHubaeromodes/8cryptdo

8cryptdo

Documentación y herramientas de ingeniería inversa para el cifrado de firmware de 8BitDo

29hace 1 díaAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir
Ver Repositorio

8CryptDo

Documentación y herramientas obtenidas mediante ingeniería inversa para el cifrado de firmware utilizado por 8BitDo en varios productos basados en GD32.

Consulta 8cryptdo.py para obtener una herramienta de descifrado y recifrado que aplica el algoritmo descrito en este documento.

La herramienta potencialmente permite la posibilidad de crear firmware personalizado. Sin embargo, este repositorio no incluye ninguno de los datos de firmware oficiales. Se puede encontrar un script para descargar los archivos de firmware más recientes en el repositorio fwupd/8bitdo-firmware, con algunos binarios de firmware más antiguos archivados.

Cabecera

Si se observa un archivo de firmware .dat en la superficie, está presente una cabecera en texto plano de 28 bytes.

root@kitploit:~
import struct

raw = open("sn30-v2_07.dat", "rb").read()
version, addr, payload_len = struct.unpack("<III", raw[:12])
# version     = 207        -> firmware == v2.07
# addr        = 0x08003400 -> destination (probably)
# payload_len = 99328      -> section payload length in bytes

En los archivos de firmware recientes hay un valor desconocido de 32 bits colocado después de estos. El resto de la cabecera es cero.

Capa de encadenamiento sin clave

En ciertos archivos de firmware aparece un patrón: hay una capa que mezcla cada palabra con la siguiente.

root@kitploit:~
def rotr(x, r):
    return ((x >> r) | (x << (32 - r))) & 0xFFFFFFFF

dechained = [words[0]]
for i in range(1, len(words)):
    dechained.append(words[i] ^ rotr(words[i - 1], 3))

Este encadenamiento se reinicia en cada límite de bloque de 128 palabras. La primera palabra de un bloque (pos = 0) no tiene término predecesor, y pos = 1..127 se encadenan desde la palabra anterior.

Una capa sin clave no añade seguridad. Al eliminarla se revela un intermedio más limpio (dechained), donde lo único que queda ocultando el texto plano es el flujo de claves.

root@kitploit:~
P[i] = dechained[i] ^ keystream[i]

Factorización del flujo de claves

El repositorio fwupd/8bitdo-firmware tiene un catálogo de firmware más antiguo. Todos estos firmwares parecían tener la capa de encadenamiento.

Al aplicar XOR entre algunos de ellos después de eliminar la capa de encadenamiento se revelan largas secuencias de ceros y una estructura legible.

root@kitploit:~
plaintext_diff = [a ^ b for a, b in zip(dechained_a, dechained_b)]

Si se utiliza el mismo flujo de claves en ambos...

root@kitploit:~
a = dechained_a[i] ^ dechained_b[i]
b = (P_a[i] ^ keystream[i]) ^ (P_b[i] ^ keystream[i])
c = P_a[i] ^ P_b[i]
assert a == b == c

el flujo de claves se cancela.

Este es un clásico many-time pad. Un flujo de claves así solo es seguro si se usa una vez. Por alguna razón, 8BitDo decidió usar no solo un flujo de claves por producto, sino un único flujo de claves en varios productos. Esto debilitó significativamente la seguridad de su cifrado e hizo posible el resto del análisis aquí presentado.

Lectura del flujo de claves

Una vez que sabes que dos textos planos están combinados con XOR, puedes adivinar parte de uno y restar tu conjetura para leer el otro. Esto recupera el keystream en ese punto.

root@kitploit:~
keystream[i] = dechained[i] ^ P[i]
for fw in all_fw:
    P[fw][i] = dechained[fw][i] ^ keystream[i]

Esto sería más fácil con cadenas, pero el truco más productivo aquí fue la reubicación entre versiones de instrucciones ARM. Una función puede aparecer en dos versiones de firmware en posiciones desplazadas. Donde se conoce el flujo de claves en una versión, puedes alinear el código compartido en otra y recolectar nuevos valores del flujo de claves.

Explotar esto llevó las palabras conocidas del flujo de claves de ~1.500 a varios miles, aproximadamente el 18% del firmware legible.

Regla del flujo de claves

Con una muestra más grande del keystream, los patrones se hicieron visibles. Tenía posiciones separadas por 16 palabras que avanzaban en una cantidad casi constante, y cada byte se movía en una cantidad predecible.

Mediante prueba y error, se descubrió que cada palabra del flujo de claves en un bloque sigue una fórmula:

root@kitploit:~
STEP  = 0x92A753FA
A_MUL = 0x80000301
ROT   = 18
UINT32_MAX  = 0xFFFFFFFF

def rotr(x, r):
    r &= 31
    return ((x >> r) | (x << (32 - r))) & UINT32_MAX if r else x

def block_base(block):
    return (block * A_MUL + block // 2) & UINT32_MAX

def keystream(block, pos, block_key):
    counter = (block_base(block) + pos * STEP) & UINT32_MAX
    mask = rotr(block_key, ROT * pos)
    return counter ^ mask

Dentro de un bloque, se recorre un contador simple (block_base(block) + pos*STEP), y se aplica XOR con una máscara que comienza en block_key y rota 18 bits en cada paso. El bloque completo de 128 palabras está determinado por un único número de 32 bits, block_key.

Una palabra conocida desbloquea un bloque entero. Reordenando la fórmula se obtiene la block_key a partir de cualquier palabra conocida del flujo de claves:

root@kitploit:~
def block_key_from_known(block, pos, known_keystream):
    counter = (block_base(block) + pos * STEP) & UINT32_MAX
    return rotr(known_keystream ^ counter, -ROT * pos & 31)

Tabla de semillas

¿De dónde venía la clave de cada bloque? Se factoriza en dos mitades de 16 bits extraídas de una tabla de semillas de 256 entradas:

root@kitploit:~
def block_key_of(block, seed_table):
    hi = seed_table[block]
    lo = seed_table[block ^ 0xF9]
    return (hi << 16) | lo

La tabla de semillas en sí parece no tener una fórmula específica, probablemente son 512 bytes constantes almacenados en algún lugar del bootloader de cada dispositivo. Sin embargo, el XOR con 0xF9 proporciona un bonito efecto secundario: una ley de espejo. Un bloque y su pareja block ^ 0xF9 se construyen a partir de las mismas dos entradas de la tabla intercambiadas.

root@kitploit:~
mirror = block_key_of(block ^ 0xF9, seed_table)
assert mirror == rotr(block_key_of(block, seed_table), 16)

Esto descifró regiones previamente desconocidas. En lugar de adivinar una block_key completa de 32 bits a ciegas, puedes adivinar dos mitades de 16 bits y puntuar qué elección convierte ambos bloques en cadenas creíbles o instrucciones ARM.

Con eso, se obtuvo una cobertura del 100% de todos los datos de firmware que utilizan este cifrado específico.

Trabajo pendiente

Este cifrado parece cubrir la mayoría de los productos 8BitDo alrededor de 2020, y los productos actuales que todavía usan SoCs GD32 (incluido el SN30 Pro).

Algunos dispositivos como el M30 y el Zero 2 parecen ser multisección, con algunos datos de firmware que usan este cifrado y otro firmware con un esquema de cifrado diferente.

Los dispositivos más nuevos, al menos aquellos con un SoC diferente, parecen tener un esquema de cifrado de firmware más fuerte. Un análisis ingenuo muestra que podría ser compartido, pero no es seguro. Es necesario investigarlo más a fondo.

Descargar herramienta