Skip to content

Commit

Permalink
Merge pull request #1 from FUSDownloaders/master
Browse files Browse the repository at this point in the history
Added support for IMEI/Serial Numbers
  • Loading branch information
MatrixEditor authored Dec 28, 2023
2 parents 1ac38b9 + fc6ed63 commit 6a56382
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 11 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Cross plattform Firmware downloader and decryptor for Samsung devices with maxim
A list of API examples are given in the documentation available at [Github-Pages](https://matrixeditor.github.io/samloader3).

> [!NOTE]
> This project was formerly hosted at `nlscc/samloader`, and has moved from `samloader/samloader` to an refactored and updated version with enhanced CLI support and an API documentation.
> This project was formerly hosted at `nlscc/samloader`, and has moved from `samloader/samloader` to a refactored and updated version with enhanced CLI support and an API documentation.
## Installation

Expand All @@ -20,7 +20,7 @@ The interface procided here is separated into two layers. In The first one, one
operate on a shell that takes commands with arguments as input.

```console
$ python3 -m samloader3 -M "SM-A336B" -R "SFR"
$ python3 -m samloader3 -M "SM-A336B" -R "SFR" -I "35117439020457"
(sl3)> # type commands here
```

Expand Down
43 changes: 36 additions & 7 deletions samloader3/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ def update():
path, out, dkey, self.argv.block_size, key_version, update
)

def run(self, specs: t.List[FirmwareSpec], model, region) -> None:
def run(self, specs: t.List[FirmwareSpec], model, region, imei) -> None:
"""
Runs the firmware download task.
Expand All @@ -262,10 +262,12 @@ def run(self, specs: t.List[FirmwareSpec], model, region) -> None:
:type model: str
:param region: The region.
:type region: str
:param imei: The serial/imei number.
:type imei: str
"""
data = []
for spec in specs:
data.append(self.client.fw_info(spec.normalized_version, model, region))
data.append(self.client.fw_info(spec.normalized_version, model, region, imei))

with self.progress:
pool = None
Expand Down Expand Up @@ -305,6 +307,8 @@ class SamLoader3CLI(cmd.Cmd):
:type model_name: Optional[str]
:param region: The default region.
:type region: Optional[str]
:param imei: The imei/serial number.
:type imei: Optional[str]
"""

prompt = "(sl3)> "
Expand All @@ -322,12 +326,14 @@ def __init__(
user_agent: t.Optional[str] = None,
model_name: t.Optional[str] = None,
region: t.Optional[str] = None,
imei: t.Optional[str] = None,
) -> None:
super().__init__()
self.parsers = external_parsers or {}
self.client = FUSClient(user_agent, timeout)
self.model = model_name
self.region = region
self.imei = imei
self.setup_parsers()

for name, parser in self.parsers.items():
Expand Down Expand Up @@ -356,13 +362,15 @@ def setup_parsers(self) -> None:
)
info_mod.add_argument("-m", "--model", required=False)
info_mod.add_argument("-r", "--region", required=False)
info_mod.add_argument("-i", "--imei", required=False)
info_mod.set_defaults(fn=self._list_info)
self.parsers["list"] = info_mod

download_mod = argparse.ArgumentParser("download")
download_mod.add_argument("versions", nargs="+")
download_mod.add_argument("-m", "--model", required=False)
download_mod.add_argument("-r", "--region", required=False)
download_mod.add_argument("-i", "--imei", required=False)
download_mod.add_argument("--chunk-size", type=int, default=32768)
download_mod.add_argument("-o", "--out", dest="dest", type=str, required=True)
download_mod.add_argument("--no-cache", action="store_true")
Expand All @@ -376,6 +384,7 @@ def setup_parsers(self) -> None:
decrypt_mod.add_argument("path")
decrypt_mod.add_argument("-m", "--model", required=False)
decrypt_mod.add_argument("-r", "--region", required=False)
decrypt_mod.add_argument("-i", "--imei", required=False)
decrypt_mod.add_argument("--key-only", action="store_true")
decrypt_mod.add_argument("-v", "--version", required=True)
decrypt_mod.add_argument("-o", "--out", default=".")
Expand Down Expand Up @@ -413,6 +422,10 @@ def do_setregion(self, args) -> None:
"""Sets the region code to use"""
self.region = args

def do_setimei(self, args) -> None:
"""Sets the imei/serial code to use"""
self.imei = args

def default(self, line) -> None:
_print_warn(f"[bold]Unknown syntax:[/] {line}")

Expand Down Expand Up @@ -447,6 +460,17 @@ def get_region(self, argv) -> t.Optional[str]:
"""
return argv.region or self.region

def get_imei(self, argv) -> t.Optional[str]:
"""
Gets the region from command line arguments.
:param argv: The command line arguments.
:type argv: argparse.Namespace
:return: The imei/serial number.
:rtype: Optional[str]
"""
return argv.imei or self.imei

def get_candidates(
self, specs, versions, include_all=False
) -> t.List[FirmwareSpec]:
Expand Down Expand Up @@ -505,6 +529,7 @@ def _list_info(self, argv) -> None:

model = self.get_model(argv)
region = self.get_region(argv)
imei = self.get_imei(argv)
if not self._verify_specs(model, region):
return

Expand All @@ -528,13 +553,13 @@ def _list_info(self, argv) -> None:

with Live(table, refresh_per_second=4):
info = self.client.fw_info(
specs.latest.normalized_version, model, region
specs.latest.normalized_version, model, region, imei
)
table.add_row(info.current_os_version, info.version, "[green]True[/]")
if not argv.latest:
for upgrade in specs.upgrade:
info = self.client.fw_info(
upgrade.normalized_version, model, region
upgrade.normalized_version, model, region, imei
)
table.add_row(
info.current_os_version, info.version, "[red]False[/]"
Expand All @@ -546,7 +571,7 @@ def _list_info(self, argv) -> None:
return

spec = candidates[0]
info = self.client.fw_info(spec.normalized_version, model, region)
info = self.client.fw_info(spec.normalized_version, model, region, imei)
tree = Tree(f"({model}): {info.version}")
for key, value in info.entries.items():
if value is not None:
Expand All @@ -559,6 +584,7 @@ def _download(self, argv) -> None:

model = self.get_model(argv)
region = self.get_region(argv)
imei = self.get_imei(argv)
versions = argv.versions

load_all = versions[0] == "*"
Expand All @@ -572,14 +598,15 @@ def _download(self, argv) -> None:

_print_info(f"Initialising Download of {len(candidates)} candidates...")
downloader = Download(self.client, argv)
downloader.run(candidates, model, region)
downloader.run(candidates, model, region, imei)

def _decrypt(self, argv) -> None:
if not self._verify_device_info(argv):
return

model = self.get_model(argv)
region = self.get_region(argv)
imei = self.get_imei(argv)

version = argv.version
path = str(argv.path)
Expand All @@ -588,7 +615,7 @@ def _decrypt(self, argv) -> None:
if not self._connect() or not self._verify_specs(model, region):
return

info = self.client.fw_info(version, model, region)
info = self.client.fw_info(version, model, region, imei)
key, dkey = v4_key(info)
key_version = "enc4"

Expand Down Expand Up @@ -616,6 +643,7 @@ def run_with_args(args: t.Optional[t.List[str]] = None) -> None:
parser.add_argument("-U", "--user-agent", default=FUS_USER_AGENT)
parser.add_argument("-R", "--region", required=False)
parser.add_argument("-M", "--model", required=False)
parser.add_argument("-I", "--imei", required=False)
parser.add_argument("-T", "--timeout", default=0x61A8, type=int)

argv = parser.parse_args(args)
Expand All @@ -624,6 +652,7 @@ def run_with_args(args: t.Optional[t.List[str]] = None) -> None:
user_agent=argv.user_agent,
model_name=argv.model,
region=argv.region,
imei=argv.imei,
)
while True:
try:
Expand Down
4 changes: 2 additions & 2 deletions samloader3/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
)
from cryptography.hazmat.primitives.padding import PKCS7

KEY_1 = b"hqzdurufm2c8mf6bsjezu1qgveouv7c7"
KEY_2 = b"w13r4cvf4hctaujv"
KEY_1 = b"vicopx7dqu06emacgpnpy8j8zwhduwlh"
KEY_2 = b"9u7qab84rpc16gvk"


def aes_decrypt(data: bytes, key: bytes) -> bytes:
Expand Down
5 changes: 5 additions & 0 deletions samloader3/fus.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ def fw_info(
version: str,
model_name: str,
local_code: str,
imei: str,
nature: BinaryNature = BinaryNature.FACTORY,
client_product: t.Optional[str] = None,
) -> FirmwareInfo:
Expand All @@ -309,6 +310,8 @@ def fw_info(
:type model_name: str
:param local_code: The local code.
:type local_code: str
:param imei: The imei/serial number.
:type imei: str
:param nature: The binary nature.
:type nature: BinaryNature, optional
:param client_product: The client product name (default is "Smart Switch").
Expand All @@ -321,6 +324,8 @@ def fw_info(
"ACCESS_MODE": 2,
"BINARY_NATURE": str(nature.value),
"CLIENT_PRODUCT": client_product or "Bogus",
"CLIENT_VERSION": "4.3.23123_1",
"DEVICE_IMEI_PUSH": imei,
"DEVICE_FW_VERSION": version,
"DEVICE_LOCAL_CODE": local_code,
"DEVICE_MODEL_NAME": model_name,
Expand Down

0 comments on commit 6a56382

Please sign in to comment.