def get_backup_gpt(self, lun, gpt_num_part_entries, gpt_part_entry_size, gpt_part_entry_start_lba, parttype="user"): data = self.readflash(addr=0, length=2 * self.config.pagesize, filename="", parttype=parttype, display=False) if data == b"": return None guid_gpt = gpt( num_part_entries=gpt_num_part_entries, part_entry_size=gpt_part_entry_size, part_entry_start_lba=gpt_part_entry_start_lba, ) header = guid_gpt.parseheader(data, self.config.SECTOR_SIZE_IN_BYTES) if "backup_lba" in header: sectors = header["first_usable_lba"] - 1 data = self.readflash(addr=header["backup_lba"] * self.config.pagesize, length=sectors * self.config.pagesize, filename="", display=False) if data == b"": return None return data else: return None
def get_backup_gpt(self, lun, gpt_num_part_entries, gpt_part_entry_size, gpt_part_entry_start_lba): data = self.cmd_read_buffer(lun, 0, 2, False) if data == b"": return None guid_gpt = gpt( num_part_entries=gpt_num_part_entries, part_entry_size=gpt_part_entry_size, part_entry_start_lba=gpt_part_entry_start_lba, ) header = guid_gpt.parseheader(data, self.cfg.SECTOR_SIZE_IN_BYTES) if "backup_lba" in header: sectors = header["first_usable_lba"] - 1 data = self.cmd_read_buffer(lun, header["backup_lba"], sectors, False) if data==b"": return None return data else: return None
def get_gpt(self, lun, gpt_num_part_entries, gpt_part_entry_size, gpt_part_entry_start_lba): data = self.cmd_read_buffer(lun, 0, 2, False) if data == "": return None, None guid_gpt = gpt( num_part_entries=gpt_num_part_entries, part_entry_size=gpt_part_entry_size, part_entry_start_lba=gpt_part_entry_start_lba, ) header = guid_gpt.parseheader(data, self.cfg.SECTOR_SIZE_IN_BYTES) if "first_usable_lba" in header: sectors = header["first_usable_lba"] if sectors == 0: return None, None data = self.cmd_read_buffer(lun, 0, sectors, False) guid_gpt.parse(data, self.cfg.SECTOR_SIZE_IN_BYTES) return data, guid_gpt else: return None, None
def get_gpt(self, gpt_num_part_entries, gpt_part_entry_size, gpt_part_entry_start_lba, parttype="user"): data = self.readflash(addr=0, length=2 * self.config.pagesize, filename="", parttype=parttype, display=False) if data[:9] == b"EMMC_BOOT" and self.read_pmt: partdata, partentries = self.read_pmt() if partdata == b"": return None, None else: return partdata, partentries if data == b"": return None, None guid_gpt = gpt( num_part_entries=gpt_num_part_entries, part_entry_size=gpt_part_entry_size, part_entry_start_lba=gpt_part_entry_start_lba, ) header = guid_gpt.parseheader(data, self.config.pagesize) if "first_usable_lba" in header: sectors = header["first_usable_lba"] if sectors == 0: return None, None data = self.readflash(addr=0, length=sectors * self.config.pagesize, filename="", parttype=parttype, display=False) if data == b"": return None, None guid_gpt.parse(data, self.config.pagesize) return data, guid_gpt else: return None, None
def main(): info = 'Qualcomm Sahara / Firehose Client (c) B.Kerler 2018-2019.' parser = argparse.ArgumentParser(description=info) print("\n" + info + "\n\n") parser.add_argument( '-loader', metavar="none,<filename>", help='[Option] Flash programmer to load e.g. prog_emmc_firehose.elf', default='') parser.add_argument('-vid', metavar="<vid>", help='[Option] Specify vid, default=0x05c6)', default="0x05C6") parser.add_argument('-pid', metavar="<pid>", help='[Option] Specify pid, default=0x9008)', default="0x9008") parser.add_argument( '-maxpayload', metavar="<bytes>", help= '[Option] The max bytes to transfer in firehose mode (default=1048576)', type=int, default=1048576) parser.add_argument( '-skipwrite', help= '[Option] Do not write actual data to disk (use this for UFS provisioning)', action="store_true") parser.add_argument( '-skipstorageinit', help= '[Option] Do not initialize storage device (use this for UFS provisioning)', action="store_true") parser.add_argument('-memory', metavar="<UFS/eMMC>", help='[Option] Memory type (default=UFS)', default='UFS') parser.add_argument('-sectorsize', metavar="<bytes>", help='[Option] Define Disk Sector Size (default=512)', type=int, default=512) parser.add_argument('-lun', metavar="<num>", help='[Option] Define LUN', type=int, default=0) #parser.add_argument('-debug', help='[Option] Enable debug output', action="store_true") parser.add_argument( '-debugmode', help='[CMD:Sahara] Switch to Memory Dump mode (Debug only)', action="store_true") parser.add_argument('-debugread', help='[CMD:Sahara] Read Debug Logs', action="store_true") parser.add_argument('-dmss', help='[CMD:Sahara] Switch to DMSS Download mode', action="store_true") parser.add_argument('-streaming', help='[CMD:Sahara] Switch to Streaming Download mode', action="store_true") parser.add_argument( '-r', metavar=("<PartName>", "<filename>"), help='[CMD:Firehose] Dump entire partition based on partition name', nargs=2, default=[]) parser.add_argument('-rf', metavar=("<filename>"), help='[CMD:Firehose] Dump whole lun', default="") parser.add_argument( '-rs', metavar=("<start_sector>", "<sectors>", "<filename>"), help='[CMD:Firehose] Dump from start sector to end sector to file', nargs=3, default=[]) parser.add_argument('-pbl', metavar=("<filename>"), help='[CMD:Firehose] Dump boot rom (pbl)', default="") parser.add_argument('-qfp', metavar=("<filename>"), help='[CMD:Firehose] Dump qfprom', default="") parser.add_argument('-memtbl', metavar=("<filename>"), help='[CMD:Firehose] Dump memory table', default="") parser.add_argument('-footer', metavar=("<filename>"), help='[CMD:Firehose] Dump crypto footer', default="") parser.add_argument('-gpt', metavar="<filename>", help='[CMD:Firehose] Dump gpt to file', default="") parser.add_argument('-printgpt', help='[CMD:Firehose] Print gpt', action="store_true") parser.add_argument( '-peek', metavar=("<offset>", "<length>", "<filename>"), help='[CMD:Firehose] Read memory from offset,length to file', nargs=3, default=[]) parser.add_argument('-w', metavar=("<partitionname>", "<filename>"), help='[CMD:Firehose] Write filename to GPT partition', nargs=2, default=[]) parser.add_argument( '-ws', metavar=("<start_sector>", "<filename>"), help='[CMD:Firehose] Write filename at sector <start_sector>', nargs=2, default=[]) parser.add_argument( '-e', metavar="<partitionname>", help='[CMD:Firehose] Erase the entire partition specified', default='') parser.add_argument( '-es', metavar=("<start_sector>", "<num_sectors>"), help= '[CMD:Firehose] Erase disk from start sector for number of sectors', nargs=2, default=[]) parser.add_argument('-reset', help='[CMD:Firehose] Reset device', action="store_true") parser.add_argument('-getstorageinfo', help='[CMD:Firehose] Get Storage/Flash Info', action="store_true") parser.add_argument( '-setbootablestoragedrive', metavar="<number>", help= '[CMD:Firehose] Set the physical partition number active for booting', default='') parser.add_argument('-x', metavar="<xmldata>", help='[CMD:Firehose] XML to run in firehose mode', default='') parser.add_argument('-gpt-num-part-entries', metavar="<number>", type=int, help='[CMD:Firehose] Number of partitions', default=None) parser.add_argument('-gpt-part-entry-size', metavar="<number>", type=int, help='[CMD:Firehose] Size of partition entry', default=None) parser.add_argument('-gpt-part-entry-start-lba', metavar="<number>", type=int, help='[CMD:Firehose] Beginning of partition entries', default=None) args = parser.parse_args() xml = xmlparser() mode = "" loop = 0 if args.vid != "": vid = int(args.vid, 16) if args.pid != "": pid = int(args.pid, 16) cdc = usb_class(vid=vid, pid=pid) sahara = qualcomm_sahara(cdc) if args.loader == 'none': print("Trying with no loader given ...") sahara.programmer = None elif (args.loader == ""): print("Trying with loaders in Loader directory ...") sahara.programmer = None elif (args.loader != ''): print(f"Using loader {args.loader} ...") with open(args.loader, "rb") as rf: sahara.programmer = rf.read() else: print( "Sorry, you need a firehose loader (-loader) or try without loader \"-loader none\" !" ) print("Use with -h for displaying help.") exit(0) print("Waiting for the device") while (cdc.connected == False): cdc.connected = cdc.connect() if cdc.connected == False: sys.stdout.write('.') if (loop >= 20): sys.stdout.write('\n') loop = 0 loop += 1 time.sleep(1) sys.stdout.flush() else: print("Device detected :)") mode = sahara.connect() print(f"Mode detected: {mode}") break if mode == "Sahara": m = sahara.info() if args.debugmode: sahara.debug_mode() exit(0) elif args.debugread: sahara.cmdexec_read_debug_data() elif args.dmss: sahara.cmdexec_switch_to_dmss_dload() exit(0) elif args.streaming: sahara.cmdexec_switch_to_stream_dload() exit(0) else: if sahara.upload_firehoseloader() == True: time.sleep(0.3) mode = "Firehose" print("Successfully uploaded programmer :)") else: sahara.bit64 = True if mode == "Firehose": cfg = qualcomm_firehose.cfg() cfg.MemoryName = args.memory cfg.ZLPAwareHost = 1 cfg.SkipStorageInit = args.skipstorageinit cfg.SkipWrite = args.skipwrite cfg.MaxPayloadSizeToTargetInBytes = args.maxpayload cfg.SECTOR_SIZE_IN_BYTES = args.sectorsize cfg.bit64 = sahara.bit64 fh = qualcomm_firehose(cdc, xml, cfg) info = fh.connect(0) if args.gpt != '': fh.cmd_read(args.lun, 0, 0x4000 // cfg.SECTOR_SIZE_IN_BYTES, args.gpt) print(f"Dumped GPT to {args.gpt}") exit(0) elif args.printgpt == True: data = fh.cmd_read_buffer(args.lun, 0, 0x4000 // cfg.SECTOR_SIZE_IN_BYTES) if data != '': guid_gpt = gpt( num_part_entries=args.gpt_num_part_entries, part_entry_size=args.gpt_part_entry_size, part_entry_start_lba=args.gpt_part_entry_start_lba, ) guid_gpt.parse(data, cfg.SECTOR_SIZE_IN_BYTES) guid_gpt.print() else: print("Error on reading GPT, maybe wrong memoryname given ?") exit(0) elif len(args.r) != 0: if len(args.r) != 2: print("Usage: -r <partitionname> <filename>") exit(0) partitionname = args.r[0] filename = args.r[1] data = fh.cmd_read_buffer(args.lun, 0, 0x4000 // cfg.SECTOR_SIZE_IN_BYTES, False) guid_gpt = gpt( num_part_entries=args.gpt_num_part_entries, part_entry_size=args.gpt_part_entry_size, part_entry_start_lba=args.gpt_part_entry_start_lba, ) guid_gpt.parse(data, cfg.SECTOR_SIZE_IN_BYTES) for partition in guid_gpt.partentries: if partition.name == partitionname: data = fh.cmd_read(args.lun, partition.sector, partition.sectors, filename) print( f"Dumped sector {str(partition.sector)} with sector count {str(partition.sectors)} as {filename}." ) exit(0) print(f"Error: Couldn't detect partition: {partitionname}") exit(0) elif args.rf != '': filename = args.rf data = fh.cmd_read_buffer(args.lun, 0, 0x4000 // cfg.SECTOR_SIZE_IN_BYTES, False) guid_gpt = gpt( num_part_entries=args.gpt_num_part_entries, part_entry_size=args.gpt_part_entry_size, part_entry_start_lba=args.gpt_part_entry_start_lba, ) guid_gpt.parse(data, cfg.SECTOR_SIZE_IN_BYTES) data = fh.cmd_read(args.lun, 0, guid_gpt.totalsectors, filename) print( f"Dumped sector 0 with sector count {str(guid_gpt.totalsectors)} as {filename}." ) exit(0) elif args.pbl != '': filename = args.pbl if fh.cfg.TargetName in infotbl: v = infotbl[fh.cfg.TargetName] if len(v[0]) > 0: if fh.cmd_peek(v[0][0], v[0][1], filename): print( f"Dumped pbl at offset {hex(v[0][0])} as {filename}." ) exit(0) else: print("No known pbl offset for this chipset") else: print("Unknown target chipset") print("Error on dumping pbl") exit(0) elif args.qfp != '': filename = args.qfp if fh.cfg.TargetName in infotbl: v = infotbl[fh.cfg.TargetName] if len(v[1]) > 0: if fh.cmd_peek(v[1][0], v[1][1], filename): print( f"Dumped qfprom at offset {hex(v[1][0])} as {filename}." ) exit(0) else: print("No known qfprom offset for this chipset") else: print("Unknown target chipset") print("Error on dumping pbl") exit(0) elif args.memtbl != '': filename = args.memtbl if fh.cfg.TargetName in infotbl: v = infotbl[fh.cfg.TargetName] if len(v[2]) > 0: if fh.cmd_peek(v[2][0], v[2][1], filename): print( f"Dumped qfprom at offset {hex(v[2][0])} as {filename}." ) exit(0) else: print("No known qfprom offset for this chipset") else: print("Unknown target chipset") print("Error on dumping pbl") exit(0) elif args.footer != '': filename = args.footer data = fh.cmd_read_buffer(args.lun, 0, 0x4000 // cfg.SECTOR_SIZE_IN_BYTES, False) guid_gpt = gpt( num_part_entries=args.gpt_num_part_entries, part_entry_size=args.gpt_part_entry_size, part_entry_start_lba=args.gpt_part_entry_start_lba, ) guid_gpt.parse(data, cfg.SECTOR_SIZE_IN_BYTES) pnames = [ "userdata2", "metadata", "userdata", "reserved1", "reserved2", "reserved3" ] for partition in guid_gpt.partentries: if partition.name in pnames: print(f"Detected partition: {partition.name}") data = fh.cmd_read_buffer( args.lun, partition.sector + (partition.sectors - (0x4000 // cfg.SECTOR_SIZE_IN_BYTES)), (0x4000 // cfg.SECTOR_SIZE_IN_BYTES), filename) val = struct.unpack("<I", data[:4])[0] if ((val & 0xFFFFFFF0) == 0xD0B5B1C0): with open(filename, "wb") as wf: wf.write(data) print( f"Dumped footer from {partition.name} as {filename}." ) exit(0) print(f"Error: Couldn't detect partition: {partitionname}") exit(0) elif len(args.rs) != 0: if len(args.rs) != 3: print("Usage: -rs <start_sector> <sectors> <filename>") exit(0) start = int(args.rs[0]) sectors = int(args.rs[1]) filename = args.rs[2] data = fh.cmd_read(args.lun, start, sectors, filename) print( f"Dumped sector {str(start)} with sector count {str(sectors)} as {filename}." ) exit(0) elif len(args.peek) != 0: if len(args.peek) != 3: print("Usage: -peek <offset> <length> <filename>") exit(0) offset = int(args.peek[0], 16) length = int(args.peek[1], 16) filename = args.peek[2] fh.cmd_peek(offset, length, filename) exit(0) elif args.reset: fh.cmd_reset() exit(0) elif args.setbootablestoragedrive != '': fh.cmd_setbootablestoragedrive(int(args.setbootablestoragedrive)) exit(0) elif args.getstorageinfo: fh.cmd_getstorageinfo() exit(0) elif len(args.w) != 0: if len(args.w) != 2: print("Usage: -w <partitionname> <filename>") exit(0) partitionname = args.w[0] filename = args.w[1] if not os.path.exists(filename): print(f"Error: Couldn't find file: {filename}") exit(0) data = fh.cmd_read_buffer(args.lun, 0, 0x4000 // cfg.SECTOR_SIZE_IN_BYTES, False) guid_gpt = gpt( num_part_entries=args.gpt_num_part_entries, part_entry_size=args.gpt_part_entry_size, part_entry_start_lba=args.gpt_part_entry_start_lba, ) guid_gpt.parse(data, cfg.SECTOR_SIZE_IN_BYTES) for partition in guid_gpt.partentries: if partition.name == partitionname: sectors = os.stat( filename).st_size // fh.cfg.SECTOR_SIZE_IN_BYTES if (os.stat(filename).st_size % fh.cfg.SECTOR_SIZE_IN_BYTES) > 0: sectors += 1 if sectors > partition.sectors: print( f"Error: {filename} has {sectors} sectors but partition only has {partition.sectors}." ) exit(0) data = fh.cmd_write(args.lun, partition.sector, filename) print( f"Wrote {filename} to sector {str(partition.sector)}.") exit(0) print(f"Error: Couldn't detect partition: {partitionname}") exit(0) elif len(args.ws) != 0: if len(args.ws) != 2: print("Usage: -ws <start_sector> <filename>") exit(0) start = int(args.ws[0]) filename = args.ws[1] if not os.path.exists(filename): print(f"Error: Couldn't find file: {filename}") exit(0) if fh.cmd_write(args.lun, start, filename) == True: print(f"Wrote {filename} to sector {str(start)}.") else: print(f"Error on writing {filename} to sector {str(start)}") exit(0) elif args.e != '': partitionname = args.e data = fh.cmd_read_buffer(args.lun, 0, 0x4000 // cfg.SECTOR_SIZE_IN_BYTES, False) guid_gpt = gpt( num_part_entries=args.gpt_num_part_entries, part_entry_size=args.gpt_part_entry_size, part_entry_start_lba=args.gpt_part_entry_start_lba, ) guid_gpt.parse(data, cfg.SECTOR_SIZE_IN_BYTES) for partition in guid_gpt.partentries: if partition.name == partitionname: fh.cmd_erase(args.lun, partition.sector, partition.sectors) print( f"Erased {partitionname} starting at sector {str(partition.sector)} with sector count {str(partition.sectors)}." ) exit(0) print(f"Error: Couldn't detect partition: {partitionname}") exit(0) elif len(args.es) != 0: if len(args.es) != 2: print("Usage: -ws <start_sector> <sectors>") exit(0) start = int(args.es[0]) sectors = int(args.es[1]) data = fh.cmd_erase(args.lun, start, sectors) print( f"Erased sector {str(start)} with sector count {str(sectors)}." ) exit(0) elif args.x != '': data = fh.cmd_xml(args.x) exit(0) else: print("Unknown/Missing command, a command is required.") exit(0) else: print("Sorry, couldn't talk to Sahara, please reboot the device !") exit(0) exit(0)