1

生のバッファとして受信したカスタム プロトコル データを解析するための C# クラスをモデル化するつもりです。バッファはすでにバイト配列として手元にあります。

このパーサーはさまざまなフィールド レイアウトに対処する必要があるため、プロトコル要素の一部は IntPtrs です。

raw バッファーを構造体にマップした後、構造体内のポインターにアクセスしようとすると、アクセス違反が発生します。

重要な部分を示す簡単な例を作成しました。

namespace CS_StructMarshalling
{
  class Packet
  {  
    public PacketStruct Fields;

    [StructLayout(LayoutKind.Explicit)]
    public struct PacketStruct
    {
      [FieldOffset(0)] public UInt16 Flags;
      [FieldOffset(2)] public IntPtr Data;
    }

    public Packet()
    {
      Fields = new PacketStruct();
    }

    public void DecompileBinaryBuffer(ref Byte[] byteBuffer)
    {
      int size = Marshal.SizeOf(typeof(PacketStruct)); 
      IntPtr ptr = Marshal.AllocHGlobal(size);

      Marshal.Copy(byteBuffer, 0, ptr, size);

      this.Fields = (PacketStruct)Marshal.PtrToStructure(ptr, typeof(PacketStruct));
      Marshal.FreeHGlobal(ptr);
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      byte[] rawBuffer = new byte[] { 1, 2, 65, 66, 67, 68, 69, 70 };

      Packet testPkt = new Packet();
      testPkt.DecompileBinaryBuffer(ref rawBuffer);

      UInt16 testFlags = testPkt.Fields.Flags;
      String testData = Marshal.PtrToStringAnsi(testPkt.Fields.Data, 6);
    }
  }
}

私はこの問題に頭を悩ませようとしていますが、役に立ちません。私の理解では、IntPtr は個別にマーシャリングする必要がありますが、これをクリーンな方法で行う方法のヒントは見つかりません。

どんなポインタも歓迎します。お時間をいただきありがとうございます。

4

3 に答える 3

1

文字データを文字データへのポインターであるかのようにデコードしているため、文字データを含まないメモリ内の任意の位置を指し、プログラムが存在するアドレス空間の一部の外側にある可能性が高くなります。使用を許可。ポインターではないため、データをポインターとしてデコードすることはできません。

データをそのままの型に変換します。文字は 8 ビット コードであるため、8 ビット文字セットを使用してデコードする必要があります。

ushort testFlags = BitConverter.ToUInt16(rawBuffer, 0);
string testData = Encoding.ASCII.GetString(rawBuffer, 2, rawBuffer.Length - 2);

結果:

513
ABCDEF
于 2012-05-16T10:27:42.273 に答える
1

ポインターが実際に何を意味するのかを誤解している可能性があるため、このアプローチには根本的な欠陥があります。ポインター (マネージ コードの IntPtr) は、単にメモリ内の場所のアドレスです。Marshal.PtrToStructure() のような、既に使用している種類のコードで指定されているものを読み取ります。

ポインターの問題は、ポインターが 1 つのプロセスでしか有効でないことです。すべてのプロセスには、独自の仮想メモリ アドレス空間があります。マネージ コードの追加の制限により、ガベージ コレクターはオブジェクトをある場所から別の場所にランダムに移動できます。これには回避策があります。ReadProcessMemory() をピン呼び出しして、別のプロセスからデータを読み取ることができます。そして、GCHandle.Alloc() オブジェクトを固定することで、ガベージ コレクターの動作を回避します。

ただし、これらはカスタム シリアライゼーション スキームに属さない回避策です。ReadProcessMemory() の明らかな障害モードは、どのプロセスにデータがあるかがわからないことです。または、それを使用するのに十分な権利を持っていません。または、別のマシンで実行されているプロセス。ポインターのピン留めには欠陥があります。

そのため、シリアライゼーションのアプローチでは、データを平坦化し、ポインターをポイント先のデータに置き換えることでポインターを排除することで、この問題を解決します。そして、デシリアライザーでオブジェクト グラフを復活させます。BinaryFormatter、XmlSerializer、DataContractSerializer、DataContractJsonSerializer などのクラスによって実行されるジョブ。そして、マークのお気に入りのようなサードパーティのもの。自分で書かないでください。正しく書くのは難しいので、たくさんあります。

于 2012-05-16T11:03:59.837 に答える
1

あなたはそれをうまく解凍しました。問題はデータです。次のハードコーディングされたデータがあります。

  • フラグ = 513
  • データ = 1145258561 (x86 を使用している場合は次の 4 バイト)

問題は単純です: ポインター値 "1145258561"は有効ではありません。

自分のものではないデータに無理やり入ることはできません。

また、x64 では、バッファが 8 バイトで構造体が 10 バイト必要なため、エラーがすぐに発生します。

さらに、 の使用が正しくないため、を使用して をref回避できる可能性があります。HGlobalstackalloc

于 2012-05-16T10:20:59.313 に答える