【问题标题】:Reading Protobuf TCP Packets with existing C# classes使用现有 C# 类读取 Protobuf TCP 数据包
【发布时间】:2014-05-06 18:40:46
【问题描述】:

这个问题看起来很简单也很可行,但我这辈子都做不到。

我有:

  • 我知道一个带有几个数据包的 PCAP 文件是某种类型的 ProtoBuf 数据(可能使用 protobuf-csharp-port 创建)
  • 程序集中所有可能的 C# 类都装饰有:

    [DebuggerNonUserCode, CompilerGenerated, GeneratedCode("ProtoGen", "2.4.1.473")]
    public sealed class thing : GeneratedMessageLite<thing, thing.Builder>
    

我要做的就是使用我从程序集文件中知道的内容来解析这些数据包。简单的?可能,但无论我做什么,实际上都没有被解析。

这是许多可能的类之一的示例:

    [DebuggerNonUserCode, CompilerGenerated, GeneratedCode("ProtoGen", "2.4.1.473")]
    public sealed class Thing: GeneratedMessageLite<Thing, Thing.Builder>
    {
        // Fields
        private static readonly string[] _thingFieldNames = new string[] { "list" };
        private static readonly uint[] _thingFieldTags = new uint[] { 10 };

        ...

        public static Builder CreateBuilder()
        {
            return new Builder();
        }

        ...

        public static thing ParseFrom(ByteString data)
        {
            return CreateBuilder().MergeFrom(data).BuildParsed();
        }

        ...

        public override void WriteTo(ICodedOutputStream output)
        {
            int serializedSize = this.SerializedSize;
            string[] strArray = _thingFieldNames;
            if (this.list_.Count > 0)
            {
                output.WriteMessageArray<thingData>(1, strArray[0], this.list_);
            }
        }

        ...

        [DebuggerNonUserCode, GeneratedCode("ProtoGen", "2.4.1.473"), CompilerGenerated]
        public static class Types
        {
            // Nested Types
            [CompilerGenerated, GeneratedCode("ProtoGen", "2.4.1.473")]
            public enum PacketID
            {
                ID = 19
            }
        }
    }

类似的还有很多。我试过对每个数据包做这样的事情(使用 protobuf-csharp-port):

    Console.WriteLine(Thing.ParseFrom(packet.Buffer).ToString());

我希望看到实际的文本数据。但我要么什么也没得到,一个关于无效数据包标签的错误,或者一个关于它是“0”的错误。

我也尝试过使用 protobuf-net,但它只是给了我关于不兼容、意外类型等的随机错误:

    Console.WriteLine(ProtoBuf.Serializer.Deserialize<Thing>(ms));

我到底做错了什么?有没有更好的方法,使用程序集中的所有已知类型,简单地解码 Protobuf 消息并查看里面有什么?理想情况下不必事先知道它是什么类型的消息?

如果你能解决这个问题,非常感谢!

【问题讨论】:

    标签: c# protocol-buffers


    【解决方案1】:

    从问题中概述的失败尝试猜测,我相信您对 pcap 文件的内容有一些误解。 尤其是这一行

    Console.WriteLine(Thing.ParseFrom(packet.Buffer).ToString());
    

    让我认为您在错误的假设下工作,即单个 pcap 数据包包含单个对象的序列化字节。不幸的是,事实并非如此。

    您可能知道,TCP/IP 网络使用分层协议栈,其中每一层都添加功能并将上层协议与下层协议的细节隔离开来(反之亦然)。这是通过封装从上层发送到网络的数据并在数据在接收端向上传输时解封装数据来完成的。 现在,您的 pcap 文件包含您的网络接口所看到的原始数据,即序列化的有效负载以及应用程序、传输、互联网和链接层添加的所有数据。

    现在,如果您想反序列化转储中包含的对象,您需要编写一些代码来删除链接层和互联网协议的所有标头,(取消)执行传输协议的工作并重新组装通过网络发送的字节流。*

    接下来,您需要分析生成的字节转储,并对应用级协议的设计进行一些复杂的猜测。它在开始通信时是否实施握手?它是否与实际有效负载一起发送校验和?数据在通过网络发送之前是否经过压缩?应用程序是否在发送数据之前对其进行加密?如果 TCP 被用作传输协议,消息帧是如何实现的等等。当然,如果您可以访问生成数据的应用程序的源代码(或至少是应用程序二进制文件),那么您可以阅读代码(或对二进制文件进行逆向工程)以找出这部分。

    一旦您在这一点上,您就可以解释原始数据。剩下的就是编写一些代码来提取相关字节,将其提供给协议缓冲区反序列化器,然后瞧,你的对象回来了!

    (*当然还有其他一些小问题,例如碎片化的 IP 数据包、乱序到达的 TCP 段以及 TCP 重传。)


    总结一下:

    • 理论上可能编写一个工具来反序列化使用来自 pcap 转储的协议缓冲区序列化的对象,前提是转储包含完整的通信在两个对等方之间,即数据包没有被生成转储等的工具截断。
    • 然而在实践中,有多个障碍需要克服,即使对于经验丰富的艺术从业者来说也是微不足道的,因为这样的工具必须:
      1. 能够处理 TCP/IP 底层协议的所有复杂问题,以重建对等点之间的数据流。
      2. 能够理解用于传输序列化对象的应用程序级协议。

    请注意,仅上述第 1 点就要求至少部分实现 TCP/IP 堆栈的功能。实现这一点的最简单方法可能是重用开源 TCP/IP 实现的代码,例如 Linux 或 *BSD 内核中的代码。许多做类似事情的工具,比如从捕获文件中重建 HTTP 流量,正是这样做的。 (参见例如Justsniffer。)

    【讨论】:

    • 既然我确实可以访问应用程序的所有源代码,这难道不重要吗?除了上面包含 Protobuf 类的示例之外,我还有以下内容: public class DefaultProtobufPacketDecoder : API.PacketDecoder where TMessage: IMessageLite where TBuilder: IBuilderLite, new () { // 方法 public override Packet HandlePacket(Packet p) { return API.PacketDecoder.HandleProtoBuf(p); } }
    • 如果你有完整的源代码,那么是的,这会让你的任务更容易一点。
    • 但是,我认为您仍然没有意识到整个任务会异常复杂。你所要求的并不简单。事实上,它与简单正好相反。
    • 不管怎样,我终于能够通过复制源代码中的一些代码来做到这一点。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2011-07-14
    • 1970-01-01
    • 1970-01-01
    • 2012-06-02
    • 1970-01-01
    • 2020-08-01
    • 2011-08-09
    • 2015-04-16
    相关资源
    最近更新 更多