Estrutura do firmwareVou fazer um resumo sobre a estrutura do firmware e como acessá-lo via cabo serial. O usuário
MCNeto e o pessoal no HT Forum falam sobre isso
aqui.
Os STBs baseados no Zinwell têm um sistema Linux embarcado. O arquivo de firmware .zim é estruturado em blocos que correspondem a partições da flash que serão montadas sob o Linux. Podemos visualizar e extrair esses blocos com o programa ZimView disponível
aqui (no site do projeto há também informações sobre a estrutura do firmware). Se um arquivo de firmware aberto no ZimView possuir apenas um bloco, é porque apenas aquele bloco será atualizado, mantendo os demais intactos na flash. Ou seja, nem sempre o arquivo de firmware possui um firmware inteiro. Os principais blocos são: Load, Root, Kernel e Code. O bloco Load contém o
CFE (Common Firmware Environment), que é aberto, sob licença da
Broadcom. O bloco Root contém uma imagem de sistema de arquivos Linux, em formato Squashfs, com o
BusyBox incorporado, compilado com apenas alguns dos principais comandos. O bloco Kernel contém o Kernel Linux compactado com gzip e o bloco Code contém o binário executável zmw_base_zinwell que será carregado na memória após a inicialização do sistema e fará todas as operações do STB. Esse bloco geralmente possui mais 2 arquivos, um pequeno script inicial preparatório para ser chamado antes do executável e um módulo (driver) Broadcom, e também está em formato Squashfs.
Quando o STB é ligado, o CFE é carregado e inicia a CPU, a memória e demais sistemas. Logo em seguida, chama o Kernel Linux, que monta todo o sistema de arquivos do bloco Root na raiz. Depois que o Kernel e seus módulos são carregados, os scripts de inicialização são executados. Por último, o bloco Code é montado e o executável é chamado. O executável inicializa o painel frontal, o sintonizador, mostra o logo na tela e passa a realizar todas as operações do STB. Isso tudo leva alguns segundos. Com um cabo serial é possível acessar o console do aparelho e interromper a inicialização para entrar no prompt do CFE, que possui comandos "Unix like", ou, após o Kernel Linux ser carregado, interromper a execução do binário zmw_base_zinwell para entrar no shell do BusyBox.
Se abrirmos a versão 1.7.2 no ZimView, veremos que só há o bloco Code. Podemos extrair o bloco e teremos uma imagem Squashfs que aparentemente está comprimida com zlib. Seria possível montar essa imagem no Linux ou extrair seu conteúdo, incluindo o binário zmw_base_zinwell, com o squash-tools. O problema é que ninguém conseguiu ainda fazer isso com esse padrão de versão (1.7.2, 1.5.5, etc). Sempre gera um erro com o gzip, o que sugere que deve ter sido usada alguma criptografia na compressão. Uma solução, então, é utilizar o cabo serial, para acessar o shell do BusyBox após a imagem Squashfs ter sido montada pelo Linux do aparelho e copiar o binário via pendrive.
Assim, para chegar ao binário zmw_base_zinwell da versão 1.7.2 do ZBT-620A, utilizei um cabo para celulares Siemens, mais precisamente um USB baseado no chip Prolific, de forma similar ao
descrito pelo Ryan para extrair o firmware dos DivX Players Mediatek. Na placa do aparelho, podemos identificar um conector de 4 pinos com a identificação "J1 Debug Message". Uma vantagem desse tipo de cabo é que você não precisa usar o pino de +5V. Basta usar 3 pinos, RX, TX e GND.
Com o cabo feito, e com um aplicativo de terminal instalado no PC, como o Putty, é possível acessar às mensagens de console e entrar no prompt, tanto no do CFE, como no do shell do BusyBox. Há um tutorial feito pelo
edufaria4 no
HT Forum. Para entrar no prompt do CFE, assim que o aparelho for ligado, deve-se apertar CTRL+C no Putty. São só 1 ou 2 segundos para fazer isso, antes do Kernel Linux ser carregado e a inicialização prosseguir. Uma vez no prompt, podemos digitar "help" e ver a lista de comandos disponíveis. Um dos comandos é o "show devices", que mostra os dispositivos montados, inclusive as partições da flash. As partições correspondem aos blocos do arquivo de firmware .zim. A tabela abaixo mostra as principais partições e a quais blocos de firmware estão destinadas:
Partição | Tamanho | Bloco |
flash0.avai0 | 3584KB | ROOT |
flash0.rootfs | 512KB | ROOT |
flash0.cfe | 320KB | LOAD |
flash0.app | 2048KB | CODE |
flash0.kernel | 1664KB | KERNEL |
O ZBT-620 parece utilizar apenas a partição maior para o bloco Root, devido ao tamanho do sistema de arquivos.
Com o CFE, é possível gravar na flash ou fazer cópia dela via rede. Para levantar a ethernet do STB, usamos o comando "ifconfig". Para fazer uma rede ponto a ponto com o PC com uma placa de rede configurada para negociar o IP de forma automática, bastaria digitar "ifconfig eth0 -auto", como apontado pelo
edufaria4. Porém, no meu caso não funcionou. Tive que usar uma configuração manual. Como exemplo, "ifconfig eth0 -addr=172.168.0.1 -mask=255.255.255.0", que levanta a placa de rede com IP 172.168.0.1. Aí, temos que configurar a rede do PC também manualmente, com Gateway 172.168.0.1 (o STB) e um IP na mesma faixa, por exemplo, 172.168.0.100. Além de ter a rede funcionando, é necessário ter um servidor FTP ou TFTP rodando no PC para receber e enviar os arquivos. Eu usei o tftpd (TFTP Server do Fedora 14), que é bem fácil configurar. Com rede e servidor FTP funcionando, podemos gravar e copiar as partições da flash com os comandos "flash" e "save". Algumas informações são necessárias para estas operações, como endereço de início do bloco e tamanho. Por exemplo, o comando "show devices" citado acima mostra, para a partição flash0.avail0, as seguintes informações:
flash0.avail0 New CFI flash at 1E000000 offset 00000000 size 3584KB
Assim, esse bloco se inicia em 1E000000 (primeiro bloco, já que o offset é 0) e tem o tamanho hexadecimal em bytes 380000 (3584*1024, transformado em hexadecimal). Para gravar essa partição no servidor FTP, usamos o seguinte comando (considerando que o PC esteja configurado com o IP 172.168.0.100):
save 172.168.0.100:flash0.avail 1E000000 380000Isso irá gerar um arquivo flash0.avail no diretório compartilhado do servidor FTP, correspondente a um bloco de firmware, mais especificamente ao bloco Root, sem o header.
Se for necessário recuperar a flash, usa-se o seguinte comando:
flash -noheader 172.168.0.100:flash0.avail flash0.availQuer dizer, pegamos o arquivo flash0.avail no servidor FTP que está rodando no IP 172.168.0.100 e o gravamos na partição flash0.avail.
Para a partição flash0.app, as informações mostradas pelo "show devices" são as seguintes:
flash0.app New CFI flash at 1E000000 offset 00450000 size 2048KB
O bloco começa em 1E000000+450000. Então, para copiar, usamos:
save 172.168.0.100:flash0.app 1E450000 200000E, se precisarmos recuperar a flash:
flash -noheader 172.168.0.100:flash0.app flash0.appEu copiei todas as partições para eventual recuperação, pois iria "brincar" com o firmware. O único cuidado é não danificar a partição flash0.cfe, que é onde fica justamente o CFE. Não sei como o sistema acha o CFE na flash. Talvez no momento do boot os headers dos blocos são lidos à procura do CFE. Mas eu não vou me arriscar a movê-lo a outro bloco.
Bom, mas isso nos dá apenas acesso aos blocos de firmware. Por exemplo, o arquivo flash0.app será exatamente o mesmo bloco Code extraído do arquivo .zim com ZimView. Não podemos, assim, acessar o binário zmw_base_zinwell. Para isso, precisamos esperar o STB bootar completamente e montar a imagem Squashfs do bloco Code para entrarmos no prompt do Busybox e copiarmos o binário do sistema de arquivos para um pendrive via USB.
Copiar o zmw_base_zinwell via USBO
MCNeto posta informações a respeito
aqui. Quando o STB termina de iniciar, devemos apertar CTRL+\ no Putty. Isso interrompe a execução do zmw_base_zinwell e nos mostra o shell do BusyBox. Aí sim, podemos navegar pelo sistema de arquivos com comandos Linux. O comando "mount", sem opções, vai listar os pontos de montagem atuais. Podemos ver que /dev/mtdblock3 está montado em /mnt/hd. "mtblock" é como o Linux identifica os dispositivos flash. mtdblock3 é o quarto bloco (começa em 0) e corresponde a flash0.app listado pelo CFE. Para listarmos o conteúdo de /mnt/hd, basta digitarmos "ls -al /mnt/hd" e veremos 3 arquivos: bcmdriver.ko, settop e zmw_base_zinwell. O que nos interessa é o zmw_base_zinwell, o binário que realiza todas as operações próprias do STB. Podemos copiá-lo para um pendrive (ou HD externo) na USB. Porém, nem sempre conectar um pendrive na USB garante que o mesmo seja montado automaticamente. No caso da versão 1.7.2, ao conectar um pendrive, o dispositivo é reconhecido, mas não podemos montá-lo nem manualmente sem que antes subamos alguns módulos do Kernel. Não é muito difícil montá-lo manualmente, mas existe uma maneira mais fácil de acessar um pendrive: sair para o BusyBox (com CTRL+\) depois de montar o pendrive pelo próprio STB. Assim, com o STB ainda funcionando (zmw_base_zinwell rodando), colocamos o pendrive na USB e apertamos a tecla arquivo (file) no controle remoto. Quando o pendrive for reconhecido pelo aparelho, interrompemos com CTRL+\ e já entramos no prompt com o pendrive montado em /mnt/usb (pode-se verificar com o comando "mount"). Pronto, copiamos o zmw_base_zinwell com o comando "cp /mnt/hd/zmw_base_zinwell /mnt/usb" e depois desmontamos o pendrive com "umount /mnt/usb" para garantir a sincronização do sistema de arquivos antes de retirá-lo.
Após copiar o zmw_base_zinwell 1.7.2 do "tijolão", copiei o do slim também. Para isso, eu instalei a versão 1.7.2 slim no meu aparelho "tijolão". Como esperado, o STB ficou inoperativo (sem controle remoto e sem painel frontal). Mas com o cabo feito e com o firmware original copiado pelo CFE, não foi problema restaurar a versão anterior após copiar o binário.
Squash-tools para a versão 1.13.6 e correlatasA versão 1.13.6, ao ser aberta no ZimView, possui os 4 blocos principais. Esse é um dos motivos que a atualização para essa versão impede a volta para a 1.7.2. Os 4 blocos são atualizados na flash. Voltar para a versão 1.7.2 apenas atualizaria o bloco Code, que é incompatível com o novo sistema de arquivos e o novo Kernel, e "mataria" o STB.
Para essa versão (e outras mais novas semelhantes), os blocos Code e Root continuam sendo imagens Squashfs, mas comprimidas com LZMA. Neste caso, é possível usar a versão correta do squash-tools com LZMA para extrair os arquivos da imagem do bloco Code (após o mesmo ser extraído do arquivo .zim pelo ZimView). A versão que usei foi a
3.3-4 de 64 bits do Mandriva. Provavelmente a de
32 bits também funciona. Como eu uso Linux Fedora (com uma versão do squash-tools LZMA mais nova mas incompatível) eu apenas extraí os binários do pacote RPM para usá-los diretamente, sem instalar. E com essa versão do squash-tools, também é possível recriar a imagem do bloco Code com o zmw_base_zinwell modificado e depois montar o novo arquivo .zim com o ZimView.
Há também muitas informações sobre a estrutura do firmware, o cabo serial e o zmw_base_zinwell no N.Z. Digital TV Forum, principalmente a partir
deste post do tópico do Zinwell 620 HD.
Início da explicação das modificaçõesPor fim, fiquei com 3 binários zmw_base_zinwell. Um da versão 1.7.2 do "tijolão" e outro da mesma versão, mas do slim. Comparei rotinas destes binários no IDA Disassembler. O código usado nos binário é MIPS 32 bits little endian. Então o processador escolhido no IDA foi o mipsl. Com base nas descobertas, modifiquei o binário da versão 1.13.6 (que era só para slim) em um editor hexadecimal. Não é preciso corrigir nenhum checksum. O legal é que, com o cabo, você pode executar o binário modificado a partir do pendrive, sem precisar gravá-lo na flash. Se o código travar, é só desligar e religar o STB. Para isso, coloca-se diretamente o zmw_base_zinwell no pendrive. Com o pendrive já montado no STB, entramos no shell do Busybox e pedimos para executar o binário com o comando "/mnt/usb/zmw_base_zinwell", ou qualquer outro nome que você tenha dado temporariamente a ele. É muito mais prático!
A primeira coisa que procurei no IDA foram bytes que correspondessem aos códigos das teclas do controle. Esses códigos são mostrados no console do Busybox (visualizado com o Putty) quando o STB está rodando. Quando a tecla é reconhecida, aparece a mensagem "Key XXXXYYYY" (onde XXXX são 2 bytes que identificam o controle e YYYY 2 bytes que identificam a tecla) seguida de mensagens relacionadas à função correspondente (vol +, channel etc). Quando o controle não é reconhecido, mas ao menos é identificado, aparece só a mensagem "Key XXXXYYYY". Na maioria dos controles, o bit mais significativo do grupo XXXX é resetado quando a tecla é premida de forma rápida e setado quando a tecla é pressionada por mais de 1 segundo, o que torna possível a utilização de duas funções para cada tecla. Além desses bytes, deve existir ao menos mais 1 para identificar a função do controle, pois testando com o controle universal da Samsung, nas funções TV e VCR não consegui nenhum retorno. Alguns poucos retornos para algumas programações de controles de DVD e Cable. E vários retornos (mas não todos) para programações da função STB, como esperado.
Para o controle prata do "tijolão", os códigos são 38C7YYYY quando o aperto de tecla é simples e B8C7YYYY quando o aperto é constante. Para o controle preto do slim, os códigos são 009FYYYY e 809FYYYY, respectivamente. Então procurei pelos bytes C738 no binário do "tijolão" e 9F00 no do slim. Bom, tem ainda mais coisas em relação ao IDA, às rotinas e ao MIPS que quero explicar no próximo post. Inclusive tem um script IDAPython, o
mips-analyser, que facilita a visualização do código MIPS, mas que só foi necessário para a achar as rotinas de inicialização do painel frontal. No entanto, se tivesse ele antes, achar as rotinas do remoto teria sido mais fácil.