271 lines
13 KiB
Markdown
271 lines
13 KiB
Markdown
# 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)
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
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)
|
||
|
||
```bash
|
||
cd qemu-fw-docker
|
||
docker build -t bintec-fw .
|
||
```
|
||
|
||
### Schritt 2: Firmware zerlegen (Phase 2)
|
||
|
||
```bash
|
||
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)
|
||
|
||
```bash
|
||
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
|
||
|
||
```bash
|
||
./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).
|