diff --git a/README.md b/README.md index ed8380c..859a05b 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 ``` diff --git a/samloader3/cli.py b/samloader3/cli.py index 2673517..b916db8 100644 --- a/samloader3/cli.py +++ b/samloader3/cli.py @@ -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. @@ -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 @@ -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)> " @@ -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(): @@ -356,6 +362,7 @@ 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 @@ -363,6 +370,7 @@ def setup_parsers(self) -> None: 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") @@ -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=".") @@ -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}") @@ -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]: @@ -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 @@ -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[/]" @@ -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: @@ -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] == "*" @@ -572,7 +598,7 @@ 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): @@ -580,6 +606,7 @@ def _decrypt(self, argv) -> None: model = self.get_model(argv) region = self.get_region(argv) + imei = self.get_imei(argv) version = argv.version path = str(argv.path) @@ -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" @@ -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) @@ -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: diff --git a/samloader3/crypto.py b/samloader3/crypto.py index 1aacf69..56e7ad6 100644 --- a/samloader3/crypto.py +++ b/samloader3/crypto.py @@ -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: diff --git a/samloader3/fus.py b/samloader3/fus.py index 28275ed..4d13b9d 100644 --- a/samloader3/fus.py +++ b/samloader3/fus.py @@ -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: @@ -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"). @@ -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,