Skip to content

Instantly share code, notes, and snippets.

@Leo-PL
Last active January 25, 2024 19:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Leo-PL/051a0b2ba26c3495afa47746e4a809cd to your computer and use it in GitHub Desktop.
Save Leo-PL/051a0b2ba26c3495afa47746e4a809cd to your computer and use it in GitHub Desktop.
Delta update finder for ZTE MF283+, MF286, MF286A and MF286D routers
#!/usr/bin/python3
# Original script by frutis, mods by Leo-PL
# Usage:
# 1. uncomment proper section for your router (MF283+, MF286, MF286A, MF286D).
# 2. set correct IME if you have one
# 3. Set correct version ID (the integrate_version from NVRAM) for your device if not already found here
# 4. Run the script - it shall return the URL of data update, if available for the set base version.
from base64 import b64decode, b64encode
from hashlib import md5
from secrets import choice
from string import printable, digits
import requests
from xml.dom.minidom import parseString
#import logging
#import http.client as http_client
#http_client.HTTPConnection.debuglevel = 1
#logging.basicConfig()
#logging.getLogger().setLevel(logging.DEBUG)
#requests_log = logging.getLogger('requests.packages.urllib3')
#requests_log.setLevel(logging.DEBUG)
#requests_log.propagate = True
# MF286D
#model = 'MF286D'
#TELIA
#firmware = 'TELIA_MF286DV1.0.0B10'
#ELISA
#firmware = 'ELISA_FI_MF286DV1.0.0B03'
#TIM
#firmware = 'TIM_IT_MF286DV1.0.0B07'
#PLAY
#firmware = 'NTT_PL_MF286DV1.0.0B02'
#imei_start = '86675404'
#URI = 'https://dmeu.ztems.com/zxmdmp/dm'
# MF286A
#model = 'MF286'
# Tele2 LT
#firmware = 'TELE2_LT_QMF286AV1.0.0B01'
#imei_start = '86821902'
#URI = 'https://dmeu.ztems.com/zxmdmp/dm'
# MF286
#model = 'MF286'
# EN_EEU (Orange PL)
#firmware = 'EN_EEU_MF286V1.0.1B01'
#imei_start = '86821902'
#URI = 'http://dms.ztems.com/zxmdmp/dm'
# MF283+
model = 'MF283PLUS'
firmware = 'EN_ORA_PL_MF283+V1.0.0B12'
#firmware = 'EN_ORA_PL_MF283+V1.0.0B06'
#firmware = 'EN_PL_MF283PLUSV1.0.0B02'
imei_start = '864780021'
URI = 'http://dms.ztems.com/zxmdmp/dm'
#request_type = 'devicerequest'
request_type = 'userrequest'
username = 'ZTEDM'
password = 'ZTEDM'
def imei_get(number):
while len(number) < 14:
number += choice(digits)
alphabet='0123456789'
imei = number + alphabet[0]
n = len(alphabet)
imei = tuple(alphabet.index(i)
for i in reversed(str(imei)))
check_digit = (sum(imei[::2]) +
sum(sum(divmod(i * 2, n))
for i in imei[1::2])) % n
number += alphabet[-check_digit]
return number
def nonce_get():
nonce = ''.join((choice(printable) for x in range(16)))
return b64encode(nonce.encode('utf-8')).decode('utf-8')
def hmac_get(nonce, body, username, password, mark=':'):
auth = username + mark + password
mark_enc = mark.encode('utf-8')
auth_b64 = b64encode(md5(auth.encode('utf-8')).digest())
nonce_md5 = b64decode(nonce)
body_b64 = b64encode(md5(body.encode('utf-8')).digest())
hmac = auth_b64 + mark_enc + nonce_md5 + mark_enc + body_b64
return b64encode(md5(hmac).digest()).decode('utf-8')
def body_get(body):
xml = ''
for line in body.splitlines():
xml += line.strip()
return xml
imei = imei_get(imei_start)
print(f'Model: {model}')
print(f'Firmware: {firmware}')
print(f'IMEI: {imei}')
try:
body1 = f'''<?xml version="1.0" encoding="UTF-8"?>
<SyncML xmlns='SYNCML:SYNCML1.2'>
<SyncHdr>
<VerDTD>1.2</VerDTD>
<VerProto>DM/1.2</VerProto>
<SessionID>3E8</SessionID>
<MsgID>1</MsgID>
<Target>
<LocURI>{URI}</LocURI>
</Target>
<Source>
<LocURI>IMEI:{imei}</LocURI>
</Source>
<Meta>
<MaxMsgSize xmlns='syncml:metinf'>5000</MaxMsgSize>
</Meta>
</SyncHdr>
<SyncBody>
<Alert>
<CmdID>1</CmdID>
<Data>1226</Data>
<Item>
<Meta>
<Format xmlns='syncml:metinf'>int</Format>
<Type xmlns='syncml:metinf'>org.openmobilealliance.dm.firmwareupdate.{request_type}</Type>
<Mark xmlns='syncml:metinf'>indeterminate</Mark>
</Meta>
<Data>0</Data>
</Item>
</Alert>
<Alert>
<CmdID>2</CmdID>
<Data>1201</Data>
</Alert>
<Replace>
<CmdID>3</CmdID>
<Item>
<Source>
<LocURI>./DevInfo/Ext/Correlator</LocURI>
</Source>
<Meta>
<Format xmlns='syncml:metinf'>chr</Format>
</Meta>
<Data>0</Data>
</Item>
<Item>
<Source>
<LocURI>./DevInfo/Ext/ErrCode</LocURI>
</Source>
<Meta>
<Format xmlns='syncml:metinf'>int</Format>
</Meta>
<Data>0</Data>
</Item>
<Item>
<Source>
<LocURI>./DevInfo/Ext/InnerV</LocURI>
</Source>
<Meta>
<Format xmlns='syncml:metinf'>chr</Format>
</Meta>
<Data></Data>
</Item>
<Item>
<Source>
<LocURI>./DevInfo/Ext/CompileT</LocURI>
</Source>
<Meta>
<Format xmlns='syncml:metinf'>chr</Format>
</Meta>
<Data></Data>
</Item>
<Item>
<Source>
<LocURI>./DevInfo/Ext</LocURI>
</Source>
<Meta>
<Format xmlns='syncml:metinf'>node</Format>
</Meta>
<Data>CompileT/InnerV/ErrCode/Correlator</Data>
</Item>
<Item>
<Source>
<LocURI>./DevInfo/DmV</LocURI>
</Source>
<Meta>
<Format xmlns='syncml:metinf'>chr</Format>
</Meta>
<Data>1.2</Data>
</Item>
<Item>
<Source>
<LocURI>./DevInfo/Lang</LocURI>
</Source>
<Meta>
<Format xmlns='syncml:metinf'>chr</Format>
</Meta>
<Data>en</Data>
</Item>
<Item>
<Source>
<LocURI>./DevInfo/DevId</LocURI>
</Source>
<Meta>
<Format xmlns='syncml:metinf'>chr</Format>
</Meta>
<Data>IMEI:{imei}</Data>
</Item>
<Item>
<Source>
<LocURI>./DevInfo/Man</LocURI>
</Source>
<Meta>
<Format xmlns='syncml:metinf'>chr</Format>
</Meta>
<Data>ZTE</Data>
</Item>
<Item>
<Source>
<LocURI>./DevInfo/Mod</LocURI>
</Source>
<Meta>
<Format xmlns='syncml:metinf'>chr</Format>
</Meta>
<Data>{model}</Data>
</Item>
</Replace>
<Final/>
</SyncBody>
</SyncML>'''
body = body_get(body1)
hmac = hmac_get(nonce_get(), body, username, password)
headers = {'Accept-Charset': 'utf-8',
'Accept-Language': 'en',
'Connection': 'close',
'Cache-Control': 'no-cache',
'Content-Type': 'application/vnd.syncml.dm+xml',
'x-syncml-hmac': f'algorithm=MD5, username="{username}", mac={hmac}',
'User-Agent': None,
'Accept-Encoding': None}
response = requests.post(URI, data=body, headers=headers)
response.raise_for_status()
resp = parseString(response.text)
resp_uri = resp.getElementsByTagName('RespURI')[0].firstChild.nodeValue.replace('&amp;', '&')
nonce = resp.getElementsByTagName('NextNonce')[0].firstChild.nodeValue
body2 = f'''<?xml version="1.0" encoding="UTF-8"?>
<SyncML xmlns='SYNCML:SYNCML1.2'>
<SyncHdr>
<VerDTD>1.2</VerDTD>
<VerProto>DM/1.2</VerProto>
<SessionID>3E8</SessionID>
<MsgID>2</MsgID>
<Target>
<LocURI><![CDATA[{resp_uri}]]></LocURI>
</Target>
<Source>
<LocURI>IMEI:{imei}</LocURI>
</Source>
<Meta>
<MaxMsgSize xmlns='syncml:metinf'>5000</MaxMsgSize>
</Meta>
</SyncHdr>
<SyncBody>
<Status>
<CmdID>1</CmdID>
<MsgRef>1</MsgRef>
<CmdRef>0</CmdRef>
<Cmd>SyncHdr</Cmd>
<TargetRef>IMEI:{imei}</TargetRef>
<SourceRef>{URI}</SourceRef>
<Chal>
<Meta>
<Format xmlns='syncml:metinf'>b64</Format>
<Type xmlns='syncml:metinf'>syncml:auth-MAC</Type>
<NextNonce xmlns='syncml:metinf'>{nonce_get()}</NextNonce>
</Meta>
</Chal>
<Data>401</Data>
</Status>
<Alert>
<CmdID>2</CmdID>
<Data>1226</Data>
<Item>
<Meta>
<Format xmlns='syncml:metinf'>int</Format>
<Type xmlns='syncml:metinf'>org.openmobilealliance.dm.firmwareupdate.{request_type}</Type>
<Mark xmlns='syncml:metinf'>indeterminate</Mark>
</Meta>
<Data>0</Data>
</Item>
</Alert>
<Alert>
<CmdID>3</CmdID>
<Data>1201</Data>
</Alert>
<Replace>
<CmdID>4</CmdID>
<Item>
<Source>
<LocURI>./DevInfo/Ext/Correlator</LocURI>
</Source>
<Meta>
<Format xmlns='syncml:metinf'>chr</Format>
</Meta>
<Data>0</Data>
</Item>
<Item>
<Source>
<LocURI>./DevInfo/Ext/ErrCode</LocURI>
</Source>
<Meta>
<Format xmlns='syncml:metinf'>int</Format>
</Meta>
<Data>0</Data>
</Item>
<Item>
<Source>
<LocURI>./DevInfo/Ext/InnerV</LocURI>
</Source>
<Meta>
<Format xmlns='syncml:metinf'>chr</Format>
</Meta>
<Data></Data>
</Item>
<Item>
<Source>
<LocURI>./DevInfo/Ext/CompileT</LocURI>
</Source>
<Meta>
<Format xmlns='syncml:metinf'>chr</Format>
</Meta>
<Data></Data>
</Item>
<Item>
<Source>
<LocURI>./DevInfo/Ext</LocURI>
</Source>
<Meta>
<Format xmlns='syncml:metinf'>node</Format>
</Meta>
<Data>CompileT/InnerV/ErrCode/Correlator</Data>
</Item>
<Item>
<Source>
<LocURI>./DevInfo/DmV</LocURI>
</Source>
<Meta>
<Format xmlns='syncml:metinf'>chr</Format>
</Meta>
<Data>1.2</Data>
</Item>
<Item>
<Source>
<LocURI>./DevInfo/Lang</LocURI>
</Source>
<Meta>
<Format xmlns='syncml:metinf'>chr</Format>
</Meta>
<Data>en</Data>
</Item>
<Item>
<Source>
<LocURI>./DevInfo/DevId</LocURI>
</Source>
<Meta>
<Format xmlns='syncml:metinf'>chr</Format>
</Meta>
<Data>IMEI:{imei}</Data>
</Item>
<Item>
<Source>
<LocURI>./DevInfo/Man</LocURI>
</Source>
<Meta>
<Format xmlns='syncml:metinf'>chr</Format>
</Meta>
<Data>ZTE</Data>
</Item>
<Item>
<Source>
<LocURI>./DevInfo/Mod</LocURI>
</Source>
<Meta>
<Format xmlns='syncml:metinf'>chr</Format>
</Meta>
<Data>{model}</Data>
</Item>
</Replace>
<Final/>
</SyncBody>
</SyncML>'''
body = body_get(body2)
hmac = hmac_get(nonce, body, username, password)
headers['x-syncml-hmac'] = f'algorithm=MD5, username="{username}", mac={hmac}'
response = requests.post(resp_uri, data=body, headers=headers)
response.raise_for_status()
resp = parseString(response.text)
nonce = resp.getElementsByTagName('NextNonce')[0].firstChild.nodeValue
body3 = f'''<?xml version="1.0" encoding="UTF-8"?>
<SyncML xmlns='SYNCML:SYNCML1.2'>
<SyncHdr>
<VerDTD>1.2</VerDTD>
<VerProto>DM/1.2</VerProto>
<SessionID>3E8</SessionID>
<MsgID>3</MsgID>
<Target>
<LocURI><![CDATA[{resp_uri}]]></LocURI>
</Target>
<Source>
<LocURI>IMEI:{imei}</LocURI>
</Source>
<Meta>
<MaxMsgSize xmlns='syncml:metinf'>5000</MaxMsgSize>
</Meta>
</SyncHdr>
<SyncBody>
<Status>
<CmdID>1</CmdID>
<MsgRef>2</MsgRef>
<CmdRef>0</CmdRef>
<Cmd>SyncHdr</Cmd>
<TargetRef>IMEI:{imei}</TargetRef>
<SourceRef><![CDATA[{resp_uri}]]></SourceRef>
<Chal>
<Meta>
<Format xmlns='syncml:metinf'>b64</Format>
<Type xmlns='syncml:metinf'>syncml:auth-MAC</Type>
<NextNonce xmlns='syncml:metinf'>{nonce_get()}</NextNonce>
</Meta>
</Chal>
<Data>200</Data>
</Status>
<Status>
<CmdID>2</CmdID>
<MsgRef>2</MsgRef>
<CmdRef>5</CmdRef>
<Cmd>Get</Cmd>
<TargetRef>./DevDetail/SwV</TargetRef>
<Data>200</Data>
</Status>
<Results>
<CmdID>3</CmdID>
<MsgRef>2</MsgRef>
<CmdRef>5</CmdRef>
<Item>
<Source>
<LocURI>./DevDetail/SwV</LocURI>
</Source>
<Meta>
<Format xmlns='syncml:metinf'>chr</Format>
</Meta>
<Data>{firmware}</Data>
</Item>
</Results>
<Final/>
</SyncBody>
</SyncML>'''
body = body_get(body3)
hmac = hmac_get(nonce, body, username, password)
headers['x-syncml-hmac'] = f'algorithm=MD5, username="{username}", mac={hmac}'
response = requests.post(resp_uri, data=body, headers=headers)
response.raise_for_status()
resp = parseString(response.text)
resp_replace = resp.getElementsByTagName('Replace')
if resp_replace:
data_uri = resp_replace[0].getElementsByTagName('Data')[1].firstChild.nodeValue.replace('&amp;', '&')
response = requests.get(data_uri, headers=headers)
response.raise_for_status()
resp = parseString(response.text)
firm_desc = resp.getElementsByTagName('description')[0].firstChild.nodeValue
firm_uri = resp.getElementsByTagName('objectURI')[0].firstChild.nodeValue.replace('&amp;', '&')
print(f'\nDescription: {firm_desc}')
print(f'URI: {firm_uri}')
else:
print('\nNothing found')
except requests.exceptions.HTTPError as http_error:
print(f'HTTP error: {http_error}')
except Exception as error:
print(f'Error: {error}')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment