Initial import of IC.Rebuild
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
using System.Buffers.Binary;
|
||||
|
||||
namespace ICRebuild.Core;
|
||||
|
||||
// The reader must copy exactly count bytes from the current process or fail.
|
||||
public delegate bool PacketMemoryReader(nint address,int count,out byte[] bytes);
|
||||
public sealed record CapturedPacket(int Opcode,uint? Target,byte[] Payload,bool Complete,string? Issue=null);
|
||||
public sealed record CaptureRead(CapturedPacket? Packet,string? Error=null);
|
||||
|
||||
public static class NativePacketCapture
|
||||
{
|
||||
public const string ClientVersion="2026.10.06.0000.0000";
|
||||
public const string BaselineClientVersion="2026.09.15.0000.0000";
|
||||
public static bool IsSupportedVersion(string? version) => version is ClientVersion or BaselineClientVersion;
|
||||
public const int IpcHeaderSize=0x10;
|
||||
public const int MaxPayload=65536;
|
||||
|
||||
public static CaptureRead Up(PacketMemoryReader read,nint data,Func<int,bool> accepts)
|
||||
{
|
||||
if(!Read(read,data,0,16,out var header)) return new(null,"上行对象头读取失败");
|
||||
var opcode=BinaryPrimitives.ReadUInt16LittleEndian(header);
|
||||
if(!accepts(opcode)) return new(null);
|
||||
return Payload(read,data,0x20,BinaryPrimitives.ReadUInt64LittleEndian(header.AsSpan(8)),opcode,null);
|
||||
}
|
||||
// Called only AFTER ZoneReceive returned success; holder owns the message until its caller releases it.
|
||||
public static CaptureRead Down(PacketMemoryReader read,nint holder,Func<int,bool> accepts)
|
||||
{
|
||||
if(!Read(read,holder,0x10,8,out var reference)) return new(null,"下行消息引用读取失败");
|
||||
var message=Pointer(reference);
|
||||
if(!Read(read,message,0x20,0x20,out var descriptor)) return new(null,"下行消息描述读取失败");
|
||||
var length=BinaryPrimitives.ReadUInt64LittleEndian(descriptor.AsSpan(0x10));
|
||||
var data=Pointer(descriptor.AsSpan(0x18));
|
||||
// Even a fallback must not read an opcode beyond the declared message.
|
||||
if(length<4 || !Read(read,data,0,4,out var ipc)) return new(null,"下行 IPC 头读取失败或长度不足");
|
||||
var opcode=BinaryPrimitives.ReadUInt16LittleEndian(ipc.AsSpan(2));
|
||||
if(!accepts(opcode)) return new(null);
|
||||
var target=NormalizeTarget(BinaryPrimitives.ReadUInt32LittleEndian(descriptor));
|
||||
return Payload(read,data,IpcHeaderSize,length,opcode,target);
|
||||
}
|
||||
private static CaptureRead Payload(PacketMemoryReader read,nint data,int offset,ulong messageLength,int opcode,uint? target)
|
||||
{
|
||||
if(!TryPayloadLength(messageLength,out var count))
|
||||
return new(new(opcode,target,[],false,$"消息长度无效或超出采集上限:{messageLength}"));
|
||||
if(count==0) return new(new(opcode,target,[],true));
|
||||
return Read(read,data,offset,count,out var bytes)
|
||||
? new(new(opcode,target,bytes,true))
|
||||
: new(new(opcode,target,[],false,"载荷读取失败,未保留部分数据"));
|
||||
}
|
||||
public static bool TryPayloadLength(ulong messageLength,out int count)
|
||||
{
|
||||
count=0;
|
||||
if(messageLength<IpcHeaderSize || messageLength>MaxPayload+IpcHeaderSize) return false;
|
||||
count=(int)messageLength-IpcHeaderSize;
|
||||
return true;
|
||||
}
|
||||
// Same reserved-target normalization as the native NetworkModule dispatcher caller.
|
||||
public static uint NormalizeTarget(uint target) => (target&0x5FF80000u)==0x5FF80000u ? 0x5FF80002u-(target&1u) : target;
|
||||
private static nint Pointer(ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
var value=BinaryPrimitives.ReadUInt64LittleEndian(bytes);
|
||||
return value>(ulong)nint.MaxValue ? 0 : (nint)value;
|
||||
}
|
||||
private static bool Read(PacketMemoryReader read,nint data,int offset,int count,out byte[] bytes)
|
||||
{
|
||||
bytes=[];
|
||||
if(data<=0 || offset<0 || count<0 || count>MaxPayload || (long)data>long.MaxValue-offset-count) return false;
|
||||
if(!read(data+offset,count,out var result) || result==null || result.Length!=count) return false;
|
||||
bytes=result;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user