// // 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.Diagnostics; using System.Runtime.InteropServices; using NosSmooth.LocalBinding.Errors; 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 )] private delegate void PacketSendDelegate(IntPtr packetObject, IntPtr packetString); [Function ( new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx }, FunctionAttribute.Register.eax, FunctionAttribute.StackCleanup.Callee )] private delegate void PacketReceiveDelegate(IntPtr packetObject, IntPtr 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.CompiledFindPattern(options.NetworkObjectPattern); if (!networkObjectAddress.Found) { return new BindingNotFoundError(options.NetworkObjectPattern, "NetworkBinding"); } var packetSendAddress = bindingManager.Scanner.CompiledFindPattern(options.SendFunctionPattern); if (!packetSendAddress.Found) { return new BindingNotFoundError(options.SendFunctionPattern, "NetworkBinding.SendPacket"); } var packetReceiveAddress = bindingManager.Scanner.CompiledFindPattern(options.ReceiveFunctionPattern); if (!packetReceiveAddress.Found) { return new BindingNotFoundError(options.ReceiveFunctionPattern, "NetworkBinding.ReceivePacket"); } var sendFunction = bindingManager.Hooks.CreateFunction (packetSendAddress.Offset + (int)process.MainModule!.BaseAddress); var sendWrapper = sendFunction.GetWrapper(); var receiveFunction = bindingManager.Hooks.CreateFunction (packetReceiveAddress.Offset + (int)process.MainModule!.BaseAddress); var receiveWrapper = receiveFunction.GetWrapper(); var binding = new NetworkBinding ( bindingManager, (IntPtr)(networkObjectAddress.Offset + (int)process.MainModule!.BaseAddress + 0x01), sendWrapper, receiveWrapper ); if (options.HookSend) { binding._sendHook = sendFunction .Hook(binding.SendPacketDetour); binding._originalSend = binding._sendHook.OriginalFunction; } if (options.HookReceive) { binding._receiveHook = receiveFunction .Hook(binding.ReceivePacketDetour); binding._originalReceive = binding._receiveHook.OriginalFunction; } binding._sendHook?.Activate(); binding._receiveHook?.Activate(); return binding; } private readonly NosBindingManager _bindingManager; private readonly IntPtr _networkManagerAddress; private IHook? _sendHook; private IHook? _receiveHook; private PacketSendDelegate _originalSend; private PacketReceiveDelegate _originalReceive; private NetworkBinding ( NosBindingManager bindingManager, IntPtr networkManagerAddress, PacketSendDelegate originalSend, PacketReceiveDelegate originalReceive ) { _bindingManager = bindingManager; _networkManagerAddress = networkManagerAddress; _originalSend = originalSend; _originalReceive = originalReceive; } /// /// 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 { using var nostaleString = NostaleStringA.Create(_bindingManager.Memory, packet); _originalSend(GetManagerAddress(false), nostaleString.Get()); } 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 { using var nostaleString = NostaleStringA.Create(_bindingManager.Memory, packet); _originalReceive(GetManagerAddress(true), nostaleString.Get()); } catch (Exception e) { return e; } return Result.FromSuccess(); } /// /// Disable all the hooks that are currently enabled. /// /// A result that may or may not have succeeded. public Result DisableHooks() { _receiveHook?.Disable(); _sendHook?.Disable(); return Result.FromSuccess(); } private IntPtr GetManagerAddress(bool third) { IntPtr 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 void SendPacketDetour(IntPtr packetObject, IntPtr packetString) { var packet = Marshal.PtrToStringAnsi(packetString); if (packet is null) { // ? _originalSend(packetObject, packetString); } else { var result = PacketSend?.Invoke(packet); if (result ?? true) { _originalSend(packetObject, packetString); } } } private void ReceivePacketDetour(IntPtr packetObject, IntPtr packetString) { var packet = Marshal.PtrToStringAnsi(packetString); if (packet is null) { // ? _originalReceive(packetObject, packetString); } else { var result = PacketReceive?.Invoke(packet); if (result ?? true) { _originalReceive(packetObject, packetString); } } } }