mirror of
https://github.com/bkerler/edl.git
synced 2024-11-14 19:14:58 -05:00
687 lines
32 KiB
Python
Executable file
687 lines
32 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
# (c) B.Kerler 2019-2023 under GPLv3 license
|
|
# If you use my code, make sure you refer to my name
|
|
#
|
|
# !!!!! If you use this code in commercial products, your product is automatically
|
|
# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
|
|
import copy
|
|
import sys
|
|
import argparse
|
|
import time
|
|
import serial.tools.list_ports
|
|
from Exscript.protocols.telnetlib import Telnet
|
|
from binascii import hexlify, unhexlify
|
|
import logging
|
|
import logging.config
|
|
import logging.handlers
|
|
import colorama
|
|
|
|
|
|
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)
|
|
|
|
|
|
'''
|
|
C7 = 7 0 0 2 7 5 0
|
|
C6 = 3 1 7 0 3 0 1
|
|
C5 = 0 2 5 3 0 3 2
|
|
C8 = 1 3 3 1 5 7 3
|
|
C4 = 5 4 1 4 1 1 4
|
|
'''
|
|
prodtable = {
|
|
"MDM8200": dict(openlock=0, openmep=1, opencnd=0, clen=8, init=[1, 3, 5, 7, 0],
|
|
run="resultbuffer[i]=self.SierraAlgo(challenge[i], 2, 4, 1, 3, 0, 3, 4, 0)"),
|
|
# MC878XC_F1.2.3.15 verified, key may be stored in nvitem 0x4e21;MC8700 M3.0.9.0 verified
|
|
"MDM9200": dict(openlock=0, openmep=1, opencnd=0, clen=8, init=[7, 3, 0, 1, 5],
|
|
run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),
|
|
# AC881U, EM8805 SWI9X15C_05.05.58.00, at!openlock?6A1F1CEA298A14B0 => AT!OPENLOCK="3A9EA70D86FEE58C"
|
|
"MDM9200_V1": dict(openlock=2, openmep=1, opencnd=0, clen=8, init=[7, 3, 0, 1, 5],
|
|
run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), # AC710
|
|
"MDM9200_V2": dict(openlock=3, openmep=1, opencnd=0, clen=8, init=[7, 3, 0, 1, 5],
|
|
run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), # AC775
|
|
"MDM9200_V3": dict(openlock=8, openmep=1, opencnd=8, clen=8, init=[7, 3, 0, 1, 5],
|
|
run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), # AC775
|
|
"MDM9x15": dict(openlock=0, openmep=1, opencnd=0, clen=8, init=[7, 3, 0, 1, 5],
|
|
run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),
|
|
# 9x15C 06.03.32.02, AC340U 1.13.12.14 verified, #AT!CUSTOM=\"ADBENABLE\",1
|
|
"MDM9x07": dict(openlock=9, openmep=10, opencnd=9, clen=8, init=[7, 3, 0, 1, 5],
|
|
run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),
|
|
# SWI9X07Y_02.25.02.01
|
|
"MDM9x30": dict(openlock=5, openmep=4, opencnd=5, clen=8, init=[7, 3, 0, 1, 5],
|
|
run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),
|
|
# MC7455_2.30.01.01 #4
|
|
"MDM9x30_V1": dict(openlock=17, openmep=15, opencnd=17, clen=8, init=[7, 3, 0, 1, 5],
|
|
run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),
|
|
# AC791L/AC790S NTG9X35C_02.08.29.00
|
|
"MDM9x40": dict(openlock=11, openmep=12, opencnd=11, clen=8, init=[7, 3, 0, 1, 5],
|
|
run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), # AC815s
|
|
"MDM9x50": dict(openlock=7, openmep=6, opencnd=7, clen=8, init=[7, 3, 0, 1, 5],
|
|
run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), # EM7565
|
|
"MDM9x06": dict(openlock=20, openmep=19, opencnd=20, clen=8, init=[7, 3, 0, 1, 5],
|
|
run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"), # WP77xx
|
|
"SDX55": dict(openlock=22, openmep=21, opencnd=22, clen=8, init=[7, 3, 0, 1, 5], # MR5100, MR6400 old fw
|
|
run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),
|
|
"MDM9x15A": dict(openlock=24, openmep=23, opencnd=24, clen=8, init=[7, 3, 0, 1, 5], # AC779S
|
|
run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),
|
|
"SDX65": dict(openlock=25, openmep=21, opencnd=26, clen=8, init=[7, 3, 0, 1, 5], # MR6400 new fw
|
|
run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)")
|
|
}
|
|
|
|
infotable = {
|
|
"MDM8200": ["M81A", "M81B", "AC880", "AC881", "MC8780", "MC8781", "AC880E", "AC881E", "EM8780", "EM8781",
|
|
"MC8780V", "MC8781V", "MC8700", "AC308U"],
|
|
"MDM9200": ["AC710", "MC8775", "MC8775V", "AC875", "MC8700", "AC313U", "MC8801", "MC7700", "MC7750", "MC7710",
|
|
"EM7700", "770S", "781S"],
|
|
"MDM9200_V1": ["AC710", "MC8775", "MC8775V", "AC875", "MC8700", "AC313U", "MC8801", "MC7700", "MC7750",
|
|
"MC7710", "EM7700"],
|
|
"MDM9200_V2": ["AC775", "PC7200"],
|
|
"MDM9200_V3": ["AC775"],
|
|
"MDM9x07": ["SWI9X07Y", "WP76xx"],
|
|
"MDM9x06": ["SWI9X06Y", "WP77xx"],
|
|
"MDM9x15": ["SWI9X15C", "AR7550", "AR7552", "AR7554", "EM7355", "EM7655", "MC7354", "WP7100", "WP7102", "WP7104",
|
|
"MC7305", "EM7305", "MC8805", "EM8805", "MC7350", "MC7350-L", "MC7802", "MC7304", "AR7556", "AR7558",
|
|
"WP75xx", "WP85xx", "WP8548", "WP8548G", "AC340U"],
|
|
"MDM9x15A": ["AC779S"],
|
|
"MDM9x30": ["EM7455", "MC7455", "EM7430", "MC7430"],
|
|
"MDM9x30_V1": ["Netgear AC790/MDM9230"],
|
|
"MDM9x40": ["MR1100", "AC815s", "AC785s", "AC797S"],
|
|
"MDM9x50": ["EM7565", "EM7565-9", "EM7511", "EM7411"],
|
|
"SDX55": ["MR5100", "MR5200", "ac797-100eus", "MR6400"],
|
|
"SDX65": ["MR6400", "MR6500", "MR6110", "MR6150", "MR6450", "MR6550"]
|
|
}
|
|
|
|
# 0 MC8775_H2.0.8.19 !OPENLOCK, !OPENCND .. MC8765V,MC8765,MC8755V,MC8775,MC8775V,MC8775,AC850,
|
|
# AC860,AC875,AC881,AC881U,AC875, AC340U 1.13.12.14
|
|
keytable = bytearray([0xF0, 0x14, 0x55, 0x0D, 0x5E, 0xDA, 0x92, 0xB3, 0xA7, 0x6C, 0xCE, 0x84, 0x90, 0xBC, 0x7F, 0xED,
|
|
# 1 MC8775_H2.0.8.19 AC340U, OPENMEP default
|
|
0x61, 0x94, 0xCE, 0xA7, 0xB0, 0xEA, 0x4F, 0x0A, 0x73, 0xC5, 0xC3, 0xA6, 0x5E, 0xEC, 0x1C, 0xE2,
|
|
# 2 AC750,AC710,AC7XX,SB750A,SB750,PC7000,AC313u OPENMEP
|
|
0x39, 0xC6, 0x7B, 0x04, 0xCA, 0x50, 0x82, 0x1F, 0x19, 0x63, 0x36, 0xDE, 0x81, 0x49, 0xF0, 0xD7,
|
|
# 3 AC775,PC7200
|
|
0xDE, 0xA5, 0xAD, 0x2E, 0xBE, 0xE1, 0xC9, 0xEF, 0xCA, 0xF9, 0xFE, 0x1F, 0x17, 0xFE, 0xED, 0x3B,
|
|
# 4 MC7455_02.30.01.01 OPENMEP
|
|
0xFE, 0xD4, 0x40, 0x52, 0x2D, 0x4B, 0x12, 0x5C, 0xE7, 0x0D, 0xF8, 0x79, 0xF8, 0xC0, 0xDD, 0x37,
|
|
# 5 MC7455_02.30.01.01 OPENLOCK
|
|
0x3B, 0x18, 0x99, 0x6B, 0x57, 0x24, 0x0A, 0xD8, 0x94, 0x6F, 0x8E, 0xD9, 0x90, 0xBC, 0x67, 0x56,
|
|
# 6 SWI9x50 Openmep Key SWI9X50C_01.08.04.00
|
|
0x47, 0x4F, 0x4F, 0x44, 0x4A, 0x4F, 0x42, 0x44, 0x45, 0x43, 0x4F, 0x44, 0x49, 0x4E, 0x47, 0x2E,
|
|
# 7 SWI9x50 Openlock Key SWI9X50C_01.08.04.00
|
|
0x4F, 0x4D, 0x41, 0x52, 0x20, 0x44, 0x49, 0x44, 0x20, 0x54, 0x48, 0x49, 0x53, 0x2E, 0x2E, 0x2E,
|
|
# 8 MDM8200 Special
|
|
0x8F, 0xA5, 0x85, 0x05, 0x5E, 0xCF, 0x44, 0xA0, 0x98, 0x8B, 0x09, 0xE8, 0xBB, 0xC6, 0xF7, 0x65,
|
|
# 9 SWI9x07 Openlock Key 02.25.02.01
|
|
0x4D, 0x42, 0xD8, 0xC1, 0x25, 0x44, 0xD8, 0xA0, 0x1D, 0x80, 0xC4, 0x52, 0x8E, 0xEC, 0x8B, 0xE3,
|
|
# 10 SWI9x07 Openmep Key 02.25.02.01
|
|
0xED, 0xA9, 0xB7, 0x0A, 0xDB, 0x85, 0x3D, 0xC0, 0x92, 0x49, 0x7D, 0x41, 0x9A, 0x91, 0x09, 0xEE,
|
|
# 11 NTG9X40C_11.14.08.11 / mdm9x40r11_core AC815s / SWI9x50 MR1100 Openlock Key
|
|
0x8A, 0x56, 0x03, 0xF0, 0xBB, 0x9C, 0x13, 0xD2, 0x4E, 0xB2, 0x45, 0xAD, 0xC4, 0x0A, 0xE7, 0x52,
|
|
# 12 SWI9x50 MR1100 Openmep Key
|
|
0x2A, 0xEF, 0x07, 0x2B, 0x19, 0x60, 0xC9, 0x01, 0x8B, 0x87, 0xF2, 0x6E, 0xC1, 0x42, 0xA8, 0x3A,
|
|
# 13 SWI9x50/SWI9x65 Unknown key
|
|
0x28, 0x55, 0x48, 0x52, 0x24, 0x72, 0x63, 0x37, 0x14, 0x26, 0x37, 0x50, 0xBE, 0xFE, 0x00, 0x00,
|
|
# 14 SWI9x50,SWI9X06Y,SWI9x65 IMEI nv key
|
|
0x22, 0x63, 0x48, 0x02, 0x24, 0x72, 0x27, 0x37, 0x19, 0x26, 0x37, 0x50, 0xBE, 0xEF, 0xCA, 0xFE,
|
|
# 15 NTG9X35C_02.08.29.00 Openmep Key AC791L/AC790S Old
|
|
0x98, 0xE1, 0xC1, 0x93, 0xC3, 0xBF, 0xC3, 0x50, 0x8D, 0xA1, 0x35, 0xFE, 0x50, 0x47, 0xB3, 0xC4,
|
|
# 16 NTG9X35C_02.08.29.00 Openmep Key AC791/AC790S, NTGX55_10.25.15.02 MR5100 Alternative, NTG9X40C_30.00.12.00 Alternative
|
|
0x61, 0x94, 0xCE, 0xA7, 0xB0, 0xEA, 0x4F, 0x0A, 0x73, 0xC5, 0xC3, 0xA6, 0x5E, 0xEC, 0x1C, 0xE2,
|
|
# 17 NTG9X35C_02.08.29.00 Openlock Key AC791/AC790S Old
|
|
0xC5, 0x50, 0x40, 0xDA, 0x23, 0xE8, 0xF4, 0x4C, 0x29, 0xE9, 0x07, 0xDE, 0x24, 0xE5, 0x2C, 0x1D,
|
|
# 18 NTG9X35C_02.08.29.00 Openlock Key AC791/AC790S, NTGX55_10.25.15.02 MR5100 Alternative, NTG9X40C_30.00.12.00 Alternative
|
|
0xF0, 0x14, 0x55, 0x0D, 0x5E, 0xDA, 0x92, 0xB3, 0xA7, 0x6C, 0xCE, 0x84, 0x90, 0xBC, 0x7F, 0xED,
|
|
# 19 SWI9X06Y_02.14.04.00 Openmep Key WP77xx
|
|
0x78, 0x19, 0xC5, 0x6D, 0xC3, 0xD8, 0x25, 0x3E, 0x51, 0x60, 0x8C, 0xA7, 0x32, 0x83, 0x37, 0x9D,
|
|
# 20 SWI9X06Y_02.14.04.00 Openlock Key WP77xx
|
|
0x12, 0xF0, 0x79, 0x6B, 0x19, 0xC7, 0xF4, 0xEC, 0x50, 0xF3, 0x8C, 0x40, 0x02, 0xC9, 0x43, 0xC8,
|
|
# 21 NTGX55 Openmep Key, NTGX55_10.25.15.02 MR5100, NTG9X40C_30.00.12.00
|
|
0x49, 0x42, 0xFF, 0x76, 0x8A, 0x95, 0xCF, 0x7B, 0xA3, 0x47, 0x5F, 0xF5, 0x8F, 0xD8, 0x45, 0xE4,
|
|
# 22 NTGX55 Openlock Key, NTGX55_10.25.15.02 MR5100, NTG9X40C_30.00.12.00
|
|
0xF8, 0x1A, 0x3A, 0xCC, 0xAA, 0x2B, 0xA5, 0xE8, 0x8B, 0x53, 0x5A, 0x55, 0xB9, 0x65, 0x57, 0x98,
|
|
# 23 NTG9X15A Openlock Key, NTG9X15A_01.08.02.00
|
|
0x54, 0xC9, 0xC7, 0xA4, 0x02, 0x1C, 0xB0, 0x11, 0x05, 0x22, 0x39, 0xB7, 0x84, 0xEF, 0x16, 0xCA,
|
|
# 24 NTG9X15A Openlock Key, NTG9X15A_01.08.02.00
|
|
0xC7, 0xE6, 0x39, 0xFE, 0x0A, 0xC7, 0xCA, 0x4D, 0x49, 0x8F, 0xD8, 0x55, 0xEB, 0x1A, 0xCD, 0x8A,
|
|
# 25 NTGX65 Openlock Key, NTGX65_10.04.13.03
|
|
0xF2, 0x4A, 0x9A, 0x2C, 0xDA, 0x3D, 0xA5, 0xE2, 0x6B, 0x56, 0x9A, 0x45, 0x29, 0x25, 0x77, 0x9A,
|
|
# 26 NTGX65 Openadm Key, NTGX65_10.04.13.03
|
|
0x46, 0x30, 0x33, 0x43, 0x44, 0x36, 0x42, 0x34, 0x41, 0x32, 0x31, 0x32, 0x30, 0x35, 0x39, 0x37
|
|
])
|
|
|
|
|
|
class SierraGenerator:
|
|
tbl = bytearray()
|
|
rtbl = bytearray()
|
|
devicegeneration = None
|
|
|
|
def __init__(self):
|
|
for _ in range(0, 0x14):
|
|
self.rtbl.append(0x0)
|
|
for _ in range(0, 0x100):
|
|
self.tbl.append(0x0)
|
|
|
|
def run(self, devicegeneration, challenge, _type):
|
|
challenge = bytearray(unhexlify(challenge))
|
|
|
|
self.devicegeneration = devicegeneration
|
|
if not devicegeneration in prodtable:
|
|
print("Sorry, " + devicegeneration + " not supported.")
|
|
exit(0)
|
|
|
|
mepid = prodtable[devicegeneration]["openmep"]
|
|
cndid = prodtable[devicegeneration]["opencnd"]
|
|
lockid = prodtable[devicegeneration]["openlock"]
|
|
clen = prodtable[devicegeneration]["clen"]
|
|
if len(challenge) < clen:
|
|
challenge = [0 for _ in range(0, clen - len(challenge))]
|
|
|
|
challengelen = len(challenge)
|
|
if _type == 0: # lockkey
|
|
idf = lockid
|
|
elif _type == 1: # mepkey
|
|
idf = mepid
|
|
elif _type == 2: # cndkey
|
|
idf = cndid
|
|
|
|
key = keytable[idf * 16:(idf * 16) + 16]
|
|
resp = self.SierraKeygen(challenge=challenge, key=key, challengelen=challengelen, keylen=16)[:challengelen]
|
|
resp = hexlify(resp).decode('utf-8').upper()
|
|
return resp
|
|
|
|
def selftest(self):
|
|
test_table = [
|
|
{"challenge": "8101A18AB3C3E66A", "devicegeneration": "MDM9x15", "response": "D1E128FCA8A963ED"},
|
|
{"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9x40", "response": "1033773720F6EE66"},
|
|
{"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9x30", "response": "1E02CE6A98B7DD2A"},
|
|
{"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9x50", "response": "32AB617DB4B1C205"},
|
|
{"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9x06", "response": "28D718CCD669DEDE"},
|
|
{"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9x07", "response": "F5A4C9A0D402E34E"},
|
|
{"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM8200", "response": "EE702212D9C12FAB"},
|
|
{"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9200_V1", "response": "A9A4E76E2653F753"},
|
|
{"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9200_V2", "response": "8B0FAB4B6F81B080"},
|
|
{"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9200_V3", "response": "4A69AD8A69F390E0"},
|
|
{"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9x30_V1", "response": "6A5E4C9CBCBDA7DC"},
|
|
{"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9200", "response": "EEDBF8BFF8DAE346"},
|
|
{"challenge": "20E253156762DACE", "devicegeneration": "SDX55", "response": "03940D7067145323"},
|
|
{"challenge": "2387885E7D290FEE", "devicegeneration": "MDM9x15A", "response": "DC3E51897BAA9C1E"},
|
|
{"challenge": "4B1FEF9FD43C6DAA", "devicegeneration": "SDX65", "response": "1253C1B1E447B697"}
|
|
]
|
|
for test in test_table:
|
|
challenge = test["challenge"]
|
|
devicegeneration = test["devicegeneration"]
|
|
response = test["response"]
|
|
openlock = self.run(devicegeneration, challenge, 0)
|
|
padding = " " * (16 - len(devicegeneration))
|
|
if openlock != response:
|
|
print(devicegeneration + padding + " FAILED!")
|
|
else:
|
|
print(devicegeneration + padding + " PASSED :)")
|
|
|
|
def SierraPreInit(self, counter, key, keylen, challengelen, mcount):
|
|
if counter != 0:
|
|
tmp2 = 0
|
|
i = 1
|
|
while i < counter:
|
|
i = 2 * i + 1
|
|
while True:
|
|
tmp = mcount
|
|
mcount = tmp + 1
|
|
challengelen = (key[tmp & 0xFF] + self.tbl[(challengelen & 0xFF)]) & 0xFF
|
|
if mcount >= keylen:
|
|
mcount = 0
|
|
challengelen = ((challengelen & 0xFF) + keylen) & 0xFF
|
|
tmp2 = tmp2 + 1
|
|
tmp3 = ((challengelen & 0xFF) & i) & 0xFF
|
|
if tmp2 >= 0xB:
|
|
tmp3 = counter % tmp3
|
|
if tmp3 <= counter:
|
|
break
|
|
counter = tmp3 & 0xFF
|
|
return [counter, challengelen, mcount]
|
|
|
|
def SierraInit(self, key, keylen):
|
|
if keylen == 0 or keylen > 0x20:
|
|
retval = [0, keylen]
|
|
elif 1 <= keylen <= 0x20:
|
|
self.tbl = [(i & 0xFF) for i in range(0, 0x100)]
|
|
mcount = 0
|
|
cl = keylen & 0xffffff00
|
|
i = 0xFF
|
|
while i > -1:
|
|
t, cl, mcount = self.SierraPreInit(i, key, keylen, cl, mcount)
|
|
m = self.tbl[i]
|
|
self.tbl[i] = self.tbl[(t & 0xff)]
|
|
i = i - 1
|
|
self.tbl[(t & 0xFF)] = m
|
|
self.rtbl[0] = self.tbl[prodtable[self.devicegeneration]["init"][0]] if \
|
|
prodtable[self.devicegeneration]["init"][0] != 0 else self.tbl[(cl & 0xFF)]
|
|
self.rtbl[1] = self.tbl[prodtable[self.devicegeneration]["init"][1]] if \
|
|
prodtable[self.devicegeneration]["init"][1] != 0 else self.tbl[(cl & 0xFF)]
|
|
self.rtbl[2] = self.tbl[prodtable[self.devicegeneration]["init"][2]] if \
|
|
prodtable[self.devicegeneration]["init"][2] != 0 else self.tbl[(cl & 0xFF)]
|
|
self.rtbl[3] = self.tbl[prodtable[self.devicegeneration]["init"][3]] if \
|
|
prodtable[self.devicegeneration]["init"][3] != 0 else self.tbl[(cl & 0xFF)]
|
|
self.rtbl[4] = self.tbl[prodtable[self.devicegeneration]["init"][4]] if \
|
|
prodtable[self.devicegeneration]["init"][4] != 0 else self.tbl[(cl & 0xFF)]
|
|
retval = [1, keylen]
|
|
return retval
|
|
|
|
def sierra_calc8F(self, challenge, a=0, b=1, c=2, d=3, e=4, ret=0, ret2=2):
|
|
# MDM9200
|
|
self.rtbl[b] = (self.rtbl[b] + self.tbl[(self.rtbl[d] & 0xFF)]) & 0xFF
|
|
uVar2 = self.rtbl[c] & 0xFF
|
|
bVar1 = self.tbl[uVar2]
|
|
uVar4 = self.rtbl[b] & 0xFF
|
|
self.tbl[uVar2] = self.tbl[uVar4]
|
|
self.rtbl[d] = (self.rtbl[d] + 1) & 0xFF
|
|
uVar5 = self.rtbl[a] & 0xFF
|
|
self.tbl[uVar4] = self.tbl[uVar5]
|
|
uVar3 = self.rtbl[d] & 0xFF
|
|
self.tbl[uVar5] = self.tbl[uVar3]
|
|
self.tbl[uVar3] = bVar1
|
|
self.rtbl[ret] = challenge # c
|
|
self.rtbl[ret2] = self.tbl[self.tbl[(self.tbl[(self.rtbl[e] + self.tbl[bVar1]) & 0xFF] + (
|
|
self.tbl[uVar5] & 0xFF) + (self.tbl[uVar2] & 0xFF) & 0xff) & 0xFF] & 0xFF] ^ self.tbl[
|
|
((self.tbl[uVar4] & 0xFF) + (bVar1 & 0xff)) & 0xFF] ^ challenge # a
|
|
self.rtbl[e] = (self.rtbl[e] + self.tbl[bVar1]) & 0xFF
|
|
return self.rtbl[ret2] & 0xFF # a
|
|
|
|
def SierraAlgo(self, challenge, a=0, b=1, c=2, d=3, e=4, ret=3, ret2=1, flag=1): # M9x15
|
|
v6 = self.rtbl[e]
|
|
v0 = (v6 + 1) & 0xFF
|
|
self.rtbl[e] = v0
|
|
self.rtbl[c] = (self.tbl[v6 + flag & 0xFF] + self.rtbl[c]) & 0xFF
|
|
v4 = self.rtbl[c] & 0xFF
|
|
v2 = self.rtbl[b] & 0xFF
|
|
v1 = self.tbl[(v2 & 0xFF)]
|
|
self.tbl[(v2 & 0xFF)] = self.tbl[(v4 & 0xFF)]
|
|
v5 = self.rtbl[d] & 0xFF
|
|
self.tbl[(v4 & 0xFF)] = self.tbl[(v5 & 0xFF)]
|
|
self.tbl[(v5 & 0xFF)] = self.tbl[(v0 & 0xFF)]
|
|
self.tbl[v0] = v1 & 0xFF
|
|
u = self.tbl[(self.tbl[(
|
|
self.tbl[((self.rtbl[a] + self.tbl[(v1 & 0xFF)]) & 0xFF)] + self.tbl[(v5 & 0xFF)] + self.tbl[
|
|
(v2 & 0xFF)] & 0xff)] & 0xFF)]
|
|
v = self.tbl[((self.tbl[(v4 & 0xFF)] + v1) & 0xFF)]
|
|
self.rtbl[ret] = u ^ v ^ challenge
|
|
self.rtbl[a] = (self.tbl[(v1 & 0xFF)] + self.rtbl[a]) & 0xFF
|
|
self.rtbl[ret2] = challenge & 0xFF
|
|
return self.rtbl[ret] & 0xFF
|
|
|
|
def SierraFinish(self):
|
|
self.tbl = [0 for _ in range(0, 0x100)]
|
|
self.rtbl[0] = 0
|
|
self.rtbl[1] = 0
|
|
self.rtbl[2] = 0
|
|
self.rtbl[3] = 0
|
|
self.rtbl[4] = 0
|
|
return 1
|
|
|
|
def SierraKeygen(self, challenge: bytearray, key: bytearray, challengelen: int, keylen: int):
|
|
challenge = challenge
|
|
resultbuffer = bytearray([0 for _ in range(0, 0x100 + 1)])
|
|
ret, keylen = self.SierraInit(key, keylen)
|
|
if ret:
|
|
for i in range(0, challengelen):
|
|
exec(prodtable[self.devicegeneration]["run"]) # uses challenge
|
|
self.SierraFinish()
|
|
return resultbuffer
|
|
|
|
|
|
class connection:
|
|
def __init__(self, port="", ip="192.168.1.1"):
|
|
self.serial = None
|
|
self.tn = None
|
|
self.connected = False
|
|
if port == "":
|
|
port = self.detect(port)
|
|
if port == "":
|
|
self.tn = Telnet(ip, 5510)
|
|
if self.tn:
|
|
self.connected = True
|
|
else:
|
|
self.connected = False
|
|
if port != "":
|
|
self.serial = serial.Serial(port=port, baudrate=115200, bytesize=8, parity='N', stopbits=1, timeout=1)
|
|
self.connected = self.serial.is_open
|
|
|
|
def detect(self, port):
|
|
if port == "":
|
|
for port in serial.tools.list_ports.comports():
|
|
if port.vid == 0x1199:
|
|
portid = port.location[-1:]
|
|
if int(portid) == 3:
|
|
print("Detected Sierra Wireless device at: " + port.device)
|
|
return port.device
|
|
elif port.vid == 0x8046:
|
|
portid = port.location[-1:]
|
|
if int(portid) == 3:
|
|
print("Detected Netgear device at: " + port.device)
|
|
return port.device
|
|
|
|
return ""
|
|
|
|
def readreply(self):
|
|
info = []
|
|
if self.serial is not None:
|
|
while True:
|
|
tmp = self.serial.readline().decode('utf-8').replace('\r', '').replace('\n', '')
|
|
if "OK" in info:
|
|
return info
|
|
elif ("ERROR" in info) or info == "":
|
|
return -1
|
|
info.append(tmp)
|
|
return info
|
|
|
|
def send(self, cmd):
|
|
if self.tn is not None:
|
|
self.tn.write(bytes(cmd + "\r", 'utf-8'))
|
|
time.sleep(0.05)
|
|
data = ""
|
|
while True:
|
|
tmp = self.tn.read_eager()
|
|
if tmp != "":
|
|
data += tmp.strip()
|
|
else:
|
|
break
|
|
return data.split("\r\n")
|
|
elif self.serial is not None:
|
|
self.serial.write(bytes(cmd + "\r", 'utf-8'))
|
|
time.sleep(0.05)
|
|
return self.readreply()
|
|
|
|
def close(self):
|
|
if self.tn is not None:
|
|
self.tn.close()
|
|
self.connected = False
|
|
if self.serial is not None:
|
|
self.serial.close()
|
|
self.connected = False
|
|
|
|
|
|
class SierraKeygen(metaclass=LogBase):
|
|
def __init__(self, cn, devicegeneration=None):
|
|
self.cn = cn
|
|
self.keygen = SierraGenerator()
|
|
if devicegeneration is None:
|
|
self.detectdevicegeneration()
|
|
else:
|
|
self.devicegeneration = devicegeneration
|
|
|
|
def run_selftest(self):
|
|
print("Running self-test ...")
|
|
self.keygen.selftest()
|
|
|
|
def detectdevicegeneration(self):
|
|
if self.cn.connected:
|
|
info = self.cn.send("ATI")
|
|
if info != -1:
|
|
revision = ""
|
|
_model = ""
|
|
for line in info:
|
|
if "Revision" in line:
|
|
revision = line.split(":")[1].strip()
|
|
if "Model" in line:
|
|
_model = line.split(":")[1].strip()
|
|
if revision != "":
|
|
if "9200" in revision:
|
|
devicegeneration = "MDM9200" # AC762S NTG9200H2_03.05.14.12ap
|
|
if "9X07" in revision:
|
|
devicegeneration = "MDM9x07"
|
|
elif "9X25" in revision:
|
|
if "NTG9X25C" in revision:
|
|
devicegeneration = "MDM9200" # AC781S NTG9X25C_01.00.57.00
|
|
elif "9X15" in revision:
|
|
if "NTG9X15A" in revision:
|
|
devicegeneration = "MDM9x15A" # Aircard 779S
|
|
elif "NTG9X15C" in revision:
|
|
devicegeneration = "MDM9200" # AC770S NTG9X15C_01.18.02.00
|
|
elif "9X15A" in revision:
|
|
devicegeneration = "MDM9x15A"
|
|
else:
|
|
devicegeneration = "MDM9x15"
|
|
elif "9X30" in revision:
|
|
if "NTG9X35C" in revision: # 790S NTG9X35C_11.11.15.03
|
|
devicegeneration = "MDM9x30_V1"
|
|
else:
|
|
devicegeneration = "MDM9x30"
|
|
elif "9X40" in revision and "9X40C" not in revision:
|
|
devicegeneration = "MDM9x40"
|
|
elif "9X50" in revision:
|
|
if "NTG9X50" in revision:
|
|
devicegeneration = "MDM9x40" # MR1100,AC797S NTG9X50C_12.06.03.00
|
|
else:
|
|
devicegeneration = "MDM9x50"
|
|
elif "9X06" in revision:
|
|
devicegeneration = "MDM9x06"
|
|
elif "X55" in revision or "9X40C" in revision:
|
|
if "NTGX55" in revision: # MR5100 NTGX55_10.25.15.02, MR5200 NTGX55_12.04.12.00
|
|
devicegeneration = "SDX55"
|
|
devicegeneration = "SDX55"
|
|
elif "X65" in revision:
|
|
if "NTGX65" in revision:
|
|
version = revision[revision.find("_") + 1:revision.find(" ")].split(".")
|
|
maj = int(version[0]) * 1000000 + int(version[1]) * 10000 + int(version[2]) * 100 + int(
|
|
version[3])
|
|
if maj < 10041303:
|
|
devicegeneration = "SDX55"
|
|
else: # MR6400 NTGX65_10.04.13.03
|
|
devicegeneration = "SDX65"
|
|
# MR6550 NTGX65_12.01.31.00
|
|
devicegeneration = "SDX65"
|
|
else:
|
|
devicegeneration = ""
|
|
# Missing:
|
|
# SDX24 Sierra
|
|
# MR2100 NTGX24_10.17.03.00
|
|
# SDX55 Sierra
|
|
# AC810S NTG9X40C_11.14.08.16
|
|
# AC800S NTG9X40C_11.14.07.00
|
|
self.devicegeneration = devicegeneration
|
|
else:
|
|
print("Error on getting ATI modem response. Wrong port? Aborting.")
|
|
self.cn.close()
|
|
exit(0)
|
|
|
|
def openlock(self):
|
|
print("Device generation detected: " + self.devicegeneration)
|
|
# print("Sending AT!ENTERCND=\"A710\" request.")
|
|
# info = self.cn.send("AT!ENTERCND=\"A710\"")
|
|
# if info == -1:
|
|
# print("Uhoh ... invalid entercnd password. Aborting ...")
|
|
# return
|
|
print("Sending AT!OPENLOCK? request")
|
|
info = self.cn.send("AT!OPENLOCK?")
|
|
challenge = ""
|
|
if info != -1:
|
|
if len(info) > 2:
|
|
challenge = info[1]
|
|
print("Received challenge: " + info[1])
|
|
else:
|
|
print("Error on AT!OPENLOCK? request. Aborting.")
|
|
return False
|
|
if challenge == "":
|
|
print("Error: Couldn't get challenge. Aborting.")
|
|
return False
|
|
resp = self.keygen.run(self.devicegeneration, challenge, 0)
|
|
print("Sending AT!OPENLOCK=\"" + resp + "\" response.")
|
|
info = self.cn.send("AT!OPENLOCK=\"" + resp + "\"")
|
|
if info == -1:
|
|
print("Damn. AT!OPENLOCK failed.")
|
|
else:
|
|
print("Success. Device is now engineer unlocked.")
|
|
return True
|
|
return False
|
|
|
|
|
|
def main(args):
|
|
version = "1.5"
|
|
info = 'Sierra Wireless Generator ' + version + ' (c) B. Kerler 2019-2021'
|
|
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description=info)
|
|
|
|
parser.add_argument(
|
|
'-openlock', '-l',
|
|
help='AT!OPENLOCK? modem response',
|
|
default="")
|
|
|
|
parser.add_argument(
|
|
'-openmep', '-m',
|
|
help='AT!OPENMEP? modem response',
|
|
default="")
|
|
|
|
parser.add_argument(
|
|
'-opencnd', '-c',
|
|
help='AT!OPENCND? modem response',
|
|
default="")
|
|
|
|
parser.add_argument(
|
|
'-devicegeneration', '-d',
|
|
help='device generation',
|
|
default="")
|
|
|
|
parser.add_argument(
|
|
'-port', '-p',
|
|
help='use com port for auto unlock',
|
|
default="")
|
|
|
|
parser.add_argument(
|
|
'-ip',
|
|
help='ip port for unlock, usually 192.168.1.1 or 192.168.2.1',
|
|
default="192.168.1.1")
|
|
|
|
parser.add_argument(
|
|
'-unlock', '-u',
|
|
help='use com port for openlock',
|
|
default=False, action='store_true')
|
|
|
|
parser.add_argument(
|
|
'-selftest', '-s',
|
|
help='run selftest',
|
|
default=False, action='store_true')
|
|
|
|
args = parser.parse_args()
|
|
|
|
openlock = args.openlock
|
|
openmep = args.openmep
|
|
opencnd = args.opencnd
|
|
devicegeneration = args.devicegeneration
|
|
|
|
if not args.selftest:
|
|
if (devicegeneration == "" or (openlock == "" and openmep == "" and opencnd == "")) and not args.unlock:
|
|
print(info)
|
|
print("------------------------------------------------------------\n")
|
|
print("Usage: ./sierrakeygen.py [-l,-m,-c] [challenge] -d [devicegeneration]")
|
|
print("Example: ./sierrakeygen.py -l BE96CBBEE0829BCA -d MDM9200")
|
|
print("or: ./sierrakeygen.py -u for auto unlock")
|
|
print("or: ./sierrakeygen.py -u -p [portname] for auto unlock with given portname")
|
|
print("or: ./sierrakeygen.py -s for self-test")
|
|
print("Supported devicegenerations :")
|
|
for key in infotable:
|
|
info = f"\t{key}:\t\t"
|
|
count = 0
|
|
for item in infotable[key]:
|
|
count += 1
|
|
if count > 15:
|
|
info += "\n\t\t\t\t\t"
|
|
count = 0
|
|
info += item + ","
|
|
|
|
info = info[:-1]
|
|
print(info)
|
|
exit(0)
|
|
|
|
if devicegeneration == "" and not args.unlock:
|
|
print("You need to specific a device generation as well. Option -d")
|
|
exit(0)
|
|
if devicegeneration == "":
|
|
devicegeneration = None
|
|
if args.selftest:
|
|
kg = SierraKeygen(None, "selftest")
|
|
kg.run_selftest()
|
|
elif args.unlock:
|
|
cn = connection(args.port, args.ip)
|
|
if cn.connected:
|
|
kg = SierraKeygen(cn, devicegeneration)
|
|
if kg.devicegeneration == "":
|
|
print("Unknown device generation. Please send me details :)")
|
|
else:
|
|
kg.openlock()
|
|
cn.close()
|
|
else:
|
|
kg = SierraKeygen(None, devicegeneration)
|
|
if openlock != "":
|
|
resp = kg.keygen.run(devicegeneration, openlock, 0)
|
|
print("AT!OPENLOCK=\"" + resp + "\"")
|
|
elif openmep != "":
|
|
resp = kg.keygen.run(devicegeneration, openmep, 1)
|
|
print("AT!OPENMEP=\"" + resp + "\"")
|
|
elif opencnd != "":
|
|
resp = kg.keygen.run(devicegeneration, opencnd, 2)
|
|
print("AT!OPENCND=\"" + resp + "\"")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main(sys.argv)
|