Created
May 22, 2022 18:47
-
-
Save halmartin/be5854c910a8e1cc538a229755d1d154 to your computer and use it in GitHub Desktop.
Siglent SDS1000X-E license key recovery
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
import re | |
import string | |
import hashlib | |
# adapted from https://www.eevblog.com/forum/testgear/unlocking-siglent-sds1104x-e-step-by-step/msg1973306/ | |
def getkeys(scopeid, serialno, memdumpfile): | |
""" | |
Parse a memory dump from a Siglent 1000X-E oscilloscope and return a dict containing | |
license keys. The 'activebw' key is the one that is currently active in the 'scope | |
(e.g. if the value of '100M' is the same as the value of 'activebw', | |
the oscilloscope is software locked to 100 MHz bandwidth) | |
""" | |
if len(scopeid) == 16 and set(scopeid) <= set(string.hexdigits): | |
scopeid = scopeid.lower().encode('utf-8') | |
else: | |
raise ValueError('Scope ID must be 16 hexadecimal characters (remove dashes).') | |
if len(serialno) == 14 and set(serialno) <= set(string.ascii_letters + string.digits): | |
serialno = serialno.upper().encode('utf-8') | |
else: | |
raise ValueError('Serial number must be 14 alphanumeric characters.') | |
f = open(memdumpfile, 'rb') | |
data = f.read() | |
f.close() | |
regex_bw = re.compile(scopeid + b'.*?'+ scopeid + b'.*?([0-9A-Z]{16}).*?([0-9A-Z]{16}).*?([0-9A-Z]{16}).*?([0-9A-Z]{16}).*?([0-9A-Z]{16})', re.DOTALL) | |
key_bw = list([n.decode('utf-8') for n in re.findall(regex_bw, data)[0]]) | |
keys = {} | |
key_labels = ('100M', '200M', '50M', '70M', 'activebw') | |
keys.update(zip(key_labels, key_bw)) | |
return(keys) | |
# adapted from https://www.eevblog.com/forum/testgear/unlocking-siglent-sds1104x-e-step-by-step/msg3407174/ | |
def genopts(serial): | |
options = {} | |
for opt in ('AWG', 'WIFI', 'MSO'): | |
h = hashlib.md5(( | |
'5zao9lyua01pp7hjzm3orcq90mds63z6zi5kv7vmv3ih981vlwn06txnjdtas3u2' | |
'wa8msx61i12ueh14t7kqwsfskg032nhyuy1d9vv2wm925rd18kih9xhkyilobbgy' + | |
'SDS1000X-E\n'.ljust(32, '\x00') + | |
opt.ljust(5, '\x00') + | |
2*((serial + '\n').ljust(32, '\x00')) + | |
'\x00'*16).encode('ascii') | |
).digest() | |
key = '' | |
for b in h: | |
if (b <= 0x2F or b > 0x39) and (b <= 0x60 or b > 0x7A): | |
m = b % 0x24 | |
b = m + (0x57 if m > 9 else 0x30) | |
if b == 0x30: b = 0x32 | |
if b == 0x31: b = 0x33 | |
if b == 0x6c: b = 0x6d | |
if b == 0x6f: b = 0x70 | |
key += chr(b) | |
options[opt] = key.upper() | |
return options | |
if __name__ == "__main__": | |
import argparse | |
parser = argparse.ArgumentParser(description="Recover Siglent SDS1004X-E bandwidth/option licenses") | |
parser.add_argument("--sid", dest="scope_id", type=str, required=False, help="Scope ID") | |
parser.add_argument("--serial", dest="serial", type=str, required=True, help="Scope serial number") | |
parser.add_argument("--dump", dest="memdump", type=str, required=False, help="Path to the memory dump from the scope") | |
args = parser.parse_args() | |
keys = dict() | |
if args.scope_id and args.memdump: | |
keys.update(getkeys(args.scope_id, args.serial, args.memdump)) | |
keys.update(genopts(args.serial)) | |
print(keys) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment