diff --git a/mtk.py b/mtk.py index ffa27af1..c0abe15a 100755 --- a/mtk.py +++ b/mtk.py @@ -92,6 +92,9 @@ def add_exploit_group(parser): g.add_argument('--skipwdt', action='store_true', help='Skip wdt init') g.add_argument('--crash', action='store_true', help='Enforce crash if device is in pl mode') g.add_argument('--appid', help='Use app id (hexstring)') + g.add_argument('--plstage', action='store_true', + help='Boot the device with --preloader first, then run the command. ' + 'Requires --preloader .') def add_gpt_group(parser): g = parser.add_argument_group("GPT & Partition") diff --git a/mtkclient/Library/mtk_class.py b/mtkclient/Library/mtk_class.py index 936e7e58..87035cf4 100644 --- a/mtkclient/Library/mtk_class.py +++ b/mtkclient/Library/mtk_class.py @@ -3,6 +3,7 @@ # Licensed under GPLv3 License import os import logging +import time from struct import unpack from mtkclient.config.usb_ids import default_ids from mtkclient.config.payloads import PathConfig @@ -143,6 +144,39 @@ def parse_preloader(self, preloader): dadata = data return daaddr, dadata + def boot_preloader(self, preloader_filename, timeout_s=2): + """ + Send a user-supplied preloader to the device and re-initialize on the + new preloader session. Returns a fresh Mtk connected to the preloader, + or None on failure. Reuses self.config to preserve config.loader. + """ + if not os.path.exists(preloader_filename): + self.error(f"Preloader file not found: {preloader_filename}") + return None + # init() is needed the first time to populate EP_IN/EP_OUT via the + # USB handshake; skip if already done to avoid a redundant reopen. + if not self.port.cdc.connected: + if not self.preloader.init(): + self.error("boot_preloader: preloader.init() failed (no device / handshake)") + return None + daaddr, dadata = self.parse_preloader(preloader_filename) + if not self.preloader.send_da(daaddr, len(dadata), 0x100, dadata): + self.error("Failed to send preloader via send_da") + return None + self.info(f"Sent preloader to {hex(daaddr)}, length {hex(len(dadata))}") + if not self.preloader.jump_da(daaddr): + self.error("Failed to jump to preloader") + return None + self.info(f"Jumped to pl {hex(daaddr)}.") + time.sleep(timeout_s) + new_mtk = Mtk(loglevel=self.__logger.level, config=self.config, + serialportname=self.port.serialportname) + if not new_mtk.preloader.init(): + self.error("Error on loading preloader after jump") + return None + self.info("Successfully connected to pl.") + return new_mtk + def setup(self, vid=None, pid=None, interface=None, serialportname: str = None): if vid is None: vid = self.vid diff --git a/mtkclient/Library/mtk_main.py b/mtkclient/Library/mtk_main.py index ab3cda28..e8fc16cb 100644 --- a/mtkclient/Library/mtk_main.py +++ b/mtkclient/Library/mtk_main.py @@ -299,24 +299,10 @@ def cmd_peek(self, mtk, addr, length, preloader, filename): if mtk is not None: if preloader is not None: if os.path.exists(preloader): - daaddr, dadata = mtk.parse_preloader(preloader) - if mtk.preloader.send_da(daaddr, len(dadata), 0x100, dadata): - self.info(f"Sent preloader to {hex(daaddr)}, length {hex(len(dadata))}") - if mtk.preloader.jump_da(daaddr): - self.info(f"Jumped to pl {hex(daaddr)}.") - time.sleep(2) - config = MtkConfig(loglevel=self.__logger.level, gui=mtk.config.gui, - guiprogress=mtk.config.guiprogress) - mtk = Mtk(loglevel=self.__logger.level, config=config, - serialportname=mtk.port.serialportname) - res = mtk.preloader.init() - if not res: - self.error("Error on loading preloader") - return - else: - self.info("Successfully connected to pl.") - # mtk.preloader.get_hw_sw_ver() - # status=mtk.preloader.jump_to_partition(b"") # Do not remove ! + mtk = mtk.boot_preloader(preloader) + if mtk is None: + self.error("Error on jumping to pl") + return else: self.error("Error on jumping to pl") return @@ -712,6 +698,16 @@ def run(self, parser): self.close() else: # DA / FLash commands start here + if getattr(self.args, "plstage", False) and cmd not in {"script", "multi"}: + if self.args.preloader is None or not os.path.exists(self.args.preloader): + self.error("--plstage requires --preloader to a valid preloader file") + self.close() + return + mtk = mtk.boot_preloader(self.args.preloader) + if mtk is None: + self.error("--plstage: failed to boot into custom preloader") + self.close() + return da_handler = DaHandler(mtk, loglevel) mtk.offset = 0 try: