Say welcome to the first gui attempt
|
@ -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
|
||||
|
||||
|
|
14
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
|
||||
|
|
690
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", 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
|
@ -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()
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"]:
|
||||
|
|
|
@ -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
|
@ -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
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
544
mtkclient/Library/mtk_main.py
Normal 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)
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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):
|
||||
self.info = logger.info
|
||||
self.debug = logger.debug
|
||||
self.error = logger.error
|
||||
self.warning = logger.warning
|
||||
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')
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
0
mtkclient/gui/__init__.py
Normal file
BIN
mtkclient/gui/images/logo_256.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
mtkclient/gui/images/logo_32.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
mtkclient/gui/images/logo_512.png
Normal file
After Width: | Height: | Size: 122 KiB |
BIN
mtkclient/gui/images/logo_64.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
mtkclient/gui/images/phone_connected.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
mtkclient/gui/images/phone_loading.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
mtkclient/gui/images/phone_notfound.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
mtkclient/gui/images/phone_success.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
180
mtkclient/gui/readFlashPartitions.py
Normal 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
|
@ -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
|
@ -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)
|
|
@ -5,6 +5,8 @@ requires = [
|
|||
"pyusb",
|
||||
"pycryptodome",
|
||||
"colorama",
|
||||
"usb"
|
||||
"usb",
|
||||
"PySide2",
|
||||
"mock"
|
||||
]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
|
|
@ -2,4 +2,6 @@ wheel
|
|||
pyusb
|
||||
pycryptodome
|
||||
colorama
|
||||
usb
|
||||
usb
|
||||
PySide2
|
||||
mock
|
6
setup.py
|
@ -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',
|
||||
|
|