Recientemente en eBay me encontré con un lote de dispositivos USB interesantes (Epiphan VGA2USB LR) que reciben entrada VGA y envían video a USB como una cámara web. Me encantó la idea de que nunca tendría que volver a molestarme con los monitores VGA, y dado el apoyo declarado para Linux, me arriesgué y compré el lote completo por alrededor de 20 libras (25 dólares).
Después de recibir el paquete, conecté el dispositivo, pero ni siquiera parecía aparecer en el sistema como
UVC . Que esta mal
Estudié el sitio web del fabricante y descubrí que se requería un controlador especial para funcionar. Para mí, este era un concepto nuevo, porque el núcleo de mi distribución de Linux generalmente tiene controladores para todos los dispositivos.
Desafortunadamente, el soporte de controladores solo para estos dispositivos terminó en Linux 4.9. Por lo tanto, ninguno de mis sistemas lo verá (Debian 10 en Linux 4.19 o la última versión de LTS Ubuntu en Linux 5.0).
Pero se puede arreglar, ¿verdad? Por supuesto, los archivos vienen en
el paquete DKMS , que, bajo demanda, recopila el controlador del código fuente, como muchos controladores comunes ...
Es triste Pero aquí no es así.
Dentro del paquete solo estaba el binario precompilado
vga2usb.o
. Comencé a estudiarlo, preguntándome la complejidad de la ingeniería inversa, y encontré algunas líneas interesantes:
$ strings vga2usb.ko | grep 'v2uco' | sort | uniq v2ucom_autofirmware v2ucom_autofirmware_ezusb v2ucom_autofirmware_fpga
Entonces, ¿es realmente
FPGA -on-a-stick? ¿Cómo hacer que algo como esto funcione?
Otro hallazgo divertido y ligeramente inquietante fue la línea con los parámetros de clave privada DSA. Esto me hizo preguntarme: ¿qué puede proteger dentro del conductor?
$ strings vga2usb.ko | grep 'epiphan' | sort | uniq epiphan_dsa_G epiphan_dsa_P epiphan_dsa_Q
Para estudiar el controlador en su entorno normal, tomé una máquina virtual Debian 9 (última versión compatible) e hice
KVM USB Passthrough para dar acceso directo al dispositivo. Luego instalé el controlador y me aseguré de que funcionara.
Después de eso, quería ver cómo se ve el protocolo de comunicación. Esperaba que el dispositivo enviara cuadros sin formato o casi sin formato, ya que eso facilitaría la escritura de un controlador para el espacio del usuario.
Para hacer esto,
usbmon
módulo
usbmon
en el
usbmon
máquina virtual y
usbmon
Wireshark para capturar el tráfico USB hacia y desde el dispositivo durante el inicio y la captura de video.

Descubrí que cuando se inicia, una gran cantidad de paquetes pequeños se transmiten al dispositivo antes de que comience a capturar la imagen. Es probable que se base en la plataforma FPGA sin almacenamiento de datos. Cada vez que se conecta, el controlador transfiere el firmware en forma de
flujo de
bits FPGA al dispositivo.
Estaba convencido de esto al abrir una de las cajas:

Como para "descargar" el dispositivo, debe enviarle un flujo de bits / firmware, deberá buscarlo en archivos binarios precompilados.
binwalk -x
y comencé a buscar algunos objetos comprimidos (zlib). Para hacer esto, escribí un script de búsqueda de secuencia hexadecimal y especifiqué tres bytes del paquete interceptado.
$ bash scan.sh "03 3f 55" trying 0.elf trying 30020 trying 30020.zlib trying 30020.zlib.decompressed ... trying 84BB0 trying 84BB0.zlib trying 84BB0.zlib.decompressed trying AA240 trying AA240.zlib trying AA240.zlib.decompressed 000288d0 07 2f 03 3f 55 50 7d 7c 00 00 00 00 00 00 00 00 |./.?UP}|........| trying C6860 trying C6860.zlib
Después de desempaquetar el archivo AA240.zlib, resultó que no había suficientes datos allí para un flujo de bits completo. Así que decidí tomar el firmware de los paquetes USB.
Tanto tshark como
tcpdump pueden
leer paquetes USB de archivos pcap, pero ambos los guardan solo parcialmente. Como cada utilidad tenía diferentes partes del rompecabezas, escribí un
pequeño programa que combina la salida de ambos programas en estructuras go para reproducir los paquetes en el dispositivo.
En este punto, noté que la descarga se realiza en dos etapas: primero un controlador USB y luego FPGA.
Estuve atrapado durante varios días: parecía que todo el flujo de bits se estaba cargando, pero el dispositivo no se inició, aunque los paquetes del controlador real y mi simulación son similares.
Al final, resolví el problema examinando cuidadosamente pcap teniendo en cuenta el tiempo de respuesta para cada paquete, y noté una gran diferencia horaria en un paquete específico:

Resultó que debido a un pequeño
error tipográfico, la grabación se produjo en el área incorrecta del dispositivo. Será una lección para mí cómo ingresar valores manualmente ...
Sin embargo, ¡el LED finalmente parpadeó en el dispositivo! Un gran logro!
¡Fue relativamente fácil replicar los mismos paquetes que activaron la transferencia de datos, por lo que pude escribir el punto final USB Bulk y descargar los datos al disco al instante!
Aquí es donde comenzaron las verdaderas dificultades. Porque después del análisis resultó que los datos no estaban codificados explícitamente de ninguna manera.
Para comenzar, ejecuté
perf para tener una idea
básica del seguimiento de la pila de controladores en tiempo de ejecución:

Aunque podía capturar funciones con datos de cuadros, no podía entender la codificación de los datos en sí.

Para comprender mejor lo que sucede dentro del controlador real, incluso probé la herramienta
Ghidra de la NSA:

Aunque Ghidra es increíble (cuando lo usé por primera vez en lugar de IDA Pro), todavía no es lo suficientemente bueno como para ayudarme a comprender el controlador. La ingeniería inversa requería un camino diferente.
Decidí recoger la máquina virtual de Windows 7 y echar un vistazo al controlador de Windows, de repente arrojará ideas. Y luego noté que hay un SDK para dispositivos. Una de las herramientas resultó ser especialmente interesante:
PS> ls Directory: epiphan_sdk-3.30.3.0007\epiphan\bin Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 10/26/2019 10:57 AM 528384 frmgrab.dll -a--- 10/27/2019 5:41 PM 1449548 out.aw -a--- 10/26/2019 10:57 AM 245760 v2u.exe -a--- 10/26/2019 10:57 AM 94208 v2u_avi.exe -a--- 10/26/2019 10:57 AM 102400 v2u_dec.exe -a--- 10/26/2019 10:57 AM 106496 v2u_dshow.exe -a--- 10/26/2019 10:57 AM 176128 v2u_ds_decoder.ax -a--- 10/26/2019 10:57 AM 90112 v2u_edid.exe -a--- 10/26/2019 10:57 AM 73728 v2u_kvm.exe -a--- 10/26/2019 10:57 AM 77824 v2u_libdec.dll PS> .\v2u_dec.exe Usage: v2u_dec <number of frames> [format] [compression level] <filename> - sets compression level [1..5], - captures and saves compressed frames to a file v2u_dec x [format] <filename> - decompresses frames from the file to separate BMP files
Esta herramienta le permite "capturar" fotogramas individuales, e inicialmente no están comprimidos, por lo que es posible procesar fotogramas más adelante en una máquina más rápida. Esto es casi perfecto, y reproduje la secuencia de paquetes USB para obtener estos blobs sin comprimir. ¡El número de bytes correspondió a aproximadamente tres (RGB) por píxel!
El procesamiento inicial de estas imágenes (simplemente aceptando la salida y escribiéndola como píxeles RGB) dio una vaga reminiscencia de la imagen real que el dispositivo recibió a través de VGA:

Después de algunas depuraciones en el editor hexadecimal, resultó que cada marcador se repite cada 1028 bytes. Es un poco vergonzoso cuánto tiempo pasé escribiendo un filtro. Por otro lado, en el proceso se pueden disfrutar algunos ejemplos de arte contemporáneo.

Luego me di cuenta de que la inclinación y la distorsión de la imagen son causadas por el salto y el ajuste de píxeles en cada línea (x = 799 no es igual a x = 800). Y luego, finalmente, obtuve una imagen casi correcta, excepto por el color:

Al principio, pensé que el problema de calibración se debía al muestreo de datos cuando la entrada VGA estaba atascada en color sólido. Para corregir, hice una nueva imagen de prueba para identificar tales problemas. En retrospectiva, entiendo que tenías que usar algo como
una tarjeta de prueba Philips PM5544 .

Subí la imagen a una computadora portátil, y produjo una imagen VGA:

Luego obtuve el recuerdo de algunos trabajos antiguos en renderizado / sombreador 3D. Era muy similar al
esquema de color YUV .
Como resultado, me sumergí en la lectura de la literatura de YUV y recordé que durante la ingeniería inversa del controlador oficial del kernel, si ponía un punto de interrupción en una función llamada
v2ucom_convertI420toBGR24
, el sistema
v2ucom_convertI420toBGR24
sin la posibilidad de renovación. Entonces, ¿tal vez la entrada era codificación I420 (de
-pix_fmt yuv420p
) y la salida era RGB?
Después de usar la función Go incorporada
YCbCrToRGB, la imagen de repente se acercó mucho más al original.

Lo hicimos! Incluso el controlador en bruto produjo 7 cuadros por segundo. Honestamente, esto es suficiente para mí, ya que uso VGA solo en caso de accidente como pantalla de respaldo.
Entonces, ahora conocemos este dispositivo lo suficientemente bien como para explicar el algoritmo para iniciarlo desde el principio:
- Necesita inicializar el controlador USB . A juzgar por la cantidad de información, de hecho, el controlador le pasa el código para descargar.
- Cuando termine de cargar el USB, el dispositivo se desconectará del bus USB y después de un momento regresará con un punto final USB.
- Ahora puede enviar el flujo de bits FPGA , un paquete USB de 64 bytes para cada transferencia de control.
- Al final de la transferencia, el indicador en el dispositivo parpadeará en verde. En este punto, puede enviar lo que parece una secuencia de parámetros (overscan y otras propiedades).
- Luego ejecute el paquete de control para obtener el marco , el paquete especificó permiso. Si envía una solicitud para un marco de 4: 3 a la entrada de pantalla panorámica, esto generalmente provocará daños en el marco.
Para una máxima facilidad de uso, implementé un pequeño servidor web en el controlador. A través de la
API MediaRecorder basada en
navegador, registra fácilmente la transmisión desde la pantalla a un archivo de video.

Previniendo las inevitables afirmaciones sobre la calidad del código experimental, diré de inmediato: no estoy orgulloso de ello. Probablemente, él está en tal estado, que es suficiente para mí para un uso aceptable.
El código y las compilaciones listas para usar para Linux y OSX
están en GitHub .
Incluso si nadie inicia el programa, para mí fue un viaje emocionante a través de la naturaleza del protocolo USB, depuración del núcleo, ingeniería inversa del módulo y el formato de decodificación de video. Si te gustan estas cosas, puedes consultar
otras publicaciones de blog .