From b0de7a0b7a9e6dc564deb7a51b271ca4750add42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Maldonado?= Date: Wed, 12 Jul 2023 17:18:52 +0200 Subject: [PATCH 1/2] Update pyusb to 1.2.1 pyusb 1.1.0 does not work on newer Python versions. For example, with Python 3.10 and Cat B35, I got the following: am@Host-002:~/github/edl$ python edl.py -loader 0x000940e100000000.mbn -printgpt Qualcomm Sahara / Firehose Client (c) B.Kerler 2018-2019. Using loader 0x000940e100000000.mbn ... Waiting for the device Device detected :) Mode detected: Sahara ------------------------ HWID: 0x000940e100000000 (MSM_ID:0x000940e1,OEM_ID:0x0000,MODEL_ID:0x0000) PK_HASH: 0xcc3153a80293939b90d02d3bf8b23e0292e452fef662c74998421adad42a380f Serial: 0x0fd85d74 SBL Version: 0x00000000 Successfully uploaded programmer :) Traceback (most recent call last): File "/home/am/.local/lib/python3.10/site-packages/usb/_interop.py", line 94, in as_array return array.array('B', data) TypeError: cannot use a str to initialize an array with typecode 'B' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/home/am/github/edl/edl.py", line 419, in main() File "/home/am/github/edl/edl.py", line 191, in main info=fh.connect(0) File "/home/am/github/edl/Library/firehose.py", line 340, in connect rsp=self.xmlsend(data) File "/home/am/github/edl/Library/firehose.py", line 66, in xmlsend self.cdc.write(data,self.cfg.MaxXMLSizeInBytes) File "/home/am/github/edl/Library/usb.py", line 61, in write self.device.write(self.EP_OUT,command[pos:pos+pktsize]) File "/home/am/.local/lib/python3.10/site-packages/usb/core.py", line 981, in write _interop.as_array(data), File "/home/am/.local/lib/python3.10/site-packages/usb/_interop.py", line 99, in as_array a.fromstring(data) # deprecated since 3.2 AttributeError: 'array.array' object has no attribute 'fromstring' --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index fbd4fac..e974361 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ capstone==4.0.2 keystone==18.0.0 -pyusb==1.1.0 +pyusb==1.2.1 pyserial==3.5 From 5c895079d92c22a3d1f425993607a067858a9fef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Maldonado?= Date: Thu, 13 Jul 2023 01:34:55 +0200 Subject: [PATCH 2/2] Fix operation on Cat B35 Since the merge of https://github.com/plugnburn/edl/pull/1 this project no longer works on Cat B35. This commit adds a '-non-standard-responses' option, false by default (restores the behavior before the PR). KaiOS Nokias / Alcatel Cingular Flip should still work by toggling '-non-standard-responses' --- Library/firehose.py | 20 +++++++++++++------- edl.py | 27 ++++++++++++++------------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/Library/firehose.py b/Library/firehose.py index 4e7b8a4..2a39a07 100644 --- a/Library/firehose.py +++ b/Library/firehose.py @@ -134,7 +134,7 @@ def cmd_setbootablestoragedrive(self,partition_number): return False - def cmd_write(self,physical_partition_number,start_sector,filename,Display=True): + def cmd_write(self, physical_partition_number, start_sector, filename, nonStandardResponses, Display=True): size = os.stat(filename).st_size with open(filename,"rb") as rf: #Make sure we fill data up to the sector size @@ -178,6 +178,12 @@ def cmd_write(self,physical_partition_number,start_sector,filename,Display=True) self.cdc.write(b'',self.cfg.MaxPayloadSizeToTargetInBytes) time.sleep(0.2) info = self.xml.getlog(self.cdc.read(self.cfg.MaxXMLSizeInBytes)) + if not nonStandardResponses: + rsp=self.xml.getresponse(self.cdc.read(self.cfg.MaxXMLSizeInBytes)) + if rsp["value"]=="ACK": + return True + else: + print(f"Error:{info[1]}") else: print(f"Error:{rsp}") return False @@ -228,14 +234,14 @@ def cmd_erase(self,physical_partition_number,start_sector,num_partition_sectors, return False return False - def cmd_read_buffer(self,physical_partition_number,start_sector,num_partition_sectors,Display=True): + def cmd_read_buffer(self, physical_partition_number, start_sector, num_partition_sectors, nonStandardResponses, Display=True): if Display: print(f"\nReading from physical partition {str(physical_partition_number)}, sector {str(start_sector)}, sectors {str(num_partition_sectors)}") data=f"\n" - rsp=self.xmlsend(data, True) + rsp=self.xmlsend(data, nonStandardResponses) resData=bytearray() if (rsp[0])==True: bytesToRead=self.cfg.SECTOR_SIZE_IN_BYTES*num_partition_sectors @@ -270,7 +276,7 @@ def cmd_read_buffer(self,physical_partition_number,start_sector,num_partition_se return "" return "" - def cmd_read(self,physical_partition_number,start_sector,num_partition_sectors,filename,Display=True): + def cmd_read(self, physical_partition_number, start_sector, num_partition_sectors, nonStandardResponses, filename, Display=True): if Display: print(f"\nReading from physical partition {str(physical_partition_number)}, sector {str(start_sector)}, sectors {str(num_partition_sectors)}") with open(filename,"wb") as wf: @@ -278,7 +284,7 @@ def cmd_read(self,physical_partition_number,start_sector,num_partition_sectors,f f" num_partition_sectors=\"{num_partition_sectors}\""+\ f" physical_partition_number=\"{physical_partition_number}\""+\ f" start_sector=\"{start_sector}\"/>\n" - rsp=self.xmlsend(data, True) + rsp=self.xmlsend(data, nonStandardResponses) if (rsp[0])==True: bytesToRead=self.cfg.SECTOR_SIZE_IN_BYTES*num_partition_sectors total=bytesToRead @@ -419,11 +425,11 @@ def cmd_peek(self,address,SizeInBytes,filename): ''' data=f"\n" ''' - + ''' try: diff --git a/edl.py b/edl.py index 6b9ecbc..0a859ac 100644 --- a/edl.py +++ b/edl.py @@ -110,6 +110,7 @@ def main(): parser.add_argument('-gpt-num-part-entries', metavar="", type=int, help='[CMD:Firehose] Number of partitions', default=None) parser.add_argument('-gpt-part-entry-size', metavar="", type=int, help='[CMD:Firehose] Size of partition entry', default=None) parser.add_argument('-gpt-part-entry-start-lba', metavar="", type=int, help='[CMD:Firehose] Beginning of partition entries', default=None) + parser.add_argument('-non-standard-responses', help='[CMD:Firehose] Enable non standard response support (for KaiOS Nokias / Alcatel Cingular Flip)', action="store_true") args = parser.parse_args() @@ -190,11 +191,11 @@ def main(): 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) + fh.cmd_read(args.lun, 0, 0x4000//cfg.SECTOR_SIZE_IN_BYTES, args.non_standard_responses, 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) + data = fh.cmd_read_buffer(args.lun, 0, 0x4000 // cfg.SECTOR_SIZE_IN_BYTES, args.non_standard_responses) if data!='': guid_gpt = gpt( num_part_entries=args.gpt_num_part_entries, @@ -212,7 +213,7 @@ def main(): 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) + data = fh.cmd_read_buffer(args.lun, 0, 0x4000 // cfg.SECTOR_SIZE_IN_BYTES, args.non_standard_responses, False) guid_gpt = gpt( num_part_entries=args.gpt_num_part_entries, part_entry_size=args.gpt_part_entry_size, @@ -222,21 +223,21 @@ def main(): for partition in guid_gpt.partentries: if partition.name==partitionname: - data = fh.cmd_read(args.lun, partition.sector, partition.sectors, filename) + data = fh.cmd_read(args.lun, partition.sector, partition.sectors, args.non_standard_responses, 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) + data = fh.cmd_read_buffer(args.lun, 0, 0x4000 // cfg.SECTOR_SIZE_IN_BYTES, args.non_standard_responses, 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) + data = fh.cmd_read(args.lun, 0, guid_gpt.totalsectors, args.non_standard_responses, filename) print(f"Dumped sector 0 with sector count {str(guid_gpt.totalsectors)} as {filename}.") exit(0) elif args.pbl!='': @@ -283,7 +284,7 @@ def main(): exit(0) elif args.footer!='': filename=args.footer - data = fh.cmd_read_buffer(args.lun, 0, 0x4000 // cfg.SECTOR_SIZE_IN_BYTES,False) + data = fh.cmd_read_buffer(args.lun, 0, 0x4000 // cfg.SECTOR_SIZE_IN_BYTES, args.non_standard_responses, False) guid_gpt = gpt( num_part_entries=args.gpt_num_part_entries, part_entry_size=args.gpt_part_entry_size, @@ -294,7 +295,7 @@ def main(): 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) + data = fh.cmd_read_buffer(args.lun, partition.sector+(partition.sectors-(0x4000 // cfg.SECTOR_SIZE_IN_BYTES)), (0x4000 // cfg.SECTOR_SIZE_IN_BYTES), args.non_standard_responses, filename) val=struct.unpack("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) + data = fh.cmd_write(args.lun, partition.sector, filename, args.non_standard_responses) print(f"Wrote {filename} to sector {str(partition.sector)}.") exit(0) print(f"Error: Couldn't detect partition: {partitionname}") @@ -370,14 +371,14 @@ def main(): 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: + if fh.cmd_write(args.lun, start, filename, args.non_standard_responses)==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) + data = fh.cmd_read_buffer(args.lun, 0, 0x4000 // cfg.SECTOR_SIZE_IN_BYTES, args.non_standard_responses, False) guid_gpt=gpt( num_part_entries=args.gpt_num_part_entries, part_entry_size=args.gpt_part_entry_size,