Protobuf- net нераспознанный префикс потока - PullRequest
1 голос
/ 24 апреля 2020

Я пытаюсь перепроектировать структуру протокола Quasar RAT protobuf. Quasar - это инструмент удаленного администрирования, написанный на C#, с открытым исходным кодом, который можно найти здесь. https://github.com/quasar/QuasarRAT

Мне удалось отменить большинство из них, и теперь я могу подключиться к клиенту сервера Quasar из сценария python. Как только один вопрос остается открытым, кажется, что каждый поток байтов, который отправляется от клиента к серверу, начинается с 3-байтового поля, которое не зарегистрировано в классе protobuf в Quasar. Похоже, в этом поле указана длина сообщения без учета префиксных байтов. Как можно видеть в этом блоке для примера потока байтов с префиксом, сгенерированного для массива размером 0x2d2, это байты с префиксом, добавляемые к сообщению.

0x0A, 0xCF, 0x05

Если я когда-нибудь решу изменить поля сообщения перед сериализацией сообщения, этот поток байтов изменится, за исключением первого 0x0A байта. Похоже, что если я продолжу добавлять байты к полям сообщения, второй байт будет расти, а если я переполню второй байт (сделаю так, чтобы он достигал 0xff ) - он увеличил бы третий байт и сбросил бы второй байт на 0x80 . Но математика не имеет смысла для меня вообще, поскольку это поле должно возвращать размер массива, но не по какой-либо разумной формуле, которую я мог бы вычислить. Я знаю, что protobuf- net может генерировать PreLengthPrefix байт для префикса сообщения с его длиной, но это не тот случай.

Любая помощь приветствуется.

1 Ответ

1 голос
/ 25 апреля 2020

Здесь приведены правила кодирования: https://developers.google.com/protocol-buffers/docs/encoding

По сути, каждое поле кодируется как заголовок поля (он же «тег»), за которым следует полезная нагрузка. Заголовок поля представляет собой «varint» (см. Руководство по кодированию), значением которого является целое число, состоящее из номера поля и типа проводника. Тип проводника - это 3 младших значащих бита, а номер поля - остаток (сдвинут на 3 бита). В случае 0x0A (двоичный 1010) тип провода равен 2 (двоичный 010), а номер поля равен 1.

Способ обработки полезной нагрузки зависит от типа провода. Для типа проводов 2 (с префиксом длины) следует ожидать следующее:

  • переменная, которая является длиной полезной нагрузки в байтах, затем
  • столько байтов фактической полезной нагрузки

К сожалению, protobuf неоднозначен без схемы, поэтому знание того, что у вас есть данные с префиксом длины, не говорит вам, что это за данные ; полезная нагрузка с префиксом длины может быть:

  • строка UTF-8
  • необработанный BLOB (bytes)
  • под-сообщение
  • «упакованный» массив некоторого примитивного типа (целые числа / числа с плавающей запятой / et c) - помня, что префикс длины - это число байтов , а не количество элементов; элементы даже не обязательно имеют фиксированный размер (сами они могут быть varints

Во многих случаях цель типа проводника не состоит в том, чтобы рассказать вам, как интерпретировать данные; это чтобы рассказать вам, как пропустить (или просто дословно сохранить) поле, если оно не является тем, о котором вы знаете. Например, кто-то еще использует V3 API, и вы только что обновили свою схему до V2 ; они отправляют сообщение V3 в API V2; в V3 есть дополнительные поля, которые вам не нужны - десериализатор не должен разрываться при попадании на них, поэтому тип проводника сообщает ему , как игнорировать поле (то есть, каковы правила для поиска следующего поля.) В противном случае мы могли бы просто использовать информацию схемы и не сохранять тип провода в полезной нагрузке вообще (хотя это также используется в качестве оптимизации при повторном примитивные данные, через «упакованные» массивы - это зависит от сериализатора, кодирует ли он, например, префикс длины или множество пар заголовок / значение поля).

...