diff --git a/ClassicAssist/Engine.cs b/ClassicAssist/Engine.cs index e17797be..9f77308b 100644 --- a/ClassicAssist/Engine.cs +++ b/ClassicAssist/Engine.cs @@ -742,27 +742,9 @@ public static void SendPacketToServer( byte[] packet, int length ) PacketWaitEntries?.CheckWait( packet, PacketDirection.Outgoing, true ); - if ( _getPacketLength != null ) + if (!VerifyPacketLengthCorrect( packet )) { - int expectedLength = _getPacketLength( packet[0] ); - - if ( expectedLength == -1 ) - { - expectedLength = ( packet[1] << 8 ) | packet[2]; - } - - if ( length != expectedLength ) - { - SentrySdk.CaptureMessage( $"Invalid packet length: {length} != {expectedLength}", scope => - { - scope.SetExtra( "Packet", packet ); - scope.SetExtra( "Length", length ); - scope.SetExtra( "Direction", PacketDirection.Outgoing ); - scope.SetExtra( "Expected Length", expectedLength ); - } ); - - return; - } + return; } ( byte[] data, int dataLength ) = Utility.CopyBuffer( packet, length ); @@ -791,30 +773,12 @@ public static void SendPacketToClient( byte[] packet, int length, bool delay = t PacketWaitEntries?.CheckWait( packet, PacketDirection.Incoming, true ); - if ( _getPacketLength != null ) + if ( !VerifyPacketLengthCorrect( packet ) || length != packet.Length) { - int expectedLength = _getPacketLength( packet[0] ); - - if ( expectedLength == -1 ) - { - expectedLength = ( packet[1] << 8 ) | packet[2]; - } - - if ( length != expectedLength ) - { - SentrySdk.CaptureMessage( $"Invalid packet length: {length} != {expectedLength}", scope => - { - scope.SetExtra( "Packet", packet ); - scope.SetExtra( "Length", length ); - scope.SetExtra( "Direction", PacketDirection.Incoming ); - scope.SetExtra( "Expected Length", expectedLength ); - } ); - - return; - } + return; } - ( byte[] data, int dataLength ) = Utility.CopyBuffer( packet, length ); + (byte[] data, int dataLength) = Utility.CopyBuffer( packet, length ); _sendToClient?.Invoke( ref data, ref dataLength ); @@ -827,6 +791,78 @@ public static void SendPacketToClient( byte[] packet, int length, bool delay = t } } + public static void HexDump( byte[] bytes, string title = null, int bytesPerLine = 16 ) + { + if ( bytes == null || bytes.Length == 0 ) + { + return; + } + + int bytesLength = bytes.Length; + + Console.WriteLine( !string.IsNullOrEmpty( title ) ? $"{title}, Packet: {bytes[0]:X}, Size: {bytes.Length}" : $"Packet: {bytes[0]:X}, Size: {bytes.Length}" ); + + char[] HexChars = "0123456789ABCDEF".ToCharArray(); + + const int firstHexColumn = 8 // 8 characters for the address + + 3; // 3 spaces + + int firstCharColumn = firstHexColumn + bytesPerLine * 3 // - 2 digit for the hexadecimal value and 1 space + + ( bytesPerLine - 1 ) / 8 // - 1 extra space every 8 characters from the 9th + + 2; // 2 spaces + + int lineLength = firstCharColumn + bytesPerLine // - characters to show the ascii value + + Environment.NewLine.Length; // Carriage return and line feed (should normally be 2) + + char[] line = ( new string( ' ', lineLength - Environment.NewLine.Length ) + Environment.NewLine ).ToCharArray(); + int expectedLines = ( bytesLength + bytesPerLine - 1 ) / bytesPerLine; + StringBuilder result = new StringBuilder( expectedLines * lineLength ); + + for ( int i = 0; i < bytesLength; i += bytesPerLine ) + { + line[0] = HexChars[( i >> 28 ) & 0xF]; + line[1] = HexChars[( i >> 24 ) & 0xF]; + line[2] = HexChars[( i >> 20 ) & 0xF]; + line[3] = HexChars[( i >> 16 ) & 0xF]; + line[4] = HexChars[( i >> 12 ) & 0xF]; + line[5] = HexChars[( i >> 8 ) & 0xF]; + line[6] = HexChars[( i >> 4 ) & 0xF]; + line[7] = HexChars[( i >> 0 ) & 0xF]; + + int hexColumn = firstHexColumn; + int charColumn = firstCharColumn; + + for ( int j = 0; j < bytesPerLine; j++ ) + { + if ( j > 0 && ( j & 7 ) == 0 ) + { + hexColumn++; + } + + if ( i + j >= bytesLength ) + { + line[hexColumn] = ' '; + line[hexColumn + 1] = ' '; + line[charColumn] = ' '; + } + else + { + byte b = bytes[i + j]; + line[hexColumn] = HexChars[( b >> 4 ) & 0xF]; + line[hexColumn + 1] = HexChars[b & 0xF]; + line[charColumn] = b < 32 ? '.' : (char) b; + } + + hexColumn += 3; + charColumn++; + } + + result.Append( line ); + } + + Console.WriteLine( result.ToString() ); + } + public static void SendPacketToClient( PacketWriter packet ) { byte[] data = packet.ToArray(); @@ -872,6 +908,42 @@ public static void SendPacketToServer( BasePacket basePacket ) SendPacketToServer( data, data.Length ); } + public static bool VerifyPacketLengthCorrect( byte[] packet, [CallerMemberName] string callerMemberName = null ) + { + int length = packet.Length; + int expectedLength = length; + + if ( _getPacketLength == null ) + { + return true; + } + + expectedLength = _getPacketLength( packet[0] ); + + if ( expectedLength == -1 ) + { + expectedLength = ( packet[1] << 8 ) | packet[2]; + } + + if ( packet.Length != expectedLength ) + { + string message = $"Invalid packet length: {length} != {expectedLength}"; + + Utility.FormatBuffer( packet, message ); + + SentrySdk.CaptureMessage( $"Invalid packet length: {length} != {expectedLength}", scope => + { + scope.SetExtra( "Packet", packet ); + scope.SetExtra( "Length", length ); + scope.SetExtra( "Direction", PacketDirection.Incoming ); + scope.SetExtra( "Expected Length", expectedLength ); + scope.SetExtra( "Callsite", callerMemberName ); + } ); + } + + return packet.Length == expectedLength; + } + public static bool Move( Direction direction, bool run ) { return _requestMove?.Invoke( (int) direction, run ) ?? false; diff --git a/ClassicAssist/Misc/Utility.cs b/ClassicAssist/Misc/Utility.cs index b14e832c..afbc26cf 100644 --- a/ClassicAssist/Misc/Utility.cs +++ b/ClassicAssist/Misc/Utility.cs @@ -1,5 +1,20 @@ -using System; +#region License + +// Copyright (C) 2026 Reetus +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY + +#endregion + +using System; using System.Linq; +using System.Text; namespace ClassicAssist.Misc { @@ -29,5 +44,77 @@ public static (byte[] data, int length) CopyBuffer( byte[] source, int sourceLen return ( data, dataLength ); } + + public static void FormatBuffer( byte[] bytes, string title = null, int bytesPerLine = 16 ) + { + if ( bytes == null || bytes.Length == 0 ) + { + return; + } + + int bytesLength = bytes.Length; + + Console.WriteLine( !string.IsNullOrEmpty( title ) ? $"{title}, Packet: {bytes[0]:X}, Size: {bytes.Length}" : $"Packet: {bytes[0]:X}, Size: {bytes.Length}" ); + + char[] HexChars = "0123456789ABCDEF".ToCharArray(); + + const int firstHexColumn = 8 // 8 characters for the address + + 3; // 3 spaces + + int firstCharColumn = firstHexColumn + bytesPerLine * 3 // - 2 digit for the hexadecimal value and 1 space + + ( bytesPerLine - 1 ) / 8 // - 1 extra space every 8 characters from the 9th + + 2; // 2 spaces + + int lineLength = firstCharColumn + bytesPerLine // - characters to show the ascii value + + Environment.NewLine.Length; // Carriage return and line feed (should normally be 2) + + char[] line = ( new string( ' ', lineLength - Environment.NewLine.Length ) + Environment.NewLine ).ToCharArray(); + int expectedLines = ( bytesLength + bytesPerLine - 1 ) / bytesPerLine; + StringBuilder result = new StringBuilder( expectedLines * lineLength ); + + for ( int i = 0; i < bytesLength; i += bytesPerLine ) + { + line[0] = HexChars[( i >> 28 ) & 0xF]; + line[1] = HexChars[( i >> 24 ) & 0xF]; + line[2] = HexChars[( i >> 20 ) & 0xF]; + line[3] = HexChars[( i >> 16 ) & 0xF]; + line[4] = HexChars[( i >> 12 ) & 0xF]; + line[5] = HexChars[( i >> 8 ) & 0xF]; + line[6] = HexChars[( i >> 4 ) & 0xF]; + line[7] = HexChars[( i >> 0 ) & 0xF]; + + int hexColumn = firstHexColumn; + int charColumn = firstCharColumn; + + for ( int j = 0; j < bytesPerLine; j++ ) + { + if ( j > 0 && ( j & 7 ) == 0 ) + { + hexColumn++; + } + + if ( i + j >= bytesLength ) + { + line[hexColumn] = ' '; + line[hexColumn + 1] = ' '; + line[charColumn] = ' '; + } + else + { + byte b = bytes[i + j]; + line[hexColumn] = HexChars[( b >> 4 ) & 0xF]; + line[hexColumn + 1] = HexChars[b & 0xF]; + line[charColumn] = b < 32 ? '.' : (char) b; + } + + hexColumn += 3; + charColumn++; + } + + result.Append( line ); + } + + Console.WriteLine( result.ToString() ); + } } } \ No newline at end of file diff --git a/ClassicAssist/UO/Network/IncomingPacketFilters.cs b/ClassicAssist/UO/Network/IncomingPacketFilters.cs index 08bb9894..fa31ba5f 100644 --- a/ClassicAssist/UO/Network/IncomingPacketFilters.cs +++ b/ClassicAssist/UO/Network/IncomingPacketFilters.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using Assistant; using ClassicAssist.Data; using ClassicAssist.Data.Filters; @@ -10,6 +6,12 @@ using ClassicAssist.UO.Network.PacketFilter; using ClassicAssist.UO.Network.Packets; using ClassicAssist.UO.Objects; +using Sentry; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ClassicAssist.Misc; namespace ClassicAssist.UO.Network { @@ -71,13 +73,16 @@ private static bool OnProperties( ref byte[] packet, ref int length ) length = packet.Length - span.Length + newArgumentsBytes.Length; - packet[19] = (byte) ( newArgumentsBytes.Length << 8 ); + packet[19] = (byte) ( newArgumentsBytes.Length >> 8 ); packet[20] = (byte) newArgumentsBytes.Length; Array.Resize( ref packet, length ); Array.Copy( newArgumentsBytes, 0, packet, 21, newArgumentsBytes.Length ); Array.Copy( remainingPacket.ToArray(), 0, packet, 21 + newArgumentsBytes.Length, remainingPacket.Length ); + packet[1] = (byte) ( length >> 8 ); + packet[2] = (byte) length; + return false; } @@ -168,6 +173,9 @@ private static bool OnASCIIMessage( ref byte[] packet, ref int length ) Array.Resize( ref nameOverrideBytes, 30 ); Array.Copy( nameOverrideBytes, 0, packet, 14, 30 ); Array.Copy( nameOverrideBytes, 0, packet, 44, length - 44 ); + + packet[1] = (byte) ( length >> 8 ); + packet[2] = (byte) length; } bool block = RepeatedMessagesFilter.CheckMessage( journalEntry ); @@ -411,7 +419,7 @@ private static bool OnLocalizedMessage( ref byte[] packet, ref int length ) Array.Resize( ref packet, length ); Array.Copy( nameOverrideBytes, 0, packet, 18, 30 ); Array.Copy( unicodeBytes, 0, packet, 47, unicodeBytes.Length ); - packet[1] = (byte) ( length << 8 ); + packet[1] = (byte) ( length >> 8 ); packet[2] = (byte) length; } } @@ -489,13 +497,16 @@ public static bool CheckPacket( ref byte[] data, ref int length ) { bool result = dynamicFilterEntry.CheckPacket( ref data, ref length, PacketDirection.Incoming ); - if ( result ) + if ( !result ) { - return true; + continue; } + + filtered = true; + break; } - return filtered; + return !Engine.VerifyPacketLengthCorrect( data ) || filtered; } } } \ No newline at end of file