Необработанный декодер для формата protobufs

Я хотел бы найти способ преобразования двоичного сообщения protobuf в человекообразное описание содержащихся данных без использования .proto файлов.

Фон состоит в том, что у меня есть сообщение .proto, что он отклоняется парсером на Android, но не совсем понятно почему. Я мог пройти через сообщение, но это довольно утомительно.

Я пробовал protoc --decode_raw, но он просто выдает ошибку "Не удалось разобрать вход". Я google надеясь/ожидая, что кто-то сделал бы полезную веб-утилиту, которая могла бы это сделать, но не нашла ничего очевидного.

Я просто надеюсь получить какой-то результат, например:

field 1: varint: 128
field 4: string: "foo"

Любые указатели в правильном направлении будут приветствоваться!

Ответы

Ответ 1

Вы можете попробовать заставить его через плагин wirehark, или, возможно, вы могли бы заимствовать часть "читателя" некоторых из реализаций (я знаю, как это сделать на С#, например, но я сомневаюсь, что это то, что вы имели в виду).

Однако будьте осторожны - строка в буферах протокола на самом деле не означает "строка" - это может быть:

  • строка UTF-8
  • необработанный BLOB произвольных данных
  • под-сообщение
  • "упакованный" массив
  • (возможно, что-то еще забываю)

Ответ 2

Для посты: Буфер протокола Google инструменты имеют возможность декодировать необработанные буферы.

Просто отправьте ему неизвестный буфер и передайте флаг --decode_raw

$ cat has_no_proto.buff | protoc --decode_raw
2 {
  2: "Error retrieving information from server. [RH-02]"
}

Итак, здесь сообщение с полем 2 установлено на встроенное сообщение, которое, в свою очередь,  его второе поле задано строкой, в которой говорилось, что я разозлился с Google Play.

Информация о типе не определена (похоже, она попытается отобразить все двоичные данные в виде строк, но ваше требование для различия varint/string/submessage выполнено).

Ответ 3

Если у вас есть двоичный файл, содержащий (несколько?) длину, префиксные сообщения protobuf, protoc ‒‒decode_raw < file не может проанализировать его из-за префиксов длины. Простым способом является разделение файла на его последовательные сообщения, а затем преобразование каждого из них с помощью protoc.

Мой прием:

var fs = File.OpenRead(filename));
var buffer = new byte[4096];
int size;
for (int part = 1; Serializer.TryReadLengthPrefix(fs, PrefixStyle.Base128, out size); part++) {
  long startPosition = fs.Position;
  using (var writer = File.OpenWrite(string.Format("{0}[{1}].pb", filename, part))) {
    for (int bytesToRead = size; bytesToRead > 0; ) {
      int bytesRead = fs.Read(buffer, 0, Math.Min(bytesToRead, buffer.Length));
      bytesToRead -= bytesRead;
      if (bytesRead <= 0) // End of file.
        break;
      writer.Write(buffer, 0, bytesRead);
    }
  }
}

Ответ 4

Как указано в Ответ Мишель де Рюйтер, возможно, что ваше сообщение protobuf имеет length-prefix. Предполагая, что это так, этот ответ должен помочь.

(ПРИМЕЧАНИЕ. Для большинства приведенных ниже команд я предполагаю, что ваше сообщение protobuf хранится в файле с именем input.)

protoc --decode_raw + dd для одного сообщения:

Если это просто одно сообщение, тогда вы действительно можете использовать protoc --decode_raw, но сначала нужно снять заголовок с префиксом длины. Предполагая, что заголовок имеет длину 4 байта, вы можете использовать dd, чтобы отключить заголовок input, а затем подать вывод в protoc.

dd bs=1 skip=4 if=input 2>/dev/null | protoc --decode_raw

protoc-decode-lenprefix --decode_raw для одного сообщения:

Я также написал script, который автоматически обрабатывает разделение заголовков:

protoc-decode-lenprefix --decode_raw < input

Этот script является просто оболочкой поверх protoc --decode_raw, но обрабатывает разбор заголовка длины и префикса, а затем вызывает protoc.

Теперь этот script не очень полезен в этом случае, потому что мы можем просто использовать трюк dd выше, чтобы отключить заголовок. Однако, скажем, у нас есть поток данных (например, файл или поток TCP), содержащий несколько сообщений, которые заключены в заголовки с префиксом длины....

protoc-decode-lenprefix --decode_raw для потока сообщений:

Вместо одного сообщения protobuf во входном файле, скажем, input содержит несколько сообщений protobuf, которые создаются заголовками префикса длины. В этом случае невозможно просто использовать трюк dd, потому что вам нужно действительно прочитать содержимое заголовка префикса длины, чтобы определить, как долго будет длиться последующее сообщение в потоке, и, следовательно, сколько байтов впереди следующего заголовка + сообщение лежит. Поэтому вместо того, чтобы беспокоиться обо всем этом, вы можете просто снова использовать protoc-decode-lenprefix:

protoc-decode-lenprefix --decode_raw < input

protoc-decode-lenprefix --decode ... foo.proto для потока сообщений

Этот script также может использоваться для полного декодирования сообщений с префиксом длины (вместо их "необработанного декодирования" ). Предполагается, что у вас есть доступ к файлам .proto, которые определяют сообщение protobuf, так же как и обернутая команда protoc. Синтаксис вызова идентичен protoc --decode. Например, используя трюк dd с protoc --decode, а вход будет Mesos task.info, синтаксис выглядит следующим образом:

dd bs=1 skip=4 if=task.info 2>/dev/null | \
protoc --decode mesos.internal.Task \
                      -I MESOS_CODE/src -I MESOS_CODE/include \
                      MESOS_CODE/src/messages/messages.proto

И при идентичном использовании параметров protoc-decode-lenprefix

cat task.info | \
protoc-decode-lenprefix --decode mesos.internal.Task \
                      -I MESOS_CODE/src -I MESOS_CODE/include \
                      MESOS_CODE/src/messages/messages.proto