-
-
Save ArturDorochowicz/dd698a475527e7f6f9c74f486fa00d2d to your computer and use it in GitHub Desktop.
Delta update finder for ZTE MF283+, MF286, MF286A and MF286D routers
This file contains hidden or 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 | |
# 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('&', '&') | |
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('&', '&') | |
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('&', '&') | |
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