def handle_streaming(self, cmd, options): mode = 0 if "<mode>" in options: mode = options["<mode>"] if self.streaming.connect(mode): xflag = 0 self.streaming.nand_init(xflag) if cmd == "gpt": directory = options["<directory>"] if directory is None: directory = "" data = self.streaming.read_partition_table() sfilename = os.path.join(directory, f"partition.bin") if data != b"": with open(sfilename, "wb") as write_handle: write_handle.write(data) self.printer(f"Dumped Partition Table to {sfilename}") else: self.LOGGER.error(f"Error on dumping partition table to {sfilename}") elif cmd == "printgpt": partitions = self.streaming.get_partitions() self.print_partitions(partitions) self.streaming.nand_post() elif cmd == "r": partitionname = options["<partitionname>"] filename = options["<filename>"] filenames = filename.split(",") partitions = partitionname.split(",") if len(partitions) != len(filenames): self.LOGGER.error("You need to gives as many filenames as given partitions.") return i = 0 rpartitions = self.streaming.get_partitions() for partition in partitions: if partition.lower() in rpartitions: spartition = rpartitions[partition] offset = spartition["offset"] length = spartition["length"] # attr1 = spartition["attr1"] # attr2 = spartition["attr2"] # attr3 = spartition["attr3"] partfilename = filenames[i] self.printer(f"Dumping Partition {partition}...") self.streaming.read_raw(offset, length, self.streaming.settings.UD_SIZE_BYTES, partfilename) self.printer(f"Dumped sector {str(offset)} with sector count {str(length)} as {partfilename}.") else: self.LOGGER.error(f"Error: Couldn't detect partition: {partition}\nAvailable partitions:") self.print_partitions(rpartitions) elif cmd == "rs": start = int(options["<start_sector>"]) sectors = int(options["<sectors>"]) filename = options["<filename>"] self.printer(f"Dumping Sector {hex(start)} with Sectorcount {hex(sectors)}...") block = 131 page = 0x20 data, extra = self.streaming.flash_read(block, page, sectors, self.streaming.settings.UD_SIZE_BYTES) try: with open(filename, "wb") as write_handle: write_handle.write(data) self.printer(f"Dumped sector {str(start)} with sector count {str(sectors)} as {filename}.") return except Exception as error: self.LOGGER.error(f"Couldn't open {filename} for writing: %s" % str(error)) self.streaming.nand_post() elif cmd == "rl": directory = options["<directory>"] if options["--skip"]: skip = options["--skip"].split(",") else: skip = [] if not os.path.exists(directory): os.mkdir(directory) storedir = directory if not os.path.exists(storedir): os.mkdir(storedir) sfilename = os.path.join(storedir, f"partition.bin") partdata = self.streaming.read_partition_table() if partdata != -1: with open(sfilename, "wb") as write_handle: write_handle.write(partdata) else: self.LOGGER.error(f"Couldn't detect partition header.") return partitions = self.streaming.get_partitions() for partition in partitions: if partition in skip: continue filename = os.path.join(storedir, partition + ".bin") spartition = partitions[partition] offset = spartition["offset"] length = spartition["length"] # attr1 = spartition["attr1"] # attr2 = spartition["attr2"] # attr3 = spartition["attr3"] partfilename = filename self.LOGGER.info(f"Dumping partition {str(partition)} with block count {str(length)} as " + f"{filename}.") self.streaming.read_raw(offset, length, self.streaming.settings.UD_SIZE_BYTES, partfilename) elif cmd == "peek": offset = int(options["<offset>"], 16) length = int(options["<length>"], 16) filename = options["<filename>"] with open(filename, "wb") as wf: while length > 0: size = 0x20000 if length < size: size = length data = self.streaming.memread(offset, size) if data != b"": wf.write(data) else: break length -= size self.LOGGER.info( f"Peek data from offset {hex(offset)} and length {hex(length)} was written to {filename}") elif cmd == "peekhex": offset = int(options["<offset>"], 16) length = int(options["<length>"], 16) resp = self.streaming.memread(offset, length) self.printer("\n") self.printer(hexlify(resp)) elif cmd == "peekqword": offset = int(options["<offset>"], 16) resp = self.streaming.memread(offset, 8) self.printer("\n") self.printer(hex(unpack("<Q", resp[:8])[0])) elif cmd == "peekdword": offset = int(options["<offset>"], 16) resp = self.streaming.mempeek(offset) self.printer("\n") self.printer(hex(resp)) elif cmd == "poke": offset = int(options["<offset>"], 16) filename = unhexlify(options["<filename>"]) try: with open(filename, "rb") as rf: data = rf.read() if self.streaming.memwrite(offset, data): self.LOGGER.info("Poke succeeded.") else: self.LOGGER.error("Poke failed.") except Exception as e: self.LOGGER.error(str(e)) elif cmd == "pokehex": offset = int(options["<offset>"], 16) data = unhexlify(options["<data>"]) if self.streaming.memwrite(offset, data): self.LOGGER.info("Poke succeeded.") else: self.LOGGER.error("Poke failed.") elif cmd == "pokeqword": offset = int(options["<offset>"], 16) data = pack("<Q", int(options["<data>"], 16)) if self.streaming.memwrite(offset, data): self.LOGGER.info("Poke succeeded.") else: self.LOGGER.error("Poke failed.") elif cmd == "pokedword": offset = int(options["<offset>"], 16) data = pack("<I", int(options["<data>"], 16)) if self.streaming.mempoke(offset, data): self.LOGGER.info("Poke succeeded.") else: self.LOGGER.error("Poke failed.") elif cmd == "reset": if self.streaming.reset(): self.LOGGER.info("Reset succeeded.") elif cmd == "memtbl": filename = options["<filename>"] memtbl = self.streaming.settings.memtbl data = self.streaming.memread(memtbl[0], memtbl[1]) if data != b"": with open(filename, "wb") as wf: wf.write(data) self.printer(f"Dumped memtbl at offset {hex(memtbl[0])} as {filename}.") else: self.LOGGER.error("Error on dumping memtbl") elif cmd == "secureboot": value = self.streaming.mempeek(self.streaming.settings.secureboot) if value != -1: is_secure = False for area in range(0, 4): sec_boot = (value >> (area * 8)) & 0xFF pk_hashindex = sec_boot & 3 oem_pkhash = True if ((sec_boot >> 4) & 1) == 1 else False auth_enabled = True if ((sec_boot >> 5) & 1) == 1 else False use_serial = True if ((sec_boot >> 6) & 1) == 1 else False if auth_enabled: is_secure = True self.printer(f"Sec_Boot{str(area)} PKHash-Index:{str(pk_hashindex)} " + f"OEM_PKHash: {str(oem_pkhash)} " + f"Auth_Enabled: {str(auth_enabled)} " + f"Use_Serial: {str(use_serial)}") if is_secure: self.printer("Secure boot enabled.") else: self.printer("Secure boot disabled.") else: self.LOGGER.error("Unknown target chipset") elif cmd == "pbl": filename = options["<filename>"] pbl = self.streaming.settings.pbl self.printer("Dumping pbl....") data = self.streaming.memread(pbl[0], pbl[1]) if data != b"": with open(filename, "wb") as wf: wf.write(data) self.printer(f"Dumped pbl at offset {hex(pbl[0])} as {filename}.") else: self.LOGGER.error("Error on dumping pbl") elif cmd == "qfp": filename = options["<filename>"] qfp = self.streaming.settings.qfprom self.printer("Dumping qfprom....") data = self.streaming.memread(qfp[0], qfp[1]) if data != b"": with open(filename, "wb") as wf: wf.write(data) self.printer(f"Dumped qfprom at offset {hex(qfp[0])} as {filename}.") else: self.LOGGER.error("Error on dumping qfprom") elif cmd == "memcpy": if not self.check_param(["<offset>", "<size>"]): return False srcoffset = int(options["<offset>"], 16) size = int(options["<size>"], 16) dstoffset = srcoffset + size if self.streaming.cmd_memcpy(dstoffset, srcoffset, size): self.printer(f"Memcpy from {hex(srcoffset)} to {hex(dstoffset)} succeeded") return True else: return False ############################### elif cmd == "nop": self.LOGGER.error("Nop command isn't supported by streaming loader") return True elif cmd == "setbootablestoragedrive": self.LOGGER.error("setbootablestoragedrive command isn't supported by streaming loader") return True elif cmd == "getstorageinfo": self.LOGGER.error("getstorageinfo command isn't supported by streaming loader") return True elif cmd == "w": if not self.check_param(["<partitionname>", "<filename>"]): return False partitionname = options["<partitionname>"] filename = options["<filename>"] if not os.path.exists(filename): self.LOGGER.error(f"Error: Couldn't find file: {filename}") return False rpartitions = self.streaming.get_partitions() if self.streaming.enter_flash_mode(): if partitionname in rpartitions: spartition = rpartitions[partitionname] offset = spartition["offset"] length = spartition["length"] # attr1 = spartition["attr1"] # attr2 = spartition["attr2"] # attr3 = spartition["attr3"] sectors = int(os.stat( filename).st_size / self.streaming.settings.num_pages_per_blk / self.streaming.settings.PAGESIZE) if sectors > length: self.LOGGER.error( f"Error: {filename} has {sectors} sectors but partition only has {length}.") return False if self.streaming.modules is not None: self.streaming.modules.prerun() if self.streaming.write_flash(partitionname, filename): self.printer(f"Wrote {filename} to sector {str(offset)}.") return True else: self.printer(f"Error writing {filename} to sector {str(offset)}.") return False else: self.LOGGER.error(f"Error: Couldn't detect partition: {partitionname}\nAvailable partitions:") self.print_partitions(rpartitions) return False elif cmd == "wl": if not self.check_param(["<directory>"]): return False directory = options["<directory>"] if options["--skip"]: skip = options["--skip"].split(",") else: skip = [] if not os.path.exists(directory): self.LOGGER.error(f"Error: Couldn't find directory: {directory}") return False filenames = [] if self.streaming.enter_flash_mode(): if self.streaming.modules is not None: self.streaming.modules.prerun() rpartitions = self.streaming.get_partitions() for dirName, subdirList, fileList in os.walk(directory): for fname in fileList: filenames.append(os.path.join(dirName, fname)) for filename in filenames: for partition in rpartitions: partname = filename[filename.rfind("/") + 1:] if ".bin" in partname[-4:]: partname = partname[:-4] if partition == partname: if partition in skip: continue spartition = rpartitions[partition] offset = spartition["offset"] length = spartition["length"] # attr1 = spartition["attr1"] # attr2 = spartition["attr2"] # attr3 = spartition["attr3"] sectors = int(os.stat(filename).st_size / self.streaming.settings.num_pages_per_blk / self.streaming.settings.PAGESIZE) if sectors > length: self.LOGGER.error( f"Error: {filename} has {sectors} sectors but partition only has {length}.") return False self.printer(f"Writing {filename} to partition {str(partition)}.") self.streaming.write_flash(partition, filename) else: self.printer("Couldn't write partition. Either wrong memorytype given or no gpt partition.") return False return True elif cmd == "ws": self.LOGGER.error("ws command isn't supported by streaming loader") # todo return False elif cmd == "wf": self.LOGGER.error("wf command isn't supported by streaming loader") # todo return False elif cmd == "e": self.LOGGER.error("e command isn't supported by streaming loader") # todo return False elif cmd == "es": self.LOGGER.error("es command isn't supported by streaming loader") # todo return False elif cmd == "xml": self.LOGGER.error("xml command isn't supported by streaming loader") return False elif cmd == "rawxml": self.LOGGER.error("rawxml command isn't supported by streaming loader") return False elif cmd == "send": self.LOGGER.error("send command isn't supported by streaming loader") return False ############################### elif cmd == "server": return do_tcp_server(self, options, self.handle_streaming) elif cmd == "modules": if not self.check_param(["<command>", "<options>"]): return False command = options["<command>"] options = options["<options>"] if self.streaming.modules is None: self.LOGGER.error("Feature is not supported") return False else: return self.streaming.modules.run(mainargs=options, command=command) else: self.LOGGER.error("Unknown/Missing command, a command is required.") return False
def handle_firehose(self, cmd, options): if cmd == "gpt": luns = self.getluns(options) directory = options["<directory>"] if directory is None: directory = "" genxml = False if "--genxml" in options: if options["--genxml"]: genxml = True for lun in luns: sfilename = os.path.join(directory, f"gpt_main{str(lun)}.bin") data, guid_gpt = self.firehose.get_gpt( lun, int(options["--gpt-num-part-entries"]), int(options["--gpt-part-entry-size"]), int(options["--gpt-part-entry-start-lba"])) if guid_gpt is None: break with open(sfilename, "wb") as write_handle: write_handle.write(data) self.printer(f"Dumped GPT from Lun {str(lun)} to {sfilename}") sfilename = os.path.join(directory, f"gpt_backup{str(lun)}.bin") with open(sfilename, "wb") as write_handle: write_handle.write( data[self.firehose.cfg.SECTOR_SIZE_IN_BYTES * 2:]) self.printer( f"Dumped Backup GPT from Lun {str(lun)} to {sfilename}") if genxml: guid_gpt.generate_rawprogram( lun, self.firehose.cfg.SECTOR_SIZE_IN_BYTES, directory) return True elif cmd == "printgpt": luns = self.getluns(options) for lun in luns: data, guid_gpt = self.firehose.get_gpt( lun, int(options["--gpt-num-part-entries"]), int(options["--gpt-part-entry-size"]), int(options["--gpt-part-entry-start-lba"])) if guid_gpt is None: break self.printer(f"\nParsing Lun {str(lun)}:") guid_gpt.print() return True elif cmd == "r": if not self.check_param(["<partitionname>", "<filename>"]): return False partitionname = options["<partitionname>"] filename = options["<filename>"] filenames = filename.split(",") partitions = partitionname.split(",") if len(partitions) != len(filenames): self.error( "You need to gives as many filenames as given partitions.") return False i = 0 for partition in partitions: partfilename = filenames[i] i += 1 res = self.firehose.detect_partition(options, partition) if res[0]: lun = res[1] rpartition = res[2] if self.firehose.cmd_read(lun, rpartition.sector, rpartition.sectors, partfilename): self.printer( f"Dumped sector {str(rpartition.sector)} with sector count {str(rpartition.sectors)} " + f"as {partfilename}.") else: fpartitions = res[1] self.error( f"Error: Couldn't detect partition: {partition}\nAvailable partitions:" ) for lun in fpartitions: for rpartition in fpartitions[lun]: if self.cfg.MemoryName == "emmc": self.error("\t" + rpartition) else: self.error(lun + ":\t" + rpartition) return False return True elif cmd == "rl": if not self.check_param(["<directory>"]): return False directory = options["<directory>"] if options["--skip"]: skip = options["--skip"].split(",") else: skip = [] genxml = False if "--genxml" in options: if options["--genxml"]: genxml = True if not os.path.exists(directory): os.mkdir(directory) luns = self.getluns(options) for lun in luns: data, guid_gpt = self.firehose.get_gpt( lun, int(options["--gpt-num-part-entries"]), int(options["--gpt-part-entry-size"]), int(options["--gpt-part-entry-start-lba"])) if guid_gpt is None: break if len(luns) > 1: storedir = os.path.join(directory, "lun" + str(lun)) else: storedir = directory if not os.path.exists(storedir): os.mkdir(storedir) sfilename = os.path.join(storedir, f"gpt_main{str(lun)}.bin") with open(sfilename, "wb") as write_handle: write_handle.write(data) sfilename = os.path.join(storedir, f"gpt_backup{str(lun)}.bin") with open(sfilename, "wb") as write_handle: write_handle.write( data[self.firehose.cfg.SECTOR_SIZE_IN_BYTES * 2:]) if genxml: guid_gpt.generate_rawprogram( lun, self.firehose.cfg.SECTOR_SIZE_IN_BYTES, storedir) for partition in guid_gpt.partentries: partitionname = partition.name if partition.name in skip: continue filename = os.path.join(storedir, partitionname + ".bin") self.info( f"Dumping partition {str(partition.name)} with sector count {str(partition.sectors)} " + f"as {filename}.") if self.firehose.cmd_read(lun, partition.sector, partition.sectors, filename): self.info( f"Dumped partition {str(partition.name)} with sector count " + f"{str(partition.sectors)} as {filename}.") return True elif cmd == "rf": if not self.check_param(["<filename>"]): return False filename = options["<filename>"] luns = self.getluns(options) for lun in luns: data, guid_gpt = self.firehose.get_gpt( lun, int(options["--gpt-num-part-entries"]), int(options["--gpt-part-entry-size"]), int(options["--gpt-part-entry-start-lba"])) if guid_gpt is None: break if len(luns) > 1: sfilename = filename + f"_lun{str(lun)}" else: sfilename = filename self.printer( f"Dumping sector 0 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}." ) return True elif cmd == "pbl": if not self.check_param(["<filename>"]): return False if not self.check_cmd("peek"): self.error("Peek command isn't supported by edl loader") return False else: filename = options["<filename>"] if self.target_name in infotbl: target_name = infotbl[self.target_name] if len(target_name[0]) > 0: if self.firehose.cmd_peek(target_name[0][0], target_name[0][1], filename, True): self.printer( f"Dumped pbl at offset {hex(target_name[0][0])} as {filename}." ) return True else: self.error("No known pbl offset for this chipset") else: self.error("Unknown target chipset") self.error("Error on dumping pbl") return False elif cmd == "qfp": if not self.check_param(["<filename>"]): return False if not self.check_cmd("peek"): self.error("Peek command isn't supported by edl loader") return False else: filename = options["<filename>"] if self.target_name not in infotbl: self.error("Unknown target chipset") else: target_name = infotbl[self.target_name] if len(target_name[1]) > 0: if self.firehose.cmd_peek(target_name[1][0], target_name[1][1], filename): self.printer( f"Dumped qfprom at offset {hex(target_name[1][0])} as {filename}." ) return True else: self.error("No known qfprom offset for this chipset") self.error("Error on dumping qfprom") return False elif cmd == "secureboot": if not self.check_cmd("peek"): self.error("Peek command isn't supported by edl loader") return False else: if self.target_name in secureboottbl: self.target_name = secureboottbl[self.target_name] value = unpack("<I", self.firehose.cmd_peek(self.target_name, 4))[0] is_secure = False for area in range(0, 4): sec_boot = (value >> (area * 8)) & 0xFF pk_hashindex = sec_boot & 3 oem_pkhash = True if ((sec_boot >> 4) & 1) == 1 else False auth_enabled = True if ((sec_boot >> 5) & 1) == 1 else False use_serial = True if ((sec_boot >> 6) & 1) == 1 else False if auth_enabled: is_secure = True self.printer(f"Sec_Boot{str(area)} " + f"PKHash-Index:{str(pk_hashindex)} " + f"OEM_PKHash: {str(oem_pkhash)} " + f"Auth_Enabled: {str(auth_enabled)}" + f"Use_Serial: {str(use_serial)}") if is_secure: self.printer("Secure boot enabled.") else: self.printer("Secure boot disabled.") return True else: self.error("Unknown target chipset") return False elif cmd == "memtbl": if not self.check_param(["<filename>"]): return False if not self.check_cmd("peek"): self.error("Peek command isn't supported by edl loader") return False else: filename = options["<filename>"] if self.target_name in infotbl: self.target_name = infotbl[self.target_name] if len(self.target_name[2]) > 0: if self.firehose.cmd_peek(self.target_name[2][0], self.target_name[2][1], filename): self.printer( f"Dumped memtbl at offset {hex(self.target_name[2][0])} as {filename}." ) return True else: self.error("No known memtbl offset for this chipset") else: self.error("Unknown target chipset") self.error("Error on dumping memtbl") return False elif cmd == "footer": if not self.check_param(["<filename>"]): return False luns = self.getluns(options) filename = options["<filename>"] for lun in luns: data, guid_gpt = self.firehose.get_gpt( lun, int(options["--gpt-num-part-entries"]), int(options["--gpt-part-entry-size"]), int(options["--gpt-part-entry-start-lba"])) if guid_gpt is None: break pnames = [ "userdata2", "metadata", "userdata", "reserved1", "reserved2", "reserved3" ] for partition in guid_gpt.partentries: if partition.name in pnames: self.printer(f"Detected partition: {partition.name}") data = self.firehose.cmd_read_buffer( lun, partition.sector + (partition.sectors - (0x4000 // self.firehose.cfg.SECTOR_SIZE_IN_BYTES)), (0x4000 // self.firehose.cfg.SECTOR_SIZE_IN_BYTES), False) if data == b"": continue val = unpack("<I", data[:4])[0] if (val & 0xFFFFFFF0) == 0xD0B5B1C0: with open(filename, "wb") as write_handle: write_handle.write(data) self.printer( f"Dumped footer from {partition.name} as {filename}." ) return True self.error("Error: Couldn't detect footer partition.") return False elif cmd == "rs": if options["--lun"] is not None: lun = int(options["--lun"]) else: lun = 0 if not self.check_param( ["<filename>", "<sectors>", "<start_sector>"]): return False start = int(options["<start_sector>"]) sectors = int(options["<sectors>"]) filename = options["<filename>"] if self.firehose.cmd_read(lun, start, sectors, filename, True): self.printer( f"Dumped sector {str(start)} with sector count {str(sectors)} as {filename}." ) return True elif cmd == "peek": if not self.check_param(["<offset>", "<length>", "<filename>"]): return False if not self.check_cmd("peek"): self.error("Peek command isn't supported by edl loader") return False else: offset = getint(options["<offset>"]) length = getint(options["<length>"]) filename = options["<filename>"] self.firehose.cmd_peek(offset, length, filename, True) self.info( f"Peek data from offset {hex(offset)} and length {hex(length)} was written to {filename}" ) return True elif cmd == "peekhex": if not self.check_param(["<offset>", "<length>"]): return False if not self.check_cmd("peek"): self.error("Peek command isn't supported by edl loader") return False else: offset = getint(options["<offset>"]) length = getint(options["<length>"]) resp = self.firehose.cmd_peek(offset, length, "", True) self.printer("\n") self.printer(hexlify(resp)) return True elif cmd == "peekqword": if not self.check_param(["<offset>"]): return False if not self.check_cmd("peek"): self.error("Peek command isn't supported by edl loader") return False else: offset = getint(options["<offset>"]) resp = self.firehose.cmd_peek(offset, 8, "", True) self.printer("\n") self.printer(hex(unpack("<Q", resp[:8])[0])) return True elif cmd == "peekdword": if not self.check_param(["<offset>"]): return False if not self.check_cmd("peek"): self.error("Peek command isn't supported by edl loader") return False else: offset = getint(options["<offset>"]) resp = self.firehose.cmd_peek(offset, 4, "", True) self.printer("\n") self.printer(hex(unpack("<I", resp[:4])[0])) return True elif cmd == "poke": if not self.check_param(["<offset>", "<filename>"]): return False if not self.check_cmd("poke"): self.error("Poke command isn't supported by edl loader") return False else: offset = getint(options["<offset>"]) filename = options["<filename>"] return self.firehose.cmd_poke(offset, "", filename, True) elif cmd == "pokehex": if not self.check_param(["<offset>", "<data>"]): return False if not self.check_cmd("poke"): self.error("Poke command isn't supported by edl loader") return False else: offset = getint(options["<offset>"]) data = unhexlify(options["<data>"]) return self.firehose.cmd_poke(offset, data, "", True) elif cmd == "pokeqword": if not self.check_param(["<offset>", "<data>"]): return False if not self.check_cmd("poke"): self.error("Poke command isn't supported by edl loader") return False else: offset = getint(options["<offset>"]) data = pack("<Q", getint(options["<data>"])) return self.firehose.cmd_poke(offset, data, "", True) elif cmd == "pokedword": if not self.check_param(["<offset>", "<data>"]): return False if not self.check_cmd("poke"): self.error("Poke command isn't supported by edl loader") return False else: offset = getint(options["<offset>"]) data = pack("<I", getint(options["<data>"])) return self.firehose.cmd_poke(offset, data, "", True) elif cmd == "memcpy": if not self.check_param(["<offset>", "<size>"]): return False if not self.check_cmd("poke"): self.printer("Poke command isn't supported by edl loader") else: srcoffset = getint(options["<offset>"]) size = getint(options["<size>"]) dstoffset = srcoffset + size if self.firehose.cmd_memcpy(dstoffset, srcoffset, size): self.printer( f"Memcpy from {hex(srcoffset)} to {hex(dstoffset)} succeeded" ) return True else: return False elif cmd == "reset": return self.firehose.cmd_reset() elif cmd == "nop": if not self.check_cmd("nop"): self.error("Nop command isn't supported by edl loader") return False else: return self.firehose.cmd_nop() elif cmd == "setbootablestoragedrive": if not self.check_param(["<lun>"]): return False if not self.check_cmd("setbootablestoragedrive"): self.error( "setbootablestoragedrive command isn't supported by edl loader" ) return False else: return self.firehose.cmd_setbootablestoragedrive( int(options["<lun>"])) elif cmd == "getstorageinfo": if not self.check_cmd("getstorageinfo"): self.error( "getstorageinfo command isn't supported by edl loader") return False else: return self.firehose.cmd_getstorageinfo_string() elif cmd == "w": if not self.check_param(["<partitionname>", "<filename>"]): return False partitionname = options["<partitionname>"] filename = options["<filename>"] if options["--lun"] is not None: lun = int(options["--lun"]) else: lun = 0 startsector = 0 if not os.path.exists(filename): self.error(f"Error: Couldn't find file: {filename}") return False if partitionname.lower() == "gpt": sectors = os.stat( filename).st_size // self.firehose.cfg.SECTOR_SIZE_IN_BYTES res = [True, lun, sectors] else: res = self.firehose.detect_partition(options, partitionname) if res[0]: lun = res[1] sectors = os.stat( filename).st_size // self.firehose.cfg.SECTOR_SIZE_IN_BYTES if (os.stat(filename).st_size % self.firehose.cfg.SECTOR_SIZE_IN_BYTES) > 0: sectors += 1 if partitionname.lower() != "gpt": partition = res[2] if sectors > partition.sectors: self.error( f"Error: {filename} has {sectors} sectors but partition only has {partition.sectors}." ) return False startsector = partition.sector if self.firehose.modules is not None: self.firehose.modules.writeprepare() if self.firehose.cmd_program(lun, startsector, filename): self.printer( f"Wrote {filename} to sector {str(startsector)}.") return True else: self.printer( f"Error writing {filename} to sector {str(startsector)}." ) return False else: if len(res) > 0: fpartitions = res[1] self.error( f"Error: Couldn't detect partition: {partitionname}\nAvailable partitions:" ) for lun in fpartitions: for partition in fpartitions[lun]: if self.cfg.MemoryName == "emmc": self.error("\t" + partition) else: self.error(lun + ":\t" + partition) return False elif cmd == "wl": if not self.check_param(["<directory>"]): return False directory = options["<directory>"] if options["--skip"]: skip = options["--skip"].split(",") else: skip = [] luns = self.getluns(options) if not os.path.exists(directory): self.error(f"Error: Couldn't find directory: {directory}") sys.exit() filenames = [] if self.firehose.modules is not None: self.firehose.modules.writeprepare() for dirName, subdirList, fileList in os.walk(directory): for fname in fileList: filenames.append(os.path.join(dirName, fname)) for lun in luns: data, guid_gpt = self.firehose.get_gpt( lun, int(options["--gpt-num-part-entries"]), int(options["--gpt-part-entry-size"]), int(options["--gpt-part-entry-start-lba"])) if guid_gpt is None: break if "partentries" in dir(guid_gpt): for filename in filenames: for partition in guid_gpt.partentries: partname = filename[filename.rfind("/") + 1:] if ".bin" in partname[-4:] or ".img" in partname[ -4:] or ".mbn" in partname[-4:]: partname = partname[:-4] if partition.name == partname: if partition.name in skip: continue sectors = os.stat( filename ).st_size // self.firehose.cfg.SECTOR_SIZE_IN_BYTES if (os.stat(filename).st_size % self.firehose.cfg.SECTOR_SIZE_IN_BYTES ) > 0: sectors += 1 if sectors > partition.sectors: self.error( f"Error: {filename} has {sectors} sectors but partition " + f"only has {partition.sectors}.") return False self.printer( f"Writing {filename} to partition {str(partition.name)}." ) self.firehose.cmd_program( lun, partition.sector, filename) else: self.printer( "Couldn't write partition. Either wrong memorytype given or no gpt partition." ) return False return True elif cmd == "ws": if not self.check_param(["<start_sector>"]): return False if options["--lun"] is not None: lun = int(options["--lun"]) else: lun = 0 start = int(options["<start_sector>"]) filename = options["<filename>"] if not os.path.exists(filename): self.error(f"Error: Couldn't find file: {filename}") return False if self.firehose.modules is not None: self.firehose.modules.writeprepare() if self.firehose.cmd_program(lun, start, filename): self.printer(f"Wrote {filename} to sector {str(start)}.") return True else: self.error( f"Error on writing {filename} to sector {str(start)}") return False elif cmd == "wf": if not self.check_param(["<filename>"]): return False if options["--lun"] is not None: lun = int(options["--lun"]) else: lun = 0 start = 0 filename = options["<filename>"] if not os.path.exists(filename): self.error(f"Error: Couldn't find file: {filename}") return False if self.firehose.modules is not None: self.firehose.modules.writeprepare() if self.firehose.cmd_program(lun, start, filename): self.printer(f"Wrote {filename} to sector {str(start)}.") return True else: self.error( f"Error on writing {filename} to sector {str(start)}") return False elif cmd == "e": if not self.check_param(["<partitionname>"]): return False luns = self.getluns(options) partitionname = options["<partitionname>"] for lun in luns: data, guid_gpt = self.firehose.get_gpt( lun, int(options["--gpt-num-part-entries"]), int(options["--gpt-part-entry-size"]), int(options["--gpt-part-entry-start-lba"])) if guid_gpt is None: break if self.firehose.modules is not None: self.firehose.modules.writeprepare() if "partentries" in dir(guid_gpt): for partition in guid_gpt.partentries: if partition.name == partitionname: self.firehose.cmd_erase(lun, partition.sector, partition.sectors) self.printer( f"Erased {partitionname} starting at sector {str(partition.sector)} " + f"with sector count {str(partition.sectors)}.") return True else: self.printer( "Couldn't erase partition. Either wrong memorytype given or no gpt partition." ) return False self.error(f"Error: Couldn't detect partition: {partitionname}") return False elif cmd == "ep": if not self.check_param(["<partitionname>", "<sectors>"]): return False luns = self.getluns(options) partitionname = options["<partitionname>"] sectors = int(options["<sectors>"]) for lun in luns: data, guid_gpt = self.firehose.get_gpt( lun, int(options["--gpt-num-part-entries"]), int(options["--gpt-part-entry-size"]), int(options["--gpt-part-entry-start-lba"])) if guid_gpt is None: break if self.firehose.modules is not None: self.firehose.modules.writeprepare() if "partentries" in dir(guid_gpt): for partition in guid_gpt.partentries: if partition.name == partitionname: self.firehose.cmd_erase(lun, partition.sector, sectors) self.printer( f"Erased {partitionname} starting at sector {str(partition.sector)} " + f"with sector count {str(sectors)}.") return True else: self.printer( "Couldn't erase partition. Either wrong memorytype given or no gpt partition." ) return False self.error(f"Error: Couldn't detect partition: {partitionname}") return False elif cmd == "es": if not self.check_param(["<start_sector>", "<sectors>"]): return False if options["--lun"] is not None: lun = int(options["--lun"]) else: lun = 0 start = int(options["<start_sector>"]) sectors = int(options["<sectors>"]) if self.firehose.modules is not None: self.firehose.modules.writeprepare() if self.firehose.cmd_erase(lun, start, sectors): self.printer( f"Erased sector {str(start)} with sector count {str(sectors)}." ) return True return False elif cmd == "xml": if not self.check_param(["<xmlfile>"]): return False return self.firehose.cmd_xml(options["<xmlfile>"]) elif cmd == "rawxml": if not self.check_param(["<xmlstring>"]): return False return self.firehose.cmd_rawxml(options["<xmlstring>"]) elif cmd == "send": if not self.check_param(["<command>"]): return False command = options["<command>"] resp = self.firehose.cmd_send(command, True) self.printer("\n") self.printer(resp) return True elif cmd == "server": return do_tcp_server(self, options, self.handle_firehose) elif cmd == "modules": if not self.check_param(["<command>", "<options>"]): return False mcommand = options["<command>"] moptions = options["<options>"] if self.firehose.modules is None: self.error("Feature is not supported") return False else: return self.firehose.modules.run(command=mcommand, args=moptions) elif cmd == "qfil": self.info("[qfil] raw programming...") rawprogram = options["<rawprogram>"].split(",") imagedir = options["<imagedir>"] patch = options["<patch>"].split(",") for xml in rawprogram: filename = os.path.join(imagedir, xml) if os.path.exists(filename): self.info("[qfil] programming %s" % xml) fl = open(filename, "r") for evt, elem in ET.iterparse(fl, events=["end"]): if elem.tag == "program": if elem.get("filename", ""): filename = os.path.join( imagedir, elem.get("filename")) if not os.path.isfile(filename): self.error("%s doesn't exist!" % filename) continue partition_number = int( elem.get("physical_partition_number")) num_disk_sectors = self.firehose.getlunsize( partition_number) start_sector = elem.get("start_sector") if "NUM_DISK_SECTORS" in start_sector: start_sector = start_sector.replace( "NUM_DISK_SECTORS", str(num_disk_sectors)) if "-" in start_sector or "*" in start_sector or "/" in start_sector or \ "+" in start_sector: start_sector = start_sector.replace( ".", "") start_sector = eval(start_sector) self.info( f"[qfil] programming {filename} to partition({partition_number})" + f"@sector({start_sector})...") self.firehose.cmd_program( int(partition_number), int(start_sector), filename) else: self.warning(f"File : {filename} not found.") self.info("[qfil] raw programming ok.") self.info("[qfil] patching...") for xml in patch: filename = os.path.join(imagedir, xml) self.info("[qfil] patching with %s" % xml) if os.path.exists(filename): fl = open(filename, "r") for evt, elem in ET.iterparse(fl, events=["end"]): if elem.tag == "patch": filename = elem.get("filename") if filename != "DISK": continue start_sector = elem.get("start_sector") size_in_bytes = elem.get("size_in_bytes") self.info( f"[qfil] patching {filename} sector({start_sector}), size={size_in_bytes}" .format(filename=filename, start_sector=start_sector, size_in_bytes=size_in_bytes)) content = ElementTree.tostring(elem).decode( "utf-8") CMD = "<?xml version=\"1.0\" ?><data>\n {content} </data>".format( content=content) print(CMD) self.firehose.xmlsend(CMD) else: self.warning(f"File : {filename} not found.") self.info("[qfil] patching ok") bootable = self.find_bootable_partition(rawprogram) if bootable != -1: if self.firehose.cmd_setbootablestoragedrive(bootable): self.info( "[qfil] partition({partition}) is now bootable\n". format(partition=bootable)) else: self.info( "[qfil] set partition({partition}) as bootable failed\n" .format(partition=bootable)) else: self.error("Unknown/Missing command, a command is required.") return False
def handle_firehose(self, cmd, options): if cmd == "gpt": luns = self.getluns(options) directory = options["<directory>"] if directory is None: directory = "" genxml = False if "--genxml" in options: if options["--genxml"]: genxml = True for lun in luns: sfilename = os.path.join(directory, f"gpt_main{str(lun)}.bin") data, guid_gpt = self.firehose.get_gpt( lun, int(options["--gpt-num-part-entries"]), int(options["--gpt-part-entry-size"]), int(options["--gpt-part-entry-start-lba"])) if guid_gpt is None: break with open(sfilename, "wb") as write_handle: write_handle.write(data) self.printer(f"Dumped GPT from Lun {str(lun)} to {sfilename}") sfilename = os.path.join(directory, f"gpt_backup{str(lun)}.bin") with open(sfilename, "wb") as write_handle: write_handle.write( data[self.firehose.cfg.SECTOR_SIZE_IN_BYTES * 2:]) self.printer( f"Dumped Backup GPT from Lun {str(lun)} to {sfilename}") if genxml: guid_gpt.generate_rawprogram( lun, self.firehose.cfg.SECTOR_SIZE_IN_BYTES, directory) return True elif cmd == "printgpt": luns = self.getluns(options) for lun in luns: data, guid_gpt = self.firehose.get_gpt( lun, int(options["--gpt-num-part-entries"]), int(options["--gpt-part-entry-size"]), int(options["--gpt-part-entry-start-lba"])) if guid_gpt is None: break self.printer(f"\nParsing Lun {str(lun)}:") guid_gpt.print() return True elif cmd == "r": if not self.check_param(["<partitionname>", "<filename>"]): return False partitionname = options["<partitionname>"] filename = options["<filename>"] filenames = filename.split(",") partitions = partitionname.split(",") if len(partitions) != len(filenames): self.LOGGER.error( "You need to gives as many filenames as given partitions.") return False i = 0 for partition in partitions: partfilename = filenames[i] i += 1 res = self.firehose.detect_partition(options, partition) if res[0]: lun = res[1] rpartition = res[2] self.firehose.cmd_read(lun, rpartition.sector, rpartition.sectors, partfilename) self.printer( f"Dumped sector {str(rpartition.sector)} with sector count {str(rpartition.sectors)} " + \ f"as {partfilename}.") else: fpartitions = res[1] self.LOGGER.error( f"Error: Couldn't detect partition: {partition}\nAvailable partitions:" ) for lun in fpartitions: for rpartition in fpartitions[lun]: if options["--memory"].lower() == "emmc": self.LOGGER.error("\t" + rpartition) else: self.LOGGER.error(lun + ":\t" + rpartition) return False return True elif cmd == "rl": if not self.check_param(["<directory>"]): return False directory = options["<directory>"] if options["--skip"]: skip = options["--skip"].split(",") else: skip = [] genxml = False if "--genxml" in options: if options["--genxml"]: genxml = True if not os.path.exists(directory): os.mkdir(directory) luns = self.getluns(options) for lun in luns: data, guid_gpt = self.firehose.get_gpt( lun, int(options["--gpt-num-part-entries"]), int(options["--gpt-part-entry-size"]), int(options["--gpt-part-entry-start-lba"])) if guid_gpt is None: break if len(luns) > 1: storedir = os.path.join(directory, "lun" + str(lun)) else: storedir = directory if not os.path.exists(storedir): os.mkdir(storedir) sfilename = os.path.join(storedir, f"gpt_main{str(lun)}.bin") with open(sfilename, "wb") as write_handle: write_handle.write(data) sfilename = os.path.join(storedir, f"gpt_backup{str(lun)}.bin") with open(sfilename, "wb") as write_handle: write_handle.write( data[self.firehose.cfg.SECTOR_SIZE_IN_BYTES * 2:]) if genxml: guid_gpt.generate_rawprogram( lun, self.firehose.cfg.SECTOR_SIZE_IN_BYTES, storedir) for partition in guid_gpt.partentries: partitionname = partition.name if partition.name in skip: continue filename = os.path.join(storedir, partitionname + ".bin") self.LOGGER.info( f"Dumping partition {str(partition.name)} with sector count {str(partition.sectors)} " + f"as {filename}.") self.firehose.cmd_read(lun, partition.sector, partition.sectors, filename) return True elif cmd == "rf": if not self.check_param(["<filename>"]): return False filename = options["<filename>"] luns = self.getluns(options) for lun in luns: data, guid_gpt = self.firehose.get_gpt( lun, int(options["--gpt-num-part-entries"]), int(options["--gpt-part-entry-size"]), int(options["--gpt-part-entry-start-lba"])) if guid_gpt is None: break if len(luns) > 1: sfilename = f"lun{str(lun)}_" + filename else: sfilename = filename self.printer( f"Dumping sector 0 with sector count {str(guid_gpt.totalsectors)} as {filename}." ) 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}." ) return True elif cmd == "pbl": if not self.check_param(["<filename>"]): return False if not self.check_cmd("peek"): self.LOGGER.error("Peek command isn't supported by edl loader") return False else: filename = options["<filename>"] if self.target_name in infotbl: target_name = infotbl[self.target_name] if len(target_name[0]) > 0: if self.firehose.cmd_peek(target_name[0][0], target_name[0][1], filename, True): self.printer( f"Dumped pbl at offset {hex(target_name[0][0])} as {filename}." ) return True else: self.LOGGER.error( "No known pbl offset for this chipset") else: self.LOGGER.error("Unknown target chipset") self.LOGGER.error("Error on dumping pbl") return False elif cmd == "qfp": if not self.check_param(["<filename>"]): return False if not self.check_cmd("peek"): self.LOGGER.error("Peek command isn't supported by edl loader") return False else: filename = options["<filename>"] if self.target_name in infotbl: target_name = infotbl[self.target_name] if len(target_name[1]) > 0: if self.firehose.cmd_peek(target_name[1][0], target_name[1][1], filename): self.printer( f"Dumped qfprom at offset {hex(target_name[1][0])} as {filename}." ) return True else: self.LOGGER.error( "No known qfprom offset for this chipset") else: self.LOGGER.error("Unknown target chipset") self.LOGGER.error("Error on dumping qfprom") return False elif cmd == "secureboot": if not self.check_cmd("peek"): self.LOGGER.error("Peek command isn't supported by edl loader") return False else: if self.target_name in secureboottbl: self.target_name = secureboottbl[self.target_name] value = unpack("<I", self.firehose.cmd_peek(self.target_name, 4))[0] is_secure = False for area in range(0, 4): sec_boot = (value >> (area * 8)) & 0xFF pk_hashindex = sec_boot & 3 oem_pkhash = True if ((sec_boot >> 4) & 1) == 1 else False auth_enabled = True if ((sec_boot >> 5) & 1) == 1 else False use_serial = True if ((sec_boot >> 6) & 1) == 1 else False if auth_enabled: is_secure = True self.printer(f"Sec_Boot{str(area)} " + f"PKHash-Index:{str(pk_hashindex)} " + f"OEM_PKHash: {str(oem_pkhash)} " + f"Auth_Enabled: {str(auth_enabled)}" + f"Use_Serial: {str(use_serial)}") if is_secure: self.printer("Secure boot enabled.") else: self.printer("Secure boot disabled.") return True else: self.LOGGER.error("Unknown target chipset") return False elif cmd == "memtbl": if not self.check_param(["<filename>"]): return False if not self.check_cmd("peek"): self.LOGGER.error("Peek command isn't supported by edl loader") return False else: filename = options["<filename>"] if self.target_name in infotbl: self.target_name = infotbl[self.target_name] if len(self.target_name[2]) > 0: if self.firehose.cmd_peek(self.target_name[2][0], self.target_name[2][1], filename): self.printer( f"Dumped memtbl at offset {hex(self.target_name[2][0])} as {filename}." ) return True else: self.LOGGER.error( "No known memtbl offset for this chipset") else: self.LOGGER.error("Unknown target chipset") self.LOGGER.error("Error on dumping memtbl") return False elif cmd == "footer": if not self.check_param(["<filename>"]): return False luns = self.getluns(options) filename = options["<filename>"] for lun in luns: data, guid_gpt = self.firehose.get_gpt( lun, int(options["--gpt-num-part-entries"]), int(options["--gpt-part-entry-size"]), int(options["--gpt-part-entry-start-lba"])) if guid_gpt is None: break pnames = [ "userdata2", "metadata", "userdata", "reserved1", "reserved2", "reserved3" ] for partition in guid_gpt.partentries: if partition.name in pnames: self.printer(f"Detected partition: {partition.name}") data = self.firehose.cmd_read_buffer( lun, partition.sector + (partition.sectors - (0x4000 // self.firehose.cfg.SECTOR_SIZE_IN_BYTES)), (0x4000 // self.firehose.cfg.SECTOR_SIZE_IN_BYTES), False) if data == b"": continue val = unpack("<I", data[:4])[0] if (val & 0xFFFFFFF0) == 0xD0B5B1C0: with open(filename, "wb") as write_handle: write_handle.write(data) self.printer( f"Dumped footer from {partition.name} as {filename}." ) return True self.LOGGER.error("Error: Couldn't detect footer partition.") return False elif cmd == "rs": if options["--lun"] != 'None': lun = int(options["--lun"]) else: lun = 0 if not self.check_param( ["<filename>", "<sectors>", "<start_sector>"]): return False start = int(options["<start_sector>"]) sectors = int(options["<sectors>"]) filename = options["<filename>"] data = self.firehose.cmd_read_buffer(lun, start, sectors, False) try: with open(filename, "wb") as write_handle: write_handle.write(data) self.printer( f"Dumped sector {str(start)} with sector count {str(sectors)} as {filename}." ) return True except Exception as error: self.LOGGER.error( f"Error: Couldn't open {filename} for writing: %s" % str(error)) return False elif cmd == "peek": if not self.check_param(["<offset>", "<length>", "<filename>"]): return False if not self.check_cmd("peek"): self.LOGGER.error("Peek command isn't supported by edl loader") return False else: offset = int(options["<offset>"], 16) length = int(options["<length>"], 16) filename = options["<filename>"] self.firehose.cmd_peek(offset, length, filename, True) self.LOGGER.info( f"Peek data from offset {hex(offset)} and length {hex(length)} was written to {filename}" ) return True elif cmd == "peekhex": if not self.check_param(["<offset>", "<length>"]): return False if not self.check_cmd("peek"): self.LOGGER.error("Peek command isn't supported by edl loader") return False else: offset = int(options["<offset>"], 16) length = int(options["<length>"], 16) resp = self.firehose.cmd_peek(offset, length, "", True) self.printer("\n") self.printer(hexlify(resp)) return True elif cmd == "peekqword": if not self.check_param(["<offset>"]): return False if not self.check_cmd("peek"): self.LOGGER.error("Peek command isn't supported by edl loader") return False else: offset = int(options["<offset>"], 16) resp = self.firehose.cmd_peek(offset, 8, "", True) self.printer("\n") self.printer(hex(unpack("<Q", resp[:8])[0])) return True elif cmd == "peekdword": if not self.check_param(["<offset>"]): return False if not self.check_cmd("peek"): self.LOGGER.error("Peek command isn't supported by edl loader") return False else: offset = int(options["<offset>"], 16) resp = self.firehose.cmd_peek(offset, 4, "", True) self.printer("\n") self.printer(hex(unpack("<I", resp[:4])[0])) return True elif cmd == "poke": if not self.check_param(["<offset>", "<filename>"]): return False if not self.check_cmd("poke"): self.LOGGER.error("Poke command isn't supported by edl loader") return False else: offset = int(options["<offset>"], 16) filename = options["<filename>"] return self.firehose.cmd_poke(offset, "", filename, True) elif cmd == "pokehex": if not self.check_param(["<offset>", "<data>"]): return False if not self.check_cmd("poke"): self.LOGGER.error("Poke command isn't supported by edl loader") return False else: offset = int(options["<offset>"], 16) data = unhexlify(options["<data>"]) return self.firehose.cmd_poke(offset, data, "", True) elif cmd == "pokeqword": if not self.check_param(["<offset>", "<data>"]): return False if not self.check_cmd("poke"): self.LOGGER.error("Poke command isn't supported by edl loader") return False else: offset = int(options["<offset>"], 16) data = pack("<Q", int(options["<data>"], 16)) return self.firehose.cmd_poke(offset, data, "", True) elif cmd == "pokedword": if not self.check_param(["<offset>", "<data>"]): return False if not self.check_cmd("poke"): self.LOGGER.error("Poke command isn't supported by edl loader") return False else: offset = int(options["<offset>"], 16) data = pack("<I", int(options["<data>"], 16)) return self.firehose.cmd_poke(offset, data, "", True) elif cmd == "memcpy": if not self.check_param(["<offset>", "<size>"]): return False if not self.check_cmd("poke"): self.printer("Poke command isn't supported by edl loader") else: srcoffset = int(options["<offset>"], 16) size = int(options["<size>"], 16) dstoffset = srcoffset + size if self.firehose.cmd_memcpy(dstoffset, srcoffset, size): self.printer( f"Memcpy from {hex(srcoffset)} to {hex(dstoffset)} succeeded" ) return True else: return False elif cmd == "reset": return self.firehose.cmd_reset() elif cmd == "nop": if not self.check_cmd("nop"): self.LOGGER.error("Nop command isn't supported by edl loader") return False else: return self.firehose.cmd_nop() elif cmd == "setbootablestoragedrive": if not self.check_param(["<lun>"]): return False if not self.check_cmd("setbootablestoragedrive"): self.LOGGER.error( "setbootablestoragedrive command isn't supported by edl loader" ) return False else: return self.firehose.cmd_setbootablestoragedrive( int(options["<lun>"])) elif cmd == "getstorageinfo": if not self.check_cmd("getstorageinfo"): self.LOGGER.error( "getstorageinfo command isn't supported by edl loader") return False else: return self.firehose.cmd_getstorageinfo() elif cmd == "w": if not self.check_param(["<partitionname>", "<filename>"]): return False partitionname = options["<partitionname>"] filename = options["<filename>"] if not os.path.exists(filename): self.LOGGER.error(f"Error: Couldn't find file: {filename}") return False res = self.firehose.detect_partition(options, partitionname) if res[0]: lun = res[1] partition = res[2] sectors = os.stat( filename).st_size // self.firehose.cfg.SECTOR_SIZE_IN_BYTES if (os.stat(filename).st_size % self.firehose.cfg.SECTOR_SIZE_IN_BYTES) > 0: sectors += 1 if sectors > partition.sectors: self.LOGGER.error( f"Error: {filename} has {sectors} sectors but partition only has {partition.sectors}." ) return False if self.firehose.modules is not None: self.firehose.modules.prerun() if self.firehose.cmd_program(lun, partition.sector, filename): self.printer( f"Wrote {filename} to sector {str(partition.sector)}.") return True else: self.printer( f"Error writing {filename} to sector {str(partition.sector)}." ) return False else: fpartitions = res[1] self.LOGGER.error( f"Error: Couldn't detect partition: {partitionname}\nAvailable partitions:" ) for lun in fpartitions: for partition in fpartitions[lun]: if options["--memory"].lower() == "emmc": self.LOGGER.error("\t" + partition) else: self.LOGGER.error(lun + ":\t" + partition) return False elif cmd == "wl": if not self.check_param(["<directory>"]): return False directory = options["<directory>"] if options["--skip"]: skip = options["--skip"].split(",") else: skip = [] luns = self.getluns(options) if not os.path.exists(directory): self.LOGGER.error( f"Error: Couldn't find directory: {directory}") sys.exit() filenames = [] if self.firehose.modules is not None: self.firehose.modules.prerun() for dirName, subdirList, fileList in os.walk(directory): for fname in fileList: filenames.append(os.path.join(dirName, fname)) for lun in luns: data, guid_gpt = self.firehose.get_gpt( lun, int(options["--gpt-num-part-entries"]), int(options["--gpt-part-entry-size"]), int(options["--gpt-part-entry-start-lba"])) if guid_gpt is None: break if "partentries" in dir(guid_gpt): for filename in filenames: for partition in guid_gpt.partentries: partname = filename[filename.rfind("/") + 1:] if ".bin" in partname[-4:]: partname = partname[:-4] if partition.name == partname: if partition.name in skip: continue sectors = os.stat( filename ).st_size // self.firehose.cfg.SECTOR_SIZE_IN_BYTES if (os.stat(filename).st_size % self.firehose.cfg.SECTOR_SIZE_IN_BYTES ) > 0: sectors += 1 if sectors > partition.sectors: self.LOGGER.error( f"Error: {filename} has {sectors} sectors but partition " + f"only has {partition.sectors}.") return False self.printer( f"Writing {filename} to partition {str(partition.name)}." ) self.firehose.cmd_program( lun, partition.sector, filename) else: self.printer( "Couldn't write partition. Either wrong memorytype given or no gpt partition." ) return False return True elif cmd == "ws": if not self.check_param(["<start_sector>"]): return False if options["--lun"] is None: lun = 0 else: lun = int(options["--lun"]) start = int(options["<start_sector>"]) filename = options["<filename>"] if not os.path.exists(filename): self.LOGGER.error(f"Error: Couldn't find file: {filename}") return False if self.firehose.modules is not None: self.firehose.modules.prerun() if self.firehose.cmd_program(lun, start, filename): self.printer(f"Wrote {filename} to sector {str(start)}.") return True else: self.LOGGER.error( f"Error on writing {filename} to sector {str(start)}") return False elif cmd == "wf": if not self.check_param(["<filename>"]): return False if options["--lun"] is None: lun = 0 else: lun = int(options["--lun"]) start = 0 filename = options["<filename>"] if not os.path.exists(filename): self.LOGGER.error(f"Error: Couldn't find file: {filename}") return False if self.firehose.modules is not None: self.firehose.modules.prerun() if self.firehose.cmd_program(lun, start, filename): self.printer(f"Wrote {filename} to sector {str(start)}.") return True else: self.LOGGER.error( f"Error on writing {filename} to sector {str(start)}") return False elif cmd == "e": if not self.check_param(["<partitionname>"]): return False luns = self.getluns(options) partitionname = options["<partitionname>"] for lun in luns: data, guid_gpt = self.firehose.get_gpt( lun, int(options["--gpt-num-part-entries"]), int(options["--gpt-part-entry-size"]), int(options["--gpt-part-entry-start-lba"])) if guid_gpt is None: break if self.firehose.modules is not None: self.firehose.modules.prerun() if "partentries" in dir(guid_gpt): for partition in guid_gpt.partentries: if partition.name == partitionname: self.firehose.cmd_erase(lun, partition.sector, partition.sectors) self.printer( f"Erased {partitionname} starting at sector {str(partition.sector)} with sector count " + f"{str(partition.sectors)}.") return True else: self.printer( "Couldn't erase partition. Either wrong memorytype given or no gpt partition." ) return False self.LOGGER.error( f"Error: Couldn't detect partition: {partitionname}") return False elif cmd == "es": if not self.check_param(["<start_sector>", "<sectors>"]): return False if options["--lun"] is None: lun = 0 else: lun = int(options["--lun"]) start = int(options["<start_sector>"]) sectors = int(options["<sectors>"]) if self.firehose.modules is not None: self.firehose.modules.prerun() if self.firehose.cmd_erase(lun, start, sectors): self.printer( f"Erased sector {str(start)} with sector count {str(sectors)}." ) return True return False elif cmd == "xml": if not self.check_param(["<xmlfile>"]): return False return self.firehose.cmd_xml(options["<xmlfile>"]) elif cmd == "rawxml": if not self.check_param(["<xmlstring>"]): return False return self.firehose.cmd_rawxml(options["<xmlstring>"]) elif cmd == "send": if not self.check_param(["<command>"]): return False command = options["<command>"] resp = self.firehose.cmd_send(command, True) self.printer("\n") self.printer(resp) return True elif cmd == "server": return do_tcp_server(self, options, self.handle_firehose) elif cmd == "modules": if not self.check_param(["<command>", "<options>"]): return False mcommand = options["<command>"] moptions = options["<options>"] if self.firehose.modules is None: self.LOGGER.error("Feature is not supported") return False else: return self.firehose.modules.run(command=mcommand, args=moptions) else: self.LOGGER.error( "Unknown/Missing command, a command is required.") return False