13 KiB
Bintec W20XXax-Firmware in QEMU (Docker) – Emulations- und Analyse-Umgebung
Die AP-Firmware (Bintec W2022ax / W2044ax, "Wanda Wave 2") laeuft nicht direkt in QEMU: Sie ist fuer einen Qualcomm IPQ60xx-SoC ("kaio") gebaut, und QEMU hat dafuer kein Maschinenmodell. Der Weg der Emulation ist daher:
Firmware auseinandernehmen -> RootFS extrahieren -> RootFS mit generischem Debian-arm64-Kernel in
qemu-system-aarch64 -M virtbooten -> Webinterface + CLI beobachten
Das gesamte Setup laeuft unprivilegiert in Docker (TCG-Emulation, kein KVM,
keine Root-Rechte auf dem Host noetig). Alles liegt in diesem Ordner
(qemu-fw-docker/), der Host bleibt unveraendert.
Ergebnis
root@osdx:/# uname -a
Linux osdx 6.1.0-50-arm64 #1 SMP Debian 6.1.176-1 aarch64 GNU/Linux
- Das OSDx-System bootet bis zur interaktiven Root-Shell auf der Serial-Console
- Das originale Webinterface antwortet (Login-Seite "bintec Wanda Wave 2", XML/XSL-GUI ueber esi.cgi) auf Port 80 und 443
- SSH ist erreichbar (Port 22 in der VM)
poweroff/rebootin der VM funktionieren sauber (Reboot-Loop ist entschaeft)
Schnellstart (VM + Webinterface)
cd qemu-fw-docker
docker build -t bintec-fw . # einmalig
./start-vm.sh # VM als Container "bintec-vm" starten
Nach ~2-3 Minuten Boot:
| Zugang | Adresse | Anmerkung |
|---|---|---|
| Webinterface HTTP | http://localhost:8080 | |
| Webinterface HTTPS | https://localhost:8443 | self-signed Zertifikat |
| SSH | ssh -p 2222 localhost | root/leer bzw. admin |
| Serial (lesend) | docker logs -f bintec-vm | |
| Serial (interaktiv) | docker attach bintec-vm | abkoppeln: Strg+P dann Strg+Q |
| Stoppen | docker stop bintec-vm && docker rm bintec-vm |
Serial-Login: die VM startet direkt in eine Root-Shell auf ttyAMA0 (keine Anmeldung noetig).
Von der Firmware zum lauffaehigen System (kompletter Ablauf)
Schritt 0: Firmware-Paket besorgen
Es gibt zwei Paketformen (beide von Bintec/Teldat, Inhalt identisch):
| Paket | Inhalt | Aufbereitung |
|---|---|---|
WiFi6_System_Software_vX.Y.Z.img |
Full-Flash-Image, direkt verwendbar | kopieren nach firmware/ |
OSDx_dist_W20XXax_vX.Y.Z.tgz |
Tar-Archiv mit full_kaio_vX.Y.Z.img + README.txt + version_map.txt |
entpacken, .img nach firmware/ |
Falls das Paket als Tar kommt:
tar xzf OSDx_dist_W20XXax_v3.6.1.2.tgz
# -> full_kaio_v3.6.1.2.img (identisch zur .img-Variante)
mkdir -p firmware && cp full_kaio_v3.6.1.2.img firmware/
Die version_map.txt im Archiv sagt, welche img-Datei zu welchem AP-Modell
gehoert (W2022ax / W2044ax / APR2044ax verwenden alle dasselbe full_kaio-Image).
Schritt 1: Container-Image bauen (einmalig)
cd qemu-fw-docker
docker build -t bintec-fw .
Schritt 2: Firmware zerlegen (Phase 2)
docker run --rm \
-v "$PWD:/build" \
-v "$PWD/firmware:/input:ro" \
bintec-fw /build/extract.sh /input/<name-der>.img
Was passiert:
carve.pysucht das SquashFS (versionsunabhaengig per Magic + Plausibilitaets- pruefung, uebernimmt aber den bekannten Offset von v3.6.1.2 als Direkttreffer) und schneidet es aus:work/rootfs.squashfs- Das FIT-Image (ARM64-Kernel gzip + Device-Trees) wird als FDT geparst:
work/kernel.img(bootfaehig),work/kernel.raw,work/<modell>.dtb unsquashfs->work/rootfs/(Debian-Userspace)- Inventur nach
logs/inventory.txt(OS-Version, Dienste, GUI-Pfade, Module)
Schritt 3: Boot-Artefakte bauen (Phase 4a)
docker run --rm -v "$PWD:/build" bintec-fw /build/build-generic.sh
- laedt Debian-arm64-Kernel (
linux-image-*-arm64, bookworm) + arm64-busybox - baut Mini-Initramfs (virtio/ext4/crc32c-Module + switch_root)
- kopiert RootFS nach
work/rootfs-generic/, legt alle Emulations-Anpassungen an (siehe Tabelle unten) und verpackt alles nachwork/rootfs.ext4 - Schritte 2+3 sind idempotent und duerfen wiederholt werden
Schritt 4: Booten
./start-vm.sh # Dauerbetrieb + Webinterface
# oder mit Log-Tail:
docker run --rm -v "$PWD:/build" bintec-fw /build/boot-generic.sh
Andere Firmware-Versionen emulieren
carve.pyist generisch gehalten: SquashFS wird ueber Magic+Superblock- Pruefung gesucht, das FIT ueberkernel@1+ beliebigesfdt@*-Node (bevorzugt w2044ax). Getestet gegen v3.6.1.1 und v3.6.1.2 (archive_.../).- Ablauf fuer eine neue Version: img in
firmware/legen, Schritte 2-4 wiederholen.work/vorher optional leeren (rm -rf work/rootfs* work/kernel* work/fit.img). - Achtung:
work/rootfs.ext4ist versionsspezifisch – nach dem Wechsel auf jeden Fall Schritt 3 neu ausfuehren. - Falls eine kuenftige Version eine andere FIT-Struktur nutzt, verraten das
logs/carve.log(Kandidaten + Metadaten) undbinwalk firmware/<img>.
Ordnerinhalt
qemu-fw-docker/
├── firmware/
│ └── WiFi6_System_Software_v3.6.1.2.img Original-Firmware (143 MB, md5 bb4c065e...)
├── Dockerfile Container: Debian bookworm-slim + QEMU/Tools
├── carve.py Firmware-Carver: SquashFS + FIT (Kernel/DTB) extrahieren
├── extract.sh Phase 2: Firmware zerlegen + Inventur (erwartet /input/*.img)
├── build-generic.sh Phase 4: Debian-Kernel laden, RootFS aufbereiten, ext4+Initramfs bauen
├── boot-native.sh Versuch 1: Original-QSDK-Kernel in -M virt (erwartungsgemaess stumm)
├── boot-generic.sh Versuch 2 mit Logging (Timeout 420 s)
├── boot-interactive.sh Interaktiver Dauerbetrieb (Serial -> docker logs/attach)
├── test-web.sh Automatischer Webinterface-Test (QEMU im Hintergrund + wget)
├── start-vm.sh Bequemer Start (Container "bintec-vm", Ports published)
├── PLAN.md Urspruenglicher Plan
├── README.md Diese Datei
├── logs/ Alle Logfiles (carve, inventory, boot-*, webtest)
└── work/ Arbeitsartefakte (RootFS, Kernel, ext4-Image, ~2 GB)
├── rootfs/ entpacktes Original-RootFS (556 MB, read-only Basis)
├── rootfs.squashfs geschnitztes SquashFS aus der Firmware
├── kernel.img Original-QSDK-Kernel 4.4.60 (ARM64, dekomprimiert)
├── kernel.raw Original-Kernel gzip-Blob
├── w2044ax.dtb Device-Tree W2044ax (aus dem FIT)
├── fit.img komplettes FIT-Image
├── initramfs.img Mini-Initramfs (busybox + virtio/ext4-Module)
├── rootfs-generic/ aufbereitete RootFS-Kopie (siehe Aenderungen unten)
├── rootfs.ext4 das gebootete Root-Dateisystem (1,5 GB sparse)
└── deb-kernel/ Debian bookworm arm64-Kernel (6.1.0-50-arm64) + busybox
Repo vs. lokal generierte Daten (firmware/ und work/)
Beide Ordner sind absichtlich nicht Teil des Repos (.gitignore):
firmware/– die Firmware ist proprietär (Bintec/Teldat). Bitte selbst herunterladen (Schritt 0 oben) und das .img dort ablegen.work/– wird komplett generiert, es gibt keine manuellen Dateien:extract.sh(Schritt 2) erzeugtwork/rootfs/aus dem Firmware-Image,build-generic.sh(Schritt 3) baut daraus Kernel, Initramfs undrootfs.ext4inkl. aller Emulations-Anpassungen (Tabelle unten). Zum Nachbauen einfach die Schritte 1-4 der Schnellstart-Anleitung ausführen.
Firmware-Layout (WiFi6_System_Software_v3.6.1.2.img)
Full-Flash-Dump "full.img", Plattform kaio (IPQ60xx), U-Boot 11.4-1-2016.01:
| Offset | Groesse | Inhalt |
|---|---|---|
| 0x00000000 | ~21 MB | Container-Header, u-boot.sbl.img, SBL1 (mit Qualcomm-Attestation) |
| 0x1503930 | 3,95 MB | FIT "ARM64 OpenWrt FIT": kernel@1 (Linux 4.4.60 QSDK, gzip, load/entry 0x41080000), fdt@w2022ax, fdt@w2044ax, configs fuer beide AP-Varianten |
| 0x18c7854 | 117 MB | SquashFS v4 (xz) = RootFS |
RootFS-Inventur (Details: logs/inventory.txt):
- Debian 10 (buster), arm64, glibc 2.28, init = systemd
- OSDx-Stack:
/etc/osdx,/opt/vyatta(Vyatta/VyOS-CLI-Framework),/opt/qsdk - Dienste: FRR (BGP/OSPF), SNMP, SSH, dnsmasq, radvd, PPP, python3.7
- Web-GUI:
/usr/sbin/gui("Bintec Elmeg VyOS GUI", boost.asio), gestartet ueberbevyosgui.service, aktiviert durch die Factory-Config-Zeileset service gui - GUI-Inhalte:
/usr/share/gui/(XML/XSL-Ajax-GUI mit Telekom/Bintec-Branding) - Login: Factory-User
admin(Config-Zeile in/opt/vyatta/etc/config-default.boot.default) - Kernelpakete:
/lib/modules/4.4.60(QSDK)
Pipeline im Detail (Technische Hintergruende)
Phase 2 – Firmware extrahieren (extract.sh + carve.py)
carve.pyliest die .img, findet per Superblock das SquashFS (Offset 0x18c7854 bei v3.6.1.x) und schneidet es exakt aus (bytes_used).- Das FIT (bei 0x1503930, endet exakt am SquashFS-Start) wird als FDT geparst;
herauskommen
kernel.raw(gzip),kernel.img(ARM64-Image, Magic "ARMd" bei Offset 0x38),<modell>.dtb. - Danach
unsquashfs->work/rootfs/, Inventur ->logs/inventory.txt.
Hinweis: das FIT-Struct-Block endet ohne FDT_END-Token (Generator-Besonderheit);
carve.py behandelt das toleriert.
Phase 4a – Kernel beschaffen + RootFS aufbereiten (build-generic.sh)
- Lautet den Debian-Package-Index (bookworm, arm64) und laedt
linux-image-6.1.0-50-arm64(aus linux-signed-arm64) +busybox-static(arm64). - Config-Check: Debian-Kernel hat PL011-Konsole =y, aber EXT4/VIRTIO_BLK/VIRTIO_PCI
=m -> deshalb Mini-Initramfs (busybox +
modprobe virtio_pci virtio_blk virtio_net crc32c ext4+ switch_root). - Die Kernel-.deb enthaelt keine modules.dep ->
depmod -bwird ausgefuehrt. - RootFS wird kopiert (
rootfs-generic), Kernel-Module fuer 6.1 hinzugefuegt und mitmkfs.ext4 -dnachrootfs.ext4(1,5 GB sparse) verpackt.
Phase 4b – Booten (boot-generic.sh / boot-interactive.sh / start-vm.sh)
qemu-system-aarch64 -M virt -cpu cortex-a53 -m 1024 -smp 1 \
-kernel vmlinuz-6.1.0-50-arm64 -initrd initramfs.img \
-drive if=virtio,format=raw,file=rootfs.ext4 \
-netdev user,id=n0,hostfwd=...:2222-:22,hostfwd=...:8080-:80,hostfwd=...:8443-:443 \
-device virtio-net-pci,netdev=n0 -no-reboot -nographic \
-append "root=/dev/vda rw console=ttyAMA0,115200"
Aenderungen im RootFS (nur in der Kopie rootfs-generic/rootfs.ext4)
Grund: OSDx erwartet die echte AP-Hardware (QCA-WLAN, Flash-Environment "0x0", LEDs, Watchdog durch cfgd). Ohne Hardware haelt sich das System nicht oben – folgende Stellschrauben machen den Boot stabil und die Shell erreichbar:
| Eingriff (per systemd-Drop-In / Symlink) | Wirkung |
|---|---|
serial-getty@ttyAMA0.service.d/autologin.conf |
Direkte Root-Shell auf ttyAMA0 (OSDx-PAM lehnt Autologin/leeres PW ab) |
vyos-cfgd.service.d/emulation.conf: WatchdogSec=0, TimeoutSec=0, StartLimitBurst=0 |
Ohne echte HW sendet cfgd keine Watchdog-Pings -> sonst StartLimitAction=reboot-force bei ~200 s |
qca-osdx-mon.service -> /dev/null (maskiert) |
QCA-WLAN-Monitor braucht echte Hardware |
osdx-modulelauncher.service.d/…: TimeoutSec=0 |
Timeout entschaeft |
systemd-reboot.service -> /dev/null (maskiert) |
osdx-load-config ruft bei Config-Fehlern reboot auf -> wird jetzt nur mit Fehler abgewiesen |
qemu-net.service |
Netz-Bringup: 10.0.2.15/24 auf eth0/br0, Default-Route ueber 10.0.2.2 (QEMU-Usernet) |
multi-user.target.wants/bevyosgui.service |
Web-GUI aktiviert |
/etc/shadow: root-Passwort geleert |
Fallback-Anmeldung |
Debian-Module 6.1.0-50-arm64 nach /lib/modules/ |
RootFS brachte nur QSDK-4.4.60-Module mit |
Logs
| Datei | Inhalt |
|---|---|
logs/carve.log |
Extraktion (Offsets, FIT-Metadaten) |
logs/inventory.txt |
RootFS-Inventur |
logs/kernel-config.txt |
Debian-Kernel-Config-Auszug |
logs/unsquashfs.log |
Entpacken |
logs/boot-native.log |
Versuch 1: Original-QSDK-Kernel – 60 s komplett stumm (Crash vor jeder Konsolenausgabe, erwartbar: kein IPQ60xx-Modell) |
logs/boot-generic.log |
Versuch 2: vollstaendiger Boot bis root@osdx:/# |
logs/boot-interactive.log |
Interaktivitaets-Nachweis (uname, ip link, /opt/vyatta, sauberes poweroff) |
logs/boot-webtest.log |
Webinterface-Test |
Bekannte Einschraenkungen
- QEMU
virtemuliert kein IPQ60xx: kein WiFi, keine Switch-/LED-/NAND-HW. Im Log normal: "Cannot open 0x0", "environment not initialized", LED-/qca-**-Service-Fehler. - OSDx-Config-Loader laeuft mit "factory configuration for default" ab (echte Configs liegen im Flash-Environment, das emuliert nicht existiert).
- Versuch 1 (Original-QSDK-Kernel) bootet nicht – dokumentiert in
logs/boot-native.log. - TCG-Emulation: Boot ~2-3 min, laeuft danach stabil.
- GUI-Session-Login laeuft ueber vyos-cfgd; falls der Browser-Login mit admin/Factory-Passwort scheitert, Serial-Shell nutzen (docker attach).