diff --git a/MANIFEST.in b/MANIFEST.in index 24f706e..7256b3d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,6 +2,7 @@ include README.md include LICENSE include fastpwn recursive-include Loaders * -include edl/Windows/*.dll -recursive-include edl/Library/TestFiles * +include edlclient/Windows/*.dll +include edlclient/Config/nvitems.xml +recursive-include edlclient/Library/TestFiles * diff --git a/README.md b/README.md index 1b68017..d88e6d8 100755 --- a/README.md +++ b/README.md @@ -91,79 +91,79 @@ or from [here](https://github.com/bkerler/Loaders) ### Generic -- ```python edl.py -h``` -> to see help with all options -- ```python edl.py server --memory=ufs --tcpport=1340``` -> Run TCP/IP server on port 1340, see tcpclient.py for an example client -- ```python edl.py xml run.xml``` -> To send a xml file run.xml via firehose -- ```python edl.py reset``` -> To reboot the phone -- ```python edl.py rawxml ``` -> To send own xml string, example : - ```python edl.py rawxml "``` -- ```python edl.py [anycommand] --debugmode``` -> enables Verbose. Only do that is REALLY needed as it will print out everything happening ! +- ```edl -h``` -> to see help with all options +- ```edl server --memory=ufs --tcpport=1340``` -> Run TCP/IP server on port 1340, see tcpclient.py for an example client +- ```edl xml run.xml``` -> To send a xml file run.xml via firehose +- ```edl reset``` -> To reboot the phone +- ```edl rawxml ``` -> To send own xml string, example : + ```edl rawxml "``` +- ```edl [anycommand] --debugmode``` -> enables Verbose. Only do that is REALLY needed as it will print out everything happening ! ### For EMMC Flash -- ```python edl.py printgpt``` -> to print gpt on device with emmc -- ```python edl.py rf flash.bin``` -> to dump whole flash for device with emmc -- ```python edl.py rl dumps --skip=userdata --genxml``` -> to dump all partitions to directory dumps for device with emmc and skipping userdata partition, write rawprogram0.xml -- ```python edl.py rs 0 15 data.bin``` -> to dump 15 sectors from starting sector 0 to file data.bin for device with emmc -- ```python edl.py rs 0 15 data.bin --skipresponse``` -> to dump 15 sectors from starting sector 0 to file data.bin for device with emmc, ignores missing ACK from phones -- ```python edl.py r boot_a boot.img``` -> to dump the partition "boot_a" to the filename boot.img for device with emmc -- ```python edl.py r boot_a,boot_b boot_a.img,boot_b.img``` -> to dump multiple partitions to multiple filenames -- ```python edl.py footer footer.bin``` -> to dump the crypto footer for Androids with emmc flash -- ```python edl.py w boot_a boot.img``` -> to write boot.img to the "boot" partition on lun 0 on the device with emmc flash -- ```python edl.py w gpt gpt.img``` -> to write gpt partition table from gpt.img to the first sector on the device with emmc flash -- ```python edl.py wl dumps``` -> to write all files from "dumps" folder to according partitions to flash -- ```python edl.py wf dump.bin``` -> to write the rawimage dump.bin to flash -- ```python edl.py e misc``` -> to erase the partition misc on emmc flash -- ```python edl.py gpt . --genxml``` -> dump gpt_main0.bin/gpt_backup0.bin and write rawpartition0.xml to current directory (".") +- ```edl printgpt``` -> to print gpt on device with emmc +- ```edl rf flash.bin``` -> to dump whole flash for device with emmc +- ```edl rl dumps --skip=userdata --genxml``` -> to dump all partitions to directory dumps for device with emmc and skipping userdata partition, write rawprogram0.xml +- ```edl rs 0 15 data.bin``` -> to dump 15 sectors from starting sector 0 to file data.bin for device with emmc +- ```edl rs 0 15 data.bin --skipresponse``` -> to dump 15 sectors from starting sector 0 to file data.bin for device with emmc, ignores missing ACK from phones +- ```edl r boot_a boot.img``` -> to dump the partition "boot_a" to the filename boot.img for device with emmc +- ```edl r boot_a,boot_b boot_a.img,boot_b.img``` -> to dump multiple partitions to multiple filenames +- ```edl footer footer.bin``` -> to dump the crypto footer for Androids with emmc flash +- ```edl w boot_a boot.img``` -> to write boot.img to the "boot" partition on lun 0 on the device with emmc flash +- ```edl w gpt gpt.img``` -> to write gpt partition table from gpt.img to the first sector on the device with emmc flash +- ```edl wl dumps``` -> to write all files from "dumps" folder to according partitions to flash +- ```edl wf dump.bin``` -> to write the rawimage dump.bin to flash +- ```edl e misc``` -> to erase the partition misc on emmc flash +- ```edl gpt . --genxml``` -> dump gpt_main0.bin/gpt_backup0.bin and write rawpartition0.xml to current directory (".") ### For UFS Flash -- ```python edl.py printgpt --memory=ufs --lun=0``` -> to print gpt on lun 0 -- ```python edl.py printgpt --memory=ufs``` -> to print gpt of all lun -- ```python edl.py rf lun0.bin --memory=ufs --lun=0``` -> to dump whole lun 0 -- ```python edl.py rf flash.bin --memory=ufs``` -> to dump all luns as lun0_flash.bin, lun1_flash.bin, ... -- ```python edl.py rl dumps --memory=ufs --lun=0 --skip=userdata,vendor_a``` -> to dump all partitions from lun0 to directory dumps for device with ufs and skip userdata and vendor_a partition -- ```python edl.py rl dumps --memory=ufs --genxml``` -> to dump all partitions from all lun to directory dumps and write rawprogram[lun].xml -- ```python edl.py rs 0 15 data.bin --memory=ufs --lun=0``` -> to dump 15 sectors from starting sector 0 from lun 0 to file data.bin -- ```python edl.py r boot_a boot.img --memory=ufs --lun=4``` -> to dump the partition "boot_a" from lun 4 to the filename boot.img -- ```python edl.py r boot_a boot.img --memory=ufs``` -> to dump the partition "boot_a" to the filename boot.img using lun autodetection -- ```python edl.py r boot_a,boot_b boot_a.img,boot_b.img --memory=ufs``` -> to dump multiple partitions to multiple filenames -- ```python edl.py footer footer.bin --memory=ufs``` -> to dump the crypto footer -- ```python edl.py w boot boot.img --memory=ufs --lun=4``` -> to write boot.img to the "boot" partition on lun 4 on the device with ufs flash -- ```python edl.py w gpt gpt.img --memory=ufs --lun=4``` -> to write gpt partition table from gpt.img to the lun 4 on the device with ufs flash -- ```python edl.py wl dumps --memory=ufs --lun=0``` -> to write all files from "dumps" folder to according partitions to flash lun 0 -- ```python edl.py wl dumps --memory=ufs``` -> to write all files from "dumps" folder to according partitions to flash and try to autodetect lun -- ```python edl.py wf dump.bin --memory=ufs --lun=0``` -> to write the rawimage dump.bin to flash lun 0 -- ```python edl.py e misc --memory=ufs --lun=0``` -> to erase the partition misc on lun 0 -- ```python edl.py gpt . --genxml --memory=ufs``` -> dump gpt_main[lun].bin/gpt_backup[lun].bin and write rawpartition[lun].xml to current directory (".") +- ```edl printgpt --memory=ufs --lun=0``` -> to print gpt on lun 0 +- ```edl printgpt --memory=ufs``` -> to print gpt of all lun +- ```edl rf lun0.bin --memory=ufs --lun=0``` -> to dump whole lun 0 +- ```edl rf flash.bin --memory=ufs``` -> to dump all luns as lun0_flash.bin, lun1_flash.bin, ... +- ```edl rl dumps --memory=ufs --lun=0 --skip=userdata,vendor_a``` -> to dump all partitions from lun0 to directory dumps for device with ufs and skip userdata and vendor_a partition +- ```edl rl dumps --memory=ufs --genxml``` -> to dump all partitions from all lun to directory dumps and write rawprogram[lun].xml +- ```edl rs 0 15 data.bin --memory=ufs --lun=0``` -> to dump 15 sectors from starting sector 0 from lun 0 to file data.bin +- ```edl r boot_a boot.img --memory=ufs --lun=4``` -> to dump the partition "boot_a" from lun 4 to the filename boot.img +- ```edl r boot_a boot.img --memory=ufs``` -> to dump the partition "boot_a" to the filename boot.img using lun autodetection +- ```edl r boot_a,boot_b boot_a.img,boot_b.img --memory=ufs``` -> to dump multiple partitions to multiple filenames +- ```edl footer footer.bin --memory=ufs``` -> to dump the crypto footer +- ```edl w boot boot.img --memory=ufs --lun=4``` -> to write boot.img to the "boot" partition on lun 4 on the device with ufs flash +- ```edl w gpt gpt.img --memory=ufs --lun=4``` -> to write gpt partition table from gpt.img to the lun 4 on the device with ufs flash +- ```edl wl dumps --memory=ufs --lun=0``` -> to write all files from "dumps" folder to according partitions to flash lun 0 +- ```edl wl dumps --memory=ufs``` -> to write all files from "dumps" folder to according partitions to flash and try to autodetect lun +- ```edl wf dump.bin --memory=ufs --lun=0``` -> to write the rawimage dump.bin to flash lun 0 +- ```edl e misc --memory=ufs --lun=0``` -> to erase the partition misc on lun 0 +- ```edl gpt . --genxml --memory=ufs``` -> dump gpt_main[lun].bin/gpt_backup[lun].bin and write rawpartition[lun].xml to current directory (".") ### QFIL emulation (credits to LyuOnLine): - For flashing full image: ``` - python edl.py qfil rawprogram0.xml patch0.xml image_dir + edl qfil rawprogram0.xml patch0.xml image_dir ``` ------------------------------------------------------------------------------------------------------------------------------------ ### For devices with peek/poke command -- ```python edl.py peek 0x200000 0x10 mem.bin``` -> To dump 0x10 bytes from offset 0x200000 to file mem.bin from memory -- ```python edl.py peekhex 0x200000 0x10``` -> To dump 0x10 bytes from offset 0x200000 as hex string from memory -- ```python edl.py peekqword 0x200000``` -> To display a qword (8-bytes) at offset 0x200000 from memory -- ```python edl.py pokeqword 0x200000 0x400000``` -> To write the q-word value 0x400000 to offset 0x200000 in memory -- ```python edl.py poke 0x200000 mem.bin``` -> To write the binary file mem.bin to offset 0x200000 in memory -- ```python edl.py secureboot``` -> To display secureboot fuses (only on EL3 loaders) -- ```python edl.py pbl pbl.bin``` -> To dump pbl (only on EL3 loaders) -- ```python edl.py qfp qfp.bin``` -> To dump qfprom fuses (only on EL3 loaders) +- ```edl peek 0x200000 0x10 mem.bin``` -> To dump 0x10 bytes from offset 0x200000 to file mem.bin from memory +- ```edl peekhex 0x200000 0x10``` -> To dump 0x10 bytes from offset 0x200000 as hex string from memory +- ```edl peekqword 0x200000``` -> To display a qword (8-bytes) at offset 0x200000 from memory +- ```edl pokeqword 0x200000 0x400000``` -> To write the q-word value 0x400000 to offset 0x200000 in memory +- ```edl poke 0x200000 mem.bin``` -> To write the binary file mem.bin to offset 0x200000 in memory +- ```edl secureboot``` -> To display secureboot fuses (only on EL3 loaders) +- ```edl pbl pbl.bin``` -> To dump pbl (only on EL3 loaders) +- ```edl qfp qfp.bin``` -> To dump qfprom fuses (only on EL3 loaders) ------------------------------------------------------------------------------------------------------------------------------------ ### For generic unlocking -- ```python edl.py modules oemunlock enable``` -> Unlocks OEM if partition "config" exists, fastboot oem unlock is still needed afterwards +- ```edl modules oemunlock enable``` -> Unlocks OEM if partition "config" exists, fastboot oem unlock is still needed afterwards #### Dump memory (0x900E mode) -- ```python edl.py memorydump``` +- ```edl memorydump``` - ------------------------------------------------------------------------------------------------------------------------------------ ### Streaming mode (credits to forth32) @@ -173,34 +173,34 @@ or from [here](https://github.com/bkerler/Loaders) ##### Sierra Wireless Modem - Send AT!BOOTHOLD and AT!QPSTDLOAD to modem port or use ```modem/boottodwnload.py``` script - Send AT!ENTERCND="A710" and then AT!EROPTION=0 for memory dump -- ```python edl.py --vid 1199 --pid 9070 --loader=loaders/NPRG9x35p.bin printgpt``` -> To show the partition table +- ```edl --vid 1199 --pid 9070 --loader=loaders/NPRG9x35p.bin printgpt``` -> To show the partition table ##### Netgear MR1100 - run ```python modem/boottodownload.py```, device will enter download mode (0x900E pid) -- ```python edl.py printgpt --loader=Loaders/qualcomm/patched/mdm9x5x/NPRG9x55p.bin```, device will reboot to 0x9008 -- now use python edl.py regulary such as ```python edl.py printgpt``` (do not use loader option) +- ```edl printgpt --loader=Loaders/qualcomm/patched/mdm9x5x/NPRG9x55p.bin```, device will reboot to 0x9008 +- now use edl regulary such as ```edl printgpt``` (do not use loader option) ##### ZTE MF920V, Quectel, Telit, etc.. Modem -- run ```python modem/enableadb.sh```, or send to at port "AT+ZCDRUN=E", or send via ```python diag.py -sahara``` +- run ```python modem/enableadb.sh```, or send to at port "AT+ZCDRUN=E", or send via ```qc_diag -sahara``` - ```adb reboot edl``` -- ```python edl.py printgpt``` -> To show the partition table +- ```edl printgpt``` -> To show the partition table ## Run Diag port tools (examples) For Oneplus 6T, enter *#801#* on dialpad, set Engineer Mode and Serial to on and try : -- ```python diag.py -vid 0x05c6 -pid 0x676c -interface 0 -info``` +- ```qc_diag -vid 0x05c6 -pid 0x676c -interface 0 -info``` ### Usage -- ```python diag.py -vid 0x1234 -pid 0x5678 -interface 0 -info``` -> Send cmd "00" and return info -- ```python diag.py -vid 0x1234 -pid 0x5678 -interface 0 -spc 303030303030``` -> Send spc "303030303030" -- ```python diag.py -vid 0x1234 -pid 0x5678 -interface 0 -cmd 00``` -> Send cmd "00" (hexstring) -- ```python diag.py -vid 0x1234 -pid 0x5678 -interface 0 -nvread 0x55``` -> Display nvitem 0x55 -- ```python diag.py -vid 0x1234 -pid 0x5678 -interface 0 -nvbackup backup.json``` -> Backup all nvitems to a json structured file -- ```python diag.py -vid 0x1234 -pid 0x5678 -interface 0 -efsread efs.bin``` -> Dump the EFS Modem partition to file efs.bin -- ```python diag.py -vid 0x1234 -pid 0x5678 -interface 0 -efslistdir /``` -> Display / directory listing of EFS +- ```qc_diag -vid 0x1234 -pid 0x5678 -interface 0 -info``` -> Send cmd "00" and return info +- ```qc_diag -vid 0x1234 -pid 0x5678 -interface 0 -spc 303030303030``` -> Send spc "303030303030" +- ```qc_diag -vid 0x1234 -pid 0x5678 -interface 0 -cmd 00``` -> Send cmd "00" (hexstring) +- ```qc_diag -vid 0x1234 -pid 0x5678 -interface 0 -nvread 0x55``` -> Display nvitem 0x55 +- ```qc_diag -vid 0x1234 -pid 0x5678 -interface 0 -nvbackup backup.json``` -> Backup all nvitems to a json structured file +- ```qc_diag -vid 0x1234 -pid 0x5678 -interface 0 -efsread efs.bin``` -> Dump the EFS Modem partition to file efs.bin +- ```qc_diag -vid 0x1234 -pid 0x5678 -interface 0 -efslistdir /``` -> Display / directory listing of EFS ## Issues @@ -212,7 +212,7 @@ For Oneplus 6T, enter *#801#* on dialpad, set Engineer Mode and Serial to on and ## Tested with -- Oneplus 3T/5/6T/7T/8/8t/N10/N100 (Read-Only), BQ X, BQ X5, BQ X2, Gigaset ME Pure, ZTE MF210, ZTE MF920V, Sierra Wireless EM7455, Netgear MR1100-10EUS, Netgear MR5100 +- Oneplus 3T/5/6T/7T/8/8t/9/Nord CE/N10/N100 (Read-Only), BQ X, BQ X5, BQ X2, Gigaset ME Pure, ZTE MF210, ZTE MF920V, Sierra Wireless EM7455, Netgear MR1100-10EUS, Netgear MR5100 Published under MIT license Additional license limitations: No use in commercial products without prior permit. diff --git a/edl b/edl new file mode 100755 index 0000000..859ca36 --- /dev/null +++ b/edl @@ -0,0 +1,362 @@ +#!/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] + edl [--loader=filename] [--memory=memtype] + edl [--debugmode] + edl [--gpt-num-part-entries=number] [--gpt-part-entry-size=number] [--gpt-part-entry-start-lba=number] + edl [--memory=memtype] [--skipstorageinit] [--maxpayload=bytes] [--sectorsize==bytes] + edl server [--tcpport=portnumber] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] + edl memorydump [--partitions=partnames] [--debugmode] [--vid=vid] [--pid=pid] + edl printgpt [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] + edl gpt [--memory=memtype] [--lun=lun] [--genxml] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] + edl r [--memory=memtype] [--sectorsize==bytes] [--lun=lun] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] + edl rl [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skip=partnames] [--genxml] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] + edl rf [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] + edl rs [--lun=lun] [--sectorsize==bytes] [--memory=memtype] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] + edl w [--partitionfilename=filename] [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skipwrite] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] + edl wl [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skip=partnames] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] + edl wf [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] + edl ws [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skipwrite] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] + edl e [--memory=memtype] [--skipwrite] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] + edl es [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skipwrite] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] + edl ep [--memory=memtype] [--skipwrite] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] + edl footer [--memory=memtype] [--lun=lun] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] + edl peek [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] + edl peekhex [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] + edl peekdword [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] + edl peekqword [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] + edl memtbl [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] + edl poke [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] + edl pokehex [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] + edl pokedword [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] + edl pokeqword [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] + edl memcpy [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] + edl secureboot [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] + edl pbl [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] + edl qfp [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] + edl getstorageinfo [--loader=filename] [--memory=memtype] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] + edl setbootablestoragedrive [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] + edl send [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] + edl xml [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] + edl rawxml [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] + edl reset [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] + edl nop [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] + edl modules [--memory=memtype] [--lun=lun] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] + edl qfil [--loader=filename] [--memory=memtype] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] + +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 + nop # Send firehose nop command + modules # Enable submodules, for example: "oemunlock enable" + qfil # Write rawprogram xml files + # : program config xml, such as rawprogram_unsparse.xml or rawprogram*.xml + # : patch config xml, such as patch0.xml or patch*.xml + # : directory name of image files + +Options: + --loader=filename Use specific EDL loader, disable autodetection [default: None] + --vid=vid Set usb vendor id used for EDL [default: -1] + --pid=pid Set usb product id used for EDL [default: -1] + --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 +""" + +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.usblib import UsbClass +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 binascii import hexlify + +args = docopt(__doc__, version='3') + +print("Qualcomm Sahara / Firehose Client V3.53 (c) B.Kerler 2018-2021.") + + +def parse_cmd(rargs): + cmds = ["server", "printgpt", "gpt", "r", "rl", "rf", "rs", "w", "wl", "wf", "ws", "e", "es", "ep", "footer", + "peek", "peekhex", + "peekdword", "peekqword", "memtbl", "poke", "pokehex", "pokedword", "pokeqword", "memcpy", "secureboot", + "pbl", + "qfp", "getstorageinfo", "setbootablestoragedrive", "send", "xml", "rawxml", "reset", "nop", "modules", + "memorydump", "qfil"] + for cmd in cmds: + if rargs[cmd]: + return cmd + return "" + + +def console_cmd(cmd): + read = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, close_fds=True) + output = read.stdout.read().decode() + return output + + +def parse_option(rargs): + options = {} + for arg in rargs: + if "--" in arg or "<" in arg: + options[arg] = rargs[arg] + return options + + +class main(metaclass=LogBase): + def __init__(self): + 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 + + def doconnect(self, loop, mode, resp): + while not self.cdc.connected: + self.cdc.connected = self.cdc.connect() + 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: + mode, resp = self.sahara.connect() + except Exception as err: # pylint: disable=broad-except + self.debug(str(err)) + if mode == "" or resp == -1: + mode, resp = self.sahara.connect() + if mode == -1: + mode, resp = self.sahara.connect() + if mode == "": + self.info("Unknown mode. Aborting.") + self.exit() + self.info(f"Mode detected: {mode}") + break + + return mode, resp + + def exit(self): + self.cdc.close() + sys.exit() + + def run(self): + if sys.platform == 'win32' or sys.platform == 'win64' or sys.platform == 'winnt': + 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) + + self.cdc = UsbClass(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 = 100 + mode, resp = self.doconnect(loop, mode, resp) + if resp == -1: + mode, resp = self.doconnect(loop, mode, resp) + if resp == -1: + self.error("USB desync, please rerun command !") + self.exit() + # print((mode, resp)) + if mode == "sahara": + if resp is None: + if mode == "sahara": + print("Sahara in error state, resetting ...") + self.sahara.cmd_reset() + data = self.cdc.read(5) + self.debug(hexlify(data).decode('utf-8')) + self.exit() + elif "mode" in resp: + mode = resp["mode"] + if mode == self.sahara.sahara_mode.SAHARA_MODE_MEMORY_DEBUG: + if args["memorydump"]: + 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: + 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() + else: + print("Device is in EDL mode .. continuing.") + self.cdc.timeout = None + sahara_info = self.sahara.cmd_info() + if sahara_info: + mode, resp = self.sahara.connect() + if mode == "sahara": + mode = self.sahara.upload_loader() + if mode == "firehose": + if "enprg" in self.sahara.programmer.lower(): + mode = "enandprg" + elif "nprg" in self.sahara.programmer.lower(): + mode = "nandprg" + if mode != "": + if mode != "firehose": + streaming = Streaming(self.cdc, self.sahara, self.__logger.level) + if streaming.connect(1): + print("Successfully uploaded programmer :)") + mode = "nandprg" + else: + print("Device is in an unknown state") + self.exit() + else: + print("Successfully uploaded programmer :)") + else: + print("No suitable loader found :(") + self.exit() + else: + print("Device is in an unknown sahara state, resetting") + print("resp={0}".format(resp)) + self.sahara.cmd_reset() + self.exit() + else: + print("Device is in an unknown state") + self.exit() + else: + self.sahara.bit64 = True + + if mode == "firehose": + self.cdc.timeout = None + fh = firehose_client(args, self.cdc, self.sahara, self.__logger.level, print) + cmd = parse_cmd(args) + options = parse_option(args) + if cmd != "": + fh.handle_firehose(cmd, options) + elif mode == "nandprg" or mode == "enandprg" or mode == "load_nandprg" or mode == "load_enandprg": + 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[""] = 1 + else: + options[""] = 0 + sc.handle_streaming(cmd, options) + else: + self.error("Sorry, couldn't talk to Sahara, please reboot the device !") + + self.exit() + + +if __name__ == '__main__': + base = main() + base.run() diff --git a/edl/Config/__init__.py b/edlclient/Config/__init__.py similarity index 100% rename from edl/Config/__init__.py rename to edlclient/Config/__init__.py diff --git a/edl/Config/nvitems.xml b/edlclient/Config/nvitems.xml similarity index 100% rename from edl/Config/nvitems.xml rename to edlclient/Config/nvitems.xml diff --git a/edl/Config/qualcomm_config.py b/edlclient/Config/qualcomm_config.py similarity index 100% rename from edl/Config/qualcomm_config.py rename to edlclient/Config/qualcomm_config.py diff --git a/edl/Config/usb_ids.py b/edlclient/Config/usb_ids.py similarity index 100% rename from edl/Config/usb_ids.py rename to edlclient/Config/usb_ids.py diff --git a/edl/Library/Modules/__init__.py b/edlclient/Library/Modules/__init__.py similarity index 100% rename from edl/Library/Modules/__init__.py rename to edlclient/Library/Modules/__init__.py diff --git a/edl/Library/Modules/generic.py b/edlclient/Library/Modules/generic.py similarity index 99% rename from edl/Library/Modules/generic.py rename to edlclient/Library/Modules/generic.py index f311d1c..c22618f 100644 --- a/edl/Library/Modules/generic.py +++ b/edlclient/Library/Modules/generic.py @@ -3,7 +3,7 @@ # (c) B.Kerler 2018-2021 import logging -from edl.Library.utils import LogBase +from edlclient.Library.utils import LogBase class generic(metaclass=LogBase): diff --git a/edl/Library/Modules/init.py b/edlclient/Library/Modules/init.py similarity index 92% rename from edl/Library/Modules/init.py rename to edlclient/Library/Modules/init.py index 55e2517..27964b2 100644 --- a/edl/Library/Modules/init.py +++ b/edlclient/Library/Modules/init.py @@ -3,22 +3,22 @@ # (c) B.Kerler 2018-2021 import logging -from edl.Library.utils import LogBase +from edlclient.Library.utils import LogBase try: - from edl.Library.Modules.generic import generic + from edlclient.Library.Modules.generic import generic except ImportError as e: generic = None pass try: - from edl.Library.Modules.oneplus import oneplus + from edlclient.Library.Modules.oneplus import oneplus except ImportError as e: oneplus = None pass try: - from edl.Library.Modules.xiaomi import xiaomi + from edlclient.Library.Modules.xiaomi import xiaomi except ImportError as e: xiaomi = None pass diff --git a/edl/Library/Modules/oneplus.py b/edlclient/Library/Modules/oneplus.py similarity index 99% rename from edl/Library/Modules/oneplus.py rename to edlclient/Library/Modules/oneplus.py index 31f9248..f28721a 100755 --- a/edl/Library/Modules/oneplus.py +++ b/edlclient/Library/Modules/oneplus.py @@ -19,10 +19,10 @@ import time import random from struct import pack import logging -from edl.Library.utils import LogBase +from edlclient.Library.utils import LogBase try: - from edl.Library.cryptutils import cryptutils + from edlclient.Library.cryptutils import cryptutils except Exception as e: print(e) from ..cryptutils import cryptutils @@ -75,6 +75,7 @@ deviceconfig = { # OP Nord, avicii "20801": dict(version=2, cm="eacf50e7", param_mode=0), + # OP N10 5G Metro, billie8t "20885": dict(version=3, cm="3a403a71", param_mode=1), # OP N10 5G Global, billie8 @@ -153,7 +154,7 @@ class oneplus(metaclass=LogBase): logfilename = "log.txt" filehandler = logging.FileHandler(logfilename) self.__logger.addHandler(filehandler) - self.ops_parm = None + self.ops_parm = None self.ops = self.convert_projid(fh, projid, serial) def getprodkey(self, projid): diff --git a/edl/Library/Modules/xiaomi.py b/edlclient/Library/Modules/xiaomi.py similarity index 98% rename from edl/Library/Modules/xiaomi.py rename to edlclient/Library/Modules/xiaomi.py index 7167ce4..6602d8a 100644 --- a/edl/Library/Modules/xiaomi.py +++ b/edlclient/Library/Modules/xiaomi.py @@ -3,7 +3,7 @@ # (c) B.Kerler 2018-2021 import logging -from edl.Library.utils import LogBase +from edlclient.Library.utils import LogBase class xiaomi(metaclass=LogBase): diff --git a/edl/Library/TestFiles/gpt_sm8180x.bin b/edlclient/Library/TestFiles/gpt_sm8180x.bin similarity index 100% rename from edl/Library/TestFiles/gpt_sm8180x.bin rename to edlclient/Library/TestFiles/gpt_sm8180x.bin diff --git a/edl/Library/__init__.py b/edlclient/Library/__init__.py similarity index 100% rename from edl/Library/__init__.py rename to edlclient/Library/__init__.py diff --git a/edl/Library/asmtools.py b/edlclient/Library/asmtools.py similarity index 100% rename from edl/Library/asmtools.py rename to edlclient/Library/asmtools.py diff --git a/edl/Library/cryptutils.py b/edlclient/Library/cryptutils.py similarity index 100% rename from edl/Library/cryptutils.py rename to edlclient/Library/cryptutils.py diff --git a/edl/Library/firehose.py b/edlclient/Library/firehose.py similarity index 98% rename from edl/Library/firehose.py rename to edlclient/Library/firehose.py index fcccb9e..0402692 100755 --- a/edl/Library/firehose.py +++ b/edlclient/Library/firehose.py @@ -11,12 +11,12 @@ from struct import unpack from binascii import hexlify from queue import Queue from threading import Thread -from edl.Library.utils import * -from edl.Library.gpt import gpt -from edl.Library.sparse import QCSparse +from edlclient.Library.utils import * +from edlclient.Library.gpt import gpt +from edlclient.Library.sparse import QCSparse try: - from edl.Library.Modules.init import modules + from edlclient.Library.Modules.init import modules except ImportError as e: pass @@ -174,23 +174,17 @@ class firehose(metaclass=LogBase): tdiff=t0-self.progtime datasize=(pos-self.progpos)/1024/1024 throughput=(((datasize)/(tdiff))) - max_pos_len = len(str(total // self.cfg.SECTOR_SIZE_IN_BYTES)) - print_progress(prog, 100, - prefix='Progress:', - suffix=prefix+' (Sector {} of {}) {:0.2f} MB/s'.format( - '{0:>{1}d}'.format(pos // self.cfg.SECTOR_SIZE_IN_BYTES, max_pos_len), - total // self.cfg.SECTOR_SIZE_IN_BYTES, - throughput), - bar_length=50) + print_progress(prog, 100, prefix='Progress:', + suffix=prefix+' (Sector %d of %d) %0.2f MB/s' % + (pos // self.cfg.SECTOR_SIZE_IN_BYTES, + total // self.cfg.SECTOR_SIZE_IN_BYTES, + throughput), bar_length=50) self.prog = prog self.progpos = pos self.progtime = t0 else: if display: - print_progress(100, 100, - prefix='Progress:', - suffix=prefix+' Complete (Sector {0} of {0})'.format(total // self.cfg.SECTOR_SIZE_IN_BYTES), - bar_length=50) + print_progress(100, 100, prefix='Progress:', suffix='Complete', bar_length=50) except: pass diff --git a/edl/Library/firehose_client.py b/edlclient/Library/firehose_client.py similarity index 99% rename from edl/Library/firehose_client.py rename to edlclient/Library/firehose_client.py index 40ecdb8..4c19ea9 100644 --- a/edl/Library/firehose_client.py +++ b/edlclient/Library/firehose_client.py @@ -8,12 +8,12 @@ import logging import json from binascii import hexlify, unhexlify from struct import unpack, pack -from edl.Library.firehose import firehose -from edl.Library.xmlparser import xmlparser -from edl.Library.utils import do_tcp_server -from edl.Library.utils import LogBase, getint -from edl.Config.qualcomm_config import memory_type -from edl.Config.qualcomm_config import infotbl, msmids, secureboottbl, sochw +from edlclient.Library.firehose import firehose +from edlclient.Library.xmlparser import xmlparser +from edlclient.Library.utils import do_tcp_server +from edlclient.Library.utils import LogBase, getint +from edlclient.Config.qualcomm_config import memory_type +from edlclient.Config.qualcomm_config import infotbl, msmids, secureboottbl, sochw try: import xml.etree.cElementTree as ET @@ -23,7 +23,7 @@ except ImportError: from xml.etree import ElementTree try: - from edl.Library.Modules.init import modules + from edlclient.Library.Modules.init import modules except ImportError as e: pass diff --git a/edl/Library/gpt.py b/edlclient/Library/gpt.py similarity index 99% rename from edl/Library/gpt.py rename to edlclient/Library/gpt.py index 14baff4..aa1afc1 100755 --- a/edl/Library/gpt.py +++ b/edlclient/Library/gpt.py @@ -10,7 +10,7 @@ from struct import unpack, pack from binascii import hexlify try: - from edl.Library.utils import LogBase, structhelper + from edlclient.Library.utils import LogBase, structhelper except: from utils import LogBase, structhelper diff --git a/edl/Library/hdlc.py b/edlclient/Library/hdlc.py similarity index 100% rename from edl/Library/hdlc.py rename to edlclient/Library/hdlc.py diff --git a/edl/Library/memparse.py b/edlclient/Library/memparse.py similarity index 100% rename from edl/Library/memparse.py rename to edlclient/Library/memparse.py diff --git a/edl/Library/nand_config.py b/edlclient/Library/nand_config.py similarity index 99% rename from edl/Library/nand_config.py rename to edlclient/Library/nand_config.py index f4630e2..992a426 100644 --- a/edl/Library/nand_config.py +++ b/edlclient/Library/nand_config.py @@ -3,7 +3,7 @@ # (c) B.Kerler 2018-2019 import ctypes from enum import Enum -from edl.Config.qualcomm_config import secgen, secureboottbl +from edlclient.Config.qualcomm_config import secgen, secureboottbl c_uint8 = ctypes.c_uint8 diff --git a/edl/Library/pt.py b/edlclient/Library/pt.py similarity index 100% rename from edl/Library/pt.py rename to edlclient/Library/pt.py diff --git a/edl/Library/pt64.py b/edlclient/Library/pt64.py similarity index 100% rename from edl/Library/pt64.py rename to edlclient/Library/pt64.py diff --git a/edl/Library/sahara.py b/edlclient/Library/sahara.py similarity index 99% rename from edl/Library/sahara.py rename to edlclient/Library/sahara.py index 373ee0c..71a2f6e 100755 --- a/edl/Library/sahara.py +++ b/edlclient/Library/sahara.py @@ -11,8 +11,8 @@ from struct import unpack, pack current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) parent_dir = os.path.dirname(current_dir) sys.path.insert(0, parent_dir) -from edl.Library.utils import read_object, print_progress, rmrf, LogBase -from edl.Config.qualcomm_config import sochw, msmids, root_cert_hash +from edlclient.Library.utils import read_object, print_progress, rmrf, LogBase +from edlclient.Config.qualcomm_config import sochw, msmids, root_cert_hash diff --git a/edl/Library/sparse.py b/edlclient/Library/sparse.py similarity index 99% rename from edl/Library/sparse.py rename to edlclient/Library/sparse.py index 7169784..99ec966 100755 --- a/edl/Library/sparse.py +++ b/edlclient/Library/sparse.py @@ -11,7 +11,7 @@ from struct import unpack current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) parent_dir = os.path.dirname(current_dir) sys.path.insert(0, parent_dir) -from edl.Library.utils import LogBase, print_progress +from edlclient.Library.utils import LogBase, print_progress class QCSparse(metaclass=LogBase): diff --git a/edl/Library/streaming.py b/edlclient/Library/streaming.py similarity index 99% rename from edl/Library/streaming.py rename to edlclient/Library/streaming.py index ced161c..d0a1841 100755 --- a/edl/Library/streaming.py +++ b/edlclient/Library/streaming.py @@ -3,9 +3,9 @@ # (c) B.Kerler 2018-2021 from struct import pack from binascii import unhexlify -from edl.Library.utils import * -from edl.Library.hdlc import * -from edl.Library.nand_config import BadFlags, SettingsOpt, nandregs, NandDevice +from edlclient.Library.utils import * +from edlclient.Library.hdlc import * +from edlclient.Library.nand_config import BadFlags, SettingsOpt, nandregs, NandDevice class Streaming(metaclass=LogBase): diff --git a/edl/Library/streaming_client.py b/edlclient/Library/streaming_client.py similarity index 99% rename from edl/Library/streaming_client.py rename to edlclient/Library/streaming_client.py index e7412cd..28d6a13 100644 --- a/edl/Library/streaming_client.py +++ b/edlclient/Library/streaming_client.py @@ -6,8 +6,8 @@ import os import logging from binascii import hexlify, unhexlify from struct import unpack, pack -from edl.Library.streaming import Streaming -from edl.Library.utils import do_tcp_server, LogBase, getint +from edlclient.Library.streaming import Streaming +from edlclient.Library.utils import do_tcp_server, LogBase, getint class streaming_client(metaclass=LogBase): diff --git a/edl/Library/usblib.py b/edlclient/Library/usblib.py similarity index 90% rename from edl/Library/usblib.py rename to edlclient/Library/usblib.py index c444f9b..82120fd 100755 --- a/edl/Library/usblib.py +++ b/edlclient/Library/usblib.py @@ -14,7 +14,7 @@ import usb.backend.libusb1 from enum import Enum from binascii import hexlify from ctypes import c_void_p, c_int -from edl.Library.utils import * +from edlclient.Library.utils import * USB_DIR_OUT = 0 # to device USB_DIR_IN = 0x80 # to host @@ -102,7 +102,10 @@ class UsbClass(metaclass=LogBase): if sys.platform.startswith('freebsd') or sys.platform.startswith('linux'): self.backend = usb.backend.libusb1.get_backend(find_library=lambda x: "libusb-1.0.so") elif sys.platform.startswith('win32'): - self.backend = usb.backend.libusb1.get_backend(find_library=lambda x: "libusb-1.0.dll") + if calcsize("P") * 8 == 64: + self.backend = usb.backend.libusb1.get_backend(find_library=lambda x: "libusb-1.0.dll") + else: + self.backend = usb.backend.libusb1.get_backend(find_library=lambda x: "libusb32-1.0.dll") if self.backend is not None: try: self.backend.lib.libusb_set_option.argtypes = [c_void_p, c_int] @@ -259,30 +262,26 @@ class UsbClass(metaclass=LogBase): if self.device is None: self.debug("Couldn't detect the device. Is it connected ?") return False - + # try: + # self.device.set_configuration() + # except: + # pass try: - if self.device is not None: - self.configuration = self.device.get_active_configuration() + self.configuration = self.device.get_active_configuration() except usb.core.USBError as e: - if e.strerror == "Configuration not set": - self.device.set_configuration() - self.configuration = self.device.get_active_configuration() - if e.errno == 13: - self.backend = usb.backend.libusb0.get_backend() - self.device = usb.core.find(idVendor=vid, idProduct=pid, backend=self.backend) - + if e.strerror == "Configuration not set": + self.device.set_configuration() + self.configuration = self.device.get_active_configuration() + if e.errno == 13: + self.backend = usb.backend.libusb0.get_backend() + self.device = usb.core.find(idVendor=self.vid, idProduct=self.pid, backend=self.backend) + if self.configuration is None: + self.error("Couldn't get device configuration.") + return False if self.interface == -1: for interfacenum in range(0, self.configuration.bNumInterfaces): itf = usb.util.find_descriptor(self.configuration, bInterfaceNumber=interfacenum) - try: - if self.device.is_kernel_driver_active(interfacenum): - self.debug("Detaching kernel driver") - self.device.detach_kernel_driver(interfacenum) - except Exception as err: - self.debug("No kernel driver supported: " + str(err)) - - usb.util.claim_interface(self.device, interfacenum) if self.devclass != -1: if itf.bInterfaceClass == self.devclass: # MassStorage self.interface = interfacenum @@ -295,20 +294,46 @@ class UsbClass(metaclass=LogBase): if self.interface > self.configuration.bNumInterfaces: print("Invalid interface, max number is %d" % self.configuration.bNumInterfaces) return False + if self.interface != -1: itf = usb.util.find_descriptor(self.configuration, bInterfaceNumber=self.interface) + try: + if self.device.is_kernel_driver_active(0): + self.debug("Detaching kernel driver") + self.device.detach_kernel_driver(0) + except Exception as err: + self.debug("No kernel driver supported: " + str(err)) + try: + usb.util.claim_interface(self.device, 0) + except: + pass + + try: + if self.device.is_kernel_driver_active(self.interface): + self.debug("Detaching kernel driver") + self.device.detach_kernel_driver(self.interface) + except Exception as err: + self.debug("No kernel driver supported: " + str(err)) + try: + if self.interface != 0: + usb.util.claim_interface(self.device, self.interface) + except: + pass + if EP_OUT == -1: - # match the first OUT endpoint self.EP_OUT = usb.util.find_descriptor(itf, - custom_match=lambda em: usb.util.endpoint_direction( - em.bEndpointAddress) == usb.util.ENDPOINT_OUT) + # match the first OUT endpoint + custom_match=lambda e: \ + usb.util.endpoint_direction(e.bEndpointAddress) == + usb.util.ENDPOINT_OUT) else: self.EP_OUT = EP_OUT if EP_IN == -1: - # match the first OUT endpoint self.EP_IN = usb.util.find_descriptor(itf, - custom_match=lambda em: usb.util.endpoint_direction( - em.bEndpointAddress) == usb.util.ENDPOINT_IN) + # match the first OUT endpoint + custom_match=lambda e: \ + usb.util.endpoint_direction(e.bEndpointAddress) == + usb.util.ENDPOINT_IN) else: self.EP_IN = EP_IN @@ -321,17 +346,18 @@ class UsbClass(metaclass=LogBase): def close(self, reset=False): if self.connected: - usb.util.dispose_resources(self.device) try: + if reset: + self.device.reset() if not self.device.is_kernel_driver_active(self.interface): # self.device.attach_kernel_driver(self.interface) #Do NOT uncomment self.device.attach_kernel_driver(0) - if reset: - self.device.reset() - except Exception as e: # pylint: disable=broad-except - self.debug(str(e)) + except Exception as err: + self.debug(str(err)) pass + usb.util.dispose_resources(self.device) del self.device + self.connected = False def write(self, command): pktsize=self.EP_OUT.wMaxPacketSize diff --git a/edl/Library/usbscsi.py b/edlclient/Library/usbscsi.py similarity index 98% rename from edl/Library/usbscsi.py rename to edlclient/Library/usbscsi.py index d550f75..6b1381d 100755 --- a/edl/Library/usbscsi.py +++ b/edlclient/Library/usbscsi.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # (c) B.Kerler 2018-2021 import argparse -from edl.Library.usblib import * +from edlclient.Library.usblib import * def main(): diff --git a/edl/Library/utils.py b/edlclient/Library/utils.py similarity index 100% rename from edl/Library/utils.py rename to edlclient/Library/utils.py diff --git a/edl/Library/xmlparser.py b/edlclient/Library/xmlparser.py similarity index 100% rename from edl/Library/xmlparser.py rename to edlclient/Library/xmlparser.py diff --git a/edl/Windows/__init__.py b/edlclient/Tools/__init__.py similarity index 100% rename from edl/Windows/__init__.py rename to edlclient/Tools/__init__.py diff --git a/edlclient/Tools/beagle_to_loader b/edlclient/Tools/beagle_to_loader new file mode 100755 index 0000000..8e48045 --- /dev/null +++ b/edlclient/Tools/beagle_to_loader @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# Beagle to EDL Loader (c) B.Kerler 2021 + +import os,sys +from struct import unpack + +def main(): + if len(sys.argv)<2: + print("Usage: ./beagle_to_loader.py [beagle_log.bin] [loader.elf]") + sys.exit(0) + with open(sys.argv[1],"rb") as rf: + data=rf.read() + outdata=bytearray() + i=0 + seq=b"\x03\x00\x00\x00\x14\x00\x00\x00\x0D\x00\x00\x00" + with open(sys.argv[2], "wb") as wf: + while True: + idx=data.find(seq) + if idx==-1: + if i==0: + seq=b"\x12\x00\x00\x00\x20\x00\x00\x00\x0D\x00\x00\x00\x00\x00\x00\x00" + i+=1 + continue + else: + break + else: + cmd=unpack("1: + data["model"]=info[1] + return data + +class adbtools(metaclass=LogBase): + def sendcmd(self, tn,cmd): + tn.write(bytes(cmd,'utf-8')+b"\n") + time.sleep(0.05) + return tn.read_eager().strip().decode('utf-8') + + def run(self, args): + port = args.port + cn = connection(port) + if cn.connected: + info=cn.ati() + if "vendor" in info: + if info["vendor"]=="Sierra Wireless" or info["vendor"]=="Netgear": + print("Sending at switch command") + kg=SierraKeygen(cn) + if kg.openlock(): + if cn.send('AT!CUSTOM="ADBENABLE",1\r')==-1: + print("Error on sending adb enable command.") + kg.openlock() + if cn.send('AT!CUSTOM="TELNETENABLE",1\r')!=-1: + time.sleep(5) + tn = Telnet("192.168.1.1", 23, 15) + tn.write(b"adbd &\r\n") + info = tn.read_eager() + print(info) + print("Enabled adb via telnet") + else: + print("Error on sending telnet enable command.") + if kg.openlock(): + if info["vendor"] == "Netgear": + print("Enabling new port config") + if cn.send("AT!UDPID=68E2"): + print("Successfully enabled PID 68E2") + elif info["vendor"]=="Quectel": + print("Sending at switch command") + salt=cn.send("AT+QADBKEY?\r") + if salt!=-1: + if len(salt)>1: + salt=salt[1] + code = crypt.crypt("SH_adb_quectel", "$1$" + salt) + code = code[12:] + cn.send("AT+QADBKEY=\"%s\"\r" % code) + if cn.send("AT+QCFG=\"usbcfg\",0x2C7C,0x125,1,1,1,1,1,1,0\r")==-1: + if cn.send("AT+QLINUXCMD=\"adbd\"")!=-1: #echo test > /dev/ttyGS0 + print("Success enabling adb") + else: + print("Success enabling adb") + print("In order to disable adb, send AT+QCFG=\"usbcfg\",0x2C7C,0x125,1,1,1,1,1,0,0") + elif info["vendor"]=="ZTE": + print("Sending switch command via diag") + if cn.send("AT+ZMODE=1")!=-1: + print("Success enabling adb") + else: + interface = 0 + diag = qcdiag(loglevel=self.__logger.level, portconfig=[[0x19d2, 0x0016, interface]]) + if diag.connect(): + res = diag.send(b"\x4B\xA3\x06\x00") + if res[0]==0x4B: + challenge=res[4:4+8] + response=hashlib.md5(challenge).digest() + res = diag.send(b"\x4B\xA3\x07\x00"+response) + if res[0]==0x4B: + if res[3]==0x00: + print("Auth success") + res=diag.send(b"\x41" + b"\x30\x30\x30\x30\x30\x30") + if res[1]==0x01: + print("SPC success") + sp=b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE" + res = diag.send(b"\x46" + sp) + if res[0] == 0x46 and res[1]==0x01: + print("SP success") + else: + res = diag.send(b"\x25" + sp) + if res[0]==0x46 and res[1]==0x01: + print("SP success") + res = diag.send(b"\x4B\xFA\x0B\x00\x01") #Enable adb serial + if res[0]!=0x13: + print("Success enabling adb serial") + res = diag.send(b"\x4B\x5D\x05\x00") #Operate ADB + if res[0]!=0x13: + print("Success enabling adb") + diag.disconnect() + elif info["vendor"]=="Simcom": + print("Sending at switch command") + # Simcom7600 + if cn.send("AT+CUSBADB=1,1")!=-1: + print("Success enabling adb") + elif info["vendor"]=="Fibocom": + print("Sending at switch command") + # FibocomL718: + if cn.send("AT+ADBDEBUG=1")!=-1: + print("Success enabling adb") + elif info["vendor"]=="Alcatel": + print("Send scsi switch command") + print("Run \"sudo sg_raw /dev/sg0 16 f9 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -v\" to enable adb") + elif info["vendor"]=="Samsung": + if cn.send("AT+USBMODEM=1"): + print("Success enabling adb") + elif cn.send("AT+SYSSCOPE=1,0,0"): + print("Success enabling adb") + + cn.close() + +def main(): + version = "1.1" + info = 'Modem Gimme-ADB ' + version + ' (c) B. Kerler 2020-2021' + parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description=info) + parser.add_argument( + '-port', '-p', + help='use com port for at', + default="") + parser.add_argument( + '-logfile', '-l', + help='use logfile for debug log', + default="") + args = parser.parse_args() + ad=adbtools() + ad.run(args) + +if __name__=="__main__": + main() + diff --git a/edlclient/Tools/fhloaderparse b/edlclient/Tools/fhloaderparse new file mode 100755 index 0000000..92b728f --- /dev/null +++ b/edlclient/Tools/fhloaderparse @@ -0,0 +1,411 @@ +#!/usr/bin/env python3 +import os +import sys +from os import walk +import hashlib +from struct import unpack, pack +from shutil import copyfile +import os, sys, inspect +current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +parent_dir = os.path.dirname(current_dir) +sys.path.insert(0, parent_dir) +from edlclient.Library.utils import elf +from edlclient.Library.sahara import convertmsmid +from edlclient.Config.qualcomm_config import vendor + +class MBN: + def __init__(self, memory): + self.imageid, self.flashpartitionversion, self.imagesrc, self.loadaddr, self.imagesz, self.codesz, \ + self.sigptr, self.sigsz, self.certptr, self.certsz = unpack(" 0: + if data[j] == 0: + break + text += chr(data[j]) + j += 1 + i -= 1 + return text + + +def extract_hdr(memsection, sign_info, mem_section, code_size, signature_size): + try: + md_size = \ + unpack("> 0) & 1 + in_use_soc_hw_version=(v >> 1) & 1 + use_serial_number_in_signing=(v >> 2) & 1 + oem_id_independent=(v >> 3) & 1 + root_revoke_activate_enable=(v >> 4) & 0b11 + uie_key_switch_enable=(v >> 6) & 0b11 + debug=(v >> 8) & 0b11 + md_offset+=4 + soc_vers=hexlify(mm[md_offset:md_offset + (12*4)]) + md_offset+=12*4 + multi_serial_numbers=hexlify(mm[md_offset:md_offset + (8*4)]) + md_offset += 8 * 4 + mrc_index=unpack("H", mem_section[signatureoffset + 2:signatureoffset + 4])[0] + 4 + casignature2offset = signatureoffset + len1 + len2 = unpack(">H", mem_section[casignature2offset + 2:casignature2offset + 4])[0] + 4 + rootsignature3offset = casignature2offset + len2 + len3 = unpack(">H", mem_section[rootsignature3offset + 2:rootsignature3offset + 4])[0] + 4 + sign_info.pk_hash = hashlib.sha384(mem_section[rootsignature3offset:rootsignature3offset + len3]).hexdigest() + except: + return None + return sign_info + + +def extract_old_hdr(signatureoffset, sign_info, mem_section, code_size, signature_size): + signature = {} + if mem_section[signatureoffset] != 0x30: + print("Error on " + sign_info.filename + ", unknown signaturelength") + return None + if signatureoffset != -1: + if len(mem_section) < signatureoffset + 4: + print("Signature error on " + sign_info.filename) + return None + len1 = unpack(">H", mem_section[signatureoffset + 2:signatureoffset + 4])[0] + 4 + casignature2offset = signatureoffset + len1 + len2 = unpack(">H", mem_section[casignature2offset + 2:casignature2offset + 4])[0] + 4 + rootsignature3offset = casignature2offset + len2 + len3 = unpack(">H", mem_section[rootsignature3offset + 2:rootsignature3offset + 4])[0] + 4 + sign_info.pk_hash = hashlib.sha256(mem_section[rootsignature3offset:rootsignature3offset + len3]).hexdigest() + idx = signatureoffset + + while idx != -1: + if idx >= len(mem_section): + break + idx = mem_section.find('\x04\x0B'.encode(), idx) + if idx == -1: + break + length = mem_section[idx + 3] + if length > 60: + idx += 1 + continue + try: + text = mem_section[idx + 4:idx + 4 + length].decode().split(' ') + signature[text[2]] = text[1] + except: + text = "" + idx += 1 + idx = mem_section.find('QC_IMAGE_VERSION_STRING='.encode(), 0) + if idx != -1: + sign_info.qc_version = grabtext(mem_section[idx + len("QC_IMAGE_VERSION_STRING="):]) + idx = mem_section.find('OEM_IMAGE_VERSION_STRING='.encode(), 0) + if idx != -1: + sign_info.oem_version = grabtext(mem_section[idx + len("OEM_IMAGE_VERSION_STRING="):]) + idx = mem_section.find('IMAGE_VARIANT_STRING='.encode(), 0) + if idx != -1: + sign_info.image_variant = grabtext(mem_section[idx + len("IMAGE_VARIANT_STRING="):]) + if "MODEL_ID" in signature: + sign_info.model_id = signature["MODEL_ID"] + if "OEM_ID" in signature: + sign_info.oem_id = signature["OEM_ID"] + if "HW_ID" in signature: + sign_info.hw_id = signature["HW_ID"] + if "SW_ID" in signature: + sign_info.sw_id = signature["SW_ID"] + if "SW_SIZE" in signature: + sign_info.sw_size = signature["SW_SIZE"] + return sign_info + + +def init_loader_db(): + loaderdb = {} + for (dirpath, dirnames, filenames) in os.walk(current_dir): + for filename in filenames: + file_name = os.path.join(dirpath, filename) + found = False + for ext in [".bin", ".mbn", ".elf", ""]: + if ext in filename[-4:]: + found = True + break + if not found: + continue + try: + hwid = filename.split("_")[0].lower() + msmid = hwid[:8] + devid = hwid[8:] + pkhash = filename.split("_")[1].lower() + msmdb = convertmsmid(msmid) + for msmid in msmdb: + mhwid = (msmid + devid).lower() + if mhwid not in loaderdb: + loaderdb[mhwid] = {} + if pkhash not in loaderdb[mhwid]: + loaderdb[mhwid][pkhash] = file_name + else: + loaderdb[mhwid][pkhash].append(file_name) + except: + continue + return loaderdb + + +def is_duplicate(loaderdb, sign_info): + lhash = sign_info.pk_hash[:16].lower() + msmid = sign_info.hw_id[:8].lower() + devid = sign_info.hw_id[8:].lower() + hwid = sign_info.hw_id.lower() + for msmid in convertmsmid(msmid): + rid = (msmid + devid).lower() + if hwid in loaderdb: + loader = loaderdb[hwid] + if lhash in loader: + return True + if rid in loaderdb: + loader = loaderdb[rid] + if lhash in loader: + return True + return False + + +def main(argv): + file_list = [] + path = "" + if len(argv) < 3: + print("Usage: fhloaderparse [FHLoaderDir] [OutputDir]") + exit(0) + else: + path = argv[1] + outputdir = argv[2] + if not os.path.exists(outputdir): + os.mkdir(outputdir) + + # First hash all loaders in Loader directory + hashes = {} + loaderdb = init_loader_db() + for mhwid in loaderdb: + for pkhash in loaderdb[mhwid]: + fname = loaderdb[mhwid][pkhash] + with open(fname, 'rb') as rhandle: + data = rhandle.read() + sha256 = hashlib.sha256() + sha256.update(data) + hashes[sha256.digest()] = fname + + # Now lets hash all files in the output directory + for (dirpath, dirnames, filenames) in walk(outputdir): + for filename in filenames: + fname = os.path.join(dirpath, filename) + with open(fname, 'rb') as rhandle: + data = rhandle.read() + sha256 = hashlib.sha256() + sha256.update(data) + hashes[sha256.digest()] = fname + + # Now lets search the input path for loaders + extensions = ["txt", "idb", "i64", "py"] + for (dirpath, dirnames, filenames) in walk(path): + for filename in filenames: + basename = os.path.basename(filename).lower() + ext = basename[basename.rfind(".") + 1:] + if ext not in extensions: + file_list.append(os.path.join(dirpath, filename)) + + if not os.path.exists(os.path.join(outputdir, "Unknown")): + os.makedirs(os.path.join(outputdir, "Unknown")) + if not os.path.exists(os.path.join(outputdir, "Duplicate")): + os.mkdir(os.path.join(outputdir, "Duplicate")) + + # Lets hash all the input files and extract the signature + filelist = [] + rt = open(os.path.join(outputdir, argv[1] + ".log"), "w") + for filename in file_list: + with open(filename, 'rb') as rhandle: + mem_section = rhandle.read() + sha256 = hashlib.sha256() + sha256.update(mem_section) + + signinfo = Signed() + signinfo.hash = sha256.digest() + signinfo.filename = filename + signinfo.filesize = os.stat(filename).st_size + if len(mem_section) < 4: + continue + hdr = unpack("= 6: # SDM + signinfo = extract_hdr(memsection, signinfo, mem_section, code_size, signature_size) + if signinfo is None: + continue + filelist.append(signinfo) + else: + print("Unknown version for " + filename) + continue + elif hdr == 0x844BDCD1: + mbn = MBN(mem_section) + if mbn.sigsz == 0: + print("%s has no signature." % filename) + copyfile(filename, os.path.join(outputdir, "Unknown", filename[filename.rfind("/") + 1:].lower())) + continue + signatureoffset = mbn.imagesrc + mbn.codesz + mbn.sigsz + signinfo = extract_old_hdr(signatureoffset, signinfo, mem_section, mbn.codesz, mbn.sigsz) + if signinfo is None: + continue + filelist.append(signinfo) + else: + print("Error on " + filename) + continue + + sorted_x = sorted(filelist, key=lambda x: (x.hw_id, -x.filesize)) + + class loaderinfo: + hw_id = '' + item = '' + + loaderlists = {} + for item in sorted_x: + if item.oem_id != '': + oemid=int(item.oem_id,16) + if oemid in vendor: + oeminfo = vendor[oemid] + else: + oeminfo=item.oem_id + if len(item.sw_id)<16: + item.sw_id="0"*(16-len(item.sw_id))+item.sw_id + info = f"OEM:{oeminfo}\tMODEL:{item.model_id}\tHWID:{item.hw_id}\tSWID:{item.sw_id}\tSWSIZE:{item.sw_size}\tPK_HASH:{item.pk_hash}\t{item.filename}\t{str(item.filesize)}" + if item.oem_version != '': + info += "\tOEMVER:" + item.oem_version + "\tQCVER:" + item.qc_version + "\tVAR:" + item.image_variant + loader_info = loaderinfo() + loader_info.hw_id = item.hw_id + loader_info.pk_hash = item.pk_hash + if item.hash not in hashes: + if loader_info not in loaderlists: + if not is_duplicate(loaderdb, item): + loaderlists[loader_info] = item.filename + print(info) + msmid = loader_info.hw_id[:8] + devid = loader_info.hw_id[8:] + for msmid in convertmsmid(msmid): + hwid = (msmid + devid).lower() + auth = "" + with open(item.filename, "rb") as rf: + data = rf.read() + if b"sig tag can" in data: + auth = "_EDLAuth" + if b"peek\x00" in data: + auth += "_peek" + fna = os.path.join(outputdir, ( + hwid + "_" + loader_info.pk_hash[0:16] + "_FHPRG" + auth + ".bin").lower()) + if not os.path.exists(fna): + copyfile(item.filename, + os.path.join(outputdir, hwid + "_" + ( + loader_info.pk_hash[0:16] + "_FHPRG" + auth + ".bin").lower())) + elif item.filesize > os.stat(fna).st_size: + copyfile(item.filename, os.path.join(outputdir, + (hwid + "_" + loader_info.pk_hash[ + 0:16] + "_FHPRG" + auth + ".bin").lower())) + else: + print("Duplicate: " + info) + copyfile(item.filename, os.path.join(outputdir, "Duplicate", + (loader_info.hw_id + "_" + loader_info.pk_hash[ + 0:16] + "_FHPRG.bin").lower())) + else: + copyfile(item.filename, os.path.join(outputdir, "Unknown", os.path.basename(item.filename).lower())) + else: + copyfile(item.filename, + os.path.join(outputdir, "Duplicate", + (loader_info.hw_id + "_" + loader_info.pk_hash[0:16] + "_FHPRG.bin").lower())) + print(item.filename + " does already exist. Skipping") + try: + rt.write(info + "\n") + except: + continue + else: + print("Unknown :"+item.filename) + copyfile(item.filename, os.path.join(outputdir, "Unknown", os.path.basename(item.filename).lower())) + + for item in filelist: + if item.oem_id == '' and (".bin" in item.filename or ".mbn" in item.filename or ".hex" in item.filename): + info = "Unsigned:" + item.filename + "\t" + str(item.filesize) + if item.oem_version != '': + info += "\tOEMVER:" + item.oem_version + "\tQCVER:" + item.qc_version + "\tVAR:" + item.image_variant + print(info) + rt.write(info + "\n") + if not os.path.exists(os.path.join(outputdir, "Unknown", item.filename)): + copyfile(item.filename, + os.path.join(outputdir, "Unknown", os.path.basename(item.filename).lower())) + + rt.close() + + +main(sys.argv) diff --git a/edlclient/Tools/qc_diag b/edlclient/Tools/qc_diag new file mode 120000 index 0000000..267a959 --- /dev/null +++ b/edlclient/Tools/qc_diag @@ -0,0 +1 @@ +qc_diag.py \ No newline at end of file diff --git a/edlclient/Tools/qc_diag.py b/edlclient/Tools/qc_diag.py new file mode 100755 index 0000000..85bfc40 --- /dev/null +++ b/edlclient/Tools/qc_diag.py @@ -0,0 +1,1329 @@ +#!/usr/bin/env python3 +""" +Licensed under MIT License, (c) B. Kerler 2018-2021 +""" + +import sys +import os +import argparse +import json +import logging +from xml.etree import ElementTree +from enum import Enum + +from struct import unpack, pack +from binascii import hexlify, unhexlify +import os, sys, inspect + +from edlclient.Library.utils import print_progress, read_object, write_object, LogBase +from edlclient.Library.usblib import UsbClass +from edlclient.Library.hdlc import hdlc +from edlclient.Config.usb_ids import default_diag_vid_pid + +qcerror = { + 1: "None", + 2: "Unknown", + 3: "Open Port Fail", + 4: "Port not open", + 5: "Buffer too small", + 6: "Read data fail", + 7: "Open file fail", + 8: "File not open", + 9: "Invalid parameter", + 10: "Send write ram failed", + 11: "Send command failed", + 12: "Offline phone failed", + 13: "Erase rom failed", + 14: "Timeout", + 15: "Go cmd failed", + 16: "Set baudrate failed", + 17: "Say hello failed", + 18: "Write port failed", + 19: "Failed to read nv", + 20: "Failed to write nv", + 21: "Last failed but not recovery", + 22: "Backup file wasn't found", + 23: "Incorrect SPC Code", + 24: "Hello pkt isn't needed", + 25: "Not active" +} + +diagerror = { + 20: "Generic error", + 21: "Bad argument", + 22: "Data too large", + 24: "Not connected", + 25: "Send pkt failed", + 26: "Receive pkt failed", + 27: "Extract pkt failed", + 29: "Open port failed", + 30: "Bad command", + 31: "Protected", + 32: "No media", + 33: "Empty", + 34: "List done" +} + +nvitem_type = [ + ("item", "H"), + ("rawdata", "128s"), + ("status", "H") +] + +subnvitem_type = [ + ("item", "H"), + ("index", "H"), + ("rawdata", "128s"), + ("status", "H") +] + + +class fs_factimage_read_info(): + def_fs_factimage_read_info = [ + ("stream_state", "B"), # 0 indicates no more data to be sent, otherwise set to 1 + ("info_cluster_sent", "B"), # 0 indicates if info_cluster was not sent, else 1 + ("cluster_map_seqno", "H"), # Sequence number of cluster map pages + ("cluster_data_seqno", "I") # Sequence number of cluster data pages + ] + + def __init__(self, stream_state, info_cluster_sent, cluster_map_seqno, cluster_data_seqno): + self.stream_state = stream_state + self.info_cluster_sent = info_cluster_sent + self.cluster_map_seqno = cluster_map_seqno + self.cluster_data_seqno = cluster_data_seqno + + def fromdata(self, data): + tmp = read_object(data[0:0x10], self.def_fs_factimage_read_info) + self.stream_state = tmp["stream_state"] + self.info_cluster_sent = tmp["info_cluster_sent"] + self.cluster_map_seqno = tmp["cluster_map_seqno"] + self.cluster_data_seqno = tmp["cluster_data_seqno"] + + def todata(self): + data = write_object(self.def_fs_factimage_read_info, self.stream_state, self.info_cluster_sent, + self.cluster_map_seqno, self.cluster_data_seqno) + return data + + +class FactoryHeader(): + def_factory_header = [ + ("magic1", "I"), + ("magic2", "I"), + ("fact_version", "H"), # Version of this cluster + # #Fields needed for the superblock + ("version", "H"), # Superblock version + ("block_size", "I"), # Pages per block. + ("page_size", "I"), # Page size in bytes. + ("block_count", "I"), # Total blocks in device. + ("space_limit", "I"), # Total number of used pages (defines the size of the map) + ("upper_data", "32I") + ] + + def __init__(self): + self.magic1 = 0 + self.magic2 = 0 + self.fact_version = 0 + self.version = 0 + self.block_size = 0 + self.page_size = 0 + self.block_count = 0 + self.space_limit = 0 + self.upper_data = [0 * 32] + + def fromdata(self, data): + tmp = read_object(data[0:0x9C], self.def_factory_header) + self.magic1 = tmp["magic1"] + self.magic2 = tmp["magic2"] + self.fact_version = tmp["fact_version"] + self.version = tmp["version"] + self.block_size = tmp["block_size"] + self.page_size = tmp["page_size"] + self.block_count = tmp["block_count"] + self.space_limit = tmp["space_limit"] + self.upper_data = tmp["upper_data"] + + def todata(self): + data = write_object(self.magic1, self.magic2, self.fact_version, self.version, self.block_size, self.page_size, + self.block_count, self.space_limit, self.upper_data) + return data + + +class nvitem(): + item = 0x0 + data = b"" + status = 0x0 + index = 0x0 + name = "" + + def __init__(self, item, index, data, status, name): + self.item = item + self.index = index + self.data = data + self.status = status + self.name = name + + +class diag_cmds(Enum): + DIAG_VERNO_F = 0 + DIAG_ESN_F = 1 + DIAG_PEEKB_F = 2 + DIAG_PEEKW_F = 3 + DIAG_PEEKD_F = 4 + DIAG_POKEB_F = 5 + DIAG_POKEW_F = 6 + DIAG_POKED_F = 7 + DIAG_OUTP_F = 8 + DIAG_OUTPW_F = 9 + DIAG_INP_F = 0xA + DIAG_INPW_F = 0xB + DIAG_STATUS_F = 0xC + DIAG_LOGMASK_F = 0xF + DIAG_LOG_F = 0x10 + DIAG_NV_PEEK_F = 0x11 + DIAG_NV_POKE_F = 0x12 + DIAG_BAD_CMD_F = 0x13 + DIAG_BAD_PARM_F = 0x14 + DIAG_BAD_LEN_F = 0x15 + DIAG_BAD_MODE_F = 0x18 + DIAG_TAGRAPH_F = 0x19 + DIAG_MARKOV_F = 0x1a + DIAG_MARKOV_RESET_F = 0x1b + DIAG_DIAG_VER_F = 0x1c + DIAG_TS_F = 0x1d + DIAG_TA_PARM_F = 0x1E + DIAG_MSG_F = 0x1f + DIAG_HS_KEY_F = 0x20 + DIAG_HS_LOCK_F = 0x21 + DIAG_HS_SCREEN_F = 0x22 + DIAG_PARM_SET_F = 0x24 + DIAG_NV_READ_F = 0x26 + DIAG_NV_WRITE_F = 0x27 + DIAG_CONTROL_F = 0x29 + DIAG_ERR_READ_F = 0x2a + DIAG_ERR_CLEAR_F = 0x2b + DIAG_SER_RESET_F = 0x2c + DIAG_SER_REPORT_F = 0x2d + DIAG_TEST_F = 0x2e + DIAG_GET_DIPSW_F = 0x2f + DIAG_SET_DIPSW_F = 0x30 + DIAG_VOC_PCM_LB_F = 0x31 + DIAG_VOC_PKT_LB_F = 0x32 + DIAG_ORIG_F = 0x35 + DIAG_END_F = 0x36 + DIAG_SW_VERSION_F = 0x38 + DIAG_DLOAD_F = 0x3a + DIAG_TMOB_F = 0x3b + DIAG_STATE_F = 0x3f + DIAG_PILOT_SETS_F = 0x40 + DIAG_SPC_F = 0x41 + DIAG_BAD_SPC_MODE_F = 0x42 + DIAG_PARM_GET2_F = 0x43 + DIAG_SERIAL_CHG_F = 0x44 + DIAG_PASSWORD_F = 0x46 + DIAG_BAD_SEC_MODE_F = 0x47 + DIAG_PR_LIST_WR_F = 0x48 + DIAG_PR_LIST_RD_F = 0x49 + DIAG_SUBSYS_CMD_F = 0x4b + DIAG_FEATURE_QUERY_F = 0x51 + DIAG_SMS_READ_F = 0x53 + DIAG_SMS_WRITE_F = 0x54 + DIAG_SUP_FER_F = 0x55 + DIAG_SUP_WALSH_CODES_F = 0x56 + DIAG_SET_MAX_SUP_CH_F = 0x57 + DIAG_PARM_GET_IS95B_F = 0x58 + DIAG_FS_OP_F = 0x59 + # DIAG_RAM_RW_F = 0x59 + DIAG_AKEY_VERIFY_F = 0x5A + # DIAG_CPU_RW_F = 0x5a + DIAG_BMP_HS_SCREEN_F = 0x5b + DIAG_CONFIG_COMM_F = 0x5c + DIAG_EXT_LOGMASK_F = 0x5d + DIAG_EVENT_REPORT_F = 0x60 + DIAG_STREAMING_CONFIG_F = 0x61 + DIAG_PARM_RETRIEVE_F = 0x62 + DIAG_STATUS_SNAPSHOT_F = 0x63 + DIAG_RPC_F = 0x64 + DIAG_GET_PROPERTY_F = 0x65 + DIAG_PUT_PROPERTY_F = 0x66 + DIAG_GET_GUID_F = 0x67 + DIAG_USER_CMD_F = 0x68 + DIAG_GET_PERM_PROPERTY_F = 0x69 + DIAG_PUT_PERM_PROPERTY_F = 0x6a + DIAG_PERM_USER_CMD_F = 0x6b + DIAG_GPS_SESS_CTRL_F = 0x6c + DIAG_GPS_GRID_F = 0x6d + DIAG_GPS_STATISTICS_F = 0x6E + DIAG_TUNNEL_F = 0x6f + DIAG_MAX_F = 0x70 + DIAG_SET_FTM_TEST_MODE = 0x72 + DIAG_EXT_BUILD_ID_F = 0x7c + + +class efs_cmds(Enum): + EFS2_DIAG_HELLO = 0 # Parameter negotiation packet + EFS2_DIAG_QUERY = 1 # Send information about EFS2 params + EFS2_DIAG_OPEN = 2 # Open a file + EFS2_DIAG_CLOSE = 3 # Close a file + EFS2_DIAG_READ = 4 # Read a file + EFS2_DIAG_WRITE = 5 # Write a file + EFS2_DIAG_SYMLINK = 6 # Create a symbolic link + EFS2_DIAG_READLINK = 7 # Read a symbolic link + EFS2_DIAG_UNLINK = 8 # Remove a symbolic link or file + EFS2_DIAG_MKDIR = 9 # Create a directory + EFS2_DIAG_RMDIR = 10 # Remove a directory + EFS2_DIAG_OPENDIR = 11 # Open a directory for reading + EFS2_DIAG_READDIR = 12 # Read a directory + EFS2_DIAG_CLOSEDIR = 13 # Close an open directory + EFS2_DIAG_RENAME = 14 # Rename a file or directory + EFS2_DIAG_STAT = 15 # Obtain information about a named file + EFS2_DIAG_LSTAT = 16 # Obtain information about a symbolic link + EFS2_DIAG_FSTAT = 17 # Obtain information about a file descriptor + EFS2_DIAG_CHMOD = 18 # Change file permissions + EFS2_DIAG_STATFS = 19 # Obtain file system information + EFS2_DIAG_ACCESS = 20 # Check a named file for accessibility + EFS2_DIAG_NAND_DEV_INFO = 21 # Get NAND device info + EFS2_DIAG_FACT_IMAGE_START = 22 # Start data output for Factory Image + EFS2_DIAG_FACT_IMAGE_READ = 23 # Get data for Factory Image + EFS2_DIAG_FACT_IMAGE_END = 24 # End data output for Factory Image + EFS2_DIAG_PREP_FACT_IMAGE = 25 # Prepare file system for image dump + EFS2_DIAG_PUT_DEPRECATED = 26 # Write an EFS item file + EFS2_DIAG_GET_DEPRECATED = 27 # Read an EFS item file + EFS2_DIAG_ERROR = 28 # Send an EFS Error Packet back through DIAG + EFS2_DIAG_EXTENDED_INFO = 29 # Get Extra information. + EFS2_DIAG_CHOWN = 30 # Change ownership + EFS2_DIAG_BENCHMARK_START_TEST = 31 # Start Benchmark + EFS2_DIAG_BENCHMARK_GET_RESULTS = 32 # Get Benchmark Report + EFS2_DIAG_BENCHMARK_INIT = 33 # Init/Reset Benchmark + EFS2_DIAG_SET_RESERVATION = 34 # Set group reservation + EFS2_DIAG_SET_QUOTA = 35 # Set group quota + EFS2_DIAG_GET_GROUP_INFO = 36 # Retrieve Q&R values + EFS2_DIAG_DELTREE = 37 # Delete a Directory Tree + EFS2_DIAG_PUT = 38 # Write a EFS item file in order + EFS2_DIAG_GET = 39 # Read a EFS item file in order + EFS2_DIAG_TRUNCATE = 40 # Truncate a file by the name + EFS2_DIAG_FTRUNCATE = 41 # Truncate a file by a descriptor + EFS2_DIAG_STATVFS_V2 = 42 # Obtains extensive file system info + + +O_RDONLY = 0 +O_WRONLY = 1 +O_RDWR = 2 +O_ACCMODE = O_RDONLY | O_WRONLY | O_RDWR +FS_DIAG_MAX_READ_REQ = 1024 + + +# define DIAG_NV_WRITE_F 0x27 +# define DIAG_NV_READ_F 0x26 + +class qcdiag(metaclass=LogBase): + def __init__(self, loglevel, portconfig, ep_in=-1, ep_out=-1): + self.portconfig = portconfig + self.nvlist = {} + self.ep_in = ep_in + self.ep_out = ep_out + self.__logger.setLevel(loglevel) + if loglevel == logging.DEBUG: + logfilename = "log.txt" + fh = logging.FileHandler(logfilename) + self.__logger.addHandler(fh) + import os, sys, inspect + current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) + parent_dir = os.path.dirname(os.path.dirname(current_dir)) + nvxml = os.path.join(parent_dir, "edlclient", "Config", "nvitems.xml") + e = ElementTree.parse(nvxml).getroot() + for atype in e.findall("nv"): + name = atype.get("name") + identifier = int(atype.get("id")) + self.nvlist[identifier] = name + + def prettyprint(self, data): + recv = "" + plain = "" + for i in range(len(data)): + inf = "%02X " % data[i] + recv += inf + if data[i] == 0x0D or data[i] == 0x0A or (0x20 <= data[i] <= 0x9A): + plain += chr(data[i]) + else: + plain += " " + if ((i + 1) % 16) == 0: + recv += "\n" + plain += "\n" + res = recv + "\n-----------------------------------------------\n" + if len(plain.replace(" ", "").replace("\n", "")) > 0: + res += plain + return res + + def decodestatus(self, data): + info = data[0] + if info == 0x13: + return "Invalid Command Response" + elif info == 0x14: + return "Invalid parameter Response" + elif info == 0x15: + return "Invalid packet length Response" + elif info == 0x17: + return "Send Security Mode" + elif info == 0x18: + return "Packet not allowed in this mode ( online vs offline )" + elif info == 0x42: + return "Invalid nv_read/write because SP is locked" + elif info == diag_cmds.DIAG_BAD_SEC_MODE_F.value: + return "Security privileges required" + else: + return "Command accepted" + + def connect(self): + self.cdc = UsbClass(portconfig=self.portconfig, loglevel=self.__logger.level) + self.hdlc = None + if self.cdc.connect(self.ep_in, self.ep_out): + self.hdlc = hdlc(self.cdc) + data = self.hdlc.receive_reply(1) + return True + return False + + def disconnect(self): + self.cdc.close(True) + + def send(self, cmd): + if self.hdlc != None: + return self.hdlc.send_cmd_np(cmd) + + def cmd_info(self): + reply = self.send(b"\x00") + return self.prettyprint(reply) + + def enforce_crash(self): + # ./diag.py -nvwrite 1027,01 enable adsp log NV_MDSP_MEM_DUMP_ENABLED_I + # ./diag.py -nvwrite 4399,01 enable download on reboot NV_DETECT_HW_RESET_I + res = self.send(b"\x4B\x25\x03\x00") + print(self.decodestatus(res)) + + def enter_downloadmode(self): + res = self.send(b"\x3A") + print(self.decodestatus(res)) + + def enter_saharamode(self): + self.hdlc.receive_reply() + res = self.send(b"\x4b\x65\x01\x00") + if res[0]==0x4b: + print("Done, switched to edl") + else: + print("Error switching to edl. Try again.") + self.disconnect() + + + def send_sp(self, sp="FFFFFFFFFFFFFFFFFFFE"): + if type(sp) == str: + sp = unhexlify(sp) + else: + sp = bytes(sp) + if len(sp) < 8: + print("SP length must be 8 bytes") + return + res = self.send(b"\x46" + sp) + if res[0] != 0x46: + res = self.send(b"\x25" + sp) + elif res[0] == 0x46: + if res[1] == 0x0: + print("Security Password is wrong") + elif res[1] == 0x1: + print("Security Password accepted.") + elif res[0] != 0x25: + print(self.decodestatus(res)) + return res + + def send_spc(self, spc="303030303030"): + if type(spc) == str: + spc = unhexlify(spc) + else: + spc = bytes(spc) + if len(spc) < 6: + print("SPC length must be 6 bytes") + return + res = self.send(b"\x41" + spc) + if res[0] != 0x41: + print(self.decodestatus(res)) + else: + if res[1] == 0x0: + print("SPC is wrong") + elif res[1] == 0x1: + print("SPC accepted.") + return res + + def DecodeNVItems(self, nvitem): + if nvitem.status == 0x1: + return "Internal DMSS use" + elif nvitem.status == 0x2: + return "Unrecognized command" + elif nvitem.status == 0x3: + return "NV memory full" + elif nvitem.status == 0x4: + return "Command failed" + elif nvitem.status == 0x5: + return "Inactive Item" + elif nvitem.status == 0x6: + return "Bad Parameter" + elif nvitem.status == 0x7: + return "Item was read-only" + elif nvitem.status == 0x8: + return "Item not defined for this target" + elif nvitem.status == 0x9: + return "No more free memory" + elif nvitem.status == 0xA: + return "Internal use" + elif nvitem.status == 0x0: + return "OK" + return "" + + def print_nvitem(self, item): + res, nvitem = self.read_nvitem(item) + if res: + info = self.DecodeNVItems(nvitem) + if res != False: + if nvitem.name != "": + ItemNumber = f"{hex(item)} ({nvitem.name}): " + else: + ItemNumber = hex(item) + ": " + returnanswer = "NVItem " + ItemNumber + info + print(returnanswer) + if nvitem.status == 0: + print("-----------------------------------------") + print(self.prettyprint(nvitem.data)) + else: + print(nvitem) + else: + print(nvitem) + + def print_nvitemsub(self, item, index): + res, nvitem = self.read_nvitemsub(item,index) + info = self.DecodeNVItems(nvitem) + if res != False: + if nvitem.name != "": + ItemNumber = f"{hex(item),hex(index)} ({nvitem.name}): " + else: + ItemNumber = hex(item)+","+hex(index) + ": " + returnanswer = "NVItem " + ItemNumber + info + print(returnanswer) + if nvitem.status == 0: + print("-----------------------------------------") + print(self.prettyprint(nvitem.data)) + else: + print(nvitem) + + def backup_nvitems(self, filename, errorlog=""): + nvitems = [] + pos = 0 + old = 0 + errors = "" + print("Dumping nvitems 0x0 to 0xFFFF.") + for item in range(0, 0xFFFF): + prog = int(float(pos) / float(0xFFFF) * float(100)) + if prog > old: + print_progress(prog, 100, prefix="Progress:", suffix=f"Complete, item {hex(item)}", bar_length=50) + old = prog + res, nvitem = self.read_nvitem(item) + if res != False: + if nvitem.status != 0x5: + nvitem.status = self.DecodeNVItems(nvitem) + nvitems.append(dict(id=nvitem.item, name=nvitem.name, data=hexlify(nvitem.data).decode("utf-8"), + status=nvitem.status)) + else: + errors += nvitem + "\n" + pos += 1 + js = json.dumps(nvitems) + with open(filename, "w") as write_handle: + write_handle.write(js) + if errorlog == "": + print(errors) + else: + with open(errorlog, "w") as write_handle: + write_handle.write(errors) + print("Done.") + + def unpackdata(self,data): + rlen = len(data) + idx = rlen - 1 + for i in range(0, rlen): + byte = data[rlen - i - 1] + if byte != 0: + break + idx = rlen - i - 1 + return data[:idx] + + def read_nvitem(self, item): + rawdata = 128 * b"\x00" + status = 0x0000 + nvrequest = b"\x26" + write_object(nvitem_type, item, rawdata, status)["raw_data"] + data = self.send(nvrequest) + if len(data) == 0: + data = self.send(nvrequest) + if len(data) > 0: + if data[0] == 0x26: + res = read_object(data[1:], nvitem_type) + name = "" + if item in self.nvlist: + name = self.nvlist[item] + data=self.unpackdata(res["rawdata"]) + res = nvitem(res["item"],0, data, res["status"], name) + return [True, res] + elif data[0] == 0x14: + return [False, f"Error 0x14 trying to read nvitem {hex(item)}."] + else: + return [False, f"Error {hex(data[0])} trying to read nvitem {hex(item)}."] + return [False, f"Empty request for nvitem {hex(item)}"] + + def read_nvitemsub(self, item, index): + rawdata = 128 * b"\x00" + status = 0x0000 + nvrequest = b"\x4B\x30\x01\x00" + write_object(subnvitem_type, item, index, rawdata, status)["raw_data"] + data = self.send(nvrequest) + if len(data) == 0: + data = self.send(nvrequest) + if len(data) > 0: + if data[0] == 0x4B: + res = read_object(data[4:], subnvitem_type) + name = "" + if item in self.nvlist: + name = self.nvlist[item] + data=self.unpackdata(res["rawdata"]) + res = nvitem(res["item"], index, data, res["status"], name) + return [True, res] + elif data[0] == 0x14: + return [False, f"Error 0x14 trying to read nvitem {hex(item)}."] + else: + return [False, f"Error {hex(data[0])} trying to read nvitem {hex(item)}."] + return [False, f"Empty request for nvitem {hex(item)}"] + + def convertimei(self,imei): + data=imei[0]+"A" + for i in range(1,len(imei),2): + data+=imei[i+1] + data+=imei[i] + return unhexlify("08"+data) + + def write_imei(self,imeis): + if "," in imeis: + imeis=imeis.split(",") + else: + imeis=[imeis] + index=0 + for imei in imeis: + data=self.convertimei(imei) + if index==0: + if not self.write_nvitem(550,data): + self.write_nvitemsub(550,index,data) + else: + self.write_nvitemsub(550,index,data) + index+=1 + + def write_nvitem(self, item, data): + rawdata = bytes(data) + while len(rawdata) < 128: + rawdata += b"\x00" + status = 0x0000 + nvrequest = b"\x27" + write_object(nvitem_type, item, rawdata, status)["raw_data"] + res = self.send(nvrequest) + if len(res) > 0: + if res[0] == 0x27: + res, nvitem = self.read_nvitem(item) + if res == False: + print(f"Error while writing nvitem {hex(item)} data, %s" % data) + else: + if nvitem.data != data: + print(f"Error while writing nvitem {hex(item)} data, verified data doesn't match") + else: + print(f"Successfully wrote nvitem {hex(item)}.") + return True + return False + else: + print(f"Error while writing nvitem {hex(item)} data, %s" % data) + + def write_nvitemsub(self, item, index, data): + rawdata = bytes(data) + while len(rawdata) < 128: + rawdata += b"\x00" + status = 0x0000 + nvrequest = b"\x4B\x30\x02\x00" + write_object(subnvitem_type, item, index, rawdata, status)["raw_data"] + res = self.send(nvrequest) + if len(res) > 0: + if res[0] == 0x4B: + res, nvitem = self.read_nvitemsub(item,index) + if res == False: + print(f"Error while writing nvitem {hex(item)} index {hex(index)} data, %s" % data) + else: + if nvitem.data != data: + print(f"Error while writing nvitem {hex(item)} index {hex(index)} data, verified data doesn't match") + else: + print(f"Successfully wrote nvitem {hex(item)} index {hex(index)}.") + return True + return False + else: + print(f"Error while writing nvitem {hex(item)} index {hex(index)} data, %s" % data) + + def efsread(self, filename): + alternateefs = b"\x4B\x3E\x19\x00" + standardefs = b"\x4B\x13\x19\x00" + resp = self.send(alternateefs) + if resp[0] == 0x4B: + efsmethod = 0x3E + else: + resp = self.send(standardefs) + if resp[0] == 0x4B: + efsmethod = 0x13 + else: + print("No known efs method detected for reading.") + return + + if filename == "": + return False + write_handle = open(filename, "wb") + if write_handle is None: + print("Error on writing file ....") + return False + + print("Reading EFS ....") + fefs = fs_factimage_read_info(0, 0, 0, 0) + + # EFS Cmd + buf = pack(" 0: + write_handle.write(resp[0x10:-0x1]) + fefs.fromdata(resp[0x8:0x10]) + fh.fromdata(resp[0x10:0x10 + (39 * 4)]) + + old = 0 + print_progress(0, 100, prefix="Progress:", suffix="Complete", bar_length=50) + total = fh.block_size * fh.block_count * (fh.page_size // 0x200) + + # Real start + for page in range(0, total): + # EFS Cmd + buf = pack(" old: + print_progress(pos, 100, prefix="Progress:", suffix="Page %d of %d" % (page, total), bar_length=50) + old = pos + + resp = self.send(buf) + if resp == 0: + resp = self.send(buf) + + if resp == 0 or resp == -1: + info = ("Page %08X !\n" % fefs.cluster_data_seqno) + print(info) + resp = self.send(buf) + if resp == 0 or resp == -1: + print("Data Error occured, " + info) + else: + dlen = len(resp) - 0x11 + if dlen == 0x200 or dlen == 0x800: + if resp[0x0] == 0x4B: + write_handle.write(resp[0x10:0x10 + dlen]) + fefs.fromdata(resp[0x8:0x10]) + else: + if (resp[0x0] == 0x13) and (resp[0x1] == 0x62) and (len(resp) > 0x200): + write_handle.write(resp[0x14:-4]) + fefs.fromdata(resp[0xc:0x14]) + if fefs.stream_state == 0x0: + break + else: + print("EFS Read error : Wrong size recieved at page %X" % page) + efserr = True + break + + print_progress(100, 100, prefix="Progress:", suffix="Complete", bar_length=50) + + buf = bytearray() + buf.append(0x4B) + buf.append(efsmethod) + buf.append(efs_cmds.EFS2_DIAG_FACT_IMAGE_END.value) # end factory image + buf.append(0x00) + + resp = self.send(buf) + if len(resp) == 0: + print("Phone does not respond. Maybe another software is blocking the port.") + return False + + write_handle.close() + if efserr == False: + print("Successfully read EFS.") + return True + else: + print("Error on reading EFS.") + return False + + def send_cmd(self, cmd): + cmdtosend = unhexlify(cmd) + reply = self.send(cmdtosend) + if reply[0] != cmdtosend[0]: + print(self.decodestatus(reply)) + result = self.prettyprint(reply) + return result + + def efsdiagerror(self, errcode): + if errcode == 0x40000001: + print("Inconsistent state.") + elif errcode == 0x40000002: + print("Invalid seq no.") + elif errcode == 0x40000003: + print("Directory not open.") + elif errcode == 0x40000004: + print("Directory entry not found.") + elif errcode == 0x40000005: + print("Invalid path.") + elif errcode == 0x40000006: + print("Path too long") + elif errcode == 0x40000007: + print("Too many open directories.") + elif errcode == 0x40000008: + print("Invalid directory entry.") + elif errcode == 0x40000009: + print("Too many open files.") + elif errcode == 0x4000000a: + print("Unknown filetype") + elif errcode == 0x4000000b: + print("Not nand falsh") + elif errcode == 0x4000000c: + print("Unavailable info") + else: + return 0 + return -1 + + def efs_closedir(self, efsmethod, dirp): + buf = pack(" 0: + [dirp, seqno, diag_errno, entry_type, mode, size, atime, mtime, ctime] = unpack(" 0: + rsize = dataleft + if rsize > FS_DIAG_MAX_READ_REQ: + rsize = FS_DIAG_MAX_READ_REQ + finfo = self.efs_read(efsmethod, fdata, rsize, offset) + if finfo == -1: + break + fdata, offset, bytes_read, data = finfo + write_handle.write(data) + offset += rsize + dataleft -= rsize + self.efs_close(efsmethod, fdata) + return num_bytes + + def efswritefile(self, srcpath, dstpath): + alternateefs = b"\x4B\x3E\x00\x00" + b"\x00" * 0x28 + standardefs = b"\x4B\x13\x00\x00" + b"\x00" * 0x28 + resp = self.send(alternateefs) + if resp[0] == 0x4B: + efsmethod = 0x3E + else: + resp = self.send(standardefs) + if resp[0] == 0x4B: + efsmethod = 0x13 + else: + logging.error("No known efs method detected for reading.") + return 0 + with open(srcpath, "rb") as rf: + fdata = self.efs_open(efsmethod, O_RDONLY, 0, srcpath) + if fdata == -1: + return 0 + mode, size, nlink, atime, mtime, ctime = self.efs_fstat(efsmethod, fdata) + if size == 0: + self.efs_close(efsmethod, fdata) + return 0 + """ + acr=(mode & O_ACCMODE) + if acr==O_RDONLY: + print("File can only be read. Aborting.") + self.efs_close(efsmethod, fdata) + return + """ + num_bytes = 0 + offset = 0 + size = os.fstat(srcpath).st_size + dataleft = size + while dataleft > 0: + rsize = dataleft + if rsize > FS_DIAG_MAX_READ_REQ: + rsize = FS_DIAG_MAX_READ_REQ + data = rf.read(rsize) + finfo = self.efs_write(efsmethod, fdata, offset, data) + if finfo == -1: + break + fdata, offset, bytes_written = finfo + offset += rsize + dataleft -= rsize + self.efs_close(efsmethod, fdata) + return num_bytes + + +class DiagTools(metaclass=LogBase): + def run(self, args): + self.interface = -1 + self.vid = None + self.pid = None + + if args.vid != "": + self.vid = int(args.vid, 16) + if args.pid != "": + self.pid = int(args.pid, 16) + if args.interface != "": + self.interface = int(args.interface, 16) + + logfilename = "diag.txt" + if args.debugmode: + 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) + + connected = False + diag = None + if self.vid is None or self.pid is None: + diag = qcdiag(loglevel=self.__logger.level, portconfig=default_diag_vid_pid) + connected = diag.connect() + else: + diag = qcdiag(loglevel=self.__logger.level, portconfig=[[self.vid, self.pid, self.interface]]) + connected = diag.connect() + if connected: + cmd = args.cmd + if cmd=="sp": + diag.send_sp(args.spval) + elif cmd=="spc": + diag.send_spc(args.spcval) + elif cmd=="cmd": + if args.cmdval=="": + print("cmd needed as hex string, example: 00") + else: + print(diag.send_cmd(args.cmdval)) + elif cmd=="info": + print(diag.cmd_info()) + elif cmd=="download": + diag.enter_downloadmode() + elif cmd=="sahara": + diag.enter_saharamode() + elif cmd=="crash": + diag.enforce_crash() + elif cmd=="efslistdir": + print(diag.efslistdir(args.path)) + elif cmd=="efsreadfile": + if args.src=="" or args.dst=="": + print("Usage: -efsreadfile -src srcfile -dst dstfile") + sys.exit() + print(diag.efsreadfile(args.src,args.dst)) + elif cmd=="nvread": + if "0x" in args.nvitem: + nvitem = int(args.nvitem, 16) + else: + nvitem = int(args.nvitem) + diag.print_nvitem(nvitem) + elif cmd=="nvreadsub": + if args.nvitem is None or args.nvindex is None: + print("Usage: nvreadsub [nvitem] [nvindex]") + exit(1) + nv = args.nvreadsub.split(",") + if "0x" in args.nvitem: + nvitem = int(args.nvitem, 16) + else: + nvitem = int(args.nvitem) + if "0x" in nv[1]: + nvindex = int(args.nvindex, 16) + else: + nvindex = int(args.nvindex) + diag.print_nvitemsub(nvitem,nvindex) + elif cmd=="nvwrite": + if args.data is None: + print("NvWrite requires data to write") + sys.exit() + if "0x" in args.nvitem: + nvitem = int(args.nvitem, 16) + else: + nvitem = int(args.nvitem) + data = unhexlify(args.data) + diag.write_nvitem(nvitem, data) + elif cmd=="nvwritesub": + if args.nvitem is None or args.nvindex is None or args.data is None: + print("NvWriteSub requires item, index and data to write") + sys.exit() + if "0x" in args.nvitem: + nvitem = int(args.nvitem, 16) + else: + nvitem = int(args.nvitem) + if "0x" in args.nvindex: + nvindex = int(args.nvindex, 16) + else: + nvindex = int(args.nvindex) + data = unhexlify(args.data) + diag.write_nvitemsub(nvitem, nvindex, data) + elif cmd=="nvbackup": + diag.backup_nvitems(args.filename, "error.log") + elif cmd=="writeimei": + diag.write_imei(args.imei) + elif cmd=="efsread": + diag.efsread(args.filename) + else: + print("A command is required. Use -cmd \"data\" for sending requests.") + print() + print("Valid commands are:") + print("-------------------") + print("info cmd sp spc nvread nvreadsub" + + " nvwrite writeimei nvwritesub nvbackup efsread efsreadfile" + + " efslistdir download sahara crash") + print() + diag.disconnect() + sys.exit() + else: + print("No diag device detected. Use -pid and -vid options. See -h for help.") + diag.disconnect() + sys.exit() + + +def main(): + info = "Qualcomm Diag Client (c) B.Kerler 2019-2021." + parser = argparse.ArgumentParser(description=info) + print("\n" + info + "\n---------------------------------------\n") + parser.add_argument("-vid", metavar="", help="[Option] Specify vid", default="") + parser.add_argument("-pid", metavar="", help="[Option] Specify pid", default="") + parser.add_argument("-interface", metavar="", help="[Option] Specify interface number, default=0)", + default="0") + parser.add_argument("--debugmode", help="[Option] Enable verbose logging", action="store_true") + + subparser = parser.add_subparsers(dest="cmd", help="Valid commands are:\ninfo cmd sp spc nvread nvreadsub" + + " nvwrite writeimei nvwritesub nvbackup efsread efsreadfile\n" + + " efslistdir download sahara crash") + + parser_info = subparser.add_parser("info", help="[Option] Get diag info") + + parser_cmd = subparser.add_parser("cmd", help="Send command") + parser_cmd.add_argument("cmdval", help="cmd to send (hexstring), default: 00", + default="", const="00", nargs="?") + + parser_sp = subparser.add_parser("sp", help="Send Security password") + parser_sp.add_argument("spval", help="Security password to send, default: FFFFFFFFFFFFFFFE", + default="FFFFFFFFFFFFFFFE", nargs="?") + + parser_spc = subparser.add_parser("spc", help="Send Security Code") + parser_spc.add_argument("spcval", help="Security code to send, default: 303030303030", + default="303030303030", nargs="?") + + parser_nvread = subparser.add_parser("nvread", help="Read nvitem") + parser_nvread.add_argument("nvitem", help="[Option] NVItem to read", default="") + + parser_nvreadsub = subparser.add_parser("nvreadsub", help="Read nvitem using subsystem") + parser_nvreadsub.add_argument("nvitem", help="[Option] NVItem to read", default="") + parser_nvreadsub.add_argument("nvindex", help="[Option] Index to read", default="") + + parser_nvwrite = subparser.add_parser("nvwrite", help="Write nvitem") + parser_nvwrite.add_argument("nvitem", help="[Option] NVItem to write", default="") + parser_nvwrite.add_argument("data", help="[Option] Data to write", default="") + + parser_nvwritesub = subparser.add_parser("nvwritesub", help="Write nvitem using subsystem") + parser_nvwritesub.add_argument("nvitem", help="[Option] NVItem to read", default="") + parser_nvwritesub.add_argument("nvindex", help="[Option] Index to read", default="") + parser_nvwritesub.add_argument("data", help="[Option] Data to write", default="") + + parser_writeimei = subparser.add_parser("writeimei", help="Write imei") + parser_writeimei.add_argument("imei", metavar=(""), help="[Option] IMEI to write", default="") + + + parser_nvbackup = subparser.add_parser("nvbackup", help="Make nvitem backup as json") + parser_nvbackup.add_argument("filename", help="[Option] Filename to write to", default="") + + parser_efsread = subparser.add_parser("efsread", help="Read efs") + parser_efsread.add_argument("filename", help="[Option] Filename to write to", default="") + + parser_efsreadfile = subparser.add_parser("efsreadfile", help="Read efs file") + parser_efsreadfile.add_argument("src", help="[Option] Source filename", default="") + parser_efsreadfile.add_argument("dst", help="[Option] Destination filename", default="") + + parser_efslistdir = subparser.add_parser("efslistdir", help="List efs directory") + parser_efslistdir.add_argument("path", help="[Option] Path to list", default="") + + parser_download = subparser.add_parser("download", help="[Option] Switch to sahara mode") + + parser_sahara = subparser.add_parser("sahara", help="[Option] Switch to sahara mode") + + parser_crash = subparser.add_parser("crash", help="[Option] Enforce crash") + + + args = parser.parse_args() + dg = DiagTools() + dg.run(args) + + +if __name__ == "__main__": + main() + diff --git a/edlclient/Tools/sierrakeygen b/edlclient/Tools/sierrakeygen new file mode 120000 index 0000000..0ca3dd2 --- /dev/null +++ b/edlclient/Tools/sierrakeygen @@ -0,0 +1 @@ +sierrakeygen.py \ No newline at end of file diff --git a/edlclient/Tools/sierrakeygen.py b/edlclient/Tools/sierrakeygen.py new file mode 100755 index 0000000..8b17864 --- /dev/null +++ b/edlclient/Tools/sierrakeygen.py @@ -0,0 +1,634 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# (c) B.Kerler 2019-2020 under MIT license +# If you use my code, make sure you refer to my name +# If you want to use in a commercial product, ask me before integrating it + +import serial +import sys +import argparse +import time +import serial.tools.list_ports +from telnetlib import Telnet +from binascii import hexlify, unhexlify + +try: + from edlclient.Library.utils import LogBase +except Exception as e: + import os,sys,inspect + current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) + parent_dir = os.path.dirname(current_dir) + sys.path.insert(0, parent_dir) + from Library.utils import LogBase + +''' +C7 = 7 0 0 2 7 5 0 +C6 = 3 1 7 0 3 0 1 +C5 = 0 2 5 3 0 3 2 +C8 = 1 3 3 1 5 7 3 +C4 = 5 4 1 4 1 1 4 +''' +prodtable = { + "MDM8200": dict(openlock=0, openmep=1, opencnd=0, clen=8, init=[1, 3, 5, 7, 0], + run="resultbuffer[i]=self.SierraAlgo(challenge[i], 2, 4, 1, 3, 0, 3, 4, 0)"), + # MC878XC_F1.2.3.15 verified, key may be stored in nvitem 0x4e21;MC8700 M3.0.9.0 verified + "MDM9200": dict(openlock=0, openmep=1, opencnd=0, clen=8, init=[7, 3, 0, 1, 5], + run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), + # AC881U, EM8805 SWI9X15C_05.05.58.00, at!openlock?6A1F1CEA298A14B0 => AT!OPENLOCK="3A9EA70D86FEE58C" + "MDM9200_V1": dict(openlock=2, openmep=1, opencnd=0, clen=8, init=[7, 3, 0, 1, 5], + run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), # AC710 + "MDM9200_V2": dict(openlock=3, openmep=1, opencnd=0, clen=8, init=[7, 3, 0, 1, 5], + run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), # AC775 + "MDM9200_V3": dict(openlock=8, openmep=1, opencnd=8, clen=8, init=[7, 3, 0, 1, 5], + run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), # AC775 + "MDM9x15": dict(openlock=0, openmep=1, opencnd=0, clen=8, init=[7, 3, 0, 1, 5], + run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), + # 9x15C 06.03.32.02, AC340U 1.13.12.14 verified, #AT!CUSTOM=\"ADBENABLE\",1 + "MDM9x07": dict(openlock=9, openmep=10, opencnd=9, clen=8, init=[7, 3, 0, 1, 5], + run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), + # SWI9X07Y_02.25.02.01 + "MDM9x30": dict(openlock=5, openmep=4, opencnd=5, clen=8, init=[7, 3, 0, 1, 5], + run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), + # MC7455_2.30.01.01 #4 + "MDM9x30_V1": dict(openlock=17, openmep=15, opencnd=17, clen=8, init=[7, 3, 0, 1, 5], + run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), + # AC791L/AC790S NTG9X35C_02.08.29.00 + "MDM9x40": dict(openlock=11, openmep=12, opencnd=11, clen=8, init=[7, 3, 0, 1, 5], + run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), # AC815s + "MDM9x50": dict(openlock=7, openmep=6, opencnd=7, clen=8, init=[7, 3, 0, 1, 5], + run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), # EM7565 + "MDM9x06": dict(openlock=20, openmep=19, opencnd=20, clen=8, init=[7, 3, 0, 1, 5], + run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), # WP77xx + "SDX55": dict(openlock=22, openmep=21, opencnd=22, clen=8, init=[7, 3, 0, 1, 5], #MR5100 + run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), + "MDM9x15A": dict(openlock=24, openmep=23, opencnd=24, clen=8, init=[7, 3, 0, 1, 5], #AC779S + run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), + +} + +infotable = { + "MDM8200": ["M81A", "M81B", "AC880", "AC881", "MC8780", "MC8781", "AC880E", "AC881E", "EM8780", "EM8781", + "MC8780V", "MC8781V", "MC8700", "AC308U"], + "MDM9200": ["AC710", "MC8775", "MC8775V", "AC875", "MC8700", "AC313U", "MC8801", "MC7700", "MC7750", "MC7710", + "EM7700", "770S", "781S"], + "MDM9200_V1": ["AC710", "MC8775", "MC8775V", "AC875", "MC8700", "AC313U", "MC8801", "MC7700", "MC7750", + "MC7710", "EM7700"], + "MDM9200_V2": ["AC775", "PC7200"], + "MDM9200_V3": ["AC775"], + "MDM9x07": ["SWI9X07Y", "WP76xx"], + "MDM9x06": ["SWI9X06Y", "WP77xx"], + "MDM9x15": ["SWI9X15C", "AR7550", "AR7552", "AR7554", "EM7355", "EM7655", "MC7354", "WP7100", "WP7102", "WP7104", + "MC7305", "EM7305", "MC8805", "EM8805", "MC7350", "MC7350-L", "MC7802", "MC7304", "AR7556", "AR7558", + "WP75xx", "WP85xx", "WP8548", "WP8548G", "AC340U"], + "MDM9x15A": ["AC779S"], + "MDM9x30": ["EM7455", "MC7455", "EM7430", "MC7430"], + "MDM9x30_V1": ["Netgear AC790/MDM9230"], + "MDM9x40": ["AC815s", "AC785s", "AC797S", "MR1100"], + "MDM9x50": ["EM7565", "EM7565-9", "EM7511", "EM7411"], + "SDX55" : ["MR5100"] +} + +keytable = bytearray([0xF0, 0x14, 0x55, 0x0D, 0x5E, 0xDA, 0x92, 0xB3, 0xA7, 0x6C, 0xCE, 0x84, 0x90, 0xBC, 0x7F, 0xED, + # 0 MC8775_H2.0.8.19 !OPENLOCK, !OPENCND .. MC8765V,MC8765,MC8755V,MC8775,MC8775V,MC8775,AC850, + # AC860,AC875,AC881,AC881U,AC875, AC340U 1.13.12.14 + 0x61, 0x94, 0xCE, 0xA7, 0xB0, 0xEA, 0x4F, 0x0A, 0x73, 0xC5, 0xC3, 0xA6, 0x5E, 0xEC, 0x1C, 0xE2, + # 1 MC8775_H2.0.8.19 AC340U, OPENMEP default + 0x39, 0xC6, 0x7B, 0x04, 0xCA, 0x50, 0x82, 0x1F, 0x19, 0x63, 0x36, 0xDE, 0x81, 0x49, 0xF0, 0xD7, + # 2 AC750,AC710,AC7XX,SB750A,SB750,PC7000,AC313u OPENMEP + 0xDE, 0xA5, 0xAD, 0x2E, 0xBE, 0xE1, 0xC9, 0xEF, 0xCA, 0xF9, 0xFE, 0x1F, 0x17, 0xFE, 0xED, 0x3B, + # 3 AC775,PC7200 + 0xFE, 0xD4, 0x40, 0x52, 0x2D, 0x4B, 0x12, 0x5C, 0xE7, 0x0D, 0xF8, 0x79, 0xF8, 0xC0, 0xDD, 0x37, + # 4 MC7455_02.30.01.01 OPENMEP + 0x3B, 0x18, 0x99, 0x6B, 0x57, 0x24, 0x0A, 0xD8, 0x94, 0x6F, 0x8E, 0xD9, 0x90, 0xBC, 0x67, 0x56, + # 5 MC7455_02.30.01.01 OPENLOCK + 0x47, 0x4F, 0x4F, 0x44, 0x4A, 0x4F, 0x42, 0x44, 0x45, 0x43, 0x4F, 0x44, 0x49, 0x4E, 0x47, 0x2E, + # 6 SWI9x50 Openmep Key SWI9X50C_01.08.04.00 + 0x4F, 0x4D, 0x41, 0x52, 0x20, 0x44, 0x49, 0x44, 0x20, 0x54, 0x48, 0x49, 0x53, 0x2E, 0x2E, 0x2E, + # 7 SWI9x50 Openlock Key SWI9X50C_01.08.04.00 + 0x8F, 0xA5, 0x85, 0x05, 0x5E, 0xCF, 0x44, 0xA0, 0x98, 0x8B, 0x09, 0xE8, 0xBB, 0xC6, 0xF7, 0x65, + # 8 MDM8200 Special + 0x4D, 0x42, 0xD8, 0xC1, 0x25, 0x44, 0xD8, 0xA0, 0x1D, 0x80, 0xC4, 0x52, 0x8E, 0xEC, 0x8B, 0xE3, + # 9 SWI9x07 Openlock Key 02.25.02.01 + 0xED, 0xA9, 0xB7, 0x0A, 0xDB, 0x85, 0x3D, 0xC0, 0x92, 0x49, 0x7D, 0x41, 0x9A, 0x91, 0x09, 0xEE, + # 10 SWI9x07 Openmep Key 02.25.02.01 + 0x8A, 0x56, 0x03, 0xF0, 0xBB, 0x9C, 0x13, 0xD2, 0x4E, 0xB2, 0x45, 0xAD, 0xC4, 0x0A, 0xE7, 0x52, + # 11 NTG9X40C_11.14.08.11 / mdm9x40r11_core AC815s / SWI9x50 MR1100 Openlock Key + 0x2A, 0xEF, 0x07, 0x2B, 0x19, 0x60, 0xC9, 0x01, 0x8B, 0x87, 0xF2, 0x6E, 0xC1, 0x42, 0xA8, 0x3A, + # 12 SWI9x50 MR1100 Openmep Key + 0x28, 0x55, 0x48, 0x52, 0x24, 0x72, 0x63, 0x37, 0x14, 0x26, 0x37, 0x50, 0xBE, 0xFE, 0x00, 0x00, + # 13 SWI9x50 Unknown key + 0x22, 0x63, 0x48, 0x02, 0x24, 0x72, 0x27, 0x37, 0x19, 0x26, 0x37, 0x50, 0xBE, 0xEF, 0xCA, 0xFE, + # 14 SWI9x50,SWI9X06Y IMEI nv key + 0x98, 0xE1, 0xC1, 0x93, 0xC3, 0xBF, 0xC3, 0x50, 0x8D, 0xA1, 0x35, 0xFE, 0x50, 0x47, 0xB3, 0xC4, + # 15 NTG9X35C_02.08.29.00 Openmep Key AC791L/AC790S Old + 0x61, 0x94, 0xCE, 0xA7, 0xB0, 0xEA, 0x4F, 0x0A, 0x73, 0xC5, 0xC3, 0xA6, 0x5E, 0xEC, 0x1C, 0xE2, + # 16 NTG9X35C_02.08.29.00 Openmep Key AC791/AC790S, NTGX55_10.25.15.02 MR5100 Alternative + 0xC5, 0x50, 0x40, 0xDA, 0x23, 0xE8, 0xF4, 0x4C, 0x29, 0xE9, 0x07, 0xDE, 0x24, 0xE5, 0x2C, 0x1D, + # 17 NTG9X35C_02.08.29.00 Openlock Key AC791/AC790S Old + 0xF0, 0x14, 0x55, 0x0D, 0x5E, 0xDA, 0x92, 0xB3, 0xA7, 0x6C, 0xCE, 0x84, 0x90, 0xBC, 0x7F, 0xED, + # 18 NTG9X35C_02.08.29.00 Openlock Key AC791/AC790S, NTGX55_10.25.15.02 MR5100 Alternative + 0x78, 0x19, 0xC5, 0x6D, 0xC3, 0xD8, 0x25, 0x3E, 0x51, 0x60, 0x8C, 0xA7, 0x32, 0x83, 0x37, 0x9D, + # 19 SWI9X06Y_02.14.04.00 Openmep Key WP77xx + 0x12, 0xF0, 0x79, 0x6B, 0x19, 0xC7, 0xF4, 0xEC, 0x50, 0xF3, 0x8C, 0x40, 0x02, 0xC9, 0x43, 0xC8, + # 20 SWI9X06Y_02.14.04.00 Openlock Key WP77xx + 0x49, 0x42, 0xFF, 0x76, 0x8A, 0x95, 0xCF, 0x7B, 0xA3, 0x47, 0x5F, 0xF5, 0x8F, 0xD8, 0x45, 0xE4, + # 21 NTGX55 Openmep Key, NTGX55_10.25.15.02 MR5100 + 0xF8, 0x1A, 0x3A, 0xCC, 0xAA, 0x2B, 0xA5, 0xE8, 0x8B, 0x53, 0x5A, 0x55, 0xB9, 0x65, 0x57, 0x98, + # 22 NTGX55 Openlock Key, NTGX55_10.25.15.02 MR5100 + 0x54, 0xC9, 0xC7, 0xA4, 0x02, 0x1C, 0xB0, 0x11, 0x05, 0x22, 0x39, 0xB7, 0x84, 0xEF, 0x16, 0xCA, + # 23 NTG9X15A Openlock Key, NTG9X15A_01.08.02.00 + 0xC7, 0xE6, 0x39, 0xFE, 0x0A, 0xC7, 0xCA, 0x4D, 0x49, 0x8F, 0xD8, 0x55, 0xEB, 0x1A, 0xCD, 0x8A + # 24 NTG9X15A Openlock Key, NTG9X15A_01.08.02.00 + ]) + + +class SierraGenerator(): + tbl = bytearray() + rtbl = bytearray() + + def __init__(self): + for i in range(0, 0x14): + self.rtbl.append(0x0) + for i in range(0, 0x100): + self.tbl.append(0x0) + + def run(self, devicegeneration, challenge, type): + challenge = bytearray(unhexlify(challenge)) + + self.devicegeneration = devicegeneration + if not devicegeneration in prodtable: + print("Sorry, " + devicegeneration + " not supported.") + exit(0) + + mepid = prodtable[devicegeneration]["openmep"] + cndid = prodtable[devicegeneration]["opencnd"] + lockid = prodtable[devicegeneration]["openlock"] + clen = prodtable[devicegeneration]["clen"] + if len(challenge) < clen: + for i in range(0, clen - len(challenge)): + challenge.append(0) + + challengelen = len(challenge) + if type == 0: # lockkey + idf = lockid + elif type == 1: # mepkey + idf = mepid + elif type == 2: # cndkey + idf = cndid + + key = keytable[idf * 16:(idf * 16) + 16] + resp = self.SierraKeygen(challenge, key, challengelen, 16)[:challengelen] + resp = hexlify(resp).decode('utf-8').upper() + return resp + + def selftest(self): + challenge = "8101A18AB3C3E66A" # Verified, EM7305 + devicegeneration = "MDM9x15" # MSM9200 + openlock = self.run(devicegeneration, challenge, 0) + if openlock != 'D1E128FCA8A963ED': + return False + + challenge = "BE96CBBEE0829BCA" # Verified + devicegeneration = "MDM9x40" + openlock = self.run(devicegeneration, challenge, 0) + if openlock != '1033773720F6EE66': + return False + + devicegeneration = "MDM9x30" + openlock = self.run(devicegeneration, challenge, 0) + if openlock != '1E02CE6A98B7DD2A': + return False + + devicegeneration = "MDM9x50" + openlock = self.run(devicegeneration, challenge, 0) + if openlock != '32AB617DB4B1C205': + return False + + devicegeneration = "MDM9x06" + openlock = self.run(devicegeneration, challenge, 0) + if openlock != '28D718CCD669DEDE': + return False + + devicegeneration = "MDM9x07" + openlock = self.run(devicegeneration, challenge, 0) + if openlock != 'F5A4C9A0D402E34E': + return False + + devicegeneration = "MDM8200" + openlock = self.run(devicegeneration, challenge, 0) + if openlock != 'EE702212D9C12FAB': + return False + + devicegeneration = "MDM9200_V1" + openlock = self.run(devicegeneration, challenge, 0) + if openlock != 'A9A4E76E2653F753': + return False + + devicegeneration = "MDM9200_V2" + openlock = self.run(devicegeneration, challenge, 0) + if openlock != '8B0FAB4B6F81B080': + return False + + devicegeneration = "MDM9200_V3" + openlock = self.run(devicegeneration, challenge, 0) + if openlock != '4A69AD8A69F390E0': + return False + + devicegeneration = "MDM9x30_V1" + openlock = self.run(devicegeneration, challenge, 0) + if openlock != '6A5E4C9CBCBDA7DC': + return False + + challenge = "BE96CBBEE0829BCA" # Verified + devicegeneration = "MDM9200" + openlock = self.run(devicegeneration, challenge, 0) + if openlock != 'EEDBF8BFF8DAE346': + return False + + challenge = "20E253156762DACE" # Verified + devicegeneration = "SDX55" + openlock = self.run(devicegeneration, challenge, 0) + if openlock != '03940D7067145323': + return False + + challenge = "2387885E7D290FEE" # Verified + devicegeneration = "MDM9x15A" + openlock = self.run(devicegeneration, challenge, 0) + if openlock != '676E10308BF05EE3': + return False + + return True + + def SierraPreInit(self, counter, key, keylen, challengelen, mcount): + if counter != 0: + tmp2 = 0 + i = 1 + while i < counter: + i = 2 * i + 1 + while True: + tmp = mcount + mcount = tmp + 1 + challengelen = (key[tmp & 0xFF] + self.tbl[(challengelen & 0xFF)]) & 0xFF + if mcount >= keylen: + mcount = 0 + challengelen = ((challengelen & 0xFF) + keylen) & 0xFF + tmp2 = tmp2 + 1 + tmp3 = ((challengelen & 0xFF) & i) & 0xFF + if tmp2 >= 0xB: + tmp3 = counter % tmp3 + if tmp3 <= counter: + break + counter = tmp3 & 0xFF + return [counter, challengelen, mcount] + + def SierraInit(self, key, keylen): + if keylen == 0 or keylen > 0x20: + retval = [0, keylen] + elif keylen >= 1 and keylen <= 0x20: + for i in range(0, 0x100): + self.tbl[i] = i & 0xFF + mcount = 0 + cl = keylen & 0xffffff00 + i = 0xFF + while i > -1: + t, cl, mcount = self.SierraPreInit(i, key, keylen, cl, mcount) + m = self.tbl[i] + self.tbl[i] = self.tbl[(t & 0xff)] + i = i - 1 + self.tbl[(t & 0xFF)] = m + self.rtbl[0] = self.tbl[prodtable[self.devicegeneration]["init"][0]] if \ + prodtable[self.devicegeneration]["init"][0] != 0 else self.tbl[(cl & 0xFF)] + self.rtbl[1] = self.tbl[prodtable[self.devicegeneration]["init"][1]] if \ + prodtable[self.devicegeneration]["init"][1] != 0 else self.tbl[(cl & 0xFF)] + self.rtbl[2] = self.tbl[prodtable[self.devicegeneration]["init"][2]] if \ + prodtable[self.devicegeneration]["init"][2] != 0 else self.tbl[(cl & 0xFF)] + self.rtbl[3] = self.tbl[prodtable[self.devicegeneration]["init"][3]] if \ + prodtable[self.devicegeneration]["init"][3] != 0 else self.tbl[(cl & 0xFF)] + self.rtbl[4] = self.tbl[prodtable[self.devicegeneration]["init"][4]] if \ + prodtable[self.devicegeneration]["init"][4] != 0 else self.tbl[(cl & 0xFF)] + retval = [1, keylen] + return retval + + def sierra_calc8F(self, challenge, a=0, b=1, c=2, d=3, e=4, ret=0, ret2=2): + # MDM9200 + self.rtbl[b] = (self.rtbl[b] + self.tbl[(self.rtbl[d] & 0xFF)]) & 0xFF + uVar2 = self.rtbl[c] & 0xFF + bVar1 = self.tbl[uVar2] + uVar4 = self.rtbl[b] & 0xFF + self.tbl[uVar2] = self.tbl[uVar4] + self.rtbl[d] = (self.rtbl[d] + 1) & 0xFF + uVar5 = self.rtbl[a] & 0xFF + self.tbl[uVar4] = self.tbl[uVar5] + uVar3 = self.rtbl[d] & 0xFF + self.tbl[uVar5] = self.tbl[uVar3] + self.tbl[uVar3] = bVar1 + self.rtbl[ret] = challenge # c + self.rtbl[ret2] = self.tbl[self.tbl[(self.tbl[(self.rtbl[e] + self.tbl[bVar1]) & 0xFF] + ( + self.tbl[uVar5] & 0xFF) + (self.tbl[uVar2] & 0xFF) & 0xff) & 0xFF] & 0xFF] ^ self.tbl[ + ((self.tbl[uVar4] & 0xFF) + (bVar1 & 0xff)) & 0xFF] ^ challenge # a + self.rtbl[e] = (self.rtbl[e] + self.tbl[bVar1]) & 0xFF + return self.rtbl[ret2] & 0xFF # a + + def SierraAlgo(self, challenge, a=0, b=1, c=2, d=3, e=4, ret=3, ret2=1, flag=1): # M9x15 + v6 = self.rtbl[e] + v0 = (v6 + 1) & 0xFF + self.rtbl[e] = v0 + self.rtbl[c] = (self.tbl[v6 + flag & 0xFF] + self.rtbl[c]) & 0xFF + v4 = self.rtbl[c] & 0xFF + v2 = self.rtbl[b] & 0xFF + v1 = self.tbl[(v2 & 0xFF)] + self.tbl[(v2 & 0xFF)] = self.tbl[(v4 & 0xFF)] + v5 = self.rtbl[d] & 0xFF + self.tbl[(v4 & 0xFF)] = self.tbl[(v5 & 0xFF)] + self.tbl[(v5 & 0xFF)] = self.tbl[(v0 & 0xFF)] + self.tbl[v0] = v1 & 0xFF + u = self.tbl[(self.tbl[( + self.tbl[((self.rtbl[a] + self.tbl[(v1 & 0xFF)]) & 0xFF)] + self.tbl[(v5 & 0xFF)] + self.tbl[ + (v2 & 0xFF)] & 0xff)] & 0xFF)] + v = self.tbl[((self.tbl[(v4 & 0xFF)] + v1) & 0xFF)] + self.rtbl[ret] = u ^ v ^ challenge + self.rtbl[a] = (self.tbl[(v1 & 0xFF)] + self.rtbl[a]) & 0xFF + self.rtbl[ret2] = challenge & 0xFF + return self.rtbl[ret] & 0xFF + + def SierraFinish(self): + for i in range(0, 0x100): + self.tbl[i] = 0 + self.rtbl[0] = 0 + self.rtbl[1] = 0 + self.rtbl[2] = 0 + self.rtbl[3] = 0 + self.rtbl[4] = 0 + return 1 + + def SierraKeygen(self, challenge, key, challengelen, keylen): + resultbuffer = bytearray() + for i in range(0, 0x100 + 1): + resultbuffer.append(0x0) + ret, keylen = self.SierraInit(key, keylen) + if ret: + for i in range(0, challengelen): + exec(prodtable[self.devicegeneration]["run"]) # uses challenge + self.SierraFinish() + return resultbuffer + + +class connection: + def __init__(self, port=""): + self.serial = None + self.tn = None + self.connected = False + if port == "": + port = self.detect(port) + if port == "": + self.tn = Telnet("192.168.1.1", 5510) + self.connected = True + if port != "": + self.serial = serial.Serial(port=port, baudrate=115200, bytesize=8, parity='N', stopbits=1, timeout=1) + self.connected = self.serial.is_open + + def detect(self, port): + if port == "": + for port in serial.tools.list_ports.comports(): + if port.vid == 0x1199: + portid = port.location[-1:] + if int(portid) == 3: + print("Detected Sierra Wireless device at: " + port.device) + return port.device + elif port.vid == 0x8046: + portid = port.location[-1:] + if int(portid) == 3: + print("Detected Netgear device at: " + port.device) + return port.device + + return "" + + def readreply(self): + info = [] + if self.serial is not None: + while (True): + tmp = self.serial.readline().decode('utf-8').replace('\r', '').replace('\n', '') + if "OK" in info: + return info + elif ("ERROR" in info) or info == "": + return -1 + info.append(tmp) + return info + + def send(self, cmd): + if self.tn is not None: + self.tn.write(bytes(cmd + "\r", 'utf-8')) + time.sleep(0.05) + data = "" + while True: + tmp = self.tn.read_eager() + if tmp != b"": + data += tmp.strip().decode('utf-8') + else: + break + return data.split("\r\n") + elif self.serial is not None: + self.serial.write(bytes(cmd + "\r", 'utf-8')) + time.sleep(0.05) + return self.readreply() + + def close(self): + if self.tn is not None: + self.tn.close() + self.connected = False + if self.serial is not None: + self.serial.close() + self.connected = False + +class SierraKeygen(metaclass=LogBase): + def __init__(self,cn,devicegeneration=None): + self.cn=cn + self.keygen = SierraGenerator() + print("Running self-test ...") + if self.keygen.selftest(): + print("PASSED!") + else: + print("FAILED!") + if devicegeneration==None: + self.detectdevicegeneration() + else: + self.devicegeneration=devicegeneration + + def detectdevicegeneration(self): + if self.cn.connected: + info = self.cn.send("ATI") + if info != -1: + revision = "" + model = "" + for line in info: + if "Revision" in line: + revision = line.split(":")[1].strip() + if "Model" in line: + model = line.split(":")[1].strip() + if revision != "": + if "9200" in revision: + devicegeneration = "MDM9200" #AC762S NTG9200H2_03.05.14.12ap + if "9X07" in revision: + devicegeneration = "MDM9x07" + elif "9X25" in revision: + if "NTG9X25C" in revision: + devicegeneration = "MDM9200" #AC781S NTG9X25C_01.00.57.00 + elif "9X15" in revision: + if "NTG9X15A" in revision: + devicegeneration = "MDM9x15A" #Aircard 779S + elif "NTG9X15C" in revision: + devicegeneration = "MDM9200" #AC770S NTG9X15C_01.18.02.00 + else: + devicegeneration = "MDM9x15" + elif "9X30" in revision: + if "NTG9X35C" in revision: #790S NTG9X35C_11.11.15.03 + devicegeneration = "MDM9x30_V1" + else: + devicegeneration = "MDM9x30" + elif "9X40" in revision: + devicegeneration = "MDM9x40" + elif "9X50" in revision: + if "NTG9X50" in revision: + devicegeneration = "MDM9x40" #MR1100,AC797S NTG9X50C_12.06.03.00 + else: + devicegeneration = "MDM9x50" + elif "9X06" in revision: + devicegeneration = "MDM9x06" + elif "X55" in revision: + if "NTGX55" in revision: #MR5100 NTGX55_10.25.15.02 + devicegeneration = "SDX55" + devicegeneration = "SDX55" + #Missing: + # SDX24 Sierra + # MR2100 NTGX24_10.17.03.00 + # SDX55 Sierra + # AC810S NTG9X40C_11.14.08.16 + # AC800S NTG9X40C_11.14.07.00 + self.devicegeneration=devicegeneration + else: + print("Error on getting ATI modem response. Wrong port? Aborting.") + self.cn.close() + exit(0) + + def openlock(self): + print("Device generation detected: " + self.devicegeneration) + #print("Sending AT!ENTERCND=\"A710\" request.") + #info = self.cn.send("AT!ENTERCND=\"A710\"") + #if info == -1: + # print("Uhoh ... invalid entercnd password. Aborting ...") + # return + print("Sending AT!OPENLOCK? request") + info = self.cn.send("AT!OPENLOCK?") + challenge = "" + if info != -1: + if len(info) > 2: + challenge = info[1] + else: + print("Error on AT!OPENLOCK? request. Aborting.") + return + if challenge == "": + print("Error: Couldn't get challenge. Aborting.") + return + resp = self.keygen.run(self.devicegeneration, challenge, 0) + print("Sending AT!OPENLOCK=\"" + resp + "\" response.") + info = self.cn.send("AT!OPENLOCK=\"" + resp + "\"") + if info == -1: + print("Damn. AT!OPENLOCK failed.") + else: + print("Success. Device is now engineer unlocked.") + return True + return False + +def main(args): + version = "1.3" + info = 'Sierra Wireless Generator ' + version + ' (c) B. Kerler 2019-2021' + parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,description=info) + + parser.add_argument( + '-openlock', '-l', + help='AT!OPENLOCK? modem response', + default="") + + parser.add_argument( + '-openmep', '-m', + help='AT!OPENMEP? modem response', + default="") + + parser.add_argument( + '-opencnd', '-c', + help='AT!OPENCND? modem response', + default="") + + parser.add_argument( + '-devicegeneration', '-d', + help='Device devicegeneration generation', + default="") + + parser.add_argument( + '-port', '-p', + help='use com port for auto unlock', + default="") + + parser.add_argument( + '-unlock', '-u', + help='use com port for openlock', + default=False, action='store_true') + + args = parser.parse_args() + + openlock = args.openlock + openmep = args.openmep + opencnd = args.opencnd + devicegeneration = args.devicegeneration + + if (devicegeneration == "" or (openlock == "" and openmep == "" and opencnd == "")) and not args.unlock: + print(info) + print("------------------------------------------------------------\n") + print("Usage: ./sierrakeygen [-l,-m,-c] [challenge] -d [devicegeneration]") + print("Example: ./sierrakeygen.py -l BE96CBBEE0829BCA -d MDM9200") + print("or: ./sierrakeygen.py -u for auto unlock") + print("or: ./sierrakeygen.py -u -p [portname] for auto unlock with given portname") + print("Supported devicegenerations :") + for key in infotable: + info = f"\t{key}:\t\t" + count = 0 + for item in infotable[key]: + count += 1 + if count > 15: + info += "\n\t\t\t\t\t" + count = 0 + info += item + "," + + info = info[:-1] + print(info) + exit(0) + + if devicegeneration == "" and not args.unlock: + print("You need to specific a device generation as well. Option -d") + exit(0) + if devicegeneration == "": + devicegeneration=None + if args.unlock: + cn = connection(args.port) + if cn.connected: + kg=SierraKeygen(cn,devicegeneration) + if kg.devicegeneration == "": + print("Unknown device generation. Please send me details :)") + else: + kg.openlock() + cn.close() + else: + kg = SierraKeygen(None, devicegeneration) + if openlock != "": + resp = kg.keygen.run(devicegeneration, openlock, 0) + print("AT!OPENLOCK=\"" + resp + "\"") + elif openmep != "": + resp = kg.keygen.run(devicegeneration, openmep, 1) + print("AT!OPENMEP=\"" + resp + "\"") + elif opencnd != "": + resp = kg.keygen.run(devicegeneration, opencnd, 2) + print("AT!OPENCND=\"" + resp + "\"") + + +if __name__ == '__main__': + main(sys.argv) diff --git a/edl/Windows/libusb-1.0.dll b/edlclient/Windows/libusb-1.0.dll similarity index 100% rename from edl/Windows/libusb-1.0.dll rename to edlclient/Windows/libusb-1.0.dll diff --git a/edlclient/Windows/libusb32-1.0.dll b/edlclient/Windows/libusb32-1.0.dll new file mode 100644 index 0000000..fd7a941 Binary files /dev/null and b/edlclient/Windows/libusb32-1.0.dll differ diff --git a/edl/__init__.py b/edlclient/__init__.py similarity index 100% rename from edl/__init__.py rename to edlclient/__init__.py diff --git a/setup.py b/setup.py index c9f3187..fc1dbf6 100755 --- a/setup.py +++ b/setup.py @@ -3,11 +3,11 @@ from setuptools import setup, find_packages import os setup( - name='edl', - version='3.5', + name='edlclient', + version='3.53', packages=find_packages(), long_description=open("README.md").read(), - scripts=['edl.py','diag.py','modem/sierrakeygen.py','modem/boottodwnload.py','modem/enableadb.py','Loaders/fhloaderparse.py','Loaders/beagle_to_loader.py'], + scripts=['edl','edlclient/Tools/qc_diag','edlclient/Tools/sierrakeygen','edlclient/Tools/boottodwnload','edlclient/Tools/enableadb','edlclient/Tools/fhloaderparse','edlclient/Tools/beagle_to_loader'], data_files = ['LICENSE','README.md'], long_description_content_type="text/markdown", url='https://github.com/bkerler/edl', diff --git a/sierrakeygen_README.md b/sierrakeygen_README.md new file mode 100755 index 0000000..2beb277 --- /dev/null +++ b/sierrakeygen_README.md @@ -0,0 +1,344 @@ +# Challenge/Response Generator for Sierra Wireless Cards V1.2 +(c) B. Kerler 2019-2020 +MIT License + +## Why + +- For enabling development mode and tests +- For setting band options if locked + +## Supported devices + "MDM8200": ["M81A", "M81B", "AC880", "AC881", "MC8780", "MC8781", "AC880E", "AC881E", "EM8780", "EM8781", + "MC8780V", "MC8781V", "MC8700", "AC308U"], + "MDM9200": ["AC710", "MC8775", "MC8775V", "AC875", "MC8700", "AC313U", "MC8801", "MC7700", "MC7750", "MC7710", + "EM7700"], + "MDM9200_V1": ["AC710", "MC8775", "MC8775V", "AC875", "MC8700", "AC313U", "MC8801", "MC7700", "MC7750", + "MC7710", "EM7700"], + "MDM9200_V2": ["AC775", "PC7200"], + "MDM9x15": ["SWI9X15C", "AR7550", "AR7552", "AR7554", "EM7355", "EM7655", "MC7354", "WP7100", "WP7102", "WP7104", + "MC7305", "EM7305", "MC8805", "EM8805", "MC7350", "MC7350-L", "MC7802", "MC7304", "AR7556", "AR7558", + "WP75xx", "WP85xx", "WP8548", "WP8548G", "AC340U"], + "MDM9x30": ["EM7455", "MC7455", "EM7430", "MC7430"], + "MDM9x30_V1": ["Netgear AC790S/AC791L"], + "MDM9x40": ["AC815s", "AC785s","Netgear MR1100"], + "MDM9x50": ["EM7565", "EM7565-9", "EM7511"], + "MDM9x06": ["WP77xx"], + "MDM9x07": ["SWI9X07Y", "WP76xx"] + +## Usage + +- Get a specific challenge for your task from the modem + ``` + AT!OPENLOCK? + ``` + + or + + ``` + AT!OPENMEP? + ``` + + or + + ``` + AT!OPENCND? + ``` + +- Run generator: + For automatic unlock, use -u: + ```bash + ~> sierrakeygen -u + ``` + + For AT!OPENLOCK use -l, for AT!OPENMEP use -m and for AT!OPENCND use -c accordingly + (here challenge is BE96CBBEE0829BCA and device generation is MDM9200) + ```bash + ~> sierrakeygen -l BE96CBBEE0829BCA -d MDM9200 + ``` + +- Send generated response back to the modem + + ``` + AT!OPENLOCK=[response from generator] + ``` + + or + + ``` + AT!OPENMEP=[response from generator] + ``` + + or + + ``` + AT!OPENCND=[response from generator] + ``` + +- Open up a terminal and enable enhanced commands (generic pwd is "A710") + + ``` + AT!ENTERCND=A710 + ``` + + Other known pwds are (thx to 4PDA): + + ``` + AC815s: "fallow" + MR1100: “lindeman” + AC790-Telstra: "sunflower" + LB1111: "granville" + AC810-100EUS: "whistler" + AC810S-1P1PLS: "seymour" + AC810S-1TLAUS: "grouse" + AC810S-1RDQAS: "cypress" + AC790-100EUS: "lavender" + AC790S-1SPSUS : "bluebell" + ``` + + After unlocking via AT!OPENLOCK, you can also set a new password via AT!SETCND="pwd", + in case the password isn't known + +## Help + + ```bash + ~> sierrakeygen -h + ``` + +## Remarks + +- MDM9200/MDM9x15/MDM9x30/MDM9x40/MDM9x50 confirmed to work + +- For AC785/AC790/AC810/MR1100, you can access the serial port via tcp: + + ```bash + HostName: 192.168.1.1 + Port: 5510 + ConnectionType: Telnet + ``` + +- Get firmware details : + + ``` + ATI + AT!PACKAGE? + ``` + +- Get flash memory info : + + ``` + AT!FMBADBLOCKS? + AT!BSINFO + ``` + +- Set password for opencnd: + + ``` + AT!SETCND="[pwd]" + ``` + + Example: + + ``` + AT!SETCND="A710" + ``` + +- For band selection, see possible bands via : + + ``` + AT!BAND=? + ``` + +- Set Modem to use all bands : + + ``` + AT!BAND=00 + ``` + +- Set Modem to only use LTE : + + ``` + AT!SELRAT=06 + ``` + +- Reboot modem and save settings : + + ``` + AT!RESET + ``` + +- To add a new band : + + ``` + AT!BAND=[index],"[name]",0,8000000 + ``` + + Examples: + + ``` + AT!BAND=03,"LTE B28 700",0,8000000 + AT!BAND=04,"LTE B1 2100",0,1 + AT!BAND=05,"LTE B3 1800",0,4 + AT!BAND=06,"LTE B7 2600",0,40 + AT!BAND=07,"LTE B8 900",0,80 + ``` + +- To remove a band : + + ``` + AT!BAND=[index],"",0,0 + ``` + + Example: + + ``` + AT!BAND=03,"",0,0 + ``` + + - Get signal info : + + ``` + AT!GSTATUS? + ``` + + - Get partition info : + + ``` + AT!PARTINFO? + ``` + + - Switch to qc download mode : + + ``` + AT!BOOTHOLD + AT!QPSTDLOAD + ``` + + - Show Secure Boot info : + + ``` + AT!SECBOOTCFG? Show Secure Boot config + AT!SECBOOTPKHASH? Show Secure Boot PKHASH + ``` + + - Show Product Info : + + ``` + AT!USBPRODUCT? + Sierra Wireless EM7565 Qualcomm® Snapdragon™ X16 LTE-A + + AT!USBMANUFACTURER? + Sierra Wireless, Incorporated + ``` + + - Set vid and pid : + + ``` + AT!USBVID=1199 Set usb vid of 0x1199 + AT!USBPID=9091,9090 Set usb pid (app=0x9091, boot=0x9090) + ``` + + - Set product identifier : + + ``` + AT!PRIID? Show product identifier + PRI Part Number: 9907344 + Revision: 002.001 + Customer: Generic-M2M + Carrier PRI: 9999999_9907259_SWI9X50C_01.08.04.00_00_GENERIC_002.012_000 + + + AT!USBPID="9907344","002.001","Generic-M2M" Set PartNr, Revision and Customer + ``` + + - Set preferred modem image : + + ``` + AT!IMPREF="GENERIC" + + AT!IMAGE=? + AT!IMAGE=[,[,[,"",""]]] + op - 0:delete 1:list 2:get max num images + type - 0:FW 1:CONFIG + slot - FW slot index - none implies all slots + AT!IMAGE?[[,]] + + AT!IMAGE=0,0,1 Op=0 (Delete), Type=0 (FW), Slot Index=1 + ``` + + - Reset to factor settings : + + ``` + AT!RMARESET=1 + ``` + + - Lenovo laptop whitelist bypass : + + ``` + AT!ENTERCND="A710" + AT!CUSTOM="FASTENUMEN",2 Disable fast enumeration and only show up after init + AT!PCOFFEN=2 Ignore W_DISABLE pin + AT!USBSPEED=0 Force usb2 mode + AT!RESET + ``` + + - Set usb composition (diag, nmea, modem, mbim, same as USBCOMP=8): + + ``` + AT!USBCOMP=1,3,0000100D + ``` + + - List custom settings : + + ``` + AT!CUSTOM? + ``` + + - Enable telnet (after sending valid openlock request) + + ``` + at!custom="TELNETENABLE",1 + ``` + + - Enable adb (after sending valid openlock request, here: MC7304/AC810) + + ``` + AT!CUSTOM="ADBENABLE", 1 + ``` + + Regulary, tcp port 5555 is used for adb + + ``` + adb tcpip 5555 + adb connect 192.168.1.1 + ``` + + - Enable telnet (after sending valid openlock request, here: MR1100) + + ``` + AT!TELEN=1 + AT!CUSTOM="RDENABLE", 1 + AT!CUSTOM="TELNETENABLE", 1 + ``` + then reboot the device. Afterwards, telnet should be available on MR1100 via 192.168.1.1:23 + + - Flash firmware : + + ```bash + ~ > sudo apt install libqmi-glib5 libqmi-proxy libqmi-utils -y + ~ > qmi-firmware-update --update -d 1199:9091 firmware.cwe firmware.nvu 1199:9091 is usb vid/pid + ``` + +## Other useful links + +- https://github.com/danielewood/sierra-wireless-modems/blob/master/README.md + +## ToDo + +- Nothing :) + +## License + +Published under MIT license +Additional license limitations: No use in commercial products without prior permit by me. + +Enjoy !