
In dieser Workshop-Sitzung extrahieren wir die Firmware aus einem EV-Ladegerät, tauchen tief in die Firmware ein und emulieren sie schließlich, damit wir in Echtzeit mit den Diensten interagieren können.
Machen Sie mit bei dieser praktischen Demo von Unblob, dem flexiblen Firmware-Extraktor. In dieser Workshop-Sitzung extrahieren wir Firmware aus einem EV-Ladegerät, tauchen in die Firmware ein und emulieren sie schließlich, damit wir in Echtzeit mit den Diensten interagieren können. Unblob funktioniert sowohl mit Hardware- als auch mit herunterladbaren Versionen von Firmware, sodass wir eine zielreiche Umgebung haben. Keine Vorerfahrung erforderlich, diese Sitzung ist für alle Kenntnisstufen geeignet, und wir freuen uns, Sie dort zu sehen.
Unser Ziel ist ein Ladesteuerungs-Controller für Elektrofahrzeuge von Phoenix Contact. Weitere Details dazu finden Sie here.
CHARX control modular, AC-Ladesteuerung, mit Embedded-Linux-System, IEC 61851-1, Betriebsmodus: Stand-Alone, Client, Server,
Schnittstellen:
- Ethernet (2x)
- Mobilfunkkommunikation (4G/2G)
- CHARX-control-modular-Systembus
- MICRO-USB Typ C
Kommunikationsprotokolle:
- OCPP 1.6J
- Modbus/TCP
- MQTT
Anschließbare Peripheriegeräte:
- Energiezähler
- RFID
- DC-Fehlerstromerkennung
- Hutschienenmontage
Für diesen Workshop benötigen wir ein paar Werkzeuge. Sie können sie installieren,
indem Sie das Skript install-prerequisites wie folgt ausführen:```sh
./install-prerequisites
## Beschaffung der Firmware
Die Firmware kann von der Website des Anbieters bezogen werden. In diesem Repository gibt es ein Skript namens
`download-firmware`, mit dem du die Firmware herunterladen kannst, ohne einen Browser öffnen zu müssen.
Unser Fokus liegt heute auf der vom Anbieter bereitgestellten Firmware, da sie alles enthält, was wir brauchen.
Aber ein ähnlicher Arbeitsablauf kann auf ein Speicherabbild angewendet werden, das von einem Live-Gerät extrahiert wurde.
Das Interessante daran ist, dass wir extrahieren, untersuchen und emulieren können, ohne ein echtes Gerät zu benötigen.
## Extraktion mit Unblob
Zuerst stellen wir sicher, dass alle Abhängigkeiten verfügbar sind:```
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 ✓
Jetzt können wir die Firmware mit unblob extrahieren:``` unblob CHARXSEC3XXXSoftwareBundleV190.raucb
Die Extraktion dauert auf einem ordentlichen Laptop etwa 3 Minuten. Sie sollten einen Fortschrittsbalken sehen, der sich nach oben bewegt:

Sobald die Extraktion abgeschlossen ist, sollte ein Verzeichnis mit dem Namen
`CHARXSEC3XXXSoftwareBundleV190.raucb_extract` sichtbar sein. Sie können
hineinwechseln und den Inhalt auflisten.
### Chunks, unbekannte Chunks
Unblob funktioniert, indem es Datenblöcke in Dateien identifiziert. Wenn ein Chunk ein
komprimierter Stream ist, wird er dekomprimiert. Wenn es sich um ein Dateisystem oder ein Archiv
handelt, wird er extrahiert. Wenn die Extraktion oder Dekompression erfolgreich war, wird der
Chunk, der auf die Festplatte geschnitten wurde, gelöscht, um Speicherplatz zurückzugewinnen.
Hier wurde ein SquashFS-Chunk Version 4 (Little Endian) auf die Festplatte geschnitten, extrahiert
und gelöscht. Dateien (und damit auch Extraktionsverzeichnisse) werden mit der
Nomenklatur `{start_offset}-{end_offset}.{type}` benannt.
Wir können sehen, dass nach dem squashfs-Dateisystem ein "unbekannter" Chunk von 11KB erscheint.```
./0-132173824.squashfs_v4_le_extract
./132173824-132184833.unknown
Du kannst binwalk darauf ausführen, um zu sehen, was es enthält:```
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
Sie können die Zertifikate mit openssl überprüfen:```
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
Das bedeutet, dass Firmwares wahrscheinlich mit dem privaten Schlüssel des Herstellers signiert sind, damit Geräte sicherstellen können, dass Firmwares authentisch sind.
Das ist einer der Vorteile von unblob: unbekannte Unbekannte in bekannte Unbekannte zu verwandeln, die untersucht werden können.
Schauen wir uns den Inhalt unseres squashfs-Dateisystems an:``` 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
Wir können ein Klartext-Manifest, ein Shell-Skript, einen MBR und ein EXT4-Dateisystem sehen:```
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)
Sowohl bootimg.vfat als auch root.ext4 wurden von unblob verarbeitet und extrahiert. Die VFAT-Partition enthält alles, was mit Boot und Betriebssystem zu tun hat (Linux-Kernel, 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
Du wirst sehen, dass unblob ein wenig gierig ist und eine ELF-Datei sowie ein CPIO-Archiv aus dem Linux-Kernel (`zImage`) extrahiert – diese entsprechen dem minimalen Kernel und der Ramdisk.
Das EXT4-Dateisystem enthält das Root-Dateisystem, das vom Linux-Kernel beim Booten eingehängt wird:```
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
Lassen Sie uns ein wenig erkunden, indem wir eine andere Reihe von unblob-Optionen verwenden:```
unblob -e /tmp/out -f -k -d 3 --report /tmp/report.json
--log /tmp/unblob.log CHARXSEC3XXXSoftwareBundleV190.raucb
Hier extrahieren wir weiterhin (`-e`) nach `/tmp/out`, erzwingen aber (`-f`) das Überschreiben, während wir (`-k`) herausgelöste Chunks behalten, aber die Rekursionstiefe (`-d`) auf 3 begrenzen. Wir schreiben einen detaillierten Bericht (`--report`) nach `/tmp/report.json` und eine Logdatei (`--log`) nach `/tmp/unblob.log`.
Wirf einen Blick in die Logdatei, dann siehst du die innere Funktionsweise von unblob.
Die Berichtsdatei enthält detaillierte Informationen über analysierte Dateien (Größe, Dateityp, Pfad, Magic, MIME-Typ, MD5/SHA1/SHA256-Hashes), Chunks (Größe, Offsets, Entropieverteilung) und Aufgaben (Extraktion, Dekompression, Carving). Es ist möglich, mit ein wenig Python ansprechende Visualisierungen aus diesen Berichtsdateien zu erzeugen.
Du kannst sie selbst mit dem Python-Skript `diagram.py` erstellen, das in diesem Repository verfügbar ist:```
python3 diagram.py /tmp/report.json sunburst
python3 diagram.py /tmp/report.json treemap
Diese Befehle öffnen im Browser eine Seite, die eine Plotly-basierte Visualisierung wie die folgenden enthält:


Nachdem wir nun einen besseren Überblick über den Inhalt haben, ist es an der Zeit aufzulisten, was wir für eine ordnungsgemäße Emulation des Geräts benötigen.
Idealerweise sollten wir die folgenden Informationen zusammentragen:
Um weitere Details über Plattform, CPU und Architektur zu erfahren, können wir einen Blick in die in der Firmware eingebetteten Device Tree Blobs werfen.
In der VFAT-Partition haben wir folgende 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
Bei diesen Dateien handelt es sich um die binäre Darstellung von Device Trees. Wir können ihren
Klartext-Quellcode mithilfe von `device-tree-compiler` wiederherstellen:```
dtc -I dtb -o zImage-imx6ul-ksp0632.dts zImage-imx6ul-ksp0632.dtb
dtc -I dtb -o oftree.dts oftree
Werfen Sie einen Blick auf das DTS, und Sie werden bald interessante Details wie das Modell finden:``` /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";
Die 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>;
};
};
Oder sogar Peripheriegeräte:``` --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"; };
### Informationen zur Linux-Kernel-Version
Wir können die Linux-Kernel-Version ermitteln, indem wir in der VFAT-Partition,
die den komprimierten Kernel enthält, mit grep suchen:```
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
Eine andere Möglichkeit besteht darin, die Kernelmodule im EXT4-Root-Dateisystem zu untersuchen:```
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)
## Emulation
Wir haben möglicherweise bereits einen kompilierten Kernel im Firmware-Paket, aber dieser
Kernel ist speziell für eine bestimmte Maschine kompiliert (das Phytec
phyBOARD-Segin i.MX6 UltraLite).
Um diesen Kernel richtig zu nutzen, müssten wir diesen Kernel in einer
Emulation genau der Maschine ausführen können, für die er kompiliert wurde. Das ist zeitintensiv,
wenn sie nicht bereits in der QEMU-Maschinenliste aufgeführt ist. Hinweis: Eine Evaluierungs-Board-
Maschine ist in QEMU verfügbar (`mcimx6ul-evk - Freescale i.MX6UL Evaluation Kit
(Cortex-A7)`), entspricht aber nicht exakt.
Stattdessen können wir in der Theorie eine QEMU-Maschine auswählen und verschiedene Metadaten verwenden, die wir
im Firmware-Image finden, um einen Kernel für diese Maschine zu konfigurieren und zu bauen,
der ebenfalls alles in unserem Dateisystem ausführen kann, so nah wie möglich am
Original.
Von hier aus haben wir einige Möglichkeiten:
- *Userspace-Emulation*: Wir könnten in unser eigenes benutzerdefiniertes ramfs/rootfs booten und dann
die Rootfs der Ziel-Firmware laden und per chroot hineinwechseln, um sie zu emulieren.
- *Full-System-Emulation*: Wir könnten versuchen, die Rootfs der Ziel-Firmware direkt zu booten
(indem wir sie direkt an die QEMU-Maschine übergeben).
Userspace-Emulation ist meist einschränkend, aber eine großartige Möglichkeit, bestimmte Binärdateien zu untersuchen, zu debuggen
und auszunutzen. Wir empfehlen dir dringend, dir
[EMUX](https://github.com/therealsaumil/emux) von Saumil Shah für alle deine
Anforderungen an die Userspace-Emulation anzusehen.
Unabhängig davon benötigen wir weiterhin einen Kernel, der in einer QEMU-Maschine bootet und
so genau wie möglich dem entspricht, was die Ziel-Firmware erwartet.
### QEMU-Maschinen
Es gibt nur wenige Kandidaten für Allzweck-Maschinen, die Flexibilität bieten.
Nach einiger Experimentierarbeit scheint `virt`
(https://qemu.readthedocs.io/en/latest/system/arm/virt.html) ein gutes
Verhältnis von Flexibilität zu Leistung für unsere Zwecke zu haben.
Beim Bauen des Kernels für eine bestimmte QEMU-Maschine ist es für uns wahrscheinlich am besten,
sicherzustellen, dass wir das allgemeine virtio-Gerät verwenden können, das QEMU mitbringt. In der
Konfiguration können diese ein oder mehrere der folgenden bedeuten:```
CONFIG_VIRTIO=y
CONFIG_SCSI_VIRTIO=y
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_BLK=y
Und mehr! Diese Liste ist unvollständig.
Es ist vorteilhaft für uns, einen Kernel zu verwenden, der eine identische (oder zumindest sehr ähnliche) Vermagic-Zeichenkette besitzt. Alle Arten von Komponenten innerhalb eines Systems könnten die Kernel-Vermagic prüfen und einen Fehler melden, wenn sie sich von der erwarteten unterscheidet. Dies ist am häufigsten bei Kernel-Modulen der Fall – ein Kernel weigert sich normalerweise, ein Modul zu laden, dessen Vermagic nicht zu seiner eigenen passt. Obwohl wir den Kernel patchen können, um dies auf Kernel-Ebene zu ignorieren, müssen wir dennoch den anfänglichen Teil „kernel release“ der Zeichenkette an die Release-Zeichenkette der Kernel-Module anpassen, da dieser als Suchpfad für die modules.dep verwendet wird.
In der Phoenix-Contact-Firmware lautet die Vermagic 5.15.195 preempt mod_unload modversions ARMv7 thumb2 p2v8.
Die Vermagic-Zeichenkette hat ein Format wie (aus 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
- Kernel-Release = die Kernel-Version + die `CONFIG_LOCALVERSION`-Zeichenkette.
- SMP = `CONFIG_SMP`
- PREEMPT = `CONFIG_PREEMPT`
- mod_unload = `CONFIG_MODULE_UNLOAD`
- modversions = `CONFIG_MODVERSIONS`
- ARMv7 thumb2 p2v8 = `MODULE_ARCH_VERMAGIC` (bezogen auf die Architektur, für die der Kernel kompiliert wurde, und daher in Zusammenhang mit der übergebenen Compiler-/Arch-Option.)
Sobald wir den extrahierten Kernelquellcode haben, können wir ihn so patchen, dass er beim Vergleich der Vermagic-Zeichenketten nicht so streng vorgeht. In kernel/module.c können wir einfach return -ENOEXEC auskommentieren:```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")) {
Es mag auf lange Sicht nicht unbedingt nützlich sein, aber es eignet sich gut zum Testen, solange es leichte Abweichungen zwischen unserem Kernel-Vermagic und dem Modul-Vermagic gibt. Die Module werden (meistens) trotzdem geladen.
HINWEIS: Hier stellen wir einen bereits gebauten Kernel bereit, der den Zielanforderungen entspricht, sodass du selbst keinen kompilieren musst. Er wurde mit einem internen Skript erstellt, das:
multi_v7_defconfig auf, um die Anfangskonfiguration festzulegenkernel/module.cVIRTIO-Optionen und spezifische Subsysteme wie
USB oder CANBUS zu aktivierenGehen wir die verschiedenen Parameter unseres qemu-system-arm-Befehls durch. Wir stellen den Kernel folgendermaßen bereit:```sh
qemu-system-arm -M virt,highmem=off
-m ${AVAILABLE_MEM_GB}G -smp ${CPU_CORES} -cpu cortex-a15
-kernel zImage
Das EXT4-Dateisystem-Image ist als SCSI-Festplatte angehängt:```sh
-device virtio-scsi-device \
-device scsi-hd,drive=SystemDisk \
-drive file=${FIRMWARE_IMAGE},format=raw,if=none,id=SystemDisk
Auf diese Weise richten wir eine ordentliche Konsolenausgabe ein:```sh
-append "${BOOT_PARAMS}"
-no-reboot
-nographic
-serial mon:stdio \
Die `BOOT_PARAMS` werden in `config.ini` gesetzt und entsprechen dem tatsächlichen Kernel-Bootcmd, mit zwei kleinen Anpassungen (Konsolenpfad und Rootfs-Pfad).
### Netzwerk
Jetzt, wo wir ein funktionierendes emuliertes System haben, ist es an der Zeit, das Netzwerk einzurichten. Das Zielsystem verfügt über Folgendes:
- zwei Ethernet-Schnittstellen (`eth0` und `eth1`)
- eine CANbus-Schnittstelle (`can0`)
- eine USB-Schnittstelle, die als Ethernet über USB fungieren kann (`usb0`)
- ein Qualcomm-High-Sierra-Mobilfunkmodem (`ppp0`)
Das Emulieren von Mobilfunkmodem- und USB-Schnittstellen ist mit QEMU mühsam und für den Gerätebetrieb nicht wirklich erforderlich, da diese Schnittstellen "optional" sind. Die CAN-Schnittstelle ist dafür da, damit der Controller mit anderen Controllern oder Ladestationen kommunizieren kann. Die `eth0`-Schnittstelle sorgt standardmäßig für Uplink-Konnektivität, indem sie eine DHCP-Lease anfordert, während `eth1` auf 192.168.4.1 gesetzt ist und für den Fall gedacht ist, dass Sie mehrere Controller in Reihe schalten möchten.
Die Ethernet-Schnittstellen sind als TAP-Schnittstellen eingerichtet und wie folgt angebunden:```
-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} \
Die CANBUS-Schnittstelle ist einfach eine PCI-Schnittstelle, die wie folgt angeschlossen wird:```
-object can-bus,id=canbus0
-device kvaser_pci,canbus=canbus0
-object can-host-socketcan,id=canhost0,if=can0,canbus=canbus0 \
Das Diagramm unten zeigt, wie das Netzwerk-Setup aussieht:

Wenn Sie die Kommunikation zwischen Controllern per Daisy-Chaining ausprobieren möchten, wie von Phoenix Contact empfohlen, könnten Sie etwa Folgendes verwenden:

## Bereit? Fertig. Los!
Da jetzt alles an seinem Platz ist, ist es Zeit, es hochzufahren!
Zuerst müssen Sie alle Netzwerkschnittstellen folgendermaßen einrichten:```
sudo ./ifup.sh
Stelle sicher, dass deine Upstream-Schnittstelle in config.ini korrekt eingestellt ist:```
OUT_IF="eth0"
Sie können dann den Emulator starten:```
sudo ./launch.sh
Sie sehen die ermittelten Adressen in den 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.
### Zugriff über SSH
Sie können dann per SSH mit dem Benutzer `user-app` auf das emulierte Gerät zugreifen:```sh
ssh [email protected]
Das Passwort ist "user". Sie werden gezwungen sein, es bei der ersten Anmeldung zu ändern:```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.
### Web-Oberfläche
Sie können auf die Web-Oberfläche unter http://192.168.4.1 zugreifen. Während das
Gerät hochfährt, wird möglicherweise eine Fehlermeldung angezeigt. Sobald alles
läuft, sollten Sie Folgendes sehen:

Sie können sich mit den folgenden Konten anmelden:
| Benutzername | Passwort |
|----------|----------|
| manufacturer | manufacturer |
| operator | operator |
### CAN-Verkehr
Wenn alles hochgefahren ist, können Sie den vom Controller ausgegebenen
CAN-Verkehr erfassen:```
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
HINWEIS: Es scheint, dass QEMU Version 8.x aus irgendeinem Grund einige Schwierigkeiten hat, den CAN-Controller anzubinden.