
Nesta sessão de workshop, vamos extrair o firmware de um carregador de EV, mergulhar no firmware e, por fim, emulá-lo para que possamos interagir com os serviços em tempo real.
Junte-se a nós nesta demonstração prática do Unblob, o extrator de firmware flexível. Durante esta sessão de workshop, vamos extrair firmware de um carregador de veículos elétricos, explorar o firmware e, por fim, emulá-lo para interagirmos com os serviços em tempo real. Unblob funciona tanto com versões de firmware em hardware quanto com versões para download, por isso temos um ambiente rico em alvos. Não é necessária experiência prévia, esta sessão é adequada para todos os níveis de habilidade e estamos ansiosos para vê-lo lá.
Nosso alvo é um controlador de estação de carregamento para veículos elétricos da Phoenix Contact. Você pode encontrar mais detalhes sobre ele aqui.
CHARX control modular, controlador de carregamento AC, com sistema Linux embarcado, IEC 61851-1, modo de operação: Stand-Alone, Client, Server,
Interfaces:
- Ethernet (2x)
- Comunicação celular (4G/2G)
- Barramento do sistema CHARX control modular
- MICRO-USB tipo C
Protocolos de comunicação:
- OCPP 1.6J
- Modbus/TCP
- MQTT
Dispositivos periféricos conectáveis:
- Medidor de energia
- RFID
- Detecção de corrente residual DC
- Montagem em trilho DIN
Há algumas ferramentas de que precisamos neste workshop. Você pode instalá-las executando o script install-prerequisites assim:```sh
./install-prerequisites
## Obtendo o firmware
O firmware pode ser obtido no site do fornecedor. Existe um script chamado
`download-firmware` neste repositório que você usa para baixar o firmware
sem a necessidade de abrir um navegador.
Nosso foco hoje é no firmware fornecido pelo fornecedor, pois ele contém tudo
o que precisamos. Mas um fluxo de trabalho semelhante pode ser aplicado a um dump de memória extraído de
um dispositivo real. O interessante aqui é que podemos extrair, explorar e
emular sem nem precisar de um dispositivo real.
## Extração com Unblob
Vamos começar garantindo que todas as dependências estejam disponíveis:```
unblob --show-external-dependencies
The following executables found installed, which are needed by unblob:
7z ✓
debugfs ✓
jefferson ✓
lz4 ✓
lziprecover ✓
lzop ✓
sasquatch ✓
sasquatch-v4be ✓
simg2img ✓
ubireader_extract_files ✓
ubireader_extract_images ✓
unar ✓
zstd ✓
Agora podemos extrair o firmware com o unblob:``` unblob CHARXSEC3XXXSoftwareBundleV190.raucb
A extração leva cerca de 3 minutos em um laptop razoável. Você deve ver uma barra de progresso avançando:

Quando a extração for concluída, um diretório chamado
`CHARXSEC3XXXSoftwareBundleV190.raucb_extract` deve estar visível. Você pode
entrar nele e listar o conteúdo.
### Chunks, Chunks Desconhecidos
O Unblob funciona identificando chunks de dados dentro dos arquivos. Se um chunk for um
fluxo comprimido, ele é descomprimido. Se for um sistema de arquivos ou um arquivo, ele
é extraído. Se a extração ou descompressão foi bem-sucedida, o chunk
que foi gravado em disco é excluído para recuperar espaço.
Aqui, um chunk little-endian da versão 4 do SquashFS foi gravado em disco, extraído,
e excluído. Os arquivos (e, portanto, os diretórios de extração) são nomeados com
a nomenclatura `{start_offset}-{end_offset}.{type}`.
Podemos ver que 11KB de chunk "desconhecido" aparecem após o sistema de arquivos squashfs.```
./0-132173824.squashfs_v4_le_extract
./132173824-132184833.unknown
Você pode executar binwalk nele para ver o que ele contém:```
binwalk 132173824-132184833.unknown
0 0x0 Object signature in DER format (PKCS header length: 4, sequence length: 10997 58 0x3A Certificate in DER format (x509 v3), header length: 4, sequence length: 4372 4434 0x1152 Certificate in DER format (x509 v3), header length: 4, sequence length: 4387
Você pode verificar os certificados com openssl:```
dd if=132173824-132184833.unknown bs=1 skip=58 | openssl x509 -in /dev/stdin -inform der -noout -text
dd if=132173824-132184833.unknown bs=1 skip=4434 | openssl x509 -in /dev/stdin -inform der -noout -text
Isso significa que os firmwares provavelmente são assinados com a chave privada do fornecedor, para que os dispositivos possam garantir que os firmwares sejam autênticos.
Essa é uma das vantagens do unblob: transformar desconhecidos desconhecidos em desconhecidos conhecidos que podem ser investigados.
Vamos ver o conteúdo do nosso sistema de arquivos squashfs:``` ls -al 0-132173824.squashfs_v4_le_extract total 460532 drwxrwxr-x 4 kali kali 4096 dec 5 09:51 . drwxrwxr-x 3 kali kali 4096 dec 5 09:51 .. -rw-rw-r-- 1 kali kali 20971520 sep 8 09:59 bootimg.vfat drwxrwxr-x 3 kali kali 4096 dec 5 09:51 bootimg.vfat_extract -rwxrwxr-x 1 kali kali 2654 sep 19 2022 hook -rw-rw-r-- 1 kali kali 442 sep 8 09:59 manifest.raucm -rw-rw-r-- 1 kali kali 450584576 sep 8 09:59 root.ext4 drwxrwxr-x 22 kali kali 4096 sep 8 09:57 root.ext4_extract
Podemos ver um manifesto em texto simples, um script de shell, um MBR e um sistema de arquivos EXT4:```
find -maxdepth 1 -type f -exec file {} \;
./manifest.raucm: ASCII text
./hook: a /usr/bin/env sh script, ASCII text executable
./bootimg.vfat: DOS/MBR boot sector, code offset 0x3c+2, OEM-ID "mkfs.fat", sectors/cluster 4, reserved sectors 4, root entries 512, sectors 40960 (volumes <=32 MB), Media descriptor 0xf8, sectors/FAT 40, sectors/track 63, heads 255, hidden sectors 163158016, reserved 0x1, serial number 0xd09aad9c, label: "KERNEL ", FAT (16 bit)
./root.ext4: Linux rev 1.0 ext4 filesystem data, UUID=8ed19606-02c4-42e7-9cfb-1a2839f93ec4 (extents) (large files) (huge files)
Ambos bootimg.vfat e root.ext4 foram processados e extraídos pelo unblob. A partição VFAT contém tudo relacionado a boot e sistema operacional (kernel Linux, DTB, TEE):```
oftree: Device Tree Blob version 17, size=27469, boot CPU=0, string block size=1657, DT structure block size=25756
tee.bin: data
zImage: Linux kernel ARM boot executable zImage (little-endian)
zImage-imx6ul-ksp0632.dtb: Device Tree Blob version 17, size=27469, boot CPU=0, string block size=1657, DT structure block size=25756
Você verá que o unblob é um pouco ganancioso e extrairá um arquivo ELF e um arquivo CPIO
do kernel Linux (`zImage`), que correspondem ao kernel mínimo e ao ramdisk.
O sistema de arquivos EXT4 contém o sistema de arquivos raiz que é montado pelo kernel
Linux na inicialização:```
ls -alh root.ext4_extract
total 88K
drwxrwxr-x 22 kali kali 4,0K sep 8 09:57 .
drwxrwxr-x 4 kali kali 4,0K dec 5 09:51 ..
drwxrwxr-x 2 kali kali 4,0K dec 5 09:51 bin
drwxrwxr-x 3 kali kali 4,0K dec 5 09:51 boot
drwxrwxr-x 16 kali kali 4,0K sep 8 09:56 data
drwxrwxr-x 2 kali kali 4,0K jul 18 00:09 dev
drwxrwxr-x 53 kali kali 4,0K dec 5 09:51 etc
drwxrwxr-x 18 kali kali 4,0K sep 8 09:56 home
drwxrwxr-x 2 kali kali 4,0K jul 18 03:13 identity
drwxrwxr-x 9 kali kali 4,0K dec 5 09:51 lib
drwxrwxr-x 2 kali kali 4,0K jul 18 03:14 log
drwxrwxr-x 2 kali kali 4,0K sep 8 09:57 lost+found
drwxrwxr-x 2 kali kali 4,0K jul 18 00:09 media
drwxrwxr-x 2 kali kali 4,0K jul 18 00:09 mnt
drwxrwxr-x 2 kali kali 4,0K jul 18 00:09 proc
drwxrwxr-x 2 kali kali 4,0K sep 8 09:57 run
drwxrwxr-x 2 kali kali 4,0K dec 5 09:51 sbin
drwxrwxr-x 2 kali kali 4,0K jul 18 03:14 sdcard
drwxrwxr-x 2 kali kali 4,0K jul 18 00:09 sys
drwxrwxrwx 2 kali kali 4,0K jul 18 00:09 tmp
drwxrwxr-x 11 kali kali 4,0K sep 8 09:56 usr
drwxrwxr-x 11 kali kali 4,0K dec 5 09:51 var
Vamos fazer um pouco de exploração usando um conjunto diferente de opções do unblob:```
unblob -e /tmp/out -f -k -d 3 --report /tmp/report.json
--log /tmp/unblob.log CHARXSEC3XXXSoftwareBundleV190.raucb
Aqui continuamos extraindo (`-e`) para `/tmp/out`, mas forçamos (`-f`) a sobrescrita, mantendo (`-k`) os pedaços extraídos, porém limitando a profundidade de recursão (`-d`) a 3. Escrevemos um relatório detalhado (`--report`) para `/tmp/report.json` e o arquivo de log (`--log`) para `/tmp/unblob.log`.
Dê uma olhada no arquivo de log; você verá o funcionamento interno do unblob.
O arquivo de relatório contém informações detalhadas sobre arquivos analisados (tamanho, tipo de arquivo, caminho, magic, mime-type, hashes MD5/SHA1/SHA256), chunks (tamanho, offsets, distribuição de entropia) e tarefas (extração, descompressão, carving). É possível gerar belas visualizações a partir desses arquivos de relatório com um pouco de Python.
Você mesmo pode criá-las com o script Python `diagram.py` disponível neste repositório:```
python3 diagram.py /tmp/report.json sunburst
python3 diagram.py /tmp/report.json treemap
Esses comandos abrirão o navegador em uma página que contém uma visualização baseada em plotly como as mostradas abaixo:


Agora que temos uma noção melhor do que há dentro, é hora de listar o que precisamos para realizar a emulação adequada do dispositivo.
Idealmente, precisamos coletar essas informações:
Para obter alguns detalhes sobre a plataforma, CPU e arquitetura, podemos analisar os device tree blobs incorporados no firmware.
Na partição VFAT, temos estes DTBs:``` oftree: Device Tree Blob version 17, size=27469, boot CPU=0, string block size=1657, DT structure block size=25756 zImage-imx6ul-ksp0632.dtb: Device Tree Blob version 17, size=27469, boot CPU=0, string block size=1657, DT structure block size=25756
Esses arquivos são a representação binária das device trees. Podemos recuperar seu código-fonte em texto puro usando o `device-tree-compiler`:```
dtc -I dtb -o zImage-imx6ul-ksp0632.dts zImage-imx6ul-ksp0632.dtb
dtc -I dtb -o oftree.dts oftree
Dê uma olhada no DTS, e você logo encontrará detalhes interessantes como o modelo:``` /dts-v1/;
/ { #address-cells = <0x01>; #size-cells = <0x01>; model = "Phytec phyBOARD-Segin i.MX6 UltraLite"; <----- right here compatible = "phytec,imx6ul-pbacd-10\0phytec,imx6ul-pcl063\0fsl,imx6ul";
A CPU:```
cpus {
#address-cells = <0x01>;
#size-cells = <0x00>;
cpu@0 {
compatible = "arm,cortex-a7"; <---- here
device_type = "cpu";
reg = <0x00>;
clock-latency = <0xee6c>;
operating-points = <0xa9ec0 0x137478 0x80e80 0x11edd8 0x60ae0 0xfa3e8 0x30570 0xe7ef0>;
fsl,soc-operating-points = <0xa9ec0 0x137478 0x80e80 0x11edd8 0x60ae0 0x11edd8 0x30570 0x11edd8>;
clocks = <0x01 0x5d 0x01 0x1a 0x01 0x26 0x01 0xdb 0x01 0x38 0x01 0x39 0x01 0x19>;
clock-names = "arm\0pll2_bus\0pll2_pfd2_396m\0secondary_sel\0step\0pll1_sw\0pll1_sys";
arm-supply = <0x02>;
soc-supply = <0x03>;
dc-supply = <0x04>;
};
};
Ou até mesmo periféricos:``` --snip-- ethernet@2188000 { compatible = "fsl,imx6ul-fec\0fsl,imx6q-fec"; reg = <0x2188000 0x4000>; interrupts = <0x00 0x76 0x04 0x00 0x77 0x04>; clocks = <0x01 0x90 0x01 0x91 0x01 0x30 0x01 0x2c 0x01 0x2c>; clock-names = "ipg\0ahb\0ptp\0enet_clk_ref\0enet_out"; fsl,num-tx-queues = <0x01>; fsl,num-rx-queues = <0x01>; status = "okay"; pinctrl-names = "default"; pinctrl-0 = <0x23>; phy-mode = "rmii"; phy-handle = <0x24>;
mdio {
#address-cells = <0x01>;
#size-cells = <0x00>;
ethernet-phy@1 {
reg = <0x01>;
interrupt-parent = <0x0b>;
interrupts = <0x02 0x08>;
micrel,led-mode = <0x01>;
clocks = <0x01 0x2c>;
clock-names = "rmii-ref";
status = "okay";
linux,phandle = <0x24>;
phandle = <0x24>;
};
ethernet-phy@2 {
reg = <0x03>;
micrel,led-mode = <0x01>;
clocks = <0x01 0x2d>;
clock-names = "rmii-ref";
status = "okay";
linux,phandle = <0x13>;
phandle = <0x13>;
};
};
};
--snip-- flexcan@2090000 { compatible = "fsl,imx6ul-flexcan\0fsl,imx6q-flexcan"; reg = <0x2090000 0x4000>; interrupts = <0x00 0x6e 0x04>; clocks = <0x01 0x94 0x01 0x95>; clock-names = "ipg\0per"; status = "okay"; pinctrl-names = "default"; pinctrl-0 = <0x0f>; xceiver-supply = <0x10>; };
flexcan@2094000 { compatible = "fsl,imx6ul-flexcan\0fsl,imx6q-flexcan"; reg = <0x2094000 0x4000>; interrupts = <0x00 0x6f 0x04>; clocks = <0x01 0x96 0x01 0x97>; clock-names = "ipg\0per"; status = "disabled"; };
### Informações da versão do kernel Linux
Podemos identificar a versão do kernel Linux usando grep na partição VFAT
que contém o kernel compactado:```
grep 'Linux version' . -ra
./zImage_extract/7296-6371487.lzo_extract/7296-6371487:
Linux version 5.15.195 (oe-user@oe-host) (gcc version 8.3.0 (GCC)) #1 SMP PREEMPT
./zImage_extract/7296-6371487.lzo_extract/7296-6371487:
Linux version %s (%s)Bluetooth subsystem version %u.%uHCI socket registration failed
Outra forma é observar os módulos do kernel dentro do filesystem raiz EXT4:```
modinfo ./CHARXSEC3XXXSoftwareBundleV190.raucb_extract/0-138014720.squashfs_v4_le_extract/root.ext4_extract/lib/modules/5.15.195/kernel/net/bluetooth/bnep/bnep.ko.xz
filename: ./CHARXSEC3XXXSoftwareBundleV190.raucb_extract/0-138014720.squashfs_v4_le_extract/root.ext4_extract/lib/modules/5.15.195/kernel/net/bluetooth/bnep/bnep.ko.xz
alias: bt-proto-4
license: GPL
version: 1.3
description: Bluetooth BNEP ver 1.3
author: Marcel Holtmann [email protected]
srcversion: 86266D360814678CE0C94E9
depends:
intree: Y
name: bnep
vermagic: 5.15.195 preempt mod_unload modversions ARMv7 thumb2 p2v8
parm: compress_src:Compress sources headers (bool)
parm: compress_dst:Compress destination headers (bool)
## Emulação
Podemos já ter um kernel compilado dentro do pacote de firmware, mas este
kernel é compilado especificamente para uma máquina em particular (a Phytec
phyBOARD-Segin i.MX6 UltraLite).
Para usar este kernel corretamente, precisaríamos ser capazes de executar este
kernel em uma emulação dessa máquina exata para a qual ele foi compilado. Isso
é demorado se ela já não estiver listada na lista de máquinas do QEMU. Nota: uma
placa de avaliação está disponível no QEMU (`mcimx6ul-evk - Freescale i.MX6UL
Evaluation Kit (Cortex-A7)`), mas não é exatamente compatível.
Em vez disso, em teoria, podemos escolher uma máquina QEMU e usar vários
metadados que encontramos na imagem de firmware para configurar e compilar um
kernel para essa máquina, que também seria capaz de executar tudo em nosso
sistema de arquivos, o mais próximo possível do original.
A partir daqui, temos algumas opções:
- *emulação de espaço de usuário*: Poderíamos inicializar com nosso próprio ramfs/rootfs personalizado e, em seguida, carregar o rootfs do firmware de destino e fazer chroot nele para emular.
- *emulação de sistema completo*: Poderíamos tentar inicializar o rootfs do firmware de destino diretamente (passando-o diretamente para a máquina QEMU).
A emulação de espaço de usuário geralmente é limitante, mas é uma ótima maneira
de investigar, depurar e explorar binários específicos. Recomendamos
fortemente que você dê uma olhada no [EMUX](https://github.com/therealsaumil/emux)
de Saumil Shah para todas as suas necessidades de emulação de espaço de usuário.
Independentemente disso, ainda precisamos de um kernel que inicialize dentro de
uma máquina QEMU, que corresponda, o mais próximo possível, ao que o firmware
de destino espera.
### Máquinas QEMU
Existem apenas alguns candidatos a máquinas de propósito geral que oferecem
flexibilidade. Após bastante experimentação, `virt`
(https://qemu.readthedocs.io/en/latest/system/arm/virt.html) parece ter uma boa
relação flexibilidade/potência para nossos propósitos.
Ao compilar o kernel para uma determinada máquina QEMU, provavelmente é melhor
garantir que possamos usar o dispositivo virtio genérico que o QEMU inclui. Na
configuração, isso pode significar um ou mais dos seguintes:```
CONFIG_VIRTIO=y
CONFIG_SCSI_VIRTIO=y
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_BLK=y
E mais! Esta lista está incompleta.
É benéfico para nós usarmos um kernel que tenha uma string vermagic idêntica (ou pelo menos muito semelhante). Todos os tipos de componentes dentro de um sistema podem estar verificando o vermagic do kernel e falhando se for diferente do que é esperado. Isso é mais comum em módulos de kernel - um kernel geralmente se recusa a carregar um módulo que não tenha um vermagic que corresponda ao seu. Embora nós possamos corrigir o kernel para ignorar isso no nível do kernel, ainda precisamos que a parte inicial “kernel release” da string corresponda à string de release dos módulos do kernel, já que é usada como caminho de busca para o modules.dep.
No firmware da Phoenix Contact, o vermagic é 5.15.195 preempt mod_unload modversions ARMv7 thumb2 p2v8.
A string vermagic está em um formato como (de include/linux/vermagic.h):```c
#define VERMAGIC_STRING
UTS_RELEASE " "
MODULE_VERMAGIC_SMP MODULE_VERMAGIC_PREEMPT
MODULE_VERMAGIC_MODULE_UNLOAD MODULE_VERMAGIC_MODVERSIONS
MODULE_ARCH_VERMAGIC
- Versão do kernel = a versão do kernel + string `CONFIG_LOCALVERSION`.
- SMP = `CONFIG_SMP`
- PREEMPT = `CONFIG_PREEMPT`
- mod_unload = `CONFIG_MODULE_UNLOAD`
- modversions = `CONFIG_MODVERSIONS`
- ARMv7 thumb2 p2v8 = `MODULE_ARCH_VERMAGIC` (relacionado à arquitetura para a qual o kernel é compilado, portanto relacionado à opção de compilador/arquitetura passada.)
Uma vez que tenhamos o código-fonte do kernel extraído, podemos aplicar um patch para que ele não seja tão rigoroso na comparação das strings vermagic. Em kernel/module.c, podemos simplesmente comentar a linha return -ENOEXEC:```diff
} else if (!same_magic(modmagic, vermagic, info->index.vers)) {
pr_err("%s: version magic '%s' should be '%s'\n", info->name, modmagic, vermagic);
- return -ENOEXEC;
+ // return -ENOEXEC;
}
if (!get_modinfo(info, "intree")) {
Pode não ser necessariamente útil a longo prazo, mas é bom para testar, desde que haja pequenas discrepâncias entre o vermagic do nosso kernel e o vermagic do módulo. Os módulos (na maioria dos casos) ainda carregam.
NOTA: aqui fornecemos um kernel já compilado que atende aos requisitos do alvo, para que você não precise compilar um você mesmo. Ele foi compilado com um script interno que:
multi_v7_defconfig para definir a configuração inicialkernel/module.cVIRTIO e subsistemas específicos como USB ou CANBUSVamos revisar os diferentes parâmetros do nosso comando qemu-system-arm. Fornecemos o kernel desta forma:```sh
qemu-system-arm -M virt,highmem=off
-m ${AVAILABLE_MEM_GB}G -smp ${CPU_CORES} -cpu cortex-a15
-kernel zImage
A imagem do sistema de arquivos EXT4 está anexada como um disco SCSI:```sh
-device virtio-scsi-device \
-device scsi-hd,drive=SystemDisk \
-drive file=${FIRMWARE_IMAGE},format=raw,if=none,id=SystemDisk
Definimos uma saída de console adequada desta forma:```sh
-append "${BOOT_PARAMS}"
-no-reboot
-nographic
-serial mon:stdio \
Os `BOOT_PARAMS` estão definidos em `config.ini` e correspondem ao `bootcmd` real do kernel, com duas pequenas adaptações (caminho do console e caminho do rootfs).
### Rede
Agora que temos um sistema emulado em funcionamento, é hora de configurar a rede. O alvo tem o seguinte:
- duas interfaces Ethernet (`eth0` e `eth1`)
- uma interface CANbus (`can0`)
- uma interface USB que pode atuar como Ethernet sobre USB (`usb0`)
- um modem celular Qualcomm High Sierra (`ppp0`)
Emular interfaces de modem celular e USB é um problema com o QEMU, e não é realmente necessário para a operação do dispositivo, já que essas interfaces são "opcionais". A interface CAN está lá para permitir que o controlador se comunique com outros controladores ou estações de carregamento. A interface `eth0` fornece conectividade upstream ao solicitar um lease DHCP por padrão, enquanto `eth1` está definida como 192.168.4.1 e existe caso você queira encadear vários controladores.
As interfaces Ethernet são configuradas como interfaces TAP e conectadas assim:```
-netdev tap,id=tap0net,ifname=${ETH_1},script=no,downscript=no \
-device virtio-net-device,netdev=tap0net,mac=${ETH_1_MAC} \
-netdev tap,id=tap1net,ifname=${ETH_0},script=no,downscript=no \
-device virtio-net-device,netdev=tap1net,mac=${ETH_0_MAC} \
A interface CANBUS é simplesmente uma interface PCI, conectada assim:```
-object can-bus,id=canbus0
-device kvaser_pci,canbus=canbus0
-object can-host-socketcan,id=canhost0,if=can0,canbus=canbus0 \
O diagrama abaixo apresenta a configuração de rede:

Se você quiser testar a comunicação entre controladores conectando-os em série (daisy-chain) conforme recomendado pela Phoenix Contact, você pode usar algo assim:

## Pronto ? Preparar. Lançar !
Agora que tudo está no lugar, é hora de iniciá-lo !
Primeiro, você precisa configurar todas as interfaces de rede assim:```
sudo ./ifup.sh
Certifique-se de que a sua interface upstream está configurada corretamente em config.ini:```
OUT_IF="eth0"
Você pode então iniciar o emulador:```
sudo ./launch.sh
Você verá os endereços obtidos nos logs:``` Dec 5 10:07:46 ev3000 daemon.info avahi-daemon[1047]: Registering new address record for 192.168.4.1 on eth1.IPv4. Dec 5 10:07:46 ev3000 daemon.info avahi-daemon[1047]: Registering new address record for 192.168.88.181 on eth0.IPv4.
### Acesso via SSH
Em seguida, você pode acessar via SSH o dispositivo emulado usando o usuário `user-app`:```sh
ssh [email protected]
A senha é "user". Você será forçado a alterá-la no primeiro login:```sh ssh [email protected] The authenticity of host '192.168.88.181 (192.168.88.181)' can't be established. ED25519 key fingerprint is SHA256:qMupzEehNFpx6eaGZ4d3AY5Jlms3Pmxh3/yg9OGA28o. This key is not known by any other names Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '192.168.88.181' (ED25519) to the list of known hosts. [email protected]'s password: WARNING: Your password has expired. You must change your password now and login again! Changing password for user-app Old password: Enter the new password (minimum of 5 characters) Please use a combination of upper and lower case letters and numbers. New password: Re-enter new password: passwd: password changed. Connection to 192.168.88.181 closed.
### Interface Web
Você pode acessar a interface web em http://192.168.4.1. Ela pode mostrar uma mensagem de erro
enquanto o dispositivo está inicializando. Assim que tudo estiver funcionando, você
deve ver isto:

Você pode fazer login com as seguintes contas:
| usuário | senha |
|----------|----------|
| manufacturer | manufacturer |
| operator | operator |
### Tráfego CAN
Quando tudo estiver funcionando, você pode capturar o tráfego CAN emitido pelo
controlador:```
candump -i can0
can0 701 [1] 00000101
can0 701 [1] 00000101
can0 701 [1] 00000101
can0 701 [1] 00000101
can0 701 [1] 00000101
can0 701 [1] 00000101
NOTA: parece que o QEMU versão 8.x tem algumas dificuldades em anexar o controlador CAN por algum motivo.