Ali*_*anC 17 c# structure bytearray pcap.net
更新:这个问题的答案帮助我在GitHub上编写了开源项目AlicanC的Modern Warfare 2 Tool.您可以看到我如何在MW2Packets.cs中读取这些数据包以及我编写的扩展,以读取Extensions.cs中的大端数据.
我在我的C#应用程序中使用Pcap.Net捕获" 使命召唤:现代战争2"的 UDP数据包.我从图书馆收到了一封.我试图像字符串一样解析它,但是效果不好.byte[]
在byte[]我有一个通用的数据包报头,然后具体到数据包类型,然后约在大堂每个玩家信息的另一头.
一个乐于助人的人为我检查了一些数据包并想出了这些结构:
// Fields are big endian unless specified otherwise.
struct packet_header
{
uint16_t magic;
uint16_t packet_size;
uint32_t unknown1;
uint32_t unknown2;
uint32_t unknown3;
uint32_t unknown4;
uint16_t unknown5;
uint16_t unknown6;
uint32_t unknown7;
uint32_t unknown8;
cstring_t packet_type; // \0 terminated string
};
// Fields are little endian unless specified otherwise.
struct header_partystate //Header for the "partystate" packet type
{
uint32_t unknown1;
uint8_t unknown2;
uint8_t player_entry_count;
uint32_t unknown4;
uint32_t unknown5;
uint32_t unknown6;
uint32_t unknown7;
uint8_t unknown8;
uint32_t unknown9;
uint16_t unknown10;
uint8_t unknown11;
uint8_t unknown12[9];
uint32_t unknown13;
uint32_t unknown14;
uint16_t unknown15;
uint16_t unknown16;
uint32_t unknown17[10];
uint32_t unknown18;
uint32_t unknown19;
uint8_t unknown20;
uint32_t unknown21;
uint32_t unknown22;
uint32_t unknown23;
};
// Fields are little endian unless specified otherwise.
struct player_entry
{
uint8_t player_id;
// The following fields may not actually exist in the data if it's an empty entry.
uint8_t unknown1[3];
cstring_t player_name;
uint32_t unknown2;
uint64_t steam_id;
uint32_t internal_ip;
uint32_t external_ip;
uint16_t unknown3;
uint16_t unknown4;
uint32_t unknown5;
uint32_t unknown6;
uint32_t unknown7;
uint32_t unknown8;
uint32_t unknown9;
uint32_t unknown10;
uint32_t unknown11;
uint32_t unknown12;
uint16_t unknown13;
uint8_t unknown14[???]; // Appears to be a bit mask, sometimes the length is zero, sometimes it's one. (First entry is always zero?)
uint8_t unknown15;
uint32_t unknown16;
uint16_t unknown17;
uint8_t unknown18[???]; // Most of the time this is 4 bytes, other times it is 3 bytes.
};
Run Code Online (Sandbox Code Playgroud)
我在我的C#应用程序中重新创建了包头结构,如下所示:
[StructLayout(LayoutKind.Sequential, Pack=1)]
struct PacketHeader
{
public UInt16 magic;
public UInt16 packetSize;
public UInt32 unknown1;
public UInt32 unknown2;
public UInt32 unknown3;
public UInt32 unknown4;
public UInt16 unknown5;
public UInt16 unknown6;
public UInt32 unknown7;
public UInt32 unknown8;
public String packetType;
}
Run Code Online (Sandbox Code Playgroud)
然后我尝试为"partystate"标题创建一个结构,但是我收到错误,说fixed关键字是不安全的:
[StructLayout(LayoutKind.Sequential, Pack=1)]
struct PartyStateHeader
{
UInt32 unknown1;
Byte unknown2;
Byte playerEntryCount;
UInt32 unknown4;
UInt32 unknown5;
UInt32 unknown6;
UInt32 unknown7;
Byte unknown8;
UInt32 unknown9;
UInt16 unknown10;
Byte unknown11;
fixed Byte unknown12[9];
UInt32 unknown13;
UInt32 unknown14;
UInt16 unknown15;
UInt16 unknown16;
fixed UInt32 unknown17[10];
UInt32 unknown18;
UInt32 unknown19;
Byte unknown20;
UInt32 unknown21;
UInt32 unknown22;
UInt32 unknown23;
}
Run Code Online (Sandbox Code Playgroud)
我不能这样做,因为的不同大小的播放器条目东西unknown14和unknown18.(玩家条目是最重要的.)
现在,不知何故,我必须将我必须投射byte[]到这些PacketHeader结构上.可悲的是,这并不容易(PacketHeader)bytes.我尝试过在互联网上找到的这种方法,但它扔了一个AccessViolationException:
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
PacketHeader packetHeader = (PacketHeader)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(PacketHeader));
Run Code Online (Sandbox Code Playgroud)
我怎样才能做到这一点?
Rob*_*tos 18
//我在以下网站找到了这个:http://code.cheesydesign.com/?p = 572(我还没有测试过,但是//乍一看它会运行良好.)
/// <summary>
/// Reads in a block from a file and converts it to the struct
/// type specified by the template parameter
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="reader"></param>
/// <returns></returns>
private static T FromBinaryReader<T>(BinaryReader reader)
{
// Read in a byte array
byte[] bytes = reader.ReadBytes(Marshal.SizeOf(typeof(T)));
// Pin the managed memory while, copy it out the data, then unpin it
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return theStructure;
}
Run Code Online (Sandbox Code Playgroud)
这就是我做的:
using System;
using System.Runtime.InteropServices;
public static object GetObjectFromBytes(byte[] buffer, Type objType)
{
object obj = null;
if ((buffer != null) && (buffer.Length > 0))
{
IntPtr ptrObj = IntPtr.Zero;
try
{
int objSize = Marshal.SizeOf(objType);
if (objSize > 0)
{
if (buffer.Length < objSize)
throw new Exception(String.Format("Buffer smaller than needed for creation of object of type {0}", objType));
ptrObj = Marshal.AllocHGlobal(objSize);
if (ptrObj != IntPtr.Zero)
{
Marshal.Copy(buffer, 0, ptrObj, objSize);
obj = Marshal.PtrToStructure(ptrObj, objType);
}
else
throw new Exception(String.Format("Couldn't allocate memory to create object of type {0}", objType));
}
}
finally
{
if (ptrObj != IntPtr.Zero)
Marshal.FreeHGlobal(ptrObj);
}
}
return obj;
}
Run Code Online (Sandbox Code Playgroud)
在结构定义中我没有使用任何fixed区域,相反,MarshalAs如果标准编组不起作用,我使用了属性.这是你需要的字符串.
你会像这样使用这个函数:
PacketHeader ph = (PacketHeader)GetObjectFromBytes(buffer, typeof(PacketHeader));
Run Code Online (Sandbox Code Playgroud)
编辑:我没有在代码示例中看到您的BigEndian"限制".只有字节为LittleEndian时,此解决方案才有效.
编辑2:在您的示例的字符串中,您将使用以下内容装饰它:
[MarshalAs(UnmanagedType.LPStr)]
Run Code Online (Sandbox Code Playgroud)
在数组中,对于n大小的数组,我会使用类似的东西:
[MarshalAs(UnmanagedType.ByValArray, SizeConst = n)]
Run Code Online (Sandbox Code Playgroud)
如果您想要快速的代码而不需要复制,这就是解决方案。我们正在处理 raw byte[],只需在代码中转换指针unsafe,就像在本机 C / C++ 中一样。因此,没有调用昂贵的框架方法、制作副本等的开销。
对非托管的任何更改都struct将反映在托管中byte[],反之亦然。
//FOR DEBUG/TEST ONLY
using System.Runtime.InteropServices;
namespace ByteStructCast1
{
class Program
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct StructTest//4B
{
[MarshalAs(UnmanagedType.U2)]
public ushort item1; //2B
public fixed byte item2[2]; //2B =2x 1B
}
static void Main(string[] args)
{
//managed byte array
byte[] DB1 = new byte[7]; //7B more than we need. byte buffer usually is greater.
DB1[0] = 2;//test data |> LITTLE ENDIAN
DB1[1] = 0;//test data |
DB1[2] = 3;//test data
DB1[3] = 4;//test data
unsafe //we'll now pin unmanaged struct over managed byte array
{
fixed(byte* db1 = DB1) //db1 is pinned pointer to DB1 byte[] array
{
//StructTest t1 = *(StructTest*)db1; //does not change DB1/db1
//t1.item1 = 11; //does not change DB1/db1
db1[0] = 22; //does CHANGE DB1/db1
DB1[0] = 33; //does CHANGE DB1/db1
StructTest* ptest = (StructTest*)db1; //does CHANGE DB1/db1
ptest->item1 = 44; //does CHANGE DB1/db1
ptest->item2[0]++; //does CHANGE DB1/db1
ptest->item2[1]--; //does CHANGE DB1/db1
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
当您使用原始类型的fixed-size 缓冲区,并且需要将其元素作为structs 和成员进行处理时,例如ulongto MyStruct,两者都是 64 位长,也可以使用此方法。
我将字节数组转换为内存流.然后在该流上实例化二进制阅读器.然后定义辅助函数,它们接受二进制读取器并解析单个类.
内置BinaryReader类总是使用小端.
我在这里使用类而不是结构.
class PacketHeader
{
uint16_t magic;
uint16_t packet_size;
uint32_t unknown1;
uint32_t unknown2;
uint32_t unknown3;
uint32_t unknown4;
uint16_t unknown5;
uint16_t unknown6;
uint32_t unknown7;
uint32_t unknown8;
string packet_type; // replaced with a real string
};
PacketHeader ReadPacketHeader(BinaryReader reader)
{
var result=new PacketHeader();
result.magic = reader.ReadInt16();
...
result.packet_type=ReadCString();//Some helper function you might need to define yourself
return result;
}
Run Code Online (Sandbox Code Playgroud)
对于那些有权访问 C# 7.3 功能的人,我使用这段不安全的代码来“序列化”为字节:
public static class Serializer
{
public static unsafe byte[] Serialize<T>(T value) where T : unmanaged
{
byte[] buffer = new byte[sizeof(T)];
fixed (byte* bufferPtr = buffer)
{
Buffer.MemoryCopy(&value, bufferPtr, sizeof(T), sizeof(T));
}
return buffer;
}
public static unsafe T Deserialize<T>(byte[] buffer) where T : unmanaged
{
T result = new T();
fixed (byte* bufferPtr = buffer)
{
Buffer.MemoryCopy(bufferPtr, &result, sizeof(T), sizeof(T));
}
return result;
}
}
Run Code Online (Sandbox Code Playgroud)
甲unmanaged类型可以是一个结构(简单的结构在不参考的类型,那些所考虑的管理结构)或天然类型诸如int,short等