Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions mtk.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 <path>.')

def add_gpt_group(parser):
g = parser.add_argument_group("GPT & Partition")
Expand Down
34 changes: 34 additions & 0 deletions mtkclient/Library/mtk_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
32 changes: 14 additions & 18 deletions mtkclient/Library/mtk_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 <path> 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:
Expand Down
Loading