Skip to content

Instantly share code, notes, and snippets.

@ArturDorochowicz
Forked from Leo-PL/mf28x_find_deltas.py
Created December 30, 2023 22:16
Show Gist options
  • Select an option

  • Save ArturDorochowicz/dd698a475527e7f6f9c74f486fa00d2d to your computer and use it in GitHub Desktop.

Select an option

Save ArturDorochowicz/dd698a475527e7f6f9c74f486fa00d2d to your computer and use it in GitHub Desktop.

Revisions

  1. @Leo-PL Leo-PL revised this gist Jul 3, 2022. 1 changed file with 7 additions and 0 deletions.
    7 changes: 7 additions & 0 deletions mf28x_find_deltas.py
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,12 @@
    #!/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
  2. @Leo-PL Leo-PL created this gist Jul 3, 2022.
    486 changes: 486 additions & 0 deletions mf28x_find_deltas.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,486 @@
    #!/usr/bin/python3

    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}')