Say welcome to the first gui attempt

This commit is contained in:
Bjoern Kerler 2021-12-25 23:01:25 +01:00
parent 82f8d2ce02
commit 535e2c2ee2
40 changed files with 1418 additions and 733 deletions

View file

@ -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

View file

@ -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

690
mtk
View file

@ -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", data[:4])[0]
if magic == 0x014D4D4D:
self.info(f"Valid preloader detected.")
daaddr = unpack("<I", data[0x1C:0x20])[0]
dasize = unpack("<I", data[0x20:0x24])[0]
maxsize = unpack("<I", data[0x24:0x28])[0]
content_offset = unpack("<I", data[0x28:0x2C])[0]
sig_length = unpack("<I", data[0x2C:0x30])[0]
jump_offset = unpack("<I", data[0x30:0x34])[0]
daaddr = jump_offset + daaddr
dadata = data[jump_offset:]
else:
self.warning("Preloader detected as shellcode, might fail to run.")
daaddr = self.config.chipconfig.da_payload_addr
dadata = data
return daaddr, dadata
def init(self, vid=None, pid=None, interface=None):
if vid is None:
vid = self.vid
if pid is None:
pid = self.pid
if interface is None:
interface = self.interface
if vid != -1 and pid != -1:
if interface == -1:
for dev in default_ids:
if dev[0] == vid and dev[1] == pid:
interface = dev[2]
break
portconfig = [[vid, pid, interface]]
else:
portconfig = default_ids
self.port = Port(self, portconfig, self.__logger.level)
self.preloader = Preloader(self, self.__logger.level)
self.daloader = DAloader(self, self.__logger.level)
def crasher(self, display=True, mode=None):
rmtk = self
plt = PLTools(self, self.__logger.level)
if self.config.enforcecrash or self.config.meid is None or not self.config.is_brom:
self.info("We're not in bootrom, trying to crash da...")
if mode is None:
for crashmode in range(0, 3):
try:
plt.crash(crashmode)
except:
pass
rmtk = Mtk(config=self.config, loglevel=self.__logger.level)
rmtk.preloader.display = display
if rmtk.preloader.init(maxtries=20):
break
else:
try:
plt.crash(mode)
except Exception as err:
self.__logger.debug(str(err))
pass
rmtk = Mtk(config=self.config, loglevel=self.__logger.level)
rmtk.preloader.display = display
if rmtk.preloader.init(maxtries=20):
return rmtk
return rmtk
def bypass_security(self):
mtk = self.crasher()
plt = PLTools(mtk, self.__logger.level)
if self.config.payloadfile is None:
if self.config.chipconfig.loader is None:
self.config.payloadfile = os.path.join(self.pathconfig.get_payloads_path(),
"generic_patcher_payload.bin")
else:
self.config.payloadfile = os.path.join(self.pathconfig.get_payloads_path(),
self.config.chipconfig.loader)
if plt.runpayload(filename=self.config.payloadfile):
mtk.port.run_handshake()
return mtk
else:
self.error("Error on running kamakiri payload")
return self
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 self.args.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 kick_watchdog
# mtk.port.usbwrite(pack("<I", 0x3001))
# ######### Jump stage1
# 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",

333
mtk_gui Executable file
View file

@ -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()

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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("<I", payloadaddr), False)
except usb.core.USBError as e:
print("USB CORE ERROR")
print(e)
return True

View file

@ -18,7 +18,7 @@ class LCmd:
class legacyext(metaclass=LogBase):
def __init__(self, mtk, legacy, loglevel):
self.pathconfig = pathconfig()
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
@ -119,7 +119,7 @@ class legacyext(metaclass=LogBase):
setup.read32 = self.readmem
setup.write32 = self.writeregister
setup.writemem = self.writemem
return hwcrypto(setup, self.loglevel)
return hwcrypto(setup, self.loglevel, self.config.gui)
def seccfg(self, lockflag):
if lockflag not in ["unlock", "lock"]:

View file

@ -22,7 +22,7 @@ class META(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

150
mtkclient/Library/mtk.py Normal file
View file

@ -0,0 +1,150 @@
#!/usr/bin/env python3
# MTK Flash Client (c) B.Kerler 2018-2021.
# Licensed under GPLv3 License
import os
import logging
from struct import unpack
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.mtk_daloader import DAloader
from mtkclient.Library.Port import Port
from mtkclient.Library.utils import LogBase, logsetup
from mtkclient.Library.error import ErrorHandler
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 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, config.gui)
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", data[:4])[0]
if magic == 0x014D4D4D:
self.info(f"Valid preloader detected.")
daaddr = unpack("<I", data[0x1C:0x20])[0]
dasize = unpack("<I", data[0x20:0x24])[0]
maxsize = unpack("<I", data[0x24:0x28])[0]
content_offset = unpack("<I", data[0x28:0x2C])[0]
sig_length = unpack("<I", data[0x2C:0x30])[0]
jump_offset = unpack("<I", data[0x30:0x34])[0]
daaddr = jump_offset + daaddr
dadata = data[jump_offset:]
else:
self.warning("Preloader detected as shellcode, might fail to run.")
daaddr = self.config.chipconfig.da_payload_addr
dadata = data
return daaddr, dadata
def init(self, vid=None, pid=None, interface=None):
if vid is None:
vid = self.vid
if pid is None:
pid = self.pid
if interface is None:
interface = self.interface
if vid != -1 and pid != -1:
if interface == -1:
for dev in default_ids:
if dev[0] == vid and dev[1] == pid:
interface = dev[2]
break
portconfig = [[vid, pid, interface]]
else:
portconfig = default_ids
self.port = Port(self, portconfig, self.__logger.level)
self.preloader = Preloader(self, self.__logger.level)
self.daloader = DAloader(self, self.__logger.level)
def crasher(self, display=True, mode=None):
rmtk = self
plt = PLTools(self, self.__logger.level)
if self.config.enforcecrash or self.config.meid is None or not self.config.is_brom:
self.info("We're not in bootrom, trying to crash da...")
if mode is None:
for crashmode in range(0, 3):
try:
plt.crash(crashmode)
except:
pass
rmtk = Mtk(config=self.config, loglevel=self.__logger.level)
rmtk.preloader.display = display
if rmtk.preloader.init(maxtries=20):
break
else:
try:
plt.crash(mode)
except Exception as err:
self.__logger.debug(str(err))
pass
rmtk = Mtk(config=self.config, loglevel=self.__logger.level)
rmtk.preloader.display = display
if rmtk.preloader.init(maxtries=20):
return rmtk
return rmtk
def bypass_security(self):
mtk = self.crasher()
plt = PLTools(mtk, self.__logger.level)
if self.config.payloadfile is None:
if self.config.chipconfig.loader is None:
self.config.payloadfile = os.path.join(self.pathconfig.get_payloads_path(),
"generic_patcher_payload.bin")
else:
self.config.payloadfile = os.path.join(self.pathconfig.get_payloads_path(),
self.config.chipconfig.loader)
if plt.runpayload(filename=self.config.payloadfile):
mtk.port.run_handshake()
return mtk
else:
self.error("Error on running kamakiri payload")
return self

View file

@ -22,7 +22,7 @@ class DA_handler(metaclass=LogBase):
self.pid = mtk.config.pid
self.interface = mtk.config.interface
self.pathconfig = pathconfig()
self.__logger = logsetup(self, self.__logger, loglevel)
self.__logger = logsetup(self, self.__logger, loglevel, mtk.config.gui)
self.eh = ErrorHandler()
self.mtk = mtk
@ -38,19 +38,20 @@ class DA_handler(metaclass=LogBase):
length = unpack("<I", data[0x20:0x24])[0]
time.sleep(0.15)
data = bytearray()
startidx = idx;
startidx = idx
multiplier = 32
while True:
try:
data.extend(b"".join([pack("<I", val) for val in self.mtk.preloader.read32(0x200000 + idx, 4)]))
idx = idx + 16;
sys.stdout.write("\r"+str(length-(idx-startidx)))
sys.stdout.flush()
data.extend(b"".join([pack("<I", val) for val in self.mtk.preloader.read32(0x200000 + idx, (4*multiplier))]))
idx = idx + (16*multiplier)
#sys.stdout.write("\r"+str(length-(idx-startidx)))
#sys.stdout.flush() sys.stdout.write("\r"+str(length-(idx-startidx)))
if ((idx-startidx) > 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:

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 kick_watchdog
# mtk.port.usbwrite(pack("<I", 0x3001))
# ######### Jump stage1
# 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)

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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("<I", value))[0]
def logsetup(self, logger, loglevel):
def logsetup(self, logger, loglevel, signal=None):
if not signal:
self.info = logger.info
self.debug = logger.debug
self.error = logger.error
self.warning = logger.warning
else:
self.info = signal.emit
self.debug = signal.emit
self.error = signal.emit
self.warning = signal.emit
if loglevel == logging.DEBUG:
logfilename = os.path.join("logs", "log.txt")
fh = logging.FileHandler(logfilename, encoding='utf-8')

View file

@ -40,7 +40,7 @@ rpmb_error = [
class xflashext(metaclass=LogBase):
def __init__(self, mtk, xflash, loglevel):
self.pathconfig = pathconfig()
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
@ -444,7 +444,7 @@ class xflashext(metaclass=LogBase):
setup.read32 = self.readmem
setup.write32 = self.writeregister
setup.writemem = self.writemem
return hwcrypto(setup, self.loglevel)
return hwcrypto(setup, self.loglevel, self.config.gui)
def seccfg(self, lockflag):
if lockflag not in ["unlock", "lock"]:
@ -562,6 +562,7 @@ class xflashext(metaclass=LogBase):
self.info("HRID : " + hexlify(hrid).decode('utf-8'))
open(os.path.join("logs", "hrid.txt"), "wb").write(hexlify(hrid))
"""
return {"rpmb": hexlify(rpmbkey).decode('utf-8'),"rpmb2":hexlify(rpmb2key).decode('utf-8'),"fde":hexlify(fdekey).decode('utf-8'),"ikey":hexlify(ikey).decode('utf-8')}
elif self.config.chipconfig.sej_base is not None:
if meid == b"":
if self.config.chipconfig.meid_addr:

View file

@ -1,5 +1,6 @@
import os
import logging
import sys
from mtkclient.Library.utils import LogBase
class damodes:
@ -1435,7 +1436,8 @@ hwconfig = {
class Mtk_Config(metaclass=LogBase):
def __init__(self, loglevel=logging.INFO):
def __init__(self, loglevel=logging.INFO, gui:bool = False):
self.gui = gui
self.pid = -1
self.vid = -1
self.var1 = 0xA

View file

@ -12,3 +12,9 @@ class pathconfig:
def get_payloads_path(self):
return os.path.abspath(os.path.join(self.scriptpath,"..","payloads"))
def get_gui_path(self):
return os.path.abspath(os.path.join(self.scriptpath,"..","gui"))
def get_images_path(self,file=""):
return os.path.abspath(os.path.join(self.scriptpath,"..","gui","images",file))

View file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View file

@ -0,0 +1,180 @@
from random import random
from PySide2.QtCore import Slot, Qt
from PySide2.QtGui import QTextOption, QPixmap
from PySide2.QtWidgets import *
import mock
from mtkclient.gui.toolkit import *
import os
import sys
import time
sys.excepthook = trap_exc_during_debug
class ReadFlashWindow(QDialog):
# Partition
@Slot()
def updateDumpState(self):
totalBytes = 0
doneBytes = 0
for partition in self.dumpStatus["allPartitions"]:
totalBytes = totalBytes + self.dumpStatus["allPartitions"][partition]['size']
if (self.dumpStatus["allPartitions"][partition]['done'] and partition != self.dumpStatus[
"currentPartition"]):
doneBytes = doneBytes + self.dumpStatus["allPartitions"][partition]['size']
doneBytes = doneBytes + self.dumpStatus["currentPartitionSizeDone"]
percentageDone = (self.dumpStatus["currentPartitionSizeDone"] / self.dumpStatus["currentPartitionSize"]) * 100
fullPercentageDone = (doneBytes / totalBytes) * 100
self.partProgress.setValue(percentageDone)
self.partProgressText.setText("Current partition: " + self.dumpStatus["currentPartition"] + " (" + str(
round((self.dumpStatus["currentPartitionSizeDone"] / 1024 / 1024))) + "Mb / " + str(
round((self.dumpStatus["currentPartitionSize"] / 1024 / 1024))) + " Mb)")
self.fullProgress.setValue(fullPercentageDone)
self.fullProgressText.setText("Total: (" + str(round((doneBytes / 1024 / 1024))) + "Mb / " + str(
round((totalBytes / 1024 / 1024))) + " Mb)")
def updateDumpStateAsync(self, toolkit, parameters):
while not self.dumpStatus["done"]:
# print(self.dumpStatus)
time.sleep(0.1)
try:
self.dumpStatus["currentPartitionSizeDone"] = os.stat(self.dumpStatus["currentPartitionFile"]).st_size
toolkit.sendUpdateSignal.emit()
# self.partProgressText.setText(str(currentPartitionSize)+"Current pa"+str(random(0,100000))+"rtition: "+self.dumpStatus["currentPartition"])
except:
time.sleep(0.1)
print("DONE")
self.startBtn.setEnabled(True)
def dumpPartDone(self):
self.sendToLogSignal.emit("dump klaar!")
def dumpPartition(self):
self.startBtn.setEnabled(False)
self.dumpFolder = str(QFileDialog.getExistingDirectory(self, "Select output directory"))
# self.startBtn.setText("In progress..")
thread = asyncThread(self, 0, self.dumpPartitionAsync, [])
thread.sendToLogSignal.connect(self.sendToLog)
thread.sendUpdateSignal.connect(self.updateDumpState)
thread.start()
def dumpPartitionAsync(self, toolkit, parameters):
# global MtkTool
# global mtkClass
self.sendToLogSignal = toolkit.sendToLogSignal
toolkit.sendToLogSignal.emit("test")
# partitionname = args.partitionname
# parttype = args.parttype
# filename = args.filename
# print(self.partitionCheckboxes)
self.dumpStatus["done"] = False
thread = asyncThread(self, 0, self.updateDumpStateAsync, [])
thread.sendUpdateSignal.connect(self.updateDumpState)
thread.start()
# calculate total bytes
self.dumpStatus["allPartitions"] = {}
# for partition in self.partitionCheckboxes:
# if self.partitionCheckboxes[partition]['box'].isChecked():
# self.dumpStatus["totalSize"] = self.dumpStatus["totalSize"]+self.partitionCheckboxes[partition]['size']
for partition in self.partitionCheckboxes:
if self.partitionCheckboxes[partition]['box'].isChecked():
self.dumpStatus["allPartitions"][partition] = {"size": self.partitionCheckboxes[partition]['size'],
"done": False}
for partition in self.partitionCheckboxes:
if self.partitionCheckboxes[partition]['box'].isChecked():
variables = mock.Mock()
variables.partitionname = partition
variables.filename = os.path.join(self.dumpFolder, partition + ".bin")
variables.parttype = None
self.dumpStatus["currentPartitionSize"] = self.partitionCheckboxes[partition]['size']
self.dumpStatus["currentPartition"] = partition
self.dumpStatus["currentPartitionFile"] = variables.filename
self.da_handler.close = self.dumpPartDone # Ignore the normally used sys.exit
self.da_handler.handle_da_cmds(self.mtkClass, "r", variables)
self.dumpStatus["allPartitions"][partition]['done'] = True
# MtkTool.cmd_stage(mtkClass, None, None, None, False)
self.dumpStatus["done"] = True
def __init__(self, parent, mtkClass, da_handler, sendToLog): # def __init__(self, *args, **kwargs):
super(ReadFlashWindow, self).__init__(parent)
self.mtkClass = mtkClass
self.sendToLog = sendToLog
self.dumpStatus = {}
self.da_handler = da_handler
self.setFixedSize(400, 500)
# widget = QWidget()
self.setWindowTitle("Read partition(s)")
title = QLabel(self)
title.setGeometry(10, 0, 480, 40)
data, guid_gpt = self.mtkClass.daloader.get_gpt()
if guid_gpt is None:
print("Error reading gpt")
title.setText("Error reading gpt")
else:
title.setText("Select partitions to dump")
# guid_gpt.print()
position = 40
partitionList = QScrollArea(self)
# partitionList.setFrameShape(frame)
partitionListWidget = QWidget(self)
partitionListWidgetVBox = QVBoxLayout()
partitionListWidget.setLayout(partitionListWidgetVBox)
partitionList.setWidget(partitionListWidget)
# partitionListWidget.addWidget(partitionList)
partitionList.setWidgetResizable(True)
# partitionListWidget = QWidget(partitionList)
# partitionListWidget.size(1000, 900)
partitionList.setGeometry(10, 40, 380, 320)
partitionList.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
partitionList.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
# partitionList.setWidget(self)
self.partitionCheckboxes = {}
print(self.partitionCheckboxes)
for partition in guid_gpt.partentries:
# print(partition.name)
# print()
self.partitionCheckboxes[partition.name] = {}
self.partitionCheckboxes[partition.name]['size'] = (partition.sectors * guid_gpt.sectorsize)
self.partitionCheckboxes[partition.name]['box'] = QCheckBox()
self.partitionCheckboxes[partition.name]['box'].setText(partition.name + " (" + str(
round(((partition.sectors * guid_gpt.sectorsize) / 1024 / 1024), 1)) + " Mb)")
partitionListWidgetVBox.addWidget(self.partitionCheckboxes[partition.name]['box'])
# partition progress bar
self.partProgressText = QLabel(self)
self.partProgressText.setText("Ready to start...")
self.partProgressText.setGeometry(10, 355, 480, 40)
self.partProgress = QProgressBar(self)
self.partProgress.setValue(0)
self.partProgress.setGeometry(10, 390, 380, 20)
self.partProgress.show()
# full progress bar
self.fullProgressText = QLabel(self)
self.fullProgressText.setText("")
self.fullProgressText.setGeometry(10, 405, 480, 40)
self.fullProgress = QProgressBar(self)
self.fullProgress.setValue(0)
self.fullProgress.setGeometry(10, 430, 380, 20)
self.fullProgress.show()
# start button
self.startBtn = QPushButton(self)
self.startBtn.setText("Start dumping")
self.startBtn.clicked.connect(self.dumpPartition)
self.startBtn.setGeometry((400 - 240 - 150), (500 - 30 - 10), 150, 30)
self.startBtn.show()
self.CloseBtn = QPushButton(self)
self.CloseBtn.setText("Close")
self.CloseBtn.clicked.connect(self.close)
self.CloseBtn.setGeometry((400 - 60), (500 - 30 - 10), 50, 30)
self.CloseBtn.show()
self.show()
# parent.setCentralWidget(self)

26
mtkclient/gui/toolkit.py Normal file
View file

@ -0,0 +1,26 @@
from PySide2.QtCore import Signal, QThread
from traceback import print_exception
class asyncThread(QThread):
sendToLogSignal = Signal(str)
sendUpdateSignal = Signal()
def __init__(self, parent, n, function, parameters):
super(asyncThread, self).__init__(parent)
# self.n = n
self.parameters = parameters
self.function = function
def run(self):
self.function(self, self.parameters)
def trap_exc_during_debug(type_, value, traceback):
print(print_exception(type_, value, traceback), flush=True)
# sendToLog("Error: "+str(value))
# when app raises uncaught exception, print info
# print("OH NO")
# print(args)
# print(traceback.print_tb(exc_traceback))
# print(traceback.format_exc())

102
mtkclient/gui/toolsMenu.py Normal file
View file

@ -0,0 +1,102 @@
from random import random
from PySide2.QtCore import Slot, Qt, QSize, QRect
from PySide2.QtGui import QTextOption, QPixmap
from PySide2.QtWidgets import *
import mock
from mtkclient.gui.toolkit import *
from mtkclient.Library.mtk import Mtk
from mtkclient.Library.mtk_da_cmd import DA_handler
import os
import sys
import time
sys.excepthook = trap_exc_during_debug
class generateKeysMenu(QDialog):
# Partition
@Slot()
def updateKeys(self):
print(self.keysStatus)
self.statusText.setText("Keys saved to hwparam.json")
# self.keyListText.setText("RPMB key: ")
self.keyValueText.setText(self.keysStatus['result']['rpmb'] + "\n" + self.keysStatus['result']['rpmb2'] + "\n" +
self.keysStatus['result']['fde'] + "\n" + self.keysStatus['result']['ikey'])
self.sendToLogSignal.emit("Keys generated!")
def generateKeys(self):
self.startBtn.setEnabled(False)
self.statusText.setText("Generating...")
thread = asyncThread(self, 0, self.generateKeysAsync, [])
thread.sendToLogSignal.connect(self.sendToLog)
thread.sendUpdateSignal.connect(self.updateKeys)
thread.start()
def generateKeysAsync(self, toolkit, parameters):
# global MtkTool
# global mtkClass
self.sendToLogSignal = toolkit.sendToLogSignal
self.sendUpdateSignal = toolkit.sendUpdateSignal
toolkit.sendToLogSignal.emit("test")
# partitionname = args.partitionname
# parttype = args.parttype
# filename = args.filename
# print(self.partitionCheckboxes)
# self.da_handler.close = self.dumpPartDone #Ignore the normally used sys.exit
self.keysStatus["result"] = self.mtkClass.daloader.keys()
# MtkTool.cmd_stage(mtkClass, None, None, None, False)
self.keysStatus["done"] = True
self.sendUpdateSignal.emit()
def __init__(self, parent, mtkClass:Mtk, da_handler:DA_handler, sendToLog): # def __init__(self, *args, **kwargs):
super(generateKeysMenu, self).__init__(parent)
self.mtkClass = mtkClass
self.sendToLog = sendToLog
self.keysStatus = {}
self.da_handler = da_handler
self.setFixedSize(700, 170)
# widget = QWidget()
self.setWindowTitle("Generate RPMB Keys")
title = QLabel(self)
title.setGeometry(10, 0, 150, 40)
title.setText("Generate RPMB Keys")
# Line
line = QFrame(self)
line.setGeometry(10, 25, 680, 20)
line.setFrameShape(QFrame.HLine)
line.setFrameShadow(QFrame.Sunken)
# Info
self.keyListText = QLabel(self)
self.keyListText.setText("RPMB:\nRPMB2:\nFDE:\niTrustee:")
self.keyListText.setStyleSheet("font-weight: bold")
self.keyListText.setGeometry(10, 30, 380, 90)
self.keyValueText = QLabel(self)
self.keyValueText.setText("")
self.keyValueText.setGeometry(100, 30, 580, 90)
# Status text
self.statusText = QLabel(self)
self.statusText.setText("Ready to start.")
self.statusText.setStyleSheet("font-style: italic color:#555")
self.statusText.setGeometry(10, 135, (580 - 200), 20)
# start button
self.startBtn = QPushButton(self)
self.startBtn.setText("Generate keys")
self.startBtn.clicked.connect(self.generateKeys)
self.startBtn.setGeometry((700 - 150 - 110), (170 - 30 - 10), 150, 30)
self.startBtn.show()
self.closeBtn = QPushButton(self)
self.closeBtn.setText("Close")
self.closeBtn.clicked.connect(self.close)
self.closeBtn.setGeometry((700 - 80 - 10), (170 - 30 - 10), 80, 30)
self.closeBtn.show()
self.show()
# parent.setCentralWidget(self)

View file

@ -5,6 +5,8 @@ requires = [
"pyusb",
"pycryptodome",
"colorama",
"usb"
"usb",
"PySide2",
"mock"
]
build-backend = "setuptools.build_meta"

View file

@ -3,3 +3,5 @@ pyusb
pycryptodome
colorama
usb
PySide2
mock

View file

@ -4,7 +4,7 @@ import os
setup(
name='mtkclient',
version='1.5',
version='1.55',
packages=find_packages(),
long_description=open("README.md").read(),
scripts=['mtk','stage2'],
@ -24,7 +24,9 @@ setup(
'colorama',
'usb',
'pyusb',
'pycryptodome'
'pycryptodome',
'PySide2',
'mock'
],
author='B. Kerler',
author_email='info@revskills.de',