Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion bindings/cs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -484,4 +484,7 @@ $RECYCLE.BIN/
*.swp

# JetBrains Rider
.idea/
.idea/

# libhat_c binaries should only exist locally
libhat_c.*
50 changes: 50 additions & 0 deletions bindings/cs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# libhat-sharp
C# bindings for libhat, a high-performance game hacking library.

## Notes
- Currently only includes the pattern scanning functionality of libhat (libhat_c only implements pattern scanning)
- Supports Windows only, x86 and x64
- Requires .NET 8, support for .NET Standard 2.0 will be added later
- Linux support will be added in the future

## Installation
The library is available on NuGet: https://www.nuget.org/packages/libhat-sharp/

You can install it using the following command:
`dotnet add package libhat-sharp`

## Usage
```csharp
using Hat;

// Parse a pattern's string representation to an array of bytes at runtime
Pattern pattern = new Pattern("48 8D 05 ? ? ? ? E8");

// Create a scanner object for a section in a specific module
Process process = /* ... */;
Scanner scanner = new Scanner(process.MainModule, ".text");

// ...or for a specific memory region
nint start = /* the start of the memory region you want to scan */;
uint size = /* ... */;
Scanner scanner = new Scanner(start, size);

// A span can also be used
Span<byte> buffer = /* ... */;
Scanner scanner = new Scanner(buffer);

// Scan for this pattern using your CPU's vectorization features
ScanResult? result = scanner.FindPattern(pattern);

// Get the address pointed at by the pattern
nint address = result!.Address;

// Resolve an RIP relative address at a given offset
//
// | signature matches here
// | | relative address located at +3
// v v
// 48 8D 05 BE 53 23 01 lea rax, [rip+0x12353be]
//
nint relativeAddress = result!.Relative(3);
```
41 changes: 41 additions & 0 deletions bindings/cs/libhat-sharp/ScanResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
namespace Hat;

/// <summary>
/// Represents the result of a pattern scan.
/// </summary>
public unsafe class ScanResult
{
/// <summary>
/// The resulting address from the pattern scan.
/// </summary>
public nint Address { get; }

internal ScanResult(nint address)
{
Address = address;
}

/// <summary>
/// Reads a value of type <typeparamref name="T"/> located at a given offset.
/// </summary>
/// <param name="offset">The offset of the value. Defaults to 0.</param>
public T Read<T>(int offset = 0) where T : unmanaged
{
return *(T*)(Address + offset);
}

/// <summary>
/// Resolves a RIP relative address located at a given offset.
/// </summary>
/// <param name="offset">The offset of the relative address.</param>
/// <param name="remaining">The amount of bytes remaining after the relative address.</param>
/// <remarks>
/// In some cases, the instruction pointed at by the result does not end with the relative address.
/// To prevent incorrect results caused by the remaining bytes, the <paramref name="remaining"/> parameter
/// should be used to specify the amount of bytes remaining in the instruction.
/// </remarks>
public nint Relative(int offset, int remaining = 0)
{
return Address + Read<int>(offset) + offset + sizeof(int) + remaining;
}
}
16 changes: 12 additions & 4 deletions bindings/cs/libhat-sharp/Scanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,22 +57,30 @@ public Scanner(Span<byte> buffer)
_size = (uint)buffer.Length;
}

/// <summary>
/// Creates a scanner for a <see cref="Memory{T}"/> of bytes.
/// </summary>
/// <param name="buffer">The buffer to scan in.</param>
public Scanner(Memory<byte> buffer) : this(buffer.Span) { }

/// <summary>
/// Scans for a pattern.
/// </summary>
/// <param name="pattern">The pattern to scan for.</param>
/// <param name="alignment">The byte alignment of the result.</param>
/// <returns>The address of the pattern if found, otherwise, 0.</returns>
public nint FindPattern(Pattern pattern, ScanAlignment alignment = ScanAlignment.X1)
/// <returns>A <see cref="ScanResult"/> containing the result of the scan if found, otherwise null.</returns>
public ScanResult? FindPattern(Pattern pattern, ScanAlignment alignment = ScanAlignment.X1)
{
if (_buffer is not null && _size is not null)
{
return Functions.libhat_find_pattern(pattern.Signature, _buffer.Value, _size.Value, alignment);
var result = Functions.libhat_find_pattern(pattern.Signature, _buffer.Value, _size.Value, alignment);
return result == 0 ? null : new ScanResult(result);
}

if (_section is not null && _module is not null)
{
return Functions.libhat_find_pattern_mod(pattern.Signature, _module.Value, _section, alignment);
var result = Functions.libhat_find_pattern_mod(pattern.Signature, _module.Value, _section, alignment);
return result == 0 ? null : new ScanResult(result);
}

throw new InvalidOperationException("Scanner is not initialized.");
Expand Down
28 changes: 28 additions & 0 deletions bindings/cs/libhat-sharp/libhat-sharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,34 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<AssemblyVersion>0.2.0</AssemblyVersion>
<FileVersion>0.2.0.1</FileVersion>

<!-- nuget package -->
<PackageId>libhat-sharp</PackageId>
<Version>0.2.0</Version>
<Authors>phase</Authors>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Title>libhat-sharp</Title>
<Description>
C# bindings for libhat, a high-performance game hacking library.
Currently only includes the pattern scanner, which uses CPU vectorization features to quickly find patterns.
</Description>
<PackageProjectUrl>https://github.com/BasedInc/libhat</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/phasephasephase/libhat</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>libhat;vectorized;game-hacking;modding;pattern-scanning</PackageTags>
<PackageReleaseNotes>
- Updated to latest libhat release
- Added missing summary for ScanResult class
</PackageReleaseNotes>
</PropertyGroup>

<ItemGroup>
<!-- make README available in the package -->
<None Include="../README.md" Pack="true" PackagePath="/" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
When building, place your compiled libhat_c binary here.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
When building, place your compiled libhat_c binary here.
8 changes: 4 additions & 4 deletions bindings/cs/libhat-tests/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@

var pattern = randomBytes.AsSpan().Slice(0x1000, 0x10).ToArray().AsPattern();
var scanner = new Scanner(Marshal.UnsafeAddrOfPinnedArrayElement(randomBytes, 0), (uint)randomBytes.Length);
var address = scanner.FindPattern(pattern);
var result = scanner.FindPattern(pattern);

Console.WriteLine($"found pattern at 0x{address:X}");
if (result != null) Console.WriteLine($"found pattern at 0x{result.Address:X}");

Console.WriteLine("\nscanning in module:");

var modulePattern = new Pattern("48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 48 81 EC");

var module = Process.GetCurrentProcess().MainModule!;
var moduleScanner = new Scanner(module);
var moduleAddress = moduleScanner.FindPattern(modulePattern);
var moduleResult = moduleScanner.FindPattern(modulePattern);

Console.WriteLine($"found pattern at 0x{moduleAddress:X}");
if (moduleResult != null) Console.WriteLine($"found pattern at 0x{moduleResult.Address:X}");
4 changes: 3 additions & 1 deletion bindings/cs/libhat-tests/libhat-tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
<RootNamespace>Hat.Tests</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Configurations>Debug;Release</Configurations>
<Platforms>AnyCPU</Platforms>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\libhat-sharp\libhat-sharp.csproj"/>
<PackageReference Include="libhat-sharp" Version="0.1.2" />
</ItemGroup>

</Project>