~ruther/NosSmooth

f8a901cda2c558c993c646e931adca56c57be4e7 — František Boháček 3 years ago d5a38ce
feat: add .net nostale bindings
A Local/NosSmooth.LocalBinding/Errors/BindingNotFoundError.cs => Local/NosSmooth.LocalBinding/Errors/BindingNotFoundError.cs +17 -0
@@ 0,0 1,17 @@
//
//  BindingNotFoundError.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 Remora.Results;

namespace NosSmooth.LocalBinding.Errors;

/// <summary>
/// The memory pattern was not found in the memory.
/// </summary>
/// <param name="Pattern">The pattern that could not be found.</param>
/// <param name="Path">The entity the pattern should represent.</param>
public record BindingNotFoundError(string Pattern, string Path)
    : ResultError($"Could not find pattern ({Pattern}) in the memory while searching for {Path}.");

A Local/NosSmooth.LocalBinding/Extensions/ServiceCollectionExtensions.cs => Local/NosSmooth.LocalBinding/Extensions/ServiceCollectionExtensions.cs +35 -0
@@ 0,0 1,35 @@
//
//  ServiceCollectionExtensions.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 Microsoft.Extensions.DependencyInjection;
using NosSmooth.LocalBinding.Objects;

namespace NosSmooth.LocalBinding.Extensions;

/// <summary>
/// Contains extension methods for <see cref="IServiceCollection"/>.
/// </summary>
public static class ServiceCollectionExtensions
{
    /// <summary>
    /// Adds bindings to Nostale objects along with <see cref="NosBindingManager"/> to initialize those.
    /// </summary>
    /// <remarks>
    /// Adds <see cref="CharacterBinding"/> and <see cref="NetworkBinding"/>.
    /// You have to initialize these using <see cref="NosBindingManager"/>
    /// prior to requesting them from the provider, otherwise an exception
    /// will be thrown.
    /// </remarks>
    /// <param name="serviceCollection">The service collection.</param>
    /// <returns>The collection.</returns>
    public static IServiceCollection AddNostaleBindings(this IServiceCollection serviceCollection)
    {
        return serviceCollection
            .AddSingleton<NosBindingManager>()
            .AddSingleton(p => p.GetRequiredService<NosBindingManager>().Character)
            .AddSingleton(p => p.GetRequiredService<NosBindingManager>().Network);
    }
}
\ No newline at end of file

A Local/NosSmooth.LocalBinding/IsExternalInit.cs => Local/NosSmooth.LocalBinding/IsExternalInit.cs +15 -0
@@ 0,0 1,15 @@
//
//  IsExternalInit.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.

namespace System.Runtime.CompilerServices
{
    /// <summary>
    /// Dummy.
    /// </summary>
    public class IsExternalInit
    {
    }
}
\ No newline at end of file

A Local/NosSmooth.LocalBinding/NosBindingManager.cs => Local/NosSmooth.LocalBinding/NosBindingManager.cs +146 -0
@@ 0,0 1,146 @@
//
//  NosBindingManager.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 Microsoft.Extensions.Options;
using NosSmooth.LocalBinding.Objects;
using NosSmooth.LocalBinding.Options;
using Reloaded.Hooks;
using Reloaded.Hooks.Definitions;
using Reloaded.Memory.Sigscan;
using Reloaded.Memory.Sources;
using Remora.Results;

namespace NosSmooth.LocalBinding;

/// <summary>
/// Nostale entity binding manager.
/// </summary>
public class NosBindingManager : IDisposable
{
    private readonly CharacterBindingOptions _characterBindingOptions;
    private readonly NetworkBindingOptions _networkBindingOptions;

    private NetworkBinding? _networkBinding;
    private CharacterBinding? _characterBinding;

    /// <summary>
    /// Initializes a new instance of the <see cref="NosBindingManager"/> class.
    /// </summary>
    /// <param name="characterBindingOptions">The character binding options.</param>
    /// <param name="networkBindingOptions">The network binding options.</param>
    public NosBindingManager
    (
        IOptions<CharacterBindingOptions> characterBindingOptions,
        IOptions<NetworkBindingOptions> networkBindingOptions
    )
    {
        Hooks = new ReloadedHooks();
        Memory = new Memory();
        Scanner = new Scanner(Process.GetCurrentProcess(), Process.GetCurrentProcess().MainModule);
        _characterBindingOptions = characterBindingOptions.Value;
        _networkBindingOptions = networkBindingOptions.Value;
    }

    /// <summary>
    /// Gets the memory scanner.
    /// </summary>
    internal Scanner Scanner { get; }

    /// <summary>
    /// Gets the reloaded hooks.
    /// </summary>
    internal IReloadedHooks Hooks { get; }

    /// <summary>
    /// Gets the current process memory.
    /// </summary>
    internal IMemory Memory { get; }

    /// <summary>
    /// Gets the network binding.
    /// </summary>
    /// <exception cref="InvalidOperationException">Thrown if the manager is not initialized yet.</exception>
    public NetworkBinding Network
    {
        get
        {
            if (_networkBinding is null)
            {
                throw new InvalidOperationException
                    ("Could not get network. The binding manager is not initialized. Did you forget to call NosBindingManager.Initialize?");
            }

            return _networkBinding;
        }
    }

    /// <summary>
    /// Gets the character binding.
    /// </summary>
    /// <exception cref="InvalidOperationException">Thrown if the manager is not initialized yet.</exception>
    public CharacterBinding Character
    {
        get
        {
            if (_characterBinding is null)
            {
                throw new InvalidOperationException
                    ("Could not get character. The binding manager is not initialized. Did you forget to call NosBindingManager.Initialize?");
            }

            return _characterBinding;
        }
    }

    /// <summary>
    /// Initialize the existing bindings and hook NosTale functions.
    /// </summary>
    /// <returns>A result that may or may not have succeeded.</returns>
    public Result Initialize()
    {
        var network = NetworkBinding.Create(this, _networkBindingOptions);
        if (!network.IsSuccess)
        {
            return Result.FromError(network);
        }
        _networkBinding = network.Entity;

        var character = CharacterBinding.Create(this, _characterBindingOptions);
        if (!character.IsSuccess)
        {
            return Result.FromError(character);
        }
        _characterBinding = character.Entity;

        return Result.FromSuccess();
    }

    /// <summary>
    /// Disable the currently enabled nostale hooks.
    /// </summary>
    /// <returns>A result that may or may not have succeeded.</returns>
    public Result DisableHooks()
    {
        if (_networkBinding is not null)
        {
            var result = _networkBinding.DisableHooks();
            if (!result.IsSuccess)
            {
                return result;
            }
        }

        return Result.FromSuccess();
    }

    /// <inheritdoc />
    public void Dispose()
    {
        Scanner.Dispose();
        DisableHooks();
    }
}
\ No newline at end of file

A Local/NosSmooth.LocalBinding/NosSmooth.LocalBinding.csproj => Local/NosSmooth.LocalBinding/NosSmooth.LocalBinding.csproj +18 -0
@@ 0,0 1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <TargetFramework>netstandard2.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <LangVersion>10</LangVersion>
    </PropertyGroup>

    <ItemGroup>
      <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
      <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
      <PackageReference Include="Reloaded.Hooks" Version="3.5.0" />
      <PackageReference Include="Reloaded.Memory.Sigscan" Version="1.2.1" />
      <PackageReference Include="Remora.Results" Version="7.1.0" />
    </ItemGroup>

</Project>

A Local/NosSmooth.LocalBinding/Objects/CharacterBinding.cs => Local/NosSmooth.LocalBinding/Objects/CharacterBinding.cs +137 -0
@@ 0,0 1,137 @@
//
//  CharacterBinding.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 NosSmooth.LocalBinding.Errors;
using NosSmooth.LocalBinding.Options;
using Reloaded.Hooks.Definitions;
using Reloaded.Hooks.Definitions.X86;
using Remora.Results;

namespace NosSmooth.LocalBinding.Objects;

/// <summary>
/// The nostale binding of a character.
/// </summary>
public class CharacterBinding
{
        [Function
    (
        new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx },
        FunctionAttribute.Register.eax,
        FunctionAttribute.StackCleanup.Callee
    )]
    private delegate void WalkDelegate(IntPtr characterObject, int position, int unknown = 1);

    /// <summary>
    /// Create the network binding with finding the network object and functions.
    /// </summary>
    /// <param name="bindingManager">The binding manager.</param>
    /// <param name="options">The options for the binding.</param>
    /// <returns>A network binding or an error.</returns>
    public static Result<CharacterBinding> Create(NosBindingManager bindingManager, CharacterBindingOptions options)
    {
        var process = Process.GetCurrentProcess();
        var characterObjectAddress = bindingManager.Scanner.CompiledFindPattern(options.CharacterObjectPattern);
        if (!characterObjectAddress.Found)
        {
            return new BindingNotFoundError(options.CharacterObjectPattern, "CharacterBinding");
        }

        var walkFunctionAddress = bindingManager.Scanner.CompiledFindPattern(options.WalkFunctionPattern);
        if (!walkFunctionAddress.Found)
        {
            return new BindingNotFoundError(options.WalkFunctionPattern, "CharacterBinding.Walk");
        }

        var walkFunction = bindingManager.Hooks.CreateFunction<WalkDelegate>
            (walkFunctionAddress.Offset + (int)process.MainModule!.BaseAddress);
        var walkWrapper = walkFunction.GetWrapper();

        var binding = new CharacterBinding
        (
            bindingManager,
            (IntPtr)(characterObjectAddress.Offset + (int)process.MainModule!.BaseAddress + 0x06),
            walkWrapper
        );

        if (options.HookWalk)
        {
            binding._walkHook = walkFunction
                .Hook(binding.WalkDetour);
            binding._originalWalk = binding._walkHook.OriginalFunction;
        }

        binding._walkHook?.Activate();
        return binding;
    }

    private readonly NosBindingManager _bindingManager;
    private readonly IntPtr _characterAddress;
    private IHook<WalkDelegate>? _walkHook;
    private WalkDelegate _originalWalk;

    private CharacterBinding
    (
        NosBindingManager bindingManager,
        IntPtr characterAddress,
        WalkDelegate originalWalk
    )
    {
        _bindingManager = bindingManager;
        _characterAddress = characterAddress;
        _originalWalk = originalWalk;
    }

    /// <summary>
    /// Event that is called when walk was called by NosTale.
    /// </summary>
    /// <remarks>
    /// The walk must be hooked for this event to be called.
    /// </remarks>
    public event Func<ushort, ushort, bool>? WalkCall;

    /// <summary>
    /// Disable all the hooks that are currently enabled.
    /// </summary>
    /// <returns>A result that may or may not have succeeded.</returns>
    public Result DisableHooks()
    {
        _walkHook?.Disable();
        return Result.FromSuccess();
    }

    private IntPtr GetCharacterAddress()
    {
        IntPtr characterAddress = _characterAddress;
        _bindingManager.Memory.Read(characterAddress, out characterAddress);
        _bindingManager.Memory.Read(characterAddress, out characterAddress);

        return characterAddress;
    }

    /// <summary>
    /// Walk to the given position.
    /// </summary>
    /// <param name="x">The x coordinate.</param>
    /// <param name="y">The y coordinate.</param>
    /// <returns>A result that may or may not have succeeded.</returns>
    public Result Walk(ushort x, ushort y)
    {
        int param = (y << 16) | x;
        _originalWalk(GetCharacterAddress(), param);
        return Result.FromSuccess();
    }

    private void WalkDetour(IntPtr characterObject, int position, int unknown)
    {
        var result = WalkCall?.Invoke((ushort)(position & 0xFFFF), (ushort)((position >> 16) & 0xFFFF));
        if (result ?? true)
        {
            _originalWalk(characterObject, position, unknown);
        }
    }
}
\ No newline at end of file

A Local/NosSmooth.LocalBinding/Objects/NetworkBinding.cs => Local/NosSmooth.LocalBinding/Objects/NetworkBinding.cs +236 -0
@@ 0,0 1,236 @@
//
//  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;

/// <summary>
/// The binding to nostale network object.
/// </summary>
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);

    /// <summary>
    /// Create the network binding with finding the network object and functions.
    /// </summary>
    /// <param name="bindingManager">The binding manager.</param>
    /// <param name="options">The options for the binding.</param>
    /// <returns>A network binding or an error.</returns>
    public static Result<NetworkBinding> 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<PacketSendDelegate>
            (packetSendAddress.Offset + (int)process.MainModule!.BaseAddress);
        var sendWrapper = sendFunction.GetWrapper();

        var receiveFunction = bindingManager.Hooks.CreateFunction<PacketReceiveDelegate>
            (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<PacketSendDelegate>? _sendHook;
    private IHook<PacketReceiveDelegate>? _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;
    }

    /// <summary>
    /// Event that is called when packet send was called by NosTale.
    /// </summary>
    /// <remarks>
    /// The send must be hooked for this event to be called.
    /// </remarks>
    public event Func<string, bool>? PacketSend;

    /// <summary>
    /// Event that is called when packet receive was called by NosTale.
    /// </summary>
    /// <remarks>
    /// The receive must be hooked for this event to be called.
    /// </remarks>
    public event Func<string, bool>? PacketReceive;

    /// <summary>
    /// Send the given packet.
    /// </summary>
    /// <param name="packet">The packet to send.</param>
    /// <returns>A result that may or may not have succeeded.</returns>
    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();
    }

    /// <summary>
    /// Receive the given packet.
    /// </summary>
    /// <param name="packet">The packet to receive.</param>
    /// <returns>A result that may or may not have succeeded.</returns>
    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();
    }

    /// <summary>
    /// Disable all the hooks that are currently enabled.
    /// </summary>
    /// <returns>A result that may or may not have succeeded.</returns>
    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);
            }
        }
    }
}
\ No newline at end of file

A Local/NosSmooth.LocalBinding/Objects/NostaleStringA.cs => Local/NosSmooth.LocalBinding/Objects/NostaleStringA.cs +84 -0
@@ 0,0 1,84 @@
//
//  NostaleStringA.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.Text;
using Reloaded.Memory.Sources;

namespace NosSmooth.LocalBinding.Objects;

/// <summary>
/// Represents nostale string object.
/// </summary>
public class NostaleStringA : IDisposable
{
    private readonly IMemory _memory;
    private IntPtr _pointer;

    /// <summary>
    /// Create an instance of <see cref="NostaleStringA"/>.
    /// </summary>
    /// <param name="memory">The memory to allocate the string on.</param>
    /// <param name="data">The string contents.</param>
    /// <returns>A nostale string.</returns>
    public static NostaleStringA Create(IMemory memory, string data)
    {
        var bytes = Encoding.ASCII.GetBytes(data);
        var allocated = memory.Allocate(bytes.Length + 1 + 8);
        memory.SafeWrite(allocated, 1);
        memory.SafeWrite(allocated + 4, data.Length);
        memory.SafeWriteRaw(allocated + 8, bytes);
        memory.SafeWrite(allocated + 8 + data.Length, 0);

        return new NostaleStringA(memory, allocated);
    }

    private NostaleStringA(IMemory memory, IntPtr pointer)
    {
        _memory = memory;
        _pointer = pointer;

    }

    /// <summary>
    /// Finalizes an instance of the <see cref="NostaleStringA"/> class.
    /// </summary>
    ~NostaleStringA()
    {
        Free();
    }

    /// <summary>
    /// Gets whether the string is still allocated.
    /// </summary>
    public bool Allocated => _pointer != IntPtr.Zero;

    /// <summary>
    /// Get the pointer to the string.
    /// </summary>
    /// <returns>A pointer to the string to pass to NosTale.</returns>
    public IntPtr Get()
    {
        return _pointer + 0x08;
    }

    /// <summary>
    /// Free the memory allocated by the string.
    /// </summary>
    public void Free()
    {
        if (Allocated)
        {
            _memory.Free(_pointer);
            _pointer = IntPtr.Zero;
        }
    }

    /// <inheritdoc />
    public void Dispose()
    {
        Free();
    }
}
\ No newline at end of file

A Local/NosSmooth.LocalBinding/Options/CharacterBindingOptions.cs => Local/NosSmooth.LocalBinding/Options/CharacterBindingOptions.cs +34 -0
@@ 0,0 1,34 @@
//
//  CharacterBindingOptions.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 NosSmooth.LocalBinding.Objects;

namespace NosSmooth.LocalBinding.Options;

/// <summary>
/// Options for <see cref="CharacterBinding"/>.
/// </summary>
public class CharacterBindingOptions
{
    /// <summary>
    /// Gets or sets whether to hook the walk function.
    /// </summary>
    public bool HookWalk { get; set; } = true;

    /// <summary>
    /// Gets or sets the pattern to find the character object at.
    /// </summary>
    /// <remarks>
    /// The address of the object is "three pointers down" from address found on this pattern.
    /// </remarks>
    public string CharacterObjectPattern { get; set; }
        = "33 C9 8B 55 FC A1 ?? ?? ?? ?? E8 ?? ?? ?? ??";

    /// <summary>
    /// Gets or sets the pattern to find the walk function at.
    /// </summary>
    public string WalkFunctionPattern { get; set; } = "55 8B EC 83 C4 EC 53 56 57 66 89 4D FA";
}
\ No newline at end of file

A Local/NosSmooth.LocalBinding/Options/NetworkBindingOptions.cs => Local/NosSmooth.LocalBinding/Options/NetworkBindingOptions.cs +44 -0
@@ 0,0 1,44 @@
//
//  NetworkBindingOptions.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 NosSmooth.LocalBinding.Objects;

namespace NosSmooth.LocalBinding.Options;

/// <summary>
/// Options for <see cref="NetworkBinding"/>.
/// </summary>
public class NetworkBindingOptions
{
    /// <summary>
    /// Gets or sets whether to hook the send packet function.
    /// </summary>
    public bool HookSend { get; set; } = true;

    /// <summary>
    /// Gets or sets whether to hook the receive packet function.
    /// </summary>
    public bool HookReceive { get; set; } = true;

    /// <summary>
    /// Gets or sets the pattern to find the network object at.
    /// </summary>
    /// <remarks>
    /// The address of the object is "three pointers down" from address found on this pattern.
    /// </remarks>
    public string NetworkObjectPattern { get; set; }
        = "A1 ?? ?? ?? ?? 8B 00 BA ?? ?? ?? ?? E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? A1 ?? ?? ?? ?? 8B 00 8B 40 40";

    /// <summary>
    /// Gets or sets the pattern to find the send packet function at.
    /// </summary>
    public string SendFunctionPattern { get; set; } = "53 56 8B F2 8B D8 EB 04";

    /// <summary>
    /// Gets or sets the pattern to find the receive function at.
    /// </summary>
    public string ReceiveFunctionPattern { get; set; } = "55 8B EC 83 C4 F4 53 56 57 33 C9 89 4D F4 89 55 FC 8B D8 8B 45 FC";
}
\ No newline at end of file

Do not follow this link