tcl-fw 3.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tcl_fw-3.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
tcl_fw-3.0.0/PKG-INFO ADDED
@@ -0,0 +1,169 @@
1
+ Metadata-Version: 2.4
2
+ Name: tcl-fw
3
+ Version: 3.0.0
4
+ Summary: Pull and decrypt official TCL (MediaTek) FOTA firmware — flashable service packages, fully offline.
5
+ Author: vehoelite, Littlenine Ennea
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/vehoelite/tcl-fota-tool
8
+ Project-URL: Credit, https://github.com/LittlenineEnnea
9
+ Keywords: tcl,fota,firmware,mediatek,android,mbn,decrypt,scatter
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Utilities
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: typer>=0.12
19
+ Requires-Dist: rich>=13.0
20
+ Requires-Dist: pycryptodome>=3.20
21
+ Provides-Extra: gui
22
+ Requires-Dist: PySide6>=6.6; extra == "gui"
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=8.0; extra == "dev"
25
+ Requires-Dist: pyinstaller>=6.0; extra == "dev"
26
+ Requires-Dist: PySide6>=6.6; extra == "dev"
27
+ Dynamic: license-file
28
+
29
+ # tcl-fw
30
+
31
+ **Pull and decrypt official TCL (MediaTek) firmware — flashable service packages, fully offline.**
32
+
33
+ `tcl-fw` talks to TCL's own FOTA download servers the way the on-device updater
34
+ does (no Google, no account, no dongle), lists a device's complete factory
35
+ "service" fileset, streams the plaintext partitions, and **AES-decrypts** the
36
+ small partitions that ship inside an encrypted 4 MiB header — producing clean,
37
+ flashable images (`lk.img`, `boot.img`, `vbmeta.img`, `preloader_*.bin`, the
38
+ scatter, …).
39
+
40
+ > ### Credit
41
+ > The header-decryption scheme that makes this tool possible — **AES-128-ECB
42
+ > with a universal key recovered from `sugar_otu_r.dll`** — was cracked by
43
+ > **[Littlenine Ennea](https://github.com/LittlenineEnnea)**. Mode 4 (full-image
44
+ > decryption) exists entirely because of that work. Thank you.
45
+
46
+ Works on TCL-made Android devices (TCL, REVVL, Alcatel).
47
+
48
+ ---
49
+
50
+ ## Install
51
+
52
+ ```bash
53
+ pip install tcl-fw # CLI only
54
+ pip install "tcl-fw[gui]" # CLI + desktop app (PySide6)
55
+ ```
56
+
57
+ Or grab the standalone `tcl-fw` / `tcl-fw.exe` (CLI) or `tcl-fw-gui.exe`
58
+ (desktop app) from
59
+ [Releases](https://github.com/vehoelite/tcl-fota-tool/releases) — no Python needed.
60
+
61
+ ## Desktop app
62
+
63
+ Prefer clicking to typing? Launch the GUI:
64
+
65
+ ```bash
66
+ tcl-fw-gui # or: python -m tcl_fw_gui
67
+ ```
68
+
69
+ Pick (or **Detect**) a device → **Load** to see every partition with real sizes
70
+ → tick what you want → **Pull**. Per-partition progress, live decrypt log, and
71
+ SHA-1 verification, all over the exact same backend as the CLI. On Windows the
72
+ GUI uses the native `adb`, so **Detect phone** works without any usbipd/WSL
73
+ plumbing.
74
+
75
+ ## Quickstart
76
+
77
+ ```bash
78
+ # Plug in a phone with USB debugging on — tcl-fw reads the curef itself:
79
+ tcl-fw pull
80
+
81
+ # …or name the device explicitly:
82
+ tcl-fw list T704SP-EAUHUS12-V # see every partition, size, name
83
+ tcl-fw pull T704SP-EAUHUS12-V # download + decrypt the whole package
84
+ tcl-fw pull T704SP-EAUHUS12-V --small # just the small parts (lk/preloader/… fast)
85
+ tcl-fw pull T704SP-EAUHUS12-V --only lk,boot,vbmeta
86
+ tcl-fw decrypt some_header.bin # decrypt one local header blob
87
+ ```
88
+
89
+ Find your curef on a handset:
90
+
91
+ ```bash
92
+ adb shell getprop ro.tct.curef
93
+ ```
94
+
95
+ ## Commands
96
+
97
+ | Command | What it does |
98
+ |---|---|
99
+ | `tcl-fw pull [curef]` | Download + decrypt a device's service package into flashable images. Auto-detects the curef from a plugged-in phone if omitted. `--small`, `--only p1,p2`, `--out DIR`, `--no-verify`. |
100
+ | `tcl-fw list [curef]` | Resolve a device and list every partition: name, real size, and whether it comes from the body or the encrypted header. |
101
+ | `tcl-fw decrypt <blob>` | Decrypt a single local encrypted-header blob and name it by content. |
102
+ | `tcl-fw devices [--detect]` | List known devices, or probe for a connected phone. |
103
+
104
+ ## How it works
105
+
106
+ TCL's FOTA server delivers each partition in one of two ways, and `tcl-fw`
107
+ handles both automatically:
108
+
109
+ - **Large partitions** (`super`, `system`, `vendor`, `boot`, `md1img`, …) — the
110
+ plaintext **body** *is* the image; it's streamed straight to disk (with resume
111
+ and SHA-1 verification against the server's `checksum.php`).
112
+ - **Small partitions** (`lk`, `preloader`, `tee`/`atf`, `vbmeta`, `spmfw`,
113
+ `scatter`, …) — the body is empty; the real image lives inside an encrypted
114
+ ~4 MiB header fetched from `encrypt_header.php`. That blob is **AES-128-ECB**
115
+ with the single universal key
116
+
117
+ ```
118
+ KEY = ascii( md5("TeleExtTest" + "t0523" + "jP7GHdmuBz").hexdigest()[:16] )
119
+ = e26baba108b08a28
120
+ ```
121
+
122
+ The header is padded with a constant filler block, which `tcl-fw` detects and
123
+ trims to recover the exact image.
124
+
125
+ Partitions are named **authoritatively** from the server: the `check_new.php`
126
+ manifest is joined to the `.sca` scatter's `rename_prefix → file_name` map, so
127
+ you get real names, not guesses. When the scatter is unavailable, images are
128
+ identified by content (MTK GFH partition name, ext4 volume label, AVB/boot/dtbo
129
+ magic).
130
+
131
+ ## Output
132
+
133
+ ```
134
+ pkg_<curef>/
135
+ lk.img boot.img vbmeta.img super.img preloader_*.bin …
136
+ <device>.sca # the flash-tool scatter
137
+ manifest.json # what was pulled, sizes, checksum results
138
+ ```
139
+
140
+ Feed these to SP Flash Tool, `fastboot`, or `mtkclient`.
141
+
142
+ ## Related — Image Anarchy
143
+
144
+ Pulled a package and want to flash, repack, or explore it? Check out
145
+ **[Image Anarchy](https://github.com/vehoelite/image-anarchy)** — a companion
146
+ toolkit for working with Android firmware images. `tcl-fw` gets you the clean,
147
+ named partitions; Image Anarchy helps you do something with them.
148
+
149
+ ## Legal / ethical use
150
+
151
+ This tool downloads firmware that TCL's own servers serve publicly, for the
152
+ purpose of repairing, restoring, or inspecting **a device you own**. It uses no
153
+ exploit against the device and asks the servers only for what the on-device
154
+ updater already requests. Respect your local laws and TCL's terms.
155
+
156
+ ## Credits
157
+
158
+ - **[Littlenine Ennea](https://github.com/LittlenineEnnea)** — cracked the
159
+ AES-128-ECB header-decryption scheme and the universal key; the reference
160
+ implementation lives in [`mode4/tcl-fw.py`](mode4/tcl-fw.py). Mode 4 is theirs.
161
+ - **[vehoelite](https://github.com/vehoelite)** — the original `tcl-fota-tool`
162
+ FOTA protocol client (check/download signing, fileset parsing), preserved in
163
+ [`legacy/`](legacy/), and the companion
164
+ [Image Anarchy](https://github.com/vehoelite/image-anarchy) firmware toolkit.
165
+ - Predecessor protocol research: `mbirth/tcl_ota_check`, `thurask/bbarchivist`.
166
+
167
+ ## License
168
+
169
+ MIT — see [LICENSE](LICENSE).
tcl_fw-3.0.0/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # tcl-fw
2
+
3
+ **Pull and decrypt official TCL (MediaTek) firmware — flashable service packages, fully offline.**
4
+
5
+ `tcl-fw` talks to TCL's own FOTA download servers the way the on-device updater
6
+ does (no Google, no account, no dongle), lists a device's complete factory
7
+ "service" fileset, streams the plaintext partitions, and **AES-decrypts** the
8
+ small partitions that ship inside an encrypted 4 MiB header — producing clean,
9
+ flashable images (`lk.img`, `boot.img`, `vbmeta.img`, `preloader_*.bin`, the
10
+ scatter, …).
11
+
12
+ > ### Credit
13
+ > The header-decryption scheme that makes this tool possible — **AES-128-ECB
14
+ > with a universal key recovered from `sugar_otu_r.dll`** — was cracked by
15
+ > **[Littlenine Ennea](https://github.com/LittlenineEnnea)**. Mode 4 (full-image
16
+ > decryption) exists entirely because of that work. Thank you.
17
+
18
+ Works on TCL-made Android devices (TCL, REVVL, Alcatel).
19
+
20
+ ---
21
+
22
+ ## Install
23
+
24
+ ```bash
25
+ pip install tcl-fw # CLI only
26
+ pip install "tcl-fw[gui]" # CLI + desktop app (PySide6)
27
+ ```
28
+
29
+ Or grab the standalone `tcl-fw` / `tcl-fw.exe` (CLI) or `tcl-fw-gui.exe`
30
+ (desktop app) from
31
+ [Releases](https://github.com/vehoelite/tcl-fota-tool/releases) — no Python needed.
32
+
33
+ ## Desktop app
34
+
35
+ Prefer clicking to typing? Launch the GUI:
36
+
37
+ ```bash
38
+ tcl-fw-gui # or: python -m tcl_fw_gui
39
+ ```
40
+
41
+ Pick (or **Detect**) a device → **Load** to see every partition with real sizes
42
+ → tick what you want → **Pull**. Per-partition progress, live decrypt log, and
43
+ SHA-1 verification, all over the exact same backend as the CLI. On Windows the
44
+ GUI uses the native `adb`, so **Detect phone** works without any usbipd/WSL
45
+ plumbing.
46
+
47
+ ## Quickstart
48
+
49
+ ```bash
50
+ # Plug in a phone with USB debugging on — tcl-fw reads the curef itself:
51
+ tcl-fw pull
52
+
53
+ # …or name the device explicitly:
54
+ tcl-fw list T704SP-EAUHUS12-V # see every partition, size, name
55
+ tcl-fw pull T704SP-EAUHUS12-V # download + decrypt the whole package
56
+ tcl-fw pull T704SP-EAUHUS12-V --small # just the small parts (lk/preloader/… fast)
57
+ tcl-fw pull T704SP-EAUHUS12-V --only lk,boot,vbmeta
58
+ tcl-fw decrypt some_header.bin # decrypt one local header blob
59
+ ```
60
+
61
+ Find your curef on a handset:
62
+
63
+ ```bash
64
+ adb shell getprop ro.tct.curef
65
+ ```
66
+
67
+ ## Commands
68
+
69
+ | Command | What it does |
70
+ |---|---|
71
+ | `tcl-fw pull [curef]` | Download + decrypt a device's service package into flashable images. Auto-detects the curef from a plugged-in phone if omitted. `--small`, `--only p1,p2`, `--out DIR`, `--no-verify`. |
72
+ | `tcl-fw list [curef]` | Resolve a device and list every partition: name, real size, and whether it comes from the body or the encrypted header. |
73
+ | `tcl-fw decrypt <blob>` | Decrypt a single local encrypted-header blob and name it by content. |
74
+ | `tcl-fw devices [--detect]` | List known devices, or probe for a connected phone. |
75
+
76
+ ## How it works
77
+
78
+ TCL's FOTA server delivers each partition in one of two ways, and `tcl-fw`
79
+ handles both automatically:
80
+
81
+ - **Large partitions** (`super`, `system`, `vendor`, `boot`, `md1img`, …) — the
82
+ plaintext **body** *is* the image; it's streamed straight to disk (with resume
83
+ and SHA-1 verification against the server's `checksum.php`).
84
+ - **Small partitions** (`lk`, `preloader`, `tee`/`atf`, `vbmeta`, `spmfw`,
85
+ `scatter`, …) — the body is empty; the real image lives inside an encrypted
86
+ ~4 MiB header fetched from `encrypt_header.php`. That blob is **AES-128-ECB**
87
+ with the single universal key
88
+
89
+ ```
90
+ KEY = ascii( md5("TeleExtTest" + "t0523" + "jP7GHdmuBz").hexdigest()[:16] )
91
+ = e26baba108b08a28
92
+ ```
93
+
94
+ The header is padded with a constant filler block, which `tcl-fw` detects and
95
+ trims to recover the exact image.
96
+
97
+ Partitions are named **authoritatively** from the server: the `check_new.php`
98
+ manifest is joined to the `.sca` scatter's `rename_prefix → file_name` map, so
99
+ you get real names, not guesses. When the scatter is unavailable, images are
100
+ identified by content (MTK GFH partition name, ext4 volume label, AVB/boot/dtbo
101
+ magic).
102
+
103
+ ## Output
104
+
105
+ ```
106
+ pkg_<curef>/
107
+ lk.img boot.img vbmeta.img super.img preloader_*.bin …
108
+ <device>.sca # the flash-tool scatter
109
+ manifest.json # what was pulled, sizes, checksum results
110
+ ```
111
+
112
+ Feed these to SP Flash Tool, `fastboot`, or `mtkclient`.
113
+
114
+ ## Related — Image Anarchy
115
+
116
+ Pulled a package and want to flash, repack, or explore it? Check out
117
+ **[Image Anarchy](https://github.com/vehoelite/image-anarchy)** — a companion
118
+ toolkit for working with Android firmware images. `tcl-fw` gets you the clean,
119
+ named partitions; Image Anarchy helps you do something with them.
120
+
121
+ ## Legal / ethical use
122
+
123
+ This tool downloads firmware that TCL's own servers serve publicly, for the
124
+ purpose of repairing, restoring, or inspecting **a device you own**. It uses no
125
+ exploit against the device and asks the servers only for what the on-device
126
+ updater already requests. Respect your local laws and TCL's terms.
127
+
128
+ ## Credits
129
+
130
+ - **[Littlenine Ennea](https://github.com/LittlenineEnnea)** — cracked the
131
+ AES-128-ECB header-decryption scheme and the universal key; the reference
132
+ implementation lives in [`mode4/tcl-fw.py`](mode4/tcl-fw.py). Mode 4 is theirs.
133
+ - **[vehoelite](https://github.com/vehoelite)** — the original `tcl-fota-tool`
134
+ FOTA protocol client (check/download signing, fileset parsing), preserved in
135
+ [`legacy/`](legacy/), and the companion
136
+ [Image Anarchy](https://github.com/vehoelite/image-anarchy) firmware toolkit.
137
+ - Predecessor protocol research: `mbirth/tcl_ota_check`, `thurask/bbarchivist`.
138
+
139
+ ## License
140
+
141
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,48 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "tcl-fw"
7
+ version = "3.0.0"
8
+ description = "Pull and decrypt official TCL (MediaTek) FOTA firmware — flashable service packages, fully offline."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "MIT" }
12
+ authors = [
13
+ { name = "vehoelite" },
14
+ { name = "Littlenine Ennea" },
15
+ ]
16
+ keywords = ["tcl", "fota", "firmware", "mediatek", "android", "mbn", "decrypt", "scatter"]
17
+ classifiers = [
18
+ "Environment :: Console",
19
+ "Intended Audience :: Developers",
20
+ "Operating System :: OS Independent",
21
+ "Programming Language :: Python :: 3",
22
+ "Topic :: Utilities",
23
+ ]
24
+ dependencies = [
25
+ "typer>=0.12",
26
+ "rich>=13.0",
27
+ "pycryptodome>=3.20",
28
+ ]
29
+
30
+ [project.optional-dependencies]
31
+ gui = ["PySide6>=6.6"]
32
+ dev = ["pytest>=8.0", "pyinstaller>=6.0", "PySide6>=6.6"]
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/vehoelite/tcl-fota-tool"
36
+ Credit = "https://github.com/LittlenineEnnea"
37
+
38
+ [project.scripts]
39
+ tcl-fw = "tcl_fw.cli:app"
40
+
41
+ [project.gui-scripts]
42
+ tcl-fw-gui = "tcl_fw_gui:main"
43
+
44
+ [tool.setuptools]
45
+ packages = ["tcl_fw", "tcl_fw_gui"]
46
+
47
+ [tool.setuptools.package-data]
48
+ tcl_fw = ["data/*.json"]
tcl_fw-3.0.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,16 @@
1
+ """
2
+ tcl-fw — pull and decrypt official TCL (MediaTek) FOTA firmware.
3
+
4
+ A firmware puller + .mbn header decryptor for TCL-made Android devices
5
+ (TCL, REVVL, Alcatel). Talks to TCL's own FOTA download servers (no Google,
6
+ no account), streams the plaintext bodies, and AES-decrypts the small
7
+ partitions that ship inside an encrypted 4 MiB header — producing a clean,
8
+ flashable service package fully offline.
9
+
10
+ The header-decryption scheme (AES-128-ECB, universal key) was cracked by
11
+ Littlenine Ennea <https://github.com/LittlenineEnnea>; mode 4 exists because
12
+ of that work. See crypto.py.
13
+ """
14
+
15
+ __version__ = "3.0.0"
16
+ __all__ = ["__version__"]
@@ -0,0 +1,5 @@
1
+ """Enable `python -m tcl_fw`."""
2
+ from .cli import app
3
+
4
+ if __name__ == "__main__":
5
+ app()
@@ -0,0 +1,99 @@
1
+ """
2
+ adb.py — read a plugged-in TCL phone's identity so the user types nothing.
3
+
4
+ The whole point of "auto-CUREF": if a phone is connected with USB debugging on,
5
+ we can read its curef (and firmware version) directly, then let fota.discover()
6
+ turn that into a tv/fw_id. No manual lookup, no dongle.
7
+
8
+ Finds adb from (in order): the TCL_FW_ADB env var, a bundled platform-tools/
9
+ next to the package, or the system PATH.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import os
15
+ import shutil
16
+ import subprocess
17
+ from dataclasses import dataclass
18
+ from pathlib import Path
19
+ from typing import Optional
20
+
21
+
22
+ @dataclass
23
+ class Device:
24
+ serial: str
25
+ curef: Optional[str] = None
26
+ fv: Optional[str] = None # ro.build.version.incremental
27
+ model: Optional[str] = None
28
+ name: Optional[str] = None # marketing name, if any
29
+
30
+
31
+ def adb_path() -> Optional[str]:
32
+ """Locate an adb binary, or None if unavailable."""
33
+ env = os.environ.get("TCL_FW_ADB")
34
+ if env and Path(env).exists():
35
+ return env
36
+ bundled = Path(__file__).resolve().parent.parent / "platform-tools" / (
37
+ "adb.exe" if os.name == "nt" else "adb"
38
+ )
39
+ if bundled.exists():
40
+ return str(bundled)
41
+ return shutil.which("adb")
42
+
43
+
44
+ def _adb(args: list[str], serial: Optional[str] = None, timeout: int = 10) -> str:
45
+ exe = adb_path()
46
+ if not exe:
47
+ raise RuntimeError("adb not found (set TCL_FW_ADB, add adb to PATH, "
48
+ "or drop platform-tools/ next to tcl-fw)")
49
+ cmd = [exe]
50
+ if serial:
51
+ cmd += ["-s", serial]
52
+ cmd += args
53
+ out = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
54
+ if out.returncode != 0:
55
+ raise RuntimeError((out.stderr or out.stdout or "adb error").strip())
56
+ return out.stdout.strip()
57
+
58
+
59
+ def available() -> bool:
60
+ return adb_path() is not None
61
+
62
+
63
+ def list_serials() -> list[str]:
64
+ """Authorized, online device serials (skips 'unauthorized' / 'offline')."""
65
+ try:
66
+ out = _adb(["devices"])
67
+ except Exception:
68
+ return []
69
+ serials = []
70
+ for line in out.splitlines()[1:]:
71
+ parts = line.split()
72
+ if len(parts) >= 2 and parts[1] == "device":
73
+ serials.append(parts[0])
74
+ return serials
75
+
76
+
77
+ def _getprop(serial: str, prop: str) -> Optional[str]:
78
+ try:
79
+ v = _adb(["shell", "getprop", prop], serial=serial).strip()
80
+ return v or None
81
+ except Exception:
82
+ return None
83
+
84
+
85
+ def read_device(serial: str) -> Device:
86
+ """Read curef / firmware-version / model from one device."""
87
+ curef = _getprop(serial, "ro.tct.curef") or _getprop(serial, "ro.vendor.tct.curef")
88
+ fv = _getprop(serial, "ro.build.version.incremental")
89
+ model = _getprop(serial, "ro.product.model")
90
+ name = _getprop(serial, "ro.tct.setupwizard.marketname") or model
91
+ return Device(serial=serial, curef=curef, fv=fv, model=model, name=name)
92
+
93
+
94
+ def detect() -> Optional[Device]:
95
+ """Return the first connected device's identity, or None if nothing usable."""
96
+ serials = list_serials()
97
+ if not serials:
98
+ return None
99
+ return read_device(serials[0])