Escrevemos drivers USB para dispositivos abandonados



Recentemente, no eBay, deparei-me com um monte de dispositivos USB interessantes (Epiphan VGA2USB LR) que recebem entrada VGA e enviam vídeo para USB como webcam. Fiquei tão encantado com a ideia de que nunca mais teria que me preocupar com os monitores VGA e, dado o suporte declarado ao Linux, arrisquei e comprei o lote inteiro por cerca de 25 libras (25 dólares americanos).

Depois de receber o pacote, conectei o dispositivo, mas ele nem pensou em aparecer no sistema como UVC . O que está errado?

Estudei o site do fabricante e descobri que era necessário um driver especial para funcionar. Para mim, esse era um conceito novo, porque o kernel da minha distribuição Linux geralmente possui drivers para todos os dispositivos.

Infelizmente, o suporte ao driver apenas para esses dispositivos terminou no Linux 4.9. Portanto, nenhum dos meus sistemas o verá (Debian 10 no Linux 4.19 ou na versão mais recente do LTS Ubuntu no Linux 5.0).

Mas pode ser consertado, certo? Obviamente, os arquivos vêm no pacote DKMS , que, sob demanda, coleta o driver do código-fonte, como muitos drivers comuns ...

Isso é triste Mas aqui não é assim.

Dentro do pacote havia apenas o binário pré-compilado vga2usb.o . Comecei a estudá-lo, imaginando a complexidade da engenharia reversa e encontrei algumas linhas interessantes:

 $ strings vga2usb.ko | grep 'v2uco' | sort | uniq v2ucom_autofirmware v2ucom_autofirmware_ezusb v2ucom_autofirmware_fpga 

Então, é realmente FPGA -on-a-stick? Como fazer algo assim funcionar?

Outro achado engraçado e um pouco perturbador foi a linha com os parâmetros de chave privada do DSA. Isso me fez pensar: o que ele pode proteger dentro do motorista?

 $ strings vga2usb.ko | grep 'epiphan' | sort | uniq epiphan_dsa_G epiphan_dsa_P epiphan_dsa_Q 

Para estudar o driver em seu ambiente normal, peguei uma máquina virtual Debian 9 (última versão suportada) e fiz o KVM USB Passthrough para fornecer acesso direto ao dispositivo. Então eu instalei o driver e verifiquei se ele funcionava.

Depois disso, eu queria ver como é o protocolo de comunicação. Eu esperava que o dispositivo enviasse quadros brutos ou quase brutos, pois isso facilitaria a gravação de um driver para o espaço do usuário.

Para fazer isso, carreguei o módulo usbmon no usbmon máquina virtual e usbmon o Wireshark para capturar o tráfego USB de e para o dispositivo durante a inicialização e a captura de vídeo.



Descobri que, quando lançado, um grande número de pequenos pacotes é transmitido ao dispositivo antes que ele comece a capturar a imagem. Provavelmente é baseado na plataforma FPGA sem armazenamento de dados. Cada vez após a conexão, o driver transferia o firmware na forma de fluxo de bits FPGA para o dispositivo.

Fiquei convencido disso abrindo uma das caixas:



Vermelho

ISL98002CRZ-170 - Funciona como ADC para sinais VGA

Amarelo

XC6SLX16 - Xilinx Spartan 6 FPGA

Ciano

64 MB DDR3

Magenta

CY7C68013A - Controlador USB / Front-End


Como para "baixar" o dispositivo que você precisa para enviar um bitstream / firmware, você deverá procurá-lo em binários pré-compilados. Corri o binwalk -x e comecei a procurar por alguns objetos compactados (zlib). Para fazer isso, escrevi um script de pesquisa de sequência hexadecimal - e especifiquei três bytes do pacote 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 

Após descompactar o arquivo AA240.zlib, descobriu-se que não havia dados suficientes para um fluxo de bits completo. Então eu decidi pegar o firmware dos pacotes USB.

O tshark e o tcpdump podem ler pacotes USB de arquivos pcap, mas ambos os salvam apenas parcialmente. Como cada utilitário tinha partes diferentes do quebra-cabeça, escrevi um pequeno programa que combina a saída de ambos os programas em estruturas go para reproduzir os pacotes de volta ao dispositivo.

Nesse ponto, notei que o download ocorre em dois estágios: primeiro um controlador USB e depois FPGA.

Fiquei preso por vários dias: parecia que todo o fluxo de bits estava carregando, mas o dispositivo não foi iniciado, embora os pacotes do driver real e minha simulação pareçam semelhantes.

No final, resolvi o problema examinando cuidadosamente o pcap, levando em consideração o tempo de resposta de cada pacote - e notei uma grande diferença de tempo em um pacote específico:



Aconteceu que, devido a um pequeno erro de digitação, a gravação ocorreu na área incorreta do dispositivo. Será uma lição para mim como inserir valores manualmente ...

No entanto, o LED finalmente piscou no dispositivo! Uma grande conquista!


Era relativamente fácil replicar os mesmos pacotes que acionavam a transferência de dados, para que eu pudesse escrever o ponto de extremidade do USB Bulk e liberar os dados no disco instantaneamente!

É aqui que as dificuldades reais começaram. Porque, após a análise, verificou-se que os dados não foram explicitamente codificados de forma alguma.

Para começar, executei o perf para obter uma idéia básica do rastreamento da pilha de drivers em tempo de execução:



Embora eu pudesse capturar funções com dados de quadro, não conseguia entender a codificação dos próprios dados.



Para entender melhor o que está acontecendo dentro do driver real, tentei até a ferramenta Ghidra da NSA:



Embora Ghidra seja incrível (quando o usei pela primeira vez em vez do IDA Pro), ainda não é bom o suficiente para me ajudar a entender o driver. A engenharia reversa exigia um caminho diferente.

Eu decidi pegar a máquina virtual do Windows 7 e dar uma olhada no driver do Windows, de repente ele vai lançar idéias. E então notei que há um SDK para dispositivos. Uma das ferramentas acabou sendo especialmente interessante:

 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 

Essa ferramenta permite "pegar" quadros únicos e, inicialmente, eles não são compactados, para que seja possível processar quadros posteriormente em uma máquina mais rápida. Isso é quase perfeito, e eu repliquei a sequência de pacotes USB para obter esses blobs não compactados. O número de bytes correspondeu a aproximadamente três (RGB) por pixel!

O processamento inicial dessas imagens (apenas aceitando a saída e gravando como pixels RGB) deu algo vagamente reminiscente da imagem real que o dispositivo recebeu via VGA:



Após alguma depuração no editor hexadecimal, todos os marcadores são repetidos a cada 1028 bytes. É um pouco embaraçoso quanto tempo passei escrevendo um filtro. Por outro lado, no processo, pode-se apreciar alguns exemplos de arte contemporânea.



Então percebi que a inclinação e distorção da imagem são causadas por pulos e quebra de pixels em cada linha (x = 799 não é igual a x = 800). E, finalmente, consegui uma imagem quase correta, exceto pela cor:



No começo, pensei que o problema de calibração fosse devido à amostragem de dados quando a entrada VGA estava presa em cores sólidas. Para correção, fiz uma nova imagem de teste para identificar esses problemas. Em retrospectiva, entendo que você precisava usar algo como um cartão de teste Philips PM5544 .



Carreguei a imagem em um laptop e ela produziu uma imagem VGA:



Então, eu tenho a memória de alguns trabalhos antigos em renderização / sombreador 3D. Era muito semelhante ao esquema de cores YUV .

Como resultado, mergulhei na leitura da literatura do YUV e lembrei que, durante a engenharia reversa do driver oficial do kernel, se eu colocasse um ponto de interrupção em uma função chamada v2ucom_convertI420toBGR24 , o sistema v2ucom_convertI420toBGR24 sem a possibilidade de renovação. Talvez a entrada tenha sido codificação I420 (de -pix_fmt yuv420p ) e a saída seja RGB?

Depois de usar a função Go incorporada YCbCrToRGB, a imagem subitamente ficou muito mais próxima do original.



Nós conseguimos! Até o driver bruto produzia 7 quadros por segundo. Honestamente, isso é o suficiente para mim, pois eu uso o VGA apenas em caso de acidente como uma tela de backup.

Portanto, agora conhecemos bem esse dispositivo para explicar o algoritmo para iniciá-lo desde o início:

  1. Você precisa inicializar o controlador USB . A julgar pela quantidade de informações, de fato, o driver passa o código para o download.
  2. Quando você terminar de carregar o USB, o dispositivo será desconectado do barramento USB e depois de um momento retornará com um ponto de extremidade USB.
  3. Agora você pode enviar o fluxo de bits FPGA , um pacote USB de 64 bytes para cada transferência de controle.
  4. No final da transferência, o indicador no dispositivo piscará em verde. Nesse ponto, você pode enviar o que parece ser uma sequência de parâmetros (overscan e outras propriedades).
  5. Em seguida, execute o pacote de controle para obter o quadro , a permissão especificada no pacote. Se você enviar uma solicitação de entrada de quadro 4: 3 para widescreen, isso geralmente causará danos ao quadro.

Para máxima facilidade de uso, implementei um pequeno servidor web no driver. Por meio da API MediaRecorder baseada no navegador , ele grava facilmente o fluxo da tela para um arquivo de vídeo.



Impedindo as reivindicações inevitáveis ​​da qualidade do código experimental, direi imediatamente: não tenho orgulho disso. Provavelmente, ele está em tal estado, o que é suficiente para mim para uso aceitável.

O código e as compilações prontas para Linux e OSX estão no GitHub .

Mesmo que ninguém inicie o programa, para mim foi uma jornada fascinante através do protocolo USB, depuração do kernel, engenharia reversa do módulo e formato de decodificação de vídeo! Se você gosta dessas coisas, pode conferir outras postagens no blog .

Source: https://habr.com/ru/post/pt476434/


All Articles