Files
bintec-w2044ax-firmware-qemu/README.md
T

13 KiB
Raw Blame History

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 virt booten -> 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/reboot in 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:

  1. carve.py sucht 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
  2. Das FIT-Image (ARM64-Kernel gzip + Device-Trees) wird als FDT geparst: work/kernel.img (bootfaehig), work/kernel.raw, work/<modell>.dtb
  3. unsquashfs -> work/rootfs/ (Debian-Userspace)
  4. 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 nach work/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.py ist generisch gehalten: SquashFS wird ueber Magic+Superblock- Pruefung gesucht, das FIT ueber kernel@1 + beliebiges fdt@*-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.ext4 ist 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) und binwalk 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) erzeugt work/rootfs/ aus dem Firmware-Image, build-generic.sh (Schritt 3) baut daraus Kernel, Initramfs und rootfs.ext4 inkl. 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 ueber bevyosgui.service, aktiviert durch die Factory-Config-Zeile set 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.py liest 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 -b wird ausgefuehrt.
  • RootFS wird kopiert (rootfs-generic), Kernel-Module fuer 6.1 hinzugefuegt und mit mkfs.ext4 -d nach rootfs.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 virt emuliert 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).