New structure

This commit is contained in:
Bjoern Kerler 2021-10-25 17:57:14 +02:00
parent 17d96e8953
commit d13f2d22c8
46 changed files with 3919 additions and 143 deletions

View file

@ -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 *

128
README.md
View file

@ -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 <xmlstring>``` -> To send own xml string, example :
```python edl.py rawxml "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><data><response value=\"ACK\" /></data>```
- ```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 <xmlstring>``` -> To send own xml string, example :
```edl rawxml "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><data><response value=\"ACK\" /></data>```
- ```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.

362
edl Executable file
View file

@ -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 <directory> [--memory=memtype] [--lun=lun] [--genxml] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid]
edl r <partitionname> <filename> [--memory=memtype] [--sectorsize==bytes] [--lun=lun] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid]
edl rl <directory> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skip=partnames] [--genxml] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl rf <filename> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid]
edl rs <start_sector> <sectors> <filename> [--lun=lun] [--sectorsize==bytes] [--memory=memtype] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid]
edl w <partitionname> <filename> [--partitionfilename=filename] [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skipwrite] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value]
edl wl <directory> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skip=partnames] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value]
edl wf <filename> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value]
edl ws <start_sector> <filename> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skipwrite] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value]
edl e <partitionname> [--memory=memtype] [--skipwrite] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value]
edl es <start_sector> <sectors> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skipwrite] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value]
edl ep <partitionname> <sectors> [--memory=memtype] [--skipwrite] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value]
edl footer <filename> [--memory=memtype] [--lun=lun] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid]
edl peek <offset> <length> <filename> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid]
edl peekhex <offset> <length> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl peekdword <offset> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl peekqword <offset> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl memtbl <filename> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl poke <offset> <filename> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl pokehex <offset> <data> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl pokedword <offset> <data> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl pokeqword <offset> <data> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl memcpy <offset> <size> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl secureboot [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl pbl <filename> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl qfp <filename> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid]
edl getstorageinfo [--loader=filename] [--memory=memtype] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid]
edl setbootablestoragedrive <lun> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid]
edl send <command> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid]
edl xml <xmlfile> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value]
edl rawxml <xmlstring> [--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 <command> <options> [--memory=memtype] [--lun=lun] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value]
edl qfil <rawprogram> <patch> <imagedir> [--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
# <rawprogram> : program config xml, such as rawprogram_unsparse.xml or rawprogram*.xml
# <patch> : patch config xml, such as patch0.xml or patch*.xml
# <imagedir> : directory name of image files
Options:
--loader=filename Use specific EDL loader, disable autodetection [default: None]
--vid=vid Set usb vendor 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["<mode>"] = 1
else:
options["<mode>"] = 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()

View file

@ -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):

View file

@ -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

View file

@ -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):

View file

@ -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):

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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):

View file

@ -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):

View file

@ -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):

View file

@ -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

View file

@ -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():

View file

@ -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("<I", data[idx:idx+4])[0]
if cmd==0x03:
cmd,tlen,slen,offset,length=unpack("<IIIII",data[idx:idx+0x14])
elif cmd==0x12:
cmd, tlen, slen, offset, length = unpack("<IIQQQ", data[idx:idx + 0x20])
data = data[idx + 0x14:]
print("Offset : %08X Length: %08X" %(offset,length))
while len(outdata)<offset+length:
outdata.append(0xFF)
outdata[offset:offset+length]=data[:length]
i+=1
wf.write(outdata)
print("Done.")
if __name__=="__main__":
main()

282
edlclient/Tools/boottodwnload Executable file
View file

@ -0,0 +1,282 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# (c) B.Kerler 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 time
import serial
import serial.tools.list_ports
import argparse
import requests
from telnetlib import Telnet
import usb.core
from enum import Enum
import os, sys, inspect
current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
sys.path.insert(0, current_dir)
try:
from edlclient.Tools.qc_diag import qcdiag
except ImportError as e:
print(current_dir)
from qc_diag import qcdiag
pass
try:
from edlclient.Library.utils import LogBase
except ImportError as e:
from Library.utils import LogBase
class vendor(Enum):
sierra = 0x1199
quectel = 0x2c7c
zte = 0x19d2
netgear = 0x0846
telit = 0x413c
class deviceclass:
vid=0
pid=0
def __init__(self,vid,pid):
self.vid=vid
self.pid=pid
class connection(metaclass=LogBase):
def __init__(self, port=""):
self.serial = None
self.tn = None
self.connected = False
if port == "":
port = self.detect(port)
if port == "":
try:
self.tn = Telnet("192.168.1.1", 5510, 5)
self.connected = True
except:
self.connected = False
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 waitforusb(self,vid,pid):
timeout = 0
while timeout < 10:
for device in self.detectusbdevices():
if device.vid == vid:
if device.pid == pid:
return True
time.sleep(1)
timeout += 1
return False
def websend(self,url):
headers = {'Referer': 'http://192.168.0.1/index.html', 'Accept-Charset': 'UTF-8'}
r = requests.get(url, headers=headers)
if b"FACTORY:ok" in r.content or b"success" in r.content:
print(f"Detected a ZTE in web mode .... switching mode success (convert back by sending \"AT+ZCDRUN=F\" via AT port)")
return self.waitforusb(vendor.zte.value,0x0016)
return False
def getserialports(self):
return [port for port in serial.tools.list_ports.comports()]
def detectusbdevices(self):
dev = usb.core.find(find_all=True)
ids=[deviceclass(cfg.idVendor,cfg.idProduct) for cfg in dev]
return ids
def detect(self, port):
vendortable={
0x1199:["Sierra Wireless",3],
0x2c7c:["Quectel",3],
0x19d2:["ZTE",2],
0x0846:["Netgear", 2],
0x413c:["Telit",0]
}
mode="Unknown"
for device in self.detectusbdevices():
if device.vid==vendor.zte.value:
if device.pid==0x0016:
print(f"Detected a {vendortable[device.vid][0]} device with pid {hex(device.pid)} in Diag mode")
mode="AT"
break
elif device.pid==0x1403:
print(f"Detected a {vendortable[device.vid][0]} device with pid {hex(device.pid)} in Web mode")
mode="Web"
# url = 'http://192.168.0.1/goform/goform_set_cmd_process?goformId=USB_MODE_SWITCH&usb_mode=1' #adb
url = 'http://192.168.0.1/goform/goform_process?goformId=MODE_SWITCH&switchCmd=FACTORY'
if self.websend(url):
mode="AT"
break
elif device.vid==vendor.telit.value:
if device.pid==0x81d7:
print(f"Detected a {vendortable[device.vid][0]} device with pid {hex(device.pid)} in Diag mode")
print("Sending download mode command")
interface = 5
diag = qcdiag(loglevel=self.__logger.level, portconfig=[[0x413c, 0x81d7, interface]])
if diag.connect():
data=diag.hdlc.receive_reply()
res = diag.send(b"\x4b\x65\x01\x00")
if res[0]==0x4B:
print("Sending download mode succeeded")
diag.disconnect()
break
if mode=="AT" or mode=="Unknown":
for port in self.getserialports():
if port.vid in vendortable:
portid = port.location[-1:]
if int(portid) == vendortable[port.vid][1]:
print(f"Detected a {vendortable[port.vid][0]} at interface at: " + port.device)
return port.device
return ""
def readreply(self):
info = []
timeout=0
if self.serial is not None:
while True:
tmp = self.serial.readline().decode('utf-8').replace('\r', '').replace('\n', '')
if "OK" in tmp:
info.append(tmp)
return info
elif "ERROR" in tmp:
return -1
if tmp!="":
info.append(tmp)
else:
timeout+=1
if timeout==20:
break
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
def ati(self):
data={}
info = self.send("ATI")
if info != -1:
for line in info:
if "Revision" in line:
data["revision"] = line.split(":")[1].strip()
if "Model" in line:
data["model"] = line.split(":")[1].strip()
if "Quectel" in line:
data["vendor"] = "Quectel"
if "Manufacturer" in line:
data["manufacturer"]=line.split(":")[1].strip()
if "Sierra Wireless" in data["manufacturer"]:
data["vendor"]="Sierra Wireless"
elif "ZTE CORPORATION" in data["manufacturer"]:
data["vendor"]="ZTE"
elif "Netgear" in data["manufacturer"]:
data["vendor"]="Netgear"
elif "Telit" in data["manufacturer"]:
data["vendor"]="Telit"
return data
class dwnloadtools(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 download mode command")
print(cn.send("AT!BOOTHOLD\r"))
print(cn.send('AT!QPSTDLOAD\r'))
print("Done switching to download mode")
elif info["vendor"]=="Quectel":
print("Sending download mode command")
interface=0
diag = qcdiag(loglevel=self.__logger.level, portconfig=[[0x2c7c,0x0125,interface]])
if diag.connect():
diag.hdlc.receive_reply()
res = diag.send(b"\x4b\x65\x01\x00")
diag.disconnect()
print("Done switching to download mode")
elif info["vendor"]=="Telit":
print("Sending download mode command")
interface=0
diag = qcdiag(loglevel=self.__logger.level, portconfig=[[0x2c7c,0x0125,interface]])
if diag.connect():
diag.hdlc.receive_reply()
res = diag.send(b"\x4b\x65\x01\x00")
diag.disconnect()
print("Done switching to download mode")
elif info["vendor"]=="ZTE":
print("Sending download mode command")
interface=0
diag = qcdiag(loglevel=self.__logger.level, portconfig=[[0x19d2,0x0016, interface]])
if diag.connect():
diag.hdlc.receive_reply()
res = diag.send(b"\x4b\x65\x01\x00")
if res[0]==0x4B:
print("Done switching to ENANDPRG mode")
else:
res = diag.send(b"\x3a")
if res[0]==0x3A:
while True:
state=cn.waitforusb(vendor.zte.value,0x0076)
if not state:
diag.disconnect()
if diag.connect():
res = diag.send(b"\x3a")
else:
break
if state:
print("Done switching to NANDPRG mode")
else:
print("Failed switching to download mode")
diag.disconnect()
cn.close()
def main():
version = "1.1"
info = 'Modem Gimme-EDL ' + 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 auto unlock',
default="")
parser.add_argument(
'-logfile', '-l',
help='use logfile for debug log',
default="")
args = parser.parse_args()
dw=dwnloadtools()
dw.run(args)
if __name__=="__main__":
main()

347
edlclient/Tools/enableadb Executable file
View file

@ -0,0 +1,347 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# (c) B.Kerler 2020-2021 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 time
from telnetlib import Telnet
import serial
import serial.tools.list_ports
import argparse
import requests
import hashlib
try:
from edlclient.Tools.qc_diag import qcdiag
except ImportError as e:
pass
import usb.core
from enum import Enum
import crypt
try:
from edlclient.Tools.sierrakeygen import SierraKeygen
except ImportError:
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 sierrakeygen import SierraKeygen
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 edlclient.Library.utils import LogBase
class vendor(Enum):
sierra = 0x1199
quectel = 0x2c7c
zte = 0x19d2
telit = 0x413c
netgear = 0x0846
class deviceclass:
vid=0
pid=0
def __init__(self,vid,pid):
self.vid=vid
self.pid=pid
class connection:
def __init__(self, port=""):
self.serial = None
self.tn = None
self.connected = False
if port == "":
port = self.detect(port)
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 waitforusb(self,vid,pid):
timeout = 0
while timeout < 10:
for device in self.detectusbdevices():
if device.vid == vid:
if device.pid == pid:
return True
time.sleep(1)
timeout += 1
return False
def websend(self,url):
headers = {'Referer': 'http://192.168.0.1/index.html', 'Accept-Charset': 'UTF-8'}
r = requests.get(url, headers=headers)
if b"FACTORY:ok" in r.content or b"success" in r.content:
print(f"Detected a ZTE in web mode .... switching mode success (convert back by sending \"AT+ZCDRUN=F\" via AT port)")
return self.waitforusb(vendor.zte.value,0x0016)
return False
def getserialports(self):
return [port for port in serial.tools.list_ports.comports()]
def detectusbdevices(self):
dev = usb.core.find(find_all=True)
ids=[deviceclass(cfg.idVendor,cfg.idProduct) for cfg in dev]
return ids
def detect(self, port):
atvendortable={
0x1199:["Sierra Wireless",3],
0x2c7c:["Quectel",3],
0x19d2:["ZTE",2],
0x413c:["Telit",3],
0x0846:["Netgear",2],
0x04E8:["Samsung", -1]
}
mode="Unknown"
for device in self.detectusbdevices():
if device.vid==vendor.zte.value:
if device.pid==0x0016:
print(f"Detected a {atvendortable[device.vid][0]} device with pid {hex(device.pid)} in AT mode")
mode="AT"
break
elif device.pid==0x1403:
print(f"Detected a {atvendortable[device.vid][0]} device with pid {hex(device.pid)} in Web mode")
mode="Web"
url = 'http://192.168.0.1/goform/goform_set_cmd_process?goformId=USB_MODE_SWITCH&usb_mode=6'
if self.websend(url):
print("Successfully enabled adb.")
break
elif device.vid==vendor.netgear.value:
try:
# vid 0846, netgear mr1100, mr5100
self.tn = Telnet("192.168.1.1", 5510, 5)
self.connected = True
except:
self.connected = False
if mode=="AT" or mode=="Unknown":
for port in self.getserialports():
if port.vid in atvendortable:
portid = port.location[-1:]
if int(portid) == atvendortable[port.vid][1]:
print(f"Detected a {atvendortable[port.vid][0]} at interface at: " + port.device)
return port.device
return ""
def readreply(self):
info = []
timeout=0
if self.serial is not None:
while True:
tmp = self.serial.readline().decode('utf-8').replace('\r', '').replace('\n', '')
if "OK" in tmp:
info.append(tmp)
return info
elif "ERROR" in tmp:
return -1
if tmp!="":
info.append(tmp)
else:
timeout+=1
if timeout==20:
break
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
if "ERROR" in data:
return -1
return data.split("\r\n")
elif self.serial is not None:
self.serial.write(bytes(cmd + "\r", 'utf-8'))
time.sleep(0.05)
resp=self.readreply()
return resp
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
def ati(self):
data={}
info = self.send("ATI")
if info != -1:
for line in info:
if "Revision" in line:
data["revision"] = line.split(":")[1].strip()
if "Model" in line:
data["model"] = line.split(":")[1].strip()
if "Quectel" in line:
data["vendor"] = "Quectel"
if "Manufacturer" in line:
data["manufacturer"]=line.split(":")[1].strip()
if "Sierra Wireless" in data["manufacturer"]:
data["vendor"]="Sierra Wireless"
elif "ZTE CORPORATION" in data["manufacturer"]:
data["vendor"]="ZTE"
elif "SIMCOM INCORPORATED" in data["manufacturer"]:
data["vendor"]="Simcom"
elif "Alcatel" in data["manufacturer"]:
data["vendor"]="Alcatel"
elif "Netgear" in data["manufacturer"]:
data["vendor"]="Netgear"
elif "SAMSUNG" in data["manufacturer"]:
data["vendor"]="Samsung"
info = self.send("AT+CGMI")
if info!=-1:
for line in info:
if "Quectel" in line:
data["vendor"] = "Quectel"
break
elif "Fibucom" in line:
data["vendor"]="Fibucom"
break
elif "Netgear" in line:
data["vendor"]="Netgear"
break
elif "SAMSUNG" in line:
data["vendor"]="Samsung"
break
info = self.send("AT+CGMR")
if info!=-1:
if len(info)>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()

411
edlclient/Tools/fhloaderparse Executable file
View file

@ -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("<IIIIIIIIII", memory[0xC:0xC + 40])
class Signed:
filename = ''
filesize = 0
oem_id = ''
model_id = ''
hw_id = ''
sw_id = ''
app_id = ''
sw_size = ''
qc_version = ''
image_variant = ''
oem_version = ''
pk_hash = ''
hash = b''
def grabtext(data):
i = len(data)
j = 0
text = ''
while i > 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("<I", mem_section[memsection.file_start_addr + 0x2C:memsection.file_start_addr + 0x2C + 0x4])[0]
md_offset = memsection.file_start_addr + 0x2C + 0x4
major, minor, sw_id, hw_id, oem_id, model_id, app_id = unpack("<IIIIIII",
mem_section[md_offset:md_offset + (7 * 4)])
sign_info.hw_id = "%08X" % hw_id
sign_info.sw_id = "%08X" % sw_id
sign_info.oem_id = "%04X" % oem_id
sign_info.model_id = "%04X" % model_id
sign_info.hw_id += sign_info.oem_id + sign_info.model_id
sign_info.app_id = "%08X" % app_id
md_offset += (7 * 4)
# v=unpack("<I", mem_section[md_offset:md_offset + 4])[0]
'''
rot_en=(v >> 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("<I", mm[md_offset:md_offset + 4])[0]
md_offset+=4
anti_rollback_version=unpack("<I", mm[md_offset:md_offset + 4])[0]
'''
signatureoffset = memsection.file_start_addr + 0x30 + md_size + code_size + signature_size
try:
if mem_section[signatureoffset] != 0x30:
print("Error on " + sign_info.filename + ", unknown signaturelength")
return None
except:
return None
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.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("<I", mem_section[0:4])[0]
if hdr&0xFFFFFF == 0x4C457F:
elfheader = elf(mem_section, signinfo.filename)
if 'memorylayout' in dir(elfheader):
memsection = elfheader.memorylayout[1]
try:
version = unpack("<I", mem_section[
memsection.file_start_addr + 0x04:memsection.file_start_addr + 0x04 + 0x4])[
0]
code_size = \
unpack("<I", mem_section[
memsection.file_start_addr + 0x14:memsection.file_start_addr + 0x14 + 0x4])[
0]
signature_size = \
unpack("<I", mem_section[
memsection.file_start_addr + 0x1C:memsection.file_start_addr + 0x1C + 0x4])[
0]
# cert_chain_size=unpack("<I", mem_section[memsection.file_start_addr + 0x24:memsection.file_start_addr + 0x24 + 0x4])[0]
except:
continue
if signature_size == 0:
print("%s has no signature." % filename)
copyfile(filename,
os.path.join(outputdir, "Unknown", filename[filename.rfind("/") + 1:].lower()))
continue
if version < 6: # MSM,MDM
signatureoffset = memsection.file_start_addr + 0x28 + code_size + signature_size
signinfo = extract_old_hdr(signatureoffset, signinfo, mem_section, code_size, signature_size)
if signinfo is None:
continue
filelist.append(signinfo)
elif version >= 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)

1
edlclient/Tools/qc_diag Symbolic link
View file

@ -0,0 +1 @@
qc_diag.py

1329
edlclient/Tools/qc_diag.py Executable file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
sierrakeygen.py

634
edlclient/Tools/sierrakeygen.py Executable file
View file

@ -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)

Binary file not shown.

View file

@ -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',

344
sierrakeygen_README.md Executable file
View file

@ -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>[,<type>[,<slot>[,"<build_id>","<unique_id>"]]]
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?[<op>[,<type>]]
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 !