diff --git a/MANIFEST.in b/MANIFEST.in index 49e5af1..e624abe 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,4 +4,5 @@ include mtkclient/Loader/*.bin include mtkclient/Loader/Preloader/*.bin include mtkclient/payloads/*.bin include mtkclient/Windows/*.dll +include mtkclient/gui/images/*.png diff --git a/README.md b/README.md index f98a58d..7a83465 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# mtkclient +# MTKClient +![Logo](gui/images/logo_256.png) + Just some mtk tool for exploitation, reading/writing flash and doing crazy stuff. For windows, you need to install the stock mtk port and the usbdk driver (see instructions below). For linux, a patched kernel is only needed when using old kamakiri (see Setup folder) (except for read/write flash). @@ -11,7 +13,7 @@ release the buttons. - kamakiri [xyzz] - linecode exploit [chimera] - Chaosmaster -- cygnusx (GUI) +- Geert-Jan Kreileman (GUI, design & fixes) - All contributors ## Installation @@ -23,7 +25,7 @@ User: user, Password:user (based on Ubuntu 22.04 LTS) ## Install -### Linux - (Ubuntu recommended, no patched kernel needed except for kamakiri) +### Linux / Mac OS - (Ubuntu recommended, no patched kernel needed except for kamakiri) #### Install python >=3.8, git and other deps @@ -115,6 +117,12 @@ sudo reboot ## Usage +### Using MTKTools via the graphical user interface: +For the 'basics' you can use the GUI interface. This supports dumping partitions for now. Run the following command: +``` +python mtk_gui.py +``` + ### Root the phone (Tested with android 9 - 12) 1. Dump boot and vbmeta diff --git a/mtk b/mtk index 8c90cdc..cf0be16 100755 --- a/mtk +++ b/mtk @@ -1,696 +1,10 @@ #!/usr/bin/env python3 # MTK Flash Client (c) B.Kerler 2018-2021. # Licensed under GPLv3 License -import shutil -import os -import json -import sys -import logging -import time import argparse -from binascii import hexlify -from struct import unpack, pack -from mtkclient.config.usb_ids import default_ids -from mtkclient.config.payloads import pathconfig -from mtkclient.Library.pltools import PLTools -from mtkclient.Library.mtk_preloader import Preloader -from mtkclient.Library.meta import META -from mtkclient.Library.mtk_daloader import DAloader -from mtkclient.Library.Port import Port -from mtkclient.Library.utils import LogBase, logsetup, getint -from mtkclient.config.brom_config import Mtk_Config -from mtkclient.Library.utils import print_progress -from mtkclient.Library.error import ErrorHandler -from mtkclient.Library.mtk_da_cmd import DA_handler -from mtkclient.Library.gpt import gpt_settings +from mtkclient.Library.mtk_main import Main, metamodes -metamodes = "[FASTBOOT, FACTFACT, METAMETA, FACTORYM, ADVEMETA]" - - -def split_by_n(seq, unit_count): - """A generator to divide a sequence into chunks of n units.""" - while seq: - yield seq[:unit_count] - seq = seq[unit_count:] - - -class ArgHandler(metaclass=LogBase): - def __init__(self, args, config): - try: - if args.vid is not None: - config.vid = getint(args.vid) - except AttributeError: - pass - try: - if args.pid is not None: - config.pid = getint(args.pid) - except AttributeError: - pass - try: - if args.payload is not None: - config.payloadfile = args.payload - except: - pass - try: - if args.loader is not None: - config.loader = args.loader - except AttributeError: - pass - try: - if args.da_address is not None: - config.chipconfig.da_payload_addr = getint(args.da_address) - self.info("O:DA offset:\t\t\t" + args.da_address) - except AttributeError: - pass - try: - if args.brom_address is not None: - config.chipconfig.brom_payload_addr = getint(args.brom_address) - self.info("O:Payload offset:\t\t" + args.brom_address) - except AttributeError: - pass - try: - if args.watchdog_address is not None: - config.chipconfig.watchdog = getint(args.wdt) - self.info("O:Watchdog addr:\t\t" + args.wdt) - except AttributeError: - pass - try: - if args.skipwdt is not None: - config.skipwdt = args.skipwdt - except AttributeError: - pass - try: - if args.uart_address is not None: - config.chipconfig.uart = getint(args.uart_address) - self.info("O:Uart addr:\t\t" + args.uart_address) - except AttributeError: - pass - try: - if args.preloader is not None: - config.chipconfig.var1 = getint(args.var1) - self.info("O:Var1:\t\t" + args.var1) - except AttributeError: - pass - try: - if args.preloader is not None: - if os.path.exists(args.preloader): - config.preloader_filename = args.preloader - config.preloader = open(config.preloader_filename,"rb").read() - except AttributeError: - pass - try: - if args.generatekeys is not None: - config.generatekeys = args.generatekeys - except AttributeError: - pass - try: - if args.ptype is not None: - config.ptype = args.ptype - except AttributeError: - pass - try: - if args.socid is not None: - config.readsocid = args.socid - except AttributeError: - pass - try: - if args.crash is not None: - config.enforcecrash = args.crash - except AttributeError: - pass - - gpt_num_part_entries = 0 - try: - if args.gpt_num_part_entries is not None: - gpt_num_part_entries = args.gpt_num_part_entries - except: - pass - - gpt_part_entry_size = 0 - try: - if args.gpt_part_entry_size is not None: - gpt_part_entry_size = args.gpt_part_entry_size - except: - pass - - gpt_part_entry_start_lba = 0 - try: - if args.gpt_part_entry_start_lba is not None: - gpt_part_entry_start_lba = args.gpt_part_entry_start_lba - except: - pass - - config.gpt_settings = gpt_settings(gpt_num_part_entries,gpt_part_entry_size, - gpt_part_entry_start_lba) - - -class Mtk(metaclass=LogBase): - def __init__(self, config, loglevel=logging.INFO, preinit=True): - self.config = config - self.loader = config.loader - self.vid = config.vid - self.pid = config.pid - self.interface = config.interface - self.pathconfig = pathconfig() - self.__logger = logsetup(self, self.__logger, loglevel) - self.eh = ErrorHandler() - if preinit: - self.init() - - def patch_preloader_security(self, data): - patched = False - data = bytearray(data) - patches = [ - ("A3687BB12846", "0123A3602846"), # oppo security - ("B3F5807F01D1", "B3F5807F01D14FF000004FF000007047"), # confirmed : mt6739 c30, mt6833 - ("B3F5807F04BF4FF4807305F011B84FF0FF307047", "B3F5807F04BF4FF480734FF000004FF000007047"), - ] - - i = 0 - for patchval in patches: - pattern = bytes.fromhex(patchval[0]) - idx = data.find(pattern) - if idx != -1: - patch = bytes.fromhex(patchval[1]) - data[idx:idx + len(patch)] = patch - patched = True - # break - i += 1 - if patched: - # with open(sys.argv[1]+".patched","wb") as wf: - # wf.write(data) - # print("Patched !") - self.info(f"Patched preloader security: {hex(i)}") - else: - self.warning(f"Failed to patch preloader security") - return data - - def parse_preloader(self, preloader): - if isinstance(preloader, str): - if os.path.exists(preloader): - with open(preloader, "rb") as rf: - data = rf.read() - else: - data = preloader - data = bytearray(data) - magic = unpack("I", 0xf00dd00d)) - # cmd write - mtk.port.usbwrite(pack(">I", 0x4000)) - # address - mtk.port.usbwrite(pack(">I", stage2addr)) - # length - mtk.port.usbwrite(pack(">I", len(stage2data))) - bytestowrite = len(stage2data) - pos = 0 - while bytestowrite > 0: - size = min(bytestowrite, 1) - if mtk.port.usbwrite(stage2data[pos:pos + size]): - bytestowrite -= size - pos += size - # mtk.port.usbwrite(b"") - time.sleep(0.1) - flag = mtk.port.rdword() - if flag != 0xD0D0D0D0: - self.error(f"Error on sending stage2, size {hex(len(stage2data))}.") - self.info(f"Done sending stage2, size {hex(len(stage2data))}.") - - if verifystage2: - self.info("Verifying stage2 data") - rdata = b"" - mtk.port.usbwrite(pack(">I", 0xf00dd00d)) - mtk.port.usbwrite(pack(">I", 0x4002)) - mtk.port.usbwrite(pack(">I", stage2addr)) - mtk.port.usbwrite(pack(">I", len(stage2data))) - bytestoread = len(stage2data) - while bytestoread > 0: - size = min(bytestoread, 1) - rdata += mtk.port.usbread(size) - bytestoread -= size - flag = mtk.port.rdword() - if flag != 0xD0D0D0D0: - self.error("Error on reading stage2 data") - if rdata != stage2data: - self.error("Stage2 data doesn't match") - with open("rdata", "wb") as wf: - wf.write(rdata) - else: - self.info("Stage2 verification passed.") - - # ####### Kick Watchdog - # magic - # mtk.port.usbwrite(pack("I", 0xf00dd00d)) - # cmd jump - mtk.port.usbwrite(pack(">I", 0x4001)) - # address - mtk.port.usbwrite(pack(">I", stage2addr)) - self.info("Done jumping stage2 at %08X" % stage2addr) - ack = unpack(">I", mtk.port.usbread(4))[0] - if ack == 0xB1B2B3B4: - self.info("Successfully loaded stage2") - - def cmd_peek(self, mtk, addr, length, preloader, filename): - if preloader is not None: - if os.path.exists(preloader): - daaddr, dadata = mtk.parse_preloader(preloader) - if mtk.preloader.init(): - if mtk.config.target_config["daa"]: - mtk = mtk.bypass_security() - 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 = Mtk_Config(loglevel=self.__logger.level) - mtk = Mtk(loglevel=self.__logger.level, config=config) - 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 ! - else: - self.error("Error on jumping to pl") - return - self.info("Starting to read ...") - dwords = length // 4 - if length % 4: - dwords += 1 - if filename != None: - wf = open(filename, "wb") - sdata = b"" - print_progress(0, 100, prefix='Progress:', - suffix='Starting, addr 0x%08X' % addr, bar_length=50) - length = dwords * 4 - old = 0 - pos = 0 - while dwords: - size = min(512 // 4, dwords) - data = b"".join(int.to_bytes(val, 4, 'little') for val in mtk.preloader.read32(addr + pos, size)) - sdata += data - if filename != "": - wf.write(data) - pos += len(data) - prog = pos / length * 100 - if round(prog, 1) > old: - print_progress(prog, 100, prefix='Progress:', - suffix='Complete, addr 0x%08X' % (addr + pos), bar_length=50) - old = round(prog, 1) - dwords = (length - pos) // 4 - print_progress(100, 100, prefix='Progress:', - suffix='Finished', bar_length=50) - if filename == "": - print(hexlify(sdata).decode('utf-8')) - else: - wf.close() - self.info(f"Data from {hex(addr)} with size of {hex(length)} was written to " + filename) - - def run(self): - try: - if self.args.debugmode: - loglevel = logging.DEBUG - self.__logger.setLevel(logging.DEBUG) - else: - loglevel = logging.INFO - self.__logger.setLevel(logging.INFO) - except: - loglevel = logging.INFO - self.__logger.setLevel(logging.INFO) - pass - config = Mtk_Config(loglevel=loglevel) - ArgHandler(self.args, config) - self.eh = ErrorHandler() - mtk = Mtk(config=config, loglevel=loglevel) - if mtk.config.debugmode: - logfilename = os.path.join("logs", "log.txt") - if os.path.exists(logfilename): - os.remove(logfilename) - fh = logging.FileHandler(logfilename, encoding='utf-8') - self.__logger.addHandler(fh) - - self.debug(" ".join(sys.argv)) - - cmd = self.args.cmd - if cmd == "dumpbrom": - if mtk.preloader.init(): - rmtk = mtk.crasher() - if rmtk is None: - sys.exit(0) - if rmtk.port.cdc.vid != 0xE8D and rmtk.port.cdc.pid != 0x0003: - self.warning("We couldn't enter preloader.") - filename = self.args.filename - if filename is None: - cpu = "" - if rmtk.config.cpu != "": - cpu = "_" + rmtk.config.cpu - filename = "brom" + cpu + "_" + hex(rmtk.config.hwcode)[2:] + ".bin" - plt = PLTools(rmtk, self.__logger.level) - plt.run_dump_brom(filename, self.args.ptype) - rmtk.port.close() - self.close() - elif cmd == "dumppreloader": - if mtk.preloader.init(): - rmtk = mtk.crasher() - if rmtk is None: - sys.exit(0) - if rmtk.port.cdc.vid != 0xE8D and rmtk.port.cdc.pid != 0x0003: - self.warning("We couldn't enter preloader.") - plt = PLTools(rmtk, self.__logger.level) - data, filename = plt.run_dump_preloader(self.args.ptype) - if data is not None: - if filename == "": - if self.args.filename is not None: - filename = self.args.filename - else: - filename = "preloader.bin" - with open(filename, 'wb') as wf: - print_progress(0, 100, prefix='Progress:', suffix='Complete', bar_length=50) - wf.write(data) - print_progress(100, 100, prefix='Progress:', suffix='Complete', bar_length=50) - self.info("Preloader dumped as: " + filename) - rmtk.port.close() - self.close() - elif cmd == "brute": - self.info("Kamakiri / DA Bruteforce run") - rmtk = Mtk(config=mtk.config, loglevel=self.__logger.level) - plt = PLTools(rmtk, self.__logger.level) - plt.runbrute(self.args) - self.close() - elif cmd == "crash": - if mtk.preloader.init(): - mtk = mtk.crasher(mode=getint(self.args.mode)) - mtk.port.close() - self.close() - elif cmd == "plstage": - if mtk.config.chipconfig.pl_payload_addr is not None: - plstageaddr = mtk.config.chipconfig.pl_payload_addr - else: - plstageaddr = 0x40200000 # 0x40001000 - if self.args.pl is None: - plstage = os.path.join(mtk.pathconfig.get_payloads_path(), "pl.bin") - else: - plstage = self.args.pl - if os.path.exists(plstage): - with open(plstage, "rb") as rf: - rf.seek(0) - pldata = rf.read() - if mtk.preloader.init(): - if mtk.config.target_config["daa"]: - mtk = mtk.bypass_security() - if mtk is None: - self.error("Error on bypassing security, aborting") - return - self.info("Connected to device, loading") - else: - self.error("Couldn't connect to device, aborting.") - if mtk.config.is_brom and mtk.config.preloader is None: - self.warning("PL stage needs preloader, please use --preloader option. " + - "Trying to dump preloader from ram.") - plt = PLTools(mtk=mtk, loglevel=self.__logger.level) - dadata, filename = plt.run_dump_preloader(self.args.ptype) - mtk.config.preloader = mtk.patch_preloader_security(dadata) - if mtk.config.preloader_filename is not None: - self.info("Using custom preloader : " + mtk.config.preloader_filename) - daaddr, dadata = mtk.parse_preloader(mtk.config.preloader) - mtk.config.preloader = mtk.patch_preloader_security(dadata) - 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"PL Jumped to daaddr {hex(daaddr)}.") - time.sleep(2) - """ - mtk = Mtk(config=mtk.config, loglevel=self.__logger.level) - res = mtk.preloader.init() - if not res: - self.error("Error on loading preloader") - return - else: - self.info("Successfully connected to pl") - - if self.args.startpartition is not None: - partition = self.args.startpartition - self.info("Booting to : " + partition) - # if data[0:4]!=b"\x88\x16\x88\x58": - # data=0x200*b"\x00"+data - mtk.preloader.send_partition_data(partition, pldata) - status = mtk.preloader.jump_to_partition(partition) # Do not remove ! - res = mtk.preloader.read32(0x10C180, 10) - for val in res: - print(hex(val)) - if status != 0x0: - self.error("Error on jumping to partition: " + self.eh.status(status)) - else: - self.info("Jumping to partition ....") - return - """ - sys.exit(0) - if mtk.preloader.send_da(plstageaddr, len(pldata), 0x100, pldata): - self.info(f"Sent stage2 to {hex(plstageaddr)}, length {hex(len(pldata))}") - mtk.preloader.get_hw_sw_ver() - if mtk.preloader.jump_da(plstageaddr): - self.info(f"Jumped to stage2 at {hex(plstageaddr)}.") - ack = unpack(">I", mtk.port.usbread(4))[0] - if ack == 0xB1B2B3B4: - self.info("Successfully loaded stage2") - return - else: - self.error("Error on jumping to pl") - return - else: - self.error("Error on sending pl") - return - self.close() - elif cmd == "peek": - addr = getint(self.args.address) - length = getint(self.args.length) - preloader = self.args.preloader - filename = self.args.filename - self.cmd_peek(mtk=mtk, addr=addr, length=length, preloader=preloader, filename=filename) - self.close() - elif cmd == "stage": - filename = self.args.filename - stage2addr = self.args.stage2addr - if self.args.stage2addr is not None: - stage2addr = getint(self.args.stage2addr) - stage2file = self.args.stage2 - verifystage2 = self.args.verifystage2 - - self.cmd_stage(mtk=mtk, filename=filename, stage2addr=stage2addr, stage2file=stage2file, - verifystage2=verifystage2) - self.close() - elif cmd == "payload": - payloadfile = self.args.payload - self.cmd_payload(mtk=mtk, payloadfile=payloadfile) - self.close() - elif cmd == "gettargetconfig": - if mtk.preloader.init(): - self.info("Getting target info...") - mtk.preloader.get_target_config() - mtk.port.close() - self.close() - elif cmd == "logs": - if args.filename is None: - filename = "log.txt" - else: - filename = args.filename - self.cmd_log(mtk=mtk, filename=filename) - mtk.port.close() - self.close() - elif cmd == "meta": - meta = META(mtk, loglevel) - if args.metamode is None: - self.error("You need to give a metamode as argument ex: " + metamodes) - else: - if meta.init(metamode=args.metamode, display=True): - self.info(f"Successfully set meta mode : {args.metamode}") - mtk.port.close() - self.close() - else: - # DA / FLash commands start here - try: - preloader = args.preloader - except: - preloader = None - da_handler = DA_handler(mtk, loglevel) - mtk = da_handler.configure_da(mtk, preloader) - da_handler.handle_da_cmds(mtk, cmd, args) - - - def cmd_log(self, mtk, filename): - if mtk.preloader.init(): - self.info("Getting target logs...") - try: - logs = mtk.preloader.get_brom_log_new() - except: - logs = mtk.preloader.get_brom_log() - if logs != b"": - with open(filename, "wb") as wf: - wf.write(logs) - self.info(f"Successfully wrote logs to \"{filename}\"") - else: - self.info("No logs found.") - - def cmd_payload(self, mtk, payloadfile): - if mtk.preloader.init(): - mtk = mtk.crasher() - plt = PLTools(mtk, self.__logger.level) - if payloadfile is None: - if mtk.config.chipconfig.loader is None: - payloadfile = os.path.join(mtk.pathconfig.get_payloads_path(), "generic_patcher_payload.bin") - else: - payloadfile = os.path.join(mtk.pathconfig.get_payloads_path(), mtk.config.chipconfig.loader) - plt.runpayload(filename=payloadfile) - if args.metamode: - mtk.port.run_handshake() - mtk.preloader.jump_bl() - mtk.port.close(reset=True) - meta = META(mtk, self.__logger.level) - if meta.init(metamode=args.metamode, display=True): - self.info(f"Successfully set meta mode : {args.metamode}") - mtk.port.close(reset=True) - - -info = "MTK Flash/Exploit Client V1.53 (c) B.Kerler 2018-2021" +info = "MTK Flash/Exploit Client V1.54 (c) B.Kerler 2018-2021" cmds = { "printgpt": "Print GPT Table information", diff --git a/mtk_gui b/mtk_gui new file mode 100755 index 0000000..b6833ad --- /dev/null +++ b/mtk_gui @@ -0,0 +1,333 @@ +#!/usr/bin/env python3 +# MTK Flash Client (c) G.Kreileman, B.Kerler 2021. +# Licensed under GPLv3 License +import sys +import time +import mock +import traceback +import math +import logging +import ctypes +from PySide2.QtCore import Qt, QVariantAnimation +from PySide2.QtGui import QTextOption, QPixmap, QTransform, QIcon +from PySide2.QtWidgets import * + +from mtkclient.Library.mtk import Mtk +from mtkclient.Library.gpt import gpt_settings +from mtkclient.Library.mtk_main import Main, Mtk_Config +from mtkclient.Library.mtk_da_cmd import DA_handler +from mtkclient.gui.readFlashPartitions import * +from mtkclient.gui.toolsMenu import * +from mtkclient.gui.toolkit import * +from mtkclient.config.payloads import pathconfig +# TO do Move all GUI modifications to signals! + +class guiLogger: + global guiState + + def info(text): + sendToLog(text) + # grab useful stuff from this log + # if ("\tCPU:" in text): + # phoneInfo['chipset'] = text.replace("\t","").replace("CPU:","").replace("()","") + # elif ("BROM mode detected" in text): + # phoneInfo['bootMode'] = "In Bootrom" + # if (guiState == "welcome") and (phoneInfo['chipset'] is not ""): + # phoneInfoTextbox.setText("Phone detected:\n" + phoneInfo['chipset']+"\n"+phoneInfo['bootMode']) + + def debug(text): + sendToLog(text) + + def error(text): + sendToLog(text) + + def setLevel(logLevel): + return True + + +# install exception hook: without this, uncaught exception would cause application to exit +sys.excepthook = trap_exc_during_debug + +# Initiate MTK classes +variables = mock.Mock() +variables.cmd = "stage" +variables.debugmode = True +path=pathconfig() +config = Mtk_Config(loglevel=logging.INFO, gui=None) +config.gpt_settings = gpt_settings(gpt_num_part_entries='0', gpt_part_entry_size='0', + gpt_part_entry_start_lba='0') # This actually sets the right GPT settings.. +# if sys.platform.startswith('darwin'): +# config.ptype = "kamakiri" #Temp for Mac testing +MtkTool = Main(variables) +mtkClass = Mtk(config=config, loglevel=logging.INFO) +loglevel = logging.INFO +da_handler = DA_handler(mtkClass, loglevel) + +guiState = "welcome" +phoneInfo = {"chipset": "", "bootMode": "", "daInit": False, "cdcInit": False} + + +def getDevInfo(self, parameters): + print(parameters) + mtkClass = parameters[0] + da_handler = parameters[1] + phoneInfo = parameters[2] + self.sendToLogSignal.emit("CONNECTING!") + mtkClass.config.gui = self.sendToLogSignal + mtkClass = da_handler.configure_da(mtkClass, preloader=None) + if mtkClass: + phoneInfo['daInit'] = True + phoneInfo['chipset'] = str(mtkClass.config.chipconfig.name) + \ + " (" + str(mtkClass.config.chipconfig.description) + ")" + if mtkClass.config.is_brom: + phoneInfo['bootMode'] = "Bootrom mode" + elif mtkClass.config.chipconfig.damode: + phoneInfo['bootMode'] = "DA mode" + else: + phoneInfo['bootMode'] = "Preloader mode" + self.sendUpdateSignal.emit() + # try: + # print(mtkClass.daloader.get_partition_data(parttype="user")) + # except Exception: + # print(traceback.format_exc()) + # MtkTool.cmd_stage(mtkClass, None, None, None, False) + + +def sendToLog(info): + t = time.localtime() + logBox.appendPlainText(time.strftime("[%H:%M:%S", t) + "]: " + info) + logBox.verticalScrollBar().setValue(logBox.verticalScrollBar().maximum()) + + +def updateGui(): + global phoneInfo + phoneInfoTextbox.setText("Phone detected:\n" + phoneInfo['chipset'] + "\n" + phoneInfo['bootMode']) + status.setText("Device detected, please wait.\nThis can take a while...") + if phoneInfo['daInit']: + status.setText("Device connected :)") + menuBar.setEnabled(True) + pixmap = QPixmap(path.get_images_path("phone_connected.png")).scaled(128, 128, + Qt.KeepAspectRatio, Qt.SmoothTransformation) + pixmap.setDevicePixelRatio(2.0) + pic.setPixmap(pixmap) + spinnerAnim.stop() + spinner_pic.setHidden(True) + else: + if not phoneInfo['cdcInit']: + status.setText("Error initialising. Did you install the drivers?") + spinnerAnim.start() + spinner_pic.setHidden(False) + + +def openReadflashWindow(q): + # status.setText("OH YEAH"+str(q.text())) + readFlashWindowVal = ReadFlashWindow(w, mtkClass, da_handler, sendToLog) + # w.close() + + +def openToolsMenu(q): + # status.setText("OH YEAH"+str(q.text())) + if q.text() == "Generate RPMB keys": + readFlashWindowVal = generateKeysMenu(w, mtkClass, da_handler, sendToLog) + # w.close() + + +if __name__ == '__main__': + # Init the app window + app = QApplication(sys.argv) + win = QMainWindow() + icon = QIcon() + icon.addFile(path.get_images_path('logo_32.png'), QSize(32, 32)) + icon.addFile(path.get_images_path('logo_64.png'), QSize(64, 64)) + icon.addFile(path.get_images_path('logo_256.png'), QSize(256, 256)) + icon.addFile(path.get_images_path('logo_512.png'), QSize(512, 512)) + app.setWindowIcon(icon) + win.setWindowIcon(icon) + if sys.platform.startswith('win'): + ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('MTKTools.Gui') + dpiMultiplier = win.logicalDpiX() + if dpiMultiplier == 72: + dpiMultiplier = 2 + else: + dpiMultiplier = 1 + addTopMargin = 20 + if sys.platform.startswith('darwin'): # MacOS has the toolbar in the top bar insted of in the app... + addTopMargin = 0 + win.setFixedSize(600, 400 + addTopMargin) + w = QWidget(win) + w.move(0,addTopMargin) + + w.setFixedSize(600, 400) + win.setWindowTitle("MTKClient - Version 2.0 beta") + # lay = QVBoxLayout(self) + + # Menubar + menuBar = QMenuBar() + menuBar.setEnabled(False) + win.setMenuBar(menuBar) + readFlashMenu = menuBar.addMenu("&Read Flash") + readFlashMenu.addAction("Read partition(s)") + # readPartitions = QAction("Read partition(s)", w) + # readFlashMenu.addAction(readPartitions) + readFlashMenu.triggered[QAction].connect(openReadflashWindow) + readFlashMenu.addAction("Read full flash") + readFlashMenu.addAction("Read offset") + + writeFlashMenu = menuBar.addMenu("&Write Flash") + writeFlashMenu.addAction("Write partition(s)") + writeFlashMenu.addAction("Write full flash") + writeFlashMenu.addAction("Write offset") + + eraseFlashMenu = menuBar.addMenu("&Erase Flash") + eraseFlashMenu.addAction("Erase partition(s)") + eraseFlashMenu.addAction("Erase bootsectors") + + toolsFlashMenu = menuBar.addMenu("&Tools") + toolsFlashMenu.addAction("Read RPMB") + toolsFlashMenu.triggered[QAction].connect(openToolsMenu) + toolsFlashMenu.addAction("Generate RPMB keys") + menuBar.show() + + # titles + title = QLabel(w) + title.setText("MTKClient v2.0") + title.setGeometry(10, 0, 480, 40) + title.setStyleSheet("font-size: 17px") + title.show() + + # status info + status = QLabel(w) + status.setAlignment(Qt.AlignLeft | Qt.AlignTop) + status.setText("Please connect a Mediatek phone to continue.\n\nHint: Power off the phone before connecting.\n" + \ + "For brom mode, press and hold vol up, vol dwn, or all hw buttons " + \ + "and connect usb.\n" + + "For preloader mode, don't press any hw button and connect usb.") + status.setGeometry(10, 30, 465, 256) + status.setWordWrap(True) + status.setStyleSheet("font-size: 12px vertical-align: top") + status.show() + + # Device info + # phone icon + pic = QLabel(w) + pixmap = QPixmap(path.get_images_path('phone_notfound.png')).scaled(128, 128, Qt.AspectRatioMode.KeepAspectRatio, + Qt.TransformationMode.SmoothTransformation) + pixmap.setDevicePixelRatio(2.0) + pic.setPixmap(pixmap) + pic.resize(pixmap.width() // 2, pixmap.height() // 2) + pic.move(545, 10) + pic.show() + + # phone spinner + spinner_pic = QLabel(w) + pixmap = QPixmap(path.get_images_path("phone_loading.png")).scaled(64, 64, Qt.AspectRatioMode.KeepAspectRatio, + Qt.TransformationMode.SmoothTransformation) + pixmap.setDevicePixelRatio(2.0) + # trans = QTransform() + # trans.rotate(90) + spinner_pic.setPixmap(pixmap) + spinner_pic.resize(pixmap.width() // 2, pixmap.height() // 2) + spinner_pic.move(551, 25) + spinner_pic.show() + + + def spinnerAnimRot(angle): + # print(angle) + trans = QTransform() + # trans.translate(pixmap.width()+angle / 2, pixmap.height() / 2) + + # trans.translate(spinner_pic.width()/2.0 , spinner_pic.height()/2.0) + trans.rotate(angle) + newPixmap = pixmap.transformed(QTransform().rotate(angle)) + xoffset = (newPixmap.width() - pixmap.width()) // 2 + yoffset = (newPixmap.height() - pixmap.height()) // 2 + rotated = newPixmap.copy(ax=xoffset, ay=yoffset, awidth=pixmap.width(), aheight=pixmap.height()) + spinner_pic.setPixmap(rotated) + + + spinnerAnim = QVariantAnimation() + spinnerAnim.setDuration(3000) + spinnerAnim.setStartValue(0) + spinnerAnim.setEndValue(360) + spinnerAnim.setLoopCount(-1) + spinnerAnim.valueChanged.connect(spinnerAnimRot) + spinner_pic.setHidden(True) + + # phone info + phoneInfoTextbox = QLabel(w) + phoneInfoTextbox.setText("No phone found") + phoneInfoTextbox.setGeometry(10, 10, 520, 100) + phoneInfoTextbox.setAlignment(Qt.AlignRight | Qt.AlignTop) + phoneInfoTextbox.setStyleSheet("font-size: 12px") + phoneInfoTextbox.show() + + # Line + line = QFrame(w) + line.setGeometry(10, 108, 580, 20) + line.setFrameShape(QFrame.HLine) + line.setFrameShadow(QFrame.Sunken) + + # logo + logoPic = QLabel(w) + logoPixmap = QPixmap(path.get_images_path("logo_512.png")).scaled(int(128 * dpiMultiplier), int(128 * dpiMultiplier), + Qt.AspectRatioMode.KeepAspectRatio, + Qt.TransformationMode.SmoothTransformation) + logoPixmap.setDevicePixelRatio(dpiMultiplier) + logoPic.setPixmap(logoPixmap) + logoPic.resize(logoPixmap.width() // dpiMultiplier, logoPixmap.height() // dpiMultiplier) + logoPic.move(10, 130) + logoPic.show() + + # Copyright info + copyrightInfo = QLabel(w) + copyrightInfo.setAlignment(Qt.AlignLeft | Qt.AlignTop) + copyrightInfo.setText("Made by: Bjoern Kerler\n" + \ + "Gui by: Geert-Jan Kreileman\n\n" + \ + "Credits: \n" + \ + "kamakiri [xyzz]\n" + + "linecode exploit [chimera]\n" + + "Chaosmaster\n" + + "and all contributers") + copyrightInfo.setGeometry(150,135,405,256) + copyrightInfo.setWordWrap(True) + copyrightInfo.setStyleSheet("font-size: 12px color: #333 vertical-align: top") + copyrightInfo.show() + + + def showDebugInfo(): + logBox.show() + if w.frameGeometry().height() < 500: + win.setFixedSize(600, 700 + addTopMargin) + w.setFixedSize(600, 700) + debugBtn.setText("Hide debug info") + else: + w.setFixedSize(600, 400) + win.setFixedSize(600, 400 + addTopMargin) + debugBtn.setText("Show debug info") + + + # debug + debugBtn = QPushButton(w) + debugBtn.setText("Show debug info") + debugBtn.clicked.connect(showDebugInfo) + debugBtn.setGeometry((600 - 150 - 10), (400 - 30 - 10), 150, 30) + debugBtn.show() + + logBox = QPlainTextEdit(w) + logBox.setGeometry(11, (700 - 280 - 10), 578, 280) + logBox.setWordWrapMode(QTextOption.NoWrap) + logBox.setReadOnly(True) + + win.show() + + # Get the device info + thread = asyncThread(app, 0, getDevInfo, [mtkClass, da_handler, phoneInfo]) + thread.sendToLogSignal.connect(sendToLog) + thread.sendUpdateSignal.connect(updateGui) + thread.start() + + # Run loop the app + app.exec_() + # Prevent thread from not being closed and call error end codes + thread.terminate() + thread.wait() diff --git a/mtkclient/Library/Port.py b/mtkclient/Library/Port.py index 00fd4cb..aa884f4 100755 --- a/mtkclient/Library/Port.py +++ b/mtkclient/Library/Port.py @@ -21,7 +21,10 @@ class Port(metaclass=LogBase): self.pid = pid def __init__(self, mtk, portconfig, loglevel=logging.INFO): - self.__logger = logsetup(self, self.__logger, loglevel) + self.__logger = logsetup(self, self.__logger, loglevel, mtk.config.gui) + self.info = self.__logger.info + self.debug = self.__logger.debug + self.error = self.__logger.error self.config = mtk.config self.mtk = mtk self.cdc = usb_class(portconfig=portconfig, loglevel=loglevel, devclass=10) diff --git a/mtkclient/Library/daconfig.py b/mtkclient/Library/daconfig.py index d9cdbdb..72c6655 100755 --- a/mtkclient/Library/daconfig.py +++ b/mtkclient/Library/daconfig.py @@ -112,7 +112,7 @@ class DA: class DAconfig(metaclass=LogBase): def __init__(self, mtk, loader=None, preloader=None, loglevel=logging.INFO): - self.__logger = logsetup(self, self.__logger, loglevel) + self.__logger = logsetup(self, self.__logger, loglevel, mtk.config.gui) self.mtk = mtk self.pathconfig = pathconfig() self.config = self.mtk.config diff --git a/mtkclient/Library/hwcrypto.py b/mtkclient/Library/hwcrypto.py index 8250602..3e40681 100755 --- a/mtkclient/Library/hwcrypto.py +++ b/mtkclient/Library/hwcrypto.py @@ -27,11 +27,11 @@ class crypto_setup: prov_addr = None class hwcrypto(metaclass=LogBase): - def __init__(self, setup, loglevel=logging.INFO): - self.__logger = logsetup(self, self.__logger, loglevel) + def __init__(self, setup, loglevel=logging.INFO, gui:bool=False): + self.__logger = logsetup(self, self.__logger, loglevel, gui) - self.dxcc = dxcc(setup, loglevel) - self.gcpu = GCpu(setup, loglevel) + self.dxcc = dxcc(setup, loglevel, gui) + self.gcpu = GCpu(setup, loglevel, gui) self.sej = sej(setup, loglevel) self.cqdma = cqdma(setup, loglevel) self.hwcode = setup.hwcode diff --git a/mtkclient/Library/hwcrypto_dxcc.py b/mtkclient/Library/hwcrypto_dxcc.py index 125d898..738eea6 100755 --- a/mtkclient/Library/hwcrypto_dxcc.py +++ b/mtkclient/Library/hwcrypto_dxcc.py @@ -1067,8 +1067,8 @@ class dxcc(metaclass=LogBase): self.write32(self.dxcc_base + self.DX_DSCRPTR_QUEUE0_WORD4, data[4]) self.write32(self.dxcc_base + self.DX_DSCRPTR_QUEUE0_WORD5, data[5]) - def __init__(self, setup, loglevel=logging.INFO): - self.__logger = logsetup(self, self.__logger, loglevel) + def __init__(self, setup, loglevel=logging.INFO, gui:bool = False): + self.__logger = logsetup(self, self.__logger, loglevel, gui) self.hwcode = setup.hwcode self.dxcc_base = setup.dxcc_base self.read32 = setup.read32 diff --git a/mtkclient/Library/hwcrypto_gcpu.py b/mtkclient/Library/hwcrypto_gcpu.py index dab69fb..c54ce9c 100755 --- a/mtkclient/Library/hwcrypto_gcpu.py +++ b/mtkclient/Library/hwcrypto_gcpu.py @@ -186,8 +186,8 @@ def xor_data(a: bytearray, b: bytearray, length=None): class GCpu(metaclass=LogBase): - def __init__(self, setup, loglevel=logging.INFO): - self.__logger = logsetup(self, self.__logger, loglevel) + def __init__(self, setup, loglevel=logging.INFO, gui:bool=False): + self.__logger = logsetup(self, self.__logger, loglevel, gui) self.read32 = setup.read32 self.write32 = setup.write32 self.reg = GCpuReg(setup) diff --git a/mtkclient/Library/kamakiri.py b/mtkclient/Library/kamakiri.py index b924aa2..a0ae728 100755 --- a/mtkclient/Library/kamakiri.py +++ b/mtkclient/Library/kamakiri.py @@ -12,7 +12,7 @@ from mtkclient.config.payloads import pathconfig class Kamakiri(metaclass=LogBase): def __init__(self, mtk, loglevel=logging.INFO): - self.__logger = logsetup(self, self.__logger, loglevel) + self.__logger = logsetup(self, self.__logger, loglevel, mtk.config.gui) self.lasterror = "" self.mtk = mtk self.chipconfig = self.mtk.config.chipconfig @@ -114,6 +114,7 @@ class Kamakiri(metaclass=LogBase): self.da_write(payloadaddr, len(payload), payload) self.da_write(ptr_send, 4, pack(" length): #done reading - break; + break except Exception as err: self.error(str(err)) - break; + break data = bytes(data) preloader = data[:length] idx = data.find(b"MTK_BLOADER_INFO") @@ -70,6 +71,7 @@ class DA_handler(metaclass=LogBase): return None def configure_da(self, mtk, preloader): + mtk.port.cdc.connected = mtk.port.cdc.connect() if mtk.port.cdc.connected is None or not mtk.port.cdc.connected: mtk.preloader.init() else: diff --git a/mtkclient/Library/mtk_dalegacy.py b/mtkclient/Library/mtk_dalegacy.py index 9459d69..353b095 100755 --- a/mtkclient/Library/mtk_dalegacy.py +++ b/mtkclient/Library/mtk_dalegacy.py @@ -654,7 +654,7 @@ class DALegacy(metaclass=LogBase): ENTER_RELAY_MODE_CMD = b"\xFF" def __init__(self, mtk, daconfig, loglevel=logging.INFO): - self.__logger = logsetup(self, self.__logger, loglevel) + self.__logger = logsetup(self, self.__logger, loglevel, mtk.config.gui) self.debug = self.debug self.error = self.error self.info = self.info diff --git a/mtkclient/Library/mtk_daloader.py b/mtkclient/Library/mtk_daloader.py index 457e8cb..967f75d 100755 --- a/mtkclient/Library/mtk_daloader.py +++ b/mtkclient/Library/mtk_daloader.py @@ -17,7 +17,7 @@ from mtkclient.Library.settings import hwparam class DAloader(metaclass=LogBase): def __init__(self, mtk, loglevel=logging.INFO): - self.__logger = logsetup(self, self.__logger, loglevel) + self.__logger = logsetup(self, self.__logger, loglevel, mtk.config.gui) self.mtk = mtk self.config = mtk.config self.loglevel = loglevel diff --git a/mtkclient/Library/mtk_daxflash.py b/mtkclient/Library/mtk_daxflash.py index f36636d..50ad768 100755 --- a/mtkclient/Library/mtk_daxflash.py +++ b/mtkclient/Library/mtk_daxflash.py @@ -124,7 +124,7 @@ class DAXFlash(metaclass=LogBase): DT_MESSAGE = 2 def __init__(self, mtk, daconfig, loglevel=logging.INFO): - self.__logger = logsetup(self, self.__logger, loglevel) + self.__logger = logsetup(self, self.__logger, loglevel, mtk.config.gui) self.info = self.__logger.info self.debug = self.__logger.debug self.error = self.__logger.error diff --git a/mtkclient/Library/mtk_main.py b/mtkclient/Library/mtk_main.py new file mode 100644 index 0000000..eff07d1 --- /dev/null +++ b/mtkclient/Library/mtk_main.py @@ -0,0 +1,544 @@ +#!/usr/bin/env python3 +# MTK Flash Client (c) B.Kerler 2018-2021. +# Licensed under GPLv3 License +import os +import sys +import logging +import time +from binascii import hexlify +from struct import unpack, pack +from mtkclient.Library.mtk import Mtk +from mtkclient.config.payloads import pathconfig +from mtkclient.Library.pltools import PLTools +from mtkclient.Library.meta import META +from mtkclient.Library.utils import LogBase, getint +from mtkclient.config.brom_config import Mtk_Config +from mtkclient.Library.utils import print_progress +from mtkclient.Library.error import ErrorHandler +from mtkclient.Library.mtk_da_cmd import DA_handler +from mtkclient.Library.gpt import gpt_settings + +metamodes = "[FASTBOOT, FACTFACT, METAMETA, FACTORYM, ADVEMETA]" + +class ArgHandler(metaclass=LogBase): + def __init__(self, args, config): + try: + if args.vid is not None: + config.vid = getint(args.vid) + except AttributeError: + pass + try: + if args.pid is not None: + config.pid = getint(args.pid) + except AttributeError: + pass + try: + if args.payload is not None: + config.payloadfile = args.payload + except: + pass + try: + if args.loader is not None: + config.loader = args.loader + except AttributeError: + pass + try: + if args.da_address is not None: + config.chipconfig.da_payload_addr = getint(args.da_address) + self.info("O:DA offset:\t\t\t" + args.da_address) + except AttributeError: + pass + try: + if args.brom_address is not None: + config.chipconfig.brom_payload_addr = getint(args.brom_address) + self.info("O:Payload offset:\t\t" + args.brom_address) + except AttributeError: + pass + try: + if args.watchdog_address is not None: + config.chipconfig.watchdog = getint(args.wdt) + self.info("O:Watchdog addr:\t\t" + args.wdt) + except AttributeError: + pass + try: + if args.skipwdt is not None: + config.skipwdt = args.skipwdt + except AttributeError: + pass + try: + if args.uart_address is not None: + config.chipconfig.uart = getint(args.uart_address) + self.info("O:Uart addr:\t\t" + args.uart_address) + except AttributeError: + pass + try: + if args.preloader is not None: + config.chipconfig.var1 = getint(args.var1) + self.info("O:Var1:\t\t" + args.var1) + except AttributeError: + pass + try: + if args.preloader is not None: + if os.path.exists(args.preloader): + config.preloader_filename = args.preloader + config.preloader = open(config.preloader_filename,"rb").read() + except AttributeError: + pass + try: + if args.generatekeys is not None: + config.generatekeys = args.generatekeys + except AttributeError: + pass + try: + if args.ptype is not None: + config.ptype = args.ptype + except AttributeError: + pass + try: + if args.socid is not None: + config.readsocid = args.socid + except AttributeError: + pass + try: + if args.crash is not None: + config.enforcecrash = args.crash + except AttributeError: + pass + + gpt_num_part_entries = 0 + try: + if args.gpt_num_part_entries is not None: + gpt_num_part_entries = args.gpt_num_part_entries + except: + pass + + gpt_part_entry_size = 0 + try: + if args.gpt_part_entry_size is not None: + gpt_part_entry_size = args.gpt_part_entry_size + except: + pass + + gpt_part_entry_start_lba = 0 + try: + if args.gpt_part_entry_start_lba is not None: + gpt_part_entry_start_lba = args.gpt_part_entry_start_lba + except: + pass + + config.gpt_settings = gpt_settings(gpt_num_part_entries,gpt_part_entry_size, + gpt_part_entry_start_lba) + +class Main(metaclass=LogBase): + def __init__(self, args): + self.__logger = self.__logger + self.info = self.__logger.info + self.debug = self.__logger.debug + self.error = self.__logger.error + self.warning = self.__logger.warning + self.args = args + if not os.path.exists("logs"): + os.mkdir("logs") + + def close(self): + sys.exit(0) + + def cmd_stage(self, mtk, filename, stage2addr, stage2file, verifystage2): + if filename is None: + pc = pathconfig() + stage1file = os.path.join(pc.get_payloads_path(), "generic_stage1_payload.bin") + else: + stage1file = filename + if not os.path.exists(stage1file): + self.error(f"Error: {stage1file} doesn't exist !") + return False + if stage2file is not None: + if not os.path.exists(stage2file): + self.error(f"Error: {stage2file} doesn't exist !") + return False + else: + stage2file = os.path.join(mtk.pathconfig.get_payloads_path(), "stage2.bin") + if mtk.preloader.init(): + mtk = mtk.crasher() + if mtk.port.cdc.pid == 0x0003: + plt = PLTools(mtk, self.__logger.level) + self.info("Uploading stage 1") + if plt.runpayload(filename=stage1file): + self.info("Successfully uploaded stage 1, sending stage 2") + with open(stage2file, "rb") as rr: + stage2data = rr.read() + while len(stage2data) % 0x200: + stage2data += b"\x00" + if stage2addr is None: + stage2addr = mtk.config.chipconfig.da_payload_addr + if stage2addr is None: + stage2addr = 0x201000 + + # ###### Send stage2 + # magic + mtk.port.usbwrite(pack(">I", 0xf00dd00d)) + # cmd write + mtk.port.usbwrite(pack(">I", 0x4000)) + # address + mtk.port.usbwrite(pack(">I", stage2addr)) + # length + mtk.port.usbwrite(pack(">I", len(stage2data))) + bytestowrite = len(stage2data) + pos = 0 + while bytestowrite > 0: + size = min(bytestowrite, 1) + if mtk.port.usbwrite(stage2data[pos:pos + size]): + bytestowrite -= size + pos += size + # mtk.port.usbwrite(b"") + time.sleep(0.1) + flag = mtk.port.rdword() + if flag != 0xD0D0D0D0: + self.error(f"Error on sending stage2, size {hex(len(stage2data))}.") + self.info(f"Done sending stage2, size {hex(len(stage2data))}.") + + if verifystage2: + self.info("Verifying stage2 data") + rdata = b"" + mtk.port.usbwrite(pack(">I", 0xf00dd00d)) + mtk.port.usbwrite(pack(">I", 0x4002)) + mtk.port.usbwrite(pack(">I", stage2addr)) + mtk.port.usbwrite(pack(">I", len(stage2data))) + bytestoread = len(stage2data) + while bytestoread > 0: + size = min(bytestoread, 1) + rdata += mtk.port.usbread(size) + bytestoread -= size + flag = mtk.port.rdword() + if flag != 0xD0D0D0D0: + self.error("Error on reading stage2 data") + if rdata != stage2data: + self.error("Stage2 data doesn't match") + with open("rdata", "wb") as wf: + wf.write(rdata) + else: + self.info("Stage2 verification passed.") + + # ####### Kick Watchdog + # magic + # mtk.port.usbwrite(pack("I", 0xf00dd00d)) + # cmd jump + mtk.port.usbwrite(pack(">I", 0x4001)) + # address + mtk.port.usbwrite(pack(">I", stage2addr)) + self.info("Done jumping stage2 at %08X" % stage2addr) + ack = unpack(">I", mtk.port.usbread(4))[0] + if ack == 0xB1B2B3B4: + self.info("Successfully loaded stage2") + + def cmd_peek(self, mtk, addr, length, preloader, filename): + if preloader is not None: + if os.path.exists(preloader): + daaddr, dadata = mtk.parse_preloader(preloader) + if mtk.preloader.init(): + if mtk.config.target_config["daa"]: + mtk = mtk.bypass_security() + 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 = Mtk_Config(loglevel=self.__logger.level) + mtk = Mtk(loglevel=self.__logger.level, config=config) + 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 ! + else: + self.error("Error on jumping to pl") + return + self.info("Starting to read ...") + dwords = length // 4 + if length % 4: + dwords += 1 + if filename != None: + wf = open(filename, "wb") + sdata = b"" + print_progress(0, 100, prefix='Progress:', + suffix='Starting, addr 0x%08X' % addr, bar_length=50) + length = dwords * 4 + old = 0 + pos = 0 + while dwords: + size = min(512 // 4, dwords) + data = b"".join(int.to_bytes(val, 4, 'little') for val in mtk.preloader.read32(addr + pos, size)) + sdata += data + if filename != "": + wf.write(data) + pos += len(data) + prog = pos / length * 100 + if round(prog, 1) > old: + print_progress(prog, 100, prefix='Progress:', + suffix='Complete, addr 0x%08X' % (addr + pos), bar_length=50) + old = round(prog, 1) + dwords = (length - pos) // 4 + print_progress(100, 100, prefix='Progress:', + suffix='Finished', bar_length=50) + if filename == "": + print(hexlify(sdata).decode('utf-8')) + else: + wf.close() + self.info(f"Data from {hex(addr)} with size of {hex(length)} was written to " + filename) + + def run(self): + try: + if self.args.debugmode: + loglevel = logging.DEBUG + self.__logger.setLevel(logging.DEBUG) + else: + loglevel = logging.INFO + self.__logger.setLevel(logging.INFO) + except: + loglevel = logging.INFO + self.__logger.setLevel(logging.INFO) + pass + config = Mtk_Config(loglevel=loglevel) + ArgHandler(self.args, config) + self.eh = ErrorHandler() + mtk = Mtk(config=config, loglevel=loglevel) + if mtk.config.debugmode: + logfilename = os.path.join("logs", "log.txt") + if os.path.exists(logfilename): + os.remove(logfilename) + fh = logging.FileHandler(logfilename, encoding='utf-8') + self.__logger.addHandler(fh) + + self.debug(" ".join(sys.argv)) + + cmd = self.args.cmd + if cmd == "dumpbrom": + if mtk.preloader.init(): + rmtk = mtk.crasher() + if rmtk is None: + sys.exit(0) + if rmtk.port.cdc.vid != 0xE8D and rmtk.port.cdc.pid != 0x0003: + self.warning("We couldn't enter preloader.") + filename = self.args.filename + if filename is None: + cpu = "" + if rmtk.config.cpu != "": + cpu = "_" + rmtk.config.cpu + filename = "brom" + cpu + "_" + hex(rmtk.config.hwcode)[2:] + ".bin" + plt = PLTools(rmtk, self.__logger.level) + plt.run_dump_brom(filename, self.args.ptype) + rmtk.port.close() + self.close() + elif cmd == "dumppreloader": + if mtk.preloader.init(): + rmtk = mtk.crasher() + if rmtk is None: + sys.exit(0) + if rmtk.port.cdc.vid != 0xE8D and rmtk.port.cdc.pid != 0x0003: + self.warning("We couldn't enter preloader.") + plt = PLTools(rmtk, self.__logger.level) + data, filename = plt.run_dump_preloader(self.args.ptype) + if data is not None: + if filename == "": + if self.args.filename is not None: + filename = self.args.filename + else: + filename = "preloader.bin" + with open(filename, 'wb') as wf: + print_progress(0, 100, prefix='Progress:', suffix='Complete', bar_length=50) + wf.write(data) + print_progress(100, 100, prefix='Progress:', suffix='Complete', bar_length=50) + self.info("Preloader dumped as: " + filename) + rmtk.port.close() + self.close() + elif cmd == "brute": + self.info("Kamakiri / DA Bruteforce run") + rmtk = Mtk(config=mtk.config, loglevel=self.__logger.level) + plt = PLTools(rmtk, self.__logger.level) + plt.runbrute(self.args) + self.close() + elif cmd == "crash": + if mtk.preloader.init(): + mtk = mtk.crasher(mode=getint(self.args.mode)) + mtk.port.close() + self.close() + elif cmd == "plstage": + if mtk.config.chipconfig.pl_payload_addr is not None: + plstageaddr = mtk.config.chipconfig.pl_payload_addr + else: + plstageaddr = 0x40200000 # 0x40001000 + if self.args.pl is None: + plstage = os.path.join(mtk.pathconfig.get_payloads_path(), "pl.bin") + else: + plstage = self.args.pl + if os.path.exists(plstage): + with open(plstage, "rb") as rf: + rf.seek(0) + pldata = rf.read() + if mtk.preloader.init(): + if mtk.config.target_config["daa"]: + mtk = mtk.bypass_security() + if mtk is None: + self.error("Error on bypassing security, aborting") + return + self.info("Connected to device, loading") + else: + self.error("Couldn't connect to device, aborting.") + if mtk.config.is_brom and mtk.config.preloader is None: + self.warning("PL stage needs preloader, please use --preloader option. " + + "Trying to dump preloader from ram.") + plt = PLTools(mtk=mtk, loglevel=self.__logger.level) + dadata, filename = plt.run_dump_preloader(self.args.ptype) + mtk.config.preloader = mtk.patch_preloader_security(dadata) + if mtk.config.preloader_filename is not None: + self.info("Using custom preloader : " + mtk.config.preloader_filename) + daaddr, dadata = mtk.parse_preloader(mtk.config.preloader) + mtk.config.preloader = mtk.patch_preloader_security(dadata) + 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"PL Jumped to daaddr {hex(daaddr)}.") + time.sleep(2) + """ + mtk = Mtk(config=mtk.config, loglevel=self.__logger.level) + res = mtk.preloader.init() + if not res: + self.error("Error on loading preloader") + return + else: + self.info("Successfully connected to pl") + + if self.args.startpartition is not None: + partition = self.args.startpartition + self.info("Booting to : " + partition) + # if data[0:4]!=b"\x88\x16\x88\x58": + # data=0x200*b"\x00"+data + mtk.preloader.send_partition_data(partition, pldata) + status = mtk.preloader.jump_to_partition(partition) # Do not remove ! + res = mtk.preloader.read32(0x10C180, 10) + for val in res: + print(hex(val)) + if status != 0x0: + self.error("Error on jumping to partition: " + self.eh.status(status)) + else: + self.info("Jumping to partition ....") + return + """ + sys.exit(0) + if mtk.preloader.send_da(plstageaddr, len(pldata), 0x100, pldata): + self.info(f"Sent stage2 to {hex(plstageaddr)}, length {hex(len(pldata))}") + mtk.preloader.get_hw_sw_ver() + if mtk.preloader.jump_da(plstageaddr): + self.info(f"Jumped to stage2 at {hex(plstageaddr)}.") + ack = unpack(">I", mtk.port.usbread(4))[0] + if ack == 0xB1B2B3B4: + self.info("Successfully loaded stage2") + return + else: + self.error("Error on jumping to pl") + return + else: + self.error("Error on sending pl") + return + self.close() + elif cmd == "peek": + addr = getint(self.args.address) + length = getint(self.args.length) + preloader = self.args.preloader + filename = self.args.filename + self.cmd_peek(mtk=mtk, addr=addr, length=length, preloader=preloader, filename=filename) + self.close() + elif cmd == "stage": + filename = self.args.filename + stage2addr = self.args.stage2addr + if self.args.stage2addr is not None: + stage2addr = getint(self.args.stage2addr) + stage2file = self.args.stage2 + verifystage2 = self.args.verifystage2 + + self.cmd_stage(mtk=mtk, filename=filename, stage2addr=stage2addr, stage2file=stage2file, + verifystage2=verifystage2) + self.close() + elif cmd == "payload": + payloadfile = self.args.payload + self.cmd_payload(mtk=mtk, payloadfile=payloadfile) + self.close() + elif cmd == "gettargetconfig": + if mtk.preloader.init(): + self.info("Getting target info...") + mtk.preloader.get_target_config() + mtk.port.close() + self.close() + elif cmd == "logs": + if self.args.filename is None: + filename = "log.txt" + else: + filename = self.args.filename + self.cmd_log(mtk=mtk, filename=filename) + mtk.port.close() + self.close() + elif cmd == "meta": + meta = META(mtk, loglevel) + if self.args.metamode is None: + self.error("You need to give a metamode as argument ex: " + metamodes) + else: + if meta.init(metamode=self.args.metamode, display=True): + self.info(f"Successfully set meta mode : {self.args.metamode}") + mtk.port.close() + self.close() + else: + # DA / FLash commands start here + try: + preloader = self.args.preloader + except: + preloader = None + da_handler = DA_handler(mtk, loglevel) + mtk = da_handler.configure_da(mtk, preloader) + da_handler.handle_da_cmds(mtk, cmd, self.args) + + + def cmd_log(self, mtk, filename): + if mtk.preloader.init(): + self.info("Getting target logs...") + try: + logs = mtk.preloader.get_brom_log_new() + except: + logs = mtk.preloader.get_brom_log() + if logs != b"": + with open(filename, "wb") as wf: + wf.write(logs) + self.info(f"Successfully wrote logs to \"{filename}\"") + else: + self.info("No logs found.") + + def cmd_payload(self, mtk, payloadfile): + if mtk.preloader.init(): + mtk = mtk.crasher() + plt = PLTools(mtk, self.__logger.level) + if payloadfile is None: + if mtk.config.chipconfig.loader is None: + payloadfile = os.path.join(mtk.pathconfig.get_payloads_path(), "generic_patcher_payload.bin") + else: + payloadfile = os.path.join(mtk.pathconfig.get_payloads_path(), mtk.config.chipconfig.loader) + plt.runpayload(filename=payloadfile) + if self.args.metamode: + mtk.port.run_handshake() + mtk.preloader.jump_bl() + mtk.port.close(reset=True) + meta = META(mtk, self.__logger.level) + if meta.init(metamode=self.args.metamode, display=True): + self.info(f"Successfully set meta mode : {self.args.metamode}") + mtk.port.close(reset=True) diff --git a/mtkclient/Library/mtk_preloader.py b/mtkclient/Library/mtk_preloader.py index 67bae2f..2e359a6 100755 --- a/mtkclient/Library/mtk_preloader.py +++ b/mtkclient/Library/mtk_preloader.py @@ -109,7 +109,7 @@ class Preloader(metaclass=LogBase): def __init__(self, mtk, loglevel=logging.INFO): self.mtk = mtk - self.__logger = logsetup(self, self.__logger, loglevel) + self.__logger = logsetup(self, self.__logger, loglevel, mtk.config.gui) self.info = self.__logger.info self.debug = self.__logger.debug self.error = self.__logger.error diff --git a/mtkclient/Library/partition.py b/mtkclient/Library/partition.py index 9f579d2..e3d349d 100755 --- a/mtkclient/Library/partition.py +++ b/mtkclient/Library/partition.py @@ -9,7 +9,7 @@ from mtkclient.Library.gpt import gpt class Partition(metaclass=LogBase): def __init__(self, mtk, readflash, read_pmt, loglevel=logging.INFO): self.mtk = mtk - self.__logger = logsetup(self, self.__logger, loglevel) + self.__logger = logsetup(self, self.__logger, loglevel, mtk.config.gui) self.config = self.mtk.config self.readflash = readflash self.read_pmt = read_pmt diff --git a/mtkclient/Library/pltools.py b/mtkclient/Library/pltools.py index cd40f9d..9f16461 100755 --- a/mtkclient/Library/pltools.py +++ b/mtkclient/Library/pltools.py @@ -14,7 +14,7 @@ from mtkclient.Library.Port import Port class PLTools(metaclass=LogBase): def __init__(self, mtk, loglevel=logging.INFO): - self.__logger = logsetup(self, self.__logger, loglevel) + self.__logger = logsetup(self, self.__logger, loglevel, mtk.config.gui) self.mtk = mtk self.chipconfig = self.mtk.config.chipconfig self.config = self.mtk.config @@ -42,7 +42,7 @@ class PLTools(metaclass=LogBase): setup.ap_dma_mem = self.mtk.config.chipconfig.ap_dma_mem setup.meid_addr = self.mtk.config.chipconfig.meid_addr setup.prov_addr = self.mtk.config.chipconfig.prov_addr - self.hwcrypto=hwcrypto(setup,loglevel) + self.hwcrypto=hwcrypto(setup,loglevel,self.mtk.config.gui) self.pathconfig = pathconfig() if loglevel == logging.DEBUG: diff --git a/mtkclient/Library/utils.py b/mtkclient/Library/utils.py index 5c90f2c..c02f500 100755 --- a/mtkclient/Library/utils.py +++ b/mtkclient/Library/utils.py @@ -14,7 +14,7 @@ import copy import time import io import datetime as dt - +from PySide2.QtCore import Signal sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding='utf-8') sys.stderr = io.TextIOWrapper(sys.stderr.detach(), encoding='utf-8') @@ -367,11 +367,17 @@ def revdword(value): return unpack(">I", pack("