XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Diagnostics;
using System.Text.Json;
using XFEEDToolBox.Core.Navigation;

namespace XFEEDToolBox.Core.Native;

/// <summary>Build-pinned VM_READ observations only. Does not invoke an SCO or fuel method.</summary>
public static class NativeCruiseOverchargeDiagnostics
{
    public static async Task<object> ReadAsync(int processId,CancellationToken token=default)
    {
        using var process=Process.GetProcessById(processId);var started=process.StartTime.ToUniversalTime().Ticks;
        var pipe=new NavigationPipeClient(processId);var health=await pipe.CallAsync(new{op="health.read"},token);
        static void CheckHealth(JsonElement value)
        {
            var age=DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()-value.GetProperty("observedAtUnixMilliseconds").GetInt64();
            if(age is <0 or >500||!value.GetProperty("localPlayerShipVerified").GetBoolean()||!value.GetProperty("shipHealth").GetProperty("verified").GetBoolean())
                throw new InvalidDataException("Fresh verified main ship health required for SCO read-only diagnostics.");
        }
        CheckHealth(health);var entity=ulong.Parse(health.GetProperty("entityId").GetString()!,System.Globalization.CultureInfo.InvariantCulture);
        using var memory=new ReadOnlyPlayerMemory(processId,started,NativeSystemLocationReader.GameSha256);var (_,life,root)=memory.Player(entity);
        var metadata=memory.U64(root+0x10);var count=memory.U64(metadata+0x18);var slots=memory.U64(metadata+0x30);var components=memory.U64(root+0x18);
        if(count is 0 or >1024||slots==0||components==0)throw new InvalidDataException("Current main ship component registry differs.");
        object Component(uint globalRva,ulong expectedTable,bool scoState)
        {
            var type=memory.U32(memory.ImageBase+globalRva);
            if(type is 0 or >99999)return new{globalRva=globalRva.ToString("x"),type,available=false,issue="Not a bounded component type",functionsCalled=false};
            var slot=memory.U64(slots+type*16UL);
            if(slot>=count)return new{globalRva=globalRva.ToString("x"),type,slot,componentCount=count,available=false,functionsCalled=false};
            var component=memory.U64(components+slot*8);
            if(component<0x10000)return new{globalRva=globalRva.ToString("x"),type,slot,componentCount=count,address=component.ToString("x"),available=false,issue="Null current interface slot",functionsCalled=false};
            var table=memory.U64(component);
            if(!memory.InImage(table))throw new InvalidDataException("Current component interface is outside the pinned game image.");
            var match=expectedTable!=0&&table==memory.ImageBase+expectedTable;int? state=scoState&&match?memory.Read(component+0x228,1)[0]:null;
            var method50=memory.U64(table+0x50);
            return new{globalRva=globalRva.ToString("x"),type,slot,componentCount=count,available=true,address=component.ToString("x"),vtableRva=(table-memory.ImageBase).ToString("x"),expectedVtableRva=expectedTable==0?null:expectedTable.ToString("x"),expectedType=expectedTable==0?(bool?)null:match,
                method50Rva=memory.InImage(method50)?(method50-memory.ImageBase).ToString("x"):"unverified",stateByte228=state,knownState=state is >=0 and <=3,functionsCalled=false};
        }
        var scoInterface=Component(0x5F44D00,0x533E6E0,true);var scoClass=Component(0x5FA7550,0x533E5B0,false);var fuelInterface=Component(0x5F45128,0x550E8A8,false);
        var scoConsumerTarget=Component(0x5F6BCE4,0,false);
        var scoGraph=new List<object>();
        var scoType=memory.U32(memory.ImageBase+0x5F44D00);
        if(scoType is 0 or >99999)throw new InvalidDataException("Original SCO component type is not bounded.");
        var scoSlot=memory.U64(slots+scoType*16UL);
        var actualSco=scoSlot<count?memory.U64(components+scoSlot*8):0;
        if(actualSco>=0x10000&&memory.U64(actualSco)==memory.ImageBase+0x533E6E0&&memory.U64(actualSco-0x58)==memory.ImageBase+0x533E5B0)
        {
            foreach(var offset in new ulong[]{0x60,0x150,0x168,0x210,0x258})
            {
                var raw=memory.U64(actualSco-0x58+offset);var inline=memory.InImage(raw);
                var address=inline?actualSco-0x58+offset:raw;var table=inline?raw:address>=0x10000?memory.U64(address):0;
                string? Method(ulong slot){if(!memory.InImage(table))return null;var function=memory.U64(table+slot);return memory.InImage(function)?(function-memory.ImageBase).ToString("x"):null;}
                scoGraph.Add(new{fieldOffset=offset.ToString("x"),raw=raw.ToString("x"),inlineVtableCandidate=inline,address=address.ToString("x"),vtableAddress=table.ToString("x"),vtableRva=memory.InImage(table)?(table-memory.ImageBase).ToString("x"):null,
                    method00Rva=Method(0),method08Rva=Method(8),method10Rva=Method(0x10),method18Rva=Method(0x18),method20Rva=Method(0x20),method50Rva=Method(0x50),functionsCalled=false});
            }
        }
        var after=await pipe.CallAsync(new{op="health.read"},token);CheckHealth(after);memory.Validate();
        if(after.GetProperty("entityId").GetString()!=health.GetProperty("entityId").GetString()||memory.U64(life+0xB0)!=entity||memory.U64(root+0x10)!=metadata
            ||memory.U64(root+0x18)!=components||memory.U64(metadata+0x18)!=count||memory.U64(metadata+0x30)!=slots)
            throw new InvalidDataException("Main ship or component registry changed during SCO diagnostics.");
        return new{at=DateTimeOffset.UtcNow,processId,started,imageBase=memory.ImageBase.ToString("x"),entity=entity.ToString(),scoInterface,scoClass,fuelInterface,scoConsumerTarget,scoGraph,processAccess="QUERY_INFORMATION | VM_READ",gameCallsSent=0,gameWritesSent=0,actionsSent=false};
    }
}