diff --git a/SCHALE.Common/Crypto/PacketCryptManager.cs b/SCHALE.Common/Crypto/PacketCryptManager.cs index 949bbf8..46115df 100644 --- a/SCHALE.Common/Crypto/PacketCryptManager.cs +++ b/SCHALE.Common/Crypto/PacketCryptManager.cs @@ -1,5 +1,6 @@ using System.IO.Compression; using System.Text; +using SCHALE.Common.Crypto; using SCHALE.Common.NetworkProtocol; using Serilog; @@ -12,17 +13,18 @@ namespace MX.Core.Crypto // private static readonly short PROTOCOL_HEAD_RESERVE = 8; private readonly XORCryptor _cryptor = new(); private readonly FastCRC _checke = new(); - //private ProtocolConverter _converter = new(); + private ProtocolConverter _converter = new(); public static PacketCryptManager Instance = new(); - public byte[] RequestToBinary(Protocol protocol, string json, int protocolConverter) + public byte[] RequestToBinary(Protocol protocol, string json) { byte[] compressedData = Compress(json); _cryptor.Encrypt(compressedData, 0, compressedData.Length); _checke.GetCRC(compressedData, 0, compressedData.Length, out uint crc); Log.Information("CRC: " + crc); - //int protocolConverter = _converter.TypeConversion(crc, protocol); - //int protocolConverter = 1797824024; + Log.Information("protocol: " + protocol); + int protocolConverter = _converter.TypeConversion(crc % 99, protocol); + Log.Information("protocolConverter: " + protocolConverter); byte[][] data = [ BitConverter.GetBytes(crc), diff --git a/SCHALE.Common/Crypto/ProtocolConverter.cs b/SCHALE.Common/Crypto/ProtocolConverter.cs new file mode 100644 index 0000000..9429b23 --- /dev/null +++ b/SCHALE.Common/Crypto/ProtocolConverter.cs @@ -0,0 +1,31 @@ +using SCHALE.Common.NetworkProtocol; +using Serilog; +using System.Text.Json; + +namespace SCHALE.Common.Crypto +{ + internal class ProtocolConverter + { + private Dictionary> ProtocolConversionMappings { get; set; } + + public ProtocolConverter() + { + var jsonPath = Path.Combine(Path.GetDirectoryName(AppContext.BaseDirectory), "protocol_converter_values.json"); + + if (!File.Exists(jsonPath)) + { + Log.Information("The Json file for Protocol Converter does not exist..."); + return; + } + + string protocolJson = File.ReadAllText(jsonPath); + + ProtocolConversionMappings = JsonSerializer.Deserialize>>(protocolJson); + } + + public int TypeConversion(uint crc, Protocol protocol) + { + return ProtocolConversionMappings[(int)protocol][(int)crc]; + } + } +} diff --git a/SCHALE.GameClient/GameClient.cs b/SCHALE.GameClient/GameClient.cs index 67d7157..b2352bc 100644 --- a/SCHALE.GameClient/GameClient.cs +++ b/SCHALE.GameClient/GameClient.cs @@ -1,8 +1,12 @@ using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json; +using SCHALE.Common.NetworkProtocol; using SCHALE.GameServer.Services; using Serilog; using Serilog.Events; +using System.Collections; +using System.Collections.Generic; namespace SCHALE.GameServer { diff --git a/SCHALE.GameClient/Models/ErrorViewModel.cs b/SCHALE.GameClient/Models/ErrorViewModel.cs deleted file mode 100644 index c93607e..0000000 --- a/SCHALE.GameClient/Models/ErrorViewModel.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace SCHALE.GameClient.Models -{ - public class ErrorViewModel - { - public string? RequestId { get; set; } - - public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - } -} diff --git a/SCHALE.GameClient/Services/PrivateClientService.cs b/SCHALE.GameClient/Services/PrivateClientService.cs index b0b1c28..3b04220 100644 --- a/SCHALE.GameClient/Services/PrivateClientService.cs +++ b/SCHALE.GameClient/Services/PrivateClientService.cs @@ -6,17 +6,10 @@ using SCHALE.Common.Crypto; using SCHALE.Common.Crypto.XXHash; using SCHALE.Common.FlatData; using SCHALE.Common.NetworkProtocol; -using SCHALE.GameServer.Services.Irc; +using SCHALE.GameServer.Utils; using Serilog; -using System.IO.Compression; -using System.Net.Http; using System.Net.Http.Headers; -using System.Reflection.PortableExecutable; -using System.Runtime.InteropServices; -using System.Security.Policy; -using System.Text; using System.Text.Json; -using System.Threading.Tasks; namespace SCHALE.GameServer.Services { @@ -24,18 +17,13 @@ namespace SCHALE.GameServer.Services { private readonly HttpClient _httpClient; - public static readonly string API_URL = "http://10.0.0.149/api/gateway"; + public static readonly string PS_URL = "http://10.0.0.149/api/gateway"; public static readonly string MITM_URL = "http://10.0.0.149:8080"; public static readonly string OFFICIAL_API_URL = "http://prod-gateway.bluearchiveyostar.com:5100/api/"; - // https://prod-game.bluearchiveyostar.com:5000/api/gateway - //public static readonly string OFFICIAL_API_URL = "https://prod-gateway.bluearchiveyostar.com:5100/api/"; - public static readonly string OFFICIAL_API_URL_Test = "ec2-52-68-9-200.ap-northeast-1.compute.amazonaws.com"; - // https://prod-game.bluearchiveyostar.com:5000/api/ - public static readonly string SessionKey = ""; - public static uint[] crc_table = [0, 79764919, 159529838, 222504665, 319059676, 398814059, 445009330, 507990021, 638119352, 583659535, 797628118, 726387553, 890018660, 835552979, 1015980042, 944750013, 1276238704, 1221641927, 1167319070, 1095957929, 1595256236, 1540665371, 1452775106, 1381403509, 1780037320, 1859660671, 1671105958, 1733955601, 2031960084, 2111593891, 1889500026, 1952343757, 2552477408, 2632100695, 2443283854, 2506133561, 2334638140, 2414271883, 2191915858, 2254759653, 3190512472, 3135915759, 3081330742, 3009969537, 2905550212, 2850959411, 2762807018, 2691435357, 3560074640, 3505614887, 3719321342, 3648080713, 3342211916, 3287746299, 3467911202, 3396681109, 4063920168, 4143685023, 4223187782, 4286162673, 3779000052, 3858754371, 3904687514, 3967668269, 881225847, 809987520, 1023691545, 969234094, 662832811, 591600412, 771767749, 717299826, 311336399, 374308984, 453813921, 533576470, 25881363, 88864420, 134795389, 214552010, 2023205639, 2086057648, 1897238633, 1976864222, 1804852699, 1867694188, 1645340341, 1724971778, 1587496639, 1516133128, 1461550545, 1406951526, 1302016099, 1230646740, 1142491917, 1087903418, 2896545431, 2825181984, 2770861561, 2716262478, 3215044683, 3143675388, 3055782693, 3001194130, 2326604591, 2389456536, 2200899649, 2280525302, 2578013683, 2640855108, 2418763421, 2498394922, 3769900519, 3832873040, 3912640137, 3992402750, 4088425275, 4151408268, 4197601365, 4277358050, 3334271071, 3263032808, 3476998961, 3422541446, 3585640067, 3514407732, 3694837229, 3640369242, 1762451694, 1842216281, 1619975040, 1682949687, 2047383090, 2127137669, 1938468188, 2001449195, 1325665622, 1271206113, 1183200824, 1111960463, 1543535498, 1489069629, 1434599652, 1363369299, 622672798, 568075817, 748617968, 677256519, 907627842, 853037301, 1067152940, 995781531, 51762726, 131386257, 177728840, 240578815, 269590778, 349224269, 429104020, 491947555, 4046411278, 4126034873, 4172115296, 4234965207, 3794477266, 3874110821, 3953728444, 4016571915, 3609705398, 3555108353, 3735388376, 3664026991, 3290680682, 3236090077, 3449943556, 3378572211, 3174993278, 3120533705, 3032266256, 2961025959, 2923101090, 2868635157, 2813903052, 2742672763, 2604032198, 2683796849, 2461293480, 2524268063, 2284983834, 2364738477, 2175806836, 2238787779, 1569362073, 1498123566, 1409854455, 1355396672, 1317987909, 1246755826, 1192025387, 1137557660, 2072149281, 2135122070, 1912620623, 1992383480, 1753615357, 1816598090, 1627664531, 1707420964, 295390185, 358241886, 404320391, 483945776, 43990325, 106832002, 186451547, 266083308, 932423249, 861060070, 1041341759, 986742920, 613929101, 542559546, 756411363, 701822548, 3316196985, 3244833742, 3425377559, 3370778784, 3601682597, 3530312978, 3744426955, 3689838204, 3819031489, 3881883254, 3928223919, 4007849240, 4037393693, 4100235434, 4180117107, 4259748804, 2310601993, 2373574846, 2151335527, 2231098320, 2596047829, 2659030626, 2470359227, 2550115596, 2947551409, 2876312838, 2788305887, 2733848168, 3165939309, 3094707162, 3040238851, 2985771188]; - public readonly uint POLYNOMIAL = 8; - public readonly uint MASK = 0; + private readonly long AccountServerId = -1; + private readonly string MxToken = ""; + private readonly long Hash = 0; public PrivateClientService(HttpClient httpClient) { @@ -44,29 +32,11 @@ namespace SCHALE.GameServer.Services protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - //await SendPostRequestAsync(OFFICIAL_API_URL, JsonSerializer.Serialize(new AcademyGetInfoRequest() - //{ - // ClientUpTime = 1, - // Resendable = true, - // Hash = , - // IsTest = false, - // SessionKey = new() - // { - // AccountServerId = , - // MxToken = "" - // }, - //})); + //await SendPostRequestAsync(OFFICIAL_API_URL, new AcademyGetInfoRequest(){ }); - - await SendPostRequestAsync(OFFICIAL_API_URL, JsonSerializer.Serialize(new ShopBuyGacha3Request() + await SendPostRequestAsync(OFFICIAL_API_URL, new ShopBuyGacha3Request() { FreeRecruitId = 0, - SessionKey = new() - { - AccountServerId = , - MxToken = SessionKey, - }, - Cost = new() { ParcelInfos = [ @@ -106,99 +76,35 @@ namespace SCHALE.GameServer.Services FurnitureDBs = [], ConsumeCondition = 0, }, - GoodsId = 35838, - ShopUniqueId = 50667, - ClientUpTime = 0, - Resendable = true, - Hash = , - IsTest = false, - })); + GoodsId = 35840, + ShopUniqueId = 50668, + }); } - - public bool GetCRC(byte[] buffer, int offset, int length, out uint crc) + + public async Task SendPostRequestAsync(string url, T requestPacket) where T : RequestPacket { - if (buffer == null || offset < 0 || length < 0 || offset + length > buffer.Length) + requestPacket.SessionKey = new() { - crc = 0; - return false; - } + MxToken = this.MxToken, + AccountServerId = this.AccountServerId, + }; - crc = MASK; - for (int i = offset; i < offset + length; i++) - { - byte index = (byte)((crc ^ buffer[i]) & 0xFF); - crc = (crc >> 8) ^ crc_table[index]; - } - crc ^= MASK; - return true; - } + requestPacket.Hash = this.Hash; + requestPacket.Resendable = true; + requestPacket.ClientUpTime = 0; + requestPacket.IsTest = false; + + string packetJsonStr = JsonSerializer.Serialize((T)requestPacket); - - public static byte[] CompressToByteArray(byte[] data) - { - if (data == null || data.Length == 0) - return null; - - using (var output = new MemoryStream()) - { - using (var gzipStream = new GZipStream(output, CompressionMode.Compress)) - { - gzipStream.Write(data, 0, data.Length); - } - // Get the compressed data as a byte array - return output.ToArray(); - } - } - - public static byte[] RequestToBinary(string jsonStr) - { - byte[] jsonPayload = Encoding.UTF8.GetBytes(jsonStr); - byte[] payloadCompressed = CompressToByteArray(jsonPayload); - - XOR.Crypt(payloadCompressed, [0xD9]); - - return payloadCompressed; - } - - - public async Task SendPostRequestAsync(string url, string jsonStr) - { - - //jsonStr = ""; Log.Information("Sending Post Request to " + url); - Log.Information($"Payload: {jsonStr}"); - byte[] payload = PacketCryptManager.Instance.RequestToBinary(Protocol.Shop_BuyGacha3, jsonStr, ); + Log.Information($"Payload: {packetJsonStr}"); + byte[] payload = PacketCryptManager.Instance.RequestToBinary(requestPacket.Protocol, packetJsonStr); File.WriteAllBytes("./mx.dat", payload); Log.Information("Writeen All Bytes"); return; - //GetCRC(payload, 0, payload.Length, out uint crc); - - ////using var xxhash = XXHash32.Create(); - ////xxhash.ComputeHash(Encoding.UTF8.GetBytes(name)); - - //Log.Information("CRC: " + crc); - - //SCHALEPacket packet = new SCHALEPacket() - //{ - // CRC = crc, - // Protocol = (ushort)Protocol.Academy_GetInfo, - // PacketSize = payload.Length, - - // Payload = payload - //}; - - //using var memoryStream = new MemoryStream(); - //using var binaryWriter = new BinaryWriter(memoryStream); - - //binaryWriter.Write(packet.CRC); - //binaryWriter.Write(packet.Protocol); - //binaryWriter.Write(packet.PacketSize); - //binaryWriter.Write(payload); - //binaryWriter.BaseStream.Position = 0; - using var fileStream = new FileStream("./mx.dat", FileMode.Create, FileAccess.Write); using var binaryWriterFile = new BinaryWriter(fileStream); @@ -210,7 +116,7 @@ namespace SCHALE.GameServer.Services mxFile.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream"); // Add the in-memory content as a file with a name - var boundary = "BestHTTP_HTTPMultiPartForm_3"; + var boundary = "BestHTTP_HTTPMultiPartForm_328B5160"; using var content = new MultipartFormDataContent(boundary); content.Add(mxFile, "mx", "1"); @@ -252,14 +158,4 @@ namespace SCHALE.GameServer.Services services.AddHostedService(); } } - - public class SCHALEPacket // total 12 bytes header - { - public uint CRC { get; set; } // 4 bytes - public int Protocol { get; set; } // - public int PacketSize { get; set; } // - - public byte[] Payload { get; set; } - - } } diff --git a/SCHALE.GameServer/Utils/PcapUtils.cs b/SCHALE.GameServer/Utils/PcapUtils.cs new file mode 100644 index 0000000..da0fd7a --- /dev/null +++ b/SCHALE.GameServer/Utils/PcapUtils.cs @@ -0,0 +1,130 @@ +using Google.FlatBuffers; +using SCHALE.Common.NetworkProtocol; +using System.Text.Json.Nodes; +using System.Text; +using System.Text.Json; +using Newtonsoft.Json; +using Serilog; +using System.Reflection; + +namespace SCHALE.GameServer.Utils +{ + public static class PcapUtils + { + private static string DefaultPcapFilePath = Path.Combine(Path.GetDirectoryName(AppContext.BaseDirectory), "packets.json"); + + private static List RequestPackets { get; } + private static List ResponsePackets { get; } + + static PcapUtils() + { + RequestPackets = new List(); + ResponsePackets = new List(); + + PcapUtils.Load(); + } + + // gets all packets of that type + public static T[] GetPacketsFromPcap(Protocol protocol, PacketType packetType) where T : BasePacket + { + if (packetType == PacketType.Request) + { + return RequestPackets.Where(x => x.Protocol == protocol).Select(x => x as T).ToArray(); + } + else + { + return ResponsePackets.Where(x => x.Protocol == protocol).Select(x => x as T).ToArray(); + } + } + + // get a single packet, remember to change the sessionkey, accountId, etc.. + public static T GetPacketFromPcap(Protocol protocol, PacketType packetType) where T : BasePacket + { + return GetPacketsFromPcap((Protocol)protocol, packetType).FirstOrDefault(); + } + + private static void Load() + { + if (!Path.Exists(DefaultPcapFilePath)) + { + Log.Information("Pcap json file was not found."); + return; + } + + var json = File.ReadAllText(DefaultPcapFilePath); + var jsonPackets = System.Text.Json.JsonSerializer.Deserialize(json); + + foreach (JsonNode packetNode in jsonPackets) + { + PacketType packetType = packetNode["type"].ToString() == "REQUEST" ? PacketType.Request : PacketType.Response; + JsonNode packetPayload = packetNode["payload"]; + + Protocol protocol = (Protocol?)packetPayload?["Protocol"]?.GetValue() ?? Protocol.None; + + if (protocol == Protocol.None) + { + Log.Warning("Failed to read protocol from JsonNode, {Payload:j}", packetNode.ToString()); + continue; + } + + Type packetClassType; + object packet; + + try + { + packetClassType = GetPacketTypeByProtocol(protocol, packetType); + + packet = packetPayload.Deserialize(packetClassType); + } catch (Exception ex) + { + Log.Warning($"Failed to deserialize {packetNode["type"]} protocol {protocol} from JsonNode, payload: {packetPayload.ToString()}"); + continue; + } + + if (packetType == PacketType.Request) + { + RequestPackets.Add((RequestPacket)packet); + } + else + { + ResponsePackets.Add((ResponsePacket)packet); + } + + Log.Information($"Loaded Pcap Type: {packetNode["type"]}, Protocol: {protocol}"); + } + } + + public static Type? GetPacketTypeByProtocol(Protocol protocol, PacketType packetType) + { + var assembly = Assembly.GetAssembly(typeof(BasePacket)); + Type baseType = packetType == PacketType.Request ? typeof(RequestPacket) : typeof(ResponsePacket); + var packetTypeCandidates = assembly.GetTypes().Where(t => baseType.IsAssignableFrom(t) && !t.IsAbstract); + + foreach (var candidate in packetTypeCandidates) + { + var protocolProperty = candidate.GetProperty("Protocol", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); + + if (protocolProperty != null && protocolProperty.PropertyType == typeof(Protocol)) + { + var instance = Activator.CreateInstance(candidate); + if (instance != null) + { + var protocolValue = protocolProperty.GetValue(instance); + if (protocolValue != null && protocolValue.Equals(protocol)) + { + return candidate; + } + } + } + } + + return null; + } + } + + public enum PacketType + { + Request, + Response, + } +}