From d81e8573798a85d9666203264be7fb4b3d2e339f Mon Sep 17 00:00:00 2001 From: Bjoern Kerler Date: Wed, 26 Jan 2022 10:08:33 +0100 Subject: [PATCH] Bump to 3.60 pre-release --- README.md | 11 + edl | 290 +++--- edlclient/Config/qualcomm_config.py | 10 +- edlclient/Library/Modules/init.py | 11 +- edlclient/Library/Modules/oneplus.py | 7 +- edlclient/Library/firehose.py | 707 ++++++++----- edlclient/Library/firehose_client.py | 136 +-- edlclient/Library/gpt.py | 343 ++++++- edlclient/Library/hdlc.py | 26 +- edlclient/Library/nand_config.py | 5 +- edlclient/Library/sahara.py | 641 ++++-------- edlclient/Library/streaming.py | 160 +-- edlclient/Library/streaming_client.py | 52 +- edlclient/Library/usblib.py | 690 ------------- edlclient/Library/usbscsi.py | 52 - edlclient/Library/utils.py | 138 ++- edlclient/Library/xmlparser.py | 14 +- edlclient/Tools/enableadb | 477 ++++++--- edlclient/Tools/fhloaderparse | 10 +- edlclient/Tools/qc_diag | 1330 +------------------------ edlclient/Tools/sierrakeygen | 592 +---------- setup.py | 4 +- 22 files changed, 1815 insertions(+), 3891 deletions(-) delete mode 100755 edlclient/Library/usblib.py delete mode 100755 edlclient/Library/usbscsi.py mode change 100755 => 120000 edlclient/Tools/qc_diag mode change 100755 => 120000 edlclient/Tools/sierrakeygen diff --git a/README.md b/README.md index 97160d1..4c221cf 100755 --- a/README.md +++ b/README.md @@ -65,6 +65,17 @@ pip3 install -r requirements.txt - Test on device connect using "UsbDkController -n" if you see a device with pid 0x9008 - Works fine under Windows 10 and 11 :D +#### Using serial port instead of usb +With Port autodetection +```bash +edl --serial +``` + +or Port name +```bash +edl --portname \\.\COM1 +``` + ------------------------------------------------------------------------------------------------------------------------------------ ## Get Loaders You should get these automatically if you do a ``` git submodule update --init --recursive ``` diff --git a/edl b/edl index dd96073..665629a 100755 --- a/edl +++ b/edl @@ -4,50 +4,51 @@ """ Usage: edl -h | --help - edl [--vid=vid] [--pid=pid] - edl [--loader=filename] [--memory=memtype] - edl [--debugmode] - edl [--gpt-num-part-entries=number] [--gpt-part-entry-size=number] [--gpt-part-entry-start-lba=number] - edl [--memory=memtype] [--skipstorageinit] [--maxpayload=bytes] [--sectorsize==bytes] - edl server [--tcpport=portnumber] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] - edl memorydump [--partitions=partnames] [--debugmode] [--vid=vid] [--pid=pid] - edl printgpt [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] - edl gpt [--memory=memtype] [--lun=lun] [--genxml] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] - edl r [--memory=memtype] [--sectorsize==bytes] [--lun=lun] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] - edl rl [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skip=partnames] [--genxml] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] - edl rf [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] - edl rs [--lun=lun] [--sectorsize==bytes] [--memory=memtype] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] - edl w [--partitionfilename=filename] [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skipwrite] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] - edl wl [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skip=partnames] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] - edl wf [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] - edl ws [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skipwrite] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] - edl e [--memory=memtype] [--skipwrite] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] - edl es [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skipwrite] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] - edl ep [--memory=memtype] [--skipwrite] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] - edl footer [--memory=memtype] [--lun=lun] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] - edl peek [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] - edl peekhex [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] - edl peekdword [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] - edl peekqword [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] - edl memtbl [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] - edl poke [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] - edl pokehex [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] - edl pokedword [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] - edl pokeqword [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] - edl memcpy [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] - edl secureboot [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] - edl pbl [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] - edl qfp [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] - edl getstorageinfo [--loader=filename] [--memory=memtype] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] - edl setbootablestoragedrive [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] - edl send [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] - edl xml [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] - edl rawxml [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] - edl reset [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] - edl nop [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] - edl modules [--memory=memtype] [--lun=lun] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] - edl provision [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] - edl qfil [--loader=filename] [--memory=memtype] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] + edl [--vid=vid] [--pid=pid] [--portname=portname] [--serial] + edl [--loader=filename] [--memory=memtype] [--portname=portname] [--serial] + edl [--debugmode] [--portname=portname] [--serial] + edl [--gpt-num-part-entries=number] [--gpt-part-entry-size=number] [--gpt-part-entry-start-lba=number] [--portname=portname] [--serial] + edl [--memory=memtype] [--skipstorageinit] [--maxpayload=bytes] [--sectorsize==bytes] [--portname=portname] [--serial] + edl server [--tcpport=portnumber] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial] + edl memorydump [--partitions=partnames] [--debugmode] [--vid=vid] [--pid=pid] [--portname=portname] [--serial] + edl printgpt [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] + edl gpt [--memory=memtype] [--lun=lun] [--genxml] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] + edl r [--memory=memtype] [--sectorsize==bytes] [--lun=lun] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] + edl rl [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skip=partnames] [--genxml] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] + edl rf [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] + edl rs [--lun=lun] [--sectorsize==bytes] [--memory=memtype] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] + edl w [--partitionfilename=filename] [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skipwrite] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial] + edl wl [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skip=partnames] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial] + edl wf [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial] + edl ws [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skipwrite] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial] + edl e [--memory=memtype] [--skipwrite] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial] + edl es [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skipwrite] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial] + edl ep [--memory=memtype] [--skipwrite] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial] + edl footer [--memory=memtype] [--lun=lun] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] + edl peek [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial] + edl peekhex [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--portname=portname] [--serial] + edl peekdword [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] + edl peekqword [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] + edl memtbl [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] + edl poke [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] + edl pokehex [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] + edl pokedword [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] + edl pokeqword [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] + edl memcpy [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--portname=portname] [--serial] + edl secureboot [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] + edl pbl [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] + edl qfp [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] + edl getstorageinfo [--loader=filename] [--memory=memtype] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] + edl setbootablestoragedrive [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial] + edl setactiveslot [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial] + edl send [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial] + edl xml [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial] + edl rawxml [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial] + edl reset [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--portname=portname] [--serial] + edl nop [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] + edl modules [--memory=memtype] [--lun=lun] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--portname=portname] [--serial] + edl provision [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial] + edl qfil [--loader=filename] [--memory=memtype] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial] Description: server # Run tcp/ip server @@ -83,9 +84,10 @@ Description: send # Send firehose command xml # Send firehose xml file rawxml # Send firehose xml raw string - reset # Send firehose reset command + reset # Send firehose reset command, reset modes: reset, off, edl nop # Send firehose nop command modules # Enable submodules, for example: "oemunlock enable" + setactiveslot # Set partition as active (Slot A/B) provision # UFS provision qfil # Write rawprogram xml files # : program config xml, such as rawprogram_unsparse.xml or rawprogram*.xml @@ -113,6 +115,9 @@ Options: --skip=partnames Skip reading partition with names "partname1,partname2,etc." --genxml Generate rawprogram[lun].xml --devicemodel=value Set device model + --portname=portname Set serial port name (/dev/ttyUSB0 for Linux/MAC; \\.\COM1 for Windows) + --serial Use serial port (port autodetection) + --slot Set active slot for setactiveslot [a or b] """ import os @@ -124,25 +129,25 @@ import re from docopt import docopt from edlclient.Config.usb_ids import default_ids from edlclient.Library.utils import LogBase -from edlclient.Library.usblib import UsbClass +from edlclient.Library.Connection.usblib import usb_class +from edlclient.Library.Connection.seriallib import serial_class from edlclient.Library.sahara import sahara from edlclient.Library.streaming_client import streaming_client from edlclient.Library.firehose_client import firehose_client from edlclient.Library.streaming import Streaming +from edlclient.Library.sahara_defs import cmd_t, sahara_mode_t from binascii import hexlify args = docopt(__doc__, version='3') -print("Qualcomm Sahara / Firehose Client V3.53 (c) B.Kerler 2018-2021.") +print("Qualcomm Sahara / Firehose Client V3.60 (c) B.Kerler 2018-2022.") def parse_cmd(rargs): cmds = ["server", "printgpt", "gpt", "r", "rl", "rf", "rs", "w", "wl", "wf", "ws", "e", "es", "ep", "footer", - "peek", "peekhex", - "peekdword", "peekqword", "memtbl", "poke", "pokehex", "pokedword", "pokeqword", "memcpy", "secureboot", - "pbl", - "qfp", "getstorageinfo", "setbootablestoragedrive", "send", "xml", "rawxml", "reset", "nop", "modules", - "memorydump", "provision", "qfil"] + "peek", "peekhex", "peekdword", "peekqword", "memtbl", "poke", "pokehex", "pokedword", "pokeqword", + "memcpy", "secureboot", "pbl", "qfp", "getstorageinfo", "setbootablestoragedrive", "setactiveslot", + "send", "xml", "rawxml", "reset", "nop", "modules", "memorydump", "provision", "qfil"] for cmd in cmds: if rargs[cmd]: return cmd @@ -177,9 +182,9 @@ class main(metaclass=LogBase): self.cdc = None self.sahara = None - def doconnect(self, loop, mode, resp): + def doconnect(self, loop): while not self.cdc.connected: - self.cdc.connected = self.cdc.connect() + self.cdc.connected = self.cdc.connect(portname=self.portname) if not self.cdc.connected: sys.stdout.write('.') if loop == 5: @@ -199,25 +204,40 @@ class main(metaclass=LogBase): else: self.info("Device detected :)") try: - mode, resp = self.sahara.connect() + resp = self.sahara.connect() except Exception as err: # pylint: disable=broad-except self.debug(str(err)) - if mode == "" or resp == -1: - mode, resp = self.sahara.connect() - if mode == -1: - mode, resp = self.sahara.connect() - if mode == "": - self.info("Unknown mode. Aborting.") - self.exit() - self.info(f"Mode detected: {mode}") - break - - return mode, resp + continue + if "mode" in resp: + mode = resp["mode"] + self.info(f"Mode detected: {mode}") + return resp + return {"mode":"error"} def exit(self): self.cdc.close() sys.exit() + def handle_streaming(self, mode): + self.cdc.timeout = None + sahara_info = self.sahara.streaminginfo() + if sahara_info: + mode, resp = self.sahara.connect() + if mode == "sahara": + mode = self.sahara.upload_loader() + if "enprg" in self.sahara.programmer.lower(): + mode = "load_enandprg" + elif "nprg" in self.sahara.programmer.lower(): + mode = "load_nandprg" + elif mode != "": + mode = "load_" + mode + if "load_" in mode: + time.sleep(0.3) + else: + print("Error, couldn't find suitable enprg/nprg loader :(") + self.exit() + return mode + def run(self): if sys.platform == 'win32' or sys.platform == 'win64' or sys.platform == 'winnt': proper_driver = console_cmd(r'reg query HKLM\HARDWARE\DEVICEMAP\SERIALCOMM') @@ -243,7 +263,20 @@ class main(metaclass=LogBase): else: self.__logger.setLevel(logging.INFO) - self.cdc = UsbClass(portconfig=portconfig, loglevel=self.__logger.level) + if args["--serial"]: + self.serial = True + else: + self.serial = False + if args["--portname"]: + self.portname = args["--portname"] + self.serial = True + else: + self.portname = "" + if self.serial: + self.cdc = serial_class(loglevel=self.__logger.level,portconfig=portconfig) + else: + self.cdc = usb_class(portconfig=portconfig, loglevel=self.__logger.level) + self.sahara = sahara(self.cdc, loglevel=self.__logger.level) if args["--loader"] == 'None': @@ -256,25 +289,14 @@ class main(metaclass=LogBase): self.info("Waiting for the device") resp = None - self.cdc.timeout = 100 - mode, resp = self.doconnect(loop, mode, resp) - if resp == -1: - mode, resp = self.doconnect(loop, mode, resp) - if resp == -1: - self.error("USB desync, please rerun command !") - self.exit() - # print((mode, resp)) + self.cdc.timeout = 1500 + conninfo = self.doconnect(loop) + mode = conninfo["mode"] if mode == "sahara": - if resp is None: - if mode == "sahara": - print("Sahara in error state, resetting ...") - self.sahara.cmd_reset() - data = self.cdc.read(5) - self.debug(hexlify(data).decode('utf-8')) - self.exit() - elif "mode" in resp: - mode = resp["mode"] - if mode == self.sahara.sahara_mode.SAHARA_MODE_MEMORY_DEBUG: + cmd = conninfo["cmd"] + data = conninfo["data"] + if cmd == cmd_t.SAHARA_END_TRANSFER: + if data.status == sahara_mode_t.SAHARA_MODE_MEMORY_DEBUG: if args["memorydump"]: time.sleep(0.5) print("Device is in memory dump mode, dumping memory") @@ -303,54 +325,41 @@ class main(metaclass=LogBase): print("Error, couldn't find suitable enprg/nprg loader :(") self.exit() else: - print("Device is in EDL mode .. continuing.") - self.cdc.timeout = None - sahara_info = self.sahara.cmd_info() - if sahara_info: - mode, resp = self.sahara.connect() - if mode == "sahara": - mode = self.sahara.upload_loader() - if mode == "firehose": - if "enprg" in self.sahara.programmer.lower(): - mode = "enandprg" - elif "nprg" in self.sahara.programmer.lower(): - mode = "nandprg" - if mode != "": - if mode != "firehose": - streaming = Streaming(self.cdc, self.sahara, self.__logger.level) - if streaming.connect(1): - print("Successfully uploaded programmer :)") - mode = "nandprg" - else: - print("Device is in an unknown state") - self.exit() - else: - print("Successfully uploaded programmer :)") - else: - print("No suitable loader found :(") - self.exit() - else: - print("Device is in an unknown sahara state, resetting") - print("resp={0}".format(resp)) - self.sahara.cmd_reset() - self.exit() - else: - print("Device is in an unknown state") - self.exit() - else: - self.sahara.bit64 = True - - if mode == "firehose": - self.cdc.timeout = None - cmd = parse_cmd(args) - if cmd == 'provision': - args["--memory"] = 'ufs' - args["--skipstorageinit"] = 1 - fh = firehose_client(args, self.cdc, self.sahara, self.__logger.level, print) - options = parse_option(args) - if cmd != "": - fh.handle_firehose(cmd, options) - elif mode == "nandprg" or mode == "enandprg" or mode == "load_nandprg" or mode == "load_enandprg": + self.error("Device is in an unknown sahara state, rebooting...") + self.sahara.cmd_reset() + data = self.cdc.read(timeout=None) + self.debug(hexlify(data).decode('utf-8')) + self.exit() + elif cmd == cmd_t.SAHARA_HELLO_REQ: + sahara_info = self.sahara.cmd_info() + if sahara_info is not None: + resp = self.sahara.connect() + mode = resp["mode"] + if "data" in resp: + data = resp["data"] + if mode == "sahara": + mode = self.sahara.upload_loader() + else: + print("Error on sahara handshake, resetting.") + self.sahara.cmd_reset() + sys.exit(1) + if mode == "error": + print("Connection detected, quiting.") + sys.exit(1) + elif mode == "firehose": + if "enprg" in self.sahara.programmer.lower(): + mode = "enandprg" + elif "nprg" in self.sahara.programmer.lower(): + mode = "nandprg" + if mode != "firehose": + streaming = Streaming(self.cdc, self.sahara, self.__logger.level) + if streaming.connect(1): + print("Successfully uploaded programmer :)") + mode = "nandprg" + else: + print("No suitable loader found :(") + self.exit() + if mode != "firehose": sc = streaming_client(args, self.cdc, self.sahara, self.__logger.level, print) cmd = parse_cmd(args) options = parse_option(args) @@ -360,10 +369,17 @@ class main(metaclass=LogBase): options[""] = 0 sc.handle_streaming(cmd, options) else: - self.error("Sorry, couldn't talk to Sahara, please reboot the device !") - - self.exit() - + self.cdc.timeout = None + cmd = parse_cmd(args) + if cmd == 'provision': + args["--memory"] = 'ufs' + args["--skipstorageinit"] = 1 + fh = firehose_client(args, self.cdc, self.sahara, self.__logger.level, print) + options = parse_option(args) + if cmd != "": + self.info("Trying to connect to firehose loader ...") + if fh.connect(sahara): + fh.handle_firehose(cmd, options) if __name__ == '__main__': base = main() diff --git a/edlclient/Config/qualcomm_config.py b/edlclient/Config/qualcomm_config.py index 64ea8fc..3753394 100644 --- a/edlclient/Config/qualcomm_config.py +++ b/edlclient/Config/qualcomm_config.py @@ -141,10 +141,10 @@ msmids = { 0x13F0E1: "bitra_SDM", # soc_vers 0x6012 SDM690 0x1410E1: "bitra_SDA", 0x1590E1: "cedros", # soc_vers 0x6017 - 0x1360E1: "kamorta", # soc_vers 0x9002 SnapDragon 460 SM4350, bengal - 0x1370E1: "kamorta_P", # soc_vers 0x9002 SnapDragon 460 SM4350, bengal - 0x1730E1: "kamorta_IoT_modem", # soc_vers 0x9002 SnapDragon 460 SM4350, bengal - 0x1740E1: "kamorta_IoT_APQ", # soc_vers 0x9002 SnapDragon 460 SM4350, bengal + 0x1360E1: "kamorta", # soc_vers 0x9002 SnapDragon 460 SM4350 + 0x1370E1: "kamorta_P", # soc_vers 0x9002 SnapDragon 460 SM4350 + 0x1730E1: "kamorta_IoT_modem", # soc_vers 0x9002 SnapDragon 460 SM4350 + 0x1740E1: "kamorta_IoT_APQ", # soc_vers 0x9002 SnapDragon 460 SM4350 0x1350E1: "lahaina", # soc_vers 0x600F sm8350, SDM875 0x1420E1: "lahaina_premier", 0x14A0E1: "SC8280X", # soc_vers 0x6014, makena @@ -189,7 +189,7 @@ msmids = { 0x0B80E1: "sc8180x", # Snapdragon 8CX, soc_vers 0x6006 0x1560E1: "SM8250", # HDK 8250 0x1510E1: "SA2150p", - 0x14D0E1: "SDM662", # sm6115 + 0x14D0E1: "SDM662", # sm6115, bengal 0x18A0E1: "fraser", #soc_vers 0x600D 0x1920E1: "sm7325", #soc_vers 0x6018 0x1930E1: "sc7280", #soc_vers 0x6018 diff --git a/edlclient/Library/Modules/init.py b/edlclient/Library/Modules/init.py index 27964b2..48d17bf 100644 --- a/edlclient/Library/Modules/init.py +++ b/edlclient/Library/Modules/init.py @@ -23,15 +23,17 @@ except ImportError as e: xiaomi = None pass + class modules(metaclass=LogBase): def __init__(self, fh, serial, supported_functions, loglevel, devicemodel, args): self.fh = fh self.args = args self.serial = serial self.error = self.__logger.error + self.info = self.__logger.info self.supported_functions = supported_functions self.__logger.setLevel(loglevel) - if loglevel==logging.DEBUG: + if loglevel == logging.DEBUG: logfilename = "log.txt" fh = logging.FileHandler(logfilename) self.__logger.addHandler(fh) @@ -45,10 +47,10 @@ class modules(metaclass=LogBase): self.ops = None try: self.ops = oneplus(fh=self.fh, projid=self.devicemodel, serial=self.serial, - supported_functions=self.supported_functions, args=self.args,loglevel=loglevel) + supported_functions=self.supported_functions, args=self.args, loglevel=loglevel) except Exception as e: pass - self.xiaomi=None + self.xiaomi = None try: self.xiaomi = xiaomi(fh=self.fh) except Exception as e: @@ -84,7 +86,7 @@ class modules(metaclass=LogBase): options[option[0]] = option[1] else: options[args[i]] = True - if command=="": + if command == "": print("Valid commands are:\noemunlock\n") return False if self.generic is not None and command == "oemunlock": @@ -96,4 +98,3 @@ class modules(metaclass=LogBase): self.error("Unknown mode given. Available are: enable, disable.") return False return self.generic.oem_unlock(enable) - return False diff --git a/edlclient/Library/Modules/oneplus.py b/edlclient/Library/Modules/oneplus.py index 682f8a3..a128cdb 100755 --- a/edlclient/Library/Modules/oneplus.py +++ b/edlclient/Library/Modules/oneplus.py @@ -143,7 +143,7 @@ class oneplus(metaclass=LogBase): lun = res[1] rpartition = res[2] data = self.fh.cmd_read_buffer(lun, rpartition.sector, 1, False) - value = data[24:24 + 5] + value = data.data[24:24 + 5] try: test = int(value.decode('utf-8')) self.info("Oneplus protection with prjid %d detected" % test) @@ -155,7 +155,7 @@ class oneplus(metaclass=LogBase): logfilename = "log.txt" filehandler = logging.FileHandler(logfilename) self.__logger.addHandler(filehandler) - self.ops_parm = None + self.ops_parm = None self.ops = self.convert_projid(fh, projid, serial) def getprodkey(self, projid): @@ -227,9 +227,6 @@ class oneplus(metaclass=LogBase): if self.ops.demacia(): return self.ops.demacia() - def enable_ops(self, data, enable, projid, serial): - return None - def addpatch(self): if "setprojmodel" in self.supported_functions or "setswprojmodel" in self.supported_functions: pk, token = self.ops.generatetoken(True) diff --git a/edlclient/Library/firehose.py b/edlclient/Library/firehose.py index 92d5b9c..fbeaf89 100755 --- a/edlclient/Library/firehose.py +++ b/edlclient/Library/firehose.py @@ -16,6 +16,18 @@ from edlclient.Library.gpt import gpt from edlclient.Library.sparse import QCSparse from edlclient.Library.utils import progress +class response: + resp=False + data=b"" + error="" + log=None + def __init__(self, resp=False, data=b"", error:str="", log:dict=""): + self.resp=resp + self.data=data + self.error=error + self.log=log + + try: from edlclient.Library.Modules.init import modules except ImportError as e: @@ -48,27 +60,40 @@ class nand_partition: which_flash = 0 magic1, magic2, version, numparts = unpack("= sectors: + sectors += self.partentries[part].sectors + self.totalsectors = sectors return True return False def print(self): - self.printer("Name Offset\t\tLength\t\tAttr\t\t\tFlash") + self.printer("Name Offset\t\tLength\t\tAttr\t\t\tFlash") self.printer("-------------------------------------------------------------") for selpart in self.partentries: partition = self.partentries[selpart] @@ -124,6 +149,7 @@ class firehose(metaclass=LogBase): bit64 = True total_blocks = 0 + num_physical = 0 block_size = 0 SECTOR_SIZE_IN_BYTES = 0 MemoryName = "eMMC" @@ -181,7 +207,7 @@ class firehose(metaclass=LogBase): def getstatus(self, resp): if "value" in resp: value = resp["value"] - if value == "ACK": + if value == "ACK" or value == "true": return True else: return False @@ -204,21 +230,20 @@ class firehose(metaclass=LogBase): pass return data - def xmlsend(self, data, skipresponse=False): + def xmlsend(self, data, skipresponse=False) -> response: + self.cdc.flush() + self.cdc.xmlread = True if isinstance(data, bytes) or isinstance(data, bytearray): self.cdc.write(data[:self.cfg.MaxXMLSizeInBytes]) else: self.cdc.write(bytes(data, 'utf-8')[:self.cfg.MaxXMLSizeInBytes]) - # time.sleep(0.01) rdata = bytearray() counter = 0 - timeout = 30 - resp = {"value": "NAK"} - status = False + timeout = 3 if not skipresponse: while b"" + def cmd_reset(self, mode="reset"): + data = "" val = self.xmlsend(data) try: v = None while v != b'': - v = self.cdc.read(self.cfg.MaxXMLSizeInBytes) + v = self.cdc.read(timeout=None) if v != b'': resp = self.xml.getlog(v)[0] else: @@ -269,31 +313,31 @@ class firehose(metaclass=LogBase): except Exception as err: self.error(str(err)) pass - if val[0]: + if val.resp: self.info("Reset succeeded.") return True else: - self.error("Reset failed.") + self.error("Reset failed: "+val.error) return False def cmd_xml(self, filename): with open(filename, 'rb') as rf: data = rf.read() val = self.xmlsend(data) - if val[0]: - self.info("Command succeeded." + str(val[2])) - return val[2] + if val.resp: + self.info("Command succeeded." + str(val.data)) + return val.data else: - self.error("Command failed:" + str(val[2])) - return val[2] + self.error("Command failed:" + str(val.error)) + return val.error def cmd_nop(self): data = "" - self.xmlsend(data, True) + resp=self.xmlsend(data, True) info = b"" tmp = None while tmp != b"": - tmp = self.cdc.read(self.cfg.MaxXMLSizeInBytes) + tmp = self.cdc.read(timeout=None) if tmp == b"": break info += tmp @@ -311,8 +355,8 @@ class firehose(metaclass=LogBase): f" physical_partition_number=\"{physical_partition_number}\"" + \ f" start_sector=\"{start_sector}\"/>\n" val = self.xmlsend(data) - if val[0]: - res = self.xml.getlog(val[2]) + if val.resp: + res = self.xml.getlog(val.data) for line in res: self.info(line) if "Digest " in res: @@ -320,29 +364,29 @@ class firehose(metaclass=LogBase): else: return res else: - self.error("GetSha256Digest failed.") + self.error("GetSha256Digest failed: "+val.error) return False def cmd_setbootablestoragedrive(self, partition_number): data = f"\n" val = self.xmlsend(data) - if val[0]: + if val.resp: self.info("Setbootablestoragedrive succeeded.") return True else: - self.error("Setbootablestoragedrive failed: %s" % val[2]) + self.error("Setbootablestoragedrive failed: " + val.error) return False def cmd_send(self, content, response=True): data = f"\n<{content} />" if response: val = self.xmlsend(data) - if val[0] and b"log value=\"ERROR\"" not in val[1]: - return val[2] + if val.resp: + return val.data else: self.error(f"{content} failed.") - self.error(f"{val[2]}") - return val[1] + self.error(f"{val.error}") + return val.error else: self.xmlsend(data, True) return True @@ -366,20 +410,20 @@ class firehose(metaclass=LogBase): data += f"/>\n" rsp = self.xmlsend(data) - if rsp[0]: + if rsp.resp: if display: self.info(f"Patch:\n--------------------\n") - self.info(rsp[1]) + self.info(rsp.data) return True else: - self.error(f"Error:{rsp}") + self.error(f"Error:{rsp.error}") return False def wait_for_data(self): tmp = bytearray() timeout = 0 while b'response value' not in tmp: - res = self.cdc.read(self.cfg.MaxXMLSizeInBytes) + res = self.cdc.read(timeout=None) if res == b'': timeout += 1 if timeout == 4: @@ -416,8 +460,7 @@ class firehose(metaclass=LogBase): data += f"/>\n" rsp = self.xmlsend(data, self.skipresponse) progbar.show_progress(prefix="Write", pos=0, total=total, display=display) - if rsp[0]: - old = 0 + if rsp.resp: while bytestowrite > 0: wlen = min(bytestowrite, self.cfg.MaxPayloadSizeToTargetInBytes) @@ -473,8 +516,7 @@ class firehose(metaclass=LogBase): rsp = self.xmlsend(data, self.skipresponse) progbar = progress(self.cfg.SECTOR_SIZE_IN_BYTES) progbar.show_progress(prefix="Write", pos=0, total=total, display=display) - if rsp[0]: - old = 0 + if rsp.resp: pos = 0 while bytestowrite > 0: wlen = min(bytestowrite, self.cfg.MaxPayloadSizeToTargetInBytes) @@ -506,6 +548,8 @@ class firehose(metaclass=LogBase): else: self.error(f"Error:{rsp}") return False + else: + self.error(f"Error:{rsp.error}") return True def cmd_erase(self, physical_partition_number, start_sector, num_partition_sectors, display=True): @@ -529,7 +573,7 @@ class firehose(metaclass=LogBase): total = self.cfg.SECTOR_SIZE_IN_BYTES * num_partition_sectors progbar = progress(self.cfg.MaxPayloadSizeToTargetInBytes) progbar.show_progress(prefix="Erase", pos=0, total=total, display=display) - if rsp[0]: + if rsp.resp: while bytestowrite > 0: wlen = min(bytestowrite, self.cfg.MaxPayloadSizeToTargetInBytes) self.cdc.write(empty[:wlen]) @@ -550,11 +594,13 @@ class firehose(metaclass=LogBase): else: self.error(f"Error:{rsp}") return False + else: + self.error(f"Error:{rsp.error}") + return False return True def cmd_read(self, physical_partition_number, start_sector, num_partition_sectors, filename, display=True): self.lasterror = b"" - prog = 0 progbar = progress(self.cfg.SECTOR_SIZE_IN_BYTES) if display: self.info( @@ -562,32 +608,36 @@ class firehose(metaclass=LogBase): f"sector {str(start_sector)}, sectors {str(num_partition_sectors)}") with open(file=filename, mode="wb", buffering=self.cfg.MaxPayloadSizeFromTargetInBytes) as wr: - data = f"\n" rsp = self.xmlsend(data, self.skipresponse) - # time.sleep(0.01) - if rsp[0]: - if "value" in rsp[1]: - if rsp[1]["value"] == "NAK": - if display: - self.error(rsp[2].decode('utf-8')) - return b"" + self.cdc.xmlread = False + time.sleep(0.01) + if not rsp.resp: + if display: + self.error(rsp.error) + return b"" + else: bytestoread = self.cfg.SECTOR_SIZE_IN_BYTES * num_partition_sectors total = bytestoread show_progress = progbar.show_progress usb_read = self.cdc.read progbar.show_progress(prefix="Read", pos=0, total=total, display=display) while bytestoread > 0: - size=min(self.cfg.MaxPayloadSizeToTargetInBytes, bytestoread) + if self.cdc.is_serial: + maxsize=self.cfg.MaxPayloadSizeFromTargetInBytes + else: + maxsize=5*1024*1024 + size=min(maxsize, bytestoread) data = usb_read(size) - wr.write(data) - bytestoread -= len(data) - show_progress(prefix="Read", pos=total - bytestoread, total=total, display=display) - # time.sleep(0.2) + if len(data)>0: + wr.write(data) + bytestoread -= len(data) + show_progress(prefix="Read", pos=total - bytestoread, total=total, display=display) + self.cdc.xmlread = True wd = self.wait_for_data() info = self.xml.getlog(wd) rsp = self.xml.getresponse(wd) @@ -602,8 +652,6 @@ class firehose(metaclass=LogBase): if display: self.error(f"Error:{rsp[2]}") return False - if display and prog != 100: - print_progress(100, 100, prefix='Progress:', suffix='Complete', bar_length=50) return True def cmd_read_buffer(self, physical_partition_number, start_sector, num_partition_sectors, display=True): @@ -622,24 +670,24 @@ class firehose(metaclass=LogBase): progbar = progress(self.cfg.SECTOR_SIZE_IN_BYTES) rsp = self.xmlsend(data, self.skipresponse) + self.cdc.xmlread = False resData = bytearray() - if rsp[0]: - if "value" in rsp[1]: - if rsp[1]["value"] == "NAK": - if display: - self.error(rsp[2].decode('utf-8')) - return -1 + if not rsp.resp: + if display: + self.error(rsp.error) + return rsp + else: bytestoread = self.cfg.SECTOR_SIZE_IN_BYTES * num_partition_sectors total = bytestoread if display: progbar.show_progress(prefix="Read", pos=total - bytestoread, total=total, display=display) while bytestoread > 0: - tmp = self.cdc.read(min(self.cdc.EP_IN.wMaxPacketSize, bytestoread)) + tmp = self.cdc.read(min(self.cdc.maxsize, bytestoread)) size = len(tmp) bytestoread -= size resData.extend(tmp) progbar.show_progress(prefix="Read", pos=total - bytestoread, total=total, display=display) - + self.cdc.xmlread = True wd = self.wait_for_data() info = self.xml.getlog(wd) rsp = self.xml.getresponse(wd) @@ -648,46 +696,64 @@ class firehose(metaclass=LogBase): self.error(f"Error:") for line in info: self.error(line) - return resData + return response(resp=False,data=resData, error=info) + elif "rawmode" in rsp: + if rsp["rawmode"] == "false": + return response(resp=True,data=resData) else: if len(rsp) > 1: if b"Failed to open the UFS Device" in rsp[2]: self.error(f"Error:{rsp[2]}") - self.lasterror = rsp[2] - return resData - if len(rsp) > 2 and not rsp[0]: + self.lasterror = rsp[2] + return response(resp=False,data=resData,error=rsp[2]) + if rsp["value"]!="ACK": self.lasterror = rsp[2] if display and prog != 100: progbar.show_progress(prefix="Read", pos=total, total=total, display=display) - return resData # Do not remove, needed for oneplus + resp=rsp["value"]=="ACK" + return response(resp=resp,data=resData,error=rsp[2]) # Do not remove, needed for oneplus def get_gpt(self, lun, gpt_num_part_entries, gpt_part_entry_size, gpt_part_entry_start_lba): try: - data = self.cmd_read_buffer(lun, 0, 2, False) + resp = self.cmd_read_buffer(lun, 0, 2, False) except Exception as err: self.debug(str(err)) self.skipresponse = True - data = self.cmd_read_buffer(lun, 0, 2, False) + resp = self.cmd_read_buffer(lun, 0, 2, False) - if data == b"" or data == -1: + if not resp.resp : + for line in resp.error: + self.error(line) return None, None + data = resp.data magic = unpack("" + \ + connectcmd = f"" + \ f"" + \ + f"SkipWrite=\"{str(int(self.cfg.SkipWrite))}\"/>" + \ "" ''' "" ''' rsp = self.xmlsend(connectcmd) - if len(rsp) > 1: - if not rsp[0]: - if b"Only nop and sig tag can be" in rsp[2]: + if not rsp.resp: + if rsp.error=="": + if "MemoryName" in rsp.data: + self.cfg.MemoryName = rsp.data["MemoryName"] + if "MaxPayloadSizeFromTargetInBytes" in rsp.data: + self.cfg.MaxPayloadSizeFromTargetInBytes = int(rsp.data["MaxPayloadSizeFromTargetInBytes"]) + if "MaxPayloadSizeToTargetInBytes" in rsp.data: + self.cfg.MaxPayloadSizeToTargetInBytes = int(rsp.data["MaxPayloadSizeToTargetInBytes"]) + if "MaxPayloadSizeToTargetInBytesSupported" in rsp.data: + self.cfg.MaxPayloadSizeToTargetInBytesSupported = int(rsp.data["MaxPayloadSizeToTargetInBytesSupported"]) + if "TargetName" in rsp.data: + self.cfg.TargetName = rsp.data["TargetName"] + return self.configure(lvl + 1) + for line in rsp.error: + if "Not support configure MemoryName eMMC" in line: + self.info("eMMC is not supported by the firehose loader. Trying UFS instead.") + self.cfg.MemoryName = "UFS" + return self.configure(lvl + 1) + elif "Only nop and sig tag can be" in line: self.info("Xiaomi EDL Auth detected.") try: self.modules = modules(fh=self, serial=self.serial, @@ -790,59 +875,34 @@ class firehose(metaclass=LogBase): self.modules = None if self.modules.edlauth(): rsp = self.xmlsend(connectcmd) - if len(rsp) > 1: - if rsp[0] and rsp[1] != {}: # On Ack - info = self.cdc.read(self.cfg.MaxXMLSizeInBytes) - if "MemoryName" not in rsp[1]: - # print(rsp[1]) - rsp[1]["MemoryName"] = "eMMC" - if "MaxXMLSizeInBytes" not in rsp[1]: - rsp[1]["MaxXMLSizeInBytes"] = "4096" - self.warning("Couldn't detect MaxPayloadSizeFromTargetinBytes") - if "MaxPayloadSizeToTargetInBytes" not in rsp[1]: - rsp[1]["MaxPayloadSizeToTargetInBytes"] = "1038576" - if "MaxPayloadSizeToTargetInBytesSupported" not in rsp[1]: - rsp[1]["MaxPayloadSizeToTargetInBytesSupported"] = "1038576" - if rsp[1]["MemoryName"].lower() != self.cfg.MemoryName.lower(): - self.warning("Memory type was set as " + self.cfg.MemoryName + " but device reported it is " + - rsp[1]["MemoryName"] + " instead.") - self.cfg.MemoryName = rsp[1]["MemoryName"] - self.cfg.MaxPayloadSizeToTargetInBytes = int(rsp[1]["MaxPayloadSizeToTargetInBytes"]) - self.cfg.MaxPayloadSizeToTargetInBytesSupported = int(rsp[1]["MaxPayloadSizeToTargetInBytesSupported"]) - self.cfg.MaxXMLSizeInBytes = int(rsp[1]["MaxXMLSizeInBytes"]) - if "MaxPayloadSizeFromTargetInBytes" in rsp[1]: - self.cfg.MaxPayloadSizeFromTargetInBytes = int(rsp[1]["MaxPayloadSizeFromTargetInBytes"]) - else: - self.cfg.MaxPayloadSizeFromTargetInBytes = self.cfg.MaxXMLSizeInBytes - self.warning("Couldn't detect MaxPayloadSizeFromTargetinBytes") - if "TargetName" in rsp[1]: - self.cfg.TargetName = rsp[1]["TargetName"] - if "MSM" not in self.cfg.TargetName: - self.cfg.TargetName = "MSM" + self.cfg.TargetName - else: - self.cfg.TargetName = "Unknown" - self.warning("Couldn't detect TargetName") - if "Version" in rsp[1]: - self.cfg.Version = rsp[1]["Version"] - else: - self.cfg.Version = 0 - self.warning("Couldn't detect Version") - else: # on NAK - if b"ERROR" in rsp[2]: - self.error(rsp[2].decode('utf-8')) - sys.exit() - if "MaxPayloadSizeToTargetInBytes" in rsp[1]: + return rsp.resp + else: + self.error("Error on EDL Authentification") + return False + elif "MaxPayloadSizeToTargetInBytes" in line: try: - self.cfg.MemoryName = rsp[1]["MemoryName"] - self.cfg.MaxPayloadSizeToTargetInBytes = int(rsp[1]["MaxPayloadSizeToTargetInBytes"]) + self.cfg.MemoryName = rsp.data["MemoryName"] + self.cfg.MaxPayloadSizeToTargetInBytes = int(rsp.data["MaxPayloadSizeToTargetInBytes"]) self.cfg.MaxPayloadSizeToTargetInBytesSupported = int( - rsp[1]["MaxPayloadSizeToTargetInBytesSupported"]) - self.cfg.MaxXMLSizeInBytes = int(rsp[1]["MaxXMLSizeInBytes"]) - self.cfg.MaxPayloadSizeFromTargetInBytes = int(rsp[1]["MaxPayloadSizeFromTargetInBytes"]) - self.cfg.TargetName = rsp[1]["TargetName"] + rsp.data["MaxPayloadSizeToTargetInBytesSupported"]) + if "MaxXMLSizeInBytes" in rsp.data: + self.cfg.MaxXMLSizeInBytes = int(rsp.data["MaxXMLSizeInBytes"]) + else: + self.cfg.MaxXMLSizeInBytes = 4096 + if "MaxPayloadSizeFromTargetInBytes" in rsp.data: + self.cfg.MaxPayloadSizeFromTargetInBytes = int(rsp.data["MaxPayloadSizeFromTargetInBytes"]) + else: + self.cfg.MaxPayloadSizeFromTargetInBytes = 4096 + if "TargetName" in rsp.data: + self.cfg.TargetName = rsp.data["TargetName"] + else: + self.cfg.TargetName = "Unknown" if "MSM" not in self.cfg.TargetName: self.cfg.TargetName = "MSM" + self.cfg.TargetName - self.cfg.Version = rsp[1]["Version"] + if "Version" in rsp.data: + self.cfg.Version = rsp.data["Version"] + else: + self.cfg.Version = "Unknown" if lvl == 0: return self.configure(lvl + 1) else: @@ -850,28 +910,93 @@ class firehose(metaclass=LogBase): sys.exit() except Exception as e: pass - self.info(f"TargetName={self.cfg.TargetName}") - self.info(f"MemoryName={self.cfg.MemoryName}") - self.info(f"Version={self.cfg.Version}") - - rsp = self.cmd_read_buffer(0, 1, 1, False) - if rsp == b"" and self.args["--memory"] is None: - if b"Failed to open the SDCC Device" in self.lasterror: - self.warning( - "Memory type eMMC doesn't seem to match (Failed to init). Trying to use UFS instead.") - self.cfg.MemoryName = "UFS" - return self.configure(0) - if b"ERROR: Failed to initialize (open whole lun) UFS Device slot" in self.lasterror: - self.warning( - "Memory type UFS doesn't seem to match (Failed to init). Trying to use eMMC instead.") - self.cfg.MemoryName = "eMMC" - return self.configure(0) - elif b"Attribute \'SECTOR_SIZE_IN_BYTES\'=4096 must be equal to disk sector size 512" in self.lasterror: - self.cfg.SECTOR_SIZE_IN_BYTES = 512 - elif b"Attribute \'SECTOR_SIZE_IN_BYTES\'=512 must be equal to disk sector size 4096" in self.lasterror: - self.cfg.SECTOR_SIZE_IN_BYTES = 4096 - self.luns = self.getluns(self.args) - return True + elif "ERROR" in line or "WARN" in line: + if "ERROR" in line: + self.error(line) + sys.exit() + elif "WARN" in line: + self.warning(line) + else: + info = self.cdc.read(timeout=1) + if isinstance(rsp.resp,dict): + field = rsp.resp + if "MemoryName" not in field: + # print(rsp[1]) + field["MemoryName"] = "eMMC" + if "MaxXMLSizeInBytes" not in field: + field["MaxXMLSizeInBytes"] = "4096" + self.warning("Couldn't detect MaxPayloadSizeFromTargetinBytes") + if "MaxPayloadSizeToTargetInBytes" not in field: + field["MaxPayloadSizeToTargetInBytes"] = "1038576" + if "MaxPayloadSizeToTargetInBytesSupported" not in field: + field["MaxPayloadSizeToTargetInBytesSupported"] = "1038576" + if field["MemoryName"].lower() != self.cfg.MemoryName.lower(): + self.warning("Memory type was set as " + self.cfg.MemoryName + " but device reported it is " + + field["MemoryName"] + " instead.") + self.cfg.MemoryName = field["MemoryName"] + if "MaxPayloadSizeToTargetInBytes" in field: + self.cfg.MaxPayloadSizeToTargetInBytes = int(field["MaxPayloadSizeToTargetInBytes"]) + else: + self.cfg.MaxPayloadSizeToTargetInBytes = 1048576 + if "MaxPayloadSizeToTargetInBytesSupported" in field: + self.cfg.MaxPayloadSizeToTargetInBytesSupported = int(field["MaxPayloadSizeToTargetInBytesSupported"]) + else: + self.cfg.MaxPayloadSizeToTargetInBytesSupported = 1048576 + if "MaxXMLSizeInBytes" in field: + self.cfg.MaxXMLSizeInBytes = int(field["MaxXMLSizeInBytes"]) + else: + self.cfg.MaxXMLSizeInBytes = 4096 + if "MaxPayloadSizeFromTargetInBytes" in field: + self.cfg.MaxPayloadSizeFromTargetInBytes = int(field["MaxPayloadSizeFromTargetInBytes"]) + else: + self.cfg.MaxPayloadSizeFromTargetInBytes = self.cfg.MaxXMLSizeInBytes + self.warning("Couldn't detect MaxPayloadSizeFromTargetinBytes") + if "TargetName" in field: + self.cfg.TargetName = field["TargetName"] + if "MSM" not in self.cfg.TargetName: + self.cfg.TargetName = "MSM" + self.cfg.TargetName + else: + self.cfg.TargetName = "Unknown" + self.warning("Couldn't detect TargetName") + if "Version" in field: + self.cfg.Version = field["Version"] + else: + self.cfg.Version = 0 + self.warning("Couldn't detect Version") + self.info(f"TargetName={self.cfg.TargetName}") + self.info(f"MemoryName={self.cfg.MemoryName}") + self.info(f"Version={self.cfg.Version}") + self.info("Trying to read first storage sector...") + rsp = self.cmd_read_buffer(0, 1, 1, False) + self.info("Running configure...") + if not rsp.resp and self.args["--memory"] is None: + for line in rsp.error: + if "Failed to set the IO options" in line: + self.warning( + "Memory type eMMC doesn't seem to match (Failed to init). Trying to use NAND instead.") + self.cfg.MemoryName = "nand" + return self.configure(0) + elif "Failed to open the SDCC Device" in line: + self.warning( + "Memory type eMMC doesn't seem to match (Failed to init). Trying to use UFS instead.") + self.cfg.MemoryName = "UFS" + return self.configure(0) + elif "Failed to initialize (open whole lun) UFS Device slot" in line: + self.warning( + "Memory type UFS doesn't seem to match (Failed to init). Trying to use eMMC instead.") + self.cfg.MemoryName = "eMMC" + return self.configure(0) + elif "Attribute \'SECTOR_SIZE_IN_BYTES\'=4096 must be equal to disk sector size 512" in line\ + or "different from device sector size (512)" in line: + self.cfg.SECTOR_SIZE_IN_BYTES = 512 + return self.configure(0) + elif "Attribute \'SECTOR_SIZE_IN_BYTES\'=512 must be equal to disk sector size 4096" in line\ + or "different from device sector size (4096)" in line: + self.cfg.SECTOR_SIZE_IN_BYTES = 4096 + return self.configure(0) + self.parse_storage() + self.luns = self.getluns(self.args) + return True def getlunsize(self, lun): if lun not in self.lunsizes: @@ -920,7 +1045,7 @@ class firehose(metaclass=LogBase): for line in self.supported_functions: info += line + "," self.info(info[:-1]) - data = self.cdc.read(self.cfg.MaxXMLSizeInBytes) # logbuf + data = self.cdc.read(timeout=None) try: self.info(data.decode('utf-8')) except Exception as err: # pylint: disable=broad-except @@ -945,8 +1070,8 @@ class firehose(metaclass=LogBase): info = [] while v != b'': try: - v = self.cdc.read(self.cfg.MaxXMLSizeInBytes) - if v == b'': + v = self.cdc.read(timeout=None) + if (b"response" in v and b"" in v) or v == b'': break data = self.xml.getlog(v) if len(data) > 0: @@ -959,27 +1084,11 @@ class firehose(metaclass=LogBase): if info == [] or (len(info) > 0 and 'ERROR' in info[0]): if len(info) > 0: self.debug(info[0]) - if self.serial is None or self.supported_functions is []: - try: - if os.path.exists("edl_config.json"): - pinfo = json.loads(open("edl_config.json", "rb").read()) - if self.supported_functions == []: - if "supported_functions" in pinfo: - self.supported_functions = pinfo["supported_functions"] - if self.serial is None: - if "serial" in pinfo: - self.serial = pinfo["serial"] - else: - self.get_supported_functions() - except: - self.get_supported_functions() - pass - - else: + if len(info)>0: supfunc = False for line in info: + self.info(line) if "chip serial num" in line.lower(): - self.info(line) try: serial = line.split("0x")[1][:-1] self.serial = int(serial, 16) @@ -994,6 +1103,14 @@ class firehose(metaclass=LogBase): self.supported_functions.append(rs) if "supported functions" in line.lower(): supfunc = True + if "program" in line.lower(): + idx=line.find("Functions: ") + if idx!=-1: + v=line[idx+11:].split(" ") + for val in v: + if val!="": + self.supported_functions.append(val) + supfunc = False try: if os.path.exists(self.cfg.programmer): data = open(self.cfg.programmer, "rb").read() @@ -1009,47 +1126,49 @@ class firehose(metaclass=LogBase): except: pass - # rsp = self.xmlsend(data, self.skipresponse) - if "getstorageinfo" in self.supported_functions and self.args["--memory"] is None: - storageinfo = self.cmd_getstorageinfo() - if storageinfo is not None and storageinfo != []: - for info in storageinfo: - if "storage_info" in info: - try: - si = json.loads(info)["storage_info"] - except Exception as err: # pylint: disable=broad-except - self.debug(str(err)) - continue - self.info("Storage report:") - for sii in si: - self.info(f"{sii}:{si[sii]}") - if "total_blocks" in si: - self.cfg.total_blocks = si["total_blocks"] + elif self.serial is None or self.supported_functions is []: + try: + if os.path.exists("edl_config.json"): + pinfo = json.loads(open("edl_config.json", "rb").read()) + if not self.supported_functions: + if "supported_functions" in pinfo: + self.supported_functions = pinfo["supported_functions"] + if self.serial is None: + if "serial" in pinfo: + self.serial = pinfo["serial"] + else: + self.get_supported_functions() + except: + self.get_supported_functions() + pass - if "block_size" in si: - self.cfg.block_size = si["block_size"] - if "page_size" in si: - self.cfg.SECTOR_SIZE_IN_BYTES = si["page_size"] - if "mem_type" in si: - self.cfg.MemoryName = si["mem_type"] - if "prod_name" in si: - self.cfg.prod_name = si["prod_name"] - if "UFS Inquiry Command Output:" in info: - self.cfg.prod_name = info.split("Output: ")[1] - self.info(info) - if "UFS Erase Block Size:" in info: - self.cfg.block_size = int(info.split("Size: ")[1], 16) - self.info(info) - if "UFS Boot" in info: - self.cfg.MemoryName = "UFS" - self.cfg.SECTOR_SIZE_IN_BYTES = 4096 - if "UFS Boot Partition Enabled: " in info: - self.info(info) - if "UFS Total Active LU: " in info: - self.cfg.maxlun = int(info.split("LU: ")[1], 16) + # rsp = self.xmlsend(data, self.skipresponse) return self.supported_functions + def parse_storage(self): + storageinfo = self.cmd_getstorageinfo() + if storageinfo is None or storageinfo.resp and len(storageinfo.data)==0: + return False + info = storageinfo.data + if "UFS Inquiry Command Output" in info: + self.cfg.prod_name = info["UFS Inquiry Command Output"] + self.info(info) + if "UFS Erase Block Size" in info: + self.cfg.block_size = int(info["UFS Erase Block Size"], 16) + self.info(info) + self.cfg.MemoryName = "UFS" + self.cfg.SECTOR_SIZE_IN_BYTES = 4096 + if "UFS Boot Partition Enabled" in info: + self.info(info["UFS Boot Partition Enabled"]) + if "UFS Total Active LU" in info: + self.cfg.maxlun = int(info["UFS Total Active LU"], 16) + if "SECTOR_SIZE_IN_BYTES" in info: + self.cfg.SECTOR_SIZE_IN_BYTES = int(info["SECTOR_SIZE_IN_BYTES"]) + if "num_physical_partitions" in info: + self.cfg.num_physical = int(info["num_physical_partitions"]) + return True + # OEM Stuff here below -------------------------------------------------- def cmd_writeimei(self, imei): @@ -1058,7 +1177,7 @@ class firehose(metaclass=LogBase): return False data = "" val = self.xmlsend(data) - if val[0]: + if val.resp: self.info("writeIMEI succeeded.") return True else: @@ -1068,22 +1187,94 @@ class firehose(metaclass=LogBase): def cmd_getstorageinfo(self): data = "" val = self.xmlsend(data) - if val[0]: - try: - data = self.xml.getlog(val[2]) - return data - except: # pylint: disable=broad-except - return None + if val.data=='' and val.log=='' and val.resp: + return None + if isinstance(val.data,dict): + if "bNumberLu" in val.data: + self.cfg.maxlun = int(val.data["bNumberLu"]) + if val.resp: + if val.log is not None: + res={} + for value in val.log: + v=value.split("=") + if len(v)>1: + res[v[0]]=v[1] + else: + if "\"storage_info\"" in value: + try: + info = value.replace("INFO:", "") + si = json.loads(info)["storage_info"] + except Exception as err: # pylint: disable=broad-except + self.debug(str(err)) + continue + self.info("Storage report:") + for sii in si: + self.info(f"{sii}:{si[sii]}") + if "total_blocks" in si: + self.cfg.total_blocks = si["total_blocks"] + if "num_physical" in si: + self.cfg.num_physical = si["num_physical"] + if "block_size" in si: + self.cfg.block_size = si["block_size"] + if "page_size" in si: + self.cfg.SECTOR_SIZE_IN_BYTES = si["page_size"] + if "mem_type" in si: + self.cfg.MemoryName = si["mem_type"] + if "prod_name" in si: + self.cfg.prod_name = si["prod_name"] + else: + v=value.split(":") + if len(v)>1: + res[v[0]]=v[1].lstrip(" ") + return response(resp=val.resp, data=res) + return response(resp=val.resp, data=val.data) else: + if val.error: + for v in val.error: + if "Failed to open the SDCC Device" in v: + self.cfg.MemoryName="ufs" + self.configure(0) + return self.cmd_getstorageinfo() self.warning("GetStorageInfo command isn't supported.") return None + def cmd_setactiveslot(self, slot:str): + if slot.lower() not in ["a","b"]: + self.error("Only slots a or b are accepted. Aborting.") + return False + partslots = {} + if slot == "a": + partslots["_a"] = True + partslots["_b"] = False + elif slot == "b": + partslots["_a"] = True + partslots["_b"] = False + fpartitions = {} + for lun in self.luns: + lunname = "Lun" + str(lun) + fpartitions[lunname] = [] + data, guid_gpt = self.get_gpt(lun, int(0), int(0), int(0)) + if guid_gpt is None: + break + else: + for partitionname in guid_gpt.partentries: + gp = gpt() + slot = partitionname.lower()[-2:] + if "_a" in slot or "_b" in slot: + pdata, offset = gp.patch(data, partitionname, active=partslots[slot]) + if data is not None: + start_sector = offset // self.cfg.SECTOR_SIZE_IN_BYTES + byte_offset = offset % self.cfg.SECTOR_SIZE_IN_BYTES + self.cmd_patch(lun,start_sector,byte_offset,pdata,len(pdata),True) + return True + return False + def cmd_test(self, cmd): token = "1234" pk = "1234" data = "\n\n<" + cmd + " token=\"" + token + "\" pk=\"" + pk + "\" />\n" val = self.xmlsend(data) - if len(val) > 1: + if val.resp: if b"raw hex token" in val[2]: return True if b"opcmd is not enabled" in val[2]: @@ -1093,9 +1284,9 @@ class firehose(metaclass=LogBase): def cmd_getstorageinfo_string(self): data = "" val = self.xmlsend(data) - if val[0]: + if val.resp: self.info(f"GetStorageInfo:\n--------------------\n") - data = self.xml.getlog(val[2]) + data = self.xml.getlog(val.data) for line in data: self.info(line) return True @@ -1148,26 +1339,26 @@ class firehose(metaclass=LogBase): except Exception as e: # pylint: disable=broad-except self.debug(str(e)) pass - addrinfo = self.cdc.read(self.cfg.MaxXMLSizeInBytes) + addrinfo = self.cdc.read(timeout=None) if b"SizeInBytes" in addrinfo or b"Invalid parameters" in addrinfo: tmp = b"" while b"NAK" not in tmp and b"ACK" not in tmp: - tmp += self.cdc.read(self.cfg.MaxXMLSizeInBytes) + tmp += self.cdc.read(timeout=None) xdata = f"\n" self.cdc.write(xdata[:self.cfg.MaxXMLSizeInBytes]) - addrinfo = self.cdc.read(self.cfg.MaxXMLSizeInBytes) + addrinfo = self.cdc.read(timeout=None) if (b'" self.cdc.write(data[:self.cfg.MaxXMLSizeInBytes]) - addrinfo = self.cdc.read(self.cfg.MaxXMLSizeInBytes) + addrinfo = self.cdc.read(timeout=None) if (b'"]): return False filename = options[""] - storageinfo = self.get_storage_info() - if storageinfo is not None and self.cfg.MemoryName.lower() in ["spinor" , "nand"]: - totalsectors = None - if "total_blocks" in storageinfo: - totalsectors = storageinfo["total_blocks"] - if "num_physical" in storageinfo: - num_physical = storageinfo["num_physical"] - luns = [0] - if num_physical > 0: - luns=[] - for i in range(num_physical): - luns.append(i) + #storageinfo = self.firehose.parse_storage() + if self.cfg.MemoryName.lower() in ["spinor", "nand"]: + luns = [0] + totalsectors = (self.cfg.block_size * self.cfg.total_blocks) // self.cfg.SECTOR_SIZE_IN_BYTES + if self.cfg.num_physical > 0: + luns = [] + for i in range(self.cfg.num_physical): + luns.append(i) + elif 99 > self.cfg.maxlun > 0: + luns = [] + for i in range(self.cfg.maxlun): + luns.append(i) if totalsectors is not None: for lun in luns: - buffer=self.firehose.cmd_read_buffer(physical_partition_number=lun, - start_sector=0, num_partition_sectors=1, display=False) - storageinfo = self.get_storage_info() - if "total_blocks" in storageinfo: - totalsectors = storageinfo["total_blocks"] - if len(luns) > 1: - sfilename = filename + f".lun{str(lun)}" - else: - sfilename = filename - self.printer(f"Dumping sector 0 with sector count {str(totalsectors)} as {filename}.") - if self.firehose.cmd_read(lun, 0, totalsectors, sfilename): - self.printer( - f"Dumped sector 0 with sector count {str(totalsectors)} as {filename}.") + buffer = self.firehose.cmd_read_buffer(physical_partition_number=lun, + start_sector=0, num_partition_sectors=1, display=False) + if self.get_storage_info(): + totalsectors = (self.cfg.block_size * + self.cfg.total_blocks ) // self.cfg.SECTOR_SIZE_IN_BYTES + + if len(luns) > 1: + sfilename = filename + f".lun{str(lun)}" + else: + sfilename = filename + self.printer(f"Dumping sector 0 with sector count {str(totalsectors)} as {filename}.") + if self.firehose.cmd_read(lun, 0, totalsectors, sfilename): + self.printer( + f"Dumped sector 0 with sector count {str(totalsectors)} as {filename}.") else: luns = self.getluns(options) for lun in luns: @@ -356,11 +362,17 @@ class firehose_client(metaclass=LogBase): break if len(luns) > 1: sfilename = filename + f".lun{str(lun)}" + self.printer(f"Dumping lun {lun} with sector count {str(guid_gpt.totalsectors)} as {filename}.") else: sfilename = filename - self.printer(f"Dumping sector 0 with sector count {str(guid_gpt.totalsectors)} as {filename}.") + self.printer(f"Dumping flash with sector count {str(guid_gpt.totalsectors)} as {filename}.") + if self.firehose.cmd_read(lun, 0, guid_gpt.totalsectors, sfilename): - self.printer(f"Dumped sector 0 with sector count {str(guid_gpt.totalsectors)} as {filename}.") + if len(luns) > 1: + self.printer(f"Dumped lun {lun} with sector count " + + f"{str(guid_gpt.totalsectors)} as {filename}.") + else: + self.printer(f"Dumped flash with sector count {str(guid_gpt.totalsectors)} as {filename}.") return True elif cmd == "pbl": if not self.check_param([""]): @@ -465,8 +477,9 @@ class firehose_client(metaclass=LogBase): break pnames = ["userdata2", "metadata", "userdata", "reserved1", "reserved2", "reserved3"] for pname in pnames: - if pname in guid_gpt.partentries: - partition = guid_gpt.partentries[pname] + for partition in guid_gpt.partentries: + if partition.name != pname: + continue self.printer(f"Detected partition: {partition.name}") data = self.firehose.cmd_read_buffer(lun, partition.sector + @@ -602,7 +615,10 @@ class firehose_client(metaclass=LogBase): else: return False elif cmd == "reset": - return self.firehose.cmd_reset() + mode = "reset" + if not self.check_param([""]): + return False + return self.firehose.cmd_reset(options[""]) elif cmd == "nop": if not self.check_cmd("nop"): self.error("Nop command isn't supported by edl loader") @@ -617,6 +633,11 @@ class firehose_client(metaclass=LogBase): return False else: return self.firehose.cmd_setbootablestoragedrive(int(options[""])) + elif cmd == "setactiveslot": + if not self.check_param([""]): + return False + else: + return self.firehose.cmd_setactiveslot(options[""]) elif cmd == "getstorageinfo": if not self.check_cmd("getstorageinfo"): self.error("getstorageinfo command isn't supported by edl loader") @@ -668,9 +689,9 @@ class firehose_client(metaclass=LogBase): for lun in fpartitions: for partition in fpartitions[lun]: if self.cfg.MemoryName == "emmc": - self.error("\t" + partition) + self.error("\t" + partition.name) else: - self.error(lun + ":\t" + partition) + self.error(lun + ":\t" + partition.name) return False elif cmd == "wl": if not self.check_param([""]): @@ -780,7 +801,8 @@ class firehose_client(metaclass=LogBase): f"with sector count {str(partition.sectors)}.") return True else: - self.printer(f"Couldn't erase partition {partitionname}. Either wrong memorytype given or no gpt partition.") + self.printer( + f"Couldn't erase partition {partitionname}. Either wrong memorytype given or no gpt partition.") return False self.error(f"Error: Couldn't detect partition: {partitionname}") return False diff --git a/edlclient/Library/gpt.py b/edlclient/Library/gpt.py index aa1afc1..0decf8c 100755 --- a/edlclient/Library/gpt.py +++ b/edlclient/Library/gpt.py @@ -1,18 +1,192 @@ #!/usr/bin/python3 # -*- coding: utf-8 -*- -# (c) B.Kerler 2018-2021 -import argparse +# (c) B.Kerler 2018-2021 MIT License import os import sys +import argparse +import colorama +import copy import logging +import logging.config from enum import Enum -from struct import unpack, pack from binascii import hexlify +from struct import calcsize, unpack, pack +from io import BytesIO -try: - from edlclient.Library.utils import LogBase, structhelper -except: - from utils import LogBase, structhelper +class ColorFormatter(logging.Formatter): + LOG_COLORS = { + logging.ERROR: colorama.Fore.RED, + logging.DEBUG: colorama.Fore.LIGHTMAGENTA_EX, + logging.WARNING: colorama.Fore.YELLOW, + } + + def format(self, record, *args, **kwargs): + # if the corresponding logger has children, they may receive modified + # record, so we want to keep it intact + new_record = copy.copy(record) + if new_record.levelno in self.LOG_COLORS: + pad = "" + if new_record.name != "root": + print(new_record.name) + pad = "[LIB]: " + # we want levelname to be in different color, so let"s modify it + new_record.msg = "{pad}{color_begin}{msg}{color_end}".format( + pad=pad, + msg=new_record.msg, + color_begin=self.LOG_COLORS[new_record.levelno], + color_end=colorama.Style.RESET_ALL, + ) + # now we can let standart formatting take care of the rest + return super(ColorFormatter, self).format(new_record, *args, **kwargs) + + +class LogBase(type): + debuglevel = logging.root.level + + def __init__(cls, *args): + super().__init__(*args) + logger_attribute_name = "_" + cls.__name__ + "__logger" + logger_debuglevel_name = "_" + cls.__name__ + "__debuglevel" + logger_name = ".".join([c.__name__ for c in cls.mro()[-2::-1]]) + log_config = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "root": { + "()": ColorFormatter, + "format": "%(name)s - %(message)s", + } + }, + "handlers": { + "root": { + # "level": cls.__logger.level, + "formatter": "root", + "class": "logging.StreamHandler", + "stream": "ext://sys.stdout", + } + }, + "loggers": { + "": { + "handlers": ["root"], + # "level": cls.debuglevel, + "propagate": False + } + }, + } + logging.config.dictConfig(log_config) + logger = logging.getLogger(logger_name) + + setattr(cls, logger_attribute_name, logger) + setattr(cls, logger_debuglevel_name, cls.debuglevel) + cls.logsetup = logsetup + + +def logsetup(self, logger, loglevel): + self.info = logger.info + self.debug = logger.debug + self.error = logger.error + self.warning = logger.warning + if loglevel == logging.DEBUG: + logfilename = os.path.join("logs", "log.txt") + if os.path.exists(logfilename): + try: + os.remove(logfilename) + except: + pass + fh = logging.FileHandler(logfilename, encoding="utf-8") + logger.addHandler(fh) + logger.setLevel(logging.DEBUG) + else: + logger.setLevel(logging.INFO) + self.loglevel = loglevel + return logger + + +def read_object(data: object, definition: object) -> object: + """ + Unpacks a structure using the given data and definition. + """ + obj = {} + object_size = 0 + pos = 0 + for (name, stype) in definition: + object_size += calcsize(stype) + obj[name] = unpack(stype, data[pos:pos + calcsize(stype)])[0] + pos += calcsize(stype) + obj["object_size"] = object_size + obj["raw_data"] = data + return obj + + +class structhelper: + pos = 0 + + def __init__(self, data, pos=0): + self.pos = 0 + self.data = data + + def qword(self, big=False): + e = ">" if big else "<" + dat = unpack(e + "Q", self.data[self.pos:self.pos + 8])[0] + self.pos += 8 + return dat + + def dword(self, big=False): + e = ">" if big else "<" + dat = unpack(e + "I", self.data[self.pos:self.pos + 4])[0] + self.pos += 4 + return dat + + def dwords(self, dwords=1, big=False): + e = ">" if big else "<" + dat = unpack(e + str(dwords) + "I", self.data[self.pos:self.pos + 4 * dwords]) + self.pos += 4 * dwords + return dat + + def qwords(self, qwords=1, big=False): + e = ">" if big else "<" + dat = unpack(e + str(qwords) + "Q", self.data[self.pos:self.pos + 8 * qwords]) + self.pos += 8 * qwords + return dat + + def short(self, big=False): + e = ">" if big else "<" + dat = unpack(e + "H", self.data[self.pos:self.pos + 2])[0] + self.pos += 2 + return dat + + def shorts(self, shorts, big=False): + e = ">" if big else "<" + dat = unpack(e + str(shorts) + "H", self.data[self.pos:self.pos + 2 * shorts]) + self.pos += 2 * shorts + return dat + + def bytes(self, rlen=1): + dat = self.data[self.pos:self.pos + rlen] + self.pos += rlen + if rlen == 1: return dat[0] + return dat + + def string(self, rlen=1): + dat = self.data[self.pos:self.pos + rlen] + self.pos += rlen + return dat + + def getpos(self): + return self.pos + + def seek(self, pos): + self.pos = pos + + +AB_FLAG_OFFSET = 6 +AB_PARTITION_ATTR_SLOT_ACTIVE = (0x1 << 2) +AB_PARTITION_ATTR_BOOT_SUCCESSFUL = (0x1 << 6) +AB_PARTITION_ATTR_UNBOOTABLE = (0x1 << 7) +AB_SLOT_ACTIVE_VAL = 0x3F +AB_SLOT_INACTIVE_VAL = 0x0 +AB_SLOT_ACTIVE = 1 +AB_SLOT_INACTIVE = 0 class gpt(metaclass=LogBase): @@ -43,6 +217,10 @@ class gpt(metaclass=LogBase): self.flags = sh.qword() self.name = sh.string(72) + def create(self): + val = pack("16s16sQQQ72s", self.type, self.unique, self.first_lba, self.last_lba, self.flags, self.name) + return val + class efi_type(Enum): EFI_UNUSED = 0x00000000 EFI_MBR = 0x024DEE41 @@ -139,18 +317,19 @@ class gpt(metaclass=LogBase): self.totalsectors = None self.header = None self.sectorsize = None - self.partentries = {} + self.partentries = [] self.error = self.__logger.error self.__logger.setLevel(loglevel) if loglevel == logging.DEBUG: logfilename = "log.txt" - fh = logging.FileHandler(logfilename) + fh = logging.FileHandler(logfilename, encoding="utf-8") self.__logger.addHandler(fh) def parseheader(self, gptdata, sectorsize=512): return self.gpt_header(gptdata[sectorsize:sectorsize + 0x5C]) + def parse(self, gptdata, sectorsize=512): self.header = self.gpt_header(gptdata[sectorsize:sectorsize + 0x5C]) self.sectorsize = sectorsize @@ -176,6 +355,7 @@ class gpt(metaclass=LogBase): sectors = 0 type = b"" name = "" + entryoffset = 0 num_part_entries = self.header.num_part_entries @@ -194,6 +374,7 @@ class gpt(metaclass=LogBase): pa.sector = partentry.first_lba pa.sectors = partentry.last_lba - partentry.first_lba + 1 pa.flags = partentry.flags + pa.entryoffset = start + (idx * entrysize) type = int(unpack("> (AB_FLAG_OFFSET*8))&0xFF) & AB_PARTITION_ATTR_SLOT_ACTIVE == AB_PARTITION_ATTR_SLOT_ACTIVE + mstr += ("{:20} Offset 0x{:016x}, Length 0x{:016x}, Flags 0x{:016x}, UUID {}, Type {}, Active {}\n".format( partition.name + ":", partition.sector * self.sectorsize, partition.sectors * self.sectorsize, - partition.flags, partition.unique, partition.type)) + partition.flags, partition.unique, partition.type, active)) mstr += ("\nTotal disk size:0x{:016x}, sectors:0x{:016x}\n".format(self.totalsectors * self.sectorsize, self.totalsectors)) return mstr @@ -227,8 +409,7 @@ class gpt(metaclass=LogBase): partofsingleimage = "false" readbackverify = "false" sparse = "false" - for selpart in self.partentries: - partition = self.partentries[selpart] + for partition in self.partentries: filename = partition.name + ".bin" mstr += f"\t\n" mstr += "" - wf.write(bytes(mstr, 'utf-8')) + wf.write(bytes(mstr, "utf-8")) print(f"Wrote partition xml as {fname}") def print_gptfile(self, filename): @@ -294,23 +475,72 @@ class gpt(metaclass=LogBase): res = self.print_gptfile(os.path.join("TestFiles", "gpt_sm8180x.bin")) assert res, "GPT Partition wasn't decoded properly" + def patch(self, data:bytes, partitionname="boot", active: bool = True): + try: + rf = BytesIO(data) + for sectorsize in [512, 4096]: + result = self.parse(data, sectorsize) + if result: + for rname in self.partentries: + if partitionname.lower() == rname.lower(): + partition = self.partentries[rname] + rf.seek(partition.entryoffset) + sdata = rf.read(self.header.part_entry_size) + partentry = self.gpt_partition(sdata) + flags = partentry.flags + if active: + flags |= AB_PARTITION_ATTR_SLOT_ACTIVE << (AB_FLAG_OFFSET*8) + else: + flags |= AB_PARTITION_ATTR_UNBOOTABLE << (AB_FLAG_OFFSET*8) + partentry.flags = flags + data = partentry.create() + return data, partition.entryoffset + break + return None, None + except Exception as e: + self.error(str(e)) + return None, None + + def get_flag(self, filename, imagename): + if "." in imagename: + imagename = imagename[:imagename.find(".")] + try: + with open(filename, "rb") as rf: + if os.stat(filename).st_size > 0x200000: + print("Error: GPT is too big or no GPT at all.") + return None + data = rf.read() + return self.get_flag_data(data, imagename) + except FileNotFoundError: + print(f"File not found : {filename}") + return None, None + + def get_flag_data(self, gpt: bytes, imagename: str): + for sectorsize in [512, 4096]: + result = self.parse(gpt, sectorsize) + if result: + for partition in self.partentries: + if imagename in partition.name.lower(): + return partition.sector, sectorsize + return None, None + if __name__ == "__main__": parser = argparse.ArgumentParser(description="GPT utils") - subparsers = parser.add_subparsers(dest="command", help='sub-command help') + parser.add_argument("image", help="The path of the GPT disk image") + subparsers = parser.add_subparsers(dest="command", help="sub-command help") parser_print = subparsers.add_parser("print", help="Print the gpt table") - parser_print.add_argument("image", help="The path of the GPT disk image") - parser_test = subparsers.add_parser("test", help="Run self-test") - + parser_patch = subparsers.add_parser("patch", help="Set active boot slot") + parser_patch.add_argument("partition", help="Extract specific partitions (separated by comma)") + parser_patch.add_argument("-active", action="store_true", help="Set bootable") parser_extract = subparsers.add_parser("extract", help="Extract the partitions") - parser_extract.add_argument("image", help="The path of the GPT disk image") parser_extract.add_argument("-out", "-o", help="The path to extract the partitions") parser_extract.add_argument("-partition", "-p", help="Extract specific partitions (separated by comma)") args = parser.parse_args() - if args.command not in ["print", "extract", "test"]: + if args.command not in ["print", "extract", "test", "patch"]: parser.error("Command is mandatory") gp = gpt() @@ -321,6 +551,22 @@ if __name__ == "__main__": gp.print_gptfile(args.image) elif args.command == "test": gp.test_gpt() + elif args.command == "patch": + partitition = args.partition + active = args.active + filesize = os.stat(args.image).st_size + with open(args.image, "rb") as rf: + size = min(32 * 4096, filesize) + data = bytearray(rf.read(size)) + pdata, offset = gp.patch(data,partitition, active=active) + if data is not None: + data[offset:offset + len(pdata)] = pdata + wfilename = args.image + ".patched" + with open(wfilename,"wb") as wf: + wf.write(data) + print(f"Successfully wrote patched gpt to {wfilename}") + else: + print("Error on setting bootable mode") elif args.command == "extract": if not os.path.exists(args.image): print(f"File {args.image} does not exist. Aborting.") @@ -335,26 +581,33 @@ if __name__ == "__main__": ssize = sectorsize break if ssize is not None: - for selpart in gp.partentries: - partition = gp.partentries[selpart] - if args.partition is not None: - if partition != args.partition: - continue - name = partition.name - start = partition.sector * ssize - length = partition.sectors * ssize - out = args.out - if out is None: - out = "." - if not os.path.exists(out): - os.makedirs(out) - filename = os.path.join(out, name) - rf.seek(start) - bytestoread = length - with open(filename, "wb", buffering=1024 * 1024) as wf: - while bytestoread > 0: - size = min(bytestoread, 0x200000) - rf.read(size) - wf.write(size) - bytestoread -= size - print(f"Extracting {name} to {filename} at {hex(start)}, length {hex(length)}") + if args.partition == "gpt": + print(f"Extracting gpt to gpt.bin at {hex(0)}, length {hex(32 * ssize)}") + rf.seek(0) + data = rf.read(32 * ssize) + with open("gpt.bin", "wb") as wf: + wf.write(data) + else: + for partition in gp.partentries: + if args.partition is not None: + if partition.name.lower() != args.partition: + continue + name = partition.name + start = partition.sector * ssize + length = partition.sectors * ssize + out = args.out + if out is None: + out = "." + else: + if not os.path.exists(out): + os.makedirs(out) + filename = os.path.join(out, name) + ".bin" + print(f"Extracting {name} to {filename} at {hex(start)}, length {hex(length)}") + rf.seek(start) + bytestoread = length + with open(filename, "wb", buffering=1024 * 1024) as wf: + while bytestoread > 0: + size = min(bytestoread, 0x200000) + data = rf.read(size) + wf.write(data) + bytestoread -= size diff --git a/edlclient/Library/hdlc.py b/edlclient/Library/hdlc.py index 4e4ce95..e48ac1d 100755 --- a/edlclient/Library/hdlc.py +++ b/edlclient/Library/hdlc.py @@ -134,15 +134,15 @@ class hdlc: replybuf = bytearray() if timeout is None: timeout = self.timeout - tmp = self.cdc.read(MAX_PACKET_LEN, timeout) + tmp = self.cdc.read(timeout=timeout) if tmp == bytearray(): - return 0 + return b"" if tmp == b"": - return 0 + return b"" retry = 0 while tmp[-1] != 0x7E: time.sleep(0.01) - tmp += self.cdc.read(MAX_PACKET_LEN, timeout) + tmp += self.cdc.read(timeout=timeout) retry += 1 if retry > 5: break @@ -150,13 +150,15 @@ class hdlc: data = unescape(replybuf) # print(hexlify(data)) if len(data) > 3: + if data[0]==0x7E: + data=data[1:] crc16val = crc16(0xFFFF, data[:-3]) reccrc = int(data[-3]) + (int(data[-2]) << 8) if crc16val != reccrc: return -1 else: time.sleep(0.01) - data = self.cdc.read(MAX_PACKET_LEN, timeout) + data = self.cdc.read(timeout=timeout) if len(data) > 3: crc16val = crc16(0xFFFF, data[:-3]) reccrc = int(data[-3]) + (int(data[-2]) << 8) @@ -169,15 +171,15 @@ class hdlc: replybuf = bytearray() if timeout is None: timeout = self.timeout - tmp = self.cdc.read(MAX_PACKET_LEN, timeout) + tmp = self.cdc.read(timeout=timeout) if tmp == bytearray(): - return 0 + return b"" if tmp == b"": - return 0 + return b"" retry = 0 while tmp[-1] != 0x7E: # time.sleep(0.05) - tmp += self.cdc.read(MAX_PACKET_LEN, timeout) + tmp += self.cdc.read(timeout=timeout) retry += 1 if retry > 5: break @@ -190,7 +192,7 @@ class hdlc: return data[:-3] else: time.sleep(0.5) - data = self.cdc.read(MAX_PACKET_LEN, timeout) + data = self.cdc.read(timeout=timeout) if len(data) > 3: # crc16val = self.crc16(0xFFFF, data[:-3]) # reccrc = int(data[-3]) + (int(data[-2]) << 8) @@ -212,6 +214,7 @@ class hdlc: if isinstance(outdata, str): outdata = bytes(outdata, 'utf-8') packet = convert_cmdbuf(bytearray(outdata)) + self.cdc.flush() if self.send_unframed_buf(packet, prefixflag): if nocrc: return self.receive_reply_nocrc() @@ -229,12 +232,9 @@ class hdlc: if len(pktbuf) == 0: return logging.error("Error: %s " % descr) - if pktbuf[1] == 0x0e: pktbuf[-4] = 0 # puts(pktbuf+2) ret = self.receive_reply() errorcode = unpack("> 8) & 0xff pid = nandid & 0xff - + self.settings.flash_fid = fid + self.settings.flash_pid = pid self.settings.flash_mfr = "" for info in nand_manuf_ids: if info[0] == pid: diff --git a/edlclient/Library/sahara.py b/edlclient/Library/sahara.py index 71a2f6e..238b044 100755 --- a/edlclient/Library/sahara.py +++ b/edlclient/Library/sahara.py @@ -13,279 +13,11 @@ parent_dir = os.path.dirname(current_dir) sys.path.insert(0, parent_dir) from edlclient.Library.utils import read_object, print_progress, rmrf, LogBase from edlclient.Config.qualcomm_config import sochw, msmids, root_cert_hash - - - - -def convertmsmid(msmid): - msmiddb = [] - if int(msmid, 16) & 0xFF == 0xe1 or msmid == '00000000': - return [msmid] - socid = int(msmid, 16) >> 16 - if socid in sochw: - names = sochw[socid].split(",") - for name in names: - for ids in msmids: - if msmids[ids] == name: - rmsmid = hex(ids)[2:].lower() - while len(rmsmid) < 8: - rmsmid = '0' + rmsmid - msmiddb.append(rmsmid) - return msmiddb - +from edlclient.Library.loader_db import loader_utils +from edlclient.Library.sahara_defs import ErrorDesc, cmd_t, exec_cmd_t, sahara_mode_t, status_t, \ + CommandHandler, SAHARA_VERSION class sahara(metaclass=LogBase): - SAHARA_VERSION = 2 - SAHARA_MIN_VERSION = 1 - - class cmd: - SAHARA_HELLO_REQ = 0x1 - SAHARA_HELLO_RSP = 0x2 - SAHARA_READ_DATA = 0x3 - SAHARA_END_TRANSFER = 0x4 - SAHARA_DONE_REQ = 0x5 - SAHARA_DONE_RSP = 0x6 - SAHARA_RESET_REQ = 0x7 - SAHARA_RESET_RSP = 0x8 - SAHARA_MEMORY_DEBUG = 0x9 - SAHARA_MEMORY_READ = 0xA - SAHARA_CMD_READY = 0xB - SAHARA_SWITCH_MODE = 0xC - SAHARA_EXECUTE_REQ = 0xD - SAHARA_EXECUTE_RSP = 0xE - SAHARA_EXECUTE_DATA = 0xF - SAHARA_64BIT_MEMORY_DEBUG = 0x10 - SAHARA_64BIT_MEMORY_READ = 0x11 - SAHARA_64BIT_MEMORY_READ_DATA = 0x12 - SAHARA_RESET_STATE_MACHINE_ID = 0x13 - - class exec_cmd: - SAHARA_EXEC_CMD_NOP = 0x00 - SAHARA_EXEC_CMD_SERIAL_NUM_READ = 0x01 - SAHARA_EXEC_CMD_MSM_HW_ID_READ = 0x02 - SAHARA_EXEC_CMD_OEM_PK_HASH_READ = 0x03 - SAHARA_EXEC_CMD_SWITCH_TO_DMSS_DLOAD = 0x04 - SAHARA_EXEC_CMD_SWITCH_TO_STREAM_DLOAD = 0x05 - SAHARA_EXEC_CMD_READ_DEBUG_DATA = 0x06 - SAHARA_EXEC_CMD_GET_SOFTWARE_VERSION_SBL = 0x07 - - class sahara_mode: - SAHARA_MODE_IMAGE_TX_PENDING = 0x0 - SAHARA_MODE_IMAGE_TX_COMPLETE = 0x1 - SAHARA_MODE_MEMORY_DEBUG = 0x2 - SAHARA_MODE_COMMAND = 0x3 - - class status: - SAHARA_STATUS_SUCCESS = 0x00 # Invalid command received in current state - SAHARA_NAK_INVALID_CMD = 0x01 # Protocol mismatch between host and target - SAHARA_NAK_PROTOCOL_MISMATCH = 0x02 # Invalid target protocol version - SAHARA_NAK_INVALID_TARGET_PROTOCOL = 0x03 # Invalid host protocol version - SAHARA_NAK_INVALID_HOST_PROTOCOL = 0x04 # Invalid packet size received - SAHARA_NAK_INVALID_PACKET_SIZE = 0x05 # Unexpected image ID received - SAHARA_NAK_UNEXPECTED_IMAGE_ID = 0x06 # Invalid image header size received - SAHARA_NAK_INVALID_HEADER_SIZE = 0x07 # Invalid image data size received - SAHARA_NAK_INVALID_DATA_SIZE = 0x08 # Invalid image type received - SAHARA_NAK_INVALID_IMAGE_TYPE = 0x09 # Invalid tranmission length - SAHARA_NAK_INVALID_TX_LENGTH = 0x0A # Invalid reception length - SAHARA_NAK_INVALID_RX_LENGTH = 0x0B # General transmission or reception error - SAHARA_NAK_GENERAL_TX_RX_ERROR = 0x0C # Error while transmitting READ_DATA packet - SAHARA_NAK_READ_DATA_ERROR = 0x0D # Cannot receive specified number of program headers - SAHARA_NAK_UNSUPPORTED_NUM_PHDRS = 0x0E # Invalid data length received for program headers - SAHARA_NAK_INVALID_PDHR_SIZE = 0x0F # Multiple shared segments found in ELF image - SAHARA_NAK_MULTIPLE_SHARED_SEG = 0x10 # Uninitialized program header location - SAHARA_NAK_UNINIT_PHDR_LOC = 0x11 # Invalid destination address - SAHARA_NAK_INVALID_DEST_ADDR = 0x12 # Invalid data size received in image header - SAHARA_NAK_INVALID_IMG_HDR_DATA_SIZE = 0x13 # Invalid ELF header received - SAHARA_NAK_INVALID_ELF_HDR = 0x14 # Unknown host error received in HELLO_RESP - SAHARA_NAK_UNKNOWN_HOST_ERROR = 0x15 # Timeout while receiving data - SAHARA_NAK_TIMEOUT_RX = 0x16 # Timeout while transmitting data - SAHARA_NAK_TIMEOUT_TX = 0x17 # Invalid mode received from host - SAHARA_NAK_INVALID_HOST_MODE = 0x18 # Invalid memory read access - SAHARA_NAK_INVALID_MEMORY_READ = 0x19 # Host cannot handle read data size requested - SAHARA_NAK_INVALID_DATA_SIZE_REQUEST = 0x1A # Memory debug not supported - SAHARA_NAK_MEMORY_DEBUG_NOT_SUPPORTED = 0x1B # Invalid mode switch - SAHARA_NAK_INVALID_MODE_SWITCH = 0x1C # Failed to execute command - SAHARA_NAK_CMD_EXEC_FAILURE = 0x1D # Invalid parameter passed to command execution - SAHARA_NAK_EXEC_CMD_INVALID_PARAM = 0x1E # Unsupported client command received - SAHARA_NAK_EXEC_CMD_UNSUPPORTED = 0x1F # Invalid client command received for data response - SAHARA_NAK_EXEC_DATA_INVALID_CLIENT_CMD = 0x20 # Failed to authenticate hash table - SAHARA_NAK_HASH_TABLE_AUTH_FAILURE = 0x21 # Failed to verify hash for a given segment of ELF image - SAHARA_NAK_HASH_VERIFICATION_FAILURE = 0x22 # Failed to find hash table in ELF image - SAHARA_NAK_HASH_TABLE_NOT_FOUND = 0x23 # Target failed to initialize - SAHARA_NAK_TARGET_INIT_FAILURE = 0x24 # Failed to authenticate generic image - SAHARA_NAK_IMAGE_AUTH_FAILURE = 0x25 # Invalid ELF hash table size. Too bit or small. - SAHARA_NAK_INVALID_IMG_HASH_TABLE_SIZE = 0x26 - SAHARA_NAK_MAX_CODE = 0x7FFFFFFF # To ensure 32-bits wide */ - - ErrorDesc = { - 0x00: "Invalid command received in current state", - 0x01: "Protocol mismatch between host and target", - 0x02: "Invalid target protocol version", - 0x03: "Invalid host protocol version", - 0x04: "Invalid packet size received", - 0x05: "Unexpected image ID received", - 0x06: "Invalid image header size received", - 0x07: "Invalid image data size received", - 0x08: "Invalid image type received", - 0x09: "Invalid tranmission length", - 0x0A: "Invalid reception length", - 0x0B: "General transmission or reception error", - 0x0C: "Error while transmitting READ_DATA packet", - 0x0D: "Cannot receive specified number of program headers", - 0x0E: "Invalid data length received for program headers", - 0x0F: "Multiple shared segments found in ELF image", - 0x10: "Uninitialized program header location", - 0x11: "Invalid destination address", - 0x12: "Invalid data size received in image header", - 0x13: "Invalid ELF header received", - 0x14: "Unknown host error received in HELLO_RESP", - 0x15: "Timeout while receiving data", - 0x16: "Timeout while transmitting data", - 0x17: "Invalid mode received from host", - 0x18: "Invalid memory read access", - 0x19: "Host cannot handle read data size requested", - 0x1A: "Memory debug not supported", - 0x1B: "Invalid mode switch", - 0x1C: "Failed to execute command", - 0x1D: "Invalid parameter passed to command execution", - 0x1E: "Unsupported client command received", - 0x1F: "Invalid client command received for data response", - 0x20: "Failed to authenticate hash table", - 0x21: "Failed to verify hash for a given segment of ELF image", - 0x22: "Failed to find hash table in ELF image", - 0x23: "Target failed to initialize", - 0x24: "Failed to authenticate generic image", - 0x25: "Invalid ELF hash table size. Too bit or small.", - 0x26: "Invalid IMG Hash Table Size" - } - - def init_loader_db(self): - loaderdb = {} - for (dirpath, dirnames, filenames) in os.walk(os.path.join(parent_dir,"..","Loaders")): - for filename in filenames: - fn = os.path.join(dirpath, filename) - found = False - for ext in [".bin", ".mbn", ".elf"]: - if ext in filename[-4:]: - found = True - break - if not found: - continue - try: - hwid = filename.split("_")[0].lower() - msmid = hwid[:8] - devid = hwid[8:] - pkhash = filename.split("_")[1].lower() - for msmid in convertmsmid(msmid): - mhwid = msmid + devid - mhwid = mhwid.lower() - if mhwid not in loaderdb: - loaderdb[mhwid] = {} - if pkhash not in loaderdb[mhwid]: - loaderdb[mhwid][pkhash] = fn - else: - loaderdb[mhwid][pkhash].append(fn) - except Exception as e: # pylint: disable=broad-except - self.debug(str(e)) - continue - self.loaderdb = loaderdb - return loaderdb - - def get_error_desc(self, status): - if status in self.ErrorDesc: - return "Error: " + self.ErrorDesc[status] - else: - return "Unknown error" - - pkt_hello_req = [ - ('cmd', 'I'), - ('len', 'I'), - ('version', 'I'), - ('version_min', 'I'), - ('max_cmd_len', 'I'), - ('mode', 'I'), - ('res1', 'I'), - ('res2', 'I'), - ('res3', 'I'), - ('res4', 'I'), - ('res5', 'I'), - ('res6', 'I')] - - pkt_cmd_hdr = [ - ('cmd', 'I'), - ('len', 'I') - ] - - pkt_read_data = [ - ('id', 'I'), - ('data_offset', 'I'), - ('data_len', 'I') - ] - - pkt_read_data_64 = [ - ('id', 'Q'), - ('data_offset', 'Q'), - ('data_len', 'Q') - ] - - pkt_memory_debug = [ - ('memory_table_addr', 'I'), - ('memory_table_length', 'I') - ] - - pkt_memory_debug_64 = [ - ('memory_table_addr', 'Q'), - ('memory_table_length', 'Q') - ] - ''' - execute_cmd=[ - ('cmd', 'I'), - ('len', 'I'), - ('client_cmd','I') - ] - ''' - - pkt_execute_rsp_cmd = [ - ('cmd', 'I'), - ('len', 'I'), - ('client_cmd', 'I'), - ('data_len', 'I') - ] - - pkt_image_end = [ - ('id', 'I'), - ('status', 'I') - ] - - pkt_done = [ - ('cmd', 'I'), - ('len', 'I'), - ('status', 'I') - ] - - pbl_info = [ - ('serial', 'I'), - ('msm_id', 'I'), - ('pk_hash', '32s'), - ('pbl_sw', 'I') - ] - - parttbl = [ - ('save_pref', 'I'), - ('mem_base', 'I'), - ('length', 'I'), - ('desc', '20s'), - ('filename', '20s') - ] - - parttbl_64bit = [ - ('save_pref', 'Q'), - ('mem_base', 'Q'), - ('length', 'Q'), - ('desc', '20s'), - ('filename', '20s') - ] - def __init__(self, cdc, loglevel): self.cdc = cdc self.__logger = self.__logger @@ -294,7 +26,6 @@ class sahara(metaclass=LogBase): self.error = self.__logger.error self.warning = self.__logger.warning self.id = None - self.loaderdb = None self.version = 2.1 self.programmer = None self.mode = "" @@ -312,8 +43,9 @@ class sahara(metaclass=LogBase): self.msm_str = None self.bit64 = False self.pktsize = None - - self.init_loader_db() + self.ch = CommandHandler() + self.loader_handler=loader_utils(loglevel=loglevel) + self.loaderdb = self.loader_handler.init_loader_db() self.__logger.setLevel(loglevel) if loglevel == logging.DEBUG: @@ -321,50 +53,51 @@ class sahara(metaclass=LogBase): fh = logging.FileHandler(logfilename) self.__logger.addHandler(fh) + def get_error_desc(self, status): + if status in ErrorDesc: + return "Error: " + ErrorDesc[status] + else: + return "Unknown error" + def get_rsp(self): - data = [] try: - v = self.cdc.read() - if v == b'': - return [None, None] - if b" 1: if v[0] == 0x01: - cmd = read_object(v[0:0x2 * 0x4], self.pkt_cmd_hdr) - if cmd['cmd'] == self.cmd.SAHARA_HELLO_REQ: - data = read_object(v[0x0:0xC * 0x4], self.pkt_hello_req) - self.pktsize = data['max_cmd_len'] - self.version = float(str(data['version']) + "." + str(data['version_min'])) - return ["sahara", data] - elif v[0] == self.cmd.SAHARA_END_TRANSFER: - return ["sahara", None] + pkt = self.ch.pkt_cmd_hdr(v) + if pkt.cmd == cmd_t.SAHARA_HELLO_REQ: + rsp = self.ch.pkt_hello_req(v) + self.pktsize = rsp.max_cmd_len + self.version = float(str(rsp.version) + "." + str(rsp.version_min)) + self.info(f"Protocol version: {self.version}") + return {"mode":"sahara", "cmd":cmd_t.SAHARA_HELLO_REQ, "data":rsp} + elif pkt.cmd == cmd_t.SAHARA_END_TRANSFER: + rsp = self.ch.pkt_image_end(v) + return {"mode":"sahara", "cmd":cmd_t.SAHARA_END_TRANSFER, "data":rsp} elif b"" self.cdc.write(data) - res = self.cdc.read() - if res == b"": - try: - data = b"\x7E\x06\x4E\x95\x7E" # Streaming nop - self.cdc.write(data) - res = self.cdc.read() - if b"\x7E\x0D\x16\x00\x00\x00\x00" in res or b"Invalid Command" in res: - return ["nandprg", None] - else: - return ["", None] - except Exception as e: # pylint: disable=broad-except - self.error(str(e)) - return ["", None] + res = self.cdc.read(timeout=1) if b" 0 and res[0] == self.cmd.SAHARA_END_TRANSFER: - print("Device is in Sahara error state, please reboot the device.") - return ["sahara", None] - else: + return {"mode": "firehose"} + elif len(res)> 0: + if res[0] == 0x7E: + return {"mode":"nandprg"} + elif res[0] == cmd_t.SAHARA_END_TRANSFER: + rsp = self.ch.pkt_image_end(res) + return {"mode": "sahara", "cmd": cmd_t.SAHARA_END_TRANSFER, "data": rsp} + elif res == b"": data = b"\x7E\x11\x00\x12\x00\xA0\xE3\x00\x00\xC1\xE5\x01\x40\xA0\xE3\x1E\xFF\x2F\xE1\x4B\xD9\x7E" self.cdc.write(data) res = self.cdc.read() if len(res) > 0 and res[1] == 0x12: - return ["nandprg", None] - else: - self.cmd_modeswitch(self.sahara_mode.SAHARA_MODE_COMMAND) - return ["sahara", None] + return {"mode":"nandprg"} + elif len(res) == 0: + print("Device is in Sahara error state, please reboot the device.") + return {"mode": "error"} except Exception as e: # pylint: disable=broad-except self.error(str(e)) - - self.cmd_modeswitch(self.sahara_mode.SAHARA_MODE_MEMORY_DEBUG) - cmd, pkt = self.get_rsp() - if None in [cmd , pkt]: - return ["", None] - return ["sahara", pkt] + return {"mode": "error"} def enter_command_mode(self): - if not self.cmd_hello(self.sahara_mode.SAHARA_MODE_COMMAND): - return False - cmd, pkt = self.get_rsp() - if cmd["cmd"] == self.cmd.SAHARA_CMD_READY: - return True - elif "status" in pkt: - self.error(self.get_error_desc(pkt["status"])) + if not self.cmd_hello(sahara_mode_t.SAHARA_MODE_COMMAND): return False + res = self.get_rsp() + if "cmd" in res: + if res["cmd"] == cmd_t.SAHARA_END_TRANSFER: + if "data" in res: + pkt = res["data"] + self.error(self.get_error_desc(pkt.status)) + return False + elif res["cmd"] == cmd_t.SAHARA_CMD_READY: + return True return False def cmdexec_nop(self): - res = self.cmd_exec(self.exec_cmd.SAHARA_EXEC_CMD_NOP) + res = self.cmd_exec(exec_cmd_t.SAHARA_EXEC_CMD_NOP) return res def cmdexec_get_serial_num(self): - res = self.cmd_exec(self.exec_cmd.SAHARA_EXEC_CMD_SERIAL_NUM_READ) + res = self.cmd_exec(exec_cmd_t.SAHARA_EXEC_CMD_SERIAL_NUM_READ) return unpack(" 0: try: @@ -689,13 +426,13 @@ class sahara(metaclass=LogBase): return True def debug_mode(self, dump_partitions=None): - if not self.cmd_hello(self.sahara_mode.SAHARA_MODE_MEMORY_DEBUG): + if not self.cmd_hello(sahara_mode_t.SAHARA_MODE_MEMORY_DEBUG): return False if os.path.exists("memory"): rmrf("memory") os.mkdir("memory") cmd, pkt = self.get_rsp() - if cmd["cmd"] == self.cmd.SAHARA_MEMORY_DEBUG or cmd["cmd"] == self.cmd.SAHARA_64BIT_MEMORY_DEBUG: + if cmd["cmd"] == cmd_t.SAHARA_MEMORY_DEBUG or cmd["cmd"] == cmd_t.SAHARA_64BIT_MEMORY_DEBUG: memory_table_addr = pkt["memory_table_addr"] memory_table_length = pkt["memory_table_length"] if self.bit64: @@ -709,8 +446,7 @@ class sahara(metaclass=LogBase): num_entries = len(ptbldata) // pktsize partitions = [] for id_entry in range(0, num_entries): - pd = read_object(ptbldata[id_entry * pktsize:(id_entry * pktsize) + pktsize], - self.parttbl_64bit) + pd = self.ch.parttbl_64bit(ptbldata[id_entry * pktsize:(id_entry * pktsize) + pktsize]) desc = pd["desc"].replace(b"\x00", b"").decode('utf-8') filename = pd["filename"].replace(b"\x00", b"").decode('utf-8') if dump_partitions and filename not in dump_partitions: @@ -738,7 +474,7 @@ class sahara(metaclass=LogBase): num_entries = len(ptbldata) // pktsize partitions = [] for id_entry in range(0, num_entries): - pd = read_object(ptbldata[id_entry * pktsize:(id_entry * pktsize) + pktsize], self.parttbl) + pd = self.parttbl(ptbldata[id_entry * pktsize:(id_entry * pktsize) + pktsize]) desc = pd["desc"].replace(b"\x00", b"").decode('utf-8') filename = pd["filename"].replace(b"\x00", b"").decode('utf-8') if dump_partitions and filename not in dump_partitions: @@ -769,80 +505,99 @@ class sahara(metaclass=LogBase): self.error(str(e)) sys.exit() - if not self.cmd_hello(self.sahara_mode.SAHARA_MODE_IMAGE_TX_PENDING): + if not self.cmd_hello(sahara_mode_t.SAHARA_MODE_IMAGE_TX_PENDING): return "" try: datalen = len(programmer) done = False + loop = 0 while datalen > 0 or done: - cmd, pkt = self.get_rsp() - if cmd == -1 or pkt == -1: + resp = self.get_rsp() + if "cmd" in resp: + cmd = resp["cmd"] + else: + cmd = None + if cmd == cmd_t.SAHARA_DONE_REQ: if self.cmd_done(): return self.mode # Do NOT remove else: self.error("Timeout while uploading loader. Wrong loader ?") return "" - if cmd["cmd"] == self.cmd.SAHARA_64BIT_MEMORY_READ_DATA: - self.bit64 = True - elif cmd["cmd"] == self.cmd.SAHARA_READ_DATA: - self.bit64 = False - elif cmd["cmd"] == self.cmd.SAHARA_END_TRANSFER: - if pkt["status"] == self.status.SAHARA_STATUS_SUCCESS: - self.cmd_done() + elif cmd in [cmd_t.SAHARA_64BIT_MEMORY_READ_DATA,cmd_t.SAHARA_READ_DATA]: + if cmd == cmd_t.SAHARA_64BIT_MEMORY_READ_DATA: + self.bit64 = True + if loop == 0: + self.info("64-Bit mode detected.") + elif cmd == cmd_t.SAHARA_READ_DATA: + self.bit64 = False + if loop == 0: + self.info("32-Bit mode detected.") + pkt = resp["data"] + self.id = pkt.id + if self.id == 0x7: + self.mode = "nandprg" + if loop == 0: + self.info("NAND mode detected, uploading...") + elif self.id == 0xB: + self.mode = "enandprg" + if loop == 0: + self.info("eNAND mode detected, uploading...") + elif self.id >= 0xC: + self.mode = "firehose" + if loop == 0: + self.info("Firehose mode detected, uploading...") + else: + self.error(f"Unknown sahara id: {self.id}") + return "error" + loop+=1 + data_offset = pkt.data_offset + data_len = pkt.data_len + if data_offset + data_len > len(programmer): + while len(programmer) < data_offset + data_len: + programmer += b"\xFF" + data_to_send = programmer[data_offset:data_offset + data_len] + self.cdc.write(data_to_send) + datalen -= data_len + elif cmd == cmd_t.SAHARA_END_TRANSFER: + pkt = resp["data"] + if pkt.status == status_t.SAHARA_STATUS_SUCCESS: + if self.cmd_done(): + self.info("Loader successfully uploaded.") + else: + self.error("Error on uploading Loader.") + sys.exit(1) return self.mode else: - return "" - elif "status" in pkt: - self.error(self.get_error_desc(pkt["status"])) - return "" + self.error(self.get_error_desc(pkt.status)) + return "error" else: - self.error("Unexpected error on uploading") - return "" - self.id = pkt["id"] - if self.id == 0x7: - self.mode = "nandprg" - elif self.id == 0xB: - self.mode = "enandprg" - elif self.id >= 0xC: - self.mode = "firehose" - - data_offset = pkt["data_offset"] - data_len = pkt["data_len"] - if data_offset + data_len > len(programmer): - while len(programmer) < data_offset + data_len: - programmer += b"\xFF" - data_to_send = programmer[data_offset:data_offset + data_len] - self.cdc.write(data_to_send) - datalen -= data_len - self.info("Loader uploaded.") - cmd, pkt = self.get_rsp() - if cmd["cmd"] == self.cmd.SAHARA_END_TRANSFER: - if pkt["status"] == self.status.SAHARA_STATUS_SUCCESS: - self.cmd_done() - return self.mode - return "" + self.error("Unknown response received on uploading loader.") + sys.exit(1) except Exception as e: # pylint: disable=broad-except self.error("Unexpected error on uploading, maybe signature of loader wasn't accepted ?\n" + str(e)) return "" def cmd_modeswitch(self, mode): - data = pack(" 0 and resp[0] == 0x16: time.sleep(0.5) return True - if len(resp) > 2 and resp[1] == 0x16: - time.sleep(0.5) - return True - self.error("Error on closing stream") + if errmode: + self.error("Error on closing stream") return False def send_section_header(self, name): # 0x1b open muliimage, 0xe for user-defined partition - resp = self.send(b"\x1b\x0e" + name + b"\x00") - if resp[1] == 0x1c: + resp = self.send(b"\x1b\x0e" + bytes("0:"+name, 'utf-8') + b"\x00") + if resp[0] == 0x1c: return True self.error("Error on sending section header") return False def enter_flash_mode(self, ptable=None): + self.info("Entering flash mode ...") self.secure_mode() - self.qclose(0) - if ptable is None: - self.send_ptable(ptable, 0) # 1 for fullflash + if not self.qclose(0): + data = self.cdc.usbread(timeout=0) + if ptable is not None: + if self.send_ptable(ptable, 0): # 1 for fullflash + return True + else: + return True + return False - def write_flash(self, partname, filename): + def write_flash(self, lba:int=0, partname="", filename="", info=True): wbsize = 1024 filesize = os.stat(filename).st_size + total = filesize + progbar = progress(1) + progbar.show_progress(prefix="Write", pos=0, total=total, display=info) with open(filename, 'rb') as rf: if self.send_section_header(partname): - adr = 0 + adr = lba while filesize > 0: subdata = rf.read(wbsize) - if len(subdata) < wbsize + 1: - subdata += b'\xFF' * ((wbsize + 1) - len(subdata)) + if len(subdata)<1024: + subdata += (1024-len(subdata))*b'\xFF' scmd = b"\x07" + pack(" old: - print_progress(prog, 100, prefix='Progress:', suffix='Complete (Sector %d)' % sector, - bar_length=50) - old = prog + progbar.show_progress(prefix="Read", pos=sector, total=sectors, display=info) self.nand_post() - if info: - print_progress(100, 100, prefix='Progress:', suffix='Complete', bar_length=50) + progbar.show_progress(prefix="Read", pos=sectors, total=sectors, display=info) return True - def send_ptable(self, parttable, mode): + def send_ptable(self, parttable, mode=0): cmdbuf = b"\x19" + pack(" 0: size = 0x20000 @@ -868,44 +901,39 @@ class Streaming(metaclass=LogBase): break toread -= size pos += size + progbar.show_progress(prefix="Read", pos=pos, total=length, display=info) if info: prog = round(float(pos) / float(length) * float(100), 1) if prog > old: print_progress(prog, 100, prefix='Progress:', suffix='Complete (Offset: %08X)' % (offset + pos), bar_length=50) old = prog - if info: - print_progress(100, 100, prefix='Progress:', suffix='Complete', bar_length=50) + progbar.show_progress(prefix="Read", pos=length, total=length, display=info) return True def read_blocks(self, fw, block, length, cwsize, savespare=False, info=True): badblocks = 0 old = 0 pos = 0 + progbar = progress(1) totallength = length * self.settings.num_pages_per_blk * self.settings.PAGESIZE - if info: - print_progress(0, 100, prefix='Progress:', suffix='Complete', bar_length=50) - startoffset = block * self.settings.num_pages_per_blk * self.settings.PAGESIZE - endoffset = startoffset + totallength + progbar.show_progress(prefix="Read", pos=pos, total=totallength, display=info) - for offset in range(startoffset, endoffset, self.settings.PAGESIZE): - pages = int(offset / self.settings.PAGESIZE) - curblock = int(pages / self.settings.num_pages_per_blk) - curpage = int(pages - curblock * self.settings.num_pages_per_blk) - data, spare = self.flash_read(curblock, curpage, self.settings.sectors_per_page, cwsize) - if self.bbtbl[curblock] != 1 or (self.settings.bad_processing_flag != BadFlags.BAD_SKIP.value): - fw.write(data) - if savespare: - fw.write(spare) + for curblock in range(block,block+length): + for curpage in range(self.settings.num_pages_per_blk): + data, spare = self.flash_read(curblock, curpage, self.settings.sectors_per_page, cwsize) + pos = (curblock * self.settings.num_pages_per_blk + curpage) * self.settings.PAGESIZE + progbar.show_progress(prefix="Read", pos=pos, total=totallength, display=info) + if self.bbtbl[curblock] != 1 or (self.settings.bad_processing_flag != BadFlags.BAD_SKIP.value): + fw.write(data) + if savespare: + fw.write(spare) else: self.debug("Bad block at block %d" % curblock) badblocks += 1 pos += self.settings.PAGESIZE - if info: - prog = round(float(pos) / float(totallength) * float(100), 1) - if prog > old: - print_progress(prog, 100, prefix='Progress:', suffix='Complete', bar_length=50) - old = prog + + progbar.show_progress(prefix="Read", pos=totallength, total=totallength, display=info) return badblocks """ diff --git a/edlclient/Library/streaming_client.py b/edlclient/Library/streaming_client.py index 28d6a13..07e99d4 100644 --- a/edlclient/Library/streaming_client.py +++ b/edlclient/Library/streaming_client.py @@ -50,12 +50,12 @@ class streaming_client(metaclass=LogBase): self.printer("-------------------------------------------------------------") for name in partitions: partition = partitions[name] + if not isinstance(partition,dict): + continue for i in range(0x10 - len(name)): name += " " - offset = partition[ - "offset"] * self.streaming.settings.num_pages_per_blk * self.streaming.settings.PAGESIZE - length = partition[ - "length"] * self.streaming.settings.num_pages_per_blk * self.streaming.settings.PAGESIZE + offset = partition["offset"] * self.streaming.settings.num_pages_per_blk * self.streaming.settings.PAGESIZE + length = partition["length"] * self.streaming.settings.num_pages_per_blk * self.streaming.settings.PAGESIZE attr1 = partition["attr1"] attr2 = partition["attr2"] attr3 = partition["attr3"] @@ -108,7 +108,7 @@ class streaming_client(metaclass=LogBase): i = 0 rpartitions = self.streaming.get_partitions() for partition in partitions: - if partition.lower() in rpartitions: + if partition in rpartitions: spartition = rpartitions[partition] offset = spartition["offset"] length = spartition["length"] @@ -314,17 +314,45 @@ class streaming_client(metaclass=LogBase): partitionfilename = "" if "--partitionfilename" in options: partitionfilename = options["--partitionfilename"] - if not os.path.exists(partitionfilename): - self.error(f"Error: Couldn't find partition file: {partitionfilename}") - return False + if partitionfilename is not None: + if not os.path.exists(partitionfilename): + self.error(f"Error: Couldn't find partition file: {partitionfilename}") + return False + else: + ptable = open(partitionfilename,"rb").read() + else: + self.error("Partition file is needed for writing (--partitionfilename)") + sys.exit(1) if not os.path.exists(filename): self.error(f"Error: Couldn't find file: {filename}") return False - if partitionfilename == "": + """ + if partitionfilename is None: + ptable = None rpartitions = self.streaming.get_partitions() + if partitionname in rpartitions: + spartition = rpartitions[partitionname] + offset = spartition["offset"] + length = spartition["length"] + attr1 = spartition["attr1"] + attr2 = spartition["attr2"] + attr3 = spartition["attr3"] + which_flash = spartition["which_flash"] + numparts = 1 + pname = bytes("0:"+partitionname,'utf-8') + pname += (16-len(pname))*b"\x00" + ptable = pack("= self.__logger.level: - self.debug(pre + hexlify(data).decode('utf-8')) - else: - if logging.DEBUG >= self.__logger.level: - self.debug(pre + data) - return data - - def getinterfacecount(self): - if self.vid is not None: - self.device = usb.core.find(idVendor=self.vid, idProduct=self.pid) - if self.device is None: - self.debug("Couldn't detect the device. Is it connected ?") - return False - try: - self.device.set_configuration() - except Exception as e: - self.debug(str(e)) - pass - self.configuration = self.device.get_active_configuration() - self.debug(2, self.configuration) - return self.configuration.bNumInterfaces - else: - self.error("No device detected. Is it connected ?") - return 0 - - def setlinecoding(self, baudrate=None, parity=0, databits=8, stopbits=1): - sbits = {1: 0, 1.5: 1, 2: 2} - dbits = {5, 6, 7, 8, 16} - pmodes = {0, 1, 2, 3, 4} - brates = {300, 600, 1200, 2400, 4800, 9600, 14400, - 19200, 28800, 38400, 57600, 115200, 230400} - - if stopbits is not None: - if stopbits not in sbits.keys(): - valid = ", ".join(str(k) for k in sorted(sbits.keys())) - raise ValueError("Valid stopbits are " + valid) - self.stopbits = stopbits - else: - self.stopbits = 0 - - if databits is not None: - if databits not in dbits: - valid = ", ".join(str(d) for d in sorted(dbits)) - raise ValueError("Valid databits are " + valid) - self.databits = databits - else: - self.databits = 0 - - if parity is not None: - if parity not in pmodes: - valid = ", ".join(str(pm) for pm in sorted(pmodes)) - raise ValueError("Valid parity modes are " + valid) - self.parity = parity - else: - self.parity = 0 - - if baudrate is not None: - if baudrate not in brates: - brs = sorted(brates) - dif = [abs(br - baudrate) for br in brs] - best = brs[dif.index(min(dif))] - raise ValueError( - "Invalid baudrates, nearest valid is {}".format(best)) - self.baudrate = baudrate - - linecode = [ - self.baudrate & 0xff, - (self.baudrate >> 8) & 0xff, - (self.baudrate >> 16) & 0xff, - (self.baudrate >> 24) & 0xff, - sbits[self.stopbits], - self.parity, - self.databits] - - txdir = 0 # 0:OUT, 1:IN - req_type = 1 # 0:std, 1:class, 2:vendor - recipient = 1 # 0:device, 1:interface, 2:endpoint, 3:other - req_type = (txdir << 7) + (req_type << 5) + recipient - data = bytearray(linecode) - wlen = self.device.ctrl_transfer( - req_type, CDC_CMDS["SET_LINE_CODING"], - data_or_wlength=data, windex=1) - self.debug("Linecoding set, {}b sent".format(wlen)) - - def setbreak(self): - txdir = 0 # 0:OUT, 1:IN - req_type = 1 # 0:std, 1:class, 2:vendor - recipient = 1 # 0:device, 1:interface, 2:endpoint, 3:other - req_type = (txdir << 7) + (req_type << 5) + recipient - wlen = self.device.ctrl_transfer( - bmrequesttype=req_type, brequest=CDC_CMDS["SEND_BREAK"], - wvalue=0, data_or_wlength=0, windex=1) - self.debug("Break set, {}b sent".format(wlen)) - - def setcontrollinestate(self, rts=None, dtr=None, isftdi=False): - ctrlstate = (2 if rts else 0) + (1 if dtr else 0) - if isftdi: - ctrlstate += (1 << 8) if dtr is not None else 0 - ctrlstate += (2 << 8) if rts is not None else 0 - txdir = 0 # 0:OUT, 1:IN - req_type = 2 if isftdi else 1 # 0:std, 1:class, 2:vendor - # 0:device, 1:interface, 2:endpoint, 3:other - recipient = 0 if isftdi else 1 - req_type = (txdir << 7) + (req_type << 5) + recipient - - wlen = self.device.ctrl_transfer( - bmrequesttype=req_type, - brequest=1 if isftdi else CDC_CMDS["SET_CONTROL_LINE_STATE"], - wvalue=ctrlstate, - windex=1, - data_or_wlength=0) - self.debug("Linecoding set, {}b sent".format(wlen)) - - def connect(self, EP_IN=-1, EP_OUT=-1): - if self.connected: - self.close() - self.connected = False - for usbid in self.portconfig: - vid = usbid[0] - pid = usbid[1] - interface = usbid[2] - self.device = usb.core.find(idVendor=vid, idProduct=pid, backend=self.backend) - if self.device is not None: - self.vid = vid - self.pid = pid - self.interface = interface - break - - if self.device is None: - self.debug("Couldn't detect the device. Is it connected ?") - return False - # try: - # self.device.set_configuration() - # except: - # pass - - try: - self.configuration = self.device.get_active_configuration() - except usb.core.USBError as e: - if e.strerror == "Configuration not set": - self.device.set_configuration() - self.configuration = self.device.get_active_configuration() - if e.errno == 13: - self.backend = usb.backend.libusb0.get_backend() - self.device = usb.core.find(idVendor=self.vid, idProduct=self.pid, backend=self.backend) - if self.configuration is None: - self.error("Couldn't get device configuration.") - return False - if self.interface == -1: - for interfacenum in range(0, self.configuration.bNumInterfaces): - itf = usb.util.find_descriptor(self.configuration, bInterfaceNumber=interfacenum) - if self.devclass != -1: - if itf.bInterfaceClass == self.devclass: # MassStorage - self.interface = interfacenum - break - else: - self.interface = interfacenum - break - - self.debug(self.configuration) - if self.interface > self.configuration.bNumInterfaces: - print("Invalid interface, max number is %d" % self.configuration.bNumInterfaces) - return False - - if self.interface != -1: - itf = usb.util.find_descriptor(self.configuration, bInterfaceNumber=self.interface) - try: - if self.device.is_kernel_driver_active(0): - self.debug("Detaching kernel driver") - self.device.detach_kernel_driver(0) - except Exception as err: - self.debug("No kernel driver supported: " + str(err)) - try: - usb.util.claim_interface(self.device, 0) - except: - pass - - try: - if self.device.is_kernel_driver_active(self.interface): - self.debug("Detaching kernel driver") - self.device.detach_kernel_driver(self.interface) - except Exception as err: - self.debug("No kernel driver supported: " + str(err)) - try: - if self.interface != 0: - usb.util.claim_interface(self.device, self.interface) - except: - pass - - if EP_OUT == -1: - self.EP_OUT = usb.util.find_descriptor(itf, - # match the first OUT endpoint - custom_match=lambda e: \ - usb.util.endpoint_direction(e.bEndpointAddress) == - usb.util.ENDPOINT_OUT) - else: - self.EP_OUT = EP_OUT - if EP_IN == -1: - self.EP_IN = usb.util.find_descriptor(itf, - # match the first OUT endpoint - custom_match=lambda e: \ - usb.util.endpoint_direction(e.bEndpointAddress) == - usb.util.ENDPOINT_IN) - else: - self.EP_IN = EP_IN - - self.connected = True - return True - else: - print("Couldn't find MassStorage interface. Aborting.") - self.connected = False - return False - - def close(self, reset=False): - if self.connected: - try: - if reset: - self.device.reset() - if not self.device.is_kernel_driver_active(self.interface): - # self.device.attach_kernel_driver(self.interface) #Do NOT uncomment - self.device.attach_kernel_driver(0) - except Exception as err: - self.debug(str(err)) - pass - usb.util.dispose_resources(self.device) - del self.device - self.connected = False - - def write(self, command): - pktsize=self.EP_OUT.wMaxPacketSize - if isinstance(command, str): - command = bytes(command, 'utf-8') - if command == b'': - try: - self.EP_OUT.write(b'') - except usb.core.USBError as e: - error = str(e.strerror) - if "timeout" in error: - time.sleep(0.01) - try: - self.EP_OUT.write(b'') - except Exception as e: # pylint: disable=broad-except - self.debug(str(e)) - return False - return True - else: - i = 0 - try: - buffer=array.array('B',command) - self.EP_OUT.write(buffer) - except Exception as e: # pylint: disable=broad-except - # print("Error while writing") - if "timed out" in str(e): - self.debug(str(e)) - time.sleep(0.01) - i += 1 - if i == 3: - return False - pass - else: - self.error(str(e)) - return False - if self.loglevel == logging.DEBUG: - self.verify_data(bytearray(command), "TX:") - return True - - def read(self, length=None, timeout=None): - if length is None: - length=self.EP_IN.wMaxPacketSize - if self.loglevel==logging.DEBUG: - self.debug(inspect.currentframe().f_back.f_code.co_name + ":" + hex(length)) - tmp = bytearray() - extend = tmp.extend - if timeout is None: - timeout = self.timeout - buffer = self.buffer[:length] - ep_read = self.EP_IN.read - while len(tmp) == 0: - try: - length=ep_read(buffer, timeout) - extend(buffer[:length]) - if len(tmp)>0: - if self.loglevel == logging.DEBUG: - self.verify_data(tmp, "RX:") - return tmp - except usb.core.USBError as e: - error = str(e.strerror) - if "timed out" in error: - # if platform.system()=='Windows': - # time.sleep(0.05) - # print("Waiting...") - self.debug("Timed out") - self.debug(tmp) - return tmp - elif "Overflow" in error: - self.error("USB Overflow") - sys.exit(0) - elif e.errno is not None: - print(repr(e), type(e), e.errno) - sys.exit(0) - else: - break - return tmp - - def ctrl_transfer(self, bmrequesttype, brequest, wvalue, windex, data_or_wlength): - ret = self.device.ctrl_transfer(bmrequesttype=bmrequesttype, brequest=brequest, wvalue=wvalue, windex=windex, - data_or_wlength=data_or_wlength) - return ret[0] | (ret[1] << 8) - - -class ScsiCmds(Enum): - SC_TEST_UNIT_READY = 0x00, - SC_REQUEST_SENSE = 0x03, - SC_FORMAT_UNIT = 0x04, - SC_READ_6 = 0x08, - SC_WRITE_6 = 0x0a, - SC_INQUIRY = 0x12, - SC_MODE_SELECT_6 = 0x15, - SC_RESERVE = 0x16, - SC_RELEASE = 0x17, - SC_MODE_SENSE_6 = 0x1a, - SC_START_STOP_UNIT = 0x1b, - SC_SEND_DIAGNOSTIC = 0x1d, - SC_PREVENT_ALLOW_MEDIUM_REMOVAL = 0x1e, - SC_READ_FORMAT_CAPACITIES = 0x23, - SC_READ_CAPACITY = 0x25, - SC_WRITE_10 = 0x2a, - SC_VERIFY = 0x2f, - SC_READ_10 = 0x28, - SC_SYNCHRONIZE_CACHE = 0x35, - SC_READ_TOC = 0x43, - SC_READ_HEADER = 0x44, - SC_MODE_SELECT_10 = 0x55, - SC_MODE_SENSE_10 = 0x5a, - SC_READ_12 = 0xa8, - SC_WRITE_12 = 0xaa, - SC_PASCAL_MODE = 0xff - - -command_block_wrapper = [ - ('dCBWSignature', '4s'), - ('dCBWTag', 'I'), - ('dCBWDataTransferLength', 'I'), - ('bmCBWFlags', 'B'), - ('bCBWLUN', 'B'), - ('bCBWCBLength', 'B'), - ('CBWCB', '16s'), -] -command_block_wrapper_len = 31 - -command_status_wrapper = [ - ('dCSWSignature', '4s'), - ('dCSWTag', 'I'), - ('dCSWDataResidue', 'I'), - ('bCSWStatus', 'B') -] -command_status_wrapper_len = 13 - - -class Scsi: - """ - FIHTDC, PCtool - """ - SC_READ_NV = 0xf0 - SC_SWITCH_STATUS = 0xf1 - SC_SWITCH_PORT = 0xf2 - SC_MODEM_STATUS = 0xf4 - SC_SHOW_PORT = 0xf5 - SC_MODEM_DISCONNECT = 0xf6 - SC_MODEM_CONNECT = 0xf7 - SC_DIAG_RUT = 0xf8 - SC_READ_BATTERY = 0xf9 - SC_READ_IMAGE = 0xfa - SC_ENABLE_ALL_PORT = 0xfd - SC_MASS_STORGE = 0xfe - SC_ENTER_DOWNLOADMODE = 0xff - SC_ENTER_FTMMODE = 0xe0 - SC_SWITCH_ROOT = 0xe1 - """ - //Div2-5-3-Peripheral-LL-ADB_ROOT-00+/* } FIHTDC, PCtool */ - //StevenCPHuang 2011/08/12 porting base on 1050 -- - //StevenCPHuang_20110820,add Moto's mode switch cmd to support PID switch function ++ - """ - SC_MODE_SWITCH = 0xD6 - - # /StevenCPHuang_20110820,add Moto's mode switch cmd to support PID switch function -- - - def __init__(self, loglevel=logging.INFO, vid=None, pid=None, interface=-1): - self.vid = vid - self.pid = pid - self.interface = interface - self.Debug = False - self.usb = None - self.loglevel = loglevel - - def connect(self): - self.usb = UsbClass(loglevel=self.loglevel, portconfig=[self.vid, self.pid, self.interface], devclass=8) - if self.usb.connect(): - return True - return False - - # htcadb = "55534243123456780002000080000616687463800100000000000000000000"; - # Len 0x6, Command 0x16, "HTC" 01 = Enable, 02 = Disable - def send_mass_storage_command(self, lun, cdb, direction, data_length): - global tag - cmd = cdb[0] - if 0 <= cmd < 0x20: - cdb_len = 6 - elif 0x20 <= cmd < 0x60: - cdb_len = 10 - elif 0x60 <= cmd < 0x80: - cdb_len = 0 - elif 0x80 <= cmd < 0xA0: - cdb_len = 16 - elif 0xA0 <= cmd < 0xC0: - cdb_len = 12 - else: - cdb_len = 6 - - if len(cdb) != cdb_len: - print("Error, cdb length doesn't fit allowed cbw packet length") - return 0 - - if (cdb_len == 0) or (cdb_len > command_block_wrapper_len): - print("Error, invalid data packet length, should be max of 31 bytes.") - return 0 - else: - data = write_object(command_block_wrapper, b"USBC", tag, data_length, direction, lun, cdb_len, cdb)[ - 'raw_data'] - print(hexlify(data)) - if len(data) != 31: - print("Error, invalid data packet length, should be 31 bytes, but length is %d" % len(data)) - return 0 - tag += 1 - self.usb.write(data, 31) - return tag - - def send_htc_adbenable(self): - # do_reserve from f_mass_storage.c - print("Sending HTC adb enable command") - common_cmnd = b"\x16htc\x80\x01" # reserve_cmd + 'htc' + len + flag - ''' - Flag values: - 1: Enable adb daemon from mass_storage - 2: Disable adb daemon from mass_storage - 3: cancel unmount BAP cdrom - 4: cancel unmount HSM rom - ''' - lun = 0 - datasize = common_cmnd[4] - timeout = 5000 - ret_tag = self.send_mass_storage_command(lun, common_cmnd, USB_DIR_IN, datasize) - ret_tag += self.send_mass_storage_command(lun, common_cmnd, USB_DIR_IN, datasize) - if datasize > 0: - data = self.usb.read(datasize, timeout) - print("DATA: " + hexlify(data).decode('utf-8')) - print("Sent HTC adb enable command") - - def send_htc_ums_adbenable(self): # HTC10 - # ums_ctrlrequest from f_mass_storage.c - print("Sending HTC ums adb enable command") - brequesttype = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE - brequest = 0xa0 - wvalue = 1 - ''' - wValue: - 0: Disable adb daemon - 1: Enable adb daemon - ''' - windex = 0 - w_length = 1 - ret = self.usb.ctrl_transfer(brequesttype, brequest, wvalue, windex, w_length) - print("Sent HTC ums adb enable command: %x" % ret) - - def send_zte_adbenable(self): # zte blade - common_cmnd = b"\x86zte\x80\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" # reserve_cmd + 'zte' + len + flag - common_cmnd2 = b"\x86zte\x80\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" # reserve_cmd + 'zte' + len + flag - ''' - Flag values: - 0: disable adbd ---for 736T - 1: enable adbd ---for 736T - 2: disable adbd ---for All except 736T - 3: enable adbd ---for All except 736T - ''' - lun = 0 - datasize = common_cmnd[4] - timeout = 5000 - ret_tag = self.send_mass_storage_command(lun, common_cmnd, USB_DIR_IN, datasize) - ret_tag += self.send_mass_storage_command(lun, common_cmnd, USB_DIR_IN, datasize) - ret_tag = self.send_mass_storage_command(lun, common_cmnd2, USB_DIR_IN, datasize) - ret_tag += self.send_mass_storage_command(lun, common_cmnd2, USB_DIR_IN, datasize) - if datasize > 0: - data = self.usb.read(datasize, timeout) - print("DATA: " + hexlify(data).decode('utf-8')) - print("Send HTC adb enable command") - - def send_fih_adbenable(self): # motorola xt560, nokia 3.1, #f_mass_storage.c - if self.usb.connect(): - print("Sending FIH adb enable command") - datasize = 0x24 - # reserve_cmd + 'FI' + flag + len + none - common_cmnd = bytes([self.SC_SWITCH_PORT]) + b"FI1" + pack("1: Enable adb daemon from mass_storage - common_cmnd[3]->0: Disable adb daemon from mass_storage - ''' - lun = 0 - # datasize=common_cmnd[4] - timeout = 5000 - ret_tag = None - ret_tag += self.send_mass_storage_command(lun, common_cmnd, USB_DIR_IN, 0x600) - # ret_tag+=self.send_mass_storage_command(lun, common_cmnd, USB_DIR_IN, 0x600) - if datasize > 0: - data = self.usb.read(datasize, timeout) - print("DATA: " + hexlify(data).decode('utf-8')) - print("Sent FIH adb enable command") - self.usb.close() - - def send_alcatel_adbenable(self): # Alcatel MW41 - if self.usb.connect(): - print("Sending alcatel adb enable command") - datasize = 0x24 - common_cmnd = b"\x16\xf9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - lun = 0 - timeout = 5000 - ret_tag = None - ret_tag += self.send_mass_storage_command(lun, common_cmnd, USB_DIR_IN, 0x600) - if datasize > 0: - data = self.usb.read(datasize, timeout) - print("DATA: " + hexlify(data).decode('utf-8')) - print("Sent alcatel adb enable command") - self.usb.close() - - def send_fih_root(self): - # motorola xt560, nokia 3.1, huawei u8850, huawei Ideos X6, - # lenovo s2109, triumph M410, viewpad 7, #f_mass_storage.c - if self.usb.connect(): - print("Sending FIH root command") - datasize = 0x24 - # reserve_cmd + 'FIH' + len + flag + none - common_cmnd = bytes([self.SC_SWITCH_ROOT]) + b"FIH" + pack(" 0: - data = self.usb.read(datasize, timeout) - print("DATA: " + hexlify(data).decode('utf-8')) - print("Sent FIH root command") - self.usb.close() - - def close(self): - self.usb.close() - return True diff --git a/edlclient/Library/usbscsi.py b/edlclient/Library/usbscsi.py deleted file mode 100755 index 6b1381d..0000000 --- a/edlclient/Library/usbscsi.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -*- -# (c) B.Kerler 2018-2021 -import argparse -from edlclient.Library.usblib import * - - -def main(): - info = 'MassStorageBackdoor (c) B.Kerler 2019.' - parser = argparse.ArgumentParser(description=info) - print("\n" + info + "\n\n") - parser.add_argument('-vid', metavar="", help='[Option] Specify vid, default=0x2e04)', default="0x2e04") - parser.add_argument('-pid', metavar="", help='[Option] Specify pid, default=0xc025)', default="0xc025") - parser.add_argument('-interface', metavar="", help='[Option] Specify interface number)', default="") - parser.add_argument('-nokia', help='[Option] Enable Nokia adb backdoor', action='store_true') - parser.add_argument('-alcatel', help='[Option] Enable alcatel adb backdoor', action='store_true') - parser.add_argument('-zte', help='[Option] Enable zte adb backdoor', action='store_true') - parser.add_argument('-htc', help='[Option] Enable htc adb backdoor', action='store_true') - parser.add_argument('-htcums', help='[Option] Enable htc ums adb backdoor', action='store_true') - args = parser.parse_args() - vid = None - pid = None - if args.vid != "": - vid = int(args.vid, 16) - if args.pid != "": - pid = int(args.pid, 16) - if args.interface != "": - interface = int(args.interface, 16) - else: - interface = -1 - - usbscsi = Scsi(vid, pid, interface) - if usbscsi.connect(): - if args.nokia: - usbscsi.send_fih_adbenable() - usbscsi.send_fih_root() - elif args.zte: - usbscsi.send_zte_adbenable() - elif args.htc: - usbscsi.send_htc_adbenable() - elif args.htcums: - usbscsi.send_htc_ums_adbenable() - elif args.alcatel: - usbscsi.send_alcatel_adbenable() - else: - print("A command is required. Use -h to see options.") - exit(0) - usbscsi.close() - - -if __name__ == '__main__': - main() diff --git a/edlclient/Library/utils.py b/edlclient/Library/utils.py index bee39a1..ff256a4 100755 --- a/edlclient/Library/utils.py +++ b/edlclient/Library/utils.py @@ -13,6 +13,7 @@ import colorama import copy import datetime as dt import time +from io import BytesIO from struct import unpack, pack try: @@ -25,6 +26,85 @@ except ImportError: print("Keystone library is missing (optional).") +class structhelper_io: + pos = 0 + + def __init__(self, data: BytesIO = None, direction='little'): + self.data = data + self.direction = direction + + def setdata(self, data, offset=0): + self.pos = offset + self.data = data + + def qword(self): + dat = int.from_bytes(self.data.read(8), self.direction) + return dat + + def dword(self): + dat = int.from_bytes(self.data.read(4), self.direction) + return dat + + def dwords(self, dwords=1): + dat = [int.from_bytes(self.data.read(4), self.direction) for _ in range(dwords)] + return dat + + def short(self): + dat = int.from_bytes(self.data.read(2), self.direction) + return dat + + def shorts(self, shorts): + dat = [int.from_bytes(self.data.read(2), self.direction) for _ in range(shorts)] + return dat + + def bytes(self, rlen=1): + dat = self.data.read(rlen) + if dat == b'': + return dat + if rlen == 1: + return dat[0] + return dat + + def string(self, rlen=1): + dat = self.data.read(rlen) + return dat + + def getpos(self): + return self.data.tell() + + def seek(self, pos): + self.data.seek(pos) + +def find_binary(data, strf, pos=0): + t = strf.split(b".") + pre = 0 + offsets = [] + while pre != -1: + pre = data[pos:].find(t[0], pre) + if pre == -1: + if len(offsets) > 0: + for offset in offsets: + error = 0 + rt = offset + len(t[0]) + for i in range(1, len(t)): + if t[i] == b'': + rt += 1 + continue + rt += 1 + prep = data[rt:].find(t[i]) + if prep != 0: + error = 1 + break + rt += len(t[i]) + if error == 0: + return offset + pos + else: + return None + else: + offsets.append(pre) + pre += 1 + return None + class progress: def __init__(self, pagesize): self.progtime = 0 @@ -45,6 +125,8 @@ class progress: return 0, 0, "" def show_progress(self, prefix, pos, total, display=True): + if pos > total: + pos = total if pos != 0: prog = round(float(pos) / float(total) * float(100), 1) else: @@ -54,13 +136,14 @@ class progress: self.start = time.time() self.progtime = time.time() self.progpos = pos - print_progress(prog, 100, prefix='Done', - suffix=prefix + ' (Sector 0x%X of 0x%X) %0.2f MB/s' % - (pos // self.pagesize, - total // self.pagesize, - 0), bar_length=50) + if display: + print_progress(prog, 100, prefix='Done', + suffix=prefix + ' (Sector 0x%X of 0x%X) %0.2f MB/s' % + (pos // self.pagesize, + total // self.pagesize, + 0), bar_length=50) - if prog > self.prog: + if prog > self.prog or prog==100.0: if display: t0 = time.time() tdiff = t0 - self.progtime @@ -87,12 +170,12 @@ class progress: hinfo = "%02dm:%02ds left" % (min, sec) else: hinfo = "%02ds left" % sec - - print_progress(prog, 100, prefix='Progress:', - suffix=prefix + f' (Sector 0x%X of 0x%X, {hinfo}) %0.2f MB/s' % - (pos // self.pagesize, - total // self.pagesize, - throughput), bar_length=50) + if display: + print_progress(prog, 100, prefix='Progress:', + suffix=prefix + f' (Sector 0x%X of 0x%X, {hinfo}) %0.2f MB/s' % + (pos // self.pagesize, + total // self.pagesize, + throughput), bar_length=50) self.prog = prog self.progpos = pos self.progtime = t0 @@ -104,28 +187,39 @@ class structhelper: self.pos = 0 self.data = data - def qword(self): - dat = unpack("1: - data["model"]=info[1] + if info != -1: + if len(info) > 1: + data["model"] = info[1] return data + class adbtools(metaclass=LogBase): - def sendcmd(self, tn,cmd): - tn.write(bytes(cmd,'utf-8')+b"\n") + def sendcmd(self, tn, cmd): + tn.write(bytes(cmd, 'utf-8') + b"\n") time.sleep(0.05) return tn.read_eager().strip().decode('utf-8') - def run(self, args): - port = args.port + def qc_diag_auth(self, diag): + if diag.connect(): + res = diag.send(b"\x4B\xA3\x06\x00") + if res[0] == 0x4B: + challenge = res[4:4 + 8] + response = hashlib.md5(challenge).digest() + res = diag.send(b"\x4B\xA3\x07\x00" + response) + if res[0] == 0x4B: + if res[3] == 0x00: + print("Auth success") + res = diag.send(b"\x41" + b"\x30\x30\x30\x30\x30\x30") + if res[1] == 0x01: + print("SPC success") + sp = b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE" + res = diag.send(b"\x46" + sp) + if res[0] == 0x46 and res[1] == 0x01: + print("SP success") + return True + else: + res = diag.send(b"\x25" + sp) + if res[0] == 0x46 and res[1] == 0x01: + print("SP success") + return True + return False + + def meta(self, port, mode=b"METAMETA"): + while True: + cn = connection(port) + if cn.connected: + while True: + resp2 = cn.serial.read(8) + if len(resp2)>0: + break + cn.serial.write(mode) + response = cn.serial.read(8) + if len(response) == 0: + print("Read timeout while switching to META mode") + elif response == b'ATEMATEM' or response == b'READYATE': + print("META Mode enabled") + elif response == b'METAFORB': + print("META mode forbidden") + else: + print("Invalid response: ", response) + + def run(self, port, enable): cn = connection(port) if cn.connected: - info=cn.ati() + info = cn.ati() + res = False if "vendor" in info: - if info["vendor"]=="Sierra Wireless" or info["vendor"]=="Netgear": + if info["vendor"] == "Sierra Wireless" or info["vendor"] == "Netgear": + res=self.SierraWireless(cn, info, enable) + elif info["vendor"] == "Quectel": print("Sending at switch command") - kg=SierraKeygen(cn) - if kg.openlock(): - if cn.send('AT!CUSTOM="ADBENABLE",1\r')==-1: - print("Error on sending adb enable command.") - kg.openlock() - if cn.send('AT!CUSTOM="TELNETENABLE",1\r')!=-1: - time.sleep(5) - tn = Telnet("192.168.1.1", 23, 15) - tn.write(b"adbd &\r\n") - info = tn.read_eager() - print(info) - print("Enabled adb via telnet") - else: - print("Error on sending telnet enable command.") - if kg.openlock(): - if info["vendor"] == "Netgear": - print("Enabling new port config") - if cn.send("AT!UDPID=68E2"): - print("Successfully enabled PID 68E2") - elif info["vendor"]=="Quectel": - print("Sending at switch command") - salt=cn.send("AT+QADBKEY?\r") - if salt!=-1: - if len(salt)>1: - salt=salt[1] - code = crypt.crypt("SH_adb_quectel", "$1$" + salt) - code = code[12:] - cn.send("AT+QADBKEY=\"%s\"\r" % code) - if cn.send("AT+QCFG=\"usbcfg\",0x2C7C,0x125,1,1,1,1,1,1,0\r")==-1: - if cn.send("AT+QLINUXCMD=\"adbd\"")!=-1: #echo test > /dev/ttyGS0 - print("Success enabling adb") - else: - print("Success enabling adb") - print("In order to disable adb, send AT+QCFG=\"usbcfg\",0x2C7C,0x125,1,1,1,1,1,0,0") - elif info["vendor"]=="ZTE": + res=self.Quectel(cn, enable) + elif info["vendor"] == "ZTE": print("Sending switch command via diag") - if cn.send("AT+ZMODE=1")!=-1: - print("Success enabling adb") - else: - interface = 0 - diag = qcdiag(loglevel=self.__logger.level, portconfig=[[0x19d2, 0x0016, interface]]) - if diag.connect(): - res = diag.send(b"\x4B\xA3\x06\x00") - if res[0]==0x4B: - challenge=res[4:4+8] - response=hashlib.md5(challenge).digest() - res = diag.send(b"\x4B\xA3\x07\x00"+response) - if res[0]==0x4B: - if res[3]==0x00: - print("Auth success") - res=diag.send(b"\x41" + b"\x30\x30\x30\x30\x30\x30") - if res[1]==0x01: - print("SPC success") - sp=b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE" - res = diag.send(b"\x46" + sp) - if res[0] == 0x46 and res[1]==0x01: - print("SP success") - else: - res = diag.send(b"\x25" + sp) - if res[0]==0x46 and res[1]==0x01: - print("SP success") - res = diag.send(b"\x4B\xFA\x0B\x00\x01") #Enable adb serial - if res[0]!=0x13: - print("Success enabling adb serial") - res = diag.send(b"\x4B\x5D\x05\x00") #Operate ADB - if res[0]!=0x13: - print("Success enabling adb") - diag.disconnect() - elif info["vendor"]=="Simcom": - print("Sending at switch command") - # Simcom7600 - if cn.send("AT+CUSBADB=1,1")!=-1: - print("Success enabling adb") - elif info["vendor"]=="Fibocom": - print("Sending at switch command") - # FibocomL718: - if cn.send("AT+ADBDEBUG=1")!=-1: - print("Success enabling adb") - elif info["vendor"]=="Alcatel": - print("Send scsi switch command") - print("Run \"sudo sg_raw /dev/sg0 16 f9 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -v\" to enable adb") - elif info["vendor"]=="Samsung": - if cn.send("AT+USBMODEM=1"): - print("Success enabling adb") - elif cn.send("AT+SYSSCOPE=1,0,0"): - print("Success enabling adb") + res=self.ZTE(cn, enable) + elif info["vendor"] == "Simcom": + res=self.Simcom(cn) + elif info["vendor"] == "Fibocom": + res=self.Fibocom(cn, enable) + elif info["vendor"] == "Alcatel": + res=self.Alcatel(enable) + elif info["vendor"] == "Samsung": + res=self.Samsung(cn, enable) + if enable: + mode="enabled" + else: + mode="disabled" + if res: + print("ADB successfully "+mode) + else: + print("ADB couldn't be "+mode) + cn.close() + else: + print("No device detected") + + def SierraWireless(self, cn, info, enable): + print("Sending at switch command") + kg = SierraKeygen(cn) + if kg.openlock(): + if enable: + if cn.send('AT!CUSTOM="ADBENABLE",1\r') != -1: + return True + kg.openlock() + if cn.send('AT!CUSTOM="TELNETENABLE",1\r') != -1: + time.sleep(5) + tn = Telnet("192.168.1.1", 23, 15) + tn.write(b"adbd &\r\n") + info = tn.read_eager() + print(info) + return True + if kg.openlock(): + if info["vendor"] == "Netgear": + print("Enabling new port config") + if cn.send("AT!UDPID=68E2"): + print("Successfully enabled PID 68E2") + return True + + index=-1 + type=-1 + bitmask=-1 + resp=cn.send("AT!USBCOMP?") + if resp!=-1: + print(resp) + for val in resp: + if "Config Index" in val: + index=val[val.find("Config Index: ")+14:] + elif "Config Type" in val: + type=val[val.find("Config Type: ")+14:].replace(" (Generic)","") + elif "Interface bitmask" in val: + bitmask=val[val.find("Interface bitmask: ")+19:] + if " " in bitmask: + bitmask="0x"+bitmask.split(" ")[0] + if index!=-1 and type!=-1 and bitmask!=1: + index=int(index) + type=int(type) + bitmask=int(bitmask,16) + # AT!USBCOMP=,, + # - configuration index to which the composition applies, should be 1 + # - 1:Generic, 2:USBIF-MBIM, 3:RNDIS + # config type 2/3 should only be used for specific Sierra PIDs: 68B1, 9068 + # customized VID/PID should use config type 1 + # - DIAG - 0x00000001, + # ADB - 0x00000002, + # NMEA - 0x00000004, + # MODEM - 0x00000008, + # RMNET0 - 0x00000100, + # RMNET1 - 0x00000400, + # RMNET2 - 0x00000800, + # MBIM - 0x00001000, + # RNDIS - 0x00004000, + # AUDIO - 0x00010000, + # ECM - 0x00080000, + # UBIST - 0x00200000 + #if enable: + cmd=f"AT!USBCOMP={index},{type},%08X" % 0x0080010E + #else: + # cmd = f"AT!USBCOMP={index},{type},%08X" % 0x0000010D + resp=cn.send(cmd) + if resp!=-1: + resp=cn.send("AT!RESET") + if resp!=-1: + return True + return False + return True + else: + if cn.send('AT!CUSTOM="ADBENABLE",0\r') != -1: + return True + kg.openlock() + if cn.send('AT!CUSTOM="TELNETENABLE",0\r') != -1: + return True + return False + + def Samsung(self, cn, enable): + if enable: + if cn.send("AT+USBMODEM=1"): + return True + elif cn.send("AT+SYSSCOPE=1,0,0"): + return True + else: + if cn.send("AT+USBMODEM=0"): + return True + elif cn.send("AT+SYSSCOPE=1,0,0"): + return True + return False + + def Alcatel(self, enable): + print("Send scsi switch command") + print("Run \"sudo sg_raw /dev/sg0 16 f9 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -v\" to enable adb") + + def Fibocom(self, cn, enable): + print("Sending at switch command") + if enable: + # FibocomL718: + if cn.send("AT+ADBDEBUG=1") != -1: + return True + else: + if cn.send("AT+ADBDEBUG=0") != -1: + return True + return False + + def Simcom(self, cn, enable): + print("Sending at switch command") + if enable: + # Simcom7600 + if cn.send("AT+CUSBADB=1,1") != -1: + return True + else: + if cn.send("AT+CUSBADB=1,1") != -1: + return True + return False + + def ZTE(self, cn, enable): + if enable: + if cn.send("AT+ZMODE=1") != -1: + return True + else: + interface = 0 + diag = qcdiag(loglevel=self.__logger.level, portconfig=[[0x19d2, 0x0016, interface]]) + if self.qc_diag_auth(diag): + res = diag.send(b"\x4B\xFA\x0B\x00\x01") # Enable adb serial + if res[0] != 0x13: + print("Success enabling adb serial") + res = diag.send(b"\x4B\x5D\x05\x00") # Operate ADB + if res[0] != 0x13: + print("Success enabling adb") + return True + diag.disconnect() + else: + if cn.send("AT+ZMODE=F") != -1: + return True + else: + interface = 0 + diag = qcdiag(loglevel=self.__logger.level, portconfig=[[0x19d2, 0x0016, interface]]) + if self.qc_diag_auth(diag): + res = diag.send(b"\x4B\xFA\x0B\x00\x00") # Enable adb serial + if res[0] != 0x13: + print("Success enabling adb serial") + res = diag.send(b"\x4B\x5D\x05\x00") # Operate ADB + if res[0] != 0x13: + print("Success enabling adb") + diag.disconnect() + return True + return False + + def Quectel(self, cn, enable: bool = True): + salt = cn.send("AT+QADBKEY?\r") + if salt != -1: + if len(salt) > 1: + salt = salt[1] + code = crypt.crypt("SH_adb_quectel", "$1$" + salt) + code = code[12:] + cn.send("AT+QADBKEY=\"%s\"\r" % code) + if enable: + if cn.send("AT+QCFG=\"usbcfg\",0x2C7C,0x125,1,1,1,1,1,1,0\r") == -1: + if cn.send("AT+QLINUXCMD=\"adbd\"") != -1: # echo test > /dev/ttyGS0 + return True + else: + return True + else: + if cn.send("AT+QCFG=\"usbcfg\",0x2C7C,0x125,1,1,1,1,1,0,0\r") != -1: + return True + return False - cn.close() def main(): - version = "1.1" - info = 'Modem Gimme-ADB ' + version + ' (c) B. Kerler 2020-2021' + version = "1.2" + info = '\nModem Gimme-ADB ' + version + ' (c) B. Kerler 2020-2021\n-------------------------------------------\n' parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description=info) + parser.add_argument( + '-mode', help='Mode: enable or disable', + default="enable") parser.add_argument( '-port', '-p', - help='use com port for at', + help='[Optional] use com port for at', default="") parser.add_argument( '-logfile', '-l', help='use logfile for debug log', default="") args = parser.parse_args() - ad=adbtools() - ad.run(args) + ad = adbtools() + print(info) + print("Supported modules: ZTE,Netgear,Sierra Wireless,Samsung,Alcatel,Quectel,Fibucom") + if args.mode.lower() == "enable": + enable = True + else: + enable = False + ad.run(port=args.port, enable=enable) + #ad.meta(port=args.port) -if __name__=="__main__": + +if __name__ == "__main__": main() - diff --git a/edlclient/Tools/fhloaderparse b/edlclient/Tools/fhloaderparse index c6b361d..ab5e0cb 100755 --- a/edlclient/Tools/fhloaderparse +++ b/edlclient/Tools/fhloaderparse @@ -10,9 +10,11 @@ current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentfra parent_dir = os.path.dirname(current_dir) sys.path.insert(0, parent_dir) from edlclient.Library.utils import elf -from edlclient.Library.sahara import convertmsmid +from edlclient.Library.loader_db import loader_utils from edlclient.Config.qualcomm_config import vendor +lu=loader_utils() + class MBN: def __init__(self, memory): self.imageid, self.flashpartitionversion, self.imagesrc, self.loadaddr, self.imagesz, self.codesz, \ @@ -176,7 +178,7 @@ def init_loader_db(): msmid = hwid[:8] devid = hwid[8:] pkhash = filename.split("_")[1].lower() - msmdb = convertmsmid(msmid) + msmdb = lu.convertmsmid(msmid) for msmid in msmdb: mhwid = (msmid + devid).lower() if mhwid not in loaderdb: @@ -195,7 +197,7 @@ def is_duplicate(loaderdb, sign_info): msmid = sign_info.hw_id[:8].lower() devid = sign_info.hw_id[8:].lower() hwid = sign_info.hw_id.lower() - for msmid in convertmsmid(msmid): + for msmid in lu.convertmsmid(msmid): rid = (msmid + devid).lower() if hwid in loaderdb: loader = loaderdb[hwid] @@ -355,7 +357,7 @@ def main(argv): print(info) msmid = loader_info.hw_id[:8] devid = loader_info.hw_id[8:] - for msmid in convertmsmid(msmid): + for msmid in lu.convertmsmid(msmid): hwid = (msmid + devid).lower() auth = "" with open(item.filename, "rb") as rf: diff --git a/edlclient/Tools/qc_diag b/edlclient/Tools/qc_diag deleted file mode 100755 index 85bfc40..0000000 --- a/edlclient/Tools/qc_diag +++ /dev/null @@ -1,1329 +0,0 @@ -#!/usr/bin/env python3 -""" -Licensed under MIT License, (c) B. Kerler 2018-2021 -""" - -import sys -import os -import argparse -import json -import logging -from xml.etree import ElementTree -from enum import Enum - -from struct import unpack, pack -from binascii import hexlify, unhexlify -import os, sys, inspect - -from edlclient.Library.utils import print_progress, read_object, write_object, LogBase -from edlclient.Library.usblib import UsbClass -from edlclient.Library.hdlc import hdlc -from edlclient.Config.usb_ids import default_diag_vid_pid - -qcerror = { - 1: "None", - 2: "Unknown", - 3: "Open Port Fail", - 4: "Port not open", - 5: "Buffer too small", - 6: "Read data fail", - 7: "Open file fail", - 8: "File not open", - 9: "Invalid parameter", - 10: "Send write ram failed", - 11: "Send command failed", - 12: "Offline phone failed", - 13: "Erase rom failed", - 14: "Timeout", - 15: "Go cmd failed", - 16: "Set baudrate failed", - 17: "Say hello failed", - 18: "Write port failed", - 19: "Failed to read nv", - 20: "Failed to write nv", - 21: "Last failed but not recovery", - 22: "Backup file wasn't found", - 23: "Incorrect SPC Code", - 24: "Hello pkt isn't needed", - 25: "Not active" -} - -diagerror = { - 20: "Generic error", - 21: "Bad argument", - 22: "Data too large", - 24: "Not connected", - 25: "Send pkt failed", - 26: "Receive pkt failed", - 27: "Extract pkt failed", - 29: "Open port failed", - 30: "Bad command", - 31: "Protected", - 32: "No media", - 33: "Empty", - 34: "List done" -} - -nvitem_type = [ - ("item", "H"), - ("rawdata", "128s"), - ("status", "H") -] - -subnvitem_type = [ - ("item", "H"), - ("index", "H"), - ("rawdata", "128s"), - ("status", "H") -] - - -class fs_factimage_read_info(): - def_fs_factimage_read_info = [ - ("stream_state", "B"), # 0 indicates no more data to be sent, otherwise set to 1 - ("info_cluster_sent", "B"), # 0 indicates if info_cluster was not sent, else 1 - ("cluster_map_seqno", "H"), # Sequence number of cluster map pages - ("cluster_data_seqno", "I") # Sequence number of cluster data pages - ] - - def __init__(self, stream_state, info_cluster_sent, cluster_map_seqno, cluster_data_seqno): - self.stream_state = stream_state - self.info_cluster_sent = info_cluster_sent - self.cluster_map_seqno = cluster_map_seqno - self.cluster_data_seqno = cluster_data_seqno - - def fromdata(self, data): - tmp = read_object(data[0:0x10], self.def_fs_factimage_read_info) - self.stream_state = tmp["stream_state"] - self.info_cluster_sent = tmp["info_cluster_sent"] - self.cluster_map_seqno = tmp["cluster_map_seqno"] - self.cluster_data_seqno = tmp["cluster_data_seqno"] - - def todata(self): - data = write_object(self.def_fs_factimage_read_info, self.stream_state, self.info_cluster_sent, - self.cluster_map_seqno, self.cluster_data_seqno) - return data - - -class FactoryHeader(): - def_factory_header = [ - ("magic1", "I"), - ("magic2", "I"), - ("fact_version", "H"), # Version of this cluster - # #Fields needed for the superblock - ("version", "H"), # Superblock version - ("block_size", "I"), # Pages per block. - ("page_size", "I"), # Page size in bytes. - ("block_count", "I"), # Total blocks in device. - ("space_limit", "I"), # Total number of used pages (defines the size of the map) - ("upper_data", "32I") - ] - - def __init__(self): - self.magic1 = 0 - self.magic2 = 0 - self.fact_version = 0 - self.version = 0 - self.block_size = 0 - self.page_size = 0 - self.block_count = 0 - self.space_limit = 0 - self.upper_data = [0 * 32] - - def fromdata(self, data): - tmp = read_object(data[0:0x9C], self.def_factory_header) - self.magic1 = tmp["magic1"] - self.magic2 = tmp["magic2"] - self.fact_version = tmp["fact_version"] - self.version = tmp["version"] - self.block_size = tmp["block_size"] - self.page_size = tmp["page_size"] - self.block_count = tmp["block_count"] - self.space_limit = tmp["space_limit"] - self.upper_data = tmp["upper_data"] - - def todata(self): - data = write_object(self.magic1, self.magic2, self.fact_version, self.version, self.block_size, self.page_size, - self.block_count, self.space_limit, self.upper_data) - return data - - -class nvitem(): - item = 0x0 - data = b"" - status = 0x0 - index = 0x0 - name = "" - - def __init__(self, item, index, data, status, name): - self.item = item - self.index = index - self.data = data - self.status = status - self.name = name - - -class diag_cmds(Enum): - DIAG_VERNO_F = 0 - DIAG_ESN_F = 1 - DIAG_PEEKB_F = 2 - DIAG_PEEKW_F = 3 - DIAG_PEEKD_F = 4 - DIAG_POKEB_F = 5 - DIAG_POKEW_F = 6 - DIAG_POKED_F = 7 - DIAG_OUTP_F = 8 - DIAG_OUTPW_F = 9 - DIAG_INP_F = 0xA - DIAG_INPW_F = 0xB - DIAG_STATUS_F = 0xC - DIAG_LOGMASK_F = 0xF - DIAG_LOG_F = 0x10 - DIAG_NV_PEEK_F = 0x11 - DIAG_NV_POKE_F = 0x12 - DIAG_BAD_CMD_F = 0x13 - DIAG_BAD_PARM_F = 0x14 - DIAG_BAD_LEN_F = 0x15 - DIAG_BAD_MODE_F = 0x18 - DIAG_TAGRAPH_F = 0x19 - DIAG_MARKOV_F = 0x1a - DIAG_MARKOV_RESET_F = 0x1b - DIAG_DIAG_VER_F = 0x1c - DIAG_TS_F = 0x1d - DIAG_TA_PARM_F = 0x1E - DIAG_MSG_F = 0x1f - DIAG_HS_KEY_F = 0x20 - DIAG_HS_LOCK_F = 0x21 - DIAG_HS_SCREEN_F = 0x22 - DIAG_PARM_SET_F = 0x24 - DIAG_NV_READ_F = 0x26 - DIAG_NV_WRITE_F = 0x27 - DIAG_CONTROL_F = 0x29 - DIAG_ERR_READ_F = 0x2a - DIAG_ERR_CLEAR_F = 0x2b - DIAG_SER_RESET_F = 0x2c - DIAG_SER_REPORT_F = 0x2d - DIAG_TEST_F = 0x2e - DIAG_GET_DIPSW_F = 0x2f - DIAG_SET_DIPSW_F = 0x30 - DIAG_VOC_PCM_LB_F = 0x31 - DIAG_VOC_PKT_LB_F = 0x32 - DIAG_ORIG_F = 0x35 - DIAG_END_F = 0x36 - DIAG_SW_VERSION_F = 0x38 - DIAG_DLOAD_F = 0x3a - DIAG_TMOB_F = 0x3b - DIAG_STATE_F = 0x3f - DIAG_PILOT_SETS_F = 0x40 - DIAG_SPC_F = 0x41 - DIAG_BAD_SPC_MODE_F = 0x42 - DIAG_PARM_GET2_F = 0x43 - DIAG_SERIAL_CHG_F = 0x44 - DIAG_PASSWORD_F = 0x46 - DIAG_BAD_SEC_MODE_F = 0x47 - DIAG_PR_LIST_WR_F = 0x48 - DIAG_PR_LIST_RD_F = 0x49 - DIAG_SUBSYS_CMD_F = 0x4b - DIAG_FEATURE_QUERY_F = 0x51 - DIAG_SMS_READ_F = 0x53 - DIAG_SMS_WRITE_F = 0x54 - DIAG_SUP_FER_F = 0x55 - DIAG_SUP_WALSH_CODES_F = 0x56 - DIAG_SET_MAX_SUP_CH_F = 0x57 - DIAG_PARM_GET_IS95B_F = 0x58 - DIAG_FS_OP_F = 0x59 - # DIAG_RAM_RW_F = 0x59 - DIAG_AKEY_VERIFY_F = 0x5A - # DIAG_CPU_RW_F = 0x5a - DIAG_BMP_HS_SCREEN_F = 0x5b - DIAG_CONFIG_COMM_F = 0x5c - DIAG_EXT_LOGMASK_F = 0x5d - DIAG_EVENT_REPORT_F = 0x60 - DIAG_STREAMING_CONFIG_F = 0x61 - DIAG_PARM_RETRIEVE_F = 0x62 - DIAG_STATUS_SNAPSHOT_F = 0x63 - DIAG_RPC_F = 0x64 - DIAG_GET_PROPERTY_F = 0x65 - DIAG_PUT_PROPERTY_F = 0x66 - DIAG_GET_GUID_F = 0x67 - DIAG_USER_CMD_F = 0x68 - DIAG_GET_PERM_PROPERTY_F = 0x69 - DIAG_PUT_PERM_PROPERTY_F = 0x6a - DIAG_PERM_USER_CMD_F = 0x6b - DIAG_GPS_SESS_CTRL_F = 0x6c - DIAG_GPS_GRID_F = 0x6d - DIAG_GPS_STATISTICS_F = 0x6E - DIAG_TUNNEL_F = 0x6f - DIAG_MAX_F = 0x70 - DIAG_SET_FTM_TEST_MODE = 0x72 - DIAG_EXT_BUILD_ID_F = 0x7c - - -class efs_cmds(Enum): - EFS2_DIAG_HELLO = 0 # Parameter negotiation packet - EFS2_DIAG_QUERY = 1 # Send information about EFS2 params - EFS2_DIAG_OPEN = 2 # Open a file - EFS2_DIAG_CLOSE = 3 # Close a file - EFS2_DIAG_READ = 4 # Read a file - EFS2_DIAG_WRITE = 5 # Write a file - EFS2_DIAG_SYMLINK = 6 # Create a symbolic link - EFS2_DIAG_READLINK = 7 # Read a symbolic link - EFS2_DIAG_UNLINK = 8 # Remove a symbolic link or file - EFS2_DIAG_MKDIR = 9 # Create a directory - EFS2_DIAG_RMDIR = 10 # Remove a directory - EFS2_DIAG_OPENDIR = 11 # Open a directory for reading - EFS2_DIAG_READDIR = 12 # Read a directory - EFS2_DIAG_CLOSEDIR = 13 # Close an open directory - EFS2_DIAG_RENAME = 14 # Rename a file or directory - EFS2_DIAG_STAT = 15 # Obtain information about a named file - EFS2_DIAG_LSTAT = 16 # Obtain information about a symbolic link - EFS2_DIAG_FSTAT = 17 # Obtain information about a file descriptor - EFS2_DIAG_CHMOD = 18 # Change file permissions - EFS2_DIAG_STATFS = 19 # Obtain file system information - EFS2_DIAG_ACCESS = 20 # Check a named file for accessibility - EFS2_DIAG_NAND_DEV_INFO = 21 # Get NAND device info - EFS2_DIAG_FACT_IMAGE_START = 22 # Start data output for Factory Image - EFS2_DIAG_FACT_IMAGE_READ = 23 # Get data for Factory Image - EFS2_DIAG_FACT_IMAGE_END = 24 # End data output for Factory Image - EFS2_DIAG_PREP_FACT_IMAGE = 25 # Prepare file system for image dump - EFS2_DIAG_PUT_DEPRECATED = 26 # Write an EFS item file - EFS2_DIAG_GET_DEPRECATED = 27 # Read an EFS item file - EFS2_DIAG_ERROR = 28 # Send an EFS Error Packet back through DIAG - EFS2_DIAG_EXTENDED_INFO = 29 # Get Extra information. - EFS2_DIAG_CHOWN = 30 # Change ownership - EFS2_DIAG_BENCHMARK_START_TEST = 31 # Start Benchmark - EFS2_DIAG_BENCHMARK_GET_RESULTS = 32 # Get Benchmark Report - EFS2_DIAG_BENCHMARK_INIT = 33 # Init/Reset Benchmark - EFS2_DIAG_SET_RESERVATION = 34 # Set group reservation - EFS2_DIAG_SET_QUOTA = 35 # Set group quota - EFS2_DIAG_GET_GROUP_INFO = 36 # Retrieve Q&R values - EFS2_DIAG_DELTREE = 37 # Delete a Directory Tree - EFS2_DIAG_PUT = 38 # Write a EFS item file in order - EFS2_DIAG_GET = 39 # Read a EFS item file in order - EFS2_DIAG_TRUNCATE = 40 # Truncate a file by the name - EFS2_DIAG_FTRUNCATE = 41 # Truncate a file by a descriptor - EFS2_DIAG_STATVFS_V2 = 42 # Obtains extensive file system info - - -O_RDONLY = 0 -O_WRONLY = 1 -O_RDWR = 2 -O_ACCMODE = O_RDONLY | O_WRONLY | O_RDWR -FS_DIAG_MAX_READ_REQ = 1024 - - -# define DIAG_NV_WRITE_F 0x27 -# define DIAG_NV_READ_F 0x26 - -class qcdiag(metaclass=LogBase): - def __init__(self, loglevel, portconfig, ep_in=-1, ep_out=-1): - self.portconfig = portconfig - self.nvlist = {} - self.ep_in = ep_in - self.ep_out = ep_out - self.__logger.setLevel(loglevel) - if loglevel == logging.DEBUG: - logfilename = "log.txt" - fh = logging.FileHandler(logfilename) - self.__logger.addHandler(fh) - import os, sys, inspect - current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) - parent_dir = os.path.dirname(os.path.dirname(current_dir)) - nvxml = os.path.join(parent_dir, "edlclient", "Config", "nvitems.xml") - e = ElementTree.parse(nvxml).getroot() - for atype in e.findall("nv"): - name = atype.get("name") - identifier = int(atype.get("id")) - self.nvlist[identifier] = name - - def prettyprint(self, data): - recv = "" - plain = "" - for i in range(len(data)): - inf = "%02X " % data[i] - recv += inf - if data[i] == 0x0D or data[i] == 0x0A or (0x20 <= data[i] <= 0x9A): - plain += chr(data[i]) - else: - plain += " " - if ((i + 1) % 16) == 0: - recv += "\n" - plain += "\n" - res = recv + "\n-----------------------------------------------\n" - if len(plain.replace(" ", "").replace("\n", "")) > 0: - res += plain - return res - - def decodestatus(self, data): - info = data[0] - if info == 0x13: - return "Invalid Command Response" - elif info == 0x14: - return "Invalid parameter Response" - elif info == 0x15: - return "Invalid packet length Response" - elif info == 0x17: - return "Send Security Mode" - elif info == 0x18: - return "Packet not allowed in this mode ( online vs offline )" - elif info == 0x42: - return "Invalid nv_read/write because SP is locked" - elif info == diag_cmds.DIAG_BAD_SEC_MODE_F.value: - return "Security privileges required" - else: - return "Command accepted" - - def connect(self): - self.cdc = UsbClass(portconfig=self.portconfig, loglevel=self.__logger.level) - self.hdlc = None - if self.cdc.connect(self.ep_in, self.ep_out): - self.hdlc = hdlc(self.cdc) - data = self.hdlc.receive_reply(1) - return True - return False - - def disconnect(self): - self.cdc.close(True) - - def send(self, cmd): - if self.hdlc != None: - return self.hdlc.send_cmd_np(cmd) - - def cmd_info(self): - reply = self.send(b"\x00") - return self.prettyprint(reply) - - def enforce_crash(self): - # ./diag.py -nvwrite 1027,01 enable adsp log NV_MDSP_MEM_DUMP_ENABLED_I - # ./diag.py -nvwrite 4399,01 enable download on reboot NV_DETECT_HW_RESET_I - res = self.send(b"\x4B\x25\x03\x00") - print(self.decodestatus(res)) - - def enter_downloadmode(self): - res = self.send(b"\x3A") - print(self.decodestatus(res)) - - def enter_saharamode(self): - self.hdlc.receive_reply() - res = self.send(b"\x4b\x65\x01\x00") - if res[0]==0x4b: - print("Done, switched to edl") - else: - print("Error switching to edl. Try again.") - self.disconnect() - - - def send_sp(self, sp="FFFFFFFFFFFFFFFFFFFE"): - if type(sp) == str: - sp = unhexlify(sp) - else: - sp = bytes(sp) - if len(sp) < 8: - print("SP length must be 8 bytes") - return - res = self.send(b"\x46" + sp) - if res[0] != 0x46: - res = self.send(b"\x25" + sp) - elif res[0] == 0x46: - if res[1] == 0x0: - print("Security Password is wrong") - elif res[1] == 0x1: - print("Security Password accepted.") - elif res[0] != 0x25: - print(self.decodestatus(res)) - return res - - def send_spc(self, spc="303030303030"): - if type(spc) == str: - spc = unhexlify(spc) - else: - spc = bytes(spc) - if len(spc) < 6: - print("SPC length must be 6 bytes") - return - res = self.send(b"\x41" + spc) - if res[0] != 0x41: - print(self.decodestatus(res)) - else: - if res[1] == 0x0: - print("SPC is wrong") - elif res[1] == 0x1: - print("SPC accepted.") - return res - - def DecodeNVItems(self, nvitem): - if nvitem.status == 0x1: - return "Internal DMSS use" - elif nvitem.status == 0x2: - return "Unrecognized command" - elif nvitem.status == 0x3: - return "NV memory full" - elif nvitem.status == 0x4: - return "Command failed" - elif nvitem.status == 0x5: - return "Inactive Item" - elif nvitem.status == 0x6: - return "Bad Parameter" - elif nvitem.status == 0x7: - return "Item was read-only" - elif nvitem.status == 0x8: - return "Item not defined for this target" - elif nvitem.status == 0x9: - return "No more free memory" - elif nvitem.status == 0xA: - return "Internal use" - elif nvitem.status == 0x0: - return "OK" - return "" - - def print_nvitem(self, item): - res, nvitem = self.read_nvitem(item) - if res: - info = self.DecodeNVItems(nvitem) - if res != False: - if nvitem.name != "": - ItemNumber = f"{hex(item)} ({nvitem.name}): " - else: - ItemNumber = hex(item) + ": " - returnanswer = "NVItem " + ItemNumber + info - print(returnanswer) - if nvitem.status == 0: - print("-----------------------------------------") - print(self.prettyprint(nvitem.data)) - else: - print(nvitem) - else: - print(nvitem) - - def print_nvitemsub(self, item, index): - res, nvitem = self.read_nvitemsub(item,index) - info = self.DecodeNVItems(nvitem) - if res != False: - if nvitem.name != "": - ItemNumber = f"{hex(item),hex(index)} ({nvitem.name}): " - else: - ItemNumber = hex(item)+","+hex(index) + ": " - returnanswer = "NVItem " + ItemNumber + info - print(returnanswer) - if nvitem.status == 0: - print("-----------------------------------------") - print(self.prettyprint(nvitem.data)) - else: - print(nvitem) - - def backup_nvitems(self, filename, errorlog=""): - nvitems = [] - pos = 0 - old = 0 - errors = "" - print("Dumping nvitems 0x0 to 0xFFFF.") - for item in range(0, 0xFFFF): - prog = int(float(pos) / float(0xFFFF) * float(100)) - if prog > old: - print_progress(prog, 100, prefix="Progress:", suffix=f"Complete, item {hex(item)}", bar_length=50) - old = prog - res, nvitem = self.read_nvitem(item) - if res != False: - if nvitem.status != 0x5: - nvitem.status = self.DecodeNVItems(nvitem) - nvitems.append(dict(id=nvitem.item, name=nvitem.name, data=hexlify(nvitem.data).decode("utf-8"), - status=nvitem.status)) - else: - errors += nvitem + "\n" - pos += 1 - js = json.dumps(nvitems) - with open(filename, "w") as write_handle: - write_handle.write(js) - if errorlog == "": - print(errors) - else: - with open(errorlog, "w") as write_handle: - write_handle.write(errors) - print("Done.") - - def unpackdata(self,data): - rlen = len(data) - idx = rlen - 1 - for i in range(0, rlen): - byte = data[rlen - i - 1] - if byte != 0: - break - idx = rlen - i - 1 - return data[:idx] - - def read_nvitem(self, item): - rawdata = 128 * b"\x00" - status = 0x0000 - nvrequest = b"\x26" + write_object(nvitem_type, item, rawdata, status)["raw_data"] - data = self.send(nvrequest) - if len(data) == 0: - data = self.send(nvrequest) - if len(data) > 0: - if data[0] == 0x26: - res = read_object(data[1:], nvitem_type) - name = "" - if item in self.nvlist: - name = self.nvlist[item] - data=self.unpackdata(res["rawdata"]) - res = nvitem(res["item"],0, data, res["status"], name) - return [True, res] - elif data[0] == 0x14: - return [False, f"Error 0x14 trying to read nvitem {hex(item)}."] - else: - return [False, f"Error {hex(data[0])} trying to read nvitem {hex(item)}."] - return [False, f"Empty request for nvitem {hex(item)}"] - - def read_nvitemsub(self, item, index): - rawdata = 128 * b"\x00" - status = 0x0000 - nvrequest = b"\x4B\x30\x01\x00" + write_object(subnvitem_type, item, index, rawdata, status)["raw_data"] - data = self.send(nvrequest) - if len(data) == 0: - data = self.send(nvrequest) - if len(data) > 0: - if data[0] == 0x4B: - res = read_object(data[4:], subnvitem_type) - name = "" - if item in self.nvlist: - name = self.nvlist[item] - data=self.unpackdata(res["rawdata"]) - res = nvitem(res["item"], index, data, res["status"], name) - return [True, res] - elif data[0] == 0x14: - return [False, f"Error 0x14 trying to read nvitem {hex(item)}."] - else: - return [False, f"Error {hex(data[0])} trying to read nvitem {hex(item)}."] - return [False, f"Empty request for nvitem {hex(item)}"] - - def convertimei(self,imei): - data=imei[0]+"A" - for i in range(1,len(imei),2): - data+=imei[i+1] - data+=imei[i] - return unhexlify("08"+data) - - def write_imei(self,imeis): - if "," in imeis: - imeis=imeis.split(",") - else: - imeis=[imeis] - index=0 - for imei in imeis: - data=self.convertimei(imei) - if index==0: - if not self.write_nvitem(550,data): - self.write_nvitemsub(550,index,data) - else: - self.write_nvitemsub(550,index,data) - index+=1 - - def write_nvitem(self, item, data): - rawdata = bytes(data) - while len(rawdata) < 128: - rawdata += b"\x00" - status = 0x0000 - nvrequest = b"\x27" + write_object(nvitem_type, item, rawdata, status)["raw_data"] - res = self.send(nvrequest) - if len(res) > 0: - if res[0] == 0x27: - res, nvitem = self.read_nvitem(item) - if res == False: - print(f"Error while writing nvitem {hex(item)} data, %s" % data) - else: - if nvitem.data != data: - print(f"Error while writing nvitem {hex(item)} data, verified data doesn't match") - else: - print(f"Successfully wrote nvitem {hex(item)}.") - return True - return False - else: - print(f"Error while writing nvitem {hex(item)} data, %s" % data) - - def write_nvitemsub(self, item, index, data): - rawdata = bytes(data) - while len(rawdata) < 128: - rawdata += b"\x00" - status = 0x0000 - nvrequest = b"\x4B\x30\x02\x00" + write_object(subnvitem_type, item, index, rawdata, status)["raw_data"] - res = self.send(nvrequest) - if len(res) > 0: - if res[0] == 0x4B: - res, nvitem = self.read_nvitemsub(item,index) - if res == False: - print(f"Error while writing nvitem {hex(item)} index {hex(index)} data, %s" % data) - else: - if nvitem.data != data: - print(f"Error while writing nvitem {hex(item)} index {hex(index)} data, verified data doesn't match") - else: - print(f"Successfully wrote nvitem {hex(item)} index {hex(index)}.") - return True - return False - else: - print(f"Error while writing nvitem {hex(item)} index {hex(index)} data, %s" % data) - - def efsread(self, filename): - alternateefs = b"\x4B\x3E\x19\x00" - standardefs = b"\x4B\x13\x19\x00" - resp = self.send(alternateefs) - if resp[0] == 0x4B: - efsmethod = 0x3E - else: - resp = self.send(standardefs) - if resp[0] == 0x4B: - efsmethod = 0x13 - else: - print("No known efs method detected for reading.") - return - - if filename == "": - return False - write_handle = open(filename, "wb") - if write_handle is None: - print("Error on writing file ....") - return False - - print("Reading EFS ....") - fefs = fs_factimage_read_info(0, 0, 0, 0) - - # EFS Cmd - buf = pack(" 0: - write_handle.write(resp[0x10:-0x1]) - fefs.fromdata(resp[0x8:0x10]) - fh.fromdata(resp[0x10:0x10 + (39 * 4)]) - - old = 0 - print_progress(0, 100, prefix="Progress:", suffix="Complete", bar_length=50) - total = fh.block_size * fh.block_count * (fh.page_size // 0x200) - - # Real start - for page in range(0, total): - # EFS Cmd - buf = pack(" old: - print_progress(pos, 100, prefix="Progress:", suffix="Page %d of %d" % (page, total), bar_length=50) - old = pos - - resp = self.send(buf) - if resp == 0: - resp = self.send(buf) - - if resp == 0 or resp == -1: - info = ("Page %08X !\n" % fefs.cluster_data_seqno) - print(info) - resp = self.send(buf) - if resp == 0 or resp == -1: - print("Data Error occured, " + info) - else: - dlen = len(resp) - 0x11 - if dlen == 0x200 or dlen == 0x800: - if resp[0x0] == 0x4B: - write_handle.write(resp[0x10:0x10 + dlen]) - fefs.fromdata(resp[0x8:0x10]) - else: - if (resp[0x0] == 0x13) and (resp[0x1] == 0x62) and (len(resp) > 0x200): - write_handle.write(resp[0x14:-4]) - fefs.fromdata(resp[0xc:0x14]) - if fefs.stream_state == 0x0: - break - else: - print("EFS Read error : Wrong size recieved at page %X" % page) - efserr = True - break - - print_progress(100, 100, prefix="Progress:", suffix="Complete", bar_length=50) - - buf = bytearray() - buf.append(0x4B) - buf.append(efsmethod) - buf.append(efs_cmds.EFS2_DIAG_FACT_IMAGE_END.value) # end factory image - buf.append(0x00) - - resp = self.send(buf) - if len(resp) == 0: - print("Phone does not respond. Maybe another software is blocking the port.") - return False - - write_handle.close() - if efserr == False: - print("Successfully read EFS.") - return True - else: - print("Error on reading EFS.") - return False - - def send_cmd(self, cmd): - cmdtosend = unhexlify(cmd) - reply = self.send(cmdtosend) - if reply[0] != cmdtosend[0]: - print(self.decodestatus(reply)) - result = self.prettyprint(reply) - return result - - def efsdiagerror(self, errcode): - if errcode == 0x40000001: - print("Inconsistent state.") - elif errcode == 0x40000002: - print("Invalid seq no.") - elif errcode == 0x40000003: - print("Directory not open.") - elif errcode == 0x40000004: - print("Directory entry not found.") - elif errcode == 0x40000005: - print("Invalid path.") - elif errcode == 0x40000006: - print("Path too long") - elif errcode == 0x40000007: - print("Too many open directories.") - elif errcode == 0x40000008: - print("Invalid directory entry.") - elif errcode == 0x40000009: - print("Too many open files.") - elif errcode == 0x4000000a: - print("Unknown filetype") - elif errcode == 0x4000000b: - print("Not nand falsh") - elif errcode == 0x4000000c: - print("Unavailable info") - else: - return 0 - return -1 - - def efs_closedir(self, efsmethod, dirp): - buf = pack(" 0: - [dirp, seqno, diag_errno, entry_type, mode, size, atime, mtime, ctime] = unpack(" 0: - rsize = dataleft - if rsize > FS_DIAG_MAX_READ_REQ: - rsize = FS_DIAG_MAX_READ_REQ - finfo = self.efs_read(efsmethod, fdata, rsize, offset) - if finfo == -1: - break - fdata, offset, bytes_read, data = finfo - write_handle.write(data) - offset += rsize - dataleft -= rsize - self.efs_close(efsmethod, fdata) - return num_bytes - - def efswritefile(self, srcpath, dstpath): - alternateefs = b"\x4B\x3E\x00\x00" + b"\x00" * 0x28 - standardefs = b"\x4B\x13\x00\x00" + b"\x00" * 0x28 - resp = self.send(alternateefs) - if resp[0] == 0x4B: - efsmethod = 0x3E - else: - resp = self.send(standardefs) - if resp[0] == 0x4B: - efsmethod = 0x13 - else: - logging.error("No known efs method detected for reading.") - return 0 - with open(srcpath, "rb") as rf: - fdata = self.efs_open(efsmethod, O_RDONLY, 0, srcpath) - if fdata == -1: - return 0 - mode, size, nlink, atime, mtime, ctime = self.efs_fstat(efsmethod, fdata) - if size == 0: - self.efs_close(efsmethod, fdata) - return 0 - """ - acr=(mode & O_ACCMODE) - if acr==O_RDONLY: - print("File can only be read. Aborting.") - self.efs_close(efsmethod, fdata) - return - """ - num_bytes = 0 - offset = 0 - size = os.fstat(srcpath).st_size - dataleft = size - while dataleft > 0: - rsize = dataleft - if rsize > FS_DIAG_MAX_READ_REQ: - rsize = FS_DIAG_MAX_READ_REQ - data = rf.read(rsize) - finfo = self.efs_write(efsmethod, fdata, offset, data) - if finfo == -1: - break - fdata, offset, bytes_written = finfo - offset += rsize - dataleft -= rsize - self.efs_close(efsmethod, fdata) - return num_bytes - - -class DiagTools(metaclass=LogBase): - def run(self, args): - self.interface = -1 - self.vid = None - self.pid = None - - if args.vid != "": - self.vid = int(args.vid, 16) - if args.pid != "": - self.pid = int(args.pid, 16) - if args.interface != "": - self.interface = int(args.interface, 16) - - logfilename = "diag.txt" - if args.debugmode: - if os.path.exists(logfilename): - os.remove(logfilename) - fh = logging.FileHandler(logfilename) - self.__logger.addHandler(fh) - self.__logger.setLevel(logging.DEBUG) - else: - self.__logger.setLevel(logging.INFO) - - connected = False - diag = None - if self.vid is None or self.pid is None: - diag = qcdiag(loglevel=self.__logger.level, portconfig=default_diag_vid_pid) - connected = diag.connect() - else: - diag = qcdiag(loglevel=self.__logger.level, portconfig=[[self.vid, self.pid, self.interface]]) - connected = diag.connect() - if connected: - cmd = args.cmd - if cmd=="sp": - diag.send_sp(args.spval) - elif cmd=="spc": - diag.send_spc(args.spcval) - elif cmd=="cmd": - if args.cmdval=="": - print("cmd needed as hex string, example: 00") - else: - print(diag.send_cmd(args.cmdval)) - elif cmd=="info": - print(diag.cmd_info()) - elif cmd=="download": - diag.enter_downloadmode() - elif cmd=="sahara": - diag.enter_saharamode() - elif cmd=="crash": - diag.enforce_crash() - elif cmd=="efslistdir": - print(diag.efslistdir(args.path)) - elif cmd=="efsreadfile": - if args.src=="" or args.dst=="": - print("Usage: -efsreadfile -src srcfile -dst dstfile") - sys.exit() - print(diag.efsreadfile(args.src,args.dst)) - elif cmd=="nvread": - if "0x" in args.nvitem: - nvitem = int(args.nvitem, 16) - else: - nvitem = int(args.nvitem) - diag.print_nvitem(nvitem) - elif cmd=="nvreadsub": - if args.nvitem is None or args.nvindex is None: - print("Usage: nvreadsub [nvitem] [nvindex]") - exit(1) - nv = args.nvreadsub.split(",") - if "0x" in args.nvitem: - nvitem = int(args.nvitem, 16) - else: - nvitem = int(args.nvitem) - if "0x" in nv[1]: - nvindex = int(args.nvindex, 16) - else: - nvindex = int(args.nvindex) - diag.print_nvitemsub(nvitem,nvindex) - elif cmd=="nvwrite": - if args.data is None: - print("NvWrite requires data to write") - sys.exit() - if "0x" in args.nvitem: - nvitem = int(args.nvitem, 16) - else: - nvitem = int(args.nvitem) - data = unhexlify(args.data) - diag.write_nvitem(nvitem, data) - elif cmd=="nvwritesub": - if args.nvitem is None or args.nvindex is None or args.data is None: - print("NvWriteSub requires item, index and data to write") - sys.exit() - if "0x" in args.nvitem: - nvitem = int(args.nvitem, 16) - else: - nvitem = int(args.nvitem) - if "0x" in args.nvindex: - nvindex = int(args.nvindex, 16) - else: - nvindex = int(args.nvindex) - data = unhexlify(args.data) - diag.write_nvitemsub(nvitem, nvindex, data) - elif cmd=="nvbackup": - diag.backup_nvitems(args.filename, "error.log") - elif cmd=="writeimei": - diag.write_imei(args.imei) - elif cmd=="efsread": - diag.efsread(args.filename) - else: - print("A command is required. Use -cmd \"data\" for sending requests.") - print() - print("Valid commands are:") - print("-------------------") - print("info cmd sp spc nvread nvreadsub" + - " nvwrite writeimei nvwritesub nvbackup efsread efsreadfile" + - " efslistdir download sahara crash") - print() - diag.disconnect() - sys.exit() - else: - print("No diag device detected. Use -pid and -vid options. See -h for help.") - diag.disconnect() - sys.exit() - - -def main(): - info = "Qualcomm Diag Client (c) B.Kerler 2019-2021." - parser = argparse.ArgumentParser(description=info) - print("\n" + info + "\n---------------------------------------\n") - parser.add_argument("-vid", metavar="", help="[Option] Specify vid", default="") - parser.add_argument("-pid", metavar="", help="[Option] Specify pid", default="") - parser.add_argument("-interface", metavar="", help="[Option] Specify interface number, default=0)", - default="0") - parser.add_argument("--debugmode", help="[Option] Enable verbose logging", action="store_true") - - subparser = parser.add_subparsers(dest="cmd", help="Valid commands are:\ninfo cmd sp spc nvread nvreadsub" + - " nvwrite writeimei nvwritesub nvbackup efsread efsreadfile\n" + - " efslistdir download sahara crash") - - parser_info = subparser.add_parser("info", help="[Option] Get diag info") - - parser_cmd = subparser.add_parser("cmd", help="Send command") - parser_cmd.add_argument("cmdval", help="cmd to send (hexstring), default: 00", - default="", const="00", nargs="?") - - parser_sp = subparser.add_parser("sp", help="Send Security password") - parser_sp.add_argument("spval", help="Security password to send, default: FFFFFFFFFFFFFFFE", - default="FFFFFFFFFFFFFFFE", nargs="?") - - parser_spc = subparser.add_parser("spc", help="Send Security Code") - parser_spc.add_argument("spcval", help="Security code to send, default: 303030303030", - default="303030303030", nargs="?") - - parser_nvread = subparser.add_parser("nvread", help="Read nvitem") - parser_nvread.add_argument("nvitem", help="[Option] NVItem to read", default="") - - parser_nvreadsub = subparser.add_parser("nvreadsub", help="Read nvitem using subsystem") - parser_nvreadsub.add_argument("nvitem", help="[Option] NVItem to read", default="") - parser_nvreadsub.add_argument("nvindex", help="[Option] Index to read", default="") - - parser_nvwrite = subparser.add_parser("nvwrite", help="Write nvitem") - parser_nvwrite.add_argument("nvitem", help="[Option] NVItem to write", default="") - parser_nvwrite.add_argument("data", help="[Option] Data to write", default="") - - parser_nvwritesub = subparser.add_parser("nvwritesub", help="Write nvitem using subsystem") - parser_nvwritesub.add_argument("nvitem", help="[Option] NVItem to read", default="") - parser_nvwritesub.add_argument("nvindex", help="[Option] Index to read", default="") - parser_nvwritesub.add_argument("data", help="[Option] Data to write", default="") - - parser_writeimei = subparser.add_parser("writeimei", help="Write imei") - parser_writeimei.add_argument("imei", metavar=(""), help="[Option] IMEI to write", default="") - - - parser_nvbackup = subparser.add_parser("nvbackup", help="Make nvitem backup as json") - parser_nvbackup.add_argument("filename", help="[Option] Filename to write to", default="") - - parser_efsread = subparser.add_parser("efsread", help="Read efs") - parser_efsread.add_argument("filename", help="[Option] Filename to write to", default="") - - parser_efsreadfile = subparser.add_parser("efsreadfile", help="Read efs file") - parser_efsreadfile.add_argument("src", help="[Option] Source filename", default="") - parser_efsreadfile.add_argument("dst", help="[Option] Destination filename", default="") - - parser_efslistdir = subparser.add_parser("efslistdir", help="List efs directory") - parser_efslistdir.add_argument("path", help="[Option] Path to list", default="") - - parser_download = subparser.add_parser("download", help="[Option] Switch to sahara mode") - - parser_sahara = subparser.add_parser("sahara", help="[Option] Switch to sahara mode") - - parser_crash = subparser.add_parser("crash", help="[Option] Enforce crash") - - - args = parser.parse_args() - dg = DiagTools() - dg.run(args) - - -if __name__ == "__main__": - main() - diff --git a/edlclient/Tools/qc_diag b/edlclient/Tools/qc_diag new file mode 120000 index 0000000..267a959 --- /dev/null +++ b/edlclient/Tools/qc_diag @@ -0,0 +1 @@ +qc_diag.py \ No newline at end of file diff --git a/edlclient/Tools/sierrakeygen b/edlclient/Tools/sierrakeygen deleted file mode 100755 index fd08e43..0000000 --- a/edlclient/Tools/sierrakeygen +++ /dev/null @@ -1,591 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# (c) B.Kerler 2019-2020 under MIT license -# If you use my code, make sure you refer to my name -# If you want to use in a commercial product, ask me before integrating it - -import serial -import sys -import argparse -import time -import serial.tools.list_ports -from telnetlib import Telnet -from binascii import hexlify, unhexlify - -try: - from edlclient.Library.utils import LogBase -except Exception as e: - import os,sys,inspect - current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) - parent_dir = os.path.dirname(current_dir) - sys.path.insert(0, parent_dir) - from Library.utils import LogBase - -''' -C7 = 7 0 0 2 7 5 0 -C6 = 3 1 7 0 3 0 1 -C5 = 0 2 5 3 0 3 2 -C8 = 1 3 3 1 5 7 3 -C4 = 5 4 1 4 1 1 4 -''' -prodtable = { - "MDM8200": dict(openlock=0, openmep=1, opencnd=0, clen=8, init=[1, 3, 5, 7, 0], - run="resultbuffer[i]=self.SierraAlgo(challenge[i], 2, 4, 1, 3, 0, 3, 4, 0)"), - # MC878XC_F1.2.3.15 verified, key may be stored in nvitem 0x4e21;MC8700 M3.0.9.0 verified - "MDM9200": dict(openlock=0, openmep=1, opencnd=0, clen=8, init=[7, 3, 0, 1, 5], - run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), - # AC881U, EM8805 SWI9X15C_05.05.58.00, at!openlock?6A1F1CEA298A14B0 => AT!OPENLOCK="3A9EA70D86FEE58C" - "MDM9200_V1": dict(openlock=2, openmep=1, opencnd=0, clen=8, init=[7, 3, 0, 1, 5], - run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), # AC710 - "MDM9200_V2": dict(openlock=3, openmep=1, opencnd=0, clen=8, init=[7, 3, 0, 1, 5], - run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), # AC775 - "MDM9200_V3": dict(openlock=8, openmep=1, opencnd=8, clen=8, init=[7, 3, 0, 1, 5], - run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), # AC775 - "MDM9x15": dict(openlock=0, openmep=1, opencnd=0, clen=8, init=[7, 3, 0, 1, 5], - run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), - # 9x15C 06.03.32.02, AC340U 1.13.12.14 verified, #AT!CUSTOM=\"ADBENABLE\",1 - "MDM9x07": dict(openlock=9, openmep=10, opencnd=9, clen=8, init=[7, 3, 0, 1, 5], - run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), - # SWI9X07Y_02.25.02.01 - "MDM9x30": dict(openlock=5, openmep=4, opencnd=5, clen=8, init=[7, 3, 0, 1, 5], - run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), - # MC7455_2.30.01.01 #4 - "MDM9x30_V1": dict(openlock=17, openmep=15, opencnd=17, clen=8, init=[7, 3, 0, 1, 5], - run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), - # AC791L/AC790S NTG9X35C_02.08.29.00 - "MDM9x40": dict(openlock=11, openmep=12, opencnd=11, clen=8, init=[7, 3, 0, 1, 5], - run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), # AC815s - "MDM9x50": dict(openlock=7, openmep=6, opencnd=7, clen=8, init=[7, 3, 0, 1, 5], - run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), # EM7565 - "MDM9x06": dict(openlock=20, openmep=19, opencnd=20, clen=8, init=[7, 3, 0, 1, 5], - run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), # WP77xx - "SDX55": dict(openlock=22, openmep=21, opencnd=22, clen=8, init=[7, 3, 0, 1, 5], #MR5100 - run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), - "MDM9x15A": dict(openlock=24, openmep=23, opencnd=24, clen=8, init=[7, 3, 0, 1, 5], #AC779S - run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), - -} - -infotable = { - "MDM8200": ["M81A", "M81B", "AC880", "AC881", "MC8780", "MC8781", "AC880E", "AC881E", "EM8780", "EM8781", - "MC8780V", "MC8781V", "MC8700", "AC308U"], - "MDM9200": ["AC710", "MC8775", "MC8775V", "AC875", "MC8700", "AC313U", "MC8801", "MC7700", "MC7750", "MC7710", - "EM7700", "770S", "781S"], - "MDM9200_V1": ["AC710", "MC8775", "MC8775V", "AC875", "MC8700", "AC313U", "MC8801", "MC7700", "MC7750", - "MC7710", "EM7700"], - "MDM9200_V2": ["AC775", "PC7200"], - "MDM9200_V3": ["AC775"], - "MDM9x07": ["SWI9X07Y", "WP76xx"], - "MDM9x06": ["SWI9X06Y", "WP77xx"], - "MDM9x15": ["SWI9X15C", "AR7550", "AR7552", "AR7554", "EM7355", "EM7655", "MC7354", "WP7100", "WP7102", "WP7104", - "MC7305", "EM7305", "MC8805", "EM8805", "MC7350", "MC7350-L", "MC7802", "MC7304", "AR7556", "AR7558", - "WP75xx", "WP85xx", "WP8548", "WP8548G", "AC340U"], - "MDM9x15A": ["AC779S"], - "MDM9x30": ["EM7455", "MC7455", "EM7430", "MC7430"], - "MDM9x30_V1": ["Netgear AC790/MDM9230"], - "MDM9x40": ["AC815s", "AC785s", "AC797S", "MR1100"], - "MDM9x50": ["EM7565", "EM7565-9", "EM7511", "EM7411"], - "SDX55" : ["MR5100","ac797-100eus"] -} - -keytable = bytearray([0xF0, 0x14, 0x55, 0x0D, 0x5E, 0xDA, 0x92, 0xB3, 0xA7, 0x6C, 0xCE, 0x84, 0x90, 0xBC, 0x7F, 0xED, - # 0 MC8775_H2.0.8.19 !OPENLOCK, !OPENCND .. MC8765V,MC8765,MC8755V,MC8775,MC8775V,MC8775,AC850, - # AC860,AC875,AC881,AC881U,AC875, AC340U 1.13.12.14 - 0x61, 0x94, 0xCE, 0xA7, 0xB0, 0xEA, 0x4F, 0x0A, 0x73, 0xC5, 0xC3, 0xA6, 0x5E, 0xEC, 0x1C, 0xE2, - # 1 MC8775_H2.0.8.19 AC340U, OPENMEP default - 0x39, 0xC6, 0x7B, 0x04, 0xCA, 0x50, 0x82, 0x1F, 0x19, 0x63, 0x36, 0xDE, 0x81, 0x49, 0xF0, 0xD7, - # 2 AC750,AC710,AC7XX,SB750A,SB750,PC7000,AC313u OPENMEP - 0xDE, 0xA5, 0xAD, 0x2E, 0xBE, 0xE1, 0xC9, 0xEF, 0xCA, 0xF9, 0xFE, 0x1F, 0x17, 0xFE, 0xED, 0x3B, - # 3 AC775,PC7200 - 0xFE, 0xD4, 0x40, 0x52, 0x2D, 0x4B, 0x12, 0x5C, 0xE7, 0x0D, 0xF8, 0x79, 0xF8, 0xC0, 0xDD, 0x37, - # 4 MC7455_02.30.01.01 OPENMEP - 0x3B, 0x18, 0x99, 0x6B, 0x57, 0x24, 0x0A, 0xD8, 0x94, 0x6F, 0x8E, 0xD9, 0x90, 0xBC, 0x67, 0x56, - # 5 MC7455_02.30.01.01 OPENLOCK - 0x47, 0x4F, 0x4F, 0x44, 0x4A, 0x4F, 0x42, 0x44, 0x45, 0x43, 0x4F, 0x44, 0x49, 0x4E, 0x47, 0x2E, - # 6 SWI9x50 Openmep Key SWI9X50C_01.08.04.00 - 0x4F, 0x4D, 0x41, 0x52, 0x20, 0x44, 0x49, 0x44, 0x20, 0x54, 0x48, 0x49, 0x53, 0x2E, 0x2E, 0x2E, - # 7 SWI9x50 Openlock Key SWI9X50C_01.08.04.00 - 0x8F, 0xA5, 0x85, 0x05, 0x5E, 0xCF, 0x44, 0xA0, 0x98, 0x8B, 0x09, 0xE8, 0xBB, 0xC6, 0xF7, 0x65, - # 8 MDM8200 Special - 0x4D, 0x42, 0xD8, 0xC1, 0x25, 0x44, 0xD8, 0xA0, 0x1D, 0x80, 0xC4, 0x52, 0x8E, 0xEC, 0x8B, 0xE3, - # 9 SWI9x07 Openlock Key 02.25.02.01 - 0xED, 0xA9, 0xB7, 0x0A, 0xDB, 0x85, 0x3D, 0xC0, 0x92, 0x49, 0x7D, 0x41, 0x9A, 0x91, 0x09, 0xEE, - # 10 SWI9x07 Openmep Key 02.25.02.01 - 0x8A, 0x56, 0x03, 0xF0, 0xBB, 0x9C, 0x13, 0xD2, 0x4E, 0xB2, 0x45, 0xAD, 0xC4, 0x0A, 0xE7, 0x52, - # 11 NTG9X40C_11.14.08.11 / mdm9x40r11_core AC815s / SWI9x50 MR1100 Openlock Key - 0x2A, 0xEF, 0x07, 0x2B, 0x19, 0x60, 0xC9, 0x01, 0x8B, 0x87, 0xF2, 0x6E, 0xC1, 0x42, 0xA8, 0x3A, - # 12 SWI9x50 MR1100 Openmep Key - 0x28, 0x55, 0x48, 0x52, 0x24, 0x72, 0x63, 0x37, 0x14, 0x26, 0x37, 0x50, 0xBE, 0xFE, 0x00, 0x00, - # 13 SWI9x50 Unknown key - 0x22, 0x63, 0x48, 0x02, 0x24, 0x72, 0x27, 0x37, 0x19, 0x26, 0x37, 0x50, 0xBE, 0xEF, 0xCA, 0xFE, - # 14 SWI9x50,SWI9X06Y IMEI nv key - 0x98, 0xE1, 0xC1, 0x93, 0xC3, 0xBF, 0xC3, 0x50, 0x8D, 0xA1, 0x35, 0xFE, 0x50, 0x47, 0xB3, 0xC4, - # 15 NTG9X35C_02.08.29.00 Openmep Key AC791L/AC790S Old - 0x61, 0x94, 0xCE, 0xA7, 0xB0, 0xEA, 0x4F, 0x0A, 0x73, 0xC5, 0xC3, 0xA6, 0x5E, 0xEC, 0x1C, 0xE2, - # 16 NTG9X35C_02.08.29.00 Openmep Key AC791/AC790S, NTGX55_10.25.15.02 MR5100 Alternative, NTG9X40C_30.00.12.00 Alternative - 0xC5, 0x50, 0x40, 0xDA, 0x23, 0xE8, 0xF4, 0x4C, 0x29, 0xE9, 0x07, 0xDE, 0x24, 0xE5, 0x2C, 0x1D, - # 17 NTG9X35C_02.08.29.00 Openlock Key AC791/AC790S Old - 0xF0, 0x14, 0x55, 0x0D, 0x5E, 0xDA, 0x92, 0xB3, 0xA7, 0x6C, 0xCE, 0x84, 0x90, 0xBC, 0x7F, 0xED, - # 18 NTG9X35C_02.08.29.00 Openlock Key AC791/AC790S, NTGX55_10.25.15.02 MR5100 Alternative, NTG9X40C_30.00.12.00 Alternative - 0x78, 0x19, 0xC5, 0x6D, 0xC3, 0xD8, 0x25, 0x3E, 0x51, 0x60, 0x8C, 0xA7, 0x32, 0x83, 0x37, 0x9D, - # 19 SWI9X06Y_02.14.04.00 Openmep Key WP77xx - 0x12, 0xF0, 0x79, 0x6B, 0x19, 0xC7, 0xF4, 0xEC, 0x50, 0xF3, 0x8C, 0x40, 0x02, 0xC9, 0x43, 0xC8, - # 20 SWI9X06Y_02.14.04.00 Openlock Key WP77xx - 0x49, 0x42, 0xFF, 0x76, 0x8A, 0x95, 0xCF, 0x7B, 0xA3, 0x47, 0x5F, 0xF5, 0x8F, 0xD8, 0x45, 0xE4, - # 21 NTGX55 Openmep Key, NTGX55_10.25.15.02 MR5100, NTG9X40C_30.00.12.00 - 0xF8, 0x1A, 0x3A, 0xCC, 0xAA, 0x2B, 0xA5, 0xE8, 0x8B, 0x53, 0x5A, 0x55, 0xB9, 0x65, 0x57, 0x98, - # 22 NTGX55 Openlock Key, NTGX55_10.25.15.02 MR5100, NTG9X40C_30.00.12.00 - 0x54, 0xC9, 0xC7, 0xA4, 0x02, 0x1C, 0xB0, 0x11, 0x05, 0x22, 0x39, 0xB7, 0x84, 0xEF, 0x16, 0xCA, - # 23 NTG9X15A Openlock Key, NTG9X15A_01.08.02.00 - 0xC7, 0xE6, 0x39, 0xFE, 0x0A, 0xC7, 0xCA, 0x4D, 0x49, 0x8F, 0xD8, 0x55, 0xEB, 0x1A, 0xCD, 0x8A - # 24 NTG9X15A Openlock Key, NTG9X15A_01.08.02.00 - ]) - - -class SierraGenerator(): - tbl = bytearray() - rtbl = bytearray() - - def __init__(self): - for i in range(0, 0x14): - self.rtbl.append(0x0) - for i in range(0, 0x100): - self.tbl.append(0x0) - - def run(self, devicegeneration, challenge, type): - challenge = bytearray(unhexlify(challenge)) - - self.devicegeneration = devicegeneration - if not devicegeneration in prodtable: - print("Sorry, " + devicegeneration + " not supported.") - exit(0) - - mepid = prodtable[devicegeneration]["openmep"] - cndid = prodtable[devicegeneration]["opencnd"] - lockid = prodtable[devicegeneration]["openlock"] - clen = prodtable[devicegeneration]["clen"] - if len(challenge) < clen: - challenge=[0 for i in range(0, clen - len(challenge))] - - challengelen = len(challenge) - if type == 0: # lockkey - idf = lockid - elif type == 1: # mepkey - idf = mepid - elif type == 2: # cndkey - idf = cndid - - key = keytable[idf * 16:(idf * 16) + 16] - resp = self.SierraKeygen(challenge=challenge, key=key, challengelen=challengelen, keylen=16)[:challengelen] - resp = hexlify(resp).decode('utf-8').upper() - return resp - - def selftest(self): - test_table=[ - {"challenge": "8101A18AB3C3E66A", "devicegeneration": "MDM9x15", "response": "D1E128FCA8A963ED"}, - {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9x40", "response": "1033773720F6EE66"}, - {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9x30", "response": "1E02CE6A98B7DD2A"}, - {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9x50", "response": "32AB617DB4B1C205"}, - {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9x06", "response": "28D718CCD669DEDE"}, - {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9x07", "response": "F5A4C9A0D402E34E"}, - {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM8200", "response": "EE702212D9C12FAB"}, - {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9200_V1", "response": "A9A4E76E2653F753"}, - {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9200_V2", "response": "8B0FAB4B6F81B080"}, - {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9200_V3", "response": "4A69AD8A69F390E0"}, - {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9x30_V1", "response": "6A5E4C9CBCBDA7DC"}, - {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9200", "response": "EEDBF8BFF8DAE346"}, - {"challenge": "20E253156762DACE", "devicegeneration": "SDX55", "response": "03940D7067145323"}, - {"challenge": "2387885E7D290FEE", "devicegeneration": "MDM9x15A", "response": "DC3E51897BAA9C1E"}, - ] - for test in test_table: - challenge = test["challenge"] - devicegeneration = test["devicegeneration"] - response = test["response"] - openlock = self.run(devicegeneration, challenge, 0) - padding = " " * (16 - len(devicegeneration)) - if openlock != response: - print(devicegeneration+padding+" FAILED!") - else: - print(devicegeneration+padding+" PASSED :)") - - def SierraPreInit(self, counter, key, keylen, challengelen, mcount): - if counter != 0: - tmp2 = 0 - i = 1 - while i < counter: - i = 2 * i + 1 - while True: - tmp = mcount - mcount = tmp + 1 - challengelen = (key[tmp & 0xFF] + self.tbl[(challengelen & 0xFF)]) & 0xFF - if mcount >= keylen: - mcount = 0 - challengelen = ((challengelen & 0xFF) + keylen) & 0xFF - tmp2 = tmp2 + 1 - tmp3 = ((challengelen & 0xFF) & i) & 0xFF - if tmp2 >= 0xB: - tmp3 = counter % tmp3 - if tmp3 <= counter: - break - counter = tmp3 & 0xFF - return [counter, challengelen, mcount] - - def SierraInit(self, key, keylen): - if keylen == 0 or keylen > 0x20: - retval = [0, keylen] - elif 1 <= keylen <= 0x20: - self.tbl = [(i&0xFF) for i in range(0, 0x100)] - mcount = 0 - cl = keylen & 0xffffff00 - i = 0xFF - while i > -1: - t, cl, mcount = self.SierraPreInit(i, key, keylen, cl, mcount) - m = self.tbl[i] - self.tbl[i] = self.tbl[(t & 0xff)] - i = i - 1 - self.tbl[(t & 0xFF)] = m - self.rtbl[0] = self.tbl[prodtable[self.devicegeneration]["init"][0]] if \ - prodtable[self.devicegeneration]["init"][0] != 0 else self.tbl[(cl & 0xFF)] - self.rtbl[1] = self.tbl[prodtable[self.devicegeneration]["init"][1]] if \ - prodtable[self.devicegeneration]["init"][1] != 0 else self.tbl[(cl & 0xFF)] - self.rtbl[2] = self.tbl[prodtable[self.devicegeneration]["init"][2]] if \ - prodtable[self.devicegeneration]["init"][2] != 0 else self.tbl[(cl & 0xFF)] - self.rtbl[3] = self.tbl[prodtable[self.devicegeneration]["init"][3]] if \ - prodtable[self.devicegeneration]["init"][3] != 0 else self.tbl[(cl & 0xFF)] - self.rtbl[4] = self.tbl[prodtable[self.devicegeneration]["init"][4]] if \ - prodtable[self.devicegeneration]["init"][4] != 0 else self.tbl[(cl & 0xFF)] - retval = [1, keylen] - return retval - - def sierra_calc8F(self, challenge, a=0, b=1, c=2, d=3, e=4, ret=0, ret2=2): - # MDM9200 - self.rtbl[b] = (self.rtbl[b] + self.tbl[(self.rtbl[d] & 0xFF)]) & 0xFF - uVar2 = self.rtbl[c] & 0xFF - bVar1 = self.tbl[uVar2] - uVar4 = self.rtbl[b] & 0xFF - self.tbl[uVar2] = self.tbl[uVar4] - self.rtbl[d] = (self.rtbl[d] + 1) & 0xFF - uVar5 = self.rtbl[a] & 0xFF - self.tbl[uVar4] = self.tbl[uVar5] - uVar3 = self.rtbl[d] & 0xFF - self.tbl[uVar5] = self.tbl[uVar3] - self.tbl[uVar3] = bVar1 - self.rtbl[ret] = challenge # c - self.rtbl[ret2] = self.tbl[self.tbl[(self.tbl[(self.rtbl[e] + self.tbl[bVar1]) & 0xFF] + ( - self.tbl[uVar5] & 0xFF) + (self.tbl[uVar2] & 0xFF) & 0xff) & 0xFF] & 0xFF] ^ self.tbl[ - ((self.tbl[uVar4] & 0xFF) + (bVar1 & 0xff)) & 0xFF] ^ challenge # a - self.rtbl[e] = (self.rtbl[e] + self.tbl[bVar1]) & 0xFF - return self.rtbl[ret2] & 0xFF # a - - def SierraAlgo(self, challenge, a=0, b=1, c=2, d=3, e=4, ret=3, ret2=1, flag=1): # M9x15 - v6 = self.rtbl[e] - v0 = (v6 + 1) & 0xFF - self.rtbl[e] = v0 - self.rtbl[c] = (self.tbl[v6 + flag & 0xFF] + self.rtbl[c]) & 0xFF - v4 = self.rtbl[c] & 0xFF - v2 = self.rtbl[b] & 0xFF - v1 = self.tbl[(v2 & 0xFF)] - self.tbl[(v2 & 0xFF)] = self.tbl[(v4 & 0xFF)] - v5 = self.rtbl[d] & 0xFF - self.tbl[(v4 & 0xFF)] = self.tbl[(v5 & 0xFF)] - self.tbl[(v5 & 0xFF)] = self.tbl[(v0 & 0xFF)] - self.tbl[v0] = v1 & 0xFF - u = self.tbl[(self.tbl[( - self.tbl[((self.rtbl[a] + self.tbl[(v1 & 0xFF)]) & 0xFF)] + self.tbl[(v5 & 0xFF)] + self.tbl[ - (v2 & 0xFF)] & 0xff)] & 0xFF)] - v = self.tbl[((self.tbl[(v4 & 0xFF)] + v1) & 0xFF)] - self.rtbl[ret] = u ^ v ^ challenge - self.rtbl[a] = (self.tbl[(v1 & 0xFF)] + self.rtbl[a]) & 0xFF - self.rtbl[ret2] = challenge & 0xFF - return self.rtbl[ret] & 0xFF - - def SierraFinish(self): - self.tbl = [0 for _ in range(0, 0x100)] - self.rtbl[0] = 0 - self.rtbl[1] = 0 - self.rtbl[2] = 0 - self.rtbl[3] = 0 - self.rtbl[4] = 0 - return 1 - - def SierraKeygen(self, challenge:bytearray, key: bytearray, challengelen:int, keylen:int): - challenge = challenge - resultbuffer=bytearray([0 for i in range(0, 0x100 + 1)]) - ret, keylen = self.SierraInit(key, keylen) - if ret: - for i in range(0, challengelen): - exec(prodtable[self.devicegeneration]["run"]) # uses challenge - self.SierraFinish() - return resultbuffer - - -class connection: - def __init__(self, port=""): - self.serial = None - self.tn = None - self.connected = False - if port == "": - port = self.detect(port) - if port == "": - self.tn = Telnet("192.168.1.1", 5510) - self.connected = True - if port != "": - self.serial = serial.Serial(port=port, baudrate=115200, bytesize=8, parity='N', stopbits=1, timeout=1) - self.connected = self.serial.is_open - - def detect(self, port): - if port == "": - for port in serial.tools.list_ports.comports(): - if port.vid == 0x1199: - portid = port.location[-1:] - if int(portid) == 3: - print("Detected Sierra Wireless device at: " + port.device) - return port.device - elif port.vid == 0x8046: - portid = port.location[-1:] - if int(portid) == 3: - print("Detected Netgear device at: " + port.device) - return port.device - - return "" - - def readreply(self): - info = [] - if self.serial is not None: - while (True): - tmp = self.serial.readline().decode('utf-8').replace('\r', '').replace('\n', '') - if "OK" in info: - return info - elif ("ERROR" in info) or info == "": - return -1 - info.append(tmp) - return info - - def send(self, cmd): - if self.tn is not None: - self.tn.write(bytes(cmd + "\r", 'utf-8')) - time.sleep(0.05) - data = "" - while True: - tmp = self.tn.read_eager() - if tmp != b"": - data += tmp.strip().decode('utf-8') - else: - break - return data.split("\r\n") - elif self.serial is not None: - self.serial.write(bytes(cmd + "\r", 'utf-8')) - time.sleep(0.05) - return self.readreply() - - def close(self): - if self.tn is not None: - self.tn.close() - self.connected = False - if self.serial is not None: - self.serial.close() - self.connected = False - -class SierraKeygen(metaclass=LogBase): - def __init__(self,cn,devicegeneration=None): - self.cn=cn - self.keygen = SierraGenerator() - if devicegeneration==None: - self.detectdevicegeneration() - else: - self.devicegeneration=devicegeneration - - def run_selftest(self): - print("Running self-test ...") - self.keygen.selftest() - - def detectdevicegeneration(self): - if self.cn.connected: - info = self.cn.send("ATI") - if info != -1: - revision = "" - model = "" - for line in info: - if "Revision" in line: - revision = line.split(":")[1].strip() - if "Model" in line: - model = line.split(":")[1].strip() - if revision != "": - if "9200" in revision: - devicegeneration = "MDM9200" #AC762S NTG9200H2_03.05.14.12ap - if "9X07" in revision: - devicegeneration = "MDM9x07" - elif "9X25" in revision: - if "NTG9X25C" in revision: - devicegeneration = "MDM9200" #AC781S NTG9X25C_01.00.57.00 - elif "9X15" in revision: - if "NTG9X15A" in revision: - devicegeneration = "MDM9x15A" #Aircard 779S - elif "NTG9X15C" in revision: - devicegeneration = "MDM9200" #AC770S NTG9X15C_01.18.02.00 - elif "9X15A" in revision: - devicegeneration = "MDM9x15A" - else: - devicegeneration = "MDM9x15" - elif "9X30" in revision: - if "NTG9X35C" in revision: #790S NTG9X35C_11.11.15.03 - devicegeneration = "MDM9x30_V1" - else: - devicegeneration = "MDM9x30" - elif "9X40" in revision and not "9X40C" in revision: - devicegeneration = "MDM9x40" - elif "9X50" in revision: - if "NTG9X50" in revision: - devicegeneration = "MDM9x40" #MR1100,AC797S NTG9X50C_12.06.03.00 - else: - devicegeneration = "MDM9x50" - elif "9X06" in revision: - devicegeneration = "MDM9x06" - elif "X55" in revision or "9X40C" in revision: - if "NTGX55" in revision: #MR5100 NTGX55_10.25.15.02 - devicegeneration = "SDX55" - devicegeneration = "SDX55" - #Missing: - # SDX24 Sierra - # MR2100 NTGX24_10.17.03.00 - # SDX55 Sierra - # AC810S NTG9X40C_11.14.08.16 - # AC800S NTG9X40C_11.14.07.00 - self.devicegeneration=devicegeneration - else: - print("Error on getting ATI modem response. Wrong port? Aborting.") - self.cn.close() - exit(0) - - def openlock(self): - print("Device generation detected: " + self.devicegeneration) - #print("Sending AT!ENTERCND=\"A710\" request.") - #info = self.cn.send("AT!ENTERCND=\"A710\"") - #if info == -1: - # print("Uhoh ... invalid entercnd password. Aborting ...") - # return - print("Sending AT!OPENLOCK? request") - info = self.cn.send("AT!OPENLOCK?") - challenge = "" - if info != -1: - if len(info) > 2: - challenge = info[1] - else: - print("Error on AT!OPENLOCK? request. Aborting.") - return - if challenge == "": - print("Error: Couldn't get challenge. Aborting.") - return - resp = self.keygen.run(self.devicegeneration, challenge, 0) - print("Sending AT!OPENLOCK=\"" + resp + "\" response.") - info = self.cn.send("AT!OPENLOCK=\"" + resp + "\"") - if info == -1: - print("Damn. AT!OPENLOCK failed.") - else: - print("Success. Device is now engineer unlocked.") - return True - return False - -def main(args): - version = "1.4" - info = 'Sierra Wireless Generator ' + version + ' (c) B. Kerler 2019-2021' - parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,description=info) - - parser.add_argument( - '-openlock', '-l', - help='AT!OPENLOCK? modem response', - default="") - - parser.add_argument( - '-openmep', '-m', - help='AT!OPENMEP? modem response', - default="") - - parser.add_argument( - '-opencnd', '-c', - help='AT!OPENCND? modem response', - default="") - - parser.add_argument( - '-devicegeneration', '-d', - help='Device devicegeneration generation', - default="") - - parser.add_argument( - '-port', '-p', - help='use com port for auto unlock', - default="") - - parser.add_argument( - '-unlock', '-u', - help='use com port for openlock', - default=False, action='store_true') - - parser.add_argument( - '-selftest', '-s', - help='run selftest', - default=False, action='store_true') - - args = parser.parse_args() - - openlock = args.openlock - openmep = args.openmep - opencnd = args.opencnd - devicegeneration = args.devicegeneration - - if not args.selftest: - if (devicegeneration == "" or (openlock == "" and openmep == "" and opencnd == "")) and not args.unlock: - print(info) - print("------------------------------------------------------------\n") - print("Usage: ./sierrakeygen [-l,-m,-c] [challenge] -d [devicegeneration]") - print("Example: ./sierrakeygen.py -l BE96CBBEE0829BCA -d MDM9200") - print("or: ./sierrakeygen.py -u for auto unlock") - print("or: ./sierrakeygen.py -u -p [portname] for auto unlock with given portname") - print("or: ./sierrakeygen.py -s for self-test") - print("Supported devicegenerations :") - for key in infotable: - info = f"\t{key}:\t\t" - count = 0 - for item in infotable[key]: - count += 1 - if count > 15: - info += "\n\t\t\t\t\t" - count = 0 - info += item + "," - - info = info[:-1] - print(info) - exit(0) - - if devicegeneration == "" and not args.unlock: - print("You need to specific a device generation as well. Option -d") - exit(0) - if devicegeneration == "": - devicegeneration=None - if args.selftest: - kg=SierraKeygen(None,"selftest") - kg.run_selftest() - elif args.unlock: - cn = connection(args.port) - if cn.connected: - kg=SierraKeygen(cn,devicegeneration) - if kg.devicegeneration == "": - print("Unknown device generation. Please send me details :)") - else: - kg.openlock() - cn.close() - else: - kg = SierraKeygen(None, devicegeneration) - if openlock != "": - resp = kg.keygen.run(devicegeneration, openlock, 0) - print("AT!OPENLOCK=\"" + resp + "\"") - elif openmep != "": - resp = kg.keygen.run(devicegeneration, openmep, 1) - print("AT!OPENMEP=\"" + resp + "\"") - elif opencnd != "": - resp = kg.keygen.run(devicegeneration, opencnd, 2) - print("AT!OPENCND=\"" + resp + "\"") - - -if __name__ == '__main__': - main(sys.argv) diff --git a/edlclient/Tools/sierrakeygen b/edlclient/Tools/sierrakeygen new file mode 120000 index 0000000..0ca3dd2 --- /dev/null +++ b/edlclient/Tools/sierrakeygen @@ -0,0 +1 @@ +sierrakeygen.py \ No newline at end of file diff --git a/setup.py b/setup.py index fc1dbf6..e857bbb 100755 --- a/setup.py +++ b/setup.py @@ -4,10 +4,10 @@ import os setup( name='edlclient', - version='3.53', + version='3.60', packages=find_packages(), long_description=open("README.md").read(), - scripts=['edl','edlclient/Tools/qc_diag','edlclient/Tools/sierrakeygen','edlclient/Tools/boottodwnload','edlclient/Tools/enableadb','edlclient/Tools/fhloaderparse','edlclient/Tools/beagle_to_loader'], + scripts=['edl','edlclient/Tools/qc_diag.py','edlclient/Tools/sierrakeygen.py','edlclient/Tools/boottodwnload','edlclient/Tools/enableadb','edlclient/Tools/fhloaderparse','edlclient/Tools/beagle_to_loader'], data_files = ['LICENSE','README.md'], long_description_content_type="text/markdown", url='https://github.com/bkerler/edl',