В этой статье я опишу один из самых частых вопросов от клиентов — «Как прочитать текст входящего сообщения? Я нашёл on-line декодер, но он показывает абракадабру». Дело в том, что у некоторых сервисов есть не только возможность отправки сообщений, но и получения на них ответных сообщений. Однако алгоритм, описанный мной, применим к любому действию в SMPP протоколе, т.к. по сути является инструкцией к спецификации протокола.
Разбор входящего сообщения без использования какого-либо SMPP ПО
Нам потребуются:
- SMPP Protocol Specification v3.4
- Wireshark
- Блокнот
- Браузер
Я не буду описывать процесс получения самого cap файла (дампа) SMPP обмена, т.к. уже есть много статей на эту тему (например эта).
Общий порядок обмена пакетами в SMPP выглядит следующим образом:
Как видно в случае входящего сообщения нас интересует пакет deliver_sm. Определение типа пакета происходит по полю command_id, которое в случае deliver_sm имеет значение 0x00000005 (пункт 5.1.2.1 в спецификации).
В итоге фильтр в wireshark выглядит так:
smpp.command_id == 0x00000005
После того как нашли пакет по фильтру копируем его содержимое в HEX формате:
Итоговая строка, которую предстоит расшифровать:
00000048000000050000000000000003000101636F6F6E6F6C616E642E727500010137393132333435363738390000000000000000
0800000424000c043f044004380432043504424
Пункт, описывающий формат пакета deliver_sm, в спецификации, имеет номер 4.6.1 “DELIVER_SM” Syntax. В этом пункте имеется таблица, в которой расписывается каждое поле пакета:
Приступим к разбору пакета
Первое поле пакета — command_length имеет размер 4 октета (столбец size octets) и тип Integer. Это означает что от начала нашей HEX строки необходимо отсчитать 4 х 2 = 8 символов.
00 00 00 48 000000050000000000000003000101636F6F6...
(HEX) 00000048 -> (DEC) 72 октета или 144 символа — длина всего SMPP пакета.
Следующее поле command_id — аналогично 4 октета и Integer.
00000048 00 00 00 05 0000000000000003000101636F6F6...
Преобразуем в читаемый вид: 00000005 = deliver_sm
Поля command_status и sequence_number так же имею по 4 октета. Где посмотреть их значения, я предполагаю Вы уже догадываетесь.
0000004800000005 00 00 00 00 00 00 00 03 000101636F6F6...
Поле service_type может содержать максимум 6 октетов и имеет тип C-Octet String. В пункте 3.1 SMPP PDU — Type Definitions сказано:
C-Octet String A series of ASCII characters terminated with the NULL character
Другими словами это последовательность ASCII символов с нулевым октетом как знак окончания поля. Т.е. это поле может содержать максимум 6 символов, но может быть и меньше — конец данного поля обозначается HEX 00. В данном конкретном примере мы сразу видим 00 — поле не содержит ни какого значения.
00000048000000050000000000000003 00 0101636F6F6...
Если бы этот столбец был заполнен, то расшифровать его можно по таблице ASCII символов.
Пропустим следующие 2 поля source_addr_ton и source_addr_npi, т.к. как их расшифровать уже ясно и разберем еще одно поле с типом C-Octet String — source_addr.
Source_addr может содержать максимум 21 октет, но в данном случае оно состоит из 13 октетов включая нулевой октет который означает окончание поля:
63 6F 6F 6E 6F 6C 61 6E 64 2E 72 75 00
В поисковике делаем запрос «ascii table» и начинаем расшифровывать:
63 = c
6F = o
6F = o
6E = n
6F = o
6C = l
61 = a
6E = n
64 = d
И так далее до символов 00.
Остальные поля я тоже пропускаю — их много и их определение ни чем не отличается от выше описанного. Перейдём к полю data_coding, без значения данного поля мы не сможем прочитать текст сообщения.
Я разделил все поля в HEX строке примера, а поле data_coding выделил жирным и вот что получилось:
00000047;00000005;00000000;00000003;00;01;01;373932393937333630333000;01;01;373932333235303039353900;00;00;00;00;00;00;00;
08;00;00;0424;000c;043f04400438043204350442
Пункт 5.2.19 спецификации указывает что используется кодировка UCS2 (ISO/IEC-10646). (HEX) 08 -> (BIN) 00001000 -> UCS2 (ISO/IEC-10646):
Ищем таблицу символов по запросу: «ucs2 code chart»
043f04400438043204350442
Выбираем первые 2 октета текста «043f» и ищем их на странице
Продолжаем выделять по 2 октета и искать:
[р] 0440 CYRILLIC SMALL LETTER ER
[и] 0438 CYRILLIC SMALL LETTER I
[в] 0432 CYRILLIC SMALL LETTER VE
[е] 0435 CYRILLIC SMALL LETTER IE
[т] 0442 CYRILLIC SMALL LETTER TE
На этом разбор пакета окончен. Надеюсь что описание получилось понятное и достаточно полное. В целом если у Вас получилось осознать как прочитать сообщение, то отправить его не составит ни какого труда. Поменяется несколько полей.
Читайте спецификации — там почти все уже написано.
Комментарии (7)
ivnik
01.10.2015 18:34+1А зачем это всё делать, если wireshark итак умеет показывать декодированный smpp pdu по полям?
vp7
02.10.2015 00:51В чём глубинный смысл? Как писали выше — wireshark великолепно и сам всё декодирует (у меня, правда, не получилось уговорить его декодировать русский текст из UCS2, но это частности).
Единственное разумное объяснение — у автора есть софт, который в логи пишет hex dump отправленных/полученных PDU'шек и есть необходимость декодировать эти SMS'кт.
Тогда цель исследования ясна — сначала разобраться самому, а потом написать парсер.
По поводу длинных SMS — вероятность получить от оператора текст в message_payload существует, но она достаточно низка.
А вот UDH заголовки для длинной SMS'ки вы будете получать практически гарантированно.ilicho
02.10.2015 07:33Глубинный смысл показать что библиотеки, которые реализуют SMPP, не содержат ни какой магии и в случае проблем вполне можно самостоятельно выяснить что именно пошло не так и исправить. Второе значение — повышение качества запросов к техподдержке компаний, предоставляющих подключение по SMPP, все мы знаем как приятно грамотно указать на чужие ошибки :)
Vamp
Есть ещё склеенные из нескольких частей сообщения и message_payload TLV. В первом случае первые 6-7 байт поля short_message будут содержать UDH, определяющий порядок склейки, а во втором случае сообщение будет находиться в другом месте.
Ручная работа с символами и их кодами лучше всего организована на unicode-table.com.
ilicho
Спасибо, за замечание. Я не раскрыл эту особенность.
Одним из признаков того что в deliver_sm использован message_payload является поле sm_length = 0.