mirror of
https://github.com/bkerler/edl.git
synced 2024-11-30 19:26:52 -05:00
393 lines
23 KiB
Python
Executable file
393 lines
23 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
# Qualcomm Sahara / Firehose Client (c) B.Kerler 2018-2021
|
|
# Licensed under MIT License
|
|
"""
|
|
Usage:
|
|
edl -h | --help
|
|
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 <directory> [--memory=memtype] [--lun=lun] [--genxml] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl r <partitionname> <filename> [--memory=memtype] [--sectorsize==bytes] [--lun=lun] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl rl <directory> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skip=partnames] [--genxml] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl rf <filename> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl rs <start_sector> <sectors> <filename> [--lun=lun] [--sectorsize==bytes] [--memory=memtype] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl w <partitionname> <filename> [--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 <directory> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skip=partnames] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl wf <filename> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl ws <start_sector> <filename> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skipwrite] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl e <partitionname> [--memory=memtype] [--skipwrite] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl es <start_sector> <sectors> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skipwrite] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl ep <partitionname> <sectors> [--memory=memtype] [--skipwrite] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl footer <filename> [--memory=memtype] [--lun=lun] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl peek <offset> <length> <filename> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]
|
|
edl peekhex <offset> <length> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]
|
|
edl peekdword <offset> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl peekqword <offset> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl memtbl <filename> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl poke <offset> <filename> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl pokehex <offset> <data> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl pokedword <offset> <data> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl pokeqword <offset> <data> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl memcpy <offset> <size> [--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 <filename> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl qfp <filename> [--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 <lun> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]
|
|
edl setactiveslot <slot> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]
|
|
edl send <command> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]
|
|
edl xml <xmlfile> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl rawxml <xmlstring> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial]
|
|
edl reset [--resetmode=mode] [--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 <command> <options> [--memory=memtype] [--lun=lun] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--portname=portname] [--serial]
|
|
edl provision <xmlfile> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]
|
|
edl qfil <rawprogram> <patch> <imagedir> [--loader=filename] [--memory=memtype] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]
|
|
|
|
Description:
|
|
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, 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
|
|
# <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
|
|
|
|
Options:
|
|
--loader=filename Use specific EDL loader, disable autodetection [default: None]
|
|
--vid=vid Set usb vendor image_id used for EDL [default: -1]
|
|
--pid=pid Set usb product image_id used for EDL [default: -1]
|
|
--lun=lun Set lun to read/write from (UFS memory only)
|
|
--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
|
|
--partitions=partnames Skip reading partition with names != "partname1,partname2,etc."
|
|
--skipwrite Do not allow any writes to flash (simulate only)
|
|
--skipresponse Do not expect a response from phone on read/write (some Qualcomms)
|
|
--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]
|
|
--tcpport=portnumber Set port for tcp server [default: 1340]
|
|
--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]
|
|
--resetmode=mode Resetmode for reset (poweroff, reset, edl, etc.)
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import time
|
|
import logging
|
|
import subprocess
|
|
import re
|
|
from docopt import docopt
|
|
from edlclient.Config.usb_ids import default_ids
|
|
from edlclient.Library.utils import LogBase
|
|
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 edlclient.Library.utils import is_windows
|
|
from binascii import hexlify
|
|
|
|
args = docopt(__doc__, version='3')
|
|
|
|
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", "setactiveslot",
|
|
"send", "xml", "rawxml", "reset", "nop", "modules", "memorydump", "provision", "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)
|
|
try:
|
|
output = read.stdout.read().decode('utf-8')
|
|
except:
|
|
output = read.stdout.read().decode("ISO-8859-1")
|
|
return output
|
|
|
|
|
|
def parse_option(rargs):
|
|
options = {}
|
|
for arg in rargs:
|
|
if "--" in arg or "<" in arg:
|
|
options[arg] = rargs[arg]
|
|
return options
|
|
|
|
|
|
class main(metaclass=LogBase):
|
|
def __init__(self):
|
|
self.serial = None
|
|
self.portname = None
|
|
self.__logger = self.__logger
|
|
self.info = self.__logger.info
|
|
self.debug = self.__logger.debug
|
|
self.error = self.__logger.error
|
|
self.warning = self.__logger.warning
|
|
self.cdc = None
|
|
self.sahara = None
|
|
self.vid = None
|
|
self.pid = None
|
|
|
|
def doconnect(self, loop):
|
|
while not self.cdc.connected:
|
|
self.cdc.connected = self.cdc.connect(portname=self.portname)
|
|
if not self.cdc.connected:
|
|
sys.stdout.write('.')
|
|
if loop == 5:
|
|
sys.stdout.write('\n')
|
|
self.info("Hint: Press and hold vol up+dwn, connect usb. For some, only use vol up.")
|
|
self.info("Xiaomi: Press and hold vol dwn + pwr, in fastboot mode connect usb.\n" +
|
|
" Run \"./fastpwn oem edl\".")
|
|
self.info("Other: Run \"adb reboot edl\".")
|
|
sys.stdout.write('\n')
|
|
|
|
if loop >= 20:
|
|
sys.stdout.write('\n')
|
|
loop = 6
|
|
loop += 1
|
|
time.sleep(1)
|
|
sys.stdout.flush()
|
|
else:
|
|
self.info("Device detected :)")
|
|
try:
|
|
resp = self.sahara.connect()
|
|
self.vid = self.cdc.vid
|
|
self.pid = self.cdc.pid
|
|
except Exception as err: # pylint: disable=broad-except
|
|
self.debug(str(err))
|
|
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 is_windows():
|
|
proper_driver = console_cmd(r'reg query HKLM\HARDWARE\DEVICEMAP\SERIALCOMM')
|
|
if re.findall(r'QCUSB', str(proper_driver)):
|
|
self.warning(f'Please first install libusb_win32 driver from Zadig')
|
|
|
|
mode = ""
|
|
loop = 0
|
|
vid = int(args["--vid"], 16)
|
|
pid = int(args["--pid"], 16)
|
|
interface = -1
|
|
if vid != -1 and pid != -1:
|
|
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)
|
|
|
|
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':
|
|
self.info("Trying with no loader given ...")
|
|
self.sahara.programmer = ""
|
|
else:
|
|
loader = args["--loader"]
|
|
self.info(f"Using loader {loader} ...")
|
|
self.sahara.programmer = loader
|
|
|
|
self.info("Waiting for the device")
|
|
resp = None
|
|
self.cdc.timeout = 1500
|
|
conninfo = self.doconnect(loop)
|
|
mode = conninfo["mode"]
|
|
if mode == "sahara":
|
|
cmd = conninfo["cmd"]
|
|
data = conninfo["data"]
|
|
if cmd == cmd_t.SAHARA_HELLO_REQ:
|
|
if data.mode == sahara_mode_t.SAHARA_MODE_MEMORY_DEBUG:
|
|
if args["memorydump"] or self.cdc.pid==0x900E:
|
|
time.sleep(0.5)
|
|
print("Device is in memory dump mode, dumping memory")
|
|
if args["--partitions"]:
|
|
self.sahara.debug_mode(args["--partitions"].split(","))
|
|
else:
|
|
self.sahara.debug_mode()
|
|
self.exit()
|
|
else:
|
|
print("Device is in streaming mode, uploading loader")
|
|
self.cdc.timeout = None
|
|
sahara_info = self.sahara.streaminginfo()
|
|
if sahara_info:
|
|
sahara_connect = self.sahara.connect()
|
|
if len(sahara_connect) == 3:
|
|
mode, cmd, resp = sahara_connect
|
|
else:
|
|
mode, resp = 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()
|
|
else:
|
|
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)
|
|
if "load_" in mode:
|
|
options["<mode>"] = 1
|
|
else:
|
|
options["<mode>"] = 0
|
|
sc.handle_streaming(cmd, options)
|
|
else:
|
|
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()
|
|
base.run()
|