mirror of
https://github.com/bkerler/edl.git
synced 2024-11-14 19:14:58 -05:00
Restructure
This commit is contained in:
parent
5db3070d06
commit
c11a1bc5a6
32 changed files with 10173 additions and 22 deletions
22
README.md
22
README.md
|
@ -33,10 +33,24 @@ sudo python3 -m pip install -r requirements.txt
|
|||
```
|
||||
|
||||
Windows:
|
||||
- Boot device into 9008 mode, install Qualcomm_Diag_QD_Loader_2016_driver.exe from Drivers\Windows
|
||||
- Use Zadig 2.5 or higher, list all devices, select QUSB_BULK device and replace
|
||||
driver with libusb >= 1.2.6.0 one (will replace original driver)
|
||||
- Get latest Zadig release [here] (https://zadig.akeo.ie/)
|
||||
#### Install python + git
|
||||
- Install python 3.9 and git
|
||||
- If you install python from microsoft store, "python setup.py install" will fail, but that step isn't required.
|
||||
- WIN+R ```cmd```
|
||||
|
||||
#### Grab files and install
|
||||
```
|
||||
git clone https://github.com/bkerler/edl
|
||||
cd edl
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
#### Get latest UsbDk 64-Bit
|
||||
- Install normal QC 9008 Serial Port driver (or use default Windows COM Port one, make sure no exclamation is seen)
|
||||
- Get usbdk installer (.msi) from [here](https://github.com/daynix/UsbDk/releases/) and install it
|
||||
- Test on device connect using "UsbDkController -n" if you see a device with pid 0x9008
|
||||
- Works fine under Windows 10 and 11 :D
|
||||
|
||||
|
||||
## Convert EDL loaders for automatic usage
|
||||
|
||||
|
|
26
edl.py
26
edl.py
|
@ -119,28 +119,18 @@ import logging
|
|||
import subprocess
|
||||
import re
|
||||
from docopt import docopt
|
||||
from Library.utils import LogBase
|
||||
from Library.usblib import UsbClass
|
||||
from Library.sahara import sahara
|
||||
from Library.streaming_client import streaming_client
|
||||
from Library.firehose_client import firehose_client
|
||||
from Library.streaming import Streaming
|
||||
from edl.Config.usb_ids import default_ids
|
||||
from edl.Library.utils import LogBase
|
||||
from edl.Library.usblib import UsbClass
|
||||
from edl.Library.sahara import sahara
|
||||
from edl.Library.streaming_client import streaming_client
|
||||
from edl.Library.firehose_client import firehose_client
|
||||
from edl.Library.streaming import Streaming
|
||||
from binascii import hexlify
|
||||
|
||||
args = docopt(__doc__, version='3')
|
||||
|
||||
default_ids = [
|
||||
[0x05c6, 0x9008, -1],
|
||||
[0x05c6, 0x900e, -1],
|
||||
[0x05c6, 0x9025, -1],
|
||||
[0x1199, 0x9062, -1],
|
||||
[0x1199, 0x9070, -1],
|
||||
[0x1199, 0x9090, -1],
|
||||
[0x0846, 0x68e0, -1],
|
||||
[0x19d2, 0x0076, -1]
|
||||
]
|
||||
|
||||
print("Qualcomm Sahara / Firehose Client V3.4 (c) B.Kerler 2018-2021.")
|
||||
print("Qualcomm Sahara / Firehose Client V3.5 (c) B.Kerler 2018-2021.")
|
||||
|
||||
|
||||
def parse_cmd(rargs):
|
||||
|
|
0
edl/Config/__init__.py
Executable file
0
edl/Config/__init__.py
Executable file
677
edl/Config/qualcomm_config.py
Normal file
677
edl/Config/qualcomm_config.py
Normal file
|
@ -0,0 +1,677 @@
|
|||
vendor = {
|
||||
0x0000: "Qualcomm ",
|
||||
0x0001: "Foxconn/Sony ",
|
||||
0x0004: "ZTE ",
|
||||
0x0011: "Smartisan ",
|
||||
0x0015: "Huawei ",
|
||||
0x0017: "Lenovo ",
|
||||
0x0020: "Samsung ",
|
||||
0x0029: "Asus ",
|
||||
0x0030: "Haier ",
|
||||
0x0031: "LG ",
|
||||
0x0035: "Foxconn/Nokia",
|
||||
0x0042: "Alcatel ",
|
||||
0x0045: "Nokia ",
|
||||
0x0048: "YuLong ",
|
||||
0x0051: "Oppo/Oneplus ",
|
||||
0x0072: "Xiaomi ",
|
||||
0x0073: "Vivo ",
|
||||
0x0130: "GlocalMe ",
|
||||
0x0139: "Lyf ",
|
||||
0x0168: "Motorola ",
|
||||
0x01B0: "Motorola ",
|
||||
0x0208: "Motorola ",
|
||||
0x0228: "Motorola ",
|
||||
0x2A96: "Micromax ",
|
||||
0x02E8: "Lenovo ",
|
||||
0x0328: "Motorola ",
|
||||
0x0368: "Motorola ",
|
||||
0x03C8: "Motorola ",
|
||||
0x00C8: "Motorola ",
|
||||
0x0348: "Motorola ",
|
||||
0x1043: "Asus ",
|
||||
0x1111: "Asus ",
|
||||
0x143A: "Asus ",
|
||||
0x1978: "Blackphone ",
|
||||
0x2A70: "Oxygen "
|
||||
}
|
||||
|
||||
root_cert_hash = {
|
||||
"secboot_sha2_pss_subca1" : "afca69d4235117e5bfc21467068b20df85e0115d7413d5821883a6d244961581",
|
||||
"secboot_sha2_pss_subca2" : "d40eee56f3194665574109a39267724ae7944134cd53cb767e293d3c40497955bc8a4519ff992b031fadc6355015ac87",
|
||||
"old" : "cc3153a80293939b90d02d3bf8b23e0292e452fef662c74998421adad42a380f",
|
||||
"new" : "7be49b72f9e4337223ccb84d6eccca4e61ce16e3602ac2008cb18b75babe6d09",
|
||||
"mdm9x60_tel" : "36c886068d9a6634e9c55185044344e9e756dcc3b5960874942c7a1a1550dee0"
|
||||
}
|
||||
|
||||
msmids = {
|
||||
# cc3153a80293939b90d02d3bf8b23e0292e452fef662c74998421adad42a380f pkhash/root-cert
|
||||
# 7be49b72f9e4337223ccb84d6eccca4e61ce16e3602ac2008cb18b75babe6d09 pkhash/root-cert
|
||||
0x9440E1: "QDF2432",
|
||||
0x9780E1: "IPQ4018",
|
||||
0x9790E1: "IPQ4019",
|
||||
0x0160E1: "QCA4020",
|
||||
0x9680E1: "APQ8009",
|
||||
0x7060E1: "APQ8016",
|
||||
0x8100E1: "APQ806x",
|
||||
0x9D00E1: "APQ8076",
|
||||
0x08A0E1: "APQ807x",
|
||||
0x9000E1: "APQ8084",
|
||||
0x9630E1: "APQ8092",
|
||||
0x0940E1: "MSM8905",
|
||||
0x9600E1: "MSM8909", # SnapDragon 210
|
||||
0x0510E1: "MSM8909W",
|
||||
0x7050E1: "MSM8916", # SnapDragon 410
|
||||
0x0560E1: "MSM8917",
|
||||
0x0860E1: "MSM8920",
|
||||
0x91B0E1: "MSM8929", # SnapDragon 415
|
||||
0x04F0E1: "MSM8937",
|
||||
0x90B0E1: "MSM8939", # SnapDragon 610
|
||||
0x90C0E1: "APQ8036",
|
||||
0x90D0E1: "APQ8039",
|
||||
0x06B0E1: "MSM8940",
|
||||
0x9720E1: "MSM8952", # SnapDragon 652
|
||||
0x0460E1: "MSM8953", # 8053lat
|
||||
0x0660E1: "APQ8053",
|
||||
0x9900E1: "MSM8976", # SnapDragon 652
|
||||
0x9690E1: "MSM8992", # SnapDragon 82x
|
||||
0x9400E1: "MSM8994", # SnapDragon 808
|
||||
0x9470E1: "MSM8996", # SnapDragon 820
|
||||
0x06F0E1: "MSM8996AU",
|
||||
0x05E0E1: "MSM8998_SDM835",
|
||||
0x94B0E1: "MSM9055",
|
||||
0x9730E1: "MDM9206_MDM9607tx",
|
||||
0x04A0E1: "MDM9607",
|
||||
0x8090E1: "MDM9916",
|
||||
0x80B0E1: "MDM9955",
|
||||
0x9210E1: "MDM9x35",
|
||||
0x9500E1: "MDM9x40",
|
||||
0x9540E1: "MDM9x45",
|
||||
0x03A0E1: "MDM9x50",
|
||||
0x7F50E1: "MDM9x25",
|
||||
0x0320E1: "MDM9250", # MDM9x50
|
||||
0x0340E1: "MDM9255", # MDM9x55
|
||||
0x0390E1: "MDM9350", # MDM9x50
|
||||
0x03B0E1: "MDM9x55",
|
||||
0x07D0E1: "MDM9x60", # SDX20
|
||||
0x07F0E1: "MDM9x65",
|
||||
0x1280E1: "fsm100xx",
|
||||
0x1650E1: "FSM10000",
|
||||
0x1680E1: "FSM10005",
|
||||
0x1690E1: "FSM10010",
|
||||
0x16A0E1: "FSM10051",
|
||||
0x16B0E1: "FSM10056",
|
||||
0x1530E1: "ipq5018",
|
||||
0x1610E1: "olympic_manar",
|
||||
0x1060E1: "qm215",
|
||||
0x0BE0E1: "SDM429",
|
||||
0x0BF0E1: "SDM439",
|
||||
0x09A0E1: "SDM450",
|
||||
0x0AC0E1: "SDM630", #0x30070x00
|
||||
0x0BA0E1: "SDM632",
|
||||
0x0BB0E1: "SDA632",
|
||||
0x08C0E1: "SDM660", # 0x30060000 soc_hw_version
|
||||
0x07B0E1: "SDX50M", # 0x soc_hw_version,
|
||||
0x0E50E1: "SDX55:CD90-PG591", # 0x600b0100 soc_hw_version, 0x8fff7000 dbgpolicy 32Bit, 0x8FCFD000 sec.elf 64Bit
|
||||
0x0CF0E1: "SDX55M:CD90-PH809", # 0x600b0100 soc_hw_version, 0x8fff7000 dbgpolicy 32Bit, 0x8FCFD000 sec.elf 64Bit, # Netgear MR5100, sdxprairie
|
||||
0x1250E1: "SA515M",
|
||||
|
||||
# afca69d4235117e5bfc21467068b20df85e0115d7413d5821883a6d244961581
|
||||
0x0AB0E1: "QCA6290", # 0x40040100 soc_hw_version
|
||||
0x0D90E1: "QCA6390", # 0x400A0000 soc_hw_version
|
||||
0x1310E1: "QCA6480",
|
||||
0x12E0E1: "QCA6481",
|
||||
0x12D0E1: "QCA6491",
|
||||
0x0D70E1: "QCA6595", # 0x400B0000 soc_hw_version
|
||||
0x0D30E1: "QCN7605", # 0x400B0000 soc_hw_version
|
||||
0x0D50E1: "QCN7606", # 0x400B0000 soc_hw_version
|
||||
0x0910E1: "SDM670", # 0x60040100 soc_hw_version
|
||||
0x0DB0E1: "SDM710",
|
||||
0x0AA0E1: "QCS605",
|
||||
0x0ED0E1: "SXR1120",
|
||||
0x0EA0E1: "SXR1130",
|
||||
0x08E0E1: "SDA845",
|
||||
|
||||
# d40eee56f3194665574109a39267724ae7944134cd53cb767e293d3c40497955
|
||||
# d40eee56f3194665574109a39267724ae7944134cd53cb767e293d3c40497955bc8a4519ff992b031fadc6355015ac87 pk-hash/root-cert
|
||||
0x1260E1: "IPQ6018",
|
||||
0x1070E1: "MDM9205", # 0x20130100
|
||||
0x1450E1: "agatti", # soc_vers 0x9003
|
||||
0x13F0E1: "bitra_SDM", # soc_vers 0x6012
|
||||
0x1410E1: "bitra_SDA",
|
||||
0x1590E1: "cedros", # soc_vers 0x6017
|
||||
0x1360E1: "kamorta", # soc_vers 0x9002 SnapDragon 662/460 SM4250/SM4350, bengal
|
||||
0x1350E1: "lahaina", # soc_vers 0x600F sm8350, SDM875
|
||||
0x1420E1: "lahaina_premier",
|
||||
0x14A0E1: "makena", # soc_vers 0x6014
|
||||
0x14B0E1: "SA8295P",
|
||||
0x14C0E1: "SA8540P",
|
||||
#0x1610E1: "mannar", # soc_vers 0x9004
|
||||
0x1470E1: "moselle", # soc_vers 0x4014
|
||||
0x10A0E1: "nicobar", # 0x90010100 soc_hw_version, 0x45FFF000 sec.elf 64Bit, 0x101FF000 dbgpolicy, 64Bit
|
||||
0x10B0E1: "qcn90xx", # soc_vers 0x400D
|
||||
0x10C0E1: "QCN9001",
|
||||
0x1150E1: "QCN9002",
|
||||
0x10D0E1: "QCN9003",
|
||||
0x10E0E1: "QCN9010",
|
||||
0x10F0E1: "QCN9011",
|
||||
0x1110E1: "QCN9012",
|
||||
0x1140E1: "QCN9013",
|
||||
0x0AF0E1: "qcs405", # 0x20140000 soc_hw_version, 0x863DB000 sec.elf 64Bit, 0x863DE000 dbgpolicy, 64Bit
|
||||
0x0400E1: "rennell", # soc_vers 0x600E7T A11 CB
|
||||
0x12A0E1: "rennell",
|
||||
0x12B0E1: "rennell_premier",
|
||||
0x1490E1: "rennell_v1.1",
|
||||
0x1630E1: "sd7250",
|
||||
0x11E0E1: "saipan", # 0x600D0100 soc_hw_version, 0x808FF000 sec.elf 64Bit, 0x1C000000 dbgpolicy, 64Bit, SM7250 Snapdragon 765G
|
||||
0x0950E1: "SM6150", # 0x60070100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
|
||||
0x0EC0E1: "SM6150p", # 0x60070100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
|
||||
0x0E60E1: "SM7150", # 0x600C0100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
|
||||
0x0A50E1: "SDM855_SM8150", # Hana 0x60030100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
|
||||
0x0A60E1: "SDM855p_SM8150p", # Hana 0x60030100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
|
||||
0x0C30E1: "SM8250:CD90-PH805-1A", # Kona, 0x60080100 soc_hw_version, 0x808FF000 sec.elf 64Bit, 0x1C000000 dbgpolicy, 64Bit
|
||||
0x0CE0E1: "SM8250:CD90-PH806-1A", # Kona 0x60080100 soc_hw_version, 0x808FF000 sec.elf 64Bit, 0x1C000000 dbgpolicy, 64Bit
|
||||
0x0B80E1: "sc8180x", # Snapdragon 8CX
|
||||
0x1560E1: "SM8250", # HDK 8250
|
||||
|
||||
# Unknown root hash
|
||||
0x0B70E1: "SDM850",
|
||||
0x0960E1: "SDX24", # 0x60020100 soc_hw_version, 0x8fff7000 dbgpolicy 32Bit, 0x8FCFD000 sec.elf 64Bit
|
||||
0x0970E1: "SDX24M", # 0x60020100 soc_hw_version, 0x8fff7000 dbgpolicy 32Bit, 0x8FCFD000 sec.elf 64Bit
|
||||
0x0E70E1: "SM7150p", # 0x600C0100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
|
||||
0x0E80E1: "SA8155", # 0x60030100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
|
||||
0x0E90E1: "SA8155p", # 0x60030100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
|
||||
0x0E40E1: "qcs403", # 0x20140000 soc_hw_version, 0x863DB000 sec.elf 64Bit, 0x863DE000 dbgpolicy, 64Bit
|
||||
0x1440E1: "chitwan", # soc_vers 0x6013
|
||||
0x1370E1: "kamortap",
|
||||
0x6220E1: "MSM7227A",
|
||||
0x8040E1: "APQ8026",
|
||||
0x0550E1: "APQ8017",
|
||||
0x90F0E1: "APQ8037",
|
||||
0x9770E1: "APQ8052",
|
||||
0x9F00E1: "APQ8056",
|
||||
0x7190E1: "APQ8064",
|
||||
0x9300E1: "APQ8092",
|
||||
0x0620E1: "APQ8098",
|
||||
0x8110E1: "MSM8210",
|
||||
0x8140E1: "MSM8212",
|
||||
0x8120E1: "MSM8610",
|
||||
0x8150E1: "MSM8612",
|
||||
0x8010E1: "MSM8626",
|
||||
0x8050E1: "MSM8926", # SnapDragon 400
|
||||
0x9180E1: "MSM8928", # SnapDragon 400
|
||||
0x7210E1: "MSM8930",
|
||||
0x72C0E1: "MSM8960",
|
||||
0x9B00E1: "MSM8956", # SnapDragon 652
|
||||
0x9100E1: "MSM8962",
|
||||
0x7B00E1: "MSM8974", # Snapdragon 800
|
||||
0x7B30E1: "MSM8974A",
|
||||
0x7B40E1: "MSM8974AB",
|
||||
0x7B80E1: "MSM8974Pro",
|
||||
0x7BC0E1: "MSM8974ABv3",
|
||||
0x6B10E1: "MSM8974AC",
|
||||
0x05F0E1: "MSM8996Pro", # SnapDragon 821
|
||||
0x0480E1: "MDM9207",
|
||||
0x0CC0E1: "SDM636",
|
||||
0x0930E1: "SDA670", # 0x60040100 soc_hw_version
|
||||
#0x0930E1: "SDA835", # 0x30020000 => HW_ID1 3002000000290022
|
||||
0x08B0E1: "SDM845", # Napali 0x60000100 => HW_ID1 6000000000010000
|
||||
#SDM840 NapaliQ ?
|
||||
#SDM640 Talos ?
|
||||
}
|
||||
|
||||
sochw = {
|
||||
0x2013: "MDM9205",
|
||||
0x2014: "qcs405",
|
||||
0x2017: "IPQ6018",
|
||||
0x3002: "MSM8998_SDM835,SDA835",
|
||||
0x3006: "SDM660",
|
||||
0x3007: "SDM630",
|
||||
0x4003: "QCA4020",
|
||||
0x4004: "IPQ8074,QCA6290",
|
||||
0x400A: "QCA6390",
|
||||
0x400B: "QCN7605,QCA6595,QCN7606",
|
||||
0x400D: "qcn90xx",
|
||||
0x4014: "moselle",
|
||||
|
||||
#: "SDM632",
|
||||
#: "SDA632",
|
||||
#: "SDM636",
|
||||
0x6000: "SDM845",
|
||||
0x6001: "SDA845",
|
||||
0x6002: "SDX24,SDX24M",
|
||||
0x6003: "SDM855_SM8150,SDM855p_SM8150p",
|
||||
0x6004: "SDA670,SDM670,SDM710",
|
||||
0x6005: "SDM670",
|
||||
#: "SDX50M",
|
||||
0x6006: "sc8180x",
|
||||
0x6007: "SM6150,SM6150p",
|
||||
0x6008: "SM8250:CD90-PH805-1A,SM8250:CD90-PH806-1A,SM8250",
|
||||
0x6009: "SDM670",
|
||||
0x600B: "SDX55:CD90-PG591,SDX55:CD90-PH809",
|
||||
0x600C: "SM7150,SM7150p",
|
||||
0x600D: "saipan",
|
||||
0x600E: "rennell",
|
||||
0x600F: "lahaina",
|
||||
0x6012: "bitra_SDM",
|
||||
0x6013: "chitwan",
|
||||
0x6014: "makena",
|
||||
0x6016: "olympic",
|
||||
0x6017: "cedros",
|
||||
0x9001: "nicobar",
|
||||
0x9002: "kamorta",
|
||||
0x9003: "agatti",
|
||||
0x9004: "mannar"
|
||||
}
|
||||
|
||||
secgen=[
|
||||
# BOOT_ROM_BASE_PHYS, SECURITY_CONTROL_BASE_PHYS, MEMORY_MAP
|
||||
[[], [0x01900000, 0x100000], []],
|
||||
[[],[0x01e20000,0x1000],[]],
|
||||
[[0xFC010000, 0x18000], [0xFC4B8000, 0x60F0], [0x200000, 0x24000]],
|
||||
[[0x100000, 0x1ffb0], [0x70000, 0x6158], [0x200000, 0x24000]],
|
||||
[[0x100000, 0x1ffb0], [0x00058000, 0x1000], [0x200000, 0x24000]],
|
||||
[[0x100000, 0x1ffb0], [0x000A0000, 0x6FFF], [0x200000, 0x24000]],
|
||||
[[0x100000, 0x1ffb0], [0x00700000, 0x6158], [0x200000, 0x24000]],
|
||||
[[0x300000, 0x3c000], [0x00780000, 0x10000], [0x14009003, 0x24000]],
|
||||
[[0x300000, 0x3c000], [0x01B40000, 0x10000], []],
|
||||
]
|
||||
|
||||
infotbl = {
|
||||
"QDF2432": secgen[0],
|
||||
"QCA6290": secgen[1],
|
||||
"QCA6390": secgen[1],
|
||||
"QCA6480": secgen[1],
|
||||
"QCA6481": secgen[1],
|
||||
"QCA6490": secgen[1],
|
||||
"QCA6491": secgen[1],
|
||||
|
||||
"APQ8084": secgen[2],
|
||||
"APQ8092": secgen[2],
|
||||
"MSM8962": secgen[2],
|
||||
"MSM8974": secgen[2],
|
||||
"MSM8974Pro": secgen[2],
|
||||
"MSM8974AB": secgen[2],
|
||||
"MSM8974ABv3": secgen[2],
|
||||
"MSM8974AC": secgen[2],
|
||||
"MSM8992": secgen[2],
|
||||
"MSM8994": secgen[2],
|
||||
"MDM9x25": secgen[2],
|
||||
"MDM9x35": secgen[2],
|
||||
|
||||
"MSM8996": secgen[3],
|
||||
"MSM8996AU": secgen[3],
|
||||
"MSM8996Pro": secgen[3],
|
||||
|
||||
"IPQ4018": secgen[4],
|
||||
"IPQ4019": secgen[4],
|
||||
"APQ8009": secgen[4],
|
||||
"APQ8016": secgen[4],
|
||||
"APQ8036": secgen[4],
|
||||
"APQ8039": secgen[4],
|
||||
"MSM8905": secgen[4],
|
||||
"MSM8909": secgen[4],
|
||||
"MSM8909W": secgen[4],
|
||||
"MSM8916": secgen[4],
|
||||
"MSM8929": secgen[4],
|
||||
"MSM8939": secgen[4],
|
||||
"MSM8952": secgen[4],
|
||||
"MDM9x40": secgen[4],
|
||||
"MDM9x45": secgen[4],
|
||||
|
||||
"APQ8017": secgen[5],
|
||||
"APQ8037": secgen[5],
|
||||
"APQ8053": secgen[5],
|
||||
"APQ8056": secgen[5],
|
||||
"APQ8076": secgen[5],
|
||||
"MSM8917": secgen[5],
|
||||
"MSM8920": secgen[5],
|
||||
"MSM8937": secgen[5],
|
||||
"MSM8940": secgen[5],
|
||||
"MSM8953": secgen[5],
|
||||
"MSM8956": secgen[5],
|
||||
"MSM8976": secgen[5],
|
||||
"MSM9206": secgen[5],
|
||||
"MDM9207": secgen[5],
|
||||
"MDM9607": secgen[5],
|
||||
"MDM9x50": secgen[5],
|
||||
"MDM9x55": secgen[5],
|
||||
"MDM9x60": secgen[5],
|
||||
"MDM9x65": secgen[5],
|
||||
"MDM9250": secgen[5],
|
||||
"MDM9350": secgen[5],
|
||||
"MDM9650": secgen[5],
|
||||
"SDM429": secgen[5],
|
||||
"SDM439": secgen[5],
|
||||
"SDM450": secgen[5],
|
||||
"SDM632": secgen[5],
|
||||
"SDA632": secgen[5],
|
||||
"SDX50M": secgen[5],
|
||||
"qcs403": secgen[5],
|
||||
"qcs405": secgen[5],
|
||||
"ipq5018": secgen[5],
|
||||
"ipq6018": secgen[5],
|
||||
"qm215": secgen[5],
|
||||
|
||||
"APQ806x": secgen[6],
|
||||
"MSM8930": secgen[6],
|
||||
"MSM8936": secgen[6],
|
||||
|
||||
"APQ8098": secgen[7],
|
||||
"MSM8998": secgen[7],
|
||||
"SDM630": secgen[7],
|
||||
"SDM636": secgen[7],
|
||||
"SDM660": secgen[7],
|
||||
"SDM670": secgen[7],
|
||||
"SDA670": secgen[7],
|
||||
"SDM710": secgen[7],
|
||||
"QCS605": secgen[7],
|
||||
"SXR1120": secgen[7],
|
||||
"SXR1130": secgen[7],
|
||||
"SDM845": secgen[7],
|
||||
"SDA845": secgen[7],
|
||||
"SDM850": secgen[7],
|
||||
"SDX24": secgen[7],
|
||||
"SDX24M": secgen[7],
|
||||
"SDX55M": secgen[7],
|
||||
"SDX55:CD90-PG591": secgen[7],
|
||||
"SDX55:CD90-PH809": secgen[7],
|
||||
"SA515M": secgen[7],
|
||||
"SM6150": secgen[7],
|
||||
"SM6150p": secgen[7],
|
||||
"SM7150": secgen[7],
|
||||
"SM7150p": secgen[7],
|
||||
"SDM855_SM8150": secgen[7],
|
||||
"SDM855p_SM8150p": secgen[7],
|
||||
"SM8250": secgen[7],
|
||||
"SM8250p": secgen[7],
|
||||
"SM8250:CD90-PH805-1A": secgen[7],
|
||||
"SM8250:CD90-PH806-1A": secgen[7],
|
||||
"saipan": secgen[7],
|
||||
"sc8180x": secgen[7],
|
||||
"bitra": secgen[7],
|
||||
"cedros": secgen[7],
|
||||
"chitwan": secgen[7],
|
||||
"lahaina": secgen[7],
|
||||
"lahaina_premier": secgen[7],
|
||||
"mannar": secgen[7],
|
||||
"rennell": secgen[7],
|
||||
"sd7250": secgen[7],
|
||||
|
||||
"nicobar": secgen[8],
|
||||
"agatti": secgen[8],
|
||||
"kamorta": secgen[8],
|
||||
"kamortap": secgen[8],
|
||||
|
||||
|
||||
# "MSM7227A": [[], [], []],
|
||||
# "MSM8210": [[], [0xFC4B8000,0x6FFF], []],
|
||||
# "MSM8212": [[], [], []],
|
||||
# "MSM8610": [[], [0xFC4B8000,0x6FFF], []],
|
||||
# "MSM8226": [[], [0xFC4B8000,0x6FFF], []],
|
||||
# "MSM8926": [[], [0xFC4B8000,0x6FFF], []],
|
||||
# "MSM8928": [[], [], []],
|
||||
}
|
||||
|
||||
class memory_type:
|
||||
nand = 0
|
||||
emmc = 1
|
||||
ufs = 2
|
||||
spinor = 3
|
||||
|
||||
preferred_memory = {
|
||||
"QDF2432": emmc,
|
||||
"QCA6290": emmc,
|
||||
"QCA6390": emmc,
|
||||
"QCA6480": emmc,
|
||||
"QCA6481": emmc,
|
||||
"QCA6490": emmc,
|
||||
"QCA6491": emmc,
|
||||
|
||||
"APQ8084": emmc,
|
||||
"APQ8092": emmc,
|
||||
"MSM8962": emmc,
|
||||
"MSM8974": emmc,
|
||||
"MSM8974Pro": emmc,
|
||||
"MSM8974AB": emmc,
|
||||
"MSM8974ABv3": emmc,
|
||||
"MSM8974AC": emmc,
|
||||
"MSM8992": emmc,
|
||||
"MSM8994": emmc,
|
||||
"MDM9x25": emmc,
|
||||
"MDM9x35": emmc,
|
||||
|
||||
"MSM8996": ufs,
|
||||
"MSM8996AU": ufs,
|
||||
"MSM8996Pro": ufs,
|
||||
|
||||
"IPQ4018": emmc,
|
||||
"IPQ4019": emmc,
|
||||
"APQ8009": emmc,
|
||||
"APQ8016": emmc,
|
||||
"APQ8036": emmc,
|
||||
"APQ8039": emmc,
|
||||
"MSM8905": emmc,
|
||||
"MSM8909": emmc,
|
||||
"MSM8909W": emmc,
|
||||
"MSM8916": emmc,
|
||||
"MSM8929": emmc,
|
||||
"MSM8939": emmc,
|
||||
"MSM8952": emmc,
|
||||
"MDM9x40": emmc,
|
||||
"MDM9x45": emmc,
|
||||
|
||||
"APQ8017": emmc,
|
||||
"APQ8037": emmc,
|
||||
"APQ8053": emmc,
|
||||
"APQ8056": emmc,
|
||||
"APQ8076": emmc,
|
||||
"MSM8917": emmc,
|
||||
"MSM8920": emmc,
|
||||
"MSM8937": emmc,
|
||||
"MSM8940": emmc,
|
||||
"MSM8953": emmc,
|
||||
"MSM8956": emmc,
|
||||
"MSM8976": emmc,
|
||||
"MSM9206": emmc,
|
||||
"MDM9207": nand,
|
||||
"MDM9607": nand,
|
||||
"MDM9x50": emmc,
|
||||
"MDM9x55": emmc,
|
||||
"MDM9x60": emmc,
|
||||
"MDM9x65": emmc,
|
||||
"MDM9250": emmc,
|
||||
"MDM9350": emmc,
|
||||
"MDM9650": emmc,
|
||||
"SDM429": emmc,
|
||||
"SDM439": emmc,
|
||||
"SDM450": emmc,
|
||||
"SDM632": emmc,
|
||||
"SDA632": emmc,
|
||||
"SDX50M": emmc,
|
||||
"qcs403": emmc,
|
||||
"qcs405": emmc,
|
||||
"ipq5018": emmc,
|
||||
"ipq6018": emmc,
|
||||
"qm215": emmc,
|
||||
|
||||
"APQ806x": emmc,
|
||||
"MSM8930": emmc,
|
||||
"MSM8936": emmc,
|
||||
|
||||
"APQ8098": emmc,
|
||||
"MSM8998": ufs,
|
||||
"SDM630": emmc,
|
||||
"SDM636": emmc,
|
||||
"SDM660": emmc,
|
||||
"SDM670": emmc,
|
||||
"SDA670": emmc,
|
||||
"SDM710": emmc,
|
||||
"QCS605": emmc,
|
||||
"SXR1120": emmc,
|
||||
"SXR1130": emmc,
|
||||
"SDM845": ufs,
|
||||
"SDA845": ufs,
|
||||
"SDM850": ufs,
|
||||
"SDX24": emmc,
|
||||
"SDX24M": emmc,
|
||||
"SDX55M": ufs,
|
||||
"SDX55:CD90-PG591": ufs,
|
||||
"SDX55:CD90-PH809": ufs,
|
||||
"SA515M": emmc,
|
||||
"SM6150": emmc,
|
||||
"SM6150p": emmc,
|
||||
"SM7150": emmc,
|
||||
"SM7150p": emmc,
|
||||
"SDM855_SM8150": ufs,
|
||||
"SDM855p_SM8150p": ufs,
|
||||
"SM8250": ufs,
|
||||
"SM8250p": ufs,
|
||||
"SM8250:CD90-PH805-1A": ufs,
|
||||
"SM8250:CD90-PH806-1A": ufs,
|
||||
"saipan": ufs,
|
||||
"sc8180x": ufs,
|
||||
"bitra": ufs,
|
||||
"cedros": ufs,
|
||||
"chitwan": ufs,
|
||||
"lahaina": ufs,
|
||||
"lahaina_premier": ufs,
|
||||
"mannar": ufs,
|
||||
"rennell": ufs,
|
||||
"sd7250": ufs,
|
||||
|
||||
"nicobar": ufs,
|
||||
"agatti": ufs,
|
||||
"kamorta": ufs,
|
||||
"kamortap": ufs,
|
||||
|
||||
# "MSM7227A": [[], [], []],
|
||||
# "MSM8210": [[], [0xFC4B8000,0x6FFF], []],
|
||||
# "MSM8212": [[], [], []],
|
||||
# "MSM8610": [[], [0xFC4B8000,0x6FFF], []],
|
||||
# "MSM8226": [[], [0xFC4B8000,0x6FFF], []],
|
||||
# "MSM8926": [[], [0xFC4B8000,0x6FFF], []],
|
||||
# "MSM8928": [[], [], []],
|
||||
}
|
||||
|
||||
|
||||
secureboottbl = {
|
||||
"QDF2432": 0x019018c8,
|
||||
"QCA6290": 0x01e20030,
|
||||
"QCA6390": 0x01e20010,
|
||||
"IPQ4018": 0x00058098,
|
||||
"IPQ4019": 0x00058098,
|
||||
"APQ8009": 0x00058098,
|
||||
"APQ8016": 0x00058098,
|
||||
"APQ8036": 0x00058098,
|
||||
"APQ8039": 0x00058098,
|
||||
"APQ8037": 0x000a01d0,
|
||||
"APQ8053": 0x000a01d0,
|
||||
"APQ8052": 0x00058098,
|
||||
"APQ8056": 0x000a01d0,
|
||||
"APQ8076": 0x000a01d0,
|
||||
"APQ8084": 0xFC4B83F8,
|
||||
"APQ8092": 0xFC4B83F8,
|
||||
"APQ8098": 0x00780350,
|
||||
"MSM8226": 0xFC4B83E8,
|
||||
"MSM8610": 0xFC4B83E8,
|
||||
"MSM8905": 0x00058098,
|
||||
"MSM8909": 0x00058098,
|
||||
"MSM8909W": 0x00058098,
|
||||
"MSM8916": 0x00058098,
|
||||
"MSM8917": 0x000A01D0,
|
||||
"MSM8920": 0x000A01D0,
|
||||
"MSM8929": 0x00058098,
|
||||
"MSM8930": 0x700310,
|
||||
"MSM8936": 0x700310,
|
||||
"MSM8937": 0x000A01D0,
|
||||
"MSM8939": 0x00058098,
|
||||
"MSM8940": 0x000A01D0,
|
||||
"MSM8952": 0x00058098,
|
||||
"MSM8953": 0x000a01d0,
|
||||
"MSM8956": 0x000a01d0,
|
||||
"MSM8974": 0xFC4B83F8,
|
||||
"MSM8974AB": 0xFC4B83F8,
|
||||
"MSM8974ABv3": 0xFC4B83F8,
|
||||
"MSM8974AC": 0xFC4B83F8,
|
||||
"MSM8976": 0x000a01d0,
|
||||
"MSM8992": 0xFC4B83F8,
|
||||
"MSM8994": 0xFC4B83F8,
|
||||
"MSM8996": 0x00070378,
|
||||
"MSM8996AU": 0x00070378,
|
||||
"MSM8996Pro": 0x00070378,
|
||||
"MSM8998_SDM835": 0x00780350,
|
||||
"MDM9205": 0x000a0320,
|
||||
"MDM9206_MDM9207tx": 0x000a01d0,
|
||||
"MDM9250": 0x000a01d0,
|
||||
"MDM9350": 0x000a01d0,
|
||||
"MDM9207": 0x000a01d0,
|
||||
"MDM9607": 0x000a01d0,
|
||||
"MDM9x25": 0xFC4B6028,
|
||||
"MDM9x30": 0xFC4B6028,
|
||||
"MDM9x35": 0xFC4B6028,
|
||||
"MDM9x40": 0x00058098,
|
||||
"MDM9x45": 0x00058098,
|
||||
"MDM9650": 0x000a01d0,
|
||||
"MDM9x50": 0x000a01d0,
|
||||
"MDM9x55": 0x000a01d0,
|
||||
"MDM9x60": 0x000a01d0,
|
||||
"MDM9x65": 0x000a01d0,
|
||||
"SDM429": 0x000a01d0,
|
||||
"SDM439": 0x000a01d0,
|
||||
"SDM450": 0x000a01d0,
|
||||
"SDM630": 0x00780350,
|
||||
"SDM632": 0x000a01d0,
|
||||
"SDA632": 0x000a01d0,
|
||||
"SDM636": 0x00780350,
|
||||
"SDM660": 0x00780350,
|
||||
"SDM670": 0x00780350, # Warlock
|
||||
"SDA670": 0x00780350,
|
||||
"SDM710": 0x00780350,
|
||||
"QCS605": 0x00780350,
|
||||
"SXR1120": 0x00780350,
|
||||
"SXR1130": 0x00780350,
|
||||
"SDM845": 0x00780350,
|
||||
"SDA845": 0x00780350,
|
||||
"SDX24" : 0x00780390,
|
||||
"SDX24M": 0x00780390,
|
||||
"SDX50M": 0x000a01e0,
|
||||
"SDX55:CD90-PG591": 0x007805E8,
|
||||
"SDX55:CD90-PH809": 0x007805E8,
|
||||
"SDX55M" : 0x007804D0,
|
||||
"SA515M" : 0x007804D0,
|
||||
"SM6150": 0x00780360,
|
||||
"SM6150p": 0x00780360,
|
||||
"SM7150": 0x00780460,
|
||||
"SM7150p": 0x00780460,
|
||||
"SDM855_SM8150": 0x007804D0,
|
||||
"SDM855p_SM8150p": 0x007804D0,
|
||||
"SM8250:CD90-PH805-1A": 0x007805E8,
|
||||
"SM8250:CD90-PH806-1A": 0x007805E8,
|
||||
"agatti": 0x01B40458,
|
||||
"bitra": 0x007804D8,
|
||||
"bitra_SDM": 0x007804D8,
|
||||
"bitra_SDA": 0x007804D8,
|
||||
"cedros": 0x00780728,
|
||||
"chitwan": 0x00780668,
|
||||
"ipq5018": 0x000A01D0,
|
||||
"ipq6018": 0x000A01D0,
|
||||
"saipan": 0x007805E8,
|
||||
"sd7250": 0x007805E8,
|
||||
"sc8180x": 0x007805E8,
|
||||
"qcs403": 0x000a0310,
|
||||
"qcs405": 0x000a0310,
|
||||
"nicobar": 0x01B40458,
|
||||
"kamorta": 0x01B40458,
|
||||
"kamorta_p": 0x01B40458,
|
||||
"lahaina": 0x780668,
|
||||
"lahaina_premier": 0x780668,
|
||||
"mannar": 0x01B40458,
|
||||
"qm215": 0x000a01d0,
|
||||
"rennell":0x000780498
|
||||
# "MSM7227A":[[], [], []],
|
||||
# "MSM8210": [[], [], []],
|
||||
# "MSM8212":
|
||||
# "MSM8926": [[], [], []],
|
||||
# "MSM8928": [[], [], []],
|
||||
}
|
10
edl/Config/usb_ids.py
Normal file
10
edl/Config/usb_ids.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
default_ids = [
|
||||
[0x05c6, 0x9008, -1],
|
||||
[0x05c6, 0x900e, -1],
|
||||
[0x05c6, 0x9025, -1],
|
||||
[0x1199, 0x9062, -1],
|
||||
[0x1199, 0x9070, -1],
|
||||
[0x1199, 0x9090, -1],
|
||||
[0x0846, 0x68e0, -1],
|
||||
[0x19d2, 0x0076, -1]
|
||||
]
|
0
edl/Library/Modules/__init__.py
Normal file
0
edl/Library/Modules/__init__.py
Normal file
113
edl/Library/Modules/generic.py
Normal file
113
edl/Library/Modules/generic.py
Normal file
|
@ -0,0 +1,113 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) B.Kerler 2018-2021
|
||||
|
||||
import logging
|
||||
from edl.Library.utils import LogBase
|
||||
|
||||
|
||||
class generic(metaclass=LogBase):
|
||||
def __init__(self, fh, serial, args, loglevel):
|
||||
self.fh = fh
|
||||
self.serial = serial
|
||||
self.args = args
|
||||
self.__logger.setLevel(loglevel)
|
||||
self.error=self.__logger.error
|
||||
if loglevel == logging.DEBUG:
|
||||
logfilename = "log.txt"
|
||||
fh = logging.FileHandler(logfilename)
|
||||
self.__logger.addHandler(fh)
|
||||
|
||||
def oem_unlock(self, enable):
|
||||
res = self.fh.detect_partition(self.args, "config")
|
||||
if res[0]:
|
||||
lun = res[1]
|
||||
rpartition = res[2]
|
||||
offsettopatch = 0x7FFFF
|
||||
sector = rpartition.sector + (offsettopatch // self.fh.cfg.SECTOR_SIZE_IN_BYTES)
|
||||
offset = offsettopatch % self.fh.cfg.SECTOR_SIZE_IN_BYTES
|
||||
if enable:
|
||||
value = 0x1
|
||||
else:
|
||||
value = 0x0
|
||||
size_in_bytes = 1
|
||||
if self.fh.cmd_patch(lun, sector, offset, value, size_in_bytes, True):
|
||||
print(f"Patched sector {str(rpartition.sector)}, offset {str(offset)} with value {value}, " +
|
||||
f"size in bytes {size_in_bytes}.")
|
||||
else:
|
||||
print(f"Error on writing sector {str(rpartition.sector)}, offset {str(offset)} with value {value}, " +
|
||||
f"size in bytes {size_in_bytes}.")
|
||||
else:
|
||||
"""
|
||||
#define DEVICE_MAGIC "ANDROID-BOOT!"
|
||||
#define DEVICE_MAGIC_SIZE 13
|
||||
#define MAX_PANEL_ID_LEN 64
|
||||
#define MAX_VERSION_LEN 64
|
||||
#if VBOOT_MOTA
|
||||
struct device_info
|
||||
{
|
||||
unsigned char magic[DEVICE_MAGIC_SIZE];
|
||||
bool is_unlocked;
|
||||
bool is_tampered;
|
||||
bool is_verified;
|
||||
bool charger_screen_enabled;
|
||||
char display_panel[MAX_PANEL_ID_LEN];
|
||||
char bootloader_version[MAX_VERSION_LEN];
|
||||
char radio_version[MAX_VERSION_LEN];
|
||||
bool is_unlock_critical;
|
||||
};
|
||||
#else
|
||||
struct device_info
|
||||
{
|
||||
unsigned char magic[DEVICE_MAGIC_SIZE];
|
||||
bool is_unlocked; #0x10
|
||||
bool is_tampered; #0x14
|
||||
bool charger_screen_enabled; #0x18
|
||||
char display_panel[MAX_PANEL_ID_LEN];
|
||||
char bootloader_version[MAX_VERSION_LEN];
|
||||
char radio_version[MAX_VERSION_LEN];
|
||||
bool verity_mode; // 1 = enforcing, 0 = logging
|
||||
bool is_unlock_critical;
|
||||
};
|
||||
#endif
|
||||
"""
|
||||
res = self.fh.detect_partition(self.args, "devinfo")
|
||||
if res[0]:
|
||||
lun = res[1]
|
||||
rpartition = res[2]
|
||||
offsettopatch1 = 0x10 # is_unlocked
|
||||
offsettopatch2 = 0x18 # is_critical_unlocked
|
||||
offsettopatch3 = 0x7FFE10 # zte
|
||||
offsettopatch4 = 0x7FFE18 # zte
|
||||
sector1, offset1 = self.fh.calc_offset(rpartition.sector, offsettopatch1)
|
||||
sector2, offset2 = self.fh.calc_offset(rpartition.sector, offsettopatch2)
|
||||
sector3, offset3 = self.fh.calc_offset(rpartition.sector, offsettopatch3)
|
||||
sector4, offset4 = self.fh.calc_offset(rpartition.sector, offsettopatch4)
|
||||
value = 0x1
|
||||
size_in_bytes = 1
|
||||
if self.fh.cmd_patch(lun, sector1, offset1, 0x1, size_in_bytes, True):
|
||||
if self.fh.cmd_patch(lun, sector2, offset2, 0x1, size_in_bytes, True):
|
||||
print(
|
||||
f"Patched sector {str(rpartition.sector)}, offset {str(offset1)} with value {value}, " +
|
||||
f"size in bytes {size_in_bytes}.")
|
||||
data = self.fh.cmd_read_buffer(lun, rpartition.sector, rpartition.sectors)
|
||||
if (len(data) > 0x7FFE20) and data[0x7FFE00:0x7FFE10] == b"ANDROID-BOOT!\x00\x00\x00":
|
||||
if self.fh.cmd_patch(lun, sector3, offset3, value, size_in_bytes, True):
|
||||
if self.fh.cmd_patch(lun, sector4, offset4, value, size_in_bytes, True):
|
||||
print(
|
||||
f"Patched sector {str(rpartition.sector)}, offset {str(offset1)} with " +
|
||||
f"value {value}, size in bytes {size_in_bytes}.")
|
||||
return True
|
||||
print(
|
||||
f"Error on writing sector {str(rpartition.sector)}, offset {str(offset1)} with value {value}, " +
|
||||
f"size in bytes {size_in_bytes}.")
|
||||
return False
|
||||
else:
|
||||
fpartitions = res[1]
|
||||
self.error(f"Error: Couldn't detect partition: \"devinfo\"\nAvailable partitions:")
|
||||
for lun in fpartitions:
|
||||
for rpartition in fpartitions[lun]:
|
||||
if self.args["--memory"].lower() == "emmc":
|
||||
self.error("\t" + rpartition)
|
||||
else:
|
||||
self.error(lun + ":\t" + rpartition)
|
99
edl/Library/Modules/init.py
Normal file
99
edl/Library/Modules/init.py
Normal file
|
@ -0,0 +1,99 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) B.Kerler 2018-2021
|
||||
|
||||
import logging
|
||||
from edl.Library.utils import LogBase
|
||||
|
||||
try:
|
||||
from edl.Library.Modules.generic import generic
|
||||
except ImportError as e:
|
||||
generic = None
|
||||
pass
|
||||
|
||||
try:
|
||||
from edl.Library.Modules.oneplus import oneplus
|
||||
except ImportError as e:
|
||||
oneplus = None
|
||||
pass
|
||||
|
||||
try:
|
||||
from edl.Library.Modules.xiaomi import xiaomi
|
||||
except ImportError as e:
|
||||
xiaomi = None
|
||||
pass
|
||||
|
||||
class modules(metaclass=LogBase):
|
||||
def __init__(self, fh, serial, supported_functions, loglevel, devicemodel, args):
|
||||
self.fh = fh
|
||||
self.args = args
|
||||
self.serial = serial
|
||||
self.error = self.__logger.error
|
||||
self.supported_functions = supported_functions
|
||||
self.__logger.setLevel(loglevel)
|
||||
if loglevel==logging.DEBUG:
|
||||
logfilename = "log.txt"
|
||||
fh = logging.FileHandler(logfilename)
|
||||
self.__logger.addHandler(fh)
|
||||
self.options = {}
|
||||
self.devicemodel = devicemodel
|
||||
self.generic = None
|
||||
try:
|
||||
self.generic = generic(fh=self.fh, serial=self.serial, args=self.args, loglevel=loglevel)
|
||||
except Exception as e:
|
||||
pass
|
||||
self.ops = None
|
||||
try:
|
||||
self.ops = oneplus(fh=self.fh, projid=self.devicemodel, serial=self.serial,
|
||||
supported_functions=self.supported_functions, args=self.args,loglevel=loglevel)
|
||||
except Exception as e:
|
||||
pass
|
||||
self.xiaomi=None
|
||||
try:
|
||||
self.xiaomi = xiaomi(fh=self.fh)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
def addpatch(self):
|
||||
if self.ops is not None:
|
||||
return self.ops.addpatch()
|
||||
return ""
|
||||
|
||||
def addprogram(self):
|
||||
if self.ops is not None:
|
||||
return self.ops.addprogram()
|
||||
return ""
|
||||
|
||||
def edlauth(self):
|
||||
if self.xiaomi is not None:
|
||||
return self.xiaomi.edl_auth()
|
||||
return True
|
||||
|
||||
def writeprepare(self):
|
||||
if self.ops is not None:
|
||||
return self.ops.run()
|
||||
return True
|
||||
|
||||
def run(self, command, args):
|
||||
args = args.split(",")
|
||||
options = {}
|
||||
for i in range(len(args)):
|
||||
if "=" in args[i]:
|
||||
option = args[i].split("=")
|
||||
if len(option) > 1:
|
||||
options[option[0]] = option[1]
|
||||
else:
|
||||
options[args[i]] = True
|
||||
if command=="":
|
||||
print("Valid commands are:\noemunlock\n")
|
||||
return False
|
||||
if self.generic is not None and command == "oemunlock":
|
||||
if "enable" in options:
|
||||
enable = True
|
||||
elif "disable" in options:
|
||||
enable = False
|
||||
else:
|
||||
self.error("Unknown mode given. Available are: enable, disable.")
|
||||
return False
|
||||
return self.generic.oem_unlock(enable)
|
||||
return False
|
602
edl/Library/Modules/oneplus.py
Executable file
602
edl/Library/Modules/oneplus.py
Executable file
|
@ -0,0 +1,602 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) B.Kerler 2018-2021
|
||||
|
||||
"""
|
||||
Usage:
|
||||
oneplus.py rawxml [--projid=value] [--serial=value]
|
||||
oneplus.py rawnewxml [--projid=value] [--ts=value] [--serial=value]
|
||||
oneplus.py setprojmodel_verify <token> <pk> [--projid=value]
|
||||
oneplus.py setswprojmodel_verify <token> <pk> [--projid=value] [--ts=value]
|
||||
oneplus.py program_verify <token> <prog_token> <pk> [--projid=value]
|
||||
Options:
|
||||
--projid=value Set the appropriate projid [default: 18825]
|
||||
--serial=value Set the appropriate serial [default: 123456]
|
||||
--ts=value Set the device timestamp [default: 1604949411]
|
||||
"""
|
||||
|
||||
import time
|
||||
import random
|
||||
from struct import pack
|
||||
import logging
|
||||
from edl.Library.utils import LogBase
|
||||
|
||||
try:
|
||||
from edl.Library.cryptutils import cryptutils
|
||||
except Exception as e:
|
||||
print(e)
|
||||
from ..cryptutils import cryptutils
|
||||
from binascii import unhexlify, hexlify
|
||||
|
||||
deviceconfig = {
|
||||
# OP5, cheeseburger
|
||||
"16859": dict(version=1, cm=None, param_mode=0),
|
||||
# OP5t, dumpling
|
||||
"17801": dict(version=1, cm=None, param_mode=0),
|
||||
# OP6, enchilada
|
||||
"17819": dict(version=1, cm=None, param_mode=0),
|
||||
# OP6t, fajita
|
||||
"18801": dict(version=1, cm=None, param_mode=0),
|
||||
# OP6t T-Mo, fajitat
|
||||
"18811": dict(version=1, cm=None, param_mode=0),
|
||||
# Oneplus 7, guacamoleb
|
||||
"18857": dict(version=1, cm=None, param_mode=0),
|
||||
# Oneplus 7 Pro, guacamole
|
||||
"18821": dict(version=1, cm=None, param_mode=0),
|
||||
# Oneplus 7 Pro 5G Sprint, guacamoles
|
||||
"18825": dict(version=1, cm=None, param_mode=0),
|
||||
# Oneplus 7 Pro 5G EE and Elisa, guacamoleg
|
||||
"18827": dict(version=1, cm=None, param_mode=0),
|
||||
# Oneplus 7 Pro T-Mo, guacamolet
|
||||
"18831": dict(version=1, cm=None, param_mode=0),
|
||||
# Oneplus 7t, hotdogb
|
||||
"18865": dict(version=1, cm=None, param_mode=0),
|
||||
# Oneplus 7t T-Mo, hotdogt
|
||||
"19863": dict(version=1, cm=None, param_mode=0),
|
||||
# Oneplus 7t Pro, hotdog
|
||||
"19801": dict(version=1, cm=None, param_mode=0),
|
||||
# Oneplus 7t Pro 5G T-Mo, hotdogg
|
||||
"19861": dict(version=1, cm=None, param_mode=0),
|
||||
|
||||
# OP8, instantnoodle
|
||||
"19821": dict(version=2, cm="0cffee8a", param_mode=0),
|
||||
# OP8 T-Mo, instantnoodlet
|
||||
"19855": dict(version=2, cm="6d9215b4", param_mode=0),
|
||||
# OP8 Verizon, instantnoodlev
|
||||
"19867": dict(version=2, cm="4107b2d4", param_mode=0),
|
||||
# OP8 Visible, instantnoodlevis
|
||||
"19868": dict(version=-1, cm="178d8213", param_mode=0),
|
||||
# OP8 Pro, instantnoodlep
|
||||
"19811": dict(version=2, cm="40217c07", param_mode=0),
|
||||
# OP8t, kebab
|
||||
"19805": dict(version=2, cm="1a5ec176", param_mode=0),
|
||||
# OP8t T-Mo, kebabt
|
||||
"20809": dict(version=2, cm="d6bc8c36", param_mode=0),
|
||||
|
||||
# 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
|
||||
"20886": dict(version=3, cm="b8bd9e39", param_mode=1),
|
||||
# billie8t, OP N10 5G TMO
|
||||
"20888": dict(version=3, cm="142f1bd7", param_mode=1),
|
||||
# OP N10 5G Europe, billie8
|
||||
"20889": dict(version=3, cm="f2056ae1", param_mode=1),
|
||||
|
||||
# OP N100 Metro, billie2t
|
||||
"20880": dict(version=3, cm="6ccf5913", param_mode=1),
|
||||
# OP N100 Global, billie2
|
||||
"20881": dict(version=3, cm="fa9ff378", param_mode=1),
|
||||
# OP N100 TMO, billie2t
|
||||
"20882": dict(version=3, cm="4ca1e84e", param_mode=1),
|
||||
# OP N100 Europe, billie2
|
||||
"20883": dict(version=3, cm="ad9dba4a", param_mode=1),
|
||||
|
||||
# OP9 Pro, lemonadep
|
||||
"19815": dict(version=2, cm="9c151c7f", param_mode=0),
|
||||
"20859": dict(version=2, cm="9c151c7f", param_mode=0),
|
||||
# OP9, lemonade
|
||||
"19825": dict(version=2, cm="0898dcd6", param_mode=1),
|
||||
# OP9R, lemonades
|
||||
"20828": dict(version=2, cm=None, param_mode=1),
|
||||
# OP9 TMO, lemonadet
|
||||
"20854": dict(version=2, cm="16225d4e", param_mode=1),
|
||||
# OP9 Pro TMO, lemonadept
|
||||
"2085A": dict(version=2, cm="7f19519a", param_mode=1),
|
||||
|
||||
# dre8t
|
||||
"20818": dict(version=2, cm=None, param_mode=1),
|
||||
# dre8m
|
||||
"2083C": dict(version=2, cm=None, param_mode=1),
|
||||
# dre9
|
||||
"2083D": dict(version=2, cm=None, param_mode=1)
|
||||
}
|
||||
|
||||
|
||||
class oneplus(metaclass=LogBase):
|
||||
def __init__(self, fh, projid="18825", serial=123456, ATOBuild=0, Flash_Mode=0, cf=0, supported_functions=None,
|
||||
args=None, loglevel=logging.INFO):
|
||||
self.fh = fh
|
||||
self.__logger=self.__logger
|
||||
self.args = args
|
||||
self.ATOBuild = ATOBuild
|
||||
self.Flash_Mode = Flash_Mode
|
||||
self.cf = cf # CustFlag
|
||||
self.supported_functions = supported_functions
|
||||
self.__logger.setLevel(loglevel)
|
||||
self.info = self.__logger.info
|
||||
self.debug = self.__logger.debug
|
||||
self.error = self.__logger.error
|
||||
if projid == "":
|
||||
res = self.fh.detect_partition(arguments=args, partitionname="param")
|
||||
if res[0]:
|
||||
lun = res[1]
|
||||
rpartition = res[2]
|
||||
data = self.fh.cmd_read_buffer(lun, rpartition.sector, 1, False)
|
||||
value = data[24:24 + 5]
|
||||
try:
|
||||
test = int(value.decode('utf-8'))
|
||||
self.info("Oneplus protection with prjid %d detected" % test)
|
||||
projid = value.decode('utf-8')
|
||||
except:
|
||||
pass
|
||||
|
||||
if loglevel == logging.DEBUG:
|
||||
logfilename = "log.txt"
|
||||
filehandler = logging.FileHandler(logfilename)
|
||||
self.__logger.addHandler(filehandler)
|
||||
self.ops_parm = None
|
||||
self.ops = self.convert_projid(fh, projid, serial)
|
||||
|
||||
def getprodkey(self, projid):
|
||||
if projid in ["18825", "18801"]: # key_guacamoles, fajiita
|
||||
prodkey = "b2fad511325185e5"
|
||||
else: # key_op7t/op8/N10
|
||||
prodkey = "7016147d58e8c038"
|
||||
return prodkey
|
||||
|
||||
def convert_projid(self, fh, projid, serial):
|
||||
prodkey = self.getprodkey(projid)
|
||||
pk = ""
|
||||
val = bytearray(b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
|
||||
for i in range(0, 16):
|
||||
rand = int(random.randint(0, 0x100))
|
||||
nr = (rand & 0xFF) % 0x3E
|
||||
pk += chr(val[nr])
|
||||
|
||||
if projid in deviceconfig:
|
||||
version = deviceconfig[projid]["version"]
|
||||
cm = deviceconfig[projid]["cm"]
|
||||
if version == 1:
|
||||
return oneplus1(fh, projid, serial, pk, prodkey, self.cf)
|
||||
elif version == 2:
|
||||
if cm is not None:
|
||||
return oneplus1(fh, cm, serial, pk, prodkey, self.cf)
|
||||
else:
|
||||
assert "Device is not supported"
|
||||
exit(0)
|
||||
elif version == 3:
|
||||
if cm is not None:
|
||||
oneplus2(fh, cm, serial, pk, prodkey, self.ATOBuild, self.Flash_Mode, self.cf)
|
||||
else:
|
||||
assert "Device is not supported"
|
||||
exit(0)
|
||||
return None
|
||||
|
||||
def run(self):
|
||||
if self.ops is not None:
|
||||
if "demacia" in self.supported_functions:
|
||||
if not self.ops.run("demacia"):
|
||||
exit(0)
|
||||
if "SetNetType" in self.supported_functions:
|
||||
self.fh.cmd_send(f"SetNetType")
|
||||
elif "setprojmodel" in self.supported_functions:
|
||||
if not self.ops.run(""):
|
||||
exit(0)
|
||||
if "setprocstart" in self.supported_functions:
|
||||
if not self.ops.run(""):
|
||||
exit(0)
|
||||
return True
|
||||
|
||||
def setprojmodel_verify(self, pk, token):
|
||||
if self.ops.setprojmodel_verify:
|
||||
return self.ops.setprojmodel_verify(pk, token)
|
||||
|
||||
def setswprojmodel_verify(self, pk, token, device_timestamp):
|
||||
if self.ops.setswprojmodel_verify:
|
||||
return self.ops.setswprojmodel_verify(pk, token, device_timestamp)
|
||||
|
||||
def program_verify(self, pk, token, tokendata):
|
||||
if self.ops.program_verify:
|
||||
return self.ops.program_verify(pk, token, tokendata)
|
||||
|
||||
def generatetoken(self, program=False, device_timestamp="123456789"):
|
||||
return self.ops.generatetoken(program=program, device_timestamp=device_timestamp)
|
||||
|
||||
def demacia(self):
|
||||
if self.ops.demacia():
|
||||
return self.ops.demacia()
|
||||
|
||||
def enable_ops(self, data, enable, projid, serial):
|
||||
return None
|
||||
|
||||
def addpatch(self):
|
||||
if "setprojmodel" in self.supported_functions or "setswprojmodel" in self.supported_functions:
|
||||
pk, token = self.ops.generatetoken(True)
|
||||
return f"pk=\"{pk}\" token=\"{token}\" "
|
||||
else:
|
||||
return ""
|
||||
|
||||
def addprogram(self):
|
||||
if "setprojmodel" in self.supported_functions or "setswprojmodel" in self.supported_functions:
|
||||
pk, token = self.ops.generatetoken(True)
|
||||
return f"pk=\"{pk}\" token=\"{token}\" "
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
class oneplus1:
|
||||
def __init__(self, fh, ModelVerifyPrjName="18825", serial=123456, pk="", prodkey="", cf=0):
|
||||
self.pk = pk
|
||||
self.prodkey = prodkey
|
||||
self.ModelVerifyPrjName = ModelVerifyPrjName
|
||||
self.fh = fh
|
||||
self.random_postfix = "8MwDdWXZO7sj0PF3"
|
||||
self.Version = "guacamoles_21_O.22_191107"
|
||||
self.cf = str(cf)
|
||||
self.soc_sn = str(serial)
|
||||
|
||||
def crypt_token(self, data, pk, decrypt=False, demacia=False):
|
||||
aes = cryptutils().aes()
|
||||
if demacia:
|
||||
aeskey = b"\x01\x63\xA0\xD1\xFD\xE2\x67\x11" + bytes(pk, 'utf-8') + b"\x48\x27\xC2\x08\xFB\xB0\xE6\xF0"
|
||||
aesiv = b"\x96\xE0\x79\x0C\xAE\x2B\xB4\xAF\x68\x4C\x36\xCB\x0B\xEC\x49\xCE"
|
||||
else:
|
||||
aeskey = b"\x10\x45\x63\x87\xE3\x7E\x23\x71" + bytes(pk, 'utf-8') + b"\xA2\xD4\xA0\x74\x0f\xD3\x28\x96"
|
||||
aesiv = b"\x9D\x61\x4A\x1E\xAC\x81\xC9\xB2\xD3\x76\xD7\x49\x31\x03\x63\x79"
|
||||
if decrypt:
|
||||
cdata = unhexlify(data)
|
||||
result = aes.aes_cbc(aeskey, aesiv, cdata)
|
||||
result = result.rstrip(b'\x00')
|
||||
if result[:16] == b"907heavyworkload":
|
||||
return result
|
||||
else:
|
||||
return result.decode('utf-8').split(',')
|
||||
else:
|
||||
if not demacia:
|
||||
while len(data) < 256:
|
||||
data += "\x00"
|
||||
pdata = bytes(data, 'utf-8')
|
||||
else:
|
||||
while len(data) < 256:
|
||||
data += b"\x00"
|
||||
pdata = data
|
||||
result = aes.aes_cbc(aeskey, aesiv, pdata, False)
|
||||
rdata = hexlify(result)
|
||||
return rdata.upper().decode('utf-8')
|
||||
|
||||
def cmd_setpro(self):
|
||||
pk, token = self.generatetoken(False)
|
||||
data = "<?xml version=\"1.0\" ?>\n<data>\n<setprojmodel token=\"" + token + "\" pk=\"" + pk + "\" />\n</data>"
|
||||
return data
|
||||
|
||||
def cmd_dem(self):
|
||||
pk, token = self.demacia()
|
||||
data = "<?xml version=\"1.0\" ?>\n<data>\n<demacia token=\"" + token + "\" pk=\"" + pk + "\" />\n</data>"
|
||||
return data
|
||||
|
||||
def generatetoken(self, program=False, device_timestamp=None):
|
||||
timestamp = str(int(time.time()))
|
||||
ha = cryptutils().hash()
|
||||
h1 = self.prodkey + self.ModelVerifyPrjName + self.random_postfix
|
||||
ModelVerifyHashToken = hexlify(ha.sha256(bytes(h1, 'utf-8'))).decode('utf-8').upper()
|
||||
# ModelVerifyPrjName=0x1C [0]
|
||||
# random_postfix=0x2D [1]
|
||||
# verify_hash=0x3E [2] Len:0x41
|
||||
# ver=0x90 [3]
|
||||
# cf=0x4 [4]
|
||||
# sn=0x14 [5]
|
||||
# ts=0x7f [6] Len:0x11
|
||||
# secret=0xd1 (hash store) [7], len:0x41
|
||||
# 0x7, Len:0x11
|
||||
# 0x24, Len:0x41
|
||||
# 0x1c 0x4 0x14 0x90 0x7f ModelVerifyHashToken
|
||||
h2 = "c4b95538c57df231" + self.ModelVerifyPrjName + self.cf + self.soc_sn + self.Version + \
|
||||
timestamp + ModelVerifyHashToken + "5b0217457e49381b"
|
||||
secret = hexlify(ha.sha256(bytes(h2, 'utf-8'))).decode('utf-8').upper() # 0xd1
|
||||
if program:
|
||||
items = [timestamp, secret]
|
||||
else:
|
||||
items = [self.ModelVerifyPrjName, self.random_postfix, ModelVerifyHashToken, self.Version, self.cf,
|
||||
self.soc_sn, timestamp, secret]
|
||||
data = ""
|
||||
for item in items:
|
||||
data += item + ","
|
||||
data = data[:-1]
|
||||
token = self.crypt_token(data, self.pk)
|
||||
return self.pk, token
|
||||
|
||||
def setprojmodel_verify(self, pk, token):
|
||||
self.pk = pk
|
||||
ha = cryptutils().hash()
|
||||
items = self.crypt_token(token, pk, True, False)
|
||||
info = ["Projid", "ModelVerifyHashToken", "Hash1", "FirmwareString", "CustFlag", "SOC_Serial", "Timestamp",
|
||||
"secret"]
|
||||
i = 0
|
||||
print()
|
||||
if len(info) == len(items):
|
||||
for item in items:
|
||||
print(info[i] + "=" + item)
|
||||
i += 1
|
||||
# Old
|
||||
# 0=ModelVerifyPrjName [param+0x1C]
|
||||
# 1=random_postfix [param+0x2D]
|
||||
# 2=hash(key+0+1) [ModelVerifyHashToken param+0x3E]
|
||||
# 3=ver [param_1+0x90]
|
||||
# 4=cf [param_1+4]
|
||||
# 5=serial? [param_1+0x14]
|
||||
# 6=timestamp [param_1+0x7F]
|
||||
|
||||
hash1 = self.prodkey + items[0] + items[1]
|
||||
res1 = hexlify(ha.sha256(bytes(hash1, 'utf-8'))).decode('utf-8').upper()
|
||||
if items[2] != res1:
|
||||
print("Hash1 failed !")
|
||||
return
|
||||
# ModelVerifyPrjName cf sn ver ts ModelVerifyHashToken
|
||||
secret = "c4b95538c57df231" + items[0] + items[4] + items[5] + items[3] + items[6] + \
|
||||
items[2] + "5b0217457e49381b"
|
||||
res2 = hexlify(ha.sha256(bytes(secret, 'utf-8'))).decode('utf-8').upper()
|
||||
if items[7] != res2:
|
||||
print("secret failed !")
|
||||
return
|
||||
print("setprojmodel good")
|
||||
return items
|
||||
|
||||
def toSigned32(self, n):
|
||||
n = n & 0xffffffff
|
||||
return (n ^ 0x80000000) - 0x80000000
|
||||
|
||||
def demacia(self):
|
||||
"""
|
||||
return "<?xml version=\"1.0\" ?>\n<data>\n " + \
|
||||
"<program SECTOR_SIZE_IN_BYTES=\"4096\" filename=\"param.bin\" " + \
|
||||
"num_partition_sectors=\"256\" partofsingleimage=\"0\" physical_partition_number=\"0\" " + \
|
||||
"read_back_verify=\"1\" start_sector=\"8456\" token=\""+token+"\" pk=\""+pk+"\" />\n</data>"
|
||||
"""
|
||||
ha = cryptutils().hash()
|
||||
serial = self.soc_sn
|
||||
while len(serial) < 10:
|
||||
serial = '0' + serial
|
||||
hash1 = "2e7006834dafe8ad" + serial + "a6674c6b039707ff"
|
||||
data = b"907heavyworkload" + ha.sha256(bytes(hash1, 'utf-8'))
|
||||
token = self.crypt_token(data, self.pk, False, True)
|
||||
return self.pk, token
|
||||
|
||||
def run(self, flag):
|
||||
if flag == "demacia":
|
||||
pk, token = self.demacia()
|
||||
res = self.fh.cmd_send(f"demacia token=\"{token}\" pk=\"{pk}\"")
|
||||
if b"verify_res=\"0\"" not in res:
|
||||
print("Demacia failed:")
|
||||
print(res)
|
||||
return False
|
||||
pk, token = self.generatetoken(False)
|
||||
res = self.fh.cmd_send(f"setprojmodel token=\"{token}\" pk=\"{pk}\"")
|
||||
if b"model_check=\"0\"" not in res or b"auth_token_verify=\"0\"" not in res:
|
||||
print("Setprojmodel failed.")
|
||||
print(res)
|
||||
return False
|
||||
return True
|
||||
|
||||
def program_verify(self, pk, token, tokendata):
|
||||
print()
|
||||
self.pk = pk
|
||||
items = self.crypt_token(token, pk, True, False)
|
||||
if len(items) == 2:
|
||||
print("Timestamp=" + items[0])
|
||||
print("secret=" + items[1])
|
||||
if items[0] != tokendata[6] or items[1] != tokendata[7]:
|
||||
print("Hash failed !")
|
||||
return
|
||||
print("program good")
|
||||
|
||||
|
||||
class oneplus2(metaclass=LogBase):
|
||||
def __init__(self, fh, ModelVerifyPrjName="20889", serial=123456, pk="", prodkey="", ATOBuild=0, Flash_Mode=0,
|
||||
cf=0, loglevel=logging.INFO):
|
||||
self.ModelVerifyPrjName = ModelVerifyPrjName
|
||||
self.pk = pk
|
||||
self.fh = fh
|
||||
self.prodkey = prodkey
|
||||
self.random_postfix = "c75oVnz8yUgLZObh" # ModelVerifyRandom
|
||||
self.Version = "billie8_14_E.01_201028" # Version
|
||||
self.device_id = str(int(ModelVerifyPrjName, 16))
|
||||
self.flash_mode = str(Flash_Mode)
|
||||
self.ato_build_state = str(ATOBuild)
|
||||
self.soc_sn = str(serial)
|
||||
self.__logger.setLevel(loglevel)
|
||||
if loglevel == logging.DEBUG:
|
||||
logfilename = "log.txt"
|
||||
fh = logging.FileHandler(logfilename)
|
||||
self.__logger.addHandler(fh)
|
||||
|
||||
def crypt_token(self, data, pk, device_timestamp, decrypt=False):
|
||||
aes = cryptutils().aes()
|
||||
aeskey = b"\x46\xA5\x97\x30\xBB\x0D\x41\xE8" + bytes(pk, 'utf-8') + \
|
||||
pack("<Q", int(device_timestamp, 10)) # we get this using setprocstart
|
||||
aesiv = b"\xDC\x91\x0D\x88\xE3\xC6\xEE\x65\xF0\xC7\x44\xB4\x02\x30\xCE\x40"
|
||||
if decrypt:
|
||||
cdata = unhexlify(data)
|
||||
result = aes.aes_cbc(aeskey, aesiv, cdata)
|
||||
result = result.rstrip(b'\x00')
|
||||
return result.decode('utf-8').split(',')
|
||||
else:
|
||||
while len(data) < 0x200:
|
||||
data += "\x00"
|
||||
pdata = bytes(data, 'utf-8')
|
||||
result = aes.aes_cbc(aeskey, aesiv, pdata, False)
|
||||
rdata = hexlify(result)
|
||||
return rdata.upper().decode('utf-8')
|
||||
|
||||
def generatetoken(self, program=False, device_timestamp=None): # setswprojmodel
|
||||
timestamp = str(int(time.time()))
|
||||
ha = cryptutils().hash()
|
||||
h1 = self.prodkey + self.ModelVerifyPrjName + self.random_postfix
|
||||
ModelVerifyHashToken = hexlify(ha.sha256(bytes(h1, 'utf-8'))).decode('utf-8').upper()
|
||||
# 0x1c[0x10] 0x4 0x90 0x7f 0x3e ModelVerifyHashToken/verify_hash
|
||||
h2 = self.prodkey + self.ModelVerifyPrjName + self.soc_sn + self.Version + timestamp + \
|
||||
ModelVerifyHashToken + "8f7359c8a2951e8c"
|
||||
secret = hexlify(ha.sha256(bytes(h2, 'utf-8'))).decode('utf-8').upper()
|
||||
if program:
|
||||
items = [timestamp, secret]
|
||||
else: # 0x1C[0x10] 0x2D[0x11] 0x3E[0x41] 0x10[0x4]
|
||||
# 0x14[0x4] 0x90[0x40] 0x4[0x4] 0x114[0x4] 0x7F[0x10] 0xD1[0x41]
|
||||
items = [self.ModelVerifyPrjName, self.random_postfix, ModelVerifyHashToken, self.ato_build_state,
|
||||
self.flash_mode, self.Version, self.soc_sn, self.device_id, timestamp, secret]
|
||||
data = ""
|
||||
for item in items:
|
||||
data += item + ","
|
||||
data = data[:-1]
|
||||
token = self.crypt_token(data, self.pk, device_timestamp)
|
||||
return self.pk, token
|
||||
|
||||
def run(self, flag):
|
||||
res = self.fh.cmd_send(f"setprocstart")
|
||||
if not b"device_timestamp" in res:
|
||||
print("Setprocstart failed.")
|
||||
print(res.decode('utf-8'))
|
||||
return False
|
||||
data = res.decode('utf-8')
|
||||
device_timestamp = data[data.rfind("device_timestamp"):].split("\"")[1]
|
||||
pk, token = self.generatetoken(False, device_timestamp)
|
||||
res = self.fh.cmd_send(f"setswprojmodel token=\"{token}\" pk=\"{pk}\"")
|
||||
if not b"model_check=\"0\"" in res or not b"auth_token_verify=\"0\"" in res:
|
||||
print("Setswprojmodel failed.")
|
||||
print(res.decode('utf-8'))
|
||||
return False
|
||||
return True
|
||||
|
||||
def setswprojmodel_verify(self, pk, token, device_timestamp):
|
||||
self.pk = pk
|
||||
ha = cryptutils().hash()
|
||||
items = self.crypt_token(token, pk, device_timestamp, True)
|
||||
info = ["ModelVerifyPrjName", "random_postfix", "ModelVerifyHashToken", "ato_build_state", "flash_mode",
|
||||
"Version", "soc_sn", "cf", "timestamp", "secret"]
|
||||
i = 0
|
||||
print()
|
||||
if len(info) == len(items):
|
||||
for item in items:
|
||||
print(info[i] + "=" + item)
|
||||
i += 1
|
||||
|
||||
# New
|
||||
# 0=ModelVerifyPrjName [param+0x1C]
|
||||
# 1=random_postfix [param+0x2D]
|
||||
# 2=hash [param_1+0xd1]
|
||||
# 3=[ATO Build state param+0x3E]
|
||||
# 4=[flash mode param_1+0x10]
|
||||
# 5=[img ver param+0x14]
|
||||
# 6=soc sn [param_1+0x90]
|
||||
# 7=cf [param_1+4]
|
||||
# 8=timestamp [param_1+0x114]
|
||||
# 9=secret [param_1+0x7f]
|
||||
|
||||
hash1 = self.prodkey + items[0] + items[1]
|
||||
res1 = hexlify(ha.sha256(bytes(hash1, 'utf-8'))).decode('utf-8').upper()
|
||||
if items[2] != res1:
|
||||
print("Hash1 failed !")
|
||||
return
|
||||
# ModelVerifyPrjName sn ver ts ModelVerifyHashToken
|
||||
secret = self.prodkey + items[0] + items[6] + items[5] + items[8] + items[2] \
|
||||
+ "8f7359c8a2951e8c"
|
||||
# h2 = self.prodkey + self.ModelVerifyPrjName + self.soc_sn + self.Version + timestamp +
|
||||
# ModelVerifyHashToken + "8f7359c8a2951e8c"
|
||||
res2 = hexlify(ha.sha256(bytes(secret, 'utf-8'))).decode('utf-8').upper()
|
||||
if items[9] != res2:
|
||||
print("secret failed !")
|
||||
return
|
||||
print("setswprojmodel good")
|
||||
return items
|
||||
|
||||
|
||||
def main():
|
||||
from docopt import docopt
|
||||
args = docopt(__doc__, version='oneplus 1.2')
|
||||
# filename="param_jacob_7pro.bin"
|
||||
# filename="chris/param.bin"
|
||||
if args["rawxml"]:
|
||||
projid = args["--projid"][0]
|
||||
serial = args["--serial"]
|
||||
op = oneplus(None, projid=projid, serial=serial)
|
||||
# 18831 2799496336,2799496336
|
||||
# 18825
|
||||
# 18857 7 Europe
|
||||
pk, token = op.demacia()
|
||||
print(
|
||||
f"./edl.py rawxml \"<?xml version=\\\"1.0\\\" ?><data><demacia " +
|
||||
f"token=\\\"{token}\\\" pk=\\\"{pk}\\\" /></data>\"")
|
||||
pk, token = op.generatetoken(False)
|
||||
print(
|
||||
f"./edl.py rawxml \"<?xml version=\\\"1.0\\\" ?><data><setprojmodel " +
|
||||
f"token=\\\"{token}\\\" pk=\\\"{pk}\\\" /></data>\" --debugmode")
|
||||
elif args["rawnewxml"]:
|
||||
serial = args["--serial"]
|
||||
device_timestamp = args["--ts"]
|
||||
op2 = oneplus(None, projid="20889", serial=serial, ATOBuild=0, Flash_Mode=0, cf=0)
|
||||
# 20889 OP N10 5G Europe
|
||||
print(f"./edl.py rawxml \"<?xml version=\\\"1.0\\\" ?><data><setprocstart /></data>\"")
|
||||
# Response should be : <?xml version="1.0" ?><data><response value=1 device_timestamp="%llu" /></data>
|
||||
pk, token = op2.generatetoken(False, device_timestamp)
|
||||
print(
|
||||
f"./edl.py rawxml \"<?xml version=\\\"1.0\\\" ?><data><setswprojmodel " +
|
||||
f"token=\\\"{token}\\\" pk=\\\"{pk}\\\" /></data>\" --debugmode")
|
||||
elif args["setprojmodel_verify"]:
|
||||
projid = args["--projid"][0]
|
||||
op = oneplus(None, projid=projid, serial=123456)
|
||||
token = args["<token>"]
|
||||
pk = args["<pk>"]
|
||||
# setprojmodel_verify 2BA77B345812E4E45DDB5E407CF9B0F20BCD3E4F0C504A86A3DAA7D70643D0D86F4F5DAEE99E21093D26FF8A8A
|
||||
# 7C2CCFED387FA4C7D3BC6D8B8C2CC2D27D398886FC150C98CDC521699568C4A419D7E2F2C1A33F6B57AA7CCB5F
|
||||
# 39D69BB87463986B2CADDD55A41F0F9404C3FB08B0325BFDFCFDE05D1D8314D22F39979A289505D5050D854092
|
||||
# CFC9FA3C101A267DD3ECA0442BF89066365ABA6607D43743D86B47B228BAAC5538B622644D74FD4049BE37C520
|
||||
# 76DE1B4BFE75187A7B0EE88E6C26E106570B8C0541C4693878BE9B23DEB8E4C530CFBFE9F25597FA3A86223711
|
||||
# 2CAF77F0D1EA4CC41EB201FFAE31036FC9E405BABAE43DE9C7E56FE1DC8E82 KHaJV1TfN45ofeLW 18865
|
||||
# setprojmodel_verify 633B7E2BBE68BAC392B3E10FC8FEAC09F152853805A6D91FAADDE5A631C7B5A6081C6156F7344BDF407ABF7598
|
||||
# 0A9E6DA96964D472FE94311FEAADF6A9032C623A1C5D5B9BDD68C5E049F13DF9D893422C1A44047B1AC8E05A0A
|
||||
# 2A942B15B409A933A06BAB09F41FB0A3A5C8FEB86B98D39739FA4E2ABDF471DE181646F7AA228C6EC81DB3BAF2
|
||||
# F2C3B5381FC9A722F9D11B6A101CAAE31ACD873B83B39AC07B7603EAA38B13F5D0B5E8F9236FB94B967AECE278
|
||||
# FEA280E9330636F7C6C72C36A6040F6B8BC3C56AEC9CB0C07360E14EA83D2F6DEC4613FA74D79C325A320B88F2
|
||||
# BF025CF9CE528E13169BA255E68909D7E902CE494B49514F6F57713D6F46BE Tgu1kbDW3NemNNqn 18831
|
||||
op.setprojmodel_verify(pk, token)
|
||||
elif args["program_verify"]:
|
||||
projid = args["--projid"][0]
|
||||
op = oneplus(None, projid=projid)
|
||||
token = args["<token>"]
|
||||
prog_token = args["<prog_token>"]
|
||||
pk = args["<pk>"]
|
||||
items = op.setprojmodel_verify(pk, token)
|
||||
op.program_verify(pk, prog_token, items)
|
||||
elif args["setswprojmodel_verify"]:
|
||||
projid = args["--projid"][0]
|
||||
device_timestamp = args["--ts"]
|
||||
op = oneplus(None, projid=projid, serial=123456)
|
||||
token = args["<token>"]
|
||||
pk = args["<pk>"]
|
||||
op.setswprojmodel_verify(pk, token, device_timestamp)
|
||||
|
||||
|
||||
def test_setswprojmodel_verify():
|
||||
deviceresp = b"RX:<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\nRX:<data>\nRX:<response value=\"ACK\" " + \
|
||||
b"device_timestamp=\"2507003650\" /></data>\n<?xmlversion=\"1.0\" ? ><data><setprocstart /></data>"
|
||||
projid = "20889"
|
||||
op = oneplus(None, projid=projid, serial=123456)
|
||||
data = deviceresp.decode('utf-8')
|
||||
device_timestamp = data[data.rfind("device_timestamp"):].split("\"")[1]
|
||||
pk, token = op.generatetoken(False, device_timestamp)
|
||||
if not op.setswprojmodel_verify(pk, token, device_timestamp):
|
||||
assert "Setswprojmodel error"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
45
edl/Library/Modules/xiaomi.py
Normal file
45
edl/Library/Modules/xiaomi.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) B.Kerler 2018-2021
|
||||
|
||||
import logging
|
||||
from edl.Library.utils import LogBase
|
||||
|
||||
|
||||
class xiaomi(metaclass=LogBase):
|
||||
def __init__(self, fh, projid="18825", serial=123456, ATOBuild=0, Flash_Mode=0, cf=0, supported_functions=None,
|
||||
loglevel=logging.INFO):
|
||||
self.fh = fh
|
||||
self.xiaomi_authdata = b"\x93\x6E\x3A\x8E\x57\x3C\xAD\x07\xC1\x67\x64\x4B\x61\x21\x78\x35\xD8\x5A\xD4\xFD" + \
|
||||
b"\xDB\x7D\x84\x0A\x2B\x72\x25\x43\x2F\xCD\xA1\x3A\x7C\x19\x2C\xFA\x97\x9E\xD1\x65" + \
|
||||
b"\x17\xE6\x97\x0B\x1B\x07\xDF\x6C\x51\x6F\xEC\x81\xF6\x96\x8F\xCF\x7F\xFD\xDB\xC3" + \
|
||||
b"\x97\xA1\x62\xC2\xCA\x3E\x5D\x76\x12\x4A\xA1\x76\x9F\x1B\x21\x64\xB3\x9B\x76\x93" + \
|
||||
b"\x0B\x4C\xC6\x75\x19\xF7\xF3\x39\x87\x76\x77\xF4\xE8\xAF\x25\x82\x86\x82\xBC\xBF" + \
|
||||
b"\x4E\x59\x3A\x57\xE7\xE3\x05\x32\x69\x92\x53\xE0\xB1\xCC\x5D\x9D\x0D\x55\x4A\xF2" + \
|
||||
b"\xBD\x46\xD5\x6F\x18\xD6\xE5\x29\x0B\xA4\xA0\xCA\xC2\x43\x1F\x9F\x19\xC4\xC1\xA3" + \
|
||||
b"\x9D\x76\x64\xFF\xAB\x48\xA9\xE1\x1A\x55\x93\x86\x81\x98\x35\xB8\x4D\xF5\x67\x5E" + \
|
||||
b"\x70\xD2\x5F\xDB\x51\x23\xE7\xB0\x40\xFE\x21\x10\x8F\x0A\xE6\xD7\xD9\xD2\x67\xF2" + \
|
||||
b"\xC9\xC6\x1A\xD0\x54\xC6\x84\x93\xDC\x4D\x33\xF7\x4D\x0C\xF2\xD4\xAA\xDC\xD4\x30" + \
|
||||
b"\x15\x2D\xB6\x7C\x22\xA1\x81\xAD\x6D\x77\x61\x63\x7F\x70\xCB\xDA\x88\x4C\xDC\x11" + \
|
||||
b"\x33\x72\x03\x83\x77\x90\xE6\x84\x5C\xA5\xA8\x76\x79\x30\xB9\xC2\x6F\xDA\x71\x27" + \
|
||||
b"\x25\x64\xCA\x34\x76\x3D\x35\x2F\x5F\xE4\x2A\xB7\x38\xFB\x38\xA5"
|
||||
self.__logger.setLevel(loglevel)
|
||||
if loglevel == logging.DEBUG:
|
||||
logfilename = "log.txt"
|
||||
fh = logging.FileHandler(logfilename)
|
||||
self.__logger.addHandler(fh)
|
||||
|
||||
def edl_auth(self):
|
||||
"""
|
||||
Poco F1, Redmi 5 Pro, 6 Pro, 7 Pro, 7A, 8, 8A, 8A Dual, 8A Pro, Y2, S2
|
||||
"""
|
||||
authcmd = b"<?xml version=\"1.0\" ?><data> <sig TargetName=\"sig\" size_in_bytes=\"256\" verbose=\"1\"/></data>"
|
||||
rsp = self.fh.xmlsend(authcmd)
|
||||
if rsp[0]:
|
||||
rsp = self.fh.xmlsend(self.xiaomi_authdata)
|
||||
if len(rsp) > 1:
|
||||
if rsp[0]:
|
||||
if b"EDL Authenticated" in rsp[2] or b"ACK" in rsp[2]:
|
||||
return True
|
||||
return True
|
||||
return False
|
BIN
edl/Library/TestFiles/gpt_sm8180x.bin
Normal file
BIN
edl/Library/TestFiles/gpt_sm8180x.bin
Normal file
Binary file not shown.
0
edl/Library/__init__.py
Executable file
0
edl/Library/__init__.py
Executable file
247
edl/Library/asmtools.py
Executable file
247
edl/Library/asmtools.py
Executable file
|
@ -0,0 +1,247 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) B.Kerler 2018-2021
|
||||
|
||||
from capstone import *
|
||||
from keystone import *
|
||||
from binascii import unhexlify
|
||||
import argparse
|
||||
|
||||
|
||||
def asm(code, cpu, mode, bigendian):
|
||||
if bigendian:
|
||||
little = KS_MODE_BIG_ENDIAN # big-endian mode
|
||||
else:
|
||||
little = KS_MODE_LITTLE_ENDIAN # little-endian mode (default mode)
|
||||
print("CPU: %s, MODE: %s" % (cpu, mode))
|
||||
ks = None
|
||||
if cpu == "arm":
|
||||
# ARM architecture (including Thumb, Thumb-2)
|
||||
if mode == "arm":
|
||||
ks = Ks(KS_ARCH_ARM, KS_MODE_ARM + little) # ARM mode
|
||||
elif mode == "thumb":
|
||||
ks = Ks(KS_ARCH_ARM, KS_MODE_THUMB + little) # THUMB mode (including Thumb-2)
|
||||
elif mode == "mclass":
|
||||
ks = Ks(KS_ARCH_ARM, KS_MODE_THUMB + little)
|
||||
elif mode == "v8":
|
||||
ks = Ks(KS_ARCH_ARM, KS_MODE_ARM + KS_MODE_V8 + little)
|
||||
elif cpu == "arm64":
|
||||
# ARM-64, also called AArch64
|
||||
ks = Ks(KS_ARCH_ARM64, little) # ARM mode
|
||||
elif cpu == "mips":
|
||||
# Mips architecture
|
||||
if mode == "micro":
|
||||
ks = Ks(KS_ARCH_MIPS, KS_MODE_MICRO + little) # MicroMips mode
|
||||
elif mode == "3":
|
||||
ks = Ks(KS_ARCH_MIPS, KS_MODE_MIPS3 + little) # Mips III ISA
|
||||
elif mode == "32R6":
|
||||
ks = Ks(KS_ARCH_MIPS, KS_MODE_MIPS32R6 + little) # Mips32r6 ISA
|
||||
elif mode == "32":
|
||||
ks = Ks(KS_ARCH_MIPS, KS_MODE_MIPS32 + little) # Mips32 ISA
|
||||
elif mode == "64":
|
||||
ks = Ks(KS_ARCH_MIPS, KS_MODE_MIPS64 + little) # Mips64 ISA
|
||||
elif cpu == "x86":
|
||||
# X86 architecture (including x86 & x86-64)
|
||||
if mode == "16":
|
||||
ks = Ks(KS_ARCH_X86, KS_MODE_16 + little) # 16-bit mode
|
||||
elif mode == "32":
|
||||
ks = Ks(KS_ARCH_X86, KS_MODE_32 + little) # 32-bit mode
|
||||
elif mode == "64":
|
||||
ks = Ks(KS_ARCH_X86, KS_MODE_64 + little) # 64-bit mode
|
||||
elif cpu == "ppc":
|
||||
# PowerPC architecture (currently unsupported)
|
||||
if mode == "32":
|
||||
ks = Ks(KS_ARCH_PPC, KS_MODE_PPC32 + little) # 32-bit mode
|
||||
elif mode == "64":
|
||||
ks = Ks(KS_ARCH_PPC, KS_MODE_PPC64 + little) # 64-bit mode
|
||||
elif mode == "qpx":
|
||||
ks = Ks(KS_ARCH_PPC, KS_MODE_QPX + little) # Quad Processing eXtensions mode
|
||||
elif cpu == "sparc":
|
||||
# Sparc architecture
|
||||
if mode == "32":
|
||||
ks = Ks(KS_ARCH_SPARC, KS_MODE_SPARC32 + little) # 32-bit mode
|
||||
elif mode == "64":
|
||||
ks = Ks(KS_ARCH_SPARC, KS_MODE_SPARC64 + little) # 64-bit mode
|
||||
elif mode == "v9":
|
||||
ks = Ks(KS_ARCH_SPARC, KS_MODE_V9 + little) # SparcV9 mode
|
||||
elif cpu == "systemz":
|
||||
ks = Ks(KS_ARCH_SYSTEMZ, KS_MODE_BIG_ENDIAN) # SystemZ architecture (S390X)
|
||||
elif cpu == "hexagon":
|
||||
ks = Ks(KS_ARCH_HEXAGON, KS_MODE_LITTLE_ENDIAN) # QDSP6 Hexagon Qualcomm
|
||||
|
||||
if ks is None:
|
||||
print("CPU and/or Mode not supported!")
|
||||
exit(0)
|
||||
|
||||
encoding, count = ks.asm(code)
|
||||
return encoding
|
||||
|
||||
|
||||
def disasm(code, cpu, mode, bigendian, size):
|
||||
cs = None
|
||||
if bigendian:
|
||||
little = CS_MODE_BIG_ENDIAN # big-endian mode
|
||||
else:
|
||||
little = CS_MODE_LITTLE_ENDIAN # little-endian mode (default mode)
|
||||
|
||||
if cpu == "arm":
|
||||
if mode == "arm":
|
||||
cs = Cs(CS_ARCH_ARM, CS_MODE_ARM + little) # ARM mode
|
||||
elif mode == "thumb":
|
||||
cs = Cs(CS_ARCH_ARM, CS_MODE_THUMB + little) # THUMB mode (including Thumb-2)
|
||||
elif mode == "mclass":
|
||||
cs = Cs(CS_ARCH_ARM, CS_MODE_THUMB + CS_MODE_MCLASS + little) # ARM Cortex-M
|
||||
elif mode == "v8":
|
||||
cs = Cs(CS_ARCH_ARM, CS_MODE_ARM + CS_MODE_V8 + little) # ARMv8 A32 encodings for ARM
|
||||
elif cpu == "arm64":
|
||||
cs = Cs(CS_ARCH_ARM64, CS_MODE_ARM + little)
|
||||
elif cpu == "mips":
|
||||
if mode == "micro":
|
||||
cs = Cs(CS_ARCH_MIPS, CS_MODE_MICRO + little) # MicroMips mode
|
||||
elif mode == "32":
|
||||
cs = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + little) # Mips III ISA
|
||||
elif mode == "64":
|
||||
cs = Cs(CS_ARCH_MIPS, CS_MODE_MIPS64 + little) # Mips III ISA
|
||||
elif mode == "32R6-Micro":
|
||||
cs = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32R6 + CS_MODE_MICRO + little) # Mips32r6 ISA
|
||||
elif mode == "32R6":
|
||||
cs = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32R6 + little) # General Purpose Registers are 64bit wide
|
||||
elif cpu == "x86":
|
||||
# X86 architecture (including x86 & x86-64)
|
||||
if mode == "16":
|
||||
cs = Cs(CS_ARCH_X86, CS_MODE_16 + little) # 16-bit mode
|
||||
elif mode == "32":
|
||||
cs = Cs(CS_ARCH_X86, CS_MODE_32 + little) # 32-bit mode
|
||||
elif mode == "64":
|
||||
cs = Cs(CS_ARCH_X86, CS_MODE_64 + little) # 64-bit mode
|
||||
elif cpu == "ppc":
|
||||
# PowerPC architecture (currently unsupported)
|
||||
if mode == "64":
|
||||
cs = Cs(CS_ARCH_PPC, little) # 64-bit mode
|
||||
elif cpu == "sparc":
|
||||
# Sparc architecture
|
||||
if mode == "None":
|
||||
cs = Cs(CS_ARCH_SPARC, CS_MODE_BIG_ENDIAN) # 32-bit mode
|
||||
elif mode == "v9":
|
||||
cs = Cs(CS_ARCH_SPARC, CS_MODE_BIG_ENDIAN + CS_MODE_V9) # SparcV9 mode
|
||||
elif cpu == "systemz":
|
||||
cs = Cs(CS_ARCH_SYSZ, 0) # SystemZ architecture (S390X)
|
||||
elif cpu == "xcore":
|
||||
cs = Cs(CS_ARCH_XCORE, 0) # XCore architecture
|
||||
|
||||
if cs is not None:
|
||||
print("CPU and/or mode not supported!")
|
||||
exit(0)
|
||||
|
||||
instr = []
|
||||
for i in cs.disasm(code, size):
|
||||
# print("0x%x:\t%s\t%s" % (i.address, i.mnemonic, i.op_str))
|
||||
instr.append("%s\t%s" % (i.mnemonic, i.op_str))
|
||||
return instr
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description='Disasm/Asm Tool (c) B. Kerler 2018')
|
||||
|
||||
parser.add_argument(
|
||||
'--infile', '-in',
|
||||
help='Input File',
|
||||
default='')
|
||||
parser.add_argument(
|
||||
'--outfile', '-out',
|
||||
help='Output File',
|
||||
default='')
|
||||
|
||||
parser.add_argument(
|
||||
'--cstyle', '-cstyle',
|
||||
help='Print in c style',
|
||||
action="store_true")
|
||||
|
||||
parser.add_argument(
|
||||
'--bigendian', '-bigendian',
|
||||
help='Big endian',
|
||||
action="store_true")
|
||||
|
||||
parser.add_argument(
|
||||
'--disasm', '-disasm',
|
||||
help='Disasm: arm[arm,thumb,mclass,v8],arm64[arm],mips[micro,3,32R6,GP64],x86[16,32,64],ppc[64],sparc[32,64,'
|
||||
'v9],systemz,xcore',
|
||||
default='')
|
||||
|
||||
parser.add_argument(
|
||||
'--asm', '-asm',
|
||||
help='Asm: arm[arm,thumb,mclass,v8],arm64[arm],mips[micro,32,64,32R6,32R6-Micro],x86[16,32,64],ppc[32,64,'
|
||||
'qpx],sparc[None,v9],systemz,hexagon',
|
||||
default='')
|
||||
|
||||
parser.add_argument(
|
||||
'--inp', '-input',
|
||||
help='Disasm: hexstring, Asm: instruction string input ',
|
||||
default='')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.asm == '' and args.disasm == '':
|
||||
print("[asmtools] Usage: -asm cpu,mode or -disasm cpu,mode")
|
||||
exit(0)
|
||||
|
||||
if args.infile == '' and args.inp == '':
|
||||
print("[asmtools] I must have an infile to work on (-in)")
|
||||
exit(0)
|
||||
|
||||
if args.asm != "":
|
||||
cpu, mode = args.asm.split(",")
|
||||
else:
|
||||
cpu, mode = args.disasm.split(",")
|
||||
|
||||
if args.inp != "":
|
||||
args.inp = args.inp.replace("\\n", "\n")
|
||||
if args.asm != "":
|
||||
aa = asm(args.inp, cpu, mode, args.bigendian)
|
||||
else:
|
||||
aa = disasm(unhexlify(args.inp), cpu, mode, args.bigendian, len(args.inp))
|
||||
else:
|
||||
with open(args.infile, "rb") as rf:
|
||||
code = rf.read()
|
||||
if args.asm != "":
|
||||
aa = asm(code, cpu, mode, args.bigendian)
|
||||
else:
|
||||
aa = disasm(code, cpu, mode, args.bigendian, len(code))
|
||||
|
||||
if args.outfile != "":
|
||||
with open(args.outfile, "wb") as wf:
|
||||
if args.asm != "":
|
||||
ba = bytearray()
|
||||
for i in aa:
|
||||
ba.append(i)
|
||||
wf.write(ba)
|
||||
else:
|
||||
wf.write(aa)
|
||||
else:
|
||||
if args.asm != "":
|
||||
sc = ""
|
||||
count = 0
|
||||
out = ""
|
||||
for i in aa:
|
||||
if args.cstyle:
|
||||
out += ("\\x%02x" % i)
|
||||
else:
|
||||
out += ("%02x" % i)
|
||||
sc += "%02x" % i
|
||||
count += 1
|
||||
print(out)
|
||||
else:
|
||||
print(aa)
|
||||
'''
|
||||
segment=bytearray(code[0x01C97C:0x01C990])
|
||||
segment[7]=0xE1
|
||||
segment[11]=0xE1
|
||||
segment[15]=0xE5
|
||||
segment[19]=0xEA
|
||||
print(hex(segment[7]))
|
||||
print(disasm(bytes(segment),len(segment)))
|
||||
'''
|
||||
|
||||
|
||||
main()
|
500
edl/Library/cryptutils.py
Executable file
500
edl/Library/cryptutils.py
Executable file
|
@ -0,0 +1,500 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) B.Kerler 2018-2019
|
||||
|
||||
import hashlib
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Util import Counter
|
||||
from Crypto.Hash import CMAC
|
||||
from Crypto.Util.number import long_to_bytes, bytes_to_long
|
||||
from binascii import hexlify, unhexlify
|
||||
|
||||
|
||||
class PKCS1BaseException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class DecryptionError(PKCS1BaseException):
|
||||
pass
|
||||
|
||||
|
||||
class MessageTooLong(PKCS1BaseException):
|
||||
pass
|
||||
|
||||
|
||||
class WrongLength(PKCS1BaseException):
|
||||
pass
|
||||
|
||||
|
||||
class MessageTooShort(PKCS1BaseException):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidSignature(PKCS1BaseException):
|
||||
pass
|
||||
|
||||
|
||||
class RSAModulusTooShort(PKCS1BaseException):
|
||||
pass
|
||||
|
||||
|
||||
class IntegerTooLarge(PKCS1BaseException):
|
||||
pass
|
||||
|
||||
|
||||
class MessageRepresentativeOutOfRange(PKCS1BaseException):
|
||||
pass
|
||||
|
||||
|
||||
class CiphertextRepresentativeOutOfRange(PKCS1BaseException):
|
||||
pass
|
||||
|
||||
|
||||
class SignatureRepresentativeOutOfRange(PKCS1BaseException):
|
||||
pass
|
||||
|
||||
|
||||
class EncodingError(PKCS1BaseException):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidInputException(Exception):
|
||||
def __init__(self, msg):
|
||||
self.msg = msg
|
||||
|
||||
def __str__(self):
|
||||
return str(self.msg)
|
||||
|
||||
|
||||
class InvalidTagException(Exception):
|
||||
def __str__(self):
|
||||
return 'The authentication tag is invalid.'
|
||||
|
||||
|
||||
class cryptutils:
|
||||
class aes:
|
||||
class AES_GCM:
|
||||
# Galois/Counter Mode with AES-128 and 96-bit IV
|
||||
"""
|
||||
Example:
|
||||
master_key = 0x0ADAABC70895E008147A48C27791F654 #54F69177C2487A1408E09508C7ABDA0A
|
||||
init_value = 0x2883B4173F9A838437C1CD86CCFAA5ED #EDA5FACC86CDC13784839A3F17B48328
|
||||
auth_tag = 46D1FA806ADA1A916E6D0D0B55A40C1F94D7820D110F3DFC984AA3EEC9D67521
|
||||
ciphertext = b"\x8A\x40\x9D\xF8\x76\x09\xCA\x10\x36\xB3\xFA\x86\x20\xC5\x85\xA3"+ \
|
||||
b"\xE3\x8E\x17\x14\x40\xBD\x6B\xA7\x26\x1F\x0B\xFE\xC5\x0A\xB0\xF0"+\
|
||||
b"\xCF\x69\x2E\x76\x18\x6D\x96\x9E\x83\x87\x63\xC7\x15\x7C\x1F\x28"+\
|
||||
b"\xEE\xE8\xF1\xD6\x1F\x02\x2A\xF1\xA2\x43\x8A\xCF\x7C\xF2\x66\x37"+\
|
||||
b"\x8B\x49\x1D\xC5\xDC\xE2\x54\x77\xED\x2F\x17\x5B\xA9\xFC\x8A\x81"+\
|
||||
b"\x60\xF6\x5A\x22\x39\xCA\x79\x32\x9B\xDB\x49\x50\xCE\x74\x2C\x56"+\
|
||||
b"\xDB\x97\xCA\x13\xDD\x25\xA3\x3C\x0F\x53\xDD\x38\xBF\x7B\x8B\xDA"+\
|
||||
b"\xD6\x74\x38\x87\x96\xA8\x10\x5A\x96\x38\x39\x7F\xFD\xEC\xC7\x62"+\
|
||||
b"\x06\x44\xF4\x0F\x78\xD6\x3D\x1A\xC5\x40\x4B\x3B\x8C\xBE\xE6\x76"+\
|
||||
b"\x65\xFA\x40\xDA\xD3\xF0\xF2\x19\x35\xB7\xB2\x91\xFC\x18\x2C\x53"+\
|
||||
b"\xA2\x3F\x1A\xA7\x4F\xFC\x42\xAE\xC1\x97\x89\xAB\x7E\x9B\xA1\x5C"+\
|
||||
b"\x3A\x3B\x2F\x01\x60\xB1\xC5\x30\x7C\xB7\x2B\xD5\xAF\x27\xA0\x4C"+\
|
||||
b"\xE9\x80\xC5\xB4\xEC\xFB\xD7\x59\xE8\x5D\xEE\xB5\x6F\x3B\xA7\xDE"+\
|
||||
b"\xDA\xD8\x55\x09\x7A\x5A\xAD\x6C\x13\x2D\xD1\x23\x7C\x13\x5F\x84"+\
|
||||
b"\x35\x29\x51\x55\xF4\x53\x12\x9C\x86\x7A\x77\x2B\xE2\x7B\x01\xA2"+\
|
||||
b"\x6B\xC8\x5D\xD8\xCA\x92\xFB\x32\x0A\x09\xAE\xB3\x45\x8D\x0B\x60"+\
|
||||
b"\x9D\xEB\xB7\x02\x07\xAB\x4A\x24\xF6\xA1\xE7\x59\xA0\xC4\xB1\xFB"+\
|
||||
b"\x44\xAD\x32\xC7\xD4\x8F\xC6\x0C\x33\xD5\x88\x82\xF4\x9A\xA2\x7C"+\
|
||||
b"\xDC\x56\x90\x96\x3C\xBC\xCF\x95\x17\x22\x55\x64\x67\x62\x52\x86"+\
|
||||
b"\xFA\x3B\xFC\xAA\xC7\x1B\xDE\x7F\x01\xB3\x61\x8C\x28\xAE\x64\x7E"+\
|
||||
b"\x43\xF0\x5A\x50\x60\x50\x85\xD4\xC4\xA6\x92\xC7\x8B\xE5\x04\x80"+\
|
||||
b"\x74\x0F\xBA\xEB\x7C\x2C\x81\x07\x99\x22\x51\xD1\x9E\xE1\x59\xEE"+\
|
||||
b"\x77\xC2\x13\x2C\x46\x16\x92\x9A\x69\xD9\x01\x75\x31\xA6\x20\xB9"+\
|
||||
b"\x13\x46\x55\xF7\x8C\xC6\xB8\x7C\x8F\xAC\x00\x1A\x58\x68\xC7\xAD"+\
|
||||
b"\x4E\x34\xB9\xEF\x5F\xCD\x87\x12\x0E\x8A\xEA\xD2\x4D\x66\x5E\x40"+\
|
||||
b"\xBD\x1D\x30\x8A\x83\xB8\x4F\xC2\xAB\x28\x58\x6C\xEA\xDB\xF5\x87"+\
|
||||
b"\xA0\x62\x9E\xF9\xF4\xE7\xE8\x65"
|
||||
my_gcm = AES_GCM(master_key)
|
||||
decrypted = my_gcm.decrypt(init_value, ciphertext, auth_tag)
|
||||
"""
|
||||
|
||||
def __init__(self, master_key):
|
||||
self.change_key(master_key)
|
||||
|
||||
# GF(2^128) defined by 1 + a + a^2 + a^7 + a^128
|
||||
# Please note the MSB is x0 and LSB is x127
|
||||
def gf_2_128_mul(self, x, y):
|
||||
assert x < (1 << 128)
|
||||
assert y < (1 << 128)
|
||||
res = 0
|
||||
for i in range(127, -1, -1):
|
||||
res ^= x * ((y >> i) & 1) # branchless
|
||||
x = (x >> 1) ^ ((x & 1) * 0xE1000000000000000000000000000000)
|
||||
assert res < 1 << 128
|
||||
return res
|
||||
|
||||
def change_key(self, master_key):
|
||||
if master_key >= (1 << 128):
|
||||
raise InvalidInputException('Master key should be 128-bit')
|
||||
|
||||
self.__master_key = long_to_bytes(master_key, 16)
|
||||
self.__aes_ecb = AES.new(self.__master_key, AES.MODE_ECB)
|
||||
self.__auth_key = bytes_to_long(self.__aes_ecb.encrypt(b'\x00' * 16))
|
||||
|
||||
# precompute the table for multiplication in finite field
|
||||
table = [] # for 8-bit
|
||||
for i in range(16):
|
||||
row = []
|
||||
for j in range(256):
|
||||
row.append(self.gf_2_128_mul(self.__auth_key, j << (8 * i)))
|
||||
table.append(tuple(row))
|
||||
self.__pre_table = tuple(table)
|
||||
|
||||
self.prev_init_value = None # reset
|
||||
|
||||
def __times_auth_key(self, val):
|
||||
res = 0
|
||||
for i in range(16):
|
||||
res ^= self.__pre_table[i][val & 0xFF]
|
||||
val >>= 8
|
||||
return res
|
||||
|
||||
def __ghash(self, aad, txt):
|
||||
len_aad = len(aad)
|
||||
len_txt = len(txt)
|
||||
|
||||
# padding
|
||||
if 0 == len_aad % 16:
|
||||
data = aad
|
||||
else:
|
||||
data = aad + b'\x00' * (16 - len_aad % 16)
|
||||
if 0 == len_txt % 16:
|
||||
data += txt
|
||||
else:
|
||||
data += txt + b'\x00' * (16 - len_txt % 16)
|
||||
|
||||
tag = 0
|
||||
assert len(data) % 16 == 0
|
||||
for i in range(len(data) // 16):
|
||||
tag ^= bytes_to_long(data[i * 16: (i + 1) * 16])
|
||||
tag = self.__times_auth_key(tag)
|
||||
# print 'X\t', hex(tag)
|
||||
tag ^= ((8 * len_aad) << 64) | (8 * len_txt)
|
||||
tag = self.__times_auth_key(tag)
|
||||
|
||||
return tag
|
||||
|
||||
def encrypt(self, init_value, plaintext, auth_data=b''):
|
||||
if init_value >= (1 << 96):
|
||||
raise InvalidInputException('IV should be 96-bit')
|
||||
# a naive checking for IV reuse
|
||||
if init_value == self.prev_init_value:
|
||||
raise InvalidInputException('IV must not be reused!')
|
||||
self.prev_init_value = init_value
|
||||
|
||||
len_plaintext = len(plaintext)
|
||||
# len_auth_data = len(auth_data)
|
||||
|
||||
if len_plaintext > 0:
|
||||
counter = Counter.new(
|
||||
nbits=32,
|
||||
prefix=long_to_bytes(init_value, 12),
|
||||
initial_value=2, # notice this
|
||||
allow_wraparound=False)
|
||||
aes_ctr = AES.new(self.__master_key, AES.MODE_CTR, counter=counter)
|
||||
|
||||
if 0 != len_plaintext % 16:
|
||||
padded_plaintext = plaintext + \
|
||||
b'\x00' * (16 - len_plaintext % 16)
|
||||
else:
|
||||
padded_plaintext = plaintext
|
||||
ciphertext = aes_ctr.encrypt(padded_plaintext)[:len_plaintext]
|
||||
|
||||
else:
|
||||
ciphertext = b''
|
||||
|
||||
auth_tag = self.__ghash(auth_data, ciphertext)
|
||||
# print 'GHASH\t', hex(auth_tag)
|
||||
auth_tag ^= bytes_to_long(self.__aes_ecb.encrypt(
|
||||
long_to_bytes((init_value << 32) | 1, 16)))
|
||||
|
||||
# assert len(ciphertext) == len(plaintext)
|
||||
assert auth_tag < (1 << 128)
|
||||
return ciphertext, auth_tag
|
||||
|
||||
def decrypt(self, init_value, ciphertext, auth_tag, auth_data=b''):
|
||||
# if init_value >= (1 << 96):
|
||||
# raise InvalidInputException('IV should be 96-bit')
|
||||
# if auth_tag >= (1 << 128):
|
||||
# raise InvalidInputException('Tag should be 128-bit')
|
||||
|
||||
if auth_tag != self.__ghash(auth_data, ciphertext) ^ \
|
||||
bytes_to_long(self.__aes_ecb.encrypt(
|
||||
long_to_bytes((init_value << 32) | 1, 16))):
|
||||
raise InvalidTagException
|
||||
|
||||
len_ciphertext = len(ciphertext)
|
||||
if len_ciphertext > 0:
|
||||
counter = Counter.new(
|
||||
nbits=32,
|
||||
prefix=long_to_bytes(init_value, 12),
|
||||
initial_value=2,
|
||||
allow_wraparound=True)
|
||||
aes_ctr = AES.new(self.__master_key, AES.MODE_CTR, counter=counter)
|
||||
|
||||
if 0 != len_ciphertext % 16:
|
||||
padded_ciphertext = ciphertext + \
|
||||
b'\x00' * (16 - len_ciphertext % 16)
|
||||
else:
|
||||
padded_ciphertext = ciphertext
|
||||
plaintext = aes_ctr.decrypt(padded_ciphertext)[:len_ciphertext]
|
||||
|
||||
else:
|
||||
plaintext = b''
|
||||
|
||||
return plaintext
|
||||
|
||||
def aes_gcm(self, ciphertext, nounce, aes_key, hdr, tag_auth, decrypt=True):
|
||||
cipher = AES.new(aes_key, AES.MODE_GCM, nounce)
|
||||
if hdr is not None:
|
||||
cipher.update(hdr)
|
||||
if decrypt:
|
||||
plaintext = cipher.decrypt(ciphertext)
|
||||
try:
|
||||
cipher.verify(tag_auth)
|
||||
return plaintext
|
||||
except ValueError:
|
||||
return None
|
||||
return None
|
||||
|
||||
def aes_cbc(self, key, iv, data, decrypt=True):
|
||||
if decrypt:
|
||||
return AES.new(key, AES.MODE_CBC, IV=iv).decrypt(data)
|
||||
else:
|
||||
return AES.new(key, AES.MODE_CBC, IV=iv).encrypt(data)
|
||||
|
||||
def aes_ecb(self, key, data, decrypt=True):
|
||||
if decrypt:
|
||||
return AES.new(key, AES.MODE_ECB).decrypt(data)
|
||||
else:
|
||||
return AES.new(key, AES.MODE_ECB).encrypt(data)
|
||||
|
||||
def aes_ctr(self, key, counter, enc_data, decrypt=True):
|
||||
ctr = Counter.new(128, initial_value=counter)
|
||||
# Create the AES cipher object and decrypt the ciphertext, basically this here is just aes ctr 256 :)
|
||||
cipher = AES.new(key, AES.MODE_CTR, counter=ctr)
|
||||
if decrypt:
|
||||
data = cipher.decrypt(enc_data)
|
||||
else:
|
||||
data = cipher.encrypt(enc_data)
|
||||
return data
|
||||
|
||||
def aes_ccm(self, key, nounce, tag_auth, data, decrypt=True):
|
||||
cipher = AES.new(key, AES.MODE_CCM, nounce)
|
||||
if decrypt:
|
||||
plaintext = cipher.decrypt(data)
|
||||
try:
|
||||
cipher.verify(tag_auth)
|
||||
return plaintext
|
||||
except ValueError:
|
||||
return None
|
||||
else:
|
||||
ciphertext = cipher.encrypt(data)
|
||||
return ciphertext
|
||||
|
||||
def aes_cmac_verify(self, key, plain, compare):
|
||||
ctx = CMAC.new(key, ciphermod=AES)
|
||||
ctx.update(plain)
|
||||
result = ctx.hexdigest()
|
||||
if result != compare:
|
||||
print("AES-CMAC failed !")
|
||||
else:
|
||||
print("AES-CMAC ok !")
|
||||
|
||||
class rsa: # RFC8017
|
||||
def __init__(self, hashtype="SHA256"):
|
||||
if hashtype == "SHA1":
|
||||
self.hash = hashlib.sha1
|
||||
self.digestLen = 0x14
|
||||
elif hashtype == "SHA256":
|
||||
self.hash = hashlib.sha256
|
||||
self.digestLen = 0x20
|
||||
|
||||
def pss_test(self):
|
||||
N = "a2ba40ee07e3b2bd2f02ce227f36a195024486e49c19cb41bbbdfbba98b22b0e577c2eeaffa20d883a76e65e394c69d4b3c05a1e8fadda27edb2a42bc000fe888b9b32c22d15add0cd76b3e7936e19955b220dd17d4ea904b1ec102b2e4de7751222aa99151024c7cb41cc5ea21d00eeb41f7c800834d2c6e06bce3bce7ea9a5"
|
||||
e = "010001"
|
||||
D = "050e2c3e38d886110288dfc68a9533e7e12e27d2aa56d2cdb3fb6efa990bcff29e1d2987fb711962860e7391b1ce01ebadb9e812d2fbdfaf25df4ae26110a6d7a26f0b810f54875e17dd5c9fb6d641761245b81e79f8c88f0e55a6dcd5f133abd35f8f4ec80adf1bf86277a582894cb6ebcd2162f1c7534f1f4947b129151b71"
|
||||
MSG = "859eef2fd78aca00308bdc471193bf55bf9d78db8f8a672b484634f3c9c26e6478ae10260fe0dd8c082e53a5293af2173cd50c6d5d354febf78b26021c25c02712e78cd4694c9f469777e451e7f8e9e04cd3739c6bbfedae487fb55644e9ca74ff77a53cb729802f6ed4a5ffa8ba159890fc "
|
||||
salt = "e3b5d5d002c1bce50c2b65ef88a188d83bce7e61"
|
||||
|
||||
N = int(N, 16)
|
||||
e = int(e, 16)
|
||||
D = int(D, 16)
|
||||
MSG = unhexlify(MSG)
|
||||
salt = unhexlify(salt)
|
||||
signature = self.pss_sign(D, N, self.hash(MSG), salt, 1024) # pkcs_1_pss_encode_sha256
|
||||
isvalid = self.pss_verify(e, N, self.hash(MSG), signature, 1024)
|
||||
if isvalid:
|
||||
print("Test passed.")
|
||||
else:
|
||||
print("Test failed.")
|
||||
|
||||
def i2osp(self, x, x_len):
|
||||
"""Converts the integer x to its big-endian representation of length
|
||||
x_len.
|
||||
"""
|
||||
if x > 256 ** x_len:
|
||||
raise IntegerTooLarge
|
||||
h = hex(x)[2:]
|
||||
if h[-1] == 'L':
|
||||
h = h[:-1]
|
||||
if len(h) & 1 == 1:
|
||||
h = '0%s' % h
|
||||
x = unhexlify(h)
|
||||
return b'\x00' * int(x_len - len(x)) + x
|
||||
|
||||
def os2ip(self, x):
|
||||
"""Converts the byte string x representing an integer reprented using the
|
||||
big-endian convient to an integer.
|
||||
"""
|
||||
h = hexlify(x)
|
||||
return int(h, 16)
|
||||
|
||||
# def os2ip(self, X):
|
||||
# return int.from_bytes(X, byteorder='big')
|
||||
|
||||
def mgf1(self, input, length):
|
||||
counter = 0
|
||||
output = b''
|
||||
while len(output) < length:
|
||||
C = self.i2osp(counter, 4)
|
||||
output += self.hash(input + C)
|
||||
counter += 1
|
||||
return output[:length]
|
||||
|
||||
def assert_int(self, var: int, name: str):
|
||||
if isinstance(var, int):
|
||||
return
|
||||
raise TypeError('%s should be an integer, not %s' % (name, var.__class__))
|
||||
|
||||
def sign(self, tosign, D, N, emBits=1024):
|
||||
self.assert_int(tosign, 'message')
|
||||
self.assert_int(D, 'D')
|
||||
self.assert_int(N, 'n')
|
||||
|
||||
if tosign < 0:
|
||||
raise ValueError('Only non-negative numbers are supported')
|
||||
|
||||
if tosign > N:
|
||||
tosign1 = divmod(tosign, N)[1]
|
||||
signature = pow(tosign1, D, N)
|
||||
raise OverflowError("The message %i is too long for n=%i" % (tosign, N))
|
||||
|
||||
signature = pow(tosign, D, N)
|
||||
hexsign = self.i2osp(signature, emBits // 8)
|
||||
return hexsign
|
||||
|
||||
def pss_sign(self, D, N, msghash, salt, emBits=1024):
|
||||
if isinstance(D, str):
|
||||
D = unhexlify(D)
|
||||
D = self.os2ip(D)
|
||||
if isinstance(N, str):
|
||||
N = unhexlify(N)
|
||||
N = self.os2ip(N)
|
||||
slen = len(salt)
|
||||
emLen = self.ceil_div(emBits, 8)
|
||||
inBlock = b"\x00" * 8 + msghash + salt
|
||||
hhash = self.hash(inBlock)
|
||||
PSlen = emLen - self.digestLen - slen - 1 - 1
|
||||
DB = (PSlen * b"\x00") + b"\x01" + salt
|
||||
rlen = emLen - len(hhash) - 1
|
||||
dbMask = self.mgf1(hhash, rlen)
|
||||
maskedDB = bytearray()
|
||||
for i in range(0, len(dbMask)):
|
||||
maskedDB.append(dbMask[i] ^ DB[i])
|
||||
maskedDB[0] = maskedDB[0] & 0x7F
|
||||
EM = maskedDB + hhash + b"\xbc"
|
||||
tosign = self.os2ip(EM)
|
||||
# EM=hexlify(EM).decode('utf-8')
|
||||
# tosign = int(EM,16)
|
||||
return self.sign(tosign, D, N, emBits)
|
||||
# 6B1EAA2042A5C8DA8B1B4A8320111A70A0CBA65233D1C6E418EF8156E82A8F96BD843F047FF25AB9702A6582C8387298753E628F23448B4580E09CBD2A483C623B888F47C4BD2C5EFF09013C6DFF67DB59BAB3037F0BEE05D5660264D28CC6251631FE75CE106D931A04FA032FEA31259715CE0FAB1AE0E2F8130807AF4019A61B9C060ECE59104F22156FEE8108F17DC80D7C2F8397AFB9780994F7C5A0652F93D1B48010B0B248AB9711235787D797FBA4D10A29BCF09628585D405640A866B15EE9D7526A2703E72A19811EF447F6E5C43F915B3808EBC79EA4BCF78903DBDE32E47E239CFB5F2B5986D0CBBFBE6BACDC29B2ADE006D23D0B90775B1AE4DD
|
||||
|
||||
def ceil_div(self, a, b):
|
||||
(q, r) = divmod(a, b)
|
||||
if r:
|
||||
return q + 1
|
||||
else:
|
||||
return q
|
||||
|
||||
def pss_verify(self, e, N, msghash, signature, emBits=1024, salt=None):
|
||||
if salt == None:
|
||||
slen = self.digestLen
|
||||
else:
|
||||
slen = len(salt)
|
||||
sig = self.os2ip(signature)
|
||||
|
||||
EM = pow(sig, e, N)
|
||||
# EM = unhexlify(hex(EM)[2:])
|
||||
EM = self.i2osp(EM, emBits // 8)
|
||||
|
||||
emLen = len(signature)
|
||||
|
||||
valBC = EM[-1]
|
||||
if valBC != 0xbc:
|
||||
print("[rsa_pss] : 0xbc check failed")
|
||||
return False
|
||||
mhash = EM[emLen - self.digestLen - 1:-1]
|
||||
maskedDB = EM[:emLen - self.digestLen - 1]
|
||||
|
||||
lmask = ~(0xFF >> (8 * emLen + 1 - emBits))
|
||||
if EM[0] & lmask:
|
||||
print("[rsa_pss] : lmask check failed")
|
||||
return False
|
||||
|
||||
dbMask = self.mgf1(mhash, emLen - self.digestLen - 1)
|
||||
|
||||
DB = bytearray()
|
||||
for i in range(0, len(dbMask)):
|
||||
DB.append(dbMask[i] ^ maskedDB[i])
|
||||
|
||||
TS = bytearray()
|
||||
TS.append(DB[0] & ~lmask)
|
||||
TS.extend(DB[1:])
|
||||
|
||||
PS = (b"\x00" * (emLen - self.digestLen - slen - 2)) + b"\x01"
|
||||
if TS[:len(PS)] != PS:
|
||||
print(TS[:len(PS)])
|
||||
print(PS)
|
||||
print("[rsa_pss] : 0x01 check failed")
|
||||
return False
|
||||
|
||||
if salt is not None:
|
||||
inBlock = b"\x00" * 8 + msghash + salt
|
||||
mhash = self.hash(inBlock)
|
||||
if mhash == mhash:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
salt = TS[-self.digestLen:]
|
||||
inBlock = b"\x00" * 8 + msghash + salt
|
||||
mhash = self.hash(inBlock)
|
||||
if mhash == mhash:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
class hash():
|
||||
def __init__(self, hashtype="SHA256"):
|
||||
if hashtype == "SHA1":
|
||||
self.hash = self.sha1
|
||||
self.digestLen = 0x14
|
||||
elif hashtype == "SHA256":
|
||||
self.hash = self.sha256
|
||||
self.digestLen = 0x20
|
||||
elif hashtype == "MD5":
|
||||
self.hash = self.md5
|
||||
self.digestLen = 0x10
|
||||
|
||||
def sha1(self, msg):
|
||||
return hashlib.sha1(msg).digest()
|
||||
|
||||
def sha256(self, msg):
|
||||
return hashlib.sha256(msg).digest()
|
||||
|
||||
def md5(self, msg):
|
||||
return hashlib.md5(msg).digest()
|
1238
edl/Library/firehose.py
Executable file
1238
edl/Library/firehose.py
Executable file
File diff suppressed because it is too large
Load diff
920
edl/Library/firehose_client.py
Normal file
920
edl/Library/firehose_client.py
Normal file
|
@ -0,0 +1,920 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) B.Kerler 2018-2019
|
||||
|
||||
import os
|
||||
import sys
|
||||
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
|
||||
|
||||
try:
|
||||
import xml.etree.cElementTree as ET
|
||||
from xml.etree import cElementTree as ElementTree
|
||||
except ImportError:
|
||||
import xml.etree.ElementTree as ET
|
||||
from xml.etree import ElementTree
|
||||
|
||||
try:
|
||||
from edl.Library.Modules.init import modules
|
||||
except ImportError as e:
|
||||
pass
|
||||
|
||||
|
||||
class firehose_client(metaclass=LogBase):
|
||||
def __init__(self, arguments, cdc, sahara, loglevel, printer):
|
||||
self.cdc = cdc
|
||||
self.sahara = sahara
|
||||
self.arguments = arguments
|
||||
self.printer = printer
|
||||
self.info = self.__logger.info
|
||||
self.error = self.__logger.error
|
||||
self.warning = self.__logger.warning
|
||||
self.__logger.setLevel(loglevel)
|
||||
if loglevel == logging.DEBUG:
|
||||
logfilename = "log.txt"
|
||||
fh = logging.FileHandler(logfilename)
|
||||
self.__logger.addHandler(fh)
|
||||
|
||||
self.cfg = firehose.cfg()
|
||||
if not arguments["--memory"] is None:
|
||||
self.cfg.MemoryName = arguments["--memory"].lower()
|
||||
else:
|
||||
self.cfg.MemoryName = ""
|
||||
self.cfg.ZLPAwareHost = 1
|
||||
self.cfg.SkipStorageInit = arguments["--skipstorageinit"]
|
||||
self.cfg.SkipWrite = arguments["--skipwrite"]
|
||||
self.cfg.MaxPayloadSizeToTargetInBytes = getint(arguments["--maxpayload"])
|
||||
self.cfg.SECTOR_SIZE_IN_BYTES = getint(arguments["--sectorsize"])
|
||||
self.cfg.bit64 = sahara.bit64
|
||||
devicemodel = ""
|
||||
skipresponse = False
|
||||
if "--skipresponse" in arguments:
|
||||
if arguments["--skipresponse"]:
|
||||
skipresponse = True
|
||||
if "--devicemodel" in arguments:
|
||||
if arguments["--devicemodel"] is not None:
|
||||
devicemodel = arguments["--devicemodel"]
|
||||
self.firehose = firehose(cdc, xmlparser(), self.cfg, self.__logger.level, devicemodel, sahara.serial,
|
||||
skipresponse,
|
||||
self.getluns(arguments), arguments)
|
||||
self.connected = False
|
||||
self.firehose.connect()
|
||||
if "hwid" in dir(sahara):
|
||||
if sahara.hwid is not None:
|
||||
hwid = (sahara.hwid >> 32) & 0xFFFFFF
|
||||
socid = ((sahara.hwid >> 32) >> 16)
|
||||
if hwid in msmids:
|
||||
self.target_name = msmids[hwid]
|
||||
self.info(f"Target detected: {self.target_name}")
|
||||
if self.cfg.MemoryName == "":
|
||||
if self.target_name in memory_type.preferred_memory:
|
||||
type = memory_type.preferred_memory[self.target_name]
|
||||
if type == memory_type.nand:
|
||||
self.cfg.MemoryName = "nand"
|
||||
if type == memory_type.spinor:
|
||||
self.cfg.MemoryName = "spinor"
|
||||
elif type == memory_type.emmc:
|
||||
self.cfg.MemoryName = "eMMC"
|
||||
elif type == memory_type.ufs:
|
||||
self.cfg.MemoryName = "UFS"
|
||||
self.warning("Based on the chipset, we assume " +
|
||||
self.cfg.MemoryName + " as default memory type..., if it fails, try using " +
|
||||
"--memory\" with \"UFS\",\"NAND\" or \"spinor\" instead !")
|
||||
elif socid in sochw:
|
||||
self.target_name = sochw[socid].split(",")[0]
|
||||
|
||||
# We assume ufs is fine (hopefully), set it as default
|
||||
if self.cfg.MemoryName == "":
|
||||
self.warning(
|
||||
"No --memory option set, we assume \"eMMC\" as default ..., if it fails, try using \"--memory\" " +
|
||||
"with \"UFS\",\"NAND\" or \"spinor\" instead !")
|
||||
self.cfg.MemoryName = "eMMC"
|
||||
|
||||
if self.firehose.configure(0):
|
||||
funcs = "Supported functions:\n-----------------\n"
|
||||
for function in self.firehose.supported_functions:
|
||||
funcs += function + ","
|
||||
funcs = funcs[:-1]
|
||||
self.info(funcs)
|
||||
self.target_name = self.firehose.cfg.TargetName
|
||||
self.connected = True
|
||||
try:
|
||||
if self.firehose.modules is None:
|
||||
self.firehose.modules = modules(fh=self.firehose, serial=self.firehose.serial,
|
||||
supported_functions=self.firehose.supported_functions,
|
||||
loglevel=self.__logger.level,
|
||||
devicemodel=self.firehose.devicemodel, args=self.arguments)
|
||||
except Exception as err: # pylint: disable=broad-except
|
||||
self.firehose.modules = None
|
||||
|
||||
def check_cmd(self, func):
|
||||
if not self.firehose.supported_functions:
|
||||
return True
|
||||
for sfunc in self.firehose.supported_functions:
|
||||
if func.lower() == sfunc.lower():
|
||||
return True
|
||||
return False
|
||||
|
||||
def find_bootable_partition(self, rawprogram):
|
||||
part = -1
|
||||
for xml in rawprogram:
|
||||
with open(xml, "r") as fl:
|
||||
for evt, elem in ET.iterparse(fl, events=["end"]):
|
||||
if elem.tag == "program":
|
||||
label = elem.get("label")
|
||||
if label in ['xbl', 'xbl_a', 'sbl1']:
|
||||
if part != -1:
|
||||
self.error("[FIREHOSE] multiple bootloader found!")
|
||||
return -1
|
||||
part = elem.get("physical_partition_number")
|
||||
return part
|
||||
|
||||
def getluns(self, argument):
|
||||
if argument["--lun"] is not None:
|
||||
return [int(argument["--lun"])]
|
||||
|
||||
luns = []
|
||||
if self.cfg.MemoryName.lower() == "ufs":
|
||||
for i in range(0, self.cfg.maxlun):
|
||||
luns.append(i)
|
||||
else:
|
||||
luns = [0]
|
||||
return luns
|
||||
|
||||
def check_param(self, parameters):
|
||||
error = False
|
||||
params = ""
|
||||
for parameter in parameters:
|
||||
params += parameter + " "
|
||||
if parameter not in parameters:
|
||||
error = True
|
||||
if error:
|
||||
if len(parameters) == 1:
|
||||
self.printer("Argument " + params + "required.")
|
||||
else:
|
||||
self.printer("Arguments " + params + "required.")
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_storage_info(self):
|
||||
if "getstorageinfo" in self.firehose.supported_functions:
|
||||
storageinfo = self.firehose.cmd_getstorageinfo()
|
||||
for info in storageinfo:
|
||||
if "storage_info" in info:
|
||||
rs = info.replace("INFO: ", "")
|
||||
field = json.loads(rs)
|
||||
if "storage_info" in field:
|
||||
info = field["storage_info"]
|
||||
return info
|
||||
return None
|
||||
|
||||
def handle_firehose(self, cmd, options):
|
||||
if cmd == "gpt":
|
||||
luns = self.getluns(options)
|
||||
directory = options["<directory>"]
|
||||
if directory is None:
|
||||
directory = ""
|
||||
genxml = False
|
||||
if "--genxml" in options:
|
||||
if options["--genxml"]:
|
||||
genxml = True
|
||||
for lun in luns:
|
||||
sfilename = os.path.join(directory, f"gpt_main{str(lun)}.bin")
|
||||
data, guid_gpt = self.firehose.get_gpt(lun, int(options["--gpt-num-part-entries"]),
|
||||
int(options["--gpt-part-entry-size"]),
|
||||
int(options["--gpt-part-entry-start-lba"]))
|
||||
if guid_gpt is None:
|
||||
break
|
||||
with open(sfilename, "wb") as write_handle:
|
||||
write_handle.write(data)
|
||||
|
||||
self.printer(f"Dumped GPT from Lun {str(lun)} to {sfilename}")
|
||||
sfilename = os.path.join(directory, f"gpt_backup{str(lun)}.bin")
|
||||
with open(sfilename, "wb") as write_handle:
|
||||
write_handle.write(data[self.firehose.cfg.SECTOR_SIZE_IN_BYTES * 2:])
|
||||
self.printer(f"Dumped Backup GPT from Lun {str(lun)} to {sfilename}")
|
||||
if genxml:
|
||||
guid_gpt.generate_rawprogram(lun, self.firehose.cfg.SECTOR_SIZE_IN_BYTES, directory)
|
||||
return True
|
||||
elif cmd == "printgpt":
|
||||
luns = self.getluns(options)
|
||||
for lun in luns:
|
||||
data, guid_gpt = self.firehose.get_gpt(lun, int(options["--gpt-num-part-entries"]),
|
||||
int(options["--gpt-part-entry-size"]),
|
||||
int(options["--gpt-part-entry-start-lba"]))
|
||||
if guid_gpt is None:
|
||||
break
|
||||
self.printer(f"\nParsing Lun {str(lun)}:")
|
||||
guid_gpt.print()
|
||||
return True
|
||||
elif cmd == "r":
|
||||
if not self.check_param(["<partitionname>", "<filename>"]):
|
||||
return False
|
||||
partitionname = options["<partitionname>"]
|
||||
filename = options["<filename>"]
|
||||
filenames = filename.split(",")
|
||||
partitions = partitionname.split(",")
|
||||
if len(partitions) != len(filenames):
|
||||
self.error("You need to gives as many filenames as given partitions.")
|
||||
return False
|
||||
i = 0
|
||||
for partition in partitions:
|
||||
if partition=="gpt":
|
||||
luns = self.getluns(options)
|
||||
for lun in luns:
|
||||
partfilename=filenames[i]+".lun%d" % lun
|
||||
if self.firehose.cmd_read(lun, 0, 32, partfilename):
|
||||
self.printer(
|
||||
f"Dumped sector {str(0)} with sector count {str(32)} " +
|
||||
f"as {partfilename}.")
|
||||
continue
|
||||
partfilename = filenames[i]
|
||||
i += 1
|
||||
res = self.firehose.detect_partition(options, partition)
|
||||
if res[0]:
|
||||
lun = res[1]
|
||||
rpartition = res[2]
|
||||
if self.firehose.cmd_read(lun, rpartition.sector, rpartition.sectors, partfilename):
|
||||
self.printer(
|
||||
f"Dumped sector {str(rpartition.sector)} with sector count {str(rpartition.sectors)} " +
|
||||
f"as {partfilename}.")
|
||||
else:
|
||||
fpartitions = res[1]
|
||||
self.error(f"Error: Couldn't detect partition: {partition}\nAvailable partitions:")
|
||||
for lun in fpartitions:
|
||||
for rpartition in fpartitions[lun]:
|
||||
if self.cfg.MemoryName == "emmc":
|
||||
self.error("\t" + rpartition)
|
||||
else:
|
||||
self.error(lun + ":\t" + rpartition)
|
||||
return False
|
||||
return True
|
||||
elif cmd == "rl":
|
||||
if not self.check_param(["<directory>"]):
|
||||
return False
|
||||
directory = options["<directory>"]
|
||||
if options["--skip"]:
|
||||
skip = options["--skip"].split(",")
|
||||
else:
|
||||
skip = []
|
||||
genxml = False
|
||||
if "--genxml" in options:
|
||||
if options["--genxml"]:
|
||||
genxml = True
|
||||
if not os.path.exists(directory):
|
||||
os.mkdir(directory)
|
||||
|
||||
luns = self.getluns(options)
|
||||
|
||||
for lun in luns:
|
||||
data, guid_gpt = self.firehose.get_gpt(lun, int(options["--gpt-num-part-entries"]),
|
||||
int(options["--gpt-part-entry-size"]),
|
||||
int(options["--gpt-part-entry-start-lba"]))
|
||||
if guid_gpt is None:
|
||||
break
|
||||
if len(luns) > 1:
|
||||
storedir = os.path.join(directory, "lun" + str(lun))
|
||||
else:
|
||||
storedir = directory
|
||||
if not os.path.exists(storedir):
|
||||
os.mkdir(storedir)
|
||||
sfilename = os.path.join(storedir, f"gpt_main{str(lun)}.bin")
|
||||
with open(sfilename, "wb") as write_handle:
|
||||
write_handle.write(data)
|
||||
|
||||
sfilename = os.path.join(storedir, f"gpt_backup{str(lun)}.bin")
|
||||
with open(sfilename, "wb") as write_handle:
|
||||
write_handle.write(data[self.firehose.cfg.SECTOR_SIZE_IN_BYTES * 2:])
|
||||
|
||||
if genxml:
|
||||
guid_gpt.generate_rawprogram(lun, self.firehose.cfg.SECTOR_SIZE_IN_BYTES, storedir)
|
||||
|
||||
for partition in guid_gpt.partentries:
|
||||
partitionname = partition.name
|
||||
if partition.name in skip:
|
||||
continue
|
||||
filename = os.path.join(storedir, partitionname + ".bin")
|
||||
self.info(
|
||||
f"Dumping partition {str(partition.name)} with sector count {str(partition.sectors)} " +
|
||||
f"as {filename}.")
|
||||
if self.firehose.cmd_read(lun, partition.sector, partition.sectors, filename):
|
||||
self.info(f"Dumped partition {str(partition.name)} with sector count " +
|
||||
f"{str(partition.sectors)} as {filename}.")
|
||||
return True
|
||||
elif cmd == "rf":
|
||||
if not self.check_param(["<filename>"]):
|
||||
return False
|
||||
filename = options["<filename>"]
|
||||
storageinfo = self.get_storage_info()
|
||||
if storageinfo is not None and self.cfg.MemoryName.lower() in ["spinor" , "nand"]:
|
||||
totalsectors = None
|
||||
if "total_blocks" in storageinfo:
|
||||
totalsectors = storageinfo["total_blocks"]
|
||||
if "num_physical" in storageinfo:
|
||||
num_physical = storageinfo["num_physical"]
|
||||
luns = [0]
|
||||
if num_physical > 0:
|
||||
luns=[]
|
||||
for i in range(num_physical):
|
||||
luns.append(i)
|
||||
if totalsectors is not None:
|
||||
for lun in luns:
|
||||
buffer=self.firehose.cmd_read_buffer(physical_partition_number=lun,
|
||||
start_sector=0, num_partition_sectors=1, display=False)
|
||||
storageinfo = self.get_storage_info()
|
||||
if "total_blocks" in storageinfo:
|
||||
totalsectors = storageinfo["total_blocks"]
|
||||
if len(luns) > 1:
|
||||
sfilename = filename + f".lun{str(lun)}"
|
||||
else:
|
||||
sfilename = filename
|
||||
self.printer(f"Dumping sector 0 with sector count {str(totalsectors)} as {filename}.")
|
||||
if self.firehose.cmd_read(lun, 0, totalsectors, sfilename):
|
||||
self.printer(
|
||||
f"Dumped sector 0 with sector count {str(totalsectors)} as {filename}.")
|
||||
else:
|
||||
luns = self.getluns(options)
|
||||
for lun in luns:
|
||||
data, guid_gpt = self.firehose.get_gpt(lun, int(options["--gpt-num-part-entries"]),
|
||||
int(options["--gpt-part-entry-size"]),
|
||||
int(options["--gpt-part-entry-start-lba"]))
|
||||
if guid_gpt is None:
|
||||
break
|
||||
if len(luns) > 1:
|
||||
sfilename = filename + f".lun{str(lun)}"
|
||||
else:
|
||||
sfilename = filename
|
||||
self.printer(f"Dumping sector 0 with sector count {str(guid_gpt.totalsectors)} as {filename}.")
|
||||
if self.firehose.cmd_read(lun, 0, guid_gpt.totalsectors, sfilename):
|
||||
self.printer(f"Dumped sector 0 with sector count {str(guid_gpt.totalsectors)} as {filename}.")
|
||||
return True
|
||||
elif cmd == "pbl":
|
||||
if not self.check_param(["<filename>"]):
|
||||
return False
|
||||
if not self.check_cmd("peek"):
|
||||
self.error("Peek command isn't supported by edl loader")
|
||||
return False
|
||||
else:
|
||||
filename = options["<filename>"]
|
||||
if self.target_name in infotbl:
|
||||
target_name = infotbl[self.target_name]
|
||||
if len(target_name[0]) > 0:
|
||||
if self.firehose.cmd_peek(target_name[0][0], target_name[0][1], filename, True):
|
||||
self.printer(f"Dumped pbl at offset {hex(target_name[0][0])} as {filename}.")
|
||||
return True
|
||||
else:
|
||||
self.error("No known pbl offset for this chipset")
|
||||
else:
|
||||
self.error("Unknown target chipset")
|
||||
self.error("Error on dumping pbl")
|
||||
return False
|
||||
elif cmd == "qfp":
|
||||
if not self.check_param(["<filename>"]):
|
||||
return False
|
||||
if not self.check_cmd("peek"):
|
||||
self.error("Peek command isn't supported by edl loader")
|
||||
return False
|
||||
else:
|
||||
filename = options["<filename>"]
|
||||
if self.target_name not in infotbl:
|
||||
self.error("Unknown target chipset")
|
||||
else:
|
||||
target_name = infotbl[self.target_name]
|
||||
if len(target_name[1]) > 0:
|
||||
if self.firehose.cmd_peek(target_name[1][0], target_name[1][1], filename):
|
||||
self.printer(f"Dumped qfprom at offset {hex(target_name[1][0])} as {filename}.")
|
||||
return True
|
||||
else:
|
||||
self.error("No known qfprom offset for this chipset")
|
||||
self.error("Error on dumping qfprom")
|
||||
return False
|
||||
elif cmd == "secureboot":
|
||||
if not self.check_cmd("peek"):
|
||||
self.error("Peek command isn't supported by edl loader")
|
||||
return False
|
||||
else:
|
||||
if self.target_name in secureboottbl:
|
||||
self.target_name = secureboottbl[self.target_name]
|
||||
value = unpack("<I", self.firehose.cmd_peek(self.target_name, 4))[0]
|
||||
is_secure = False
|
||||
for area in range(0, 4):
|
||||
sec_boot = (value >> (area * 8)) & 0xFF
|
||||
pk_hashindex = sec_boot & 3
|
||||
oem_pkhash = True if ((sec_boot >> 4) & 1) == 1 else False
|
||||
auth_enabled = True if ((sec_boot >> 5) & 1) == 1 else False
|
||||
use_serial = True if ((sec_boot >> 6) & 1) == 1 else False
|
||||
if auth_enabled:
|
||||
is_secure = True
|
||||
self.printer(f"Sec_Boot{str(area)} " +
|
||||
f"PKHash-Index:{str(pk_hashindex)} " +
|
||||
f"OEM_PKHash: {str(oem_pkhash)} " +
|
||||
f"Auth_Enabled: {str(auth_enabled)}" +
|
||||
f"Use_Serial: {str(use_serial)}")
|
||||
if is_secure:
|
||||
self.printer("Secure boot enabled.")
|
||||
else:
|
||||
self.printer("Secure boot disabled.")
|
||||
return True
|
||||
else:
|
||||
self.error("Unknown target chipset")
|
||||
return False
|
||||
elif cmd == "memtbl":
|
||||
if not self.check_param(["<filename>"]):
|
||||
return False
|
||||
if not self.check_cmd("peek"):
|
||||
self.error("Peek command isn't supported by edl loader")
|
||||
return False
|
||||
else:
|
||||
filename = options["<filename>"]
|
||||
if self.target_name in infotbl:
|
||||
self.target_name = infotbl[self.target_name]
|
||||
if len(self.target_name[2]) > 0:
|
||||
if self.firehose.cmd_peek(self.target_name[2][0], self.target_name[2][1], filename):
|
||||
self.printer(f"Dumped memtbl at offset {hex(self.target_name[2][0])} as {filename}.")
|
||||
return True
|
||||
else:
|
||||
self.error("No known memtbl offset for this chipset")
|
||||
else:
|
||||
self.error("Unknown target chipset")
|
||||
self.error("Error on dumping memtbl")
|
||||
return False
|
||||
elif cmd == "footer":
|
||||
if not self.check_param(["<filename>"]):
|
||||
return False
|
||||
luns = self.getluns(options)
|
||||
filename = options["<filename>"]
|
||||
for lun in luns:
|
||||
data, guid_gpt = self.firehose.get_gpt(lun, int(options["--gpt-num-part-entries"]),
|
||||
int(options["--gpt-part-entry-size"]),
|
||||
int(options["--gpt-part-entry-start-lba"]))
|
||||
if guid_gpt is None:
|
||||
break
|
||||
pnames = ["userdata2", "metadata", "userdata", "reserved1", "reserved2", "reserved3"]
|
||||
for partition in guid_gpt.partentries:
|
||||
if partition.name in pnames:
|
||||
self.printer(f"Detected partition: {partition.name}")
|
||||
data = self.firehose.cmd_read_buffer(lun,
|
||||
partition.sector +
|
||||
(partition.sectors -
|
||||
(0x4000 // self.firehose.cfg.SECTOR_SIZE_IN_BYTES)),
|
||||
(0x4000 // self.firehose.cfg.SECTOR_SIZE_IN_BYTES), False)
|
||||
if data == b"":
|
||||
continue
|
||||
val = unpack("<I", data[:4])[0]
|
||||
if (val & 0xFFFFFFF0) == 0xD0B5B1C0:
|
||||
with open(filename, "wb") as write_handle:
|
||||
write_handle.write(data)
|
||||
self.printer(f"Dumped footer from {partition.name} as {filename}.")
|
||||
return True
|
||||
self.error("Error: Couldn't detect footer partition.")
|
||||
return False
|
||||
elif cmd == "rs":
|
||||
if options["--lun"] is not None:
|
||||
lun = int(options["--lun"])
|
||||
else:
|
||||
lun = 0
|
||||
if not self.check_param(["<filename>", "<sectors>", "<start_sector>"]):
|
||||
return False
|
||||
start = int(options["<start_sector>"])
|
||||
sectors = int(options["<sectors>"])
|
||||
filename = options["<filename>"]
|
||||
if self.firehose.cmd_read(lun, start, sectors, filename, True):
|
||||
self.printer(f"Dumped sector {str(start)} with sector count {str(sectors)} as {filename}.")
|
||||
return True
|
||||
elif cmd == "peek":
|
||||
if not self.check_param(["<offset>", "<length>", "<filename>"]):
|
||||
return False
|
||||
if not self.check_cmd("peek"):
|
||||
self.error("Peek command isn't supported by edl loader")
|
||||
return False
|
||||
else:
|
||||
offset = getint(options["<offset>"])
|
||||
length = getint(options["<length>"])
|
||||
filename = options["<filename>"]
|
||||
self.firehose.cmd_peek(offset, length, filename, True)
|
||||
self.info(
|
||||
f"Peek data from offset {hex(offset)} and length {hex(length)} was written to {filename}")
|
||||
return True
|
||||
elif cmd == "peekhex":
|
||||
if not self.check_param(["<offset>", "<length>"]):
|
||||
return False
|
||||
if not self.check_cmd("peek"):
|
||||
self.error("Peek command isn't supported by edl loader")
|
||||
return False
|
||||
else:
|
||||
offset = getint(options["<offset>"])
|
||||
length = getint(options["<length>"])
|
||||
resp = self.firehose.cmd_peek(offset, length, "", True)
|
||||
self.printer("\n")
|
||||
self.printer(hexlify(resp))
|
||||
return True
|
||||
elif cmd == "peekqword":
|
||||
if not self.check_param(["<offset>"]):
|
||||
return False
|
||||
if not self.check_cmd("peek"):
|
||||
self.error("Peek command isn't supported by edl loader")
|
||||
return False
|
||||
else:
|
||||
offset = getint(options["<offset>"])
|
||||
resp = self.firehose.cmd_peek(offset, 8, "", True)
|
||||
self.printer("\n")
|
||||
self.printer(hex(unpack("<Q", resp[:8])[0]))
|
||||
return True
|
||||
elif cmd == "peekdword":
|
||||
if not self.check_param(["<offset>"]):
|
||||
return False
|
||||
if not self.check_cmd("peek"):
|
||||
self.error("Peek command isn't supported by edl loader")
|
||||
return False
|
||||
else:
|
||||
offset = getint(options["<offset>"])
|
||||
resp = self.firehose.cmd_peek(offset, 4, "", True)
|
||||
self.printer("\n")
|
||||
self.printer(hex(unpack("<I", resp[:4])[0]))
|
||||
return True
|
||||
elif cmd == "poke":
|
||||
if not self.check_param(["<offset>", "<filename>"]):
|
||||
return False
|
||||
if not self.check_cmd("poke"):
|
||||
self.error("Poke command isn't supported by edl loader")
|
||||
return False
|
||||
else:
|
||||
offset = getint(options["<offset>"])
|
||||
filename = options["<filename>"]
|
||||
return self.firehose.cmd_poke(offset, "", filename, True)
|
||||
elif cmd == "pokehex":
|
||||
if not self.check_param(["<offset>", "<data>"]):
|
||||
return False
|
||||
if not self.check_cmd("poke"):
|
||||
self.error("Poke command isn't supported by edl loader")
|
||||
return False
|
||||
else:
|
||||
offset = getint(options["<offset>"])
|
||||
data = unhexlify(options["<data>"])
|
||||
return self.firehose.cmd_poke(offset, data, "", True)
|
||||
elif cmd == "pokeqword":
|
||||
if not self.check_param(["<offset>", "<data>"]):
|
||||
return False
|
||||
if not self.check_cmd("poke"):
|
||||
self.error("Poke command isn't supported by edl loader")
|
||||
return False
|
||||
else:
|
||||
offset = getint(options["<offset>"])
|
||||
data = pack("<Q", getint(options["<data>"]))
|
||||
return self.firehose.cmd_poke(offset, data, "", True)
|
||||
elif cmd == "pokedword":
|
||||
if not self.check_param(["<offset>", "<data>"]):
|
||||
return False
|
||||
if not self.check_cmd("poke"):
|
||||
self.error("Poke command isn't supported by edl loader")
|
||||
return False
|
||||
else:
|
||||
offset = getint(options["<offset>"])
|
||||
data = pack("<I", getint(options["<data>"]))
|
||||
return self.firehose.cmd_poke(offset, data, "", True)
|
||||
elif cmd == "memcpy":
|
||||
if not self.check_param(["<offset>", "<size>"]):
|
||||
return False
|
||||
if not self.check_cmd("poke"):
|
||||
self.printer("Poke command isn't supported by edl loader")
|
||||
else:
|
||||
srcoffset = getint(options["<offset>"])
|
||||
size = getint(options["<size>"])
|
||||
dstoffset = srcoffset + size
|
||||
if self.firehose.cmd_memcpy(dstoffset, srcoffset, size):
|
||||
self.printer(f"Memcpy from {hex(srcoffset)} to {hex(dstoffset)} succeeded")
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
elif cmd == "reset":
|
||||
return self.firehose.cmd_reset()
|
||||
elif cmd == "nop":
|
||||
if not self.check_cmd("nop"):
|
||||
self.error("Nop command isn't supported by edl loader")
|
||||
return False
|
||||
else:
|
||||
return self.firehose.cmd_nop()
|
||||
elif cmd == "setbootablestoragedrive":
|
||||
if not self.check_param(["<lun>"]):
|
||||
return False
|
||||
if not self.check_cmd("setbootablestoragedrive"):
|
||||
self.error("setbootablestoragedrive command isn't supported by edl loader")
|
||||
return False
|
||||
else:
|
||||
return self.firehose.cmd_setbootablestoragedrive(int(options["<lun>"]))
|
||||
elif cmd == "getstorageinfo":
|
||||
if not self.check_cmd("getstorageinfo"):
|
||||
self.error("getstorageinfo command isn't supported by edl loader")
|
||||
return False
|
||||
else:
|
||||
return self.firehose.cmd_getstorageinfo_string()
|
||||
elif cmd == "w":
|
||||
if not self.check_param(["<partitionname>", "<filename>"]):
|
||||
return False
|
||||
partitionname = options["<partitionname>"]
|
||||
filename = options["<filename>"]
|
||||
if options["--lun"] is not None:
|
||||
lun = int(options["--lun"])
|
||||
else:
|
||||
lun = 0
|
||||
startsector = 0
|
||||
if not os.path.exists(filename):
|
||||
self.error(f"Error: Couldn't find file: {filename}")
|
||||
return False
|
||||
if partitionname.lower() == "gpt":
|
||||
sectors = os.stat(filename).st_size // self.firehose.cfg.SECTOR_SIZE_IN_BYTES
|
||||
res = [True, lun, sectors]
|
||||
else:
|
||||
res = self.firehose.detect_partition(options, partitionname)
|
||||
if res[0]:
|
||||
lun = res[1]
|
||||
sectors = os.stat(filename).st_size // self.firehose.cfg.SECTOR_SIZE_IN_BYTES
|
||||
if (os.stat(filename).st_size % self.firehose.cfg.SECTOR_SIZE_IN_BYTES) > 0:
|
||||
sectors += 1
|
||||
if partitionname.lower() != "gpt":
|
||||
partition = res[2]
|
||||
if sectors > partition.sectors:
|
||||
self.error(
|
||||
f"Error: {filename} has {sectors} sectors but partition only has {partition.sectors}.")
|
||||
return False
|
||||
startsector = partition.sector
|
||||
if self.firehose.modules is not None:
|
||||
self.firehose.modules.writeprepare()
|
||||
if self.firehose.cmd_program(lun, startsector, filename):
|
||||
self.printer(f"Wrote {filename} to sector {str(startsector)}.")
|
||||
return True
|
||||
else:
|
||||
self.printer(f"Error writing {filename} to sector {str(startsector)}.")
|
||||
return False
|
||||
else:
|
||||
if len(res) > 0:
|
||||
fpartitions = res[1]
|
||||
self.error(f"Error: Couldn't detect partition: {partitionname}\nAvailable partitions:")
|
||||
for lun in fpartitions:
|
||||
for partition in fpartitions[lun]:
|
||||
if self.cfg.MemoryName == "emmc":
|
||||
self.error("\t" + partition)
|
||||
else:
|
||||
self.error(lun + ":\t" + partition)
|
||||
return False
|
||||
elif cmd == "wl":
|
||||
if not self.check_param(["<directory>"]):
|
||||
return False
|
||||
directory = options["<directory>"]
|
||||
if options["--skip"]:
|
||||
skip = options["--skip"].split(",")
|
||||
else:
|
||||
skip = []
|
||||
luns = self.getluns(options)
|
||||
|
||||
if not os.path.exists(directory):
|
||||
self.error(f"Error: Couldn't find directory: {directory}")
|
||||
sys.exit()
|
||||
filenames = []
|
||||
if self.firehose.modules is not None:
|
||||
self.firehose.modules.writeprepare()
|
||||
for dirName, subdirList, fileList in os.walk(directory):
|
||||
for fname in fileList:
|
||||
filenames.append(os.path.join(dirName, fname))
|
||||
for lun in luns:
|
||||
data, guid_gpt = self.firehose.get_gpt(lun, int(options["--gpt-num-part-entries"]),
|
||||
int(options["--gpt-part-entry-size"]),
|
||||
int(options["--gpt-part-entry-start-lba"]))
|
||||
if guid_gpt is None:
|
||||
break
|
||||
if "partentries" in dir(guid_gpt):
|
||||
for filename in filenames:
|
||||
for partition in guid_gpt.partentries:
|
||||
partname = filename[filename.rfind("/") + 1:]
|
||||
if ".bin" in partname[-4:] or ".img" in partname[-4:] or ".mbn" in partname[-4:]:
|
||||
partname = partname[:-4]
|
||||
if partition.name == partname:
|
||||
if partition.name in skip:
|
||||
continue
|
||||
sectors = os.stat(filename).st_size // self.firehose.cfg.SECTOR_SIZE_IN_BYTES
|
||||
if (os.stat(filename).st_size % self.firehose.cfg.SECTOR_SIZE_IN_BYTES) > 0:
|
||||
sectors += 1
|
||||
if sectors > partition.sectors:
|
||||
self.error(f"Error: {filename} has {sectors} sectors but partition " +
|
||||
f"only has {partition.sectors}.")
|
||||
return False
|
||||
self.printer(f"Writing {filename} to partition {str(partition.name)}.")
|
||||
self.firehose.cmd_program(lun, partition.sector, filename)
|
||||
else:
|
||||
self.printer("Couldn't write partition. Either wrong memorytype given or no gpt partition.")
|
||||
return False
|
||||
return True
|
||||
elif cmd == "ws":
|
||||
if not self.check_param(["<start_sector>"]):
|
||||
return False
|
||||
if options["--lun"] is not None:
|
||||
lun = int(options["--lun"])
|
||||
else:
|
||||
lun = 0
|
||||
start = int(options["<start_sector>"])
|
||||
filename = options["<filename>"]
|
||||
if not os.path.exists(filename):
|
||||
self.error(f"Error: Couldn't find file: {filename}")
|
||||
return False
|
||||
if self.firehose.modules is not None:
|
||||
self.firehose.modules.writeprepare()
|
||||
if self.firehose.cmd_program(lun, start, filename):
|
||||
self.printer(f"Wrote {filename} to sector {str(start)}.")
|
||||
return True
|
||||
else:
|
||||
self.error(f"Error on writing {filename} to sector {str(start)}")
|
||||
return False
|
||||
elif cmd == "wf":
|
||||
if not self.check_param(["<filename>"]):
|
||||
return False
|
||||
if options["--lun"] is not None:
|
||||
lun = int(options["--lun"])
|
||||
else:
|
||||
lun = 0
|
||||
start = 0
|
||||
filename = options["<filename>"]
|
||||
if not os.path.exists(filename):
|
||||
self.error(f"Error: Couldn't find file: {filename}")
|
||||
return False
|
||||
if self.firehose.modules is not None:
|
||||
self.firehose.modules.writeprepare()
|
||||
if self.firehose.cmd_program(lun, start, filename):
|
||||
self.printer(f"Wrote {filename} to sector {str(start)}.")
|
||||
return True
|
||||
else:
|
||||
self.error(f"Error on writing {filename} to sector {str(start)}")
|
||||
return False
|
||||
elif cmd == "e":
|
||||
if not self.check_param(["<partitionname>"]):
|
||||
return False
|
||||
luns = self.getluns(options)
|
||||
partitionname = options["<partitionname>"]
|
||||
for lun in luns:
|
||||
data, guid_gpt = self.firehose.get_gpt(lun, int(options["--gpt-num-part-entries"]),
|
||||
int(options["--gpt-part-entry-size"]),
|
||||
int(options["--gpt-part-entry-start-lba"]))
|
||||
if guid_gpt is None:
|
||||
break
|
||||
if self.firehose.modules is not None:
|
||||
self.firehose.modules.writeprepare()
|
||||
if "partentries" in dir(guid_gpt):
|
||||
for partition in guid_gpt.partentries:
|
||||
if partition.name == partitionname:
|
||||
self.firehose.cmd_erase(lun, partition.sector, partition.sectors)
|
||||
self.printer(
|
||||
f"Erased {partitionname} starting at sector {str(partition.sector)} " +
|
||||
f"with sector count {str(partition.sectors)}.")
|
||||
return True
|
||||
else:
|
||||
self.printer("Couldn't erase partition. Either wrong memorytype given or no gpt partition.")
|
||||
return False
|
||||
self.error(f"Error: Couldn't detect partition: {partitionname}")
|
||||
return False
|
||||
elif cmd == "ep":
|
||||
if not self.check_param(["<partitionname>", "<sectors>"]):
|
||||
return False
|
||||
luns = self.getluns(options)
|
||||
partitionname = options["<partitionname>"]
|
||||
sectors = int(options["<sectors>"])
|
||||
|
||||
for lun in luns:
|
||||
data, guid_gpt = self.firehose.get_gpt(lun, int(options["--gpt-num-part-entries"]),
|
||||
int(options["--gpt-part-entry-size"]),
|
||||
int(options["--gpt-part-entry-start-lba"]))
|
||||
if guid_gpt is None:
|
||||
break
|
||||
if self.firehose.modules is not None:
|
||||
self.firehose.modules.writeprepare()
|
||||
if "partentries" in dir(guid_gpt):
|
||||
for partition in guid_gpt.partentries:
|
||||
if partition.name == partitionname:
|
||||
self.firehose.cmd_erase(lun, partition.sector, sectors)
|
||||
self.printer(
|
||||
f"Erased {partitionname} starting at sector {str(partition.sector)} " +
|
||||
f"with sector count {str(sectors)}.")
|
||||
return True
|
||||
else:
|
||||
self.printer("Couldn't erase partition. Either wrong memorytype given or no gpt partition.")
|
||||
return False
|
||||
self.error(f"Error: Couldn't detect partition: {partitionname}")
|
||||
return False
|
||||
elif cmd == "es":
|
||||
if not self.check_param(["<start_sector>", "<sectors>"]):
|
||||
return False
|
||||
if options["--lun"] is not None:
|
||||
lun = int(options["--lun"])
|
||||
else:
|
||||
lun = 0
|
||||
start = int(options["<start_sector>"])
|
||||
sectors = int(options["<sectors>"])
|
||||
if self.firehose.modules is not None:
|
||||
self.firehose.modules.writeprepare()
|
||||
if self.firehose.cmd_erase(lun, start, sectors):
|
||||
self.printer(f"Erased sector {str(start)} with sector count {str(sectors)}.")
|
||||
return True
|
||||
return False
|
||||
elif cmd == "xml":
|
||||
if not self.check_param(["<xmlfile>"]):
|
||||
return False
|
||||
return self.firehose.cmd_xml(options["<xmlfile>"])
|
||||
elif cmd == "rawxml":
|
||||
if not self.check_param(["<xmlstring>"]):
|
||||
return False
|
||||
return self.firehose.cmd_rawxml(options["<xmlstring>"])
|
||||
elif cmd == "send":
|
||||
if not self.check_param(["<command>"]):
|
||||
return False
|
||||
command = options["<command>"]
|
||||
resp = self.firehose.cmd_send(command, True)
|
||||
self.printer("\n")
|
||||
self.printer(resp)
|
||||
return True
|
||||
elif cmd == "server":
|
||||
return do_tcp_server(self, options, self.handle_firehose)
|
||||
elif cmd == "modules":
|
||||
if not self.check_param(["<command>", "<options>"]):
|
||||
return False
|
||||
mcommand = options["<command>"]
|
||||
moptions = options["<options>"]
|
||||
if self.firehose.modules is None:
|
||||
self.error("Feature is not supported")
|
||||
return False
|
||||
else:
|
||||
return self.firehose.modules.run(command=mcommand, args=moptions)
|
||||
elif cmd == "qfil":
|
||||
self.info("[qfil] raw programming...")
|
||||
rawprogram = options["<rawprogram>"].split(",")
|
||||
imagedir = options["<imagedir>"]
|
||||
patch = options["<patch>"].split(",")
|
||||
for xml in rawprogram:
|
||||
filename = os.path.join(imagedir, xml)
|
||||
if os.path.exists(filename):
|
||||
self.info("[qfil] programming %s" % xml)
|
||||
fl = open(filename, "r")
|
||||
for evt, elem in ET.iterparse(fl, events=["end"]):
|
||||
if elem.tag == "program":
|
||||
if elem.get("filename", ""):
|
||||
filename = os.path.join(imagedir, elem.get("filename"))
|
||||
if not os.path.isfile(filename):
|
||||
self.error("%s doesn't exist!" % filename)
|
||||
continue
|
||||
partition_number = int(elem.get("physical_partition_number"))
|
||||
num_disk_sectors = self.firehose.getlunsize(partition_number)
|
||||
start_sector = elem.get("start_sector")
|
||||
if "NUM_DISK_SECTORS" in start_sector:
|
||||
start_sector = start_sector.replace("NUM_DISK_SECTORS", str(num_disk_sectors))
|
||||
if "-" in start_sector or "*" in start_sector or "/" in start_sector or \
|
||||
"+" in start_sector:
|
||||
start_sector = start_sector.replace(".", "")
|
||||
start_sector = eval(start_sector)
|
||||
self.info(f"[qfil] programming {filename} to partition({partition_number})" +
|
||||
f"@sector({start_sector})...")
|
||||
|
||||
self.firehose.cmd_program(int(partition_number), int(start_sector), filename)
|
||||
else:
|
||||
self.warning(f"File : {filename} not found.")
|
||||
self.info("[qfil] raw programming ok.")
|
||||
|
||||
self.info("[qfil] patching...")
|
||||
for xml in patch:
|
||||
filename = os.path.join(imagedir, xml)
|
||||
self.info("[qfil] patching with %s" % xml)
|
||||
if os.path.exists(filename):
|
||||
fl = open(filename, "r")
|
||||
for evt, elem in ET.iterparse(fl, events=["end"]):
|
||||
if elem.tag == "patch":
|
||||
filename = elem.get("filename")
|
||||
if filename != "DISK":
|
||||
continue
|
||||
start_sector = elem.get("start_sector")
|
||||
size_in_bytes = elem.get("size_in_bytes")
|
||||
self.info(
|
||||
f"[qfil] patching {filename} sector({start_sector}), size={size_in_bytes}".format(
|
||||
filename=filename, start_sector=start_sector, size_in_bytes=size_in_bytes))
|
||||
content = ElementTree.tostring(elem).decode("utf-8")
|
||||
CMD = "<?xml version=\"1.0\" ?><data>\n {content} </data>".format(
|
||||
content=content)
|
||||
print(CMD)
|
||||
self.firehose.xmlsend(CMD)
|
||||
else:
|
||||
self.warning(f"File : {filename} not found.")
|
||||
self.info("[qfil] patching ok")
|
||||
bootable = self.find_bootable_partition(rawprogram)
|
||||
if bootable != -1:
|
||||
if self.firehose.cmd_setbootablestoragedrive(bootable):
|
||||
self.info("[qfil] partition({partition}) is now bootable\n".format(partition=bootable))
|
||||
else:
|
||||
self.info(
|
||||
"[qfil] set partition({partition}) as bootable failed\n".format(partition=bootable))
|
||||
|
||||
else:
|
||||
self.error("Unknown/Missing command, a command is required.")
|
||||
return False
|
357
edl/Library/gpt.py
Executable file
357
edl/Library/gpt.py
Executable file
|
@ -0,0 +1,357 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) B.Kerler 2018-2021
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
from enum import Enum
|
||||
from struct import unpack, pack
|
||||
from binascii import hexlify
|
||||
|
||||
try:
|
||||
from edl.Library.utils import LogBase, structhelper
|
||||
except:
|
||||
from utils import LogBase, structhelper
|
||||
|
||||
|
||||
class gpt(metaclass=LogBase):
|
||||
class gpt_header:
|
||||
def __init__(self, data):
|
||||
sh = structhelper(data)
|
||||
self.signature = sh.bytes(8)
|
||||
self.revision = sh.dword()
|
||||
self.header_size = sh.dword()
|
||||
self.crc32 = sh.dword()
|
||||
self.reserved = sh.dword()
|
||||
self.current_lba = sh.qword()
|
||||
self.backup_lba = sh.qword()
|
||||
self.first_usable_lba = sh.qword()
|
||||
self.last_usable_lba = sh.qword()
|
||||
self.disk_guid = sh.bytes(16)
|
||||
self.part_entry_start_lba = sh.qword()
|
||||
self.num_part_entries = sh.dword()
|
||||
self.part_entry_size = sh.dword()
|
||||
|
||||
class gpt_partition:
|
||||
def __init__(self, data):
|
||||
sh = structhelper(data)
|
||||
self.type = sh.bytes(16)
|
||||
self.unique = sh.bytes(16)
|
||||
self.first_lba = sh.qword()
|
||||
self.last_lba = sh.qword()
|
||||
self.flags = sh.qword()
|
||||
self.name = sh.string(72)
|
||||
|
||||
class efi_type(Enum):
|
||||
EFI_UNUSED = 0x00000000
|
||||
EFI_MBR = 0x024DEE41
|
||||
EFI_SYSTEM = 0xC12A7328
|
||||
EFI_BIOS_BOOT = 0x21686148
|
||||
EFI_IFFS = 0xD3BFE2DE
|
||||
EFI_SONY_BOOT = 0xF4019732
|
||||
EFI_LENOVO_BOOT = 0xBFBFAFE7
|
||||
EFI_MSR = 0xE3C9E316
|
||||
EFI_BASIC_DATA = 0xEBD0A0A2
|
||||
EFI_LDM_META = 0x5808C8AA
|
||||
EFI_LDM = 0xAF9B60A0
|
||||
EFI_RECOVERY = 0xDE94BBA4
|
||||
EFI_GPFS = 0x37AFFC90
|
||||
EFI_STORAGE_SPACES = 0xE75CAF8F
|
||||
EFI_HPUX_DATA = 0x75894C1E
|
||||
EFI_HPUX_SERVICE = 0xE2A1E728
|
||||
EFI_LINUX_DAYA = 0x0FC63DAF
|
||||
EFI_LINUX_RAID = 0xA19D880F
|
||||
EFI_LINUX_ROOT32 = 0x44479540
|
||||
EFI_LINUX_ROOT64 = 0x4F68BCE3
|
||||
EFI_LINUX_ROOT_ARM32 = 0x69DAD710
|
||||
EFI_LINUX_ROOT_ARM64 = 0xB921B045
|
||||
EFI_LINUX_SWAP = 0x0657FD6D
|
||||
EFI_LINUX_LVM = 0xE6D6D379
|
||||
EFI_LINUX_HOME = 0x933AC7E1
|
||||
EFI_LINUX_SRV = 0x3B8F8425
|
||||
EFI_LINUX_DM_CRYPT = 0x7FFEC5C9
|
||||
EFI_LINUX_LUKS = 0xCA7D7CCB
|
||||
EFI_LINUX_RESERVED = 0x8DA63339
|
||||
EFI_FREEBSD_BOOT = 0x83BD6B9D
|
||||
EFI_FREEBSD_DATA = 0x516E7CB4
|
||||
EFI_FREEBSD_SWAP = 0x516E7CB5
|
||||
EFI_FREEBSD_UFS = 0x516E7CB6
|
||||
EFI_FREEBSD_VINUM = 0x516E7CB8
|
||||
EFI_FREEBSD_ZFS = 0x516E7CBA
|
||||
EFI_OSX_HFS = 0x48465300
|
||||
EFI_OSX_UFS = 0x55465300
|
||||
EFI_OSX_ZFS = 0x6A898CC3
|
||||
EFI_OSX_RAID = 0x52414944
|
||||
EFI_OSX_RAID_OFFLINE = 0x52414944
|
||||
EFI_OSX_RECOVERY = 0x426F6F74
|
||||
EFI_OSX_LABEL = 0x4C616265
|
||||
EFI_OSX_TV_RECOVERY = 0x5265636F
|
||||
EFI_OSX_CORE_STORAGE = 0x53746F72
|
||||
EFI_SOLARIS_BOOT = 0x6A82CB45
|
||||
EFI_SOLARIS_ROOT = 0x6A85CF4D
|
||||
EFI_SOLARIS_SWAP = 0x6A87C46F
|
||||
EFI_SOLARIS_BACKUP = 0x6A8B642B
|
||||
EFI_SOLARIS_USR = 0x6A898CC3
|
||||
EFI_SOLARIS_VAR = 0x6A8EF2E9
|
||||
EFI_SOLARIS_HOME = 0x6A90BA39
|
||||
EFI_SOLARIS_ALTERNATE = 0x6A9283A5
|
||||
EFI_SOLARIS_RESERVED1 = 0x6A945A3B
|
||||
EFI_SOLARIS_RESERVED2 = 0x6A9630D1
|
||||
EFI_SOLARIS_RESERVED3 = 0x6A980767
|
||||
EFI_SOLARIS_RESERVED4 = 0x6A96237F
|
||||
EFI_SOLARIS_RESERVED5 = 0x6A8D2AC7
|
||||
EFI_NETBSD_SWAP = 0x49F48D32
|
||||
EFI_NETBSD_FFS = 0x49F48D5A
|
||||
EFI_NETBSD_LFS = 0x49F48D82
|
||||
EFI_NETBSD_RAID = 0x49F48DAA
|
||||
EFI_NETBSD_CONCAT = 0x2DB519C4
|
||||
EFI_NETBSD_ENCRYPT = 0x2DB519EC
|
||||
EFI_CHROMEOS_KERNEL = 0xFE3A2A5D
|
||||
EFI_CHROMEOS_ROOTFS = 0x3CB8E202
|
||||
EFI_CHROMEOS_FUTURE = 0x2E0A753D
|
||||
EFI_HAIKU = 0x42465331
|
||||
EFI_MIDNIGHTBSD_BOOT = 0x85D5E45E
|
||||
EFI_MIDNIGHTBSD_DATA = 0x85D5E45A
|
||||
EFI_MIDNIGHTBSD_SWAP = 0x85D5E45B
|
||||
EFI_MIDNIGHTBSD_UFS = 0x0394EF8B
|
||||
EFI_MIDNIGHTBSD_VINUM = 0x85D5E45C
|
||||
EFI_MIDNIGHTBSD_ZFS = 0x85D5E45D
|
||||
EFI_CEPH_JOURNAL = 0x45B0969E
|
||||
EFI_CEPH_ENCRYPT = 0x45B0969E
|
||||
EFI_CEPH_OSD = 0x4FBD7E29
|
||||
EFI_CEPH_ENCRYPT_OSD = 0x4FBD7E29
|
||||
EFI_CEPH_CREATE = 0x89C57F98
|
||||
EFI_CEPH_ENCRYPT_CREATE = 0x89C57F98
|
||||
EFI_OPENBSD = 0x824CC7A0
|
||||
EFI_QNX = 0xCEF5A9AD
|
||||
EFI_PLAN9 = 0xC91818F9
|
||||
EFI_VMWARE_VMKCORE = 0x9D275380
|
||||
EFI_VMWARE_VMFS = 0xAA31E02A
|
||||
EFI_VMWARE_RESERVED = 0x9198EFFC
|
||||
|
||||
def __init__(self, num_part_entries=0, part_entry_size=0, part_entry_start_lba=0, loglevel=logging.INFO, *args,
|
||||
**kwargs):
|
||||
self.num_part_entries = num_part_entries
|
||||
self.__logger = self.__logger
|
||||
self.part_entry_size = part_entry_size
|
||||
self.part_entry_start_lba = part_entry_start_lba
|
||||
self.totalsectors = None
|
||||
self.header = None
|
||||
self.sectorsize = None
|
||||
self.partentries = []
|
||||
|
||||
self.error = self.__logger.error
|
||||
self.__logger.setLevel(loglevel)
|
||||
if loglevel == logging.DEBUG:
|
||||
logfilename = "log.txt"
|
||||
fh = logging.FileHandler(logfilename)
|
||||
self.__logger.addHandler(fh)
|
||||
|
||||
def parseheader(self, gptdata, sectorsize=512):
|
||||
return self.gpt_header(gptdata[sectorsize:sectorsize + 0x5C])
|
||||
|
||||
def parse(self, gptdata, sectorsize=512):
|
||||
self.header = self.gpt_header(gptdata[sectorsize:sectorsize + 0x5C])
|
||||
self.sectorsize = sectorsize
|
||||
if self.header.signature != b"EFI PART":
|
||||
return False
|
||||
if self.header.revision != 0x10000:
|
||||
self.error("Unknown GPT revision.")
|
||||
return False
|
||||
if self.part_entry_start_lba != 0:
|
||||
start = self.part_entry_start_lba
|
||||
else:
|
||||
start = self.header.part_entry_start_lba * sectorsize
|
||||
|
||||
entrysize = self.header.part_entry_size
|
||||
self.partentries = []
|
||||
|
||||
class partf:
|
||||
unique = b""
|
||||
first_lba = 0
|
||||
last_lba = 0
|
||||
flags = 0
|
||||
sector = 0
|
||||
sectors = 0
|
||||
type = b""
|
||||
name = ""
|
||||
|
||||
num_part_entries = self.header.num_part_entries
|
||||
|
||||
for idx in range(0, num_part_entries):
|
||||
data = gptdata[start + (idx * entrysize):start + (idx * entrysize) + entrysize]
|
||||
if int(hexlify(data[16:32]), 16) == 0:
|
||||
break
|
||||
partentry = self.gpt_partition(data)
|
||||
pa = partf()
|
||||
guid1 = unpack("<I", partentry.unique[0:0x4])[0]
|
||||
guid2 = unpack("<H", partentry.unique[0x4:0x6])[0]
|
||||
guid3 = unpack("<H", partentry.unique[0x6:0x8])[0]
|
||||
guid4 = unpack("<H", partentry.unique[0x8:0xA])[0]
|
||||
guid5 = hexlify(partentry.unique[0xA:0x10]).decode('utf-8')
|
||||
pa.unique = "{:08x}-{:04x}-{:04x}-{:04x}-{}".format(guid1, guid2, guid3, guid4, guid5)
|
||||
pa.sector = partentry.first_lba
|
||||
pa.sectors = partentry.last_lba - partentry.first_lba + 1
|
||||
pa.flags = partentry.flags
|
||||
type = int(unpack("<I", partentry.type[0:0x4])[0])
|
||||
try:
|
||||
pa.type = self.efi_type(type).name
|
||||
except:
|
||||
pa.type = hex(type)
|
||||
pa.name = partentry.name.replace(b"\x00\x00", b"").decode('utf-16')
|
||||
if pa.type == "EFI_UNUSED":
|
||||
continue
|
||||
self.partentries.append(pa)
|
||||
self.totalsectors = self.header.last_usable_lba + 34
|
||||
return True
|
||||
|
||||
def print(self):
|
||||
print(self.tostring())
|
||||
|
||||
def tostring(self):
|
||||
mstr = "\nGPT Table:\n-------------\n"
|
||||
for partition in self.partentries:
|
||||
mstr += ("{:20} Offset 0x{:016x}, Length 0x{:016x}, Flags 0x{:08x}, UUID {}, Type {}\n".format(
|
||||
partition.name + ":", partition.sector * self.sectorsize, partition.sectors * self.sectorsize,
|
||||
partition.flags, partition.unique, partition.type))
|
||||
mstr += ("\nTotal disk size:0x{:016x}, sectors:0x{:016x}\n".format(self.totalsectors * self.sectorsize,
|
||||
self.totalsectors))
|
||||
return mstr
|
||||
|
||||
def generate_rawprogram(self, lun, sectorsize, directory):
|
||||
fname = "rawprogram" + str(lun) + ".xml"
|
||||
with open(os.path.join(directory, fname), "wb") as wf:
|
||||
mstr = "<?xml version=\"1.0\" ?>\n<data>\n"
|
||||
partofsingleimage = "false"
|
||||
readbackverify = "false"
|
||||
sparse = "false"
|
||||
for partition in self.partentries:
|
||||
filename = partition.name + ".bin"
|
||||
mstr += f"\t<program SECTOR_SIZE_IN_BYTES=\"{sectorsize}\" " + \
|
||||
f"file_sector_offset=\"0\" " \
|
||||
f"filename=\"{filename}\" " + \
|
||||
f"label=\"{partition.name}\" " \
|
||||
f"num_partition_sectors=\"{partition.sectors}\" " + \
|
||||
f"partofsingleimage=\"{partofsingleimage}\" " \
|
||||
f"physical_partition_number=\"{str(lun)}\" " + \
|
||||
f"readbackverify=\"{readbackverify}\" " \
|
||||
f"size_in_KB=\"{(partition.sectors * sectorsize / 1024):.1f}\" " \
|
||||
f"sparse=\"{sparse}\" " + \
|
||||
f"start_byte_hex=\"{hex(partition.sector * sectorsize)}\" " \
|
||||
f"start_sector=\"{partition.sector}\"/>\n"
|
||||
partofsingleimage = "true"
|
||||
sectors = self.header.first_usable_lba
|
||||
mstr += f"\t<program SECTOR_SIZE_IN_BYTES=\"{sectorsize}\" " + \
|
||||
f"file_sector_offset=\"0\" " + \
|
||||
f"filename=\"gpt_main{str(lun)}.bin\" " + \
|
||||
f"label=\"PrimaryGPT\" " + \
|
||||
f"num_partition_sectors=\"{sectors}\" " + \
|
||||
f"partofsingleimage=\"{partofsingleimage}\" " + \
|
||||
f"physical_partition_number=\"{str(lun)}\" " + \
|
||||
f"readbackverify=\"{readbackverify}\" " + \
|
||||
f"size_in_KB=\"{(sectors * sectorsize / 1024):.1f}\" " + \
|
||||
f"sparse=\"{sparse}\" " + \
|
||||
f"start_byte_hex=\"0x0\" " + \
|
||||
f"start_sector=\"0\"/>\n"
|
||||
sectors = self.header.first_usable_lba - 1
|
||||
mstr += f"\t<program SECTOR_SIZE_IN_BYTES=\"{sectorsize}\" " + \
|
||||
f"file_sector_offset=\"0\" " + \
|
||||
f"filename=\"gpt_backup{str(lun)}.bin\" " + \
|
||||
f"label=\"BackupGPT\" " + \
|
||||
f"num_partition_sectors=\"{sectors}\" " + \
|
||||
f"partofsingleimage=\"{partofsingleimage}\" " + \
|
||||
f"physical_partition_number=\"{str(lun)}\" " + \
|
||||
f"readbackverify=\"{readbackverify}\" " + \
|
||||
f"size_in_KB=\"{(sectors * sectorsize / 1024):.1f}\" " + \
|
||||
f"sparse=\"{sparse}\" " + \
|
||||
f"start_byte_hex=\"({sectorsize}*NUM_DISK_SECTORS)-{sectorsize * sectors}.\" " + \
|
||||
f"start_sector=\"NUM_DISK_SECTORS-{sectors}.\"/>\n"
|
||||
mstr += "</data>"
|
||||
wf.write(bytes(mstr, 'utf-8'))
|
||||
print(f"Wrote partition xml as {fname}")
|
||||
|
||||
def print_gptfile(self, filename):
|
||||
try:
|
||||
filesize = os.stat(filename).st_size
|
||||
with open(filename, "rb") as rf:
|
||||
size = min(32 * 4096, filesize)
|
||||
data = rf.read(size)
|
||||
for sectorsize in [512, 4096]:
|
||||
result = self.parse(data, sectorsize)
|
||||
if result:
|
||||
break
|
||||
if result:
|
||||
print(self.tostring())
|
||||
return result
|
||||
except Exception as e:
|
||||
self.error(str(e))
|
||||
return ""
|
||||
|
||||
def test_gpt(self):
|
||||
res = self.print_gptfile(os.path.join("TestFiles", "gpt_sm8180x.bin"))
|
||||
assert res, "GPT Partition wasn't decoded properly"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="GPT utils")
|
||||
subparsers = parser.add_subparsers(dest="command", help='sub-command help')
|
||||
|
||||
parser_print = subparsers.add_parser("print", help="Print the gpt table")
|
||||
parser_print.add_argument("image", help="The path of the GPT disk image")
|
||||
|
||||
parser_test = subparsers.add_parser("test", help="Run self-test")
|
||||
|
||||
parser_extract = subparsers.add_parser("extract", help="Extract the partitions")
|
||||
parser_extract.add_argument("image", help="The path of the GPT disk image")
|
||||
parser_extract.add_argument("-out", "-o", help="The path to extract the partitions")
|
||||
parser_extract.add_argument("-partition", "-p", help="Extract specific partitions (separated by comma)")
|
||||
|
||||
args = parser.parse_args()
|
||||
if args.command not in ["print", "extract", "test"]:
|
||||
parser.error("Command is mandatory")
|
||||
|
||||
gp = gpt()
|
||||
if args.command == "print":
|
||||
if not os.path.exists(args.image):
|
||||
print(f"File {args.image} does not exist. Aborting.")
|
||||
sys.exit(1)
|
||||
gp.print_gptfile(args.image)
|
||||
elif args.command == "test":
|
||||
gp.test_gpt()
|
||||
elif args.command == "extract":
|
||||
if not os.path.exists(args.image):
|
||||
print(f"File {args.image} does not exist. Aborting.")
|
||||
sys.exit(1)
|
||||
filesize = os.stat(args.image).st_size
|
||||
with open(args.image, "rb", buffering=1024 * 1024) as rf:
|
||||
data = rf.read(min(32 * 4096, filesize))
|
||||
ssize = None
|
||||
for sectorsize in [512, 4096]:
|
||||
result = gp.parse(data, sectorsize)
|
||||
if result:
|
||||
ssize = sectorsize
|
||||
break
|
||||
if ssize is not None:
|
||||
for partition in gp.partentries:
|
||||
if args.partition is not None:
|
||||
if partition != args.partition:
|
||||
continue
|
||||
name = partition.name
|
||||
start = partition.sector * ssize
|
||||
length = partition.sectors * ssize
|
||||
out = args.out
|
||||
if out is None:
|
||||
out = "."
|
||||
if not os.path.exists(out):
|
||||
os.makedirs(out)
|
||||
filename = os.path.join(out, name)
|
||||
rf.seek(start)
|
||||
bytestoread = length
|
||||
with open(filename, "wb", buffering=1024 * 1024) as wf:
|
||||
while bytestoread > 0:
|
||||
size = min(bytestoread, 0x200000)
|
||||
rf.read(size)
|
||||
wf.write(size)
|
||||
bytestoread -= size
|
||||
print(f"Extracting {name} to {filename} at {hex(start)}, length {hex(length)}")
|
240
edl/Library/hdlc.py
Executable file
240
edl/Library/hdlc.py
Executable file
|
@ -0,0 +1,240 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) B.Kerler 2018-2019
|
||||
|
||||
import logging
|
||||
from binascii import hexlify
|
||||
from struct import unpack
|
||||
import time
|
||||
|
||||
MAX_PACKET_LEN = 4096
|
||||
|
||||
crcTbl = (
|
||||
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
|
||||
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
|
||||
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
|
||||
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
|
||||
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
|
||||
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
|
||||
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
|
||||
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
|
||||
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
|
||||
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
|
||||
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
|
||||
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
|
||||
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
|
||||
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
|
||||
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
|
||||
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
|
||||
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
|
||||
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
|
||||
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
|
||||
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
|
||||
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
|
||||
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
|
||||
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
|
||||
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
|
||||
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
|
||||
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
|
||||
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
|
||||
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
|
||||
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
|
||||
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
|
||||
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
|
||||
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78)
|
||||
|
||||
|
||||
def serial16le(data):
|
||||
out = bytearray()
|
||||
out.append(data & 0xFF)
|
||||
out.append((data >> 8) & 0xFF)
|
||||
return out
|
||||
|
||||
|
||||
def serial16(data):
|
||||
out = bytearray()
|
||||
out.append((data >> 8) & 0xFF)
|
||||
out.append(data & 0xFF)
|
||||
return out
|
||||
|
||||
|
||||
def serial32le(data):
|
||||
out = bytearray()
|
||||
out += serial16le(data & 0xFFFF)
|
||||
out += serial16le((data >> 16) & 0xFFFF)
|
||||
return out
|
||||
|
||||
|
||||
def crc16(iv, data):
|
||||
for byte in data:
|
||||
iv = ((iv >> 8) & 0xFFFF) ^ crcTbl[(iv ^ byte) & 0xFF]
|
||||
return ~iv & 0xFFFF
|
||||
|
||||
|
||||
def serial32(data):
|
||||
out = bytearray()
|
||||
out += serial16((data >> 16) & 0xFFFF)
|
||||
out += serial16(data & 0xFFFF)
|
||||
return out
|
||||
|
||||
|
||||
def escape(indata):
|
||||
outdata = bytearray()
|
||||
for i in range(0, len(indata)):
|
||||
buf = indata[i]
|
||||
if buf == 0x7e:
|
||||
outdata.append(0x7d)
|
||||
outdata.append(0x5e)
|
||||
elif buf == 0x7d:
|
||||
outdata.append(0x7d)
|
||||
outdata.append(0x5d)
|
||||
else:
|
||||
outdata.append(buf)
|
||||
return outdata
|
||||
|
||||
|
||||
def unescape(indata):
|
||||
mescape = False
|
||||
out = bytearray()
|
||||
for buf in indata:
|
||||
if mescape:
|
||||
if buf == 0x5e:
|
||||
out.append(0x7e)
|
||||
elif buf == 0x5d:
|
||||
out.append(0x7d)
|
||||
else:
|
||||
logging.error("Fatal error unescaping buffer!")
|
||||
return None
|
||||
mescape = False
|
||||
else:
|
||||
if buf == 0x7d:
|
||||
mescape = True
|
||||
else:
|
||||
out.append(buf)
|
||||
if len(out) == 0:
|
||||
return None
|
||||
return out
|
||||
|
||||
|
||||
def convert_cmdbuf(indata):
|
||||
crc16val = crc16(0xFFFF, indata)
|
||||
indata.extend(bytearray(serial16le(crc16val)))
|
||||
outdata = escape(indata)
|
||||
outdata.append(0x7E)
|
||||
return outdata
|
||||
|
||||
|
||||
class hdlc:
|
||||
def __init__(self, cdc):
|
||||
self.cdc = cdc
|
||||
self.programmer = None
|
||||
self.timeout = 1500
|
||||
|
||||
def receive_reply(self, timeout=None):
|
||||
replybuf = bytearray()
|
||||
if timeout is None:
|
||||
timeout = self.timeout
|
||||
tmp = self.cdc.read(MAX_PACKET_LEN, timeout)
|
||||
if tmp == bytearray():
|
||||
return 0
|
||||
if tmp == b"":
|
||||
return 0
|
||||
retry = 0
|
||||
while tmp[-1] != 0x7E:
|
||||
time.sleep(0.01)
|
||||
tmp += self.cdc.read(MAX_PACKET_LEN, timeout)
|
||||
retry += 1
|
||||
if retry > 5:
|
||||
break
|
||||
replybuf.extend(tmp)
|
||||
data = unescape(replybuf)
|
||||
# print(hexlify(data))
|
||||
if len(data) > 3:
|
||||
crc16val = crc16(0xFFFF, data[:-3])
|
||||
reccrc = int(data[-3]) + (int(data[-2]) << 8)
|
||||
if crc16val != reccrc:
|
||||
return -1
|
||||
else:
|
||||
time.sleep(0.01)
|
||||
data = self.cdc.read(MAX_PACKET_LEN, timeout)
|
||||
if len(data) > 3:
|
||||
crc16val = crc16(0xFFFF, data[:-3])
|
||||
reccrc = int(data[-3]) + (int(data[-2]) << 8)
|
||||
if crc16val != reccrc:
|
||||
return -1
|
||||
return data
|
||||
return data[:-3]
|
||||
|
||||
def receive_reply_nocrc(self, timeout=None):
|
||||
replybuf = bytearray()
|
||||
if timeout is None:
|
||||
timeout = self.timeout
|
||||
tmp = self.cdc.read(MAX_PACKET_LEN, timeout)
|
||||
if tmp == bytearray():
|
||||
return 0
|
||||
if tmp == b"":
|
||||
return 0
|
||||
retry = 0
|
||||
while tmp[-1] != 0x7E:
|
||||
# time.sleep(0.05)
|
||||
tmp += self.cdc.read(MAX_PACKET_LEN, timeout)
|
||||
retry += 1
|
||||
if retry > 5:
|
||||
break
|
||||
replybuf.extend(tmp)
|
||||
data = unescape(replybuf)
|
||||
# print(hexlify(data))
|
||||
if len(data) > 3:
|
||||
# crc16val = self.crc16(0xFFFF, data[:-3])
|
||||
# reccrc = int(data[-3]) + (int(data[-2]) << 8)
|
||||
return data[:-3]
|
||||
else:
|
||||
time.sleep(0.5)
|
||||
data = self.cdc.read(MAX_PACKET_LEN, timeout)
|
||||
if len(data) > 3:
|
||||
# crc16val = self.crc16(0xFFFF, data[:-3])
|
||||
# reccrc = int(data[-3]) + (int(data[-2]) << 8)
|
||||
return data[:-3]
|
||||
else:
|
||||
return data
|
||||
|
||||
def send_unframed_buf(self, outdata, prefixflag):
|
||||
# ttyflush()
|
||||
if prefixflag:
|
||||
tmp = bytearray()
|
||||
tmp.append(0x7E)
|
||||
tmp.extend(outdata)
|
||||
outdata = tmp
|
||||
return self.cdc.write(outdata[:MAX_PACKET_LEN])
|
||||
# FlushFileBuffers(ser)
|
||||
|
||||
def send_cmd_base(self, outdata, prefixflag, nocrc=False):
|
||||
if isinstance(outdata, str):
|
||||
outdata = bytes(outdata, 'utf-8')
|
||||
packet = convert_cmdbuf(bytearray(outdata))
|
||||
if self.send_unframed_buf(packet, prefixflag):
|
||||
if nocrc:
|
||||
return self.receive_reply_nocrc()
|
||||
else:
|
||||
return self.receive_reply()
|
||||
return b""
|
||||
|
||||
def send_cmd(self, outdata, nocrc=False):
|
||||
return self.send_cmd_base(outdata, 1, nocrc)
|
||||
|
||||
def send_cmd_np(self, outdata, nocrc=False):
|
||||
return self.send_cmd_base(outdata, 0, nocrc)
|
||||
|
||||
def show_errpacket(self, descr, pktbuf):
|
||||
if len(pktbuf) == 0:
|
||||
return
|
||||
logging.error("Error: %s " % descr)
|
||||
|
||||
if pktbuf[1] == 0x0e:
|
||||
pktbuf[-4] = 0
|
||||
# puts(pktbuf+2)
|
||||
ret = self.receive_reply()
|
||||
errorcode = unpack("<I", ret[2:2 + 4])
|
||||
logging.error("Error code = %08x\n\n", errorcode)
|
||||
else:
|
||||
print(hexlify(pktbuf))
|
78
edl/Library/memparse.py
Executable file
78
edl/Library/memparse.py
Executable file
|
@ -0,0 +1,78 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) B.Kerler 2018-2021
|
||||
|
||||
import os
|
||||
import pt64
|
||||
import pt
|
||||
import argparse
|
||||
|
||||
|
||||
def pt64_walk(data, ttbr, tnsz, levels=3):
|
||||
print("Dumping page tables (levels=%d)" % levels)
|
||||
print("First level (ptbase = %016x)" % ttbr)
|
||||
print("---------------------------------------------")
|
||||
fl = data[ttbr - ttbr:ttbr - ttbr + 0x1000]
|
||||
|
||||
if levels <= 1:
|
||||
return
|
||||
|
||||
for (va, fle) in pt64.parse_pt(fl, 0, tnsz, 1):
|
||||
if "TABLE" in str(fle):
|
||||
print("Second level (ptbase = %016x)" % fle.output)
|
||||
print("---------------------------------------------")
|
||||
|
||||
sl = data[fle.output - ttbr:fle.output - ttbr + 0x4000]
|
||||
sl = pt64.parse_pt(sl, va, tnsz, 2)
|
||||
|
||||
if levels <= 2:
|
||||
continue
|
||||
|
||||
for (mva, sle) in sl:
|
||||
if "TABLE" in str(sle):
|
||||
print("Third level (ptbase = %016x)" % sle.output)
|
||||
print("---------------------------------------------")
|
||||
tl = data[sle.output - ttbr:sle.output - ttbr + 0x1000]
|
||||
pt64.parse_pt(tl, mva, tnsz, 3)
|
||||
|
||||
|
||||
def pt32_walk(data, ttbr, skip):
|
||||
print("First level (va = %08x)" % ttbr)
|
||||
print("---------------------------------------------")
|
||||
fl = data[ttbr - ttbr:ttbr - ttbr + 0x4000]
|
||||
|
||||
i = 0
|
||||
for (va, fl) in pt.parse_pt(fl):
|
||||
i += 1
|
||||
if i <= skip:
|
||||
continue
|
||||
if type(fl) == pt.pt_desc:
|
||||
print("")
|
||||
print("Second level (va = %08x)" % va)
|
||||
print("---------------------------------------------")
|
||||
sldata = data[fl.coarse_base - ttbr:fl.coarse_base - ttbr + 0x400]
|
||||
pt.parse_spt(sldata, va)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="memparse",
|
||||
usage="python memparse.py -arch <32,64> -in <filename> -mem <offset>",
|
||||
formatter_class=argparse.RawTextHelpFormatter)
|
||||
parser.add_argument('-in', '--in', dest='infile', help='memory dump', default="")
|
||||
parser.add_argument('-arch', '--arch', dest='arch', help='architecture=32,64', default="32")
|
||||
parser.add_argument('-mem', '--mem', dest='mem', help='memoryoffset', default="0x200000")
|
||||
args = parser.parse_args()
|
||||
if args.infile == "":
|
||||
print("You need to add an -in [memorydump filename]")
|
||||
return
|
||||
|
||||
with open(args.infile, "rb") as rf:
|
||||
data = rf.read()
|
||||
if args.arch == "32":
|
||||
pt32_walk(data, int(args.mem, 16), False)
|
||||
else:
|
||||
pt64_walk(data, int(args.mem, 16), 0, 3)
|
||||
|
||||
|
||||
main()
|
789
edl/Library/nand_config.py
Normal file
789
edl/Library/nand_config.py
Normal file
|
@ -0,0 +1,789 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) B.Kerler 2018-2019
|
||||
import ctypes
|
||||
from enum import Enum
|
||||
from edl.Config.qualcomm_config import secgen, secureboottbl
|
||||
|
||||
c_uint8 = ctypes.c_uint8
|
||||
|
||||
# nandbase MSM_NAND_BASE
|
||||
# qfprom SECURITY_CONTROL_BASE_PHYS
|
||||
config_tbl = {
|
||||
# bam nandbase bcraddr secureboot pbl qfprom memtbl
|
||||
3: ["9x25", 1, 0xf9af0000, 0xfc401a40, secureboottbl["MDM9x25"], secgen[2][0], secgen[2][1], secgen[2][2]],
|
||||
8: ["9x35", 1, 0xf9af0000, 0xfc401a40, secureboottbl["MDM9x35"], secgen[2][0], secgen[2][1], secgen[2][2]],
|
||||
10: ["9x45", 1, 0x79B0000, 0x183f000, secureboottbl["MDM9x45"], secgen[2][0], secgen[2][1], secgen[2][2]],
|
||||
16: ["9x55", 0, 0x79B0000, 0x183f000, secureboottbl["MDM9x45"], secgen[5][0], secgen[5][1], secgen[5][2]],
|
||||
17: ["9x60", 0, 0x79B0000, 0x183f000, secureboottbl["MDM9x60"], secgen[5][0], secgen[5][1], secgen[5][2]],
|
||||
12: ["9x07", 0, 0x79B0000, 0x183f000, secureboottbl["MDM9607"], secgen[5][0], secgen[5][1], secgen[5][2]]
|
||||
}
|
||||
|
||||
supported_flash = {
|
||||
# Flash ID Density(MB) Wid Pgsz Blksz oobsz onenand Manuf */
|
||||
0x2690ac2c: [(512 << 20), 0, 4096, (4096 << 6), 224, 0], # QUECTEL_NAND_FM6BD4G2GXA
|
||||
0x2690ac98: [(512 << 20), 0, 4096, (4096 << 6), 256, 0], # QUECTEL_NAND_NM14FSK2LAXCL
|
||||
0x1500aa98: [(256 << 20), 0, 2048, (2048 << 6), 64, 0],
|
||||
0x5500ba98: [(256 << 20), 1, 2048, (2048 << 6), 64, 0],
|
||||
0xd580b12c: [(256 << 20), 1, 2048, (2048 << 6), 64, 0],
|
||||
0x5590bc2c: [(512 << 20), 1, 2048, (2048 << 6), 64, 0],
|
||||
0x1580aa2c: [(256 << 20), 0, 2048, (2048 << 6), 64, 0],
|
||||
0x1590aa2c: [(256 << 20), 0, 2048, (2048 << 6), 64, 0],
|
||||
0x1590ac2c: [(512 << 20), 0, 2048, (2048 << 6), 64, 0],
|
||||
0x5580baad: [(256 << 20), 1, 2048, (2048 << 6), 64, 0],
|
||||
0x5510baad: [(256 << 20), 1, 2048, (2048 << 6), 64, 0],
|
||||
# 0x004000ec: [(256 << 20), 0, 2048, (2048 << 6), 64, 1],
|
||||
# 0x005c00ec: [(256 << 20), 0, 2048, (2048 << 6), 64, 1],
|
||||
# 0x005800ec: [(256 << 20), 0, 2048, (2048 << 6), 64, 1],
|
||||
0x5580ba2c: [(256 << 20), 1, 2048, (2048 << 6), 64, 0],
|
||||
0x6600b3ec: [(1024 << 20), 1, 4096, (4096 << 6), 128, 0],
|
||||
0x55d1b32c: [(1024 << 20), 1, 2048, (2048 << 6), 64, 0]
|
||||
# 0x1500aaec: 0xFF00FFFF, (256 << 20), 0, 2048, (2048 << 6), 64, 0],
|
||||
# 0x5500baec: 0xFF00FFFF, (256 << 20), 1, 2048, (2048 << 6), 64, 0}, /*Sams */
|
||||
# 0x6600bcec: 0xFF00FFFF, (512 << 20), 1, 4096, (4096 << 6), 128, 0}, /*Sams */
|
||||
# 0x2600482c: 0xFF00FFFF, (2048 << 20), 0, 4096, (4096 << 7), 224, 0}, /*8bit bch ecc */
|
||||
}
|
||||
|
||||
|
||||
class BadFlags(Enum):
|
||||
BAD_UNDEF = 0
|
||||
BAD_FILL = 1
|
||||
BAD_SKIP = 2
|
||||
BAD_IGNORE = 3
|
||||
BAD_DISABLE = 4
|
||||
|
||||
|
||||
nand_ids = [
|
||||
("NAND 16MiB 1,8V 8-bit", 0x33, 16),
|
||||
("NAND 16MiB 3,3V 8-bit", 0x73, 16),
|
||||
("NAND 16MiB 1,8V 16-bit", 0x43, 16),
|
||||
("NAND 16MiB 3,3V 16-bit", 0x53, 16),
|
||||
|
||||
("NAND 32MiB 1,8V 8-bit", 0x35, 32),
|
||||
("NAND 32MiB 3,3V 8-bit", 0x75, 32),
|
||||
("NAND 32MiB 1,8V 16-bit", 0x45, 32),
|
||||
("NAND 32MiB 3,3V 16-bit", 0x55, 32),
|
||||
|
||||
("NAND 64MiB 1,8V 8-bit", 0x36, 64),
|
||||
("NAND 64MiB 3,3V 8-bit", 0x76, 64),
|
||||
("NAND 64MiB 1,8V 16-bit", 0x46, 64),
|
||||
("NAND 64MiB 3,3V 16-bit", 0x56, 64),
|
||||
|
||||
("NAND 128MiB 1,8V 8-bit", 0x78, 128),
|
||||
("NAND 128MiB 1,8V 8-bit", 0x39, 128),
|
||||
("NAND 128MiB 3,3V 8-bit", 0x79, 128),
|
||||
("NAND 128MiB 1,8V 16-bit", 0x72, 128),
|
||||
("NAND 128MiB 1,8V 16-bit", 0x49, 128),
|
||||
("NAND 128MiB 3,3V 16-bit", 0x74, 128),
|
||||
("NAND 128MiB 3,3V 16-bit", 0x59, 128),
|
||||
("NAND 256MiB 3,3V 8-bit", 0x71, 256),
|
||||
|
||||
# 512 Megabit
|
||||
("NAND 64MiB 1,8V 8-bit", 0xA2, 64),
|
||||
("NAND 64MiB 1,8V 8-bit", 0xA0, 64),
|
||||
("NAND 64MiB 3,3V 8-bit", 0xF2, 64),
|
||||
("NAND 64MiB 3,3V 8-bit", 0xD0, 64),
|
||||
("NAND 64MiB 1,8V 16-bit", 0xB2, 64),
|
||||
("NAND 64MiB 1,8V 16-bit", 0xB0, 64),
|
||||
("NAND 64MiB 3,3V 16-bit", 0xC2, 64),
|
||||
("NAND 64MiB 3,3V 16-bit", 0xC0, 64),
|
||||
|
||||
# 1 Gigabit
|
||||
("NAND 128MiB 1,8V 8-bit", 0xA1, 128),
|
||||
("NAND 128MiB 3,3V 8-bit", 0xF1, 128),
|
||||
("NAND 128MiB 3,3V 8-bit", 0xD1, 128),
|
||||
("NAND 128MiB 1,8V 16-bit", 0xB1, 128),
|
||||
("NAND 128MiB 3,3V 16-bit", 0xC1, 128),
|
||||
("NAND 128MiB 1,8V 16-bit", 0xAD, 128),
|
||||
|
||||
# 2 Gigabit
|
||||
("NAND 256MiB 1.8V 8-bit", 0xAA, 256),
|
||||
("NAND 256MiB 3.3V 8-bit", 0xDA, 256),
|
||||
("NAND 256MiB 1.8V 16-bit", 0xBA, 256),
|
||||
("NAND 256MiB 3.3V 16-bit", 0xCA, 256),
|
||||
|
||||
# 4 Gigabit
|
||||
("NAND 512MiB 1.8V 8-bit", 0xAC, 512),
|
||||
("NAND 512MiB 3.3V 8-bit", 0xDC, 512),
|
||||
("NAND 512MiB 1.8V 16-bit", 0xBC, 512),
|
||||
("NAND 512MiB 3.3V 16-bit", 0xCC, 512),
|
||||
|
||||
# 8 Gigabit
|
||||
("NAND 1GiB 1.8V 8-bit", 0xA3, 1024),
|
||||
("NAND 1GiB 3.3V 8-bit", 0xD3, 1024),
|
||||
("NAND 1GiB 1.8V 16-bit", 0xB3, 1024),
|
||||
("NAND 1GiB 3.3V 16-bit", 0xC3, 1024),
|
||||
|
||||
# 16 Gigabit
|
||||
("NAND 2GiB 1.8V 8-bit", 0xA5, 2048),
|
||||
("NAND 2GiB 3.3V 8-bit", 0xD5, 2048),
|
||||
("NAND 2GiB 1.8V 16-bit", 0xB5, 2048),
|
||||
("NAND 2GiB 3.3V 16-bit", 0xC5, 2048),
|
||||
|
||||
# 32 Gigabit
|
||||
("NAND 4GiB 1.8V 8-bit", 0xA7, 4096),
|
||||
("NAND 4GiB 3.3V 8-bit", 0xD7, 4096),
|
||||
("NAND 4GiB 1.8V 16-bit", 0xB7, 4096),
|
||||
("NAND 4GiB 3.3V 16-bit", 0xC7, 4096),
|
||||
|
||||
# 64 Gigabit
|
||||
("NAND 8GiB 1.8V 8-bit", 0xAE, 8192),
|
||||
("NAND 8GiB 3.3V 8-bit", 0xDE, 8192),
|
||||
("NAND 8GiB 1.8V 16-bit", 0xBE, 8192),
|
||||
("NAND 8GiB 3.3V 16-bit", 0xCE, 8192),
|
||||
|
||||
# 128 Gigabit
|
||||
("NAND 16GiB 1.8V 8-bit", 0x1A, 16384),
|
||||
("NAND 16GiB 3.3V 8-bit", 0x3A, 16384),
|
||||
("NAND 16GiB 1.8V 16-bit", 0x2A, 16384),
|
||||
("NAND 16GiB 3.3V 16-bit", 0x4A, 16384),
|
||||
|
||||
# 256 Gigabit
|
||||
("NAND 32GiB 1.8V 8-bit", 0x1C, 32768),
|
||||
("NAND 32GiB 3.3V 8-bit", 0x3C, 32768),
|
||||
("NAND 32GiB 1.8V 16-bit", 0x2C, 32768),
|
||||
("NAND 32GiB 3.3V 16-bit", 0x4C, 32768),
|
||||
|
||||
# 512 Gigabit
|
||||
("NAND 64GiB 1.8V 8-bit", 0x1E, 65536),
|
||||
("NAND 64GiB 3.3V 8-bit", 0x3E, 65536),
|
||||
("NAND 64GiB 1.8V 16-bit", 0x2E, 65536),
|
||||
("NAND 64GiB 3.3V 16-bit", 0x4E, 65536),
|
||||
(0, 0, 0),
|
||||
]
|
||||
|
||||
nand_manuf_ids = [
|
||||
(0x98, "Toshiba"),
|
||||
(0xec, "Samsung"),
|
||||
(0x04, "Fujitsu"),
|
||||
(0x8f, "National"),
|
||||
(0x07, "Renesas"),
|
||||
(0x20, "ST Micro"),
|
||||
(0xad, "Hynix"),
|
||||
(0x2c, "Micron"),
|
||||
(0xc8, "Elite Semiconductor"),
|
||||
(0x01, "Spansion/AMD"),
|
||||
(0xef, "Winbond"),
|
||||
(0x0, "")
|
||||
]
|
||||
|
||||
toshiba_tbl = {
|
||||
# small_slc,device_width,density_mbits
|
||||
0x36: [True, 8, 512],
|
||||
0x46: [True, 16, 512],
|
||||
0x79: [True, 8, 1024],
|
||||
0xA0: [False, -1, 512],
|
||||
0xB0: [False, -1, 512],
|
||||
0xC0: [False, -1, 512],
|
||||
0xD0: [False, -1, 512],
|
||||
0xA1: [False, -1, 1024],
|
||||
0xB1: [False, -1, 1024],
|
||||
0xC1: [False, -1, 1024],
|
||||
0xD1: [False, -1, 1024],
|
||||
0xAA: [False, -1, 2048],
|
||||
0xBA: [False, -1, 2048],
|
||||
0xCA: [False, -1, 2048],
|
||||
0xDA: [False, -1, 2048],
|
||||
0xAC: [False, -1, 4096],
|
||||
0xBC: [False, -1, 4096],
|
||||
0xCC: [False, -1, 4096],
|
||||
0xDC: [False, -1, 4096],
|
||||
0xA3: [False, -1, 8192],
|
||||
0xB3: [False, -1, 8192],
|
||||
0xC3: [False, -1, 8192],
|
||||
0xD3: [False, -1, 8192],
|
||||
0xA5: [False, -1, 16384],
|
||||
0xB5: [False, -1, 16384],
|
||||
0xC5: [False, -1, 16384],
|
||||
0xD5: [False, -1, 16384],
|
||||
}
|
||||
|
||||
samsung_tbl = {
|
||||
# small_slc,device_width,density_mbits
|
||||
0x45: [True, 16, 256],
|
||||
0x55: [True, 16, 256],
|
||||
0x35: [True, 8, 256],
|
||||
0x75: [True, 8, 256],
|
||||
0x46: [True, 16, 512],
|
||||
0x56: [True, 16, 512],
|
||||
0x76: [True, 8, 512],
|
||||
0x36: [True, 8, 512],
|
||||
0x72: [True, 16, 1024],
|
||||
0x74: [True, 16, 1024],
|
||||
0x79: [True, 8, 1024],
|
||||
0x78: [True, 8, 1024],
|
||||
0x71: [True, 8, 2048],
|
||||
0xDC: [True, 8, 4096],
|
||||
0xA1: [False, -1, 1024],
|
||||
0xB1: [False, -1, 1024],
|
||||
0xC1: [False, -1, 1024],
|
||||
0xD1: [False, -1, 1024],
|
||||
0xAA: [False, -1, 2048],
|
||||
0xBA: [False, -1, 2048],
|
||||
0xCA: [False, -1, 2048],
|
||||
0xDA: [False, -1, 2048],
|
||||
0xAC: [False, -1, 4096],
|
||||
0xBC: [False, -1, 4096],
|
||||
0xCC: [False, -1, 4096],
|
||||
0xA3: [False, -1, 8192],
|
||||
0xB3: [False, -1, 8192],
|
||||
0xC3: [False, -1, 8192],
|
||||
0xD3: [False, -1, 8192],
|
||||
0xA5: [False, -1, 16384],
|
||||
0xB5: [False, -1, 16384],
|
||||
0xC5: [False, -1, 16384],
|
||||
0xD5: [False, -1, 16384]
|
||||
}
|
||||
|
||||
|
||||
class SettingsOpt:
|
||||
def __init__(self, parent, chipset):
|
||||
self.PAGESIZE = 4096
|
||||
self.parent = parent
|
||||
self.bad_loader = 0
|
||||
self.sectors_per_page = 0
|
||||
self.sectorsize = 512
|
||||
self.flash_mfr = ""
|
||||
self.flash_descr = ""
|
||||
self.IsWideFlash = 0
|
||||
self.badsector = 0
|
||||
self.badflag = 0
|
||||
self.badposition = 0
|
||||
self.badplace = 0
|
||||
self.bch_mode = 0
|
||||
self.ecc_size = 0
|
||||
self.ecc_bit = 0
|
||||
self.num_pages_per_blk = 64
|
||||
self.udflag = 0
|
||||
self.sahara = 1
|
||||
self.args_disable_ecc = 0 # 0=enable, 1=disable
|
||||
self.bad_processing_flag = BadFlags.BAD_SKIP.value
|
||||
self.cwsize = 0
|
||||
self.rflag = 0
|
||||
self.cfg1_enable_bch_ecc = 0
|
||||
self.bad_loader = 0
|
||||
self.OOBSIZE = 0
|
||||
self.MAXBLOCK = 0
|
||||
self.UD_SIZE_BYTES = 516
|
||||
self.BAD_BLOCK_BYTE_NUM = 0
|
||||
self.BAD_BLOCK_IN_SPARE_AREA = 0
|
||||
self.ECC_MODE = 0
|
||||
self.bad_loader = 1
|
||||
self.secureboot = secureboottbl["MDM9607"]
|
||||
self.pbl = secgen[5][0]
|
||||
self.qfprom = secgen[5][1]
|
||||
self.memtbl = secgen[5][2]
|
||||
self.chipname = "Unknown"
|
||||
if chipset in config_tbl:
|
||||
self.chipname, self.bam, self.nandbase, self.bcraddr, self.secureboot, self.pbl, \
|
||||
self.qfprom, self.memtbl = config_tbl[chipset]
|
||||
self.bad_loader = 0
|
||||
else:
|
||||
loadername = parent.sahara.programmer.lower()
|
||||
for chipid in config_tbl:
|
||||
if config_tbl[chipid][0] in loadername:
|
||||
self.chipname, self.bam, self.nandbase, self.bcraddr, self.secureboot, \
|
||||
self.pbl, self.qfprom, self.memtbl = config_tbl[chipid]
|
||||
self.bad_loader = 0
|
||||
if chipset == 0xFF:
|
||||
self.bad_loader = 0
|
||||
|
||||
|
||||
class nand_toshiba_ids(ctypes.LittleEndianStructure):
|
||||
_fields_ = [
|
||||
("mid", c_uint8, 8),
|
||||
("did", c_uint8, 8),
|
||||
("icn", c_uint8, 2),
|
||||
("bpc", c_uint8, 2),
|
||||
("rsvd0", c_uint8, 4),
|
||||
("page_size", c_uint8, 2),
|
||||
("spare_size", c_uint8, 2),
|
||||
("block_size", c_uint8, 2),
|
||||
("org", c_uint8, 1),
|
||||
("rsvd1", c_uint8, 1),
|
||||
("rsvd2", c_uint8, 8),
|
||||
]
|
||||
|
||||
|
||||
class nand_toshiba_id_t(ctypes.Union):
|
||||
_anonymous_ = ("bit",)
|
||||
_fields_ = [
|
||||
("bit", nand_toshiba_ids),
|
||||
("asDword", ctypes.c_uint32)
|
||||
]
|
||||
|
||||
|
||||
class nand_samsung_ids(ctypes.LittleEndianStructure):
|
||||
_fields_ = [
|
||||
("mid", c_uint8, 8),
|
||||
("did", c_uint8, 8),
|
||||
("icn", c_uint8, 2),
|
||||
("bpc", c_uint8, 2),
|
||||
("nspp", c_uint8, 2),
|
||||
("ip", c_uint8, 1),
|
||||
("cp", c_uint8, 1),
|
||||
("page_size", c_uint8, 2),
|
||||
("spare_size", c_uint8, 2),
|
||||
("sam0", c_uint8, 1),
|
||||
("block_size", c_uint8, 2),
|
||||
("org", c_uint8, 1),
|
||||
("sam1", c_uint8, 1),
|
||||
("ecc", c_uint8, 2),
|
||||
("plane", c_uint8, 2),
|
||||
("plane_size", c_uint8, 3),
|
||||
("rsvd", c_uint8, 1),
|
||||
]
|
||||
|
||||
|
||||
class nand_samsung_id_t(ctypes.Union):
|
||||
_anonymous_ = ("bit",)
|
||||
_fields_ = [
|
||||
("bit", nand_toshiba_ids),
|
||||
("asDword", ctypes.c_uint32)
|
||||
]
|
||||
|
||||
|
||||
class NandDevice:
|
||||
# NAND_DEVn_CFG0 bits
|
||||
DISABLE_STATUS_AFTER_WRITE = 4
|
||||
CW_PER_PAGE = 6
|
||||
UD_SIZE_BYTES = 9
|
||||
ECC_PARITY_SIZE_BYTES_RS = 19
|
||||
SPARE_SIZE_BYTES = 23
|
||||
NUM_ADDR_CYCLES = 27
|
||||
STATUS_BFR_READ = 30
|
||||
SET_RD_MODE_AFTER_STATUS = 31
|
||||
|
||||
# NAND_DEV1_CFG0 bits
|
||||
DEV0_CFG1_ECC_DISABLE = 0
|
||||
WIDE_FLASH = 1
|
||||
NAND_RECOVERY_CYCLES = 2
|
||||
CS_ACTIVE_BSY = 5
|
||||
BAD_BLOCK_BYTE_NUM = 6
|
||||
BAD_BLOCK_IN_SPARE_AREA = 16
|
||||
WR_RD_BSY_GAP = 17
|
||||
ENABLE_BCH_ECC = 27
|
||||
ECC_ENCODER_CGC_EN = 23
|
||||
ECC_DECODER_CGC_EN = 24
|
||||
DISABLE_ECC_RESET_AFTER_OPDONE = 25
|
||||
ENABLE_NEW_ECC = 27
|
||||
ECC_MODE_DEV1 = 28 # 28:29
|
||||
|
||||
# NAND_DEV0_ECC_CFG bits
|
||||
ECC_CFG_ECC_DISABLE = 0
|
||||
ECC_SW_RESET = 1
|
||||
ECC_MODE = 4
|
||||
ECC_PARITY_SIZE_BYTES_BCH = 8
|
||||
ECC_NUM_DATA_BYTES = 16
|
||||
ECC_ENC_CLK_SHUTDOWN = 28
|
||||
ECC_DEC_CLK_SHUTDOWN = 29
|
||||
ECC_FORCE_CLK_OPEN = 30
|
||||
|
||||
# NAND_DEV_CMD1 bits
|
||||
READ_ADDR = 0
|
||||
|
||||
# NAND_DEV_CMD_VLD bits
|
||||
READ_START_VLD = 0
|
||||
READ_STOP_VLD = 1
|
||||
WRITE_START_VLD = 2
|
||||
ERASE_START_VLD = 3
|
||||
SEQ_READ_START_VLD = 4
|
||||
|
||||
NAND_CMD_PARAM = 0xec
|
||||
ECC_BCH_4BIT = 2
|
||||
|
||||
def __init__(self, settings):
|
||||
self.settings = settings
|
||||
# device commands
|
||||
self.NAND_CMD_SOFT_RESET = 0x01
|
||||
self.NAND_CMD_PAGE_READ = 0x32
|
||||
self.NAND_CMD_PAGE_READ_ECC = 0x33
|
||||
self.NAND_CMD_PAGE_READ_ALL = 0x34
|
||||
self.NAND_CMD_SEQ_PAGE_READ = 0x15
|
||||
self.NAND_CMD_PRG_PAGE = 0x36
|
||||
self.NAND_CMD_PRG_PAGE_ECC = 0x37
|
||||
self.NAND_CMD_PRG_PAGE_ALL = 0x39
|
||||
self.NAND_CMD_BLOCK_ERASE = 0x3A
|
||||
self.NAND_CMD_FETCH_ID = 0x0B
|
||||
self.NAND_CMD_STATUS = 0x0C
|
||||
self.NAND_CMD_RESET = 0x0D
|
||||
|
||||
# addr offsets
|
||||
self.NAND_FLASH_CMD = settings.nandbase + 0
|
||||
self.NAND_ADDR0 = settings.nandbase + 4
|
||||
self.NAND_ADDR1 = settings.nandbase + 8
|
||||
self.NAND_FLASH_CHIP_SELECT = settings.nandbase + 0xc
|
||||
self.NAND_EXEC_CMD = settings.nandbase + 0x10
|
||||
self.NAND_FLASH_STATUS = settings.nandbase + 0x14
|
||||
self.NAND_BUFFER_STATUS = settings.nandbase + 0x18
|
||||
self.NAND_DEV0_CFG0 = settings.nandbase + 0x20
|
||||
self.NAND_DEV0_CFG1 = settings.nandbase + 0x24
|
||||
self.NAND_DEV0_ECC_CFG = settings.nandbase + 0x28
|
||||
self.NAND_DEV1_ECC_CFG = settings.nandbase + 0x2C
|
||||
self.NAND_DEV1_CFG0 = settings.nandbase + 0x30
|
||||
self.NAND_DEV1_CFG1 = settings.nandbase + 0x34
|
||||
self.NAND_SFLASHC_CMD = settings.nandbase + 0x38
|
||||
self.NAND_SFLASHC_EXEC = settings.nandbase + 0x3C
|
||||
self.NAND_READ_ID = settings.nandbase + 0x40
|
||||
self.NAND_READ_STATUS = settings.nandbase + 0x44
|
||||
self.NAND_CONFIG_DATA = settings.nandbase + 0x50
|
||||
self.NAND_CONFIG = settings.nandbase + 0x54
|
||||
self.NAND_CONFIG_MODE = settings.nandbase + 0x58
|
||||
self.NAND_CONFIG_STATUS = settings.nandbase + 0x60
|
||||
self.NAND_DEV_CMD0 = settings.nandbase + 0xA0
|
||||
self.NAND_DEV_CMD1 = settings.nandbase + 0xA4
|
||||
self.NAND_DEV_CMD2 = settings.nandbase + 0xA8
|
||||
self.NAND_DEV_CMD_VLD = settings.nandbase + 0xAC
|
||||
self.SFLASHC_BURST_CFG = settings.nandbase + 0xE0
|
||||
self.NAND_EBI2_ECC_BUF_CFG = settings.nandbase + 0xF0
|
||||
self.NAND_HW_INFO = settings.nandbase + 0xFC
|
||||
self.NAND_FLASH_BUFFER = settings.nandbase + 0x100
|
||||
|
||||
self.PAGE_ACC = 1 << 4
|
||||
self.LAST_PAGE = 1 << 5
|
||||
self.CW_PER_PAGE = 6
|
||||
self.ECC_CFG_ECC_DISABLE = 0
|
||||
self.flashinfo = None
|
||||
|
||||
def gettbl(self, nandid, tbl):
|
||||
flashinfo = {}
|
||||
tid = nand_toshiba_id_t()
|
||||
tid.asDword = nandid
|
||||
# did,slc_small_device,device_width,density_mbits
|
||||
if tid.did in tbl:
|
||||
fdev = tbl[tid.did]
|
||||
small_slc = fdev[0]
|
||||
slc_device_width = fdev[1]
|
||||
density_mbits = fdev[2]
|
||||
else:
|
||||
return None
|
||||
|
||||
if small_slc:
|
||||
flashinfo["page_size"] = 512
|
||||
flashinfo["feature_flags1_ecc"] = 2
|
||||
flashinfo["block_size_kbytes"] = 16
|
||||
flashinfo["param_per_block"] = 32
|
||||
flashinfo["spare_size"] = 16
|
||||
flashinfo["otp_sequence_cfg"] = "FLASH_NAND_OTP_SEQUENCE_CFG6"
|
||||
flashinfo["dev_width"] = slc_device_width
|
||||
else:
|
||||
if tid.org == 0:
|
||||
flashinfo["dev_width"] = 8
|
||||
else:
|
||||
flashinfo["dev_width"] = 16
|
||||
if tid.spare_size == 0 or tid.spare_size == 1:
|
||||
flashinfo["feature_flags1_ecc"] = 1
|
||||
elif tid.spare_size == 2:
|
||||
flashinfo["feature_flags1_ecc"] = 8
|
||||
else:
|
||||
flashinfo["feature_flags1_ecc"] = 0
|
||||
flashinfo["page_size"] = 1024 << tid.page_size
|
||||
flashinfo["page_size_kb"] = flashinfo["page_size"] >> 10
|
||||
flashinfo["block_size_kb"] = 64 << tid.block_size
|
||||
flashinfo["pages_per_block"] = flashinfo["block_size_kb"] // flashinfo["page_size_kb"]
|
||||
flashinfo["spare_size"] = (8 << tid.spare_size) * (flashinfo["page_size"] // 512)
|
||||
if flashinfo["page_size"] == 2048 or flashinfo["page_size"] == 4096:
|
||||
flashinfo["otp_sequence_cfg"] = "FLASH_NAND_OTP_SEQUENCE_CFG2"
|
||||
else:
|
||||
flashinfo["otp_sequence_cfg"] = "FLASH_NAND_OTP_SEQUENCE_UNKNOWN"
|
||||
flashinfo["block_count"] = (1024 // 8 * density_mbits) // flashinfo["block_size_kb"]
|
||||
if flashinfo["page_size"] == 2048 and flashinfo["feature_flags1_ecc"] > 0:
|
||||
flashinfo["bad_block_info_byte_offset"] = 2048
|
||||
flashinfo["udata_max"] = 16
|
||||
flashinfo["max_corrected_udata_bytes"] = 16
|
||||
flashinfo["bad_block_info_byte_length"] = 1 if flashinfo["dev_width"] == 8 else 2
|
||||
elif flashinfo["page_size"] == 4096 and flashinfo["feature_flags1_ecc"] > 0:
|
||||
flashinfo["bad_block_info_byte_offset"] = 4096
|
||||
flashinfo["udata_max"] = 32
|
||||
flashinfo["max_corrected_udata_bytes"] = 32
|
||||
flashinfo["bad_block_info_byte_length"] = 1 if flashinfo["dev_width"] == 8 else 2
|
||||
self.settings.PAGESIZE = flashinfo["page_size"]
|
||||
self.settings.BLOCKSIZE = flashinfo["block_size_kb"] * 1024
|
||||
if flashinfo["dev_width"] == 8:
|
||||
self.settings.IsWideFlash = 0
|
||||
else:
|
||||
self.settings.IsWideFlash = 1
|
||||
self.settings.MAXBLOCK = flashinfo["block_count"]
|
||||
|
||||
self.settings.BAD_BLOCK_IN_SPARE_AREA = flashinfo["bad_block_info_byte_offset"]
|
||||
return flashinfo
|
||||
|
||||
def toshiba_config(self, nandid):
|
||||
flashinfo = self.gettbl(nandid, toshiba_tbl)
|
||||
self.flashinfo = flashinfo
|
||||
if (nandid >> 8) & 0xFF == 0xac:
|
||||
self.settings.OOBSIZE = 256
|
||||
self.settings.PAGESIZE = 4096
|
||||
self.settings.MAXBLOCK = 2048
|
||||
# 8Bit_HW_ECC
|
||||
elif (nandid >> 8) & 0xFF == 0xaa:
|
||||
self.settings.OOBSIZE = 128
|
||||
self.settings.PAGESIZE = 2048
|
||||
self.settings.MAXBLOCK = 2048
|
||||
# 8Bit_HW_ECC
|
||||
elif (nandid >> 8) & 0xFF == 0xa1:
|
||||
self.settings.OOBSIZE = 128
|
||||
self.settings.PAGESIZE = 2048
|
||||
self.settings.MAXBLOCK = 1024
|
||||
# 8Bit_HW_ECC
|
||||
|
||||
self.settings.CW_PER_PAGE = (self.settings.PAGESIZE >> 9) - 1
|
||||
self.settings.SPARE_SIZE_BYTES = 0
|
||||
|
||||
def samsung_config(self, nandid):
|
||||
flashinfo = self.gettbl(nandid, samsung_tbl)
|
||||
self.flashinfo = flashinfo
|
||||
|
||||
# self.settings.SPARE_SIZE_BYTES = flashinfo["spare_size"]
|
||||
self.settings.CW_PER_PAGE = (self.settings.PAGESIZE >> 9) - 1
|
||||
self.settings.SPARE_SIZE_BYTES = 0
|
||||
|
||||
def generic_config(self, nandid, chipsize):
|
||||
devcfg = (nandid >> 24) & 0xff
|
||||
self.settings.PAGESIZE = 1024 << (devcfg & 0x3)
|
||||
self.settings.BLOCKSIZE = 64 << ((devcfg >> 4) & 0x3)
|
||||
|
||||
if chipsize != 0:
|
||||
self.settings.MAXBLOCK = chipsize * 1024 // self.settings.BLOCKSIZE
|
||||
else:
|
||||
self.settings.MAXBLOCK = 0x800
|
||||
self.settings.CW_PER_PAGE = (self.settings.PAGESIZE >> 9) - 1
|
||||
|
||||
def nand_setup(self, nandid):
|
||||
"""
|
||||
qcommand -p%qdl% -k11 -c "m 79b0020 295409c0" #NAND_DEV0_CFG0
|
||||
qcommand -p%qdl% -k11 -c "m 79b0024 08065d5d" #NAND_DEV0_CFG1
|
||||
qcommand -p%qdl% -k11 -c "m 79b0028 42040d10" #NAND_DEV0_ECC_CFG
|
||||
qcommand -p%qdl% -k11 -c "m 79b00f0 00000203" NAND_EBI2_ECC_BUF_CFG
|
||||
"""
|
||||
|
||||
fid = (nandid >> 8) & 0xff
|
||||
pid = nandid & 0xff
|
||||
|
||||
self.settings.flash_mfr = ""
|
||||
for info in nand_manuf_ids:
|
||||
if info[0] == pid:
|
||||
self.settings.flash_mfr = info[1]
|
||||
break
|
||||
|
||||
chipsize = 0
|
||||
for info in nand_ids:
|
||||
if info[1] == fid:
|
||||
chipsize = info[2]
|
||||
self.settings.flash_descr = info[0]
|
||||
break
|
||||
|
||||
self.settings.cfg1_enable_bch_ecc = 1
|
||||
self.settings.IsWideFlash = 0
|
||||
self.settings.SPARE_SIZE_BYTES = 0
|
||||
self.settings.OOBSIZE = 0
|
||||
self.settings.ECC_PARITY_SIZE_BYTES = 0
|
||||
self.settings.BAD_BLOCK_BYTE_NUM = 0
|
||||
self.settings.ecc_bit = 4
|
||||
|
||||
if pid == 0x98: # Toshiba
|
||||
self.toshiba_config(nandid)
|
||||
if nandid == 0x2690AC98:
|
||||
self.settings.ecc_bit = 8
|
||||
elif pid == 0xEC: # Samsung
|
||||
self.samsung_config(nandid)
|
||||
elif pid == 0x2C: # Micron
|
||||
self.generic_config(nandid, chipsize)
|
||||
# MT29AZ5A3CHHWD
|
||||
if nandid == 0x2690AC2C or nandid == 0x26D0A32C:
|
||||
self.settings.ecc_bit = 8
|
||||
elif pid == 0x01:
|
||||
self.generic_config(nandid, chipsize)
|
||||
if nandid == 0x1590AC01: # jsfc 4G
|
||||
self.settings.OOBSIZE = 128
|
||||
self.settings.PAGESIZE = 2048
|
||||
self.settings.SPARE_SIZE_BYTES = 4
|
||||
else:
|
||||
self.generic_config(nandid, chipsize)
|
||||
|
||||
if nandid in supported_flash:
|
||||
nd = supported_flash[nandid]
|
||||
# density = nd[]
|
||||
# width
|
||||
chipsize = nd[0] // 1024
|
||||
self.settings.IsWideFlash = nd[1]
|
||||
self.settings.PAGESIZE = nd[2]
|
||||
self.settings.BLOCKSIZE = nd[3]
|
||||
self.settings.OOBSIZE = nd[4]
|
||||
self.settings.IsOneNand = nd[5]
|
||||
|
||||
if chipsize != 0:
|
||||
self.settings.MAXBLOCK = chipsize * 1024 // self.settings.BLOCKSIZE
|
||||
else:
|
||||
self.settings.MAXBLOCK = 0x800
|
||||
|
||||
self.settings.sectorsize = 512
|
||||
self.settings.sectors_per_page = self.settings.PAGESIZE // self.settings.sectorsize
|
||||
|
||||
if self.settings.ecc_bit == 4:
|
||||
self.settings.ECC_MODE = 0 # 0=4 bit ECC error
|
||||
elif self.settings.ecc_bit == 8:
|
||||
self.settings.ECC_MODE = 1 # 1=8 bit ECC error
|
||||
elif self.settings.ecc_bit == 16:
|
||||
self.settings.ECC_MODE = 2 # 2=16 bit ECC error
|
||||
|
||||
if self.settings.ecc_size == 0:
|
||||
if self.settings.ecc_bit == 4:
|
||||
self.settings.ecc_size = 1
|
||||
elif self.settings.ecc_bit == 8 or self.settings.ecc_bit == 16:
|
||||
self.settings.ecc_size = 2
|
||||
|
||||
if self.settings.OOBSIZE == 0:
|
||||
self.settings.OOBSIZE = (8 << self.settings.ecc_size) * (self.settings.CW_PER_PAGE + 1)
|
||||
|
||||
if 256 >= self.settings.OOBSIZE > 128:
|
||||
self.settings.OOBSIZE = 256
|
||||
|
||||
if self.settings.SPARE_SIZE_BYTES == 0:
|
||||
# HAM1
|
||||
if self.settings.ECC_MODE == 0:
|
||||
self.settings.SPARE_SIZE_BYTES = 4
|
||||
else:
|
||||
self.settings.SPARE_SIZE_BYTES = 2
|
||||
|
||||
if self.settings.cfg1_enable_bch_ecc:
|
||||
hw_ecc_bytes = 0
|
||||
self.settings.UD_SIZE_BYTES = self.settings.SPARE_SIZE_BYTES + self.settings.sectorsize # 516 or 517
|
||||
if self.settings.SPARE_SIZE_BYTES == 2:
|
||||
self.settings.UD_SIZE_BYTES += 2
|
||||
if self.settings.IsWideFlash:
|
||||
self.settings.UD_SIZE_BYTES += 1
|
||||
else:
|
||||
hw_ecc_bytes = 10
|
||||
self.settings.UD_SIZE_BYTES = 512
|
||||
|
||||
if self.settings.ECC_PARITY_SIZE_BYTES == 0:
|
||||
self.settings.ECC_PARITY_SIZE_BYTES = 3 # HAM1
|
||||
if self.settings.ecc_bit == 4: # BCH4
|
||||
self.settings.ECC_PARITY_SIZE_BYTES = 7
|
||||
elif self.settings.ecc_bit == 8: # BCH8
|
||||
self.settings.ECC_PARITY_SIZE_BYTES = 13
|
||||
elif self.settings.ecc_bit == 16: # BCH16
|
||||
self.settings.ECC_PARITY_SIZE_BYTES = 26
|
||||
|
||||
linuxcwsize = 528
|
||||
if self.settings.cfg1_enable_bch_ecc and self.settings.ecc_bit == 8:
|
||||
linuxcwsize = 532
|
||||
if nandid == 0x1590AC2C: # fixme
|
||||
linuxcwsize = 532
|
||||
if self.settings.BAD_BLOCK_BYTE_NUM == 0:
|
||||
self.settings.BAD_BLOCK_BYTE_NUM = (
|
||||
self.settings.PAGESIZE - (linuxcwsize * (self.settings.sectors_per_page - 1)) + 1)
|
||||
|
||||
# UD_SIZE_BYTES must be 512, 516 or 517. If ECC-Protection 516 for x16Bit-Nand and 517 for x8-bit Nand
|
||||
cfg0 = 0 << self.SET_RD_MODE_AFTER_STATUS \
|
||||
| 0 << self.STATUS_BFR_READ \
|
||||
| 5 << self.NUM_ADDR_CYCLES \
|
||||
| self.settings.SPARE_SIZE_BYTES << self.SPARE_SIZE_BYTES \
|
||||
| hw_ecc_bytes << self.ECC_PARITY_SIZE_BYTES_RS \
|
||||
| self.settings.UD_SIZE_BYTES << self.UD_SIZE_BYTES \
|
||||
| self.settings.CW_PER_PAGE << self.CW_PER_PAGE \
|
||||
| 0 << self.DISABLE_STATUS_AFTER_WRITE
|
||||
|
||||
bad_block_byte = self.settings.BAD_BLOCK_BYTE_NUM
|
||||
wide_bus = self.settings.IsWideFlash
|
||||
bch_disabled = self.settings.args_disable_ecc # option in gui, implemented
|
||||
|
||||
cfg1 = 0 << self.ECC_MODE_DEV1 \
|
||||
| 1 << self.ENABLE_NEW_ECC \
|
||||
| 0 << self.DISABLE_ECC_RESET_AFTER_OPDONE \
|
||||
| 0 << self.ECC_DECODER_CGC_EN \
|
||||
| 0 << self.ECC_ENCODER_CGC_EN \
|
||||
| 2 << self.WR_RD_BSY_GAP \
|
||||
| 0 << self.BAD_BLOCK_IN_SPARE_AREA \
|
||||
| bad_block_byte << self.BAD_BLOCK_BYTE_NUM \
|
||||
| 0 << self.CS_ACTIVE_BSY \
|
||||
| 7 << self.NAND_RECOVERY_CYCLES \
|
||||
| wide_bus << self.WIDE_FLASH \
|
||||
| bch_disabled << self.ENABLE_BCH_ECC
|
||||
|
||||
"""
|
||||
cfg0_raw = (self.settings.CW_PER_PAGE-1) << CW_PER_PAGE \
|
||||
| self.settings.UD_SIZE_BYTES << UD_SIZE_BYTES \
|
||||
| 5 << NUM_ADDR_CYCLES \
|
||||
| 0 << SPARE_SIZE_BYTES
|
||||
|
||||
cfg1_raw = 7 << NAND_RECOVERY_CYCLES \
|
||||
| 0 << CS_ACTIVE_BSY \
|
||||
| 17 << BAD_BLOCK_BYTE_NUM \
|
||||
| 1 << BAD_BLOCK_IN_SPARE_AREA \
|
||||
| 2 << WR_RD_BSY_GAP \
|
||||
| wide_bus << WIDE_FLASH \
|
||||
| 1 << DEV0_CFG1_ECC_DISABLE
|
||||
"""
|
||||
ecc_bch_cfg = 1 << self.ECC_FORCE_CLK_OPEN \
|
||||
| 0 << self.ECC_DEC_CLK_SHUTDOWN \
|
||||
| 0 << self.ECC_ENC_CLK_SHUTDOWN \
|
||||
| self.settings.UD_SIZE_BYTES << self.ECC_NUM_DATA_BYTES \
|
||||
| self.settings.ECC_PARITY_SIZE_BYTES << self.ECC_PARITY_SIZE_BYTES_BCH \
|
||||
| self.settings.ECC_MODE << self.ECC_MODE \
|
||||
| 0 << self.ECC_SW_RESET \
|
||||
| bch_disabled << self.ECC_CFG_ECC_DISABLE
|
||||
|
||||
if self.settings.UD_SIZE_BYTES == 516:
|
||||
ecc_buf_cfg = 0x203
|
||||
elif self.settings.UD_SIZE_BYTES == 517:
|
||||
ecc_buf_cfg = 0x204
|
||||
else:
|
||||
ecc_buf_cfg = 0x1FF
|
||||
|
||||
return cfg0, cfg1, ecc_buf_cfg, ecc_bch_cfg
|
||||
|
||||
|
||||
class nandregs:
|
||||
def __init__(self, parent):
|
||||
self.register_mapping = {
|
||||
}
|
||||
self.reverse_mapping = {}
|
||||
self.create_reverse_mapping()
|
||||
self.parent = parent
|
||||
|
||||
def __getattribute__(self, name):
|
||||
if name in ("register_mapping", "parent"):
|
||||
return super(nandregs, self).__getattribute__(name)
|
||||
|
||||
if name in self.register_mapping:
|
||||
return self.parent.mempeek(self.register_mapping[name])
|
||||
|
||||
return super(nandregs, self).__getattribute__(name)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if name in ("register_mapping", "parent"):
|
||||
super(nandregs, self).__setattr__(name, value)
|
||||
|
||||
if name in self.register_mapping:
|
||||
self.parent.mempoke(self.register_mapping[name], value)
|
||||
else:
|
||||
super(nandregs, self).__setattr__(name, value)
|
||||
|
||||
def read(self, register):
|
||||
if isinstance(register, str):
|
||||
register = self.register_mapping.get(register.lower(), None)
|
||||
return self.parent.mempeek(register)
|
||||
|
||||
def write(self, register, value):
|
||||
if isinstance(register, str):
|
||||
register = self.register_mapping.get(register.lower(), None)
|
||||
return self.parent.mempoke(register, value)
|
||||
|
||||
def save(self):
|
||||
reg_dict = {}
|
||||
for reg in self.register_mapping:
|
||||
reg_v = self.read(reg)
|
||||
reg_dict[reg] = reg_v
|
||||
return reg_dict
|
||||
|
||||
def restore(self, value=None):
|
||||
if value is None:
|
||||
value = {}
|
||||
for reg in self.register_mapping:
|
||||
reg_v = value[reg]
|
||||
self.write(reg, reg_v)
|
||||
|
||||
def create_reverse_mapping(self):
|
||||
self.reverse_mapping = {v: k for k, v in self.register_mapping.items()}
|
171
edl/Library/pt.py
Executable file
171
edl/Library/pt.py
Executable file
|
@ -0,0 +1,171 @@
|
|||
import struct
|
||||
|
||||
|
||||
def get_n(x):
|
||||
return int(x[6:8] + x[4:6] + x[2:4] + x[0:2], 16)
|
||||
|
||||
|
||||
def parse_pt(data):
|
||||
va = 0
|
||||
entries = []
|
||||
while va < len(data):
|
||||
entry = struct.unpack("<L", data[va:va + 4])[0]
|
||||
f = get_fld(entry)
|
||||
|
||||
if f is None:
|
||||
va += 4
|
||||
continue
|
||||
|
||||
entries.append((int(va / 4) << 20, f))
|
||||
print("%08x %s" % ((int(va / 4) << 20), str(f)))
|
||||
va += 4
|
||||
|
||||
return entries
|
||||
|
||||
|
||||
def parse_spt(data, base):
|
||||
va = 0
|
||||
while va < 0x400:
|
||||
entry = struct.unpack("<L", data[va:va + 4])[0]
|
||||
|
||||
f = get_sld(entry)
|
||||
if f != 'UNSUPPORTED' and f.apx == 0 and f.ap == 3 and f.nx == 0:
|
||||
print("%08x %s - WX !!" % (base + (int(va / 4) << 12), f))
|
||||
else:
|
||||
print("%08x %s" % (base + (int(va / 4) << 12), f))
|
||||
va += 4
|
||||
|
||||
|
||||
def get_fld(mfld):
|
||||
s = mfld & 3
|
||||
if s == 0:
|
||||
return fault_desc(mfld)
|
||||
|
||||
if s == 1:
|
||||
return pt_desc(mfld)
|
||||
|
||||
if s == 2:
|
||||
return section_desc(mfld)
|
||||
|
||||
if s == 3:
|
||||
return reserved_desc(mfld)
|
||||
return None
|
||||
|
||||
|
||||
def get_sld(msld):
|
||||
s = msld & 3
|
||||
if s == 1:
|
||||
return sld_lp(msld)
|
||||
|
||||
if s > 1:
|
||||
return sld_xsp(msld)
|
||||
|
||||
return "UNSUPPORTED"
|
||||
|
||||
|
||||
class descriptor(object):
|
||||
def __init__(self, mfld):
|
||||
pass
|
||||
|
||||
def get_name(self):
|
||||
pass
|
||||
|
||||
def __repr__(self):
|
||||
s = "%8s " % self.get_name()
|
||||
for attr, value in self.__dict__.items():
|
||||
try:
|
||||
s += "%s=%s, " % (attr, hex(value))
|
||||
except:
|
||||
s += "%s=%s, " % (attr, value)
|
||||
|
||||
return s
|
||||
|
||||
|
||||
class fld(descriptor):
|
||||
pass
|
||||
|
||||
|
||||
class fault_desc(fld):
|
||||
|
||||
def get_name(self):
|
||||
return "FAULT"
|
||||
|
||||
|
||||
class reserved_desc(fld):
|
||||
|
||||
def get_name(self):
|
||||
return "RESERVED"
|
||||
|
||||
|
||||
class pt_desc(fld):
|
||||
|
||||
def __init__(self, desc):
|
||||
self.coarse_base = (desc >> 10) << 10
|
||||
self.p = (desc >> 9) & 1
|
||||
self.domain = (desc >> 5) & 15
|
||||
self.sbz1 = (desc >> 4) & 1
|
||||
self.ns = (desc >> 3) & 1
|
||||
self.sbz2 = (desc >> 2) & 1
|
||||
|
||||
def get_name(self):
|
||||
return "PT"
|
||||
|
||||
|
||||
class section_desc(fld):
|
||||
def __init__(self, desc):
|
||||
self.section_base = (desc >> 20) << 20
|
||||
self.ns = (desc >> 19) & 1
|
||||
self.zero = ns = (desc >> 18) & 1
|
||||
self.ng = (desc >> 17) & 1
|
||||
self.s = (desc >> 16) & 1
|
||||
self.apx = (desc >> 15) & 1
|
||||
self.tex = (desc >> 12) & 7
|
||||
self.ap = (desc >> 10) & 3
|
||||
self.p = (desc >> 9) & 1
|
||||
self.domain = (desc >> 5) & 15
|
||||
self.nx = (desc >> 4) & 1
|
||||
self.c = (desc >> 3) & 1
|
||||
self.b = (desc >> 2) & 1
|
||||
|
||||
def get_name(self):
|
||||
return "SECTION"
|
||||
|
||||
|
||||
class sld(descriptor):
|
||||
pass
|
||||
|
||||
|
||||
class sld_lp(sld):
|
||||
|
||||
def __init__(self, desc):
|
||||
self.page_base = (desc >> 16) << 16
|
||||
self.nx = (desc >> 15) & 1
|
||||
self.tex = (desc >> 12) & 7
|
||||
self.ng = (desc >> 11) & 1
|
||||
self.s = (desc >> 10) & 1
|
||||
self.apx = (desc >> 9) & 1
|
||||
self.sbz = (desc >> 6) & 7
|
||||
self.ap = (desc >> 4) & 3
|
||||
self.c = (desc >> 3) & 1
|
||||
self.b = (desc >> 2) & 1
|
||||
|
||||
def get_name(self):
|
||||
return "LARGEPAGE"
|
||||
|
||||
|
||||
class sld_xsp(sld):
|
||||
|
||||
def __init__(self, desc):
|
||||
self.desc = desc
|
||||
self.page_base = (desc >> 12) << 12
|
||||
self.ng = (desc >> 11) & 1
|
||||
self.s = (desc >> 10) & 1
|
||||
self.apx = (desc >> 9) & 1
|
||||
self.tex = (desc >> 6) & 7
|
||||
self.ap = (desc >> 4) & 3
|
||||
self.c = (desc >> 3) & 1
|
||||
self.b = (desc >> 2) & 1
|
||||
self.nx = desc & 1
|
||||
|
||||
def get_name(self):
|
||||
return "XSMALLPAGE"
|
153
edl/Library/pt64.py
Executable file
153
edl/Library/pt64.py
Executable file
|
@ -0,0 +1,153 @@
|
|||
import struct
|
||||
|
||||
"""
|
||||
only supports 4KB granule w/ 25<=TnSZ<=33
|
||||
https://armv8-ref.codingbelief.com/en/chapter_d4/d42_7_the_algorithm_for_finding_the_translation_table_entries.html
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def get_level_index(va, level):
|
||||
if level == 1:
|
||||
return (va >> 30) & 0x3F
|
||||
|
||||
if level == 2:
|
||||
return (va >> 21) & 0x1FF
|
||||
|
||||
if level == 3:
|
||||
return (va >> 12) & 0x1FF
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def get_level_bits(level, tnsz):
|
||||
if level == 1:
|
||||
return 37 - tnsz + 26 + 1 - 30
|
||||
|
||||
if level == 2:
|
||||
return 9
|
||||
|
||||
if level == 3:
|
||||
return 9
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def get_level_size(tnsz, level):
|
||||
return 2 ** get_level_bits(level, tnsz) * 8
|
||||
|
||||
|
||||
def get_va_for_level(va, index, level):
|
||||
if level == 1:
|
||||
return va + (index << 30)
|
||||
|
||||
if level == 2:
|
||||
return va + (index << 21)
|
||||
|
||||
if level == 3:
|
||||
return va + (index << 12)
|
||||
|
||||
return va
|
||||
|
||||
|
||||
def parse_pt(data, base, tnsz, level=1):
|
||||
i = 0
|
||||
entries = []
|
||||
while i < min(len(data), get_level_size(tnsz, level)):
|
||||
mentry = struct.unpack("<Q", data[i:i + 8])[0]
|
||||
|
||||
f = get_fld(mentry, level)
|
||||
if f is None:
|
||||
i += 8
|
||||
continue
|
||||
va = get_va_for_level(base, int(i / 8), level)
|
||||
if f != 'UNSUPPORTED' and f.apx == 0 and f.ap == 3 and f.xn == 0:
|
||||
print("%016x %s - WX !!" % (va, f))
|
||||
else:
|
||||
print("%016x %s" % (va, f))
|
||||
|
||||
entries.append((va, f))
|
||||
i += 8
|
||||
|
||||
return entries
|
||||
|
||||
|
||||
def get_fld(mfld, level):
|
||||
s = mfld & 3
|
||||
if s == 0:
|
||||
return None
|
||||
|
||||
if s == 1:
|
||||
return block_entry4k(mfld, level)
|
||||
|
||||
if s == 2:
|
||||
return None
|
||||
|
||||
if s == 3:
|
||||
return table_entry4k(mfld, level)
|
||||
return None
|
||||
|
||||
class descriptor(object):
|
||||
def get_name(self):
|
||||
pass
|
||||
|
||||
def __repr__(self):
|
||||
s = "%8s " % self.get_name()
|
||||
for attr, value in self.__dict__.items():
|
||||
try:
|
||||
s += "%s=%s, " % (attr, hex(value))
|
||||
except:
|
||||
s += "%s=%s, " % (attr, value)
|
||||
|
||||
return s
|
||||
|
||||
|
||||
class fld(descriptor):
|
||||
pass
|
||||
|
||||
|
||||
class entry(fld):
|
||||
def __init__(self, desc, level):
|
||||
self.level = level
|
||||
self.nshigh = desc >> 63
|
||||
self.apx = (desc >> 61) & 3
|
||||
self.xn = (desc >> 60) & 1
|
||||
self.pxn = (desc >> 59) & 1
|
||||
self.attrindex = (desc >> 2) & 7
|
||||
self.ns = (desc >> 5) & 1
|
||||
self.ap = (desc >> 6) & 3
|
||||
self.sh = (desc >> 8) & 3
|
||||
self.af = (desc >> 10) & 1
|
||||
self.nG = (desc >> 11) & 1
|
||||
|
||||
|
||||
class entry4k(entry):
|
||||
def __init__(self, desc, level):
|
||||
entry.__init__(self, desc, level)
|
||||
self.output = ((desc & 0xFFFFFFFFFFFF) >> 12) << 12
|
||||
|
||||
|
||||
class fault_entry(fld):
|
||||
|
||||
def get_name(self):
|
||||
return "FAULT"
|
||||
|
||||
|
||||
class block_entry4k(entry4k):
|
||||
|
||||
def __init__(self, desc, level):
|
||||
entry4k.__init__(self, desc, level)
|
||||
# shift = 39-9*level
|
||||
# self.output = ((desc & 0xFFFFFFFFFFFFL) >> shift) << shift
|
||||
|
||||
def get_name(self):
|
||||
return "BLOCK4"
|
||||
|
||||
|
||||
class table_entry4k(entry4k):
|
||||
|
||||
def __init__(self, desc, level):
|
||||
entry4k.__init__(self, desc, level)
|
||||
|
||||
def get_name(self):
|
||||
return "TABLE4"
|
834
edl/Library/sahara.py
Executable file
834
edl/Library/sahara.py
Executable file
|
@ -0,0 +1,834 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) B.Kerler 2018-2021
|
||||
import binascii
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import inspect
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
def convertmsmid(msmid):
|
||||
msmiddb = []
|
||||
if int(msmid, 16) & 0xFF == 0xe1 or msmid == '00000000':
|
||||
return [msmid]
|
||||
socid = int(msmid, 16) >> 16
|
||||
if socid in sochw:
|
||||
names = sochw[socid].split(",")
|
||||
for name in names:
|
||||
for ids in msmids:
|
||||
if msmids[ids] == name:
|
||||
rmsmid = hex(ids)[2:].lower()
|
||||
while len(rmsmid) < 8:
|
||||
rmsmid = '0' + rmsmid
|
||||
msmiddb.append(rmsmid)
|
||||
return msmiddb
|
||||
|
||||
|
||||
class sahara(metaclass=LogBase):
|
||||
SAHARA_VERSION = 2
|
||||
SAHARA_MIN_VERSION = 1
|
||||
|
||||
class cmd:
|
||||
SAHARA_HELLO_REQ = 0x1
|
||||
SAHARA_HELLO_RSP = 0x2
|
||||
SAHARA_READ_DATA = 0x3
|
||||
SAHARA_END_TRANSFER = 0x4
|
||||
SAHARA_DONE_REQ = 0x5
|
||||
SAHARA_DONE_RSP = 0x6
|
||||
SAHARA_RESET_REQ = 0x7
|
||||
SAHARA_RESET_RSP = 0x8
|
||||
SAHARA_MEMORY_DEBUG = 0x9
|
||||
SAHARA_MEMORY_READ = 0xA
|
||||
SAHARA_CMD_READY = 0xB
|
||||
SAHARA_SWITCH_MODE = 0xC
|
||||
SAHARA_EXECUTE_REQ = 0xD
|
||||
SAHARA_EXECUTE_RSP = 0xE
|
||||
SAHARA_EXECUTE_DATA = 0xF
|
||||
SAHARA_64BIT_MEMORY_DEBUG = 0x10
|
||||
SAHARA_64BIT_MEMORY_READ = 0x11
|
||||
SAHARA_64BIT_MEMORY_READ_DATA = 0x12
|
||||
SAHARA_RESET_STATE_MACHINE_ID = 0x13
|
||||
|
||||
class exec_cmd:
|
||||
SAHARA_EXEC_CMD_NOP = 0x00
|
||||
SAHARA_EXEC_CMD_SERIAL_NUM_READ = 0x01
|
||||
SAHARA_EXEC_CMD_MSM_HW_ID_READ = 0x02
|
||||
SAHARA_EXEC_CMD_OEM_PK_HASH_READ = 0x03
|
||||
SAHARA_EXEC_CMD_SWITCH_TO_DMSS_DLOAD = 0x04
|
||||
SAHARA_EXEC_CMD_SWITCH_TO_STREAM_DLOAD = 0x05
|
||||
SAHARA_EXEC_CMD_READ_DEBUG_DATA = 0x06
|
||||
SAHARA_EXEC_CMD_GET_SOFTWARE_VERSION_SBL = 0x07
|
||||
|
||||
class sahara_mode:
|
||||
SAHARA_MODE_IMAGE_TX_PENDING = 0x0
|
||||
SAHARA_MODE_IMAGE_TX_COMPLETE = 0x1
|
||||
SAHARA_MODE_MEMORY_DEBUG = 0x2
|
||||
SAHARA_MODE_COMMAND = 0x3
|
||||
|
||||
class status:
|
||||
SAHARA_STATUS_SUCCESS = 0x00 # Invalid command received in current state
|
||||
SAHARA_NAK_INVALID_CMD = 0x01 # Protocol mismatch between host and target
|
||||
SAHARA_NAK_PROTOCOL_MISMATCH = 0x02 # Invalid target protocol version
|
||||
SAHARA_NAK_INVALID_TARGET_PROTOCOL = 0x03 # Invalid host protocol version
|
||||
SAHARA_NAK_INVALID_HOST_PROTOCOL = 0x04 # Invalid packet size received
|
||||
SAHARA_NAK_INVALID_PACKET_SIZE = 0x05 # Unexpected image ID received
|
||||
SAHARA_NAK_UNEXPECTED_IMAGE_ID = 0x06 # Invalid image header size received
|
||||
SAHARA_NAK_INVALID_HEADER_SIZE = 0x07 # Invalid image data size received
|
||||
SAHARA_NAK_INVALID_DATA_SIZE = 0x08 # Invalid image type received
|
||||
SAHARA_NAK_INVALID_IMAGE_TYPE = 0x09 # Invalid tranmission length
|
||||
SAHARA_NAK_INVALID_TX_LENGTH = 0x0A # Invalid reception length
|
||||
SAHARA_NAK_INVALID_RX_LENGTH = 0x0B # General transmission or reception error
|
||||
SAHARA_NAK_GENERAL_TX_RX_ERROR = 0x0C # Error while transmitting READ_DATA packet
|
||||
SAHARA_NAK_READ_DATA_ERROR = 0x0D # Cannot receive specified number of program headers
|
||||
SAHARA_NAK_UNSUPPORTED_NUM_PHDRS = 0x0E # Invalid data length received for program headers
|
||||
SAHARA_NAK_INVALID_PDHR_SIZE = 0x0F # Multiple shared segments found in ELF image
|
||||
SAHARA_NAK_MULTIPLE_SHARED_SEG = 0x10 # Uninitialized program header location
|
||||
SAHARA_NAK_UNINIT_PHDR_LOC = 0x11 # Invalid destination address
|
||||
SAHARA_NAK_INVALID_DEST_ADDR = 0x12 # Invalid data size received in image header
|
||||
SAHARA_NAK_INVALID_IMG_HDR_DATA_SIZE = 0x13 # Invalid ELF header received
|
||||
SAHARA_NAK_INVALID_ELF_HDR = 0x14 # Unknown host error received in HELLO_RESP
|
||||
SAHARA_NAK_UNKNOWN_HOST_ERROR = 0x15 # Timeout while receiving data
|
||||
SAHARA_NAK_TIMEOUT_RX = 0x16 # Timeout while transmitting data
|
||||
SAHARA_NAK_TIMEOUT_TX = 0x17 # Invalid mode received from host
|
||||
SAHARA_NAK_INVALID_HOST_MODE = 0x18 # Invalid memory read access
|
||||
SAHARA_NAK_INVALID_MEMORY_READ = 0x19 # Host cannot handle read data size requested
|
||||
SAHARA_NAK_INVALID_DATA_SIZE_REQUEST = 0x1A # Memory debug not supported
|
||||
SAHARA_NAK_MEMORY_DEBUG_NOT_SUPPORTED = 0x1B # Invalid mode switch
|
||||
SAHARA_NAK_INVALID_MODE_SWITCH = 0x1C # Failed to execute command
|
||||
SAHARA_NAK_CMD_EXEC_FAILURE = 0x1D # Invalid parameter passed to command execution
|
||||
SAHARA_NAK_EXEC_CMD_INVALID_PARAM = 0x1E # Unsupported client command received
|
||||
SAHARA_NAK_EXEC_CMD_UNSUPPORTED = 0x1F # Invalid client command received for data response
|
||||
SAHARA_NAK_EXEC_DATA_INVALID_CLIENT_CMD = 0x20 # Failed to authenticate hash table
|
||||
SAHARA_NAK_HASH_TABLE_AUTH_FAILURE = 0x21 # Failed to verify hash for a given segment of ELF image
|
||||
SAHARA_NAK_HASH_VERIFICATION_FAILURE = 0x22 # Failed to find hash table in ELF image
|
||||
SAHARA_NAK_HASH_TABLE_NOT_FOUND = 0x23 # Target failed to initialize
|
||||
SAHARA_NAK_TARGET_INIT_FAILURE = 0x24 # Failed to authenticate generic image
|
||||
SAHARA_NAK_IMAGE_AUTH_FAILURE = 0x25 # Invalid ELF hash table size. Too bit or small.
|
||||
SAHARA_NAK_INVALID_IMG_HASH_TABLE_SIZE = 0x26
|
||||
SAHARA_NAK_MAX_CODE = 0x7FFFFFFF # To ensure 32-bits wide */
|
||||
|
||||
ErrorDesc = {
|
||||
0x00: "Invalid command received in current state",
|
||||
0x01: "Protocol mismatch between host and target",
|
||||
0x02: "Invalid target protocol version",
|
||||
0x03: "Invalid host protocol version",
|
||||
0x04: "Invalid packet size received",
|
||||
0x05: "Unexpected image ID received",
|
||||
0x06: "Invalid image header size received",
|
||||
0x07: "Invalid image data size received",
|
||||
0x08: "Invalid image type received",
|
||||
0x09: "Invalid tranmission length",
|
||||
0x0A: "Invalid reception length",
|
||||
0x0B: "General transmission or reception error",
|
||||
0x0C: "Error while transmitting READ_DATA packet",
|
||||
0x0D: "Cannot receive specified number of program headers",
|
||||
0x0E: "Invalid data length received for program headers",
|
||||
0x0F: "Multiple shared segments found in ELF image",
|
||||
0x10: "Uninitialized program header location",
|
||||
0x11: "Invalid destination address",
|
||||
0x12: "Invalid data size received in image header",
|
||||
0x13: "Invalid ELF header received",
|
||||
0x14: "Unknown host error received in HELLO_RESP",
|
||||
0x15: "Timeout while receiving data",
|
||||
0x16: "Timeout while transmitting data",
|
||||
0x17: "Invalid mode received from host",
|
||||
0x18: "Invalid memory read access",
|
||||
0x19: "Host cannot handle read data size requested",
|
||||
0x1A: "Memory debug not supported",
|
||||
0x1B: "Invalid mode switch",
|
||||
0x1C: "Failed to execute command",
|
||||
0x1D: "Invalid parameter passed to command execution",
|
||||
0x1E: "Unsupported client command received",
|
||||
0x1F: "Invalid client command received for data response",
|
||||
0x20: "Failed to authenticate hash table",
|
||||
0x21: "Failed to verify hash for a given segment of ELF image",
|
||||
0x22: "Failed to find hash table in ELF image",
|
||||
0x23: "Target failed to initialize",
|
||||
0x24: "Failed to authenticate generic image",
|
||||
0x25: "Invalid ELF hash table size. Too bit or small.",
|
||||
0x26: "Invalid IMG Hash Table Size"
|
||||
}
|
||||
|
||||
def init_loader_db(self):
|
||||
loaderdb = {}
|
||||
for (dirpath, dirnames, filenames) in os.walk(os.path.join(parent_dir,"Loaders")):
|
||||
for filename in filenames:
|
||||
fn = 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()
|
||||
for msmid in convertmsmid(msmid):
|
||||
mhwid = msmid + devid
|
||||
mhwid = mhwid.lower()
|
||||
if mhwid not in loaderdb:
|
||||
loaderdb[mhwid] = {}
|
||||
if pkhash not in loaderdb[mhwid]:
|
||||
loaderdb[mhwid][pkhash] = fn
|
||||
else:
|
||||
loaderdb[mhwid][pkhash].append(fn)
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
self.debug(str(e))
|
||||
continue
|
||||
self.loaderdb = loaderdb
|
||||
return loaderdb
|
||||
|
||||
def get_error_desc(self, status):
|
||||
if status in self.ErrorDesc:
|
||||
return "Error: " + self.ErrorDesc[status]
|
||||
else:
|
||||
return "Unknown error"
|
||||
|
||||
pkt_hello_req = [
|
||||
('cmd', 'I'),
|
||||
('len', 'I'),
|
||||
('version', 'I'),
|
||||
('version_min', 'I'),
|
||||
('max_cmd_len', 'I'),
|
||||
('mode', 'I'),
|
||||
('res1', 'I'),
|
||||
('res2', 'I'),
|
||||
('res3', 'I'),
|
||||
('res4', 'I'),
|
||||
('res5', 'I'),
|
||||
('res6', 'I')]
|
||||
|
||||
pkt_cmd_hdr = [
|
||||
('cmd', 'I'),
|
||||
('len', 'I')
|
||||
]
|
||||
|
||||
pkt_read_data = [
|
||||
('id', 'I'),
|
||||
('data_offset', 'I'),
|
||||
('data_len', 'I')
|
||||
]
|
||||
|
||||
pkt_read_data_64 = [
|
||||
('id', 'Q'),
|
||||
('data_offset', 'Q'),
|
||||
('data_len', 'Q')
|
||||
]
|
||||
|
||||
pkt_memory_debug = [
|
||||
('memory_table_addr', 'I'),
|
||||
('memory_table_length', 'I')
|
||||
]
|
||||
|
||||
pkt_memory_debug_64 = [
|
||||
('memory_table_addr', 'Q'),
|
||||
('memory_table_length', 'Q')
|
||||
]
|
||||
'''
|
||||
execute_cmd=[
|
||||
('cmd', 'I'),
|
||||
('len', 'I'),
|
||||
('client_cmd','I')
|
||||
]
|
||||
'''
|
||||
|
||||
pkt_execute_rsp_cmd = [
|
||||
('cmd', 'I'),
|
||||
('len', 'I'),
|
||||
('client_cmd', 'I'),
|
||||
('data_len', 'I')
|
||||
]
|
||||
|
||||
pkt_image_end = [
|
||||
('id', 'I'),
|
||||
('status', 'I')
|
||||
]
|
||||
|
||||
pkt_done = [
|
||||
('cmd', 'I'),
|
||||
('len', 'I'),
|
||||
('status', 'I')
|
||||
]
|
||||
|
||||
pbl_info = [
|
||||
('serial', 'I'),
|
||||
('msm_id', 'I'),
|
||||
('pk_hash', '32s'),
|
||||
('pbl_sw', 'I')
|
||||
]
|
||||
|
||||
parttbl = [
|
||||
('save_pref', 'I'),
|
||||
('mem_base', 'I'),
|
||||
('length', 'I'),
|
||||
('desc', '20s'),
|
||||
('filename', '20s')
|
||||
]
|
||||
|
||||
parttbl_64bit = [
|
||||
('save_pref', 'Q'),
|
||||
('mem_base', 'Q'),
|
||||
('length', 'Q'),
|
||||
('desc', '20s'),
|
||||
('filename', '20s')
|
||||
]
|
||||
|
||||
def __init__(self, cdc, loglevel):
|
||||
self.cdc = cdc
|
||||
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.id = None
|
||||
self.loaderdb = None
|
||||
self.version = 2.1
|
||||
self.programmer = None
|
||||
self.mode = ""
|
||||
self.serial = None
|
||||
|
||||
self.serials = None
|
||||
self.sblversion = None
|
||||
self.hwid = None
|
||||
self.pkhash = None
|
||||
self.hwidstr = None
|
||||
self.msm_id = None
|
||||
self.oem_id = None
|
||||
self.model_id = None
|
||||
self.oem_str = None
|
||||
self.msm_str = None
|
||||
self.bit64 = False
|
||||
self.pktsize = None
|
||||
|
||||
self.init_loader_db()
|
||||
|
||||
self.__logger.setLevel(loglevel)
|
||||
if loglevel == logging.DEBUG:
|
||||
logfilename = "log.txt"
|
||||
fh = logging.FileHandler(logfilename)
|
||||
self.__logger.addHandler(fh)
|
||||
|
||||
def get_rsp(self):
|
||||
data = []
|
||||
try:
|
||||
v = self.cdc.read()
|
||||
if v == b'':
|
||||
return [None, None]
|
||||
if b"<?xml" in v:
|
||||
return ["firehose", None]
|
||||
pkt = read_object(v[0:0x2 * 0x4], self.pkt_cmd_hdr)
|
||||
if "cmd" in pkt:
|
||||
cmd = pkt["cmd"]
|
||||
if cmd == self.cmd.SAHARA_HELLO_REQ:
|
||||
data = read_object(v[0x0:0xC * 0x4], self.pkt_hello_req)
|
||||
elif cmd == self.cmd.SAHARA_DONE_RSP:
|
||||
data = read_object(v[0x0:0x3 * 4], self.pkt_done)
|
||||
elif cmd == self.cmd.SAHARA_END_TRANSFER:
|
||||
data = read_object(v[0x8:0x8 + 0x2 * 0x4], self.pkt_image_end)
|
||||
elif cmd == self.cmd.SAHARA_64BIT_MEMORY_READ_DATA:
|
||||
self.bit64 = True
|
||||
data = read_object(v[0x8:0x8 + 0x3 * 0x8], self.pkt_read_data_64)
|
||||
elif cmd == self.cmd.SAHARA_READ_DATA:
|
||||
self.bit64 = False
|
||||
data = read_object(v[0x8:0x8 + 0x3 * 0x4], self.pkt_read_data)
|
||||
elif cmd == self.cmd.SAHARA_64BIT_MEMORY_DEBUG:
|
||||
self.bit64 = True
|
||||
data = read_object(v[0x8:0x8 + 0x2 * 0x8], self.pkt_memory_debug_64)
|
||||
elif cmd == self.cmd.SAHARA_MEMORY_DEBUG:
|
||||
self.bit64 = False
|
||||
data = read_object(v[0x8:0x8 + 0x2 * 0x4], self.pkt_memory_debug)
|
||||
elif cmd == self.cmd.SAHARA_EXECUTE_RSP:
|
||||
data = read_object(v[0:0x4 * 0x4], self.pkt_execute_rsp_cmd)
|
||||
elif cmd == self.cmd.SAHARA_CMD_READY or cmd == self.cmd.SAHARA_RESET_RSP:
|
||||
data = []
|
||||
else:
|
||||
return [None,None]
|
||||
return [pkt, data]
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
self.error(str(e))
|
||||
return [None,None]
|
||||
|
||||
def cmd_hello(self, mode, version_min=1, max_cmd_len=0): # CMD 0x1, RSP 0x2
|
||||
cmd = self.cmd.SAHARA_HELLO_RSP
|
||||
length = 0x30
|
||||
version = self.SAHARA_VERSION
|
||||
responsedata = pack("<IIIIIIIIIIII", cmd, length, version, version_min, max_cmd_len, mode, 0, 0, 0, 0, 0, 0)
|
||||
try:
|
||||
self.cdc.write(responsedata)
|
||||
return True
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
self.error(str(e))
|
||||
return False
|
||||
|
||||
def connect(self):
|
||||
try:
|
||||
v = self.cdc.read()
|
||||
if len(v) > 1:
|
||||
if v[0] == 0x01:
|
||||
cmd = read_object(v[0:0x2 * 0x4], self.pkt_cmd_hdr)
|
||||
if cmd['cmd'] == self.cmd.SAHARA_HELLO_REQ:
|
||||
data = read_object(v[0x0:0xC * 0x4], self.pkt_hello_req)
|
||||
self.pktsize = data['max_cmd_len']
|
||||
self.version = float(str(data['version']) + "." + str(data['version_min']))
|
||||
return ["sahara", data]
|
||||
elif v[0] == self.cmd.SAHARA_END_TRANSFER:
|
||||
return ["sahara", None]
|
||||
elif b"<?xml" in v:
|
||||
return ["firehose", None]
|
||||
elif v[0] == 0x7E:
|
||||
return ["nandprg", None]
|
||||
else:
|
||||
data = b"<?xml version=\"1.0\" ?><data><nop /></data>"
|
||||
self.cdc.write(data)
|
||||
res = self.cdc.read()
|
||||
if res == b"":
|
||||
try:
|
||||
data = b"\x7E\x06\x4E\x95\x7E" # Streaming nop
|
||||
self.cdc.write(data)
|
||||
res = self.cdc.read()
|
||||
if b"\x7E\x0D\x16\x00\x00\x00\x00" in res or b"Invalid Command" in res:
|
||||
return ["nandprg", None]
|
||||
else:
|
||||
return ["", None]
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
self.error(str(e))
|
||||
return ["", None]
|
||||
if b"<?xml" in res:
|
||||
return ["firehose", None]
|
||||
elif len(res) > 0 and res[0] == self.cmd.SAHARA_END_TRANSFER:
|
||||
print("Device is in Sahara error state, please reboot the device.")
|
||||
return ["sahara", None]
|
||||
else:
|
||||
data = b"\x7E\x11\x00\x12\x00\xA0\xE3\x00\x00\xC1\xE5\x01\x40\xA0\xE3\x1E\xFF\x2F\xE1\x4B\xD9\x7E"
|
||||
self.cdc.write(data)
|
||||
res = self.cdc.read()
|
||||
if len(res) > 0 and res[1] == 0x12:
|
||||
return ["nandprg", None]
|
||||
else:
|
||||
self.cmd_modeswitch(self.sahara_mode.SAHARA_MODE_COMMAND)
|
||||
return ["sahara", None]
|
||||
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
self.error(str(e))
|
||||
|
||||
self.cmd_modeswitch(self.sahara_mode.SAHARA_MODE_MEMORY_DEBUG)
|
||||
cmd, pkt = self.get_rsp()
|
||||
if None in [cmd , pkt]:
|
||||
return ["", None]
|
||||
return ["sahara", pkt]
|
||||
|
||||
def enter_command_mode(self):
|
||||
if not self.cmd_hello(self.sahara_mode.SAHARA_MODE_COMMAND):
|
||||
return False
|
||||
cmd, pkt = self.get_rsp()
|
||||
if cmd["cmd"] == self.cmd.SAHARA_CMD_READY:
|
||||
return True
|
||||
elif "status" in pkt:
|
||||
self.error(self.get_error_desc(pkt["status"]))
|
||||
return False
|
||||
return False
|
||||
|
||||
def cmdexec_nop(self):
|
||||
res = self.cmd_exec(self.exec_cmd.SAHARA_EXEC_CMD_NOP)
|
||||
return res
|
||||
|
||||
def cmdexec_get_serial_num(self):
|
||||
res = self.cmd_exec(self.exec_cmd.SAHARA_EXEC_CMD_SERIAL_NUM_READ)
|
||||
return unpack("<I", res)[0]
|
||||
|
||||
def cmdexec_get_msm_hwid(self):
|
||||
res = self.cmd_exec(self.exec_cmd.SAHARA_EXEC_CMD_MSM_HW_ID_READ)
|
||||
try:
|
||||
return unpack("<Q", res[0:0x8])[0]
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
self.debug(str(e))
|
||||
return None
|
||||
|
||||
def cmdexec_get_pkhash(self):
|
||||
try:
|
||||
res = self.cmd_exec(self.exec_cmd.SAHARA_EXEC_CMD_OEM_PK_HASH_READ)[0:0x20]
|
||||
return binascii.hexlify(res).decode('utf-8')
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
self.error(str(e))
|
||||
return None
|
||||
|
||||
def cmdexec_get_sbl_version(self):
|
||||
res = self.cmd_exec(self.exec_cmd.SAHARA_EXEC_CMD_GET_SOFTWARE_VERSION_SBL)
|
||||
return unpack("<I", res)[0]
|
||||
|
||||
def cmdexec_switch_to_dmss_dload(self):
|
||||
res = self.cmd_exec(self.exec_cmd.SAHARA_EXEC_CMD_SWITCH_TO_DMSS_DLOAD)
|
||||
return res
|
||||
|
||||
def cmdexec_switch_to_stream_dload(self):
|
||||
res = self.cmd_exec(self.exec_cmd.SAHARA_EXEC_CMD_SWITCH_TO_STREAM_DLOAD)
|
||||
return res
|
||||
|
||||
def cmdexec_read_debug_data(self):
|
||||
res = self.cmd_exec(self.exec_cmd.SAHARA_EXEC_CMD_READ_DEBUG_DATA)
|
||||
return res
|
||||
|
||||
def cmd_info(self):
|
||||
if self.enter_command_mode():
|
||||
self.serial = self.cmdexec_get_serial_num()
|
||||
self.serials = "{:08x}".format(self.serial)
|
||||
self.hwid = self.cmdexec_get_msm_hwid()
|
||||
self.pkhash = self.cmdexec_get_pkhash()
|
||||
# if self.version>=2.4:
|
||||
# self.sblversion = "{:08x}".format(self.cmdexec_get_sbl_version())
|
||||
if self.hwid is not None:
|
||||
self.hwidstr = "{:016x}".format(self.hwid)
|
||||
self.msm_id = int(self.hwidstr[2:8], 16)
|
||||
self.oem_id = int(self.hwidstr[-8:-4], 16)
|
||||
self.model_id = int(self.hwidstr[-4:], 16)
|
||||
self.oem_str = "{:04x}".format(self.oem_id)
|
||||
self.model_id = "{:04x}".format(self.model_id)
|
||||
self.msm_str = "{:08x}".format(self.msm_id)
|
||||
if self.msm_id in msmids:
|
||||
cpustr = f"CPU detected: \"{msmids[self.msm_id]}\"\n"
|
||||
else:
|
||||
cpustr = "Unknown CPU, please send log as issue to https://github.com/bkerler/edl\n"
|
||||
"""
|
||||
if self.version >= 2.4:
|
||||
self.info(f"\n------------------------\n" +
|
||||
f"HWID: 0x{self.hwidstr} (MSM_ID:0x{self.msm_str}," +
|
||||
f"OEM_ID:0x{self.oem_str}," +
|
||||
f"MODEL_ID:0x{self.model_id})\n" +
|
||||
f"PK_HASH: 0x{self.pkhash}\n" +
|
||||
f"Serial: 0x{self.serials}\n" +
|
||||
f"SBL Version: 0x{self.sblversion}\n")
|
||||
else:
|
||||
"""
|
||||
self.info(f"\n------------------------\n" +
|
||||
f"HWID: 0x{self.hwidstr} (MSM_ID:0x{self.msm_str}," +
|
||||
f"OEM_ID:0x{self.oem_str}," +
|
||||
f"MODEL_ID:0x{self.model_id})\n" +
|
||||
cpustr +
|
||||
f"PK_HASH: 0x{self.pkhash}\n" +
|
||||
f"Serial: 0x{self.serials}\n")
|
||||
if self.programmer == "":
|
||||
if self.hwidstr in self.loaderdb:
|
||||
mt = self.loaderdb[self.hwidstr]
|
||||
unfused = False
|
||||
for rootcert in root_cert_hash:
|
||||
if self.pkhash[0:16] in root_cert_hash[rootcert]:
|
||||
unfused = True
|
||||
if unfused:
|
||||
self.info("Possibly unfused device detected, so any loader should be fine...")
|
||||
if self.pkhash[0:16] in mt:
|
||||
self.programmer = mt[self.pkhash[0:16]]
|
||||
self.info(f"Trying loader: {self.programmer}")
|
||||
else:
|
||||
for loader in mt:
|
||||
self.programmer = mt[loader]
|
||||
self.info(f"Possible loader available: {self.programmer}")
|
||||
for loader in mt:
|
||||
self.programmer = mt[loader]
|
||||
self.info(f"Trying loader: {self.programmer}")
|
||||
break
|
||||
elif self.pkhash[0:16] in mt:
|
||||
self.programmer = self.loaderdb[self.hwidstr][self.pkhash[0:16]]
|
||||
self.info(f"Detected loader: {self.programmer}")
|
||||
else:
|
||||
for loader in self.loaderdb[self.hwidstr]:
|
||||
self.programmer = self.loaderdb[self.hwidstr][loader]
|
||||
self.info(f"Trying loader: {self.programmer}")
|
||||
break
|
||||
# print("Couldn't find a loader for given hwid and pkhash :(")
|
||||
# exit(0)
|
||||
elif self.hwidstr is not None and self.pkhash is not None:
|
||||
msmid = self.hwidstr[:8]
|
||||
for hwidstr in self.loaderdb:
|
||||
if msmid == hwidstr[:8]:
|
||||
for pkhash in self.loaderdb[hwidstr]:
|
||||
if self.pkhash[0:16] == pkhash:
|
||||
self.programmer = self.loaderdb[hwidstr][pkhash]
|
||||
self.info(f"Trying loader: {self.programmer}")
|
||||
self.cmd_modeswitch(self.sahara_mode.SAHARA_MODE_COMMAND)
|
||||
return True
|
||||
self.error(
|
||||
f"Couldn't find a loader for given hwid and pkhash ({self.hwidstr}_{self.pkhash[0:16]}" +
|
||||
"_[FHPRG/ENPRG].bin) :(")
|
||||
return False
|
||||
else:
|
||||
self.error(f"Couldn't find a suitable loader :(")
|
||||
return False
|
||||
|
||||
self.cmd_modeswitch(self.sahara_mode.SAHARA_MODE_COMMAND)
|
||||
return True
|
||||
return False
|
||||
|
||||
def streaminginfo(self):
|
||||
if self.enter_command_mode():
|
||||
self.serial = self.cmdexec_get_serial_num()
|
||||
self.info(f"Device serial : {hex(self.serial)}")
|
||||
self.cmd_modeswitch(self.sahara_mode.SAHARA_MODE_COMMAND)
|
||||
return True
|
||||
return False
|
||||
|
||||
def cmd_done(self):
|
||||
if self.cdc.write(pack("<II", self.cmd.SAHARA_DONE_REQ, 0x8)):
|
||||
cmd, pkt = self.get_rsp()
|
||||
time.sleep(0.3)
|
||||
if cmd["cmd"] == self.cmd.SAHARA_DONE_RSP:
|
||||
return True
|
||||
elif cmd["cmd"] == self.cmd.SAHARA_END_TRANSFER:
|
||||
if pkt["status"] == self.status.SAHARA_NAK_INVALID_CMD:
|
||||
self.error("Invalid Transfer command received.")
|
||||
return False
|
||||
return True
|
||||
return False
|
||||
|
||||
def cmd_reset(self):
|
||||
self.cdc.write(pack("<II", self.cmd.SAHARA_RESET_REQ, 0x8))
|
||||
try:
|
||||
cmd, pkt = self.get_rsp()
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
self.debug(str(e))
|
||||
return False
|
||||
if cmd["cmd"] == self.cmd.SAHARA_RESET_RSP:
|
||||
return True
|
||||
elif "status" in pkt:
|
||||
self.error(self.get_error_desc(pkt["status"]))
|
||||
return False
|
||||
return False
|
||||
|
||||
def read_memory(self, addr, bytestoread, display=False, wf=None):
|
||||
data = b""
|
||||
old = 0
|
||||
pos = 0
|
||||
total = bytestoread
|
||||
if display:
|
||||
print_progress(0, 100, prefix='Progress:', suffix='Complete', bar_length=50)
|
||||
while bytestoread > 0:
|
||||
if bytestoread > 0x080000:
|
||||
length = 0x080000
|
||||
else:
|
||||
length = bytestoread
|
||||
bytesread = 0
|
||||
try:
|
||||
self.cdc.read(1, 1)
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
self.debug(str(e))
|
||||
pass
|
||||
if self.bit64:
|
||||
if not self.cdc.write(pack("<IIQQ", self.cmd.SAHARA_64BIT_MEMORY_READ, 0x8 + 8 + 8, addr + pos,
|
||||
length)):
|
||||
return None
|
||||
else:
|
||||
if not self.cdc.write(
|
||||
pack("<IIII", self.cmd.SAHARA_MEMORY_READ, 0x8 + 4 + 4, addr + pos, length)):
|
||||
return None
|
||||
while length > 0:
|
||||
try:
|
||||
tmp = self.cdc.read(length)
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
self.debug(str(e))
|
||||
return None
|
||||
length -= len(tmp)
|
||||
pos += len(tmp)
|
||||
bytesread += len(tmp)
|
||||
if wf is not None:
|
||||
wf.write(tmp)
|
||||
else:
|
||||
data += tmp
|
||||
if display:
|
||||
prog = round(float(pos) / float(total) * float(100), 1)
|
||||
if prog > old:
|
||||
if display:
|
||||
print_progress(prog, 100, prefix='Progress:', suffix='Complete', bar_length=50)
|
||||
old = prog
|
||||
bytestoread -= bytesread
|
||||
if display:
|
||||
print_progress(100, 100, prefix='Progress:', suffix='Complete', bar_length=50)
|
||||
'''
|
||||
try:
|
||||
self.cdc.read(0)
|
||||
except:
|
||||
return data
|
||||
'''
|
||||
return data
|
||||
|
||||
def dump_partitions(self, partition):
|
||||
for part in partition:
|
||||
filename = part["filename"]
|
||||
desc = part["desc"]
|
||||
mem_base = part["mem_base"]
|
||||
length = part["length"]
|
||||
print(f"Dumping {filename}({desc}) at {hex(mem_base)}, length {hex(length)}")
|
||||
fname = os.path.join("memory", filename)
|
||||
with open(fname, "wb") as wf:
|
||||
if self.read_memory(mem_base, length, True, wf):
|
||||
print("Done dumping memory")
|
||||
else:
|
||||
self.error("Error dumping memory")
|
||||
self.cmd_reset()
|
||||
return True
|
||||
|
||||
def debug_mode(self):
|
||||
if not self.cmd_hello(self.sahara_mode.SAHARA_MODE_MEMORY_DEBUG):
|
||||
return False
|
||||
if os.path.exists("memory"):
|
||||
rmrf("memory")
|
||||
os.mkdir("memory")
|
||||
cmd, pkt = self.get_rsp()
|
||||
if cmd["cmd"] == self.cmd.SAHARA_MEMORY_DEBUG or cmd["cmd"] == self.cmd.SAHARA_64BIT_MEMORY_DEBUG:
|
||||
memory_table_addr = pkt["memory_table_addr"]
|
||||
memory_table_length = pkt["memory_table_length"]
|
||||
if self.bit64:
|
||||
pktsize = 8 + 8 + 8 + 20 + 20
|
||||
if memory_table_length % pktsize == 0:
|
||||
if memory_table_length != 0:
|
||||
print(
|
||||
f"Reading 64-Bit partition from {hex(memory_table_addr)} with length of " +
|
||||
"{hex(memory_table_length)}")
|
||||
ptbldata = self.read_memory(memory_table_addr, memory_table_length)
|
||||
num_entries = len(ptbldata) // pktsize
|
||||
partitions = []
|
||||
for id_entry in range(0, num_entries):
|
||||
pd = read_object(ptbldata[id_entry * pktsize:(id_entry * pktsize) + pktsize],
|
||||
self.parttbl_64bit)
|
||||
desc = pd["desc"].replace(b"\x00", b"").decode('utf-8')
|
||||
filename = pd["filename"].replace(b"\x00", b"").decode('utf-8')
|
||||
mem_base = pd["mem_base"]
|
||||
save_pref = pd["save_pref"]
|
||||
length = pd["length"]
|
||||
partitions.append(dict(desc=desc, filename=filename, mem_base=mem_base, length=length,
|
||||
save_pref=save_pref))
|
||||
print(
|
||||
f"{filename}({desc}): Offset {hex(mem_base)}, Length {hex(length)}, " +
|
||||
"SavePref {hex(save_pref)}")
|
||||
|
||||
self.dump_partitions(partitions)
|
||||
return True
|
||||
|
||||
return True
|
||||
else:
|
||||
pktsize = (4 + 4 + 4 + 20 + 20)
|
||||
if memory_table_length % pktsize == 0:
|
||||
if memory_table_length != 0:
|
||||
print(f"Reading 32-Bit partition from {hex(memory_table_addr)} " +
|
||||
f"with length of {hex(memory_table_length)}")
|
||||
ptbldata = self.read_memory(memory_table_addr, memory_table_length)
|
||||
num_entries = len(ptbldata) // pktsize
|
||||
partitions = []
|
||||
for id_entry in range(0, num_entries):
|
||||
pd = read_object(ptbldata[id_entry * pktsize:(id_entry * pktsize) + pktsize], self.parttbl)
|
||||
desc = pd["desc"].replace(b"\x00", b"").decode('utf-8')
|
||||
filename = pd["filename"].replace(b"\x00", b"").decode('utf-8')
|
||||
mem_base = pd["mem_base"]
|
||||
save_pref = pd["save_pref"]
|
||||
length = pd["length"]
|
||||
partitions.append(dict(desc=desc, filename=filename, mem_base=mem_base, length=length,
|
||||
save_pref=save_pref))
|
||||
print(f"{filename}({desc}): Offset {hex(mem_base)}, " +
|
||||
f"Length {hex(length)}, SavePref {hex(save_pref)}")
|
||||
|
||||
self.dump_partitions(partitions)
|
||||
return True
|
||||
elif "status" in pkt:
|
||||
self.error(self.get_error_desc(pkt["status"]))
|
||||
return False
|
||||
return False
|
||||
|
||||
def upload_loader(self):
|
||||
if self.programmer == "":
|
||||
return ""
|
||||
try:
|
||||
self.info(f"Uploading loader {self.programmer} ...")
|
||||
with open(self.programmer, "rb") as rf:
|
||||
programmer = rf.read()
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
self.error(str(e))
|
||||
sys.exit()
|
||||
|
||||
if not self.cmd_hello(self.sahara_mode.SAHARA_MODE_IMAGE_TX_PENDING):
|
||||
return ""
|
||||
|
||||
try:
|
||||
datalen = len(programmer)
|
||||
done = False
|
||||
while datalen > 0 or done:
|
||||
cmd, pkt = self.get_rsp()
|
||||
if cmd == -1 or pkt == -1:
|
||||
if self.cmd_done():
|
||||
return self.mode # Do NOT remove
|
||||
else:
|
||||
self.error("Timeout while uploading loader. Wrong loader ?")
|
||||
return ""
|
||||
if cmd["cmd"] == self.cmd.SAHARA_64BIT_MEMORY_READ_DATA:
|
||||
self.bit64 = True
|
||||
elif cmd["cmd"] == self.cmd.SAHARA_READ_DATA:
|
||||
self.bit64 = False
|
||||
elif cmd["cmd"] == self.cmd.SAHARA_END_TRANSFER:
|
||||
if pkt["status"] == self.status.SAHARA_STATUS_SUCCESS:
|
||||
self.cmd_done()
|
||||
return self.mode
|
||||
else:
|
||||
return ""
|
||||
elif "status" in pkt:
|
||||
self.error(self.get_error_desc(pkt["status"]))
|
||||
return ""
|
||||
else:
|
||||
self.error("Unexpected error on uploading")
|
||||
return ""
|
||||
self.id = pkt["id"]
|
||||
if self.id == 0x7:
|
||||
self.mode = "nandprg"
|
||||
elif self.id == 0xB:
|
||||
self.mode = "enandprg"
|
||||
elif self.id >= 0xC:
|
||||
self.mode = "firehose"
|
||||
|
||||
data_offset = pkt["data_offset"]
|
||||
data_len = pkt["data_len"]
|
||||
if data_offset + data_len > len(programmer):
|
||||
while len(programmer) < data_offset + data_len:
|
||||
programmer += b"\xFF"
|
||||
data_to_send = programmer[data_offset:data_offset + data_len]
|
||||
self.cdc.write(data_to_send)
|
||||
datalen -= data_len
|
||||
self.info("Loader uploaded.")
|
||||
cmd, pkt = self.get_rsp()
|
||||
if cmd["cmd"] == self.cmd.SAHARA_END_TRANSFER:
|
||||
if pkt["status"] == self.status.SAHARA_STATUS_SUCCESS:
|
||||
self.cmd_done()
|
||||
return self.mode
|
||||
return ""
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
self.error("Unexpected error on uploading, maybe signature of loader wasn't accepted ?\n" + str(e))
|
||||
return ""
|
||||
|
||||
def cmd_modeswitch(self, mode):
|
||||
data = pack("<III", self.cmd.SAHARA_SWITCH_MODE, 0xC, mode)
|
||||
self.cdc.write(data)
|
||||
|
||||
def cmd_exec(self, mcmd): # CMD 0xD, RSP 0xE, CMD2 0xF
|
||||
# Send request
|
||||
data = pack("<III", self.cmd.SAHARA_EXECUTE_REQ, 0xC, mcmd)
|
||||
self.cdc.write(data)
|
||||
# Get info about request
|
||||
cmd, pkt = self.get_rsp()
|
||||
if cmd["cmd"] == self.cmd.SAHARA_EXECUTE_RSP:
|
||||
# Ack
|
||||
data = pack("<III", self.cmd.SAHARA_EXECUTE_DATA, 0xC, mcmd)
|
||||
self.cdc.write(data)
|
||||
payload = self.cdc.read(pkt["data_len"])
|
||||
return payload
|
||||
elif "status" in pkt:
|
||||
self.error(self.get_error_desc(pkt["status"]))
|
||||
return None
|
||||
return [cmd, pkt]
|
236
edl/Library/sparse.py
Executable file
236
edl/Library/sparse.py
Executable file
|
@ -0,0 +1,236 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) B.Kerler 2018-2021
|
||||
import logging
|
||||
import sys
|
||||
import os
|
||||
import sys
|
||||
import inspect
|
||||
from queue import Queue
|
||||
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
|
||||
|
||||
|
||||
class QCSparse(metaclass=LogBase):
|
||||
def __init__(self, filename, loglevel):
|
||||
self.rf = open(filename, 'rb')
|
||||
self.data = Queue()
|
||||
self.__logger = self.__logger
|
||||
self.offset = 0
|
||||
self.tmpdata = bytearray()
|
||||
self.__logger.setLevel(loglevel)
|
||||
|
||||
self.major_version = None
|
||||
self.minor_version = None
|
||||
self.file_hdr_sz = None
|
||||
self.chunk_hdr_sz = None
|
||||
self.blk_sz = None
|
||||
self.total_blks = None
|
||||
self.total_chunks = None
|
||||
self.image_checksum = None
|
||||
|
||||
self.info = self.__logger.info
|
||||
self.debug = self.__logger.debug
|
||||
self.error = self.__logger.error
|
||||
self.warning = self.__logger.warning
|
||||
|
||||
if loglevel == logging.DEBUG:
|
||||
logfilename = "log.txt"
|
||||
fh = logging.FileHandler(logfilename)
|
||||
self.__logger.addHandler(fh)
|
||||
|
||||
def readheader(self):
|
||||
header = unpack("<I4H4I", self.rf.read(0x1C))
|
||||
magic = header[0]
|
||||
self.major_version = header[1]
|
||||
self.minor_version = header[2]
|
||||
self.file_hdr_sz = header[3]
|
||||
self.chunk_hdr_sz = header[4]
|
||||
self.blk_sz = header[5]
|
||||
self.total_blks = header[6]
|
||||
self.total_chunks = header[7]
|
||||
self.image_checksum = header[8]
|
||||
if magic != 0xED26FF3A:
|
||||
return False
|
||||
if self.file_hdr_sz != 28:
|
||||
self.error("The file header size was expected to be 28, but is %u." % self.file_hdr_sz)
|
||||
return False
|
||||
if self.chunk_hdr_sz != 12:
|
||||
self.error("The chunk header size was expected to be 12, but is %u." % self.chunk_hdr_sz)
|
||||
return False
|
||||
self.info("Sparse Format detected. Using unpacked image.")
|
||||
return True
|
||||
|
||||
def get_chunk_size(self):
|
||||
if self.total_blks < self.offset:
|
||||
self.error(
|
||||
"The header said we should have %u output blocks, but we saw %u" % (self.total_blks, self.offset))
|
||||
return -1
|
||||
header = unpack("<2H2I", self.rf.read(self.chunk_hdr_sz))
|
||||
chunk_type = header[0]
|
||||
chunk_sz = header[2]
|
||||
total_sz = header[3]
|
||||
data_sz = total_sz - 12
|
||||
if chunk_type == 0xCAC1:
|
||||
if data_sz != (chunk_sz * self.blk_sz):
|
||||
self.error(
|
||||
"Raw chunk input size (%u) does not match output size (%u)" % (data_sz, chunk_sz * self.blk_sz))
|
||||
return -1
|
||||
else:
|
||||
self.rf.seek(self.rf.tell() + chunk_sz * self.blk_sz)
|
||||
return chunk_sz * self.blk_sz
|
||||
elif chunk_type == 0xCAC2:
|
||||
if data_sz != 4:
|
||||
self.error("Fill chunk should have 4 bytes of fill, but this has %u" % data_sz)
|
||||
return -1
|
||||
else:
|
||||
return chunk_sz * self.blk_sz // 4
|
||||
elif chunk_type == 0xCAC3:
|
||||
return chunk_sz * self.blk_sz
|
||||
elif chunk_type == 0xCAC4:
|
||||
if data_sz != 4:
|
||||
self.error("CRC32 chunk should have 4 bytes of CRC, but this has %u" % data_sz)
|
||||
return -1
|
||||
else:
|
||||
self.rf.seek(self.rf.tell() + 4)
|
||||
return 0
|
||||
else:
|
||||
self.debug("Unknown chunk type 0x%04X" % chunk_type)
|
||||
return -1
|
||||
|
||||
def unsparse(self):
|
||||
if self.total_blks < self.offset:
|
||||
self.error(
|
||||
"The header said we should have %u output blocks, but we saw %u" % (self.total_blks, self.offset))
|
||||
return -1
|
||||
header = unpack("<2H2I", self.rf.read(self.chunk_hdr_sz))
|
||||
chunk_type = header[0]
|
||||
chunk_sz = header[2]
|
||||
total_sz = header[3]
|
||||
data_sz = total_sz - 12
|
||||
if chunk_type == 0xCAC1:
|
||||
if data_sz != (chunk_sz * self.blk_sz):
|
||||
self.error(
|
||||
"Raw chunk input size (%u) does not match output size (%u)" % (data_sz, chunk_sz * self.blk_sz))
|
||||
return -1
|
||||
else:
|
||||
self.debug("Raw data")
|
||||
data = self.rf.read(chunk_sz * self.blk_sz)
|
||||
self.offset += chunk_sz
|
||||
return data
|
||||
elif chunk_type == 0xCAC2:
|
||||
if data_sz != 4:
|
||||
self.error("Fill chunk should have 4 bytes of fill, but this has %u" % data_sz)
|
||||
return -1
|
||||
else:
|
||||
fill_bin = self.rf.read(4)
|
||||
fill = unpack("<I", fill_bin)
|
||||
self.debug(format("Fill with 0x%08X" % fill))
|
||||
data = fill_bin * (chunk_sz * self.blk_sz // 4)
|
||||
self.offset += chunk_sz
|
||||
return data
|
||||
elif chunk_type == 0xCAC3:
|
||||
data = b'\x00' * chunk_sz * self.blk_sz
|
||||
self.offset += chunk_sz
|
||||
return data
|
||||
elif chunk_type == 0xCAC4:
|
||||
if data_sz != 4:
|
||||
self.error("CRC32 chunk should have 4 bytes of CRC, but this has %u" % data_sz)
|
||||
return -1
|
||||
else:
|
||||
crc_bin = self.rf.read(4)
|
||||
crc = unpack("<I", crc_bin)
|
||||
self.debug(format("Unverified CRC32 0x%08X" % crc))
|
||||
return b""
|
||||
else:
|
||||
self.debug("Unknown chunk type 0x%04X" % chunk_type)
|
||||
return -1
|
||||
|
||||
def getsize(self):
|
||||
self.rf.seek(0x1C)
|
||||
length = 0
|
||||
chunk = 0
|
||||
while chunk < self.total_chunks:
|
||||
tlen = self.get_chunk_size()
|
||||
if tlen == -1:
|
||||
break
|
||||
length += tlen
|
||||
chunk += 1
|
||||
self.rf.seek(0x1C)
|
||||
return length
|
||||
|
||||
def read(self, length=None):
|
||||
if length is None:
|
||||
return self.unsparse()
|
||||
if length <= len(self.tmpdata):
|
||||
tdata = self.tmpdata[:length]
|
||||
self.tmpdata = self.tmpdata[length:]
|
||||
return tdata
|
||||
while len(self.tmpdata) < length:
|
||||
self.tmpdata.extend(self.unsparse())
|
||||
if length <= len(self.tmpdata):
|
||||
tdata = self.tmpdata[:length]
|
||||
self.tmpdata = self.tmpdata[length:]
|
||||
return tdata
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 3:
|
||||
print("./sparse.py <sparse_partition.img> <outfile>")
|
||||
sys.exit()
|
||||
sp = QCSparse(sys.argv[1], logging.INFO)
|
||||
if sp.readheader():
|
||||
print("Extracting sectors to " + sys.argv[2])
|
||||
with open(sys.argv[2], "wb") as wf:
|
||||
"""
|
||||
old=0
|
||||
while sp.offset<sp.total_blks:
|
||||
prog=int(sp.offset / sp.total_blks * 100)
|
||||
if prog>old:
|
||||
print_progress(prog, 100, prefix='Progress:', suffix='Complete', bar_length=50)
|
||||
old=prog
|
||||
data=sp.read()
|
||||
if data==b"" or data==-1:
|
||||
break
|
||||
wf.write(data)
|
||||
if len(sp.tmpdata)>0:
|
||||
wf.write(sp.tmpdata)
|
||||
sp.tmpdata=bytearray()
|
||||
print_progress(100, 100, prefix='Progress:', suffix='Complete', bar_length=50)
|
||||
"""
|
||||
|
||||
fsize = sp.getsize()
|
||||
SECTOR_SIZE_IN_BYTES = 4096
|
||||
num_partition_sectors = 5469709
|
||||
MaxPayloadSizeToTargetInBytes = 0x200000
|
||||
bytesToWrite = SECTOR_SIZE_IN_BYTES * num_partition_sectors
|
||||
total = SECTOR_SIZE_IN_BYTES * num_partition_sectors
|
||||
old = 0
|
||||
pos = 0
|
||||
while fsize > 0:
|
||||
wlen = MaxPayloadSizeToTargetInBytes // SECTOR_SIZE_IN_BYTES * SECTOR_SIZE_IN_BYTES
|
||||
if fsize < wlen:
|
||||
wlen = fsize
|
||||
wdata = sp.read(wlen)
|
||||
bytesToWrite -= wlen
|
||||
fsize -= wlen
|
||||
pos += wlen
|
||||
pv = wlen % SECTOR_SIZE_IN_BYTES
|
||||
if pv != 0:
|
||||
filllen = (wlen // SECTOR_SIZE_IN_BYTES * SECTOR_SIZE_IN_BYTES) + \
|
||||
SECTOR_SIZE_IN_BYTES
|
||||
wdata += b"\x00" * (filllen - wlen)
|
||||
wlen = len(wdata)
|
||||
|
||||
wf.write(wdata)
|
||||
|
||||
prog = round(float(pos) / float(total) * float(100), 1)
|
||||
if prog > old:
|
||||
print_progress(prog, 100, prefix='Progress:', suffix='Complete (Sector %d)'
|
||||
% (pos // SECTOR_SIZE_IN_BYTES),
|
||||
bar_length=50)
|
||||
|
||||
print("Done.")
|
1069
edl/Library/streaming.py
Executable file
1069
edl/Library/streaming.py
Executable file
File diff suppressed because it is too large
Load diff
436
edl/Library/streaming_client.py
Normal file
436
edl/Library/streaming_client.py
Normal file
|
@ -0,0 +1,436 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) B.Kerler 2018-2021
|
||||
import sys
|
||||
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
|
||||
|
||||
|
||||
class streaming_client(metaclass=LogBase):
|
||||
def __init__(self, arguments, cdc, sahara, loglevel, printer):
|
||||
self.cdc = cdc
|
||||
self.__logger = self.__logger
|
||||
self.sahara = sahara
|
||||
self.arguments = arguments
|
||||
self.streaming = Streaming(cdc, sahara, loglevel)
|
||||
self.printer = printer
|
||||
self.__logger.setLevel(loglevel)
|
||||
self.error = self.__logger.error
|
||||
self.info = self.__logger.info
|
||||
if loglevel == logging.DEBUG:
|
||||
logfilename = "log.txt"
|
||||
fh = logging.FileHandler(logfilename)
|
||||
self.__logger.addHandler(fh)
|
||||
|
||||
def disconnect(self):
|
||||
self.cdc.close()
|
||||
sys.exit(0)
|
||||
|
||||
def check_param(self, parameters):
|
||||
error = False
|
||||
params = ""
|
||||
for parameter in parameters:
|
||||
params += parameter + " "
|
||||
if parameter not in parameters:
|
||||
error = True
|
||||
if error:
|
||||
if len(parameters) == 1:
|
||||
self.printer("Argument " + params + "required.")
|
||||
else:
|
||||
self.printer("Arguments " + params + "required.")
|
||||
return False
|
||||
return True
|
||||
|
||||
def print_partitions(self, partitions):
|
||||
self.printer("Name Offset\t\tLength\t\tAttr\t\t\tFlash")
|
||||
self.printer("-------------------------------------------------------------")
|
||||
for name in partitions:
|
||||
partition = partitions[name]
|
||||
for i in range(0x10 - len(name)):
|
||||
name += " "
|
||||
offset = partition[
|
||||
"offset"] * self.streaming.settings.num_pages_per_blk * self.streaming.settings.PAGESIZE
|
||||
length = partition[
|
||||
"length"] * self.streaming.settings.num_pages_per_blk * self.streaming.settings.PAGESIZE
|
||||
attr1 = partition["attr1"]
|
||||
attr2 = partition["attr2"]
|
||||
attr3 = partition["attr3"]
|
||||
which_flash = partition["which_flash"]
|
||||
self.printer(
|
||||
f"{name}\t%08X\t%08X\t{hex(attr1)}/{hex(attr2)}/{hex(attr3)}\t{which_flash}" % (offset, length))
|
||||
|
||||
def handle_streaming(self, cmd, options):
|
||||
mode = 0
|
||||
"""
|
||||
offset = getint(options["<offset>"])
|
||||
length = getint(options["<length>"])
|
||||
filename = options["<filename>"]
|
||||
self.streaming.streaming_mode=self.streaming.Qualcomm
|
||||
self.streaming.memread=self.streaming.qc_memread
|
||||
self.streaming.memtofile(offset, length, filename)
|
||||
"""
|
||||
|
||||
if "<mode>" in options:
|
||||
mode = options["<mode>"]
|
||||
if self.streaming.connect(mode):
|
||||
xflag = 0
|
||||
self.streaming.hdlc.receive_reply(5)
|
||||
if self.streaming.streaming_mode == self.streaming.Patched:
|
||||
self.streaming.nand_init(xflag)
|
||||
if cmd == "gpt":
|
||||
directory = options["<directory>"]
|
||||
if directory is None:
|
||||
directory = ""
|
||||
data = self.streaming.read_partition_table()
|
||||
sfilename = os.path.join(directory, f"partition.bin")
|
||||
if data != b"":
|
||||
with open(sfilename, "wb") as write_handle:
|
||||
write_handle.write(data)
|
||||
self.printer(f"Dumped Partition Table to {sfilename}")
|
||||
else:
|
||||
self.error(f"Error on dumping partition table to {sfilename}")
|
||||
elif cmd == "printgpt":
|
||||
partitions = self.streaming.get_partitions()
|
||||
self.print_partitions(partitions)
|
||||
self.streaming.nand_post()
|
||||
elif cmd == "r":
|
||||
partitionname = options["<partitionname>"]
|
||||
filename = options["<filename>"]
|
||||
filenames = filename.split(",")
|
||||
partitions = partitionname.split(",")
|
||||
if len(partitions) != len(filenames):
|
||||
self.error("You need to gives as many filenames as given partitions.")
|
||||
return
|
||||
i = 0
|
||||
rpartitions = self.streaming.get_partitions()
|
||||
for partition in partitions:
|
||||
if partition.lower() in rpartitions:
|
||||
spartition = rpartitions[partition]
|
||||
offset = spartition["offset"]
|
||||
length = spartition["length"]
|
||||
# attr1 = spartition["attr1"]
|
||||
# attr2 = spartition["attr2"]
|
||||
# attr3 = spartition["attr3"]
|
||||
partfilename = filenames[i]
|
||||
self.printer(f"Dumping Partition {partition}...")
|
||||
self.streaming.read_raw(offset, length, self.streaming.settings.UD_SIZE_BYTES, partfilename)
|
||||
self.printer(f"Dumped sector {str(offset)} with sector count {str(length)} as {partfilename}.")
|
||||
else:
|
||||
self.error(f"Error: Couldn't detect partition: {partition}\nAvailable partitions:")
|
||||
self.print_partitions(rpartitions)
|
||||
elif cmd == "rs":
|
||||
sector = getint(options["<start_sector>"]) # Page
|
||||
sectors = getint(options["<sectors>"])
|
||||
filename = options["<filename>"]
|
||||
self.printer(f"Dumping at Sector {hex(sector)} with Sectorcount {hex(sectors)}...")
|
||||
if self.streaming.read_sectors(sector, sectors, filename, True):
|
||||
self.printer(f"Dumped sector {str(sector)} with sector count {str(sectors)} as {filename}.")
|
||||
elif cmd == "rf":
|
||||
sector = 0
|
||||
sectors = self.streaming.settings.MAXBLOCK * self.streaming.settings.num_pages_per_blk * \
|
||||
self.streaming.settings.sectors_per_page
|
||||
filename = options["<filename>"]
|
||||
self.printer(f"Dumping Flash from sector 0 to sector {hex(sectors)}...")
|
||||
if self.streaming.read_sectors(sector, sectors, filename, True):
|
||||
self.printer(f"Dumped sector {str(sector)} with sector count {str(sectors)} as {filename}.")
|
||||
elif cmd == "rl":
|
||||
directory = options["<directory>"]
|
||||
if options["--skip"]:
|
||||
skip = options["--skip"].split(",")
|
||||
else:
|
||||
skip = []
|
||||
if not os.path.exists(directory):
|
||||
os.mkdir(directory)
|
||||
storedir = directory
|
||||
if not os.path.exists(storedir):
|
||||
os.mkdir(storedir)
|
||||
sfilename = os.path.join(storedir, f"partition.bin")
|
||||
partdata = self.streaming.read_partition_table()
|
||||
if partdata != -1:
|
||||
with open(sfilename, "wb") as write_handle:
|
||||
write_handle.write(partdata)
|
||||
else:
|
||||
self.error(f"Couldn't detect partition header.")
|
||||
return
|
||||
partitions = self.streaming.get_partitions()
|
||||
for partition in partitions:
|
||||
if partition in skip:
|
||||
continue
|
||||
filename = os.path.join(storedir, partition + ".bin")
|
||||
spartition = partitions[partition]
|
||||
offset = spartition["offset"]
|
||||
length = spartition["length"]
|
||||
# attr1 = spartition["attr1"]
|
||||
# attr2 = spartition["attr2"]
|
||||
# attr3 = spartition["attr3"]
|
||||
partfilename = filename
|
||||
self.info(f"Dumping partition {str(partition)} with block count {str(length)} as " +
|
||||
f"{filename}.")
|
||||
self.streaming.read_raw(offset, length, self.streaming.settings.UD_SIZE_BYTES, partfilename)
|
||||
elif cmd == "peek":
|
||||
offset = getint(options["<offset>"])
|
||||
length = getint(options["<length>"])
|
||||
filename = options["<filename>"]
|
||||
if self.streaming.memtofile(offset, length, filename):
|
||||
self.info(
|
||||
f"Peek data from offset {hex(offset)} and length {hex(length)} was written to {filename}")
|
||||
elif cmd == "peekhex":
|
||||
offset = getint(options["<offset>"])
|
||||
length = getint(options["<length>"])
|
||||
resp = self.streaming.memread(offset, length)
|
||||
self.printer("\n")
|
||||
self.printer(hexlify(resp))
|
||||
elif cmd == "peekqword":
|
||||
offset = getint(options["<offset>"])
|
||||
resp = self.streaming.memread(offset, 8)
|
||||
self.printer("\n")
|
||||
self.printer(hex(unpack("<Q", resp[:8])[0]))
|
||||
elif cmd == "peekdword":
|
||||
offset = getint(options["<offset>"])
|
||||
resp = self.streaming.mempeek(offset)
|
||||
self.printer("\n")
|
||||
self.printer(hex(resp))
|
||||
elif cmd == "poke":
|
||||
offset = getint(options["<offset>"])
|
||||
filename = unhexlify(options["<filename>"])
|
||||
try:
|
||||
with open(filename, "rb") as rf:
|
||||
data = rf.read()
|
||||
if self.streaming.memwrite(offset, data):
|
||||
self.info("Poke succeeded.")
|
||||
else:
|
||||
self.error("Poke failed.")
|
||||
except Exception as e:
|
||||
self.error(str(e))
|
||||
elif cmd == "pokehex":
|
||||
offset = getint(options["<offset>"])
|
||||
data = unhexlify(options["<data>"])
|
||||
if self.streaming.memwrite(offset, data):
|
||||
self.info("Poke succeeded.")
|
||||
else:
|
||||
self.error("Poke failed.")
|
||||
elif cmd == "pokeqword":
|
||||
offset = getint(options["<offset>"])
|
||||
data = pack("<Q", getint(options["<data>"]))
|
||||
if self.streaming.memwrite(offset, data):
|
||||
self.info("Poke succeeded.")
|
||||
else:
|
||||
self.error("Poke failed.")
|
||||
elif cmd == "pokedword":
|
||||
offset = getint(options["<offset>"])
|
||||
data = pack("<I", getint(options["<data>"]))
|
||||
if self.streaming.mempoke(offset, data):
|
||||
self.info("Poke succeeded.")
|
||||
else:
|
||||
self.error("Poke failed.")
|
||||
elif cmd == "reset":
|
||||
if self.streaming.reset():
|
||||
self.info("Reset succeeded.")
|
||||
elif cmd == "memtbl":
|
||||
filename = options["<filename>"]
|
||||
memtbl = self.streaming.settings.memtbl
|
||||
data = self.streaming.memread(memtbl[0], memtbl[1])
|
||||
if data != b"":
|
||||
with open(filename, "wb") as wf:
|
||||
wf.write(data)
|
||||
self.printer(f"Dumped memtbl at offset {hex(memtbl[0])} as {filename}.")
|
||||
else:
|
||||
self.error("Error on dumping memtbl")
|
||||
elif cmd == "secureboot":
|
||||
value = self.streaming.mempeek(self.streaming.settings.secureboot)
|
||||
if value != -1:
|
||||
is_secure = False
|
||||
for area in range(0, 4):
|
||||
sec_boot = (value >> (area * 8)) & 0xFF
|
||||
pk_hashindex = sec_boot & 3
|
||||
oem_pkhash = True if ((sec_boot >> 4) & 1) == 1 else False
|
||||
auth_enabled = True if ((sec_boot >> 5) & 1) == 1 else False
|
||||
use_serial = True if ((sec_boot >> 6) & 1) == 1 else False
|
||||
if auth_enabled:
|
||||
is_secure = True
|
||||
self.printer(f"Sec_Boot{str(area)} PKHash-Index:{str(pk_hashindex)} " +
|
||||
f"OEM_PKHash: {str(oem_pkhash)} " +
|
||||
f"Auth_Enabled: {str(auth_enabled)} " +
|
||||
f"Use_Serial: {str(use_serial)}")
|
||||
if is_secure:
|
||||
self.printer("Secure boot enabled.")
|
||||
else:
|
||||
self.printer("Secure boot disabled.")
|
||||
else:
|
||||
self.error("Unknown target chipset")
|
||||
elif cmd == "pbl":
|
||||
filename = options["<filename>"]
|
||||
pbl = self.streaming.settings.pbl
|
||||
self.printer("Dumping pbl....")
|
||||
data = self.streaming.memread(pbl[0], pbl[1])
|
||||
if data != b"":
|
||||
with open(filename, "wb") as wf:
|
||||
wf.write(data)
|
||||
self.printer(f"Dumped pbl at offset {hex(pbl[0])} as {filename}.")
|
||||
else:
|
||||
self.error("Error on dumping pbl")
|
||||
elif cmd == "qfp":
|
||||
filename = options["<filename>"]
|
||||
qfp = self.streaming.settings.qfprom
|
||||
self.printer("Dumping qfprom....")
|
||||
data = self.streaming.memread(qfp[0], qfp[1])
|
||||
if data != b"":
|
||||
with open(filename, "wb") as wf:
|
||||
wf.write(data)
|
||||
self.printer(f"Dumped qfprom at offset {hex(qfp[0])} as {filename}.")
|
||||
else:
|
||||
self.error("Error on dumping qfprom")
|
||||
elif cmd == "memcpy":
|
||||
if not self.check_param(["<offset>", "<size>"]):
|
||||
return False
|
||||
srcoffset = getint(options["<offset>"])
|
||||
size = getint(options["<size>"])
|
||||
dstoffset = srcoffset + size
|
||||
if self.streaming.cmd_memcpy(dstoffset, srcoffset, size):
|
||||
self.printer(f"Memcpy from {hex(srcoffset)} to {hex(dstoffset)} succeeded")
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
###############################
|
||||
elif cmd == "nop":
|
||||
# resp=self.streaming.send(b"\x7E\x09")
|
||||
self.error("Nop command isn't supported by streaming loader")
|
||||
return True
|
||||
elif cmd == "setbootablestoragedrive":
|
||||
self.error("setbootablestoragedrive command isn't supported by streaming loader")
|
||||
return True
|
||||
elif cmd == "getstorageinfo":
|
||||
self.error("getstorageinfo command isn't supported by streaming loader")
|
||||
return True
|
||||
elif cmd == "w":
|
||||
if not self.check_param(["<partitionname>", "<filename>"]):
|
||||
return False
|
||||
partitionname = options["<partitionname>"]
|
||||
filename = options["<filename>"]
|
||||
partitionfilename = ""
|
||||
if "--partitionfilename" in options:
|
||||
partitionfilename = options["--partitionfilename"]
|
||||
if not os.path.exists(partitionfilename):
|
||||
self.error(f"Error: Couldn't find partition file: {partitionfilename}")
|
||||
return False
|
||||
if not os.path.exists(filename):
|
||||
self.error(f"Error: Couldn't find file: {filename}")
|
||||
return False
|
||||
if partitionfilename == "":
|
||||
rpartitions = self.streaming.get_partitions()
|
||||
else:
|
||||
rpartitions = self.streaming.get_partitions(partitionfilename)
|
||||
if self.streaming.enter_flash_mode():
|
||||
if partitionname in rpartitions:
|
||||
spartition = rpartitions[partitionname]
|
||||
offset = spartition["offset"]
|
||||
length = spartition["length"]
|
||||
# attr1 = spartition["attr1"]
|
||||
# attr2 = spartition["attr2"]
|
||||
# attr3 = spartition["attr3"]
|
||||
sectors = int(os.stat(
|
||||
filename).st_size / self.streaming.settings.num_pages_per_blk /
|
||||
self.streaming.settings.PAGESIZE)
|
||||
if sectors > length:
|
||||
self.error(
|
||||
f"Error: {filename} has {sectors} sectors but partition only has {length}.")
|
||||
return False
|
||||
if self.streaming.modules is not None:
|
||||
self.streaming.modules.writeprepare()
|
||||
if self.streaming.write_flash(partitionname, filename):
|
||||
self.printer(f"Wrote {filename} to sector {str(offset)}.")
|
||||
return True
|
||||
else:
|
||||
self.printer(f"Error writing {filename} to sector {str(offset)}.")
|
||||
return False
|
||||
else:
|
||||
self.error(f"Error: Couldn't detect partition: {partitionname}\nAvailable partitions:")
|
||||
self.print_partitions(rpartitions)
|
||||
return False
|
||||
elif cmd == "wl":
|
||||
if not self.check_param(["<directory>"]):
|
||||
return False
|
||||
directory = options["<directory>"]
|
||||
if options["--skip"]:
|
||||
skip = options["--skip"].split(",")
|
||||
else:
|
||||
skip = []
|
||||
if not os.path.exists(directory):
|
||||
self.error(f"Error: Couldn't find directory: {directory}")
|
||||
return False
|
||||
filenames = []
|
||||
if self.streaming.enter_flash_mode():
|
||||
if self.streaming.modules is not None:
|
||||
self.streaming.modules.writeprepare()
|
||||
rpartitions = self.streaming.get_partitions()
|
||||
for dirName, subdirList, fileList in os.walk(directory):
|
||||
for fname in fileList:
|
||||
filenames.append(os.path.join(dirName, fname))
|
||||
for filename in filenames:
|
||||
for partition in rpartitions:
|
||||
partname = filename[filename.rfind("/") + 1:]
|
||||
if ".bin" in partname[-4:]:
|
||||
partname = partname[:-4]
|
||||
if partition == partname:
|
||||
if partition in skip:
|
||||
continue
|
||||
spartition = rpartitions[partition]
|
||||
offset = spartition["offset"]
|
||||
length = spartition["length"]
|
||||
# attr1 = spartition["attr1"]
|
||||
# attr2 = spartition["attr2"]
|
||||
# attr3 = spartition["attr3"]
|
||||
sectors = int(os.stat(filename).st_size /
|
||||
self.streaming.settings.num_pages_per_blk /
|
||||
self.streaming.settings.PAGESIZE)
|
||||
if sectors > length:
|
||||
self.error(
|
||||
f"Error: {filename} has {sectors} sectors but partition only has {length}.")
|
||||
return False
|
||||
self.printer(f"Writing {filename} to partition {str(partition)}.")
|
||||
self.streaming.write_flash(partition, filename)
|
||||
else:
|
||||
self.printer("Couldn't write partition. Either wrong memorytype given or no gpt partition.")
|
||||
return False
|
||||
return True
|
||||
elif cmd == "ws":
|
||||
self.error("ws command isn't supported by streaming loader") # todo
|
||||
return False
|
||||
elif cmd == "wf":
|
||||
self.error("wf command isn't supported by streaming loader") # todo
|
||||
return False
|
||||
elif cmd == "e":
|
||||
self.error("e command isn't supported by streaming loader") # todo
|
||||
return False
|
||||
elif cmd == "es":
|
||||
self.error("es command isn't supported by streaming loader") # todo
|
||||
return False
|
||||
elif cmd == "xml":
|
||||
self.error("xml command isn't supported by streaming loader")
|
||||
return False
|
||||
elif cmd == "rawxml":
|
||||
self.error("rawxml command isn't supported by streaming loader")
|
||||
return False
|
||||
elif cmd == "send":
|
||||
self.error("send command isn't supported by streaming loader")
|
||||
return False
|
||||
###############################
|
||||
elif cmd == "server":
|
||||
return do_tcp_server(self, options, self.handle_streaming)
|
||||
elif cmd == "modules":
|
||||
if not self.check_param(["<command>", "<options>"]):
|
||||
return False
|
||||
command = options["<command>"]
|
||||
options = options["<options>"]
|
||||
if self.streaming.modules is None:
|
||||
self.error("Feature is not supported")
|
||||
return False
|
||||
else:
|
||||
return self.streaming.modules.run(mainargs=options, command=command)
|
||||
else:
|
||||
self.error("Unknown/Missing command, a command is required.")
|
||||
return False
|
658
edl/Library/usblib.py
Executable file
658
edl/Library/usblib.py
Executable file
|
@ -0,0 +1,658 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) B.Kerler 2018-2021
|
||||
import io
|
||||
import logging
|
||||
|
||||
import usb.core # pyusb
|
||||
import usb.util
|
||||
import time
|
||||
import inspect
|
||||
import array
|
||||
import usb.backend.libusb0
|
||||
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 *
|
||||
|
||||
USB_DIR_OUT = 0 # to device
|
||||
USB_DIR_IN = 0x80 # to host
|
||||
|
||||
# USB types, the second of three bRequestType fields
|
||||
USB_TYPE_MASK = (0x03 << 5)
|
||||
USB_TYPE_STANDARD = (0x00 << 5)
|
||||
USB_TYPE_CLASS = (0x01 << 5)
|
||||
USB_TYPE_VENDOR = (0x02 << 5)
|
||||
USB_TYPE_RESERVED = (0x03 << 5)
|
||||
|
||||
# USB recipients, the third of three bRequestType fields
|
||||
USB_RECIP_MASK = 0x1f
|
||||
USB_RECIP_DEVICE = 0x00
|
||||
USB_RECIP_INTERFACE = 0x01
|
||||
USB_RECIP_ENDPOINT = 0x02
|
||||
USB_RECIP_OTHER = 0x03
|
||||
# From Wireless USB 1.0
|
||||
USB_RECIP_PORT = 0x04
|
||||
USB_RECIP_RPIPE = 0x05
|
||||
|
||||
tag = 0
|
||||
|
||||
CDC_CMDS = {
|
||||
"SEND_ENCAPSULATED_COMMAND": 0x00,
|
||||
"GET_ENCAPSULATED_RESPONSE": 0x01,
|
||||
"SET_COMM_FEATURE": 0x02,
|
||||
"GET_COMM_FEATURE": 0x03,
|
||||
"CLEAR_COMM_FEATURE": 0x04,
|
||||
"SET_LINE_CODING": 0x20,
|
||||
"GET_LINE_CODING": 0x21,
|
||||
"SET_CONTROL_LINE_STATE": 0x22,
|
||||
"SEND_BREAK": 0x23, # wValue is break time
|
||||
}
|
||||
|
||||
|
||||
class UsbClass(metaclass=LogBase):
|
||||
|
||||
def load_windows_dll(self):
|
||||
if os.name == 'nt':
|
||||
windows_dir = None
|
||||
try:
|
||||
# add pygame folder to Windows DLL search paths
|
||||
windows_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "Windows")
|
||||
try:
|
||||
os.add_dll_directory(windows_dir)
|
||||
except Exception:
|
||||
pass
|
||||
os.environ['PATH'] = windows_dir + ';' + os.environ['PATH']
|
||||
except Exception:
|
||||
pass
|
||||
del windows_dir
|
||||
|
||||
def __init__(self, loglevel=logging.INFO, portconfig=None, devclass=-1):
|
||||
self.load_windows_dll()
|
||||
self.connected = False
|
||||
self.timeout = None
|
||||
self.vid = None
|
||||
self.pid = None
|
||||
self.device = None
|
||||
self.EP_IN = None
|
||||
self.EP_OUT = None
|
||||
self.interface = None
|
||||
self.stopbits = None
|
||||
self.databits = None
|
||||
self.baudrate = None
|
||||
self.parity = None
|
||||
self.configuration = None
|
||||
self.backend = None
|
||||
self.loglevel = loglevel
|
||||
self.portconfig = portconfig
|
||||
self.devclass = devclass
|
||||
self.__logger = self.__logger
|
||||
self.info = self.__logger.info
|
||||
self.error = self.__logger.error
|
||||
self.warning = self.__logger.warning
|
||||
self.debug = self.__logger.debug
|
||||
self.__logger.setLevel(loglevel)
|
||||
self.buffer = array.array('B', [0]) * 1048576
|
||||
if loglevel == logging.DEBUG:
|
||||
logfilename = "log.txt"
|
||||
fh = logging.FileHandler(logfilename)
|
||||
self.__logger.addHandler(fh)
|
||||
|
||||
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 self.backend is not None:
|
||||
self.backend.lib.libusb_set_option.argtypes = [c_void_p, c_int]
|
||||
self.backend.lib.libusb_set_option(self.backend.ctx, 1)
|
||||
|
||||
def verify_data(self, data, pre="RX:"):
|
||||
self.debug("", stack_info=True)
|
||||
if isinstance(data, bytes) or isinstance(data, bytearray):
|
||||
if data[:5] == b"<?xml":
|
||||
try:
|
||||
rdata = b""
|
||||
for line in data.split(b"\n"):
|
||||
try:
|
||||
self.debug(pre + line.decode('utf-8'))
|
||||
rdata += line + b"\n"
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
v = hexlify(line)
|
||||
self.debug(str(e))
|
||||
self.debug(pre + v.decode('utf-8'))
|
||||
return rdata
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
self.debug(str(e))
|
||||
pass
|
||||
if logging.DEBUG >= self.__logger.level:
|
||||
self.debug(pre + hexlify(data).decode('utf-8'))
|
||||
else:
|
||||
if logging.DEBUG >= self.__logger.level:
|
||||
self.debug(pre + data)
|
||||
return data
|
||||
|
||||
def getinterfacecount(self):
|
||||
if self.vid is not None:
|
||||
self.device = usb.core.find(idVendor=self.vid, idProduct=self.pid)
|
||||
if self.device is None:
|
||||
self.debug("Couldn't detect the device. Is it connected ?")
|
||||
return False
|
||||
try:
|
||||
self.device.set_configuration()
|
||||
except Exception as e:
|
||||
self.debug(str(e))
|
||||
pass
|
||||
self.configuration = self.device.get_active_configuration()
|
||||
self.debug(2, self.configuration)
|
||||
return self.configuration.bNumInterfaces
|
||||
else:
|
||||
self.error("No device detected. Is it connected ?")
|
||||
return 0
|
||||
|
||||
def setlinecoding(self, baudrate=None, parity=0, databits=8, stopbits=1):
|
||||
sbits = {1: 0, 1.5: 1, 2: 2}
|
||||
dbits = {5, 6, 7, 8, 16}
|
||||
pmodes = {0, 1, 2, 3, 4}
|
||||
brates = {300, 600, 1200, 2400, 4800, 9600, 14400,
|
||||
19200, 28800, 38400, 57600, 115200, 230400}
|
||||
|
||||
if stopbits is not None:
|
||||
if stopbits not in sbits.keys():
|
||||
valid = ", ".join(str(k) for k in sorted(sbits.keys()))
|
||||
raise ValueError("Valid stopbits are " + valid)
|
||||
self.stopbits = stopbits
|
||||
else:
|
||||
self.stopbits = 0
|
||||
|
||||
if databits is not None:
|
||||
if databits not in dbits:
|
||||
valid = ", ".join(str(d) for d in sorted(dbits))
|
||||
raise ValueError("Valid databits are " + valid)
|
||||
self.databits = databits
|
||||
else:
|
||||
self.databits = 0
|
||||
|
||||
if parity is not None:
|
||||
if parity not in pmodes:
|
||||
valid = ", ".join(str(pm) for pm in sorted(pmodes))
|
||||
raise ValueError("Valid parity modes are " + valid)
|
||||
self.parity = parity
|
||||
else:
|
||||
self.parity = 0
|
||||
|
||||
if baudrate is not None:
|
||||
if baudrate not in brates:
|
||||
brs = sorted(brates)
|
||||
dif = [abs(br - baudrate) for br in brs]
|
||||
best = brs[dif.index(min(dif))]
|
||||
raise ValueError(
|
||||
"Invalid baudrates, nearest valid is {}".format(best))
|
||||
self.baudrate = baudrate
|
||||
|
||||
linecode = [
|
||||
self.baudrate & 0xff,
|
||||
(self.baudrate >> 8) & 0xff,
|
||||
(self.baudrate >> 16) & 0xff,
|
||||
(self.baudrate >> 24) & 0xff,
|
||||
sbits[self.stopbits],
|
||||
self.parity,
|
||||
self.databits]
|
||||
|
||||
txdir = 0 # 0:OUT, 1:IN
|
||||
req_type = 1 # 0:std, 1:class, 2:vendor
|
||||
recipient = 1 # 0:device, 1:interface, 2:endpoint, 3:other
|
||||
req_type = (txdir << 7) + (req_type << 5) + recipient
|
||||
data = bytearray(linecode)
|
||||
wlen = self.device.ctrl_transfer(
|
||||
req_type, CDC_CMDS["SET_LINE_CODING"],
|
||||
data_or_wlength=data, windex=1)
|
||||
self.debug("Linecoding set, {}b sent".format(wlen))
|
||||
|
||||
def setbreak(self):
|
||||
txdir = 0 # 0:OUT, 1:IN
|
||||
req_type = 1 # 0:std, 1:class, 2:vendor
|
||||
recipient = 1 # 0:device, 1:interface, 2:endpoint, 3:other
|
||||
req_type = (txdir << 7) + (req_type << 5) + recipient
|
||||
wlen = self.device.ctrl_transfer(
|
||||
bmrequesttype=req_type, brequest=CDC_CMDS["SEND_BREAK"],
|
||||
wvalue=0, data_or_wlength=0, windex=1)
|
||||
self.debug("Break set, {}b sent".format(wlen))
|
||||
|
||||
def setcontrollinestate(self, rts=None, dtr=None, isftdi=False):
|
||||
ctrlstate = (2 if rts else 0) + (1 if dtr else 0)
|
||||
if isftdi:
|
||||
ctrlstate += (1 << 8) if dtr is not None else 0
|
||||
ctrlstate += (2 << 8) if rts is not None else 0
|
||||
txdir = 0 # 0:OUT, 1:IN
|
||||
req_type = 2 if isftdi else 1 # 0:std, 1:class, 2:vendor
|
||||
# 0:device, 1:interface, 2:endpoint, 3:other
|
||||
recipient = 0 if isftdi else 1
|
||||
req_type = (txdir << 7) + (req_type << 5) + recipient
|
||||
|
||||
wlen = self.device.ctrl_transfer(
|
||||
bmrequesttype=req_type,
|
||||
brequest=1 if isftdi else CDC_CMDS["SET_CONTROL_LINE_STATE"],
|
||||
wvalue=ctrlstate,
|
||||
windex=1,
|
||||
data_or_wlength=0)
|
||||
self.debug("Linecoding set, {}b sent".format(wlen))
|
||||
|
||||
def connect(self, EP_IN=-1, EP_OUT=-1):
|
||||
if self.connected:
|
||||
self.close()
|
||||
self.connected = False
|
||||
for usbid in self.portconfig:
|
||||
vid = usbid[0]
|
||||
pid = usbid[1]
|
||||
interface = usbid[2]
|
||||
self.device = usb.core.find(idVendor=vid, idProduct=pid, backend=self.backend)
|
||||
if self.device is not None:
|
||||
self.vid = vid
|
||||
self.pid = pid
|
||||
self.interface = interface
|
||||
break
|
||||
|
||||
if self.device is None:
|
||||
self.debug("Couldn't detect the device. Is it connected ?")
|
||||
return False
|
||||
|
||||
|
||||
try:
|
||||
if self.device is not None:
|
||||
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 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
|
||||
break
|
||||
else:
|
||||
self.interface = interfacenum
|
||||
break
|
||||
|
||||
self.debug(self.configuration)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
else:
|
||||
self.EP_IN = EP_IN
|
||||
|
||||
self.connected = True
|
||||
return True
|
||||
else:
|
||||
print("Couldn't find MassStorage interface. Aborting.")
|
||||
self.connected = False
|
||||
return False
|
||||
|
||||
def close(self, reset=False):
|
||||
if self.connected:
|
||||
usb.util.dispose_resources(self.device)
|
||||
try:
|
||||
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))
|
||||
pass
|
||||
del self.device
|
||||
|
||||
def write(self, command):
|
||||
pktsize=self.EP_OUT.wMaxPacketSize
|
||||
if isinstance(command, str):
|
||||
command = bytes(command, 'utf-8')
|
||||
if command == b'':
|
||||
try:
|
||||
self.EP_OUT.write(b'')
|
||||
except usb.core.USBError as e:
|
||||
error = str(e.strerror)
|
||||
if "timeout" in error:
|
||||
time.sleep(0.01)
|
||||
try:
|
||||
self.EP_OUT.write(b'')
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
self.debug(str(e))
|
||||
return False
|
||||
return True
|
||||
else:
|
||||
i = 0
|
||||
try:
|
||||
buffer=array.array('B',command)
|
||||
self.EP_OUT.write(buffer)
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
# print("Error while writing")
|
||||
if "timed out" in str(e):
|
||||
self.debug(str(e))
|
||||
time.sleep(0.01)
|
||||
i += 1
|
||||
if i == 3:
|
||||
return False
|
||||
pass
|
||||
else:
|
||||
self.error(str(e))
|
||||
return False
|
||||
if self.loglevel == logging.DEBUG:
|
||||
self.verify_data(bytearray(command), "TX:")
|
||||
return True
|
||||
|
||||
def read(self, length=0x80, timeout=None):
|
||||
if self.loglevel==logging.DEBUG:
|
||||
self.debug(inspect.currentframe().f_back.f_code.co_name + ":" + hex(length))
|
||||
tmp = bytearray()
|
||||
extend = tmp.extend
|
||||
if timeout is None:
|
||||
timeout = self.timeout
|
||||
buffer = self.buffer
|
||||
ep_read = self.EP_IN.read
|
||||
while len(tmp) == 0:
|
||||
try:
|
||||
length=ep_read(buffer, timeout)
|
||||
extend(buffer[:length])
|
||||
if len(tmp)>0:
|
||||
return tmp
|
||||
except usb.core.USBError as e:
|
||||
error = str(e.strerror)
|
||||
if "timed out" in error:
|
||||
# if platform.system()=='Windows':
|
||||
# time.sleep(0.05)
|
||||
# print("Waiting...")
|
||||
self.debug("Timed out")
|
||||
self.debug(tmp)
|
||||
return tmp
|
||||
elif "Overflow" in error:
|
||||
self.error("USB Overflow")
|
||||
sys.exit(0)
|
||||
elif e.errno is not None:
|
||||
print(repr(e), type(e), e.errno)
|
||||
sys.exit(0)
|
||||
else:
|
||||
break
|
||||
if self.loglevel == logging.DEBUG:
|
||||
self.verify_data(tmp, "RX:")
|
||||
return tmp
|
||||
|
||||
def ctrl_transfer(self, bmrequesttype, brequest, wvalue, windex, data_or_wlength):
|
||||
ret = self.device.ctrl_transfer(bmrequesttype=bmrequesttype, brequest=brequest, wvalue=wvalue, windex=windex,
|
||||
data_or_wlength=data_or_wlength)
|
||||
return ret[0] | (ret[1] << 8)
|
||||
|
||||
|
||||
class ScsiCmds(Enum):
|
||||
SC_TEST_UNIT_READY = 0x00,
|
||||
SC_REQUEST_SENSE = 0x03,
|
||||
SC_FORMAT_UNIT = 0x04,
|
||||
SC_READ_6 = 0x08,
|
||||
SC_WRITE_6 = 0x0a,
|
||||
SC_INQUIRY = 0x12,
|
||||
SC_MODE_SELECT_6 = 0x15,
|
||||
SC_RESERVE = 0x16,
|
||||
SC_RELEASE = 0x17,
|
||||
SC_MODE_SENSE_6 = 0x1a,
|
||||
SC_START_STOP_UNIT = 0x1b,
|
||||
SC_SEND_DIAGNOSTIC = 0x1d,
|
||||
SC_PREVENT_ALLOW_MEDIUM_REMOVAL = 0x1e,
|
||||
SC_READ_FORMAT_CAPACITIES = 0x23,
|
||||
SC_READ_CAPACITY = 0x25,
|
||||
SC_WRITE_10 = 0x2a,
|
||||
SC_VERIFY = 0x2f,
|
||||
SC_READ_10 = 0x28,
|
||||
SC_SYNCHRONIZE_CACHE = 0x35,
|
||||
SC_READ_TOC = 0x43,
|
||||
SC_READ_HEADER = 0x44,
|
||||
SC_MODE_SELECT_10 = 0x55,
|
||||
SC_MODE_SENSE_10 = 0x5a,
|
||||
SC_READ_12 = 0xa8,
|
||||
SC_WRITE_12 = 0xaa,
|
||||
SC_PASCAL_MODE = 0xff
|
||||
|
||||
|
||||
command_block_wrapper = [
|
||||
('dCBWSignature', '4s'),
|
||||
('dCBWTag', 'I'),
|
||||
('dCBWDataTransferLength', 'I'),
|
||||
('bmCBWFlags', 'B'),
|
||||
('bCBWLUN', 'B'),
|
||||
('bCBWCBLength', 'B'),
|
||||
('CBWCB', '16s'),
|
||||
]
|
||||
command_block_wrapper_len = 31
|
||||
|
||||
command_status_wrapper = [
|
||||
('dCSWSignature', '4s'),
|
||||
('dCSWTag', 'I'),
|
||||
('dCSWDataResidue', 'I'),
|
||||
('bCSWStatus', 'B')
|
||||
]
|
||||
command_status_wrapper_len = 13
|
||||
|
||||
|
||||
class Scsi:
|
||||
"""
|
||||
FIHTDC, PCtool
|
||||
"""
|
||||
SC_READ_NV = 0xf0
|
||||
SC_SWITCH_STATUS = 0xf1
|
||||
SC_SWITCH_PORT = 0xf2
|
||||
SC_MODEM_STATUS = 0xf4
|
||||
SC_SHOW_PORT = 0xf5
|
||||
SC_MODEM_DISCONNECT = 0xf6
|
||||
SC_MODEM_CONNECT = 0xf7
|
||||
SC_DIAG_RUT = 0xf8
|
||||
SC_READ_BATTERY = 0xf9
|
||||
SC_READ_IMAGE = 0xfa
|
||||
SC_ENABLE_ALL_PORT = 0xfd
|
||||
SC_MASS_STORGE = 0xfe
|
||||
SC_ENTER_DOWNLOADMODE = 0xff
|
||||
SC_ENTER_FTMMODE = 0xe0
|
||||
SC_SWITCH_ROOT = 0xe1
|
||||
"""
|
||||
//Div2-5-3-Peripheral-LL-ADB_ROOT-00+/* } FIHTDC, PCtool */
|
||||
//StevenCPHuang 2011/08/12 porting base on 1050 --
|
||||
//StevenCPHuang_20110820,add Moto's mode switch cmd to support PID switch function ++
|
||||
"""
|
||||
SC_MODE_SWITCH = 0xD6
|
||||
|
||||
# /StevenCPHuang_20110820,add Moto's mode switch cmd to support PID switch function --
|
||||
|
||||
def __init__(self, loglevel=logging.INFO, vid=None, pid=None, interface=-1):
|
||||
self.vid = vid
|
||||
self.pid = pid
|
||||
self.interface = interface
|
||||
self.Debug = False
|
||||
self.usb = None
|
||||
self.loglevel = loglevel
|
||||
|
||||
def connect(self):
|
||||
self.usb = UsbClass(loglevel=self.loglevel, portconfig=[self.vid, self.pid, self.interface], devclass=8)
|
||||
if self.usb.connect():
|
||||
return True
|
||||
return False
|
||||
|
||||
# htcadb = "55534243123456780002000080000616687463800100000000000000000000";
|
||||
# Len 0x6, Command 0x16, "HTC" 01 = Enable, 02 = Disable
|
||||
def send_mass_storage_command(self, lun, cdb, direction, data_length):
|
||||
global tag
|
||||
cmd = cdb[0]
|
||||
if 0 <= cmd < 0x20:
|
||||
cdb_len = 6
|
||||
elif 0x20 <= cmd < 0x60:
|
||||
cdb_len = 10
|
||||
elif 0x60 <= cmd < 0x80:
|
||||
cdb_len = 0
|
||||
elif 0x80 <= cmd < 0xA0:
|
||||
cdb_len = 16
|
||||
elif 0xA0 <= cmd < 0xC0:
|
||||
cdb_len = 12
|
||||
else:
|
||||
cdb_len = 6
|
||||
|
||||
if len(cdb) != cdb_len:
|
||||
print("Error, cdb length doesn't fit allowed cbw packet length")
|
||||
return 0
|
||||
|
||||
if (cdb_len == 0) or (cdb_len > command_block_wrapper_len):
|
||||
print("Error, invalid data packet length, should be max of 31 bytes.")
|
||||
return 0
|
||||
else:
|
||||
data = write_object(command_block_wrapper, b"USBC", tag, data_length, direction, lun, cdb_len, cdb)[
|
||||
'raw_data']
|
||||
print(hexlify(data))
|
||||
if len(data) != 31:
|
||||
print("Error, invalid data packet length, should be 31 bytes, but length is %d" % len(data))
|
||||
return 0
|
||||
tag += 1
|
||||
self.usb.write(data, 31)
|
||||
return tag
|
||||
|
||||
def send_htc_adbenable(self):
|
||||
# do_reserve from f_mass_storage.c
|
||||
print("Sending HTC adb enable command")
|
||||
common_cmnd = b"\x16htc\x80\x01" # reserve_cmd + 'htc' + len + flag
|
||||
'''
|
||||
Flag values:
|
||||
1: Enable adb daemon from mass_storage
|
||||
2: Disable adb daemon from mass_storage
|
||||
3: cancel unmount BAP cdrom
|
||||
4: cancel unmount HSM rom
|
||||
'''
|
||||
lun = 0
|
||||
datasize = common_cmnd[4]
|
||||
timeout = 5000
|
||||
ret_tag = self.send_mass_storage_command(lun, common_cmnd, USB_DIR_IN, datasize)
|
||||
ret_tag += self.send_mass_storage_command(lun, common_cmnd, USB_DIR_IN, datasize)
|
||||
if datasize > 0:
|
||||
data = self.usb.read(datasize, timeout)
|
||||
print("DATA: " + hexlify(data).decode('utf-8'))
|
||||
print("Sent HTC adb enable command")
|
||||
|
||||
def send_htc_ums_adbenable(self): # HTC10
|
||||
# ums_ctrlrequest from f_mass_storage.c
|
||||
print("Sending HTC ums adb enable command")
|
||||
brequesttype = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE
|
||||
brequest = 0xa0
|
||||
wvalue = 1
|
||||
'''
|
||||
wValue:
|
||||
0: Disable adb daemon
|
||||
1: Enable adb daemon
|
||||
'''
|
||||
windex = 0
|
||||
w_length = 1
|
||||
ret = self.usb.ctrl_transfer(brequesttype, brequest, wvalue, windex, w_length)
|
||||
print("Sent HTC ums adb enable command: %x" % ret)
|
||||
|
||||
def send_zte_adbenable(self): # zte blade
|
||||
common_cmnd = b"\x86zte\x80\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" # reserve_cmd + 'zte' + len + flag
|
||||
common_cmnd2 = b"\x86zte\x80\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" # reserve_cmd + 'zte' + len + flag
|
||||
'''
|
||||
Flag values:
|
||||
0: disable adbd ---for 736T
|
||||
1: enable adbd ---for 736T
|
||||
2: disable adbd ---for All except 736T
|
||||
3: enable adbd ---for All except 736T
|
||||
'''
|
||||
lun = 0
|
||||
datasize = common_cmnd[4]
|
||||
timeout = 5000
|
||||
ret_tag = self.send_mass_storage_command(lun, common_cmnd, USB_DIR_IN, datasize)
|
||||
ret_tag += self.send_mass_storage_command(lun, common_cmnd, USB_DIR_IN, datasize)
|
||||
ret_tag = self.send_mass_storage_command(lun, common_cmnd2, USB_DIR_IN, datasize)
|
||||
ret_tag += self.send_mass_storage_command(lun, common_cmnd2, USB_DIR_IN, datasize)
|
||||
if datasize > 0:
|
||||
data = self.usb.read(datasize, timeout)
|
||||
print("DATA: " + hexlify(data).decode('utf-8'))
|
||||
print("Send HTC adb enable command")
|
||||
|
||||
def send_fih_adbenable(self): # motorola xt560, nokia 3.1, #f_mass_storage.c
|
||||
if self.usb.connect():
|
||||
print("Sending FIH adb enable command")
|
||||
datasize = 0x24
|
||||
# reserve_cmd + 'FI' + flag + len + none
|
||||
common_cmnd = bytes([self.SC_SWITCH_PORT]) + b"FI1" + struct.pack("<H", datasize)
|
||||
'''
|
||||
Flag values:
|
||||
common_cmnd[3]->1: Enable adb daemon from mass_storage
|
||||
common_cmnd[3]->0: Disable adb daemon from mass_storage
|
||||
'''
|
||||
lun = 0
|
||||
# datasize=common_cmnd[4]
|
||||
timeout = 5000
|
||||
ret_tag = None
|
||||
ret_tag += self.send_mass_storage_command(lun, common_cmnd, USB_DIR_IN, 0x600)
|
||||
# ret_tag+=self.send_mass_storage_command(lun, common_cmnd, USB_DIR_IN, 0x600)
|
||||
if datasize > 0:
|
||||
data = self.usb.read(datasize, timeout)
|
||||
print("DATA: " + hexlify(data).decode('utf-8'))
|
||||
print("Sent FIH adb enable command")
|
||||
self.usb.close()
|
||||
|
||||
def send_alcatel_adbenable(self): # Alcatel MW41
|
||||
if self.usb.connect():
|
||||
print("Sending alcatel adb enable command")
|
||||
datasize = 0x24
|
||||
common_cmnd = b"\x16\xf9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
lun = 0
|
||||
timeout = 5000
|
||||
ret_tag = None
|
||||
ret_tag += self.send_mass_storage_command(lun, common_cmnd, USB_DIR_IN, 0x600)
|
||||
if datasize > 0:
|
||||
data = self.usb.read(datasize, timeout)
|
||||
print("DATA: " + hexlify(data).decode('utf-8'))
|
||||
print("Sent alcatel adb enable command")
|
||||
self.usb.close()
|
||||
|
||||
def send_fih_root(self):
|
||||
# motorola xt560, nokia 3.1, huawei u8850, huawei Ideos X6,
|
||||
# lenovo s2109, triumph M410, viewpad 7, #f_mass_storage.c
|
||||
if self.usb.connect():
|
||||
print("Sending FIH root command")
|
||||
datasize = 0x24
|
||||
# reserve_cmd + 'FIH' + len + flag + none
|
||||
common_cmnd = bytes([self.SC_SWITCH_ROOT]) + b"FIH" + struct.pack("<H", datasize)
|
||||
lun = 0
|
||||
# datasize = common_cmnd[4]
|
||||
timeout = 5000
|
||||
ret_tag = self.send_mass_storage_command(lun, common_cmnd, USB_DIR_IN, 0x600)
|
||||
ret_tag += self.send_mass_storage_command(lun, common_cmnd, USB_DIR_IN, 0x600)
|
||||
if datasize > 0:
|
||||
data = self.usb.read(datasize, timeout)
|
||||
print("DATA: " + hexlify(data).decode('utf-8'))
|
||||
print("Sent FIH root command")
|
||||
self.usb.close()
|
||||
|
||||
def close(self):
|
||||
self.usb.close()
|
||||
return True
|
52
edl/Library/usbscsi.py
Executable file
52
edl/Library/usbscsi.py
Executable file
|
@ -0,0 +1,52 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) B.Kerler 2018-2021
|
||||
import argparse
|
||||
from edl.Library.usblib import *
|
||||
|
||||
|
||||
def main():
|
||||
info = 'MassStorageBackdoor (c) B.Kerler 2019.'
|
||||
parser = argparse.ArgumentParser(description=info)
|
||||
print("\n" + info + "\n\n")
|
||||
parser.add_argument('-vid', metavar="<vid>", help='[Option] Specify vid, default=0x2e04)', default="0x2e04")
|
||||
parser.add_argument('-pid', metavar="<pid>", help='[Option] Specify pid, default=0xc025)', default="0xc025")
|
||||
parser.add_argument('-interface', metavar="<pid>", help='[Option] Specify interface number)', default="")
|
||||
parser.add_argument('-nokia', help='[Option] Enable Nokia adb backdoor', action='store_true')
|
||||
parser.add_argument('-alcatel', help='[Option] Enable alcatel adb backdoor', action='store_true')
|
||||
parser.add_argument('-zte', help='[Option] Enable zte adb backdoor', action='store_true')
|
||||
parser.add_argument('-htc', help='[Option] Enable htc adb backdoor', action='store_true')
|
||||
parser.add_argument('-htcums', help='[Option] Enable htc ums adb backdoor', action='store_true')
|
||||
args = parser.parse_args()
|
||||
vid = None
|
||||
pid = None
|
||||
if args.vid != "":
|
||||
vid = int(args.vid, 16)
|
||||
if args.pid != "":
|
||||
pid = int(args.pid, 16)
|
||||
if args.interface != "":
|
||||
interface = int(args.interface, 16)
|
||||
else:
|
||||
interface = -1
|
||||
|
||||
usbscsi = Scsi(vid, pid, interface)
|
||||
if usbscsi.connect():
|
||||
if args.nokia:
|
||||
usbscsi.send_fih_adbenable()
|
||||
usbscsi.send_fih_root()
|
||||
elif args.zte:
|
||||
usbscsi.send_zte_adbenable()
|
||||
elif args.htc:
|
||||
usbscsi.send_htc_adbenable()
|
||||
elif args.htcums:
|
||||
usbscsi.send_htc_ums_adbenable()
|
||||
elif args.alcatel:
|
||||
usbscsi.send_alcatel_adbenable()
|
||||
else:
|
||||
print("A command is required. Use -h to see options.")
|
||||
exit(0)
|
||||
usbscsi.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
587
edl/Library/utils.py
Executable file
587
edl/Library/utils.py
Executable file
|
@ -0,0 +1,587 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) B.Kerler 2018-2021
|
||||
import sys
|
||||
import logging
|
||||
import logging.config
|
||||
import codecs
|
||||
import struct
|
||||
import os
|
||||
import shutil
|
||||
import stat
|
||||
import colorama
|
||||
import copy
|
||||
from struct import unpack, pack
|
||||
|
||||
try:
|
||||
from capstone import *
|
||||
except ImportError:
|
||||
print("Capstone library is missing (optional).")
|
||||
try:
|
||||
from keystone import *
|
||||
except ImportError:
|
||||
print("Keystone library is missing (optional).")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class structhelper:
|
||||
pos = 0
|
||||
|
||||
def __init__(self, data, pos=0):
|
||||
self.pos = 0
|
||||
self.data = data
|
||||
|
||||
def qword(self):
|
||||
dat = unpack("<Q", self.data[self.pos:self.pos + 8])[0]
|
||||
self.pos += 8
|
||||
return dat
|
||||
|
||||
def dword(self):
|
||||
dat = unpack("<I", self.data[self.pos:self.pos + 4])[0]
|
||||
self.pos += 4
|
||||
return dat
|
||||
|
||||
def dwords(self, dwords=1):
|
||||
dat = unpack("<" + str(dwords) + "I", self.data[self.pos:self.pos + 4 * dwords])
|
||||
self.pos += 4 * dwords
|
||||
return dat
|
||||
|
||||
def short(self):
|
||||
dat = unpack("<H", self.data[self.pos:self.pos + 2])[0]
|
||||
self.pos += 2
|
||||
return dat
|
||||
|
||||
def shorts(self, shorts):
|
||||
dat = unpack("<" + str(shorts) + "H", self.data[self.pos:self.pos + 2*shorts])
|
||||
self.pos += 2 * shorts
|
||||
return dat
|
||||
|
||||
def bytes(self, rlen=1):
|
||||
dat = self.data[self.pos:self.pos + rlen]
|
||||
self.pos += rlen
|
||||
if rlen == 1: return dat[0]
|
||||
return dat
|
||||
|
||||
def string(self, rlen=1):
|
||||
dat = self.data[self.pos:self.pos + rlen]
|
||||
self.pos += rlen
|
||||
return dat
|
||||
|
||||
def getpos(self):
|
||||
return self.pos
|
||||
|
||||
def seek(self, pos):
|
||||
self.pos = pos
|
||||
|
||||
def do_tcp_server(client, arguments, handler):
|
||||
def tcpprint(arg):
|
||||
if isinstance(arg, bytes) or isinstance(arg, bytearray):
|
||||
return connection.sendall(arg)
|
||||
else:
|
||||
return connection.sendall(bytes(str(arg), 'utf-8'))
|
||||
|
||||
client.printer = tcpprint
|
||||
import socket
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
port = int(arguments["--tcpport"])
|
||||
server_address = ('localhost', port)
|
||||
print('starting up on %s port %s' % server_address)
|
||||
sock.bind(server_address)
|
||||
sock.listen(1)
|
||||
response = None
|
||||
while True:
|
||||
print('waiting for a connection')
|
||||
connection, client_address = sock.accept()
|
||||
try:
|
||||
print('connection from', client_address)
|
||||
while True:
|
||||
data = connection.recv(4096).decode('utf-8')
|
||||
if data == '':
|
||||
break
|
||||
print('received %s' % data)
|
||||
if data:
|
||||
print('handling request')
|
||||
lines = data.split("\n")
|
||||
for line in lines:
|
||||
if ":" in line:
|
||||
cmd = line.split(":")[0]
|
||||
marguments = line.split(":")[1]
|
||||
try:
|
||||
opts = parse_args(cmd, marguments, arguments)
|
||||
except:
|
||||
response = "Wrong arguments\n<NAK>\n"
|
||||
opts = None
|
||||
if opts is not None:
|
||||
if handler(cmd, opts):
|
||||
response = "<ACK>\n"
|
||||
else:
|
||||
response = "<NAK>\n"
|
||||
connection.sendall(bytes(response, 'utf-8'))
|
||||
finally:
|
||||
connection.close()
|
||||
|
||||
|
||||
def parse_args(cmd, args, mainargs):
|
||||
options = {}
|
||||
opts = None
|
||||
if "," in args:
|
||||
opts = args.split(",")
|
||||
else:
|
||||
opts = [args]
|
||||
for arg in mainargs:
|
||||
if "--" in arg:
|
||||
options[arg] = mainargs[arg]
|
||||
if cmd == "gpt":
|
||||
options["<directory>"] = opts[0]
|
||||
elif cmd == "r":
|
||||
options["<partitionname>"] = opts[0]
|
||||
options["<filename>"] = opts[1]
|
||||
elif cmd == "rl":
|
||||
options["<directory>"] = opts[0]
|
||||
elif cmd == "rf":
|
||||
options["<filename>"] = opts[0]
|
||||
elif cmd == "rs":
|
||||
options["<start_sector>"] = opts[0]
|
||||
options["<sectors>"] = opts[1]
|
||||
options["<filename>"] = opts[2]
|
||||
elif cmd == "w":
|
||||
options["<partitionname>"] = opts[0]
|
||||
options["<filename>"] = opts[0]
|
||||
elif cmd == "wl":
|
||||
options["<directory>"] = opts[0]
|
||||
elif cmd == "wf":
|
||||
options["<filename>"] = opts[0]
|
||||
elif cmd == "ws":
|
||||
options["<start_sector>"] = opts[0]
|
||||
options["<filename>"] = opts[1]
|
||||
elif cmd == "e":
|
||||
options["<partitionname>"] = opts[0]
|
||||
elif cmd == "es":
|
||||
options["<start_sector>"] = opts[0]
|
||||
options["<sectors>"] = opts[1]
|
||||
elif cmd == "footer":
|
||||
options["<filename>"] = opts[0]
|
||||
elif cmd == "peek":
|
||||
options["<offset>"] = opts[0]
|
||||
options["<length>"] = opts[1]
|
||||
options["<filename>"] = opts[2]
|
||||
elif cmd == "peekhex":
|
||||
options["<offset>"] = opts[0]
|
||||
options["<length>"] = opts[1]
|
||||
elif cmd == "peekdword":
|
||||
options["<offset>"] = opts[0]
|
||||
elif cmd == "peekqword":
|
||||
options["<offset>"] = opts[0]
|
||||
elif cmd == "memtbl":
|
||||
options["<filename>"] = opts[0]
|
||||
elif cmd == "poke":
|
||||
options["<offset>"] = opts[0]
|
||||
options["<filename>"] = opts[1]
|
||||
elif cmd == "pokehex":
|
||||
options["<offset>"] = opts[0]
|
||||
options["<data>"] = opts[1]
|
||||
elif cmd == "pokedword":
|
||||
options["<offset>"] = opts[0]
|
||||
options["<data>"] = opts[1]
|
||||
elif cmd == "pokeqword":
|
||||
options["<offset>"] = opts[0]
|
||||
options["<data>"] = opts[1]
|
||||
elif cmd == "memcpy":
|
||||
options["<offset>"] = opts[0]
|
||||
options["<size>"] = opts[1]
|
||||
elif cmd == "pbl":
|
||||
options["<filename>"] = opts[0]
|
||||
elif cmd == "qfp":
|
||||
options["<filename>"] = opts[0]
|
||||
elif cmd == "setbootablestoragedrive":
|
||||
options["<lun>"] = opts[0]
|
||||
elif cmd == "send":
|
||||
options["<command>"] = opts[0]
|
||||
elif cmd == "xml":
|
||||
options["<xmlfile>"] = opts[0]
|
||||
elif cmd == "rawxml":
|
||||
options["<xmlstring>"] = opts[0]
|
||||
return options
|
||||
|
||||
|
||||
def getint(valuestr):
|
||||
try:
|
||||
return int(valuestr)
|
||||
except:
|
||||
try:
|
||||
return int(valuestr, 16)
|
||||
except:
|
||||
return 0
|
||||
|
||||
|
||||
class ColorFormatter(logging.Formatter):
|
||||
LOG_COLORS = {
|
||||
logging.ERROR: colorama.Fore.RED,
|
||||
logging.DEBUG: colorama.Fore.LIGHTMAGENTA_EX,
|
||||
logging.WARNING: colorama.Fore.YELLOW,
|
||||
}
|
||||
|
||||
def format(self, record, *args, **kwargs):
|
||||
# if the corresponding logger has children, they may receive modified
|
||||
# record, so we want to keep it intact
|
||||
new_record = copy.copy(record)
|
||||
if new_record.levelno in self.LOG_COLORS:
|
||||
pad = ""
|
||||
if new_record.name != "root":
|
||||
print(new_record.name)
|
||||
pad = "[LIB]: "
|
||||
# we want levelname to be in different color, so let's modify it
|
||||
new_record.msg = "{pad}{color_begin}{msg}{color_end}".format(
|
||||
pad=pad,
|
||||
msg=new_record.msg,
|
||||
color_begin=self.LOG_COLORS[new_record.levelno],
|
||||
color_end=colorama.Style.RESET_ALL,
|
||||
)
|
||||
# now we can let standart formatting take care of the rest
|
||||
return super(ColorFormatter, self).format(new_record, *args, **kwargs)
|
||||
|
||||
|
||||
class LogBase(type):
|
||||
debuglevel = logging.root.level
|
||||
|
||||
def __init__(cls, *args):
|
||||
super().__init__(*args)
|
||||
logger_attribute_name = '_' + cls.__name__ + '__logger'
|
||||
logger_debuglevel_name = '_' + cls.__name__ + '__debuglevel'
|
||||
logger_name = '.'.join([c.__name__ for c in cls.mro()[-2::-1]])
|
||||
LOG_CONFIG = {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"formatters": {
|
||||
"root": {
|
||||
"()": ColorFormatter,
|
||||
"format": "%(name)s - %(message)s",
|
||||
}
|
||||
},
|
||||
"handlers": {
|
||||
"root": {
|
||||
# "level": cls.__logger.level,
|
||||
"formatter": "root",
|
||||
"class": "logging.StreamHandler",
|
||||
"stream": "ext://sys.stdout",
|
||||
}
|
||||
},
|
||||
"loggers": {
|
||||
"": {
|
||||
"handlers": ["root"],
|
||||
# "level": cls.debuglevel,
|
||||
"propagate": False
|
||||
}
|
||||
},
|
||||
}
|
||||
logging.config.dictConfig(LOG_CONFIG)
|
||||
logger = logging.getLogger(logger_name)
|
||||
|
||||
setattr(cls, logger_attribute_name, logger)
|
||||
setattr(cls, logger_debuglevel_name, cls.debuglevel)
|
||||
|
||||
|
||||
def del_rw(action, name, exc):
|
||||
os.chmod(name, stat.S_IWRITE)
|
||||
os.remove(name)
|
||||
|
||||
|
||||
def rmrf(path):
|
||||
if os.path.exists(path):
|
||||
if os.path.isfile(path):
|
||||
del_rw("", path, "")
|
||||
else:
|
||||
shutil.rmtree(path, onerror=del_rw)
|
||||
|
||||
|
||||
class elf:
|
||||
class memorysegment:
|
||||
phy_addr = 0
|
||||
virt_start_addr = 0
|
||||
virt_end_addr = 0
|
||||
file_start_addr = 0
|
||||
file_end_addr = 0
|
||||
|
||||
def __init__(self, indata, filename):
|
||||
self.data = indata
|
||||
self.filename = filename
|
||||
self.header, self.pentry = self.parse()
|
||||
self.memorylayout = []
|
||||
for entry in self.pentry:
|
||||
ms = self.memorysegment()
|
||||
ms.phy_addr = entry.phy_addr
|
||||
ms.virt_start_addr = entry.virt_addr
|
||||
ms.virt_end_addr = entry.virt_addr + entry.seg_mem_len
|
||||
ms.file_start_addr = entry.from_file
|
||||
ms.file_end_addr = entry.from_file + entry.seg_file_len
|
||||
self.memorylayout.append(ms)
|
||||
|
||||
def getfileoffset(self, offset):
|
||||
for memsegment in self.memorylayout:
|
||||
if memsegment.virt_end_addr >= offset >= memsegment.virt_start_addr:
|
||||
return offset - memsegment.virt_start_addr + memsegment.file_start_addr
|
||||
return None
|
||||
|
||||
def getvirtaddr(self, fileoffset):
|
||||
for memsegment in self.memorylayout:
|
||||
if memsegment.file_end_addr >= fileoffset >= memsegment.file_start_addr:
|
||||
return memsegment.virt_start_addr + fileoffset - memsegment.file_start_addr
|
||||
return None
|
||||
|
||||
def getbaseaddr(self, offset):
|
||||
for memsegment in self.memorylayout:
|
||||
if memsegment.virt_end_addr >= offset >= memsegment.virt_start_addr:
|
||||
return memsegment.virt_start_addr
|
||||
return None
|
||||
|
||||
class programentry:
|
||||
p_type = 0
|
||||
from_file = 0
|
||||
virt_addr = 0
|
||||
phy_addr = 0
|
||||
seg_file_len = 0
|
||||
seg_mem_len = 0
|
||||
p_flags = 0
|
||||
p_align = 0
|
||||
|
||||
def parse_programentry(self, dat):
|
||||
pe = self.programentry()
|
||||
if self.elfclass == 1:
|
||||
(pe.p_type, pe.from_file, pe.virt_addr, pe.phy_addr, pe.seg_file_len, pe.seg_mem_len, pe.p_flags,
|
||||
pe.p_align) = struct.unpack("<IIIIIIII", dat)
|
||||
elif self.elfclass == 2:
|
||||
(pe.p_type, pe.p_flags, pe.from_file, pe.virt_addr, pe.phy_addr, pe.seg_file_len, pe.seg_mem_len,
|
||||
pe.p_align) = struct.unpack("<IIQQQQQQ", dat)
|
||||
return pe
|
||||
|
||||
def parse(self):
|
||||
self.elfclass = self.data[4]
|
||||
if self.elfclass == 1: # 32Bit
|
||||
start = 0x28
|
||||
elif self.elfclass == 2: # 64Bit
|
||||
start = 0x34
|
||||
else:
|
||||
print("Error on parsing " + self.filename)
|
||||
return ['', '']
|
||||
elfheadersize, programheaderentrysize, programheaderentrycount = struct.unpack("<HHH",
|
||||
self.data[start:start + 3 * 2])
|
||||
programheadersize = programheaderentrysize * programheaderentrycount
|
||||
header = self.data[0:elfheadersize + programheadersize]
|
||||
pentry = []
|
||||
for i in range(0, programheaderentrycount):
|
||||
start = elfheadersize + (i * programheaderentrysize)
|
||||
end = start + programheaderentrysize
|
||||
pentry.append(self.parse_programentry(self.data[start:end]))
|
||||
|
||||
return [header, pentry]
|
||||
|
||||
|
||||
class patchtools:
|
||||
cstyle = False
|
||||
bDebug = False
|
||||
|
||||
def __init__(self, bdebug=False):
|
||||
self.bDebug = bdebug
|
||||
|
||||
def has_bad_uart_chars(self, data):
|
||||
badchars = [b'\x00', b'\n', b'\r', b'\x08', b'\x7f', b'\x20', b'\x09']
|
||||
for idx, c in enumerate(data):
|
||||
c = bytes([c])
|
||||
if c in badchars:
|
||||
return True
|
||||
return False
|
||||
|
||||
def generate_offset(self, offset):
|
||||
div = 0
|
||||
found = False
|
||||
while not found and div < 0x606:
|
||||
data = struct.pack("<I", offset + div)
|
||||
data2 = struct.pack("<H", div)
|
||||
badchars = self.has_bad_uart_chars(data)
|
||||
if not badchars:
|
||||
badchars = self.has_bad_uart_chars(data2)
|
||||
if not (badchars):
|
||||
return div
|
||||
div += 4
|
||||
|
||||
# if div is not found within positive offset, try negative offset
|
||||
div = 0
|
||||
while not found and div < 0x606:
|
||||
data = struct.pack("<I", offset - div)
|
||||
data2 = struct.pack("<H", div)
|
||||
badchars = self.has_bad_uart_chars(data)
|
||||
if not badchars:
|
||||
badchars = self.has_bad_uart_chars(data2)
|
||||
if not badchars:
|
||||
return -div
|
||||
div += 4
|
||||
return 0
|
||||
|
||||
# Usage: offset, "X24"
|
||||
def generate_offset_asm(self, offset, reg):
|
||||
div = self.generate_offset(offset)
|
||||
abase = ((offset + div) & 0xFFFF0000) >> 16
|
||||
a = ((offset + div) & 0xFFFF)
|
||||
strasm = ""
|
||||
if div > 0:
|
||||
strasm += "# " + hex(offset) + "\n"
|
||||
strasm += "mov " + reg + ", #" + hex(a) + ";\n"
|
||||
strasm += "movk " + reg + ", #" + hex(abase) + ", LSL#16;\n"
|
||||
strasm += "sub " + reg + ", " + reg + ", #" + hex(div) + ";\n"
|
||||
else:
|
||||
strasm += "# " + hex(offset) + "\n"
|
||||
strasm += "mov " + reg + ", #" + hex(a) + ";\n"
|
||||
strasm += "movk " + reg + ", #" + hex(abase) + ", LSL#16;\n"
|
||||
strasm += "add " + reg + ", " + reg + ", #" + hex(-div) + ";\n"
|
||||
return strasm
|
||||
|
||||
def uart_valid_sc(self, sc):
|
||||
badchars = [b'\x00', b'\n', b'\r', b'\x08', b'\x7f', b'\x20', b'\x09']
|
||||
for idx, c in enumerate(sc):
|
||||
c = bytes([c])
|
||||
if c in badchars:
|
||||
print("bad char 0x%s in SC at offset %d, opcode # %d!\n" % (codecs.encode(c, 'hex'), idx, idx / 4))
|
||||
print(codecs.encode(sc, 'hex'))
|
||||
return False
|
||||
return True
|
||||
|
||||
def disasm(self, code, size):
|
||||
cs = Cs(CS_ARCH_ARM64, CS_MODE_LITTLE_ENDIAN)
|
||||
instr = []
|
||||
for i in cs.disasm(code, size):
|
||||
# print("0x%x:\t%s\t%s" % (i.address, i.mnemonic, i.op_str))
|
||||
instr.append("%s\t%s" % (i.mnemonic, i.op_str))
|
||||
return instr
|
||||
|
||||
def assembler(self, code):
|
||||
ks = Ks(KS_ARCH_ARM64, KS_MODE_LITTLE_ENDIAN)
|
||||
if self.bDebug:
|
||||
try:
|
||||
encoding, count = ks.asm(code)
|
||||
except KsError as e:
|
||||
print(e)
|
||||
print(e.stat_count)
|
||||
print(code[e.stat_count:e.stat_count + 10])
|
||||
exit(0)
|
||||
if self.bDebug:
|
||||
# walk every line to find the (first) error
|
||||
for idx, line in enumerate(code.splitlines()):
|
||||
print("%02d: %s" % (idx, line))
|
||||
if len(line) and line[0] != '.':
|
||||
try:
|
||||
encoding, count = ks.asm(line)
|
||||
except Exception as e:
|
||||
print("bummer: " + str(e))
|
||||
else:
|
||||
exit(0)
|
||||
else:
|
||||
encoding, count = ks.asm(code)
|
||||
|
||||
sc = ""
|
||||
count = 0
|
||||
out = ""
|
||||
for i in encoding:
|
||||
if self.cstyle:
|
||||
out += ("\\x%02x" % i)
|
||||
else:
|
||||
out += ("%02x" % i)
|
||||
sc += "%02x" % i
|
||||
|
||||
count += 1
|
||||
# if bDebug and count % 4 == 0:
|
||||
# out += ("\n")
|
||||
|
||||
return out
|
||||
|
||||
def find_binary(self, data, strf, pos=0):
|
||||
t = strf.split(b".")
|
||||
pre = 0
|
||||
offsets = []
|
||||
while pre != -1:
|
||||
pre = data[pos:].find(t[0], pre)
|
||||
if pre == -1:
|
||||
if len(offsets) > 0:
|
||||
for offset in offsets:
|
||||
error = 0
|
||||
rt = offset + len(t[0])
|
||||
for i in range(1, len(t)):
|
||||
if t[i] == b'':
|
||||
rt += 1
|
||||
continue
|
||||
rt += 1
|
||||
prep = data[rt:].find(t[i])
|
||||
if (prep != 0):
|
||||
error = 1
|
||||
break
|
||||
rt += len(t[i])
|
||||
if error == 0:
|
||||
return offset
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
offsets.append(pre)
|
||||
pre += 1
|
||||
return None
|
||||
|
||||
|
||||
def read_object(data: object, definition: object) -> object:
|
||||
"""
|
||||
Unpacks a structure using the given data and definition.
|
||||
"""
|
||||
obj = {}
|
||||
object_size = 0
|
||||
pos = 0
|
||||
for (name, stype) in definition:
|
||||
object_size += struct.calcsize(stype)
|
||||
obj[name] = struct.unpack(stype, data[pos:pos + struct.calcsize(stype)])[0]
|
||||
pos += struct.calcsize(stype)
|
||||
obj['object_size'] = object_size
|
||||
obj['raw_data'] = data
|
||||
return obj
|
||||
|
||||
|
||||
def write_object(definition, *args):
|
||||
"""
|
||||
Unpacks a structure using the given data and definition.
|
||||
"""
|
||||
obj = {}
|
||||
object_size = 0
|
||||
data = b""
|
||||
i = 0
|
||||
for (name, stype) in definition:
|
||||
object_size += struct.calcsize(stype)
|
||||
arg = args[i]
|
||||
try:
|
||||
data += struct.pack(stype, arg)
|
||||
except Exception as e:
|
||||
print("Error:" + str(e))
|
||||
break
|
||||
i += 1
|
||||
obj['object_size'] = len(data)
|
||||
obj['raw_data'] = data
|
||||
return obj
|
||||
|
||||
|
||||
def print_progress(iteration, total, prefix='', suffix='', decimals=1, bar_length=100):
|
||||
"""
|
||||
Call in a loop to create terminal progress bar
|
||||
@params:
|
||||
iteration - Required : current iteration (Int)
|
||||
total - Required : total iterations (Int)
|
||||
prefix - Optional : prefix string (Str)
|
||||
suffix - Optional : suffix string (Str)
|
||||
decimals - Optional : positive number of decimals in percent complete (Int)
|
||||
bar_length - Optional : character length of bar (Int)
|
||||
"""
|
||||
str_format = "{0:." + str(decimals) + "f}"
|
||||
percents = str_format.format(100 * (iteration / float(total)))
|
||||
filled_length = int(round(bar_length * iteration / float(total)))
|
||||
bar = '█' * filled_length + '-' * (bar_length - filled_length)
|
||||
|
||||
sys.stdout.write('\r%s |%s| %s%s %s' % (prefix, bar, percents, '%', suffix))
|
||||
|
||||
if iteration == total:
|
||||
sys.stdout.write('\n')
|
||||
sys.stdout.flush()
|
36
edl/Library/xmlparser.py
Executable file
36
edl/Library/xmlparser.py
Executable file
|
@ -0,0 +1,36 @@
|
|||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) B.Kerler 2018-2021
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
|
||||
class xmlparser:
|
||||
def getresponse(self, input):
|
||||
lines = input.split(b"<?xml")
|
||||
content = {}
|
||||
for line in lines:
|
||||
if line == b'':
|
||||
continue
|
||||
line = b"<?xml" + line
|
||||
parser = ET.XMLParser(encoding="utf-8")
|
||||
tree = ET.fromstring(line, parser=parser)
|
||||
e = ET.ElementTree(tree).getroot()
|
||||
for atype in e.findall('response'):
|
||||
for field in atype.attrib:
|
||||
content[field] = atype.attrib[field]
|
||||
return content
|
||||
|
||||
def getlog(self, input):
|
||||
lines = input.split(b"<?xml")
|
||||
data = []
|
||||
for line in lines:
|
||||
if line == b'':
|
||||
continue
|
||||
line = b"<?xml" + line
|
||||
parser = ET.XMLParser(encoding="utf-8")
|
||||
tree = ET.fromstring(line, parser=parser)
|
||||
e = ET.ElementTree(tree).getroot()
|
||||
for atype in e.findall('log'):
|
||||
if 'value' in atype.attrib:
|
||||
data.append(atype.attrib['value'])
|
||||
return data
|
BIN
edl/Windows/libusb-1.0.dll
Executable file
BIN
edl/Windows/libusb-1.0.dll
Executable file
Binary file not shown.
0
edl/__init__.py
Executable file
0
edl/__init__.py
Executable file
Loading…
Reference in a new issue