edl/edl.py

369 lines
19 KiB
Python
Raw Normal View History

2019-01-27 11:20:59 -05:00
#!/usr/bin/env python3
2021-05-09 10:44:26 -04:00
# Qualcomm Sahara / Firehose Client (c) B.Kerler 2018-2021
2019-11-23 13:06:21 -05:00
# Licensed under MIT License
"""
Usage:
edl.py -h | --help
edl.py [--vid=vid] [--pid=pid]
2019-12-08 08:22:33 -05:00
edl.py [--loader=filename] [--memory=memtype]
2019-11-23 13:06:21 -05:00
edl.py [--debugmode]
edl.py [--gpt-num-part-entries=number] [--gpt-part-entry-size=number] [--gpt-part-entry-start-lba=number]
edl.py [--memory=memtype] [--skipstorageinit] [--maxpayload=bytes] [--sectorsize==bytes]
2020-11-14 08:59:03 -05:00
edl.py server [--tcpport=portnumber] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value]
2020-12-22 16:19:37 -05:00
edl.py memorydump [--debugmode] [--vid=vid] [--pid=pid]
edl.py printgpt [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid]
2020-09-23 05:20:41 -04:00
edl.py gpt <directory> [--memory=memtype] [--lun=lun] [--genxml] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid]
2020-12-22 16:19:37 -05:00
edl.py r <partitionname> <filename> [--memory=memtype] [--sectorsize==bytes] [--lun=lun] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid]
edl.py rl <directory> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skip=partnames] [--genxml] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl.py rf <filename> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid]
2021-01-31 19:02:15 -05:00
edl.py rs <start_sector> <sectors> <filename> [--lun=lun] [--sectorsize==bytes] [--memory=memtype] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid]
edl.py w <partitionname> <filename> [--partitionfilename=filename] [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skipwrite] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value]
2020-12-22 16:19:37 -05:00
edl.py wl <directory> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skip=partnames] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value]
edl.py wf <filename> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value]
edl.py ws <start_sector> <filename> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skipwrite] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value]
edl.py e <partitionname> [--memory=memtype] [--skipwrite] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value]
edl.py es <start_sector> <sectors> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skipwrite] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value]
2021-01-15 11:34:22 -05:00
edl.py ep <partitionname> <sectors> [--memory=memtype] [--skipwrite] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value]
2020-09-23 05:20:41 -04:00
edl.py footer <filename> [--memory=memtype] [--lun=lun] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid]
edl.py peek <offset> <length> <filename> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid]
2019-12-16 15:54:25 -05:00
edl.py peekhex <offset> <length> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl.py peekdword <offset> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl.py peekqword <offset> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl.py memtbl <filename> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl.py poke <offset> <filename> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl.py pokehex <offset> <data> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl.py pokedword <offset> <data> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl.py pokeqword <offset> <data> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
2020-12-22 16:19:37 -05:00
edl.py memcpy <offset> <size> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
2019-12-16 15:54:25 -05:00
edl.py secureboot [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl.py pbl <filename> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl.py qfp <filename> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl.py getstorageinfo [--loader=filename] [--memory=memtype] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid]
2020-09-23 05:20:41 -04:00
edl.py setbootablestoragedrive <lun> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid]
edl.py send <command> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid]
2020-11-14 08:59:03 -05:00
edl.py xml <xmlfile> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value]
edl.py rawxml <xmlstring> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value]
2019-12-16 15:54:25 -05:00
edl.py reset [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl.py nop [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
2020-11-14 08:59:03 -05:00
edl.py modules <command> <options> [--memory=memtype] [--lun=lun] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value]
2021-01-31 19:02:15 -05:00
edl.py qfil <rawprogram> <patch> <imagedir> [--loader=filename] [--memory=memtype] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid]
2020-06-15 08:40:36 -04:00
2019-11-23 13:06:21 -05:00
Description:
2021-04-13 04:08:29 -04:00
server # Run tcp/ip server
printgpt # Print GPT Table information
gpt # Save gpt table to given directory
r # Read flash to filename
rl # Read all partitions from flash to a directory
rf # Read whole flash to file
rs # Read sectors starting at start_sector to filename
w # Write filename to partition to flash
wl # Write all files from directory to flash
wf # Write whole filename to flash
ws # Write filename to flash at start_sector
e # Erase partition from flash
es # Erase sectors at start_sector from flash
ep # Erase sector count from flash partition
footer # Read crypto footer from flash
peek # Dump memory at offset with given length to filename
peekhex # Dump memory at offset and given length
peekdword # Dump DWORD at memory offset
peekqword # Dump QWORD at memory offset
memtbl # Dump memory table to file
poke # Write filename to memory at offset to memory
pokehex # Write hex string data at offset to memory
pokedword # Write DWORD to memory at offset
pokeqword # Write QWORD to memory at offset
memcpy # Copy memory from srcoffset with given size to dstoffset
secureboot # Print secureboot fields from qfprom fuses
pbl # Dump primary bootloader to filename
qfp # Dump QFPROM fuses to filename
getstorageinfo # Print storage info in firehose mode
setbootablestoragedrive # Change bootable storage drive to lun number
send # Send firehose command
xml # Send firehose xml file
rawxml # Send firehose xml raw string
reset # Send firehose reset command
nop # Send firehose nop command
modules # Enable submodules, for example: "oemunlock enable"
qfil # Write rawprogram xml files
# <rawprogram> : program config xml, such as rawprogram_unsparse.xml or rawprogram*.xml
# <patch> : patch config xml, such as patch0.xml or patch*.xml
# <imagedir> : directory name of image files
2019-11-23 13:06:21 -05:00
Options:
--loader=filename Use specific EDL loader, disable autodetection [default: None]
--vid=vid Set usb vendor id used for EDL [default: -1]
--pid=pid Set usb product id used for EDL [default: -1]
2021-02-09 07:54:23 -05:00
--lun=lun Set lun to read/write from (UFS memory only)
2019-11-23 13:06:21 -05:00
--maxpayload=bytes Set the maximum payload for EDL [default: 0x100000]
--sectorsize=bytes Set default sector size
--memory=memtype Set memory type ("NAND", "eMMC", "UFS", "spinor")
--partitionfilename=filename Set partition table as filename for streaming mode
2019-11-23 13:06:21 -05:00
--skipwrite Do not allow any writes to flash (simulate only)
2020-09-23 05:20:41 -04:00
--skipresponse Do not expect a response from phone on read/write (some Qualcomms)
2019-11-23 13:06:21 -05:00
--skipstorageinit Skip storage initialisation
--debugmode Enable verbose mode
--gpt-num-part-entries=number Set GPT entry count [default: 0]
--gpt-part-entry-size=number Set GPT entry size [default: 0]
--gpt-part-entry-start-lba=number Set GPT entry start lba sector [default: 0]
2020-12-22 16:19:37 -05:00
--tcpport=portnumber Set port for tcp server [default: 1340]
2019-11-27 15:26:44 -05:00
--skip=partnames Skip reading partition with names "partname1,partname2,etc."
--genxml Generate rawprogram[lun].xml
2020-11-14 08:59:03 -05:00
--devicemodel=value Set device model [default: ""]
2019-11-23 13:06:21 -05:00
"""
2021-01-15 11:34:22 -05:00
import os
2020-12-22 16:19:37 -05:00
import sys
2019-12-17 15:13:32 -05:00
import time
2020-12-22 16:19:37 -05:00
import logging
import subprocess
import re
2020-12-22 16:19:37 -05:00
from docopt import docopt
2021-01-15 11:34:22 -05:00
from Library.utils import LogBase
2021-02-21 15:10:44 -05:00
from Library.usblib import UsbClass
2021-01-15 11:34:22 -05:00
from Library.sahara import sahara
2020-12-22 16:19:37 -05:00
from Library.streaming_client import streaming_client
from Library.firehose_client import firehose_client
2021-01-15 11:34:22 -05:00
from Library.streaming import Streaming
2021-04-13 04:08:29 -04:00
from binascii import hexlify
2019-01-27 11:20:59 -05:00
2021-02-19 16:15:11 -05:00
args = docopt(__doc__, version='3')
default_ids = [
[0x05c6, 0x9008, -1],
[0x05c6, 0x900e, -1],
[0x05c6, 0x9025, -1],
[0x1199, 0x9062, -1],
[0x1199, 0x9070, -1],
[0x1199, 0x9090, -1],
[0x0846, 0x68e0, -1],
[0x19d2, 0x0076, -1]
]
print("Qualcomm Sahara / Firehose Client V3.3 (c) B.Kerler 2018-2021.")
2021-02-19 16:15:11 -05:00
2021-04-13 04:08:29 -04:00
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", "qfil"]
for cmd in cmds:
if rargs[cmd]:
return cmd
return ""
def console_cmd(cmd):
read = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, close_fds=True)
output = read.stdout.read().decode()
return output
def parse_option(rargs):
options = {}
for arg in rargs:
if "--" in arg or "<" in arg:
options[arg] = rargs[arg]
return options
2021-01-15 11:34:22 -05:00
class main(metaclass=LogBase):
2021-02-19 16:15:11 -05:00
def __init__(self):
2021-04-13 04:08:29 -04:00
self.__logger = self.__logger
2021-02-19 16:15:11 -05:00
self.info = self.__logger.info
self.debug = self.__logger.debug
self.error = self.__logger.error
self.warning = self.__logger.warning
2021-04-13 04:08:29 -04:00
self.cdc = None
self.sahara = None
2021-02-19 16:15:11 -05:00
def doconnect(self, loop, mode, resp):
while not self.cdc.connected:
self.cdc.connected = self.cdc.connect()
if not self.cdc.connected:
2021-01-15 11:34:22 -05:00
sys.stdout.write('.')
if loop == 5:
sys.stdout.write('\n')
2021-02-19 16:15:11 -05:00
self.info("Hint: Press and hold vol up+dwn, connect usb. For some, only use vol up.")
self.info("Xiaomi: Press and hold Vol up + pwr, in fastboot mode connect usb.\n" +
" Run \"./fastboot oem edl\".")
self.info("Other: Run \"adb reboot edl\".")
sys.stdout.write('\n')
2021-01-15 11:34:22 -05:00
if loop >= 20:
sys.stdout.write('\n')
loop = 6
2021-01-15 11:34:22 -05:00
loop += 1
time.sleep(1)
sys.stdout.flush()
else:
2021-02-19 16:15:11 -05:00
self.info("Device detected :)")
2021-01-15 11:34:22 -05:00
try:
mode, resp = self.sahara.connect()
2021-04-13 04:08:29 -04:00
except Exception as err: # pylint: disable=broad-except
self.debug(str(err))
2021-01-15 11:34:22 -05:00
if mode == "" or resp == -1:
mode, resp = self.sahara.connect()
if mode == -1:
mode, resp = self.sahara.connect()
2021-01-15 11:34:22 -05:00
if mode == "":
2021-02-19 16:15:11 -05:00
self.info("Unknown mode. Aborting.")
self.exit()
2021-02-19 16:15:11 -05:00
self.info(f"Mode detected: {mode}")
2021-01-15 11:34:22 -05:00
break
2020-12-22 16:19:37 -05:00
2021-01-15 11:34:22 -05:00
return mode, resp
2019-11-20 11:16:45 -05:00
def exit(self):
self.cdc.close()
2021-01-15 11:34:22 -05:00
sys.exit()
2020-12-22 16:19:37 -05:00
2021-01-15 11:34:22 -05:00
def run(self):
if sys.platform == 'win32' or sys.platform == 'win64' or sys.platform == 'winnt':
2021-04-13 04:08:29 -04:00
proper_driver = console_cmd(r'reg query HKLM\HARDWARE\DEVICEMAP\SERIALCOMM')
if re.findall(r'QCUSB', str(proper_driver)):
2021-02-19 16:15:11 -05:00
self.warning(f'Please first install libusb_win32 driver from Zadig')
2021-01-15 11:34:22 -05:00
mode = ""
loop = 0
vid = int(args["--vid"], 16)
pid = int(args["--pid"], 16)
interface = -1
2021-02-19 16:15:11 -05:00
if vid != -1 and pid != -1:
2021-01-15 11:34:22 -05:00
portconfig = [[vid, pid, interface]]
else:
portconfig = default_ids
if args["--debugmode"]:
logfilename = "log.txt"
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)
2019-12-16 15:54:25 -05:00
2021-02-21 15:10:44 -05:00
self.cdc = UsbClass(portconfig=portconfig, loglevel=self.__logger.level)
2021-02-19 16:15:11 -05:00
self.sahara = sahara(self.cdc, loglevel=self.__logger.level)
2019-01-27 11:20:59 -05:00
2021-01-15 11:34:22 -05:00
if args["--loader"] == 'None':
2021-02-19 16:15:11 -05:00
self.info("Trying with no loader given ...")
self.sahara.programmer = ""
2021-01-15 11:34:22 -05:00
else:
loader = args["--loader"]
2021-02-19 16:15:11 -05:00
self.info(f"Using loader {loader} ...")
self.sahara.programmer = loader
2019-01-27 11:20:59 -05:00
2021-02-19 16:15:11 -05:00
self.info("Waiting for the device")
2021-01-15 11:34:22 -05:00
resp = None
self.cdc.timeout = 100
mode, resp = self.doconnect(loop, mode, resp)
if resp == -1:
mode, resp = self.doconnect(loop, mode, resp)
2021-01-15 11:34:22 -05:00
if resp == -1:
2021-02-19 16:15:11 -05:00
self.error("USB desync, please rerun command !")
self.exit()
2021-01-15 11:34:22 -05:00
# print((mode, resp))
if mode == "sahara":
if resp is None:
2021-02-19 16:15:11 -05:00
if mode == "sahara":
2021-01-15 11:34:22 -05:00
print("Sahara in error state, resetting ...")
self.sahara.cmd_reset()
2021-02-19 16:15:11 -05:00
data = self.cdc.read(5)
2021-04-13 04:08:29 -04:00
self.debug(hexlify(data).decode('utf-8'))
self.exit()
2021-01-15 11:34:22 -05:00
elif "mode" in resp:
mode = resp["mode"]
if mode == self.sahara.sahara_mode.SAHARA_MODE_MEMORY_DEBUG:
2021-01-15 11:34:22 -05:00
if args["memorydump"]:
time.sleep(0.5)
print("Device is in memory dump mode, dumping memory")
self.sahara.debug_mode()
self.exit()
2021-01-15 11:34:22 -05:00
else:
print("Device is in streaming mode, uploading loader")
self.cdc.timeout = None
sahara_info = self.sahara.streaminginfo()
2021-01-15 11:34:22 -05:00
if sahara_info:
mode, resp = self.sahara.connect()
2021-01-15 11:34:22 -05:00
if mode == "sahara":
mode = self.sahara.upload_loader()
if "enprg" in self.sahara.programmer.lower():
2021-01-15 11:34:22 -05:00
mode = "load_enandprg"
elif "nprg" in self.sahara.programmer.lower():
2021-01-15 11:34:22 -05:00
mode = "load_nandprg"
2021-02-19 16:15:11 -05:00
elif mode != "":
2021-01-15 11:34:22 -05:00
mode = "load_" + mode
if "load_" in mode:
time.sleep(0.3)
else:
print("Error, couldn't find suitable enprg/nprg loader :(")
self.exit()
2020-12-22 16:19:37 -05:00
else:
2021-01-15 11:34:22 -05:00
print("Device is in EDL mode .. continuing.")
self.cdc.timeout = None
2021-02-21 13:58:05 -05:00
sahara_info = self.sahara.cmd_info()
2020-12-22 16:19:37 -05:00
if sahara_info:
mode, resp = self.sahara.connect()
2020-12-22 16:19:37 -05:00
if mode == "sahara":
mode = self.sahara.upload_loader()
2021-01-15 11:34:22 -05:00
if mode == "firehose":
if "enprg" in self.sahara.programmer.lower():
2021-01-15 11:34:22 -05:00
mode = "enandprg"
elif "nprg" in self.sahara.programmer.lower():
2020-12-22 16:19:37 -05:00
mode = "nandprg"
2021-01-15 11:34:22 -05:00
if mode != "":
if mode != "firehose":
streaming = Streaming(self.cdc, self.sahara, self.__logger.level)
2021-01-15 11:34:22 -05:00
if streaming.connect(1):
print("Successfully uploaded programmer :)")
mode = "nandprg"
else:
print("Device is in an unknown state")
self.exit()
2020-12-22 16:19:37 -05:00
else:
2021-01-15 11:34:22 -05:00
print("Successfully uploaded programmer :)")
2020-12-22 16:19:37 -05:00
else:
2021-01-15 11:34:22 -05:00
print("No suitable loader found :(")
self.exit()
2021-01-15 11:34:22 -05:00
else:
print("Device is in an unknown sahara state, resetting")
2021-01-15 11:34:22 -05:00
print("resp={0}".format(resp))
self.sahara.cmd_reset()
self.exit()
2021-01-15 11:34:22 -05:00
else:
print("Device is in an unknown state")
self.exit()
else:
self.sahara.bit64 = True
2021-01-15 11:34:22 -05:00
if mode == "firehose":
self.cdc.timeout = None
2021-02-19 16:15:11 -05:00
fh = firehose_client(args, self.cdc, self.sahara, self.__logger.level, print)
2021-04-13 04:08:29 -04:00
cmd = parse_cmd(args)
options = parse_option(args)
2021-02-19 16:15:11 -05:00
if cmd != "":
fh.handle_firehose(cmd, options)
2021-01-15 11:34:22 -05:00
elif mode == "nandprg" or mode == "enandprg" or mode == "load_nandprg" or mode == "load_enandprg":
2021-02-19 16:15:11 -05:00
sc = streaming_client(args, self.cdc, self.sahara, self.__logger.level, print)
2021-04-13 04:08:29 -04:00
cmd = parse_cmd(args)
options = parse_option(args)
2021-01-15 11:34:22 -05:00
if "load_" in mode:
options["<mode>"] = 1
else:
options["<mode>"] = 0
sc.handle_streaming(cmd, options)
2019-01-27 11:20:59 -05:00
else:
2021-02-19 16:15:11 -05:00
self.error("Sorry, couldn't talk to Sahara, please reboot the device !")
2019-12-03 02:46:31 -05:00
self.exit()
2019-01-27 11:20:59 -05:00
if __name__ == '__main__':
2021-02-19 16:15:11 -05:00
base = main()
2021-01-15 11:34:22 -05:00
base.run()