// // NetworkBinding.cs // // Copyright (c) František Boháček. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.Concurrent; using System.Diagnostics; using System.Runtime.InteropServices; using NosSmooth.LocalBinding.Errors; using NosSmooth.LocalBinding.Extensions; using NosSmooth.LocalBinding.Options; using Reloaded.Hooks.Definitions; using Reloaded.Hooks.Definitions.X86; using Remora.Results; namespace NosSmooth.LocalBinding.Objects; /// /// The binding to nostale network object. /// public class NetworkBinding { [Function ( new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx }, FunctionAttribute.Register.eax, FunctionAttribute.StackCleanup.Callee, new[] { FunctionAttribute.Register.ebx, FunctionAttribute.Register.esi, FunctionAttribute.Register.edi, FunctionAttribute.Register.ebp } )] private delegate nuint PacketSendDelegate(nuint packetObject, nuint packetString); [Function ( new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx }, FunctionAttribute.Register.eax, FunctionAttribute.StackCleanup.Callee, new[] { FunctionAttribute.Register.ebx, FunctionAttribute.Register.esi, FunctionAttribute.Register.edi, FunctionAttribute.Register.ebp } )] private delegate nuint PacketReceiveDelegate(nuint packetObject, nuint packetString); /// /// Create the network binding with finding the network object and functions. /// /// The binding manager. /// The options for the binding. /// A network binding or an error. public static Result Create(NosBindingManager bindingManager, NetworkBindingOptions options) { var process = Process.GetCurrentProcess(); var networkObjectAddress = bindingManager.Scanner.FindPattern(options.NetworkObjectPattern); if (!networkObjectAddress.Found) { return new BindingNotFoundError(options.NetworkObjectPattern, "NetworkBinding"); } var binding = new NetworkBinding ( bindingManager, (nuint)(networkObjectAddress.Offset + (int)process.MainModule!.BaseAddress + 0x01) ); var sendHookResult = bindingManager.CreateCustomAsmHookFromPattern ("NetworkBinding.SendPacket", binding.SendPacketDetour, options.PacketSendHook); if (!sendHookResult.IsDefined(out var sendHook)) { return Result.FromError(sendHookResult); } var receiveHookResult = bindingManager.CreateCustomAsmHookFromPattern ("NetworkBinding.ReceivePacket", binding.ReceivePacketDetour, options.PacketReceiveHook); if (!receiveHookResult.IsDefined(out var receiveHook)) { return Result.FromError(receiveHookResult); } binding._sendHook = sendHook; binding._receiveHook = receiveHook; return binding; } private readonly NosBindingManager _bindingManager; private readonly nuint _networkManagerAddress; private NosAsmHook _sendHook = null!; private NosAsmHook _receiveHook = null!; private bool _callingReceive; private bool _callingSend; private NetworkBinding ( NosBindingManager bindingManager, nuint networkManagerAddress ) { _bindingManager = bindingManager; _networkManagerAddress = networkManagerAddress; } /// /// Event that is called when packet send was called by NosTale. /// /// /// The send must be hooked for this event to be called. /// public event Func? PacketSend; /// /// Event that is called when packet receive was called by NosTale. /// /// /// The receive must be hooked for this event to be called. /// public event Func? PacketReceive; /// /// Send the given packet. /// /// The packet to send. /// A result that may or may not have succeeded. public Result SendPacket(string packet) { try { _callingSend = true; using var nostaleString = NostaleStringA.Create(_bindingManager.Memory, packet); _sendHook.OriginalFunction.GetWrapper()(GetManagerAddress(false), nostaleString.Get()); _callingSend = false; } catch (Exception e) { return e; } return Result.FromSuccess(); } /// /// Receive the given packet. /// /// The packet to receive. /// A result that may or may not have succeeded. public Result ReceivePacket(string packet) { try { _callingReceive = true; using var nostaleString = NostaleStringA.Create(_bindingManager.Memory, packet); _receiveHook.OriginalFunction.GetWrapper()(GetManagerAddress(true), nostaleString.Get()); _callingReceive = false; } catch (Exception e) { return e; } return Result.FromSuccess(); } /// /// Enable all networking hooks. /// public void EnableHooks() { _receiveHook.Hook.EnableOrActivate(); _sendHook.Hook.EnableOrActivate(); } /// /// Disable all the hooks that are currently enabled. /// public void DisableHooks() { _receiveHook.Hook.Disable(); _sendHook.Hook.Disable(); } private nuint GetManagerAddress(bool third) { nuint networkManager = _networkManagerAddress; _bindingManager.Memory.Read(networkManager, out networkManager); _bindingManager.Memory.Read(networkManager, out networkManager); _bindingManager.Memory.Read(networkManager, out networkManager); if (third) { _bindingManager.Memory.Read(networkManager + 0x34, out networkManager); } return networkManager; } private nuint SendPacketDetour(nuint packetObject, nuint packetString) { if (_callingSend) { return 1; } var packet = Marshal.PtrToStringAnsi((IntPtr)packetString); if (packet is null) { // ? return 1; } var result = PacketSend?.Invoke(packet); return result ?? true ? (nuint)1 : 0; } private nuint ReceivePacketDetour(nuint packetObject, nuint packetString) { if (_callingReceive) { return 1; } var packet = Marshal.PtrToStringAnsi((IntPtr)packetString); if (packet is null) { // ? return 1; } var result = PacketReceive?.Invoke(packet); return result ?? true ? (nuint)1 : 0; } }