Skip to content

Instantly share code, notes, and snippets.

@dream-svg
Forked from Leo-PL/mf28x_find_deltas.py
Last active August 5, 2024 19:41
Show Gist options
  • Save dream-svg/f0ff6b8500f8eb09c5803883977ff28a to your computer and use it in GitHub Desktop.
Save dream-svg/f0ff6b8500f8eb09c5803883977ff28a 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 & Dream-Svg
# 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
from urllib3.util import SKIP_HEADER
#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': SKIP_HEADER,
'Accept-Encoding': SKIP_HEADER}
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