Llama.Memory
1.0.3
dotnet add package Llama.Memory --version 1.0.3
NuGet\Install-Package Llama.Memory -Version 1.0.3
<PackageReference Include="Llama.Memory" Version="1.0.3" />
<PackageVersion Include="Llama.Memory" Version="1.0.3" />
<PackageReference Include="Llama.Memory" />
paket add Llama.Memory --version 1.0.3
#r "nuget: Llama.Memory, 1.0.3"
#:package Llama.Memory@1.0.3
#addin nuget:?package=Llama.Memory&version=1.0.3
#tool nuget:?package=Llama.Memory&version=1.0.3
Llama.Memory
Llama.Memory is a lightweight byte-pattern scanning and PE header utility library for .NET.
It provides GreyMagic-style pattern strings with wildcard bytes and post-match cursor commands, plus a dependency-free PE header parser for locating image sections in unmanaged memory snapshots or raw executable files.
Features
- Pattern searching: Scans contiguous byte buffers using
Span<T>,SearchValues<T>, and optimized anchor selection. - Multi-pattern scanning: Resolves the first hit for each pattern, or all hits for each pattern, in one scanner call.
- PE header parsing: Reads DOS/NT headers, image base, and section data without third-party PE parsing dependencies.
- Cursor commands in patterns: Append address adjustment, reads, and relative tracing commands after the byte pattern.
- Unmanaged memory support:
UnmanagedMemoryManager<T>can wrap raw unmanaged memory asMemory<T>for APIs that consumeMemory<T>.
Pattern Format
Patterns are space-separated tokens:
{hex bytes and wildcards} {optional commands}
Wildcard bytes can be written as ?? or ?. Half-byte wildcards such as 4? and ?F are also accepted.
Commands are executed sequentially after the initial byte pattern is matched:
Add #- Shifts the result pointer forward by#bytes. Decimal and hex operands are supported.Sub #- Shifts the result pointer backward by#bytes. Decimal and hex operands are supported.Read8- Reads one byte from the current result offset.Read16- Reads a little-endianInt16from the current result offset.Read32- Reads a little-endianInt32from the current result offset.Read64- Reads a little-endianInt64from the current result offset.TraceRelative- Reads a 32-bit relative displacement at the current result offset and returnscurrent + 4 + displacement + ImageBase.TraceCall- Resolves anE8 rel32-style call by reading the 32-bit displacement atcurrent + 1and returningcurrent + 5 + displacement + ImageBase.
Example:
48 8D 0D ? ? ? ? E8 ? ? ? ? 48 8B F0 48 85 C0 74 ? 48 83 38 Add 3 TraceRelative
Usage Examples
Offline Analysis From a File
using Llama.Memory;
var peHeader = PeHeaderParser.GetPeHeaders(@"ffxiv_dx11.exe");
var textSection = peHeader.TextSection
?? throw new InvalidDataException("The PE file does not contain a .text section.");
var fileBytes = File.ReadAllBytes(@"ffxiv_dx11.exe");
var sectionData = fileBytes.AsMemory(
checked((int)textSection.PointerToRawData),
checked((int)textSection.SizeOfRawData));
var scanner = new PatternSearcher(
sectionData,
new IntPtr(textSection.VirtualAddress));
var result = scanner.Search(
"48 8D 0D ? ? ? ? E8 ? ? ? ? 48 8B F0 48 85 C0 74 ? 48 83 38 Add 3 TraceRelative");
Console.WriteLine($"Match found at: 0x{result.ToInt64():X}");
The ImageBase constructor argument is added to raw match offsets when results are returned. For PE section scans, pass the section VirtualAddress if you want returned values expressed as RVAs for that section.
Live Memory Snapshot
For an external process, read the target memory range into a byte buffer first, then scan that buffer:
using System.Diagnostics;
using Llama.Memory;
var process = Process.GetCurrentProcess();
var module = process.MainModule
?? throw new InvalidOperationException("The process has no main module.");
var memorySize = module.ModuleMemorySize;
var baseAddress = module.BaseAddress;
var bytes = new byte[memorySize];
// Fill 'bytes' with the target range, for example via ReadProcessMemory.
var scanner = new PatternSearcher(bytes, baseAddress);
var result = scanner.Search(
"48 8D 0D ? ? ? ? E8 ? ? ? ? 48 8B F0 48 85 C0 74 ? 48 83 38 Add 3 TraceRelative");
For an internal or injected scenario with an unmanaged pointer, wrap the pointer in UnmanagedMemoryManager<byte> and pass its Memory to PatternSearcher.
API Reference
PatternSearcher
Constructors:
PatternSearcher(byte[] assemblyData, IntPtr imageBase)PatternSearcher(Span<byte> assemblyData, IntPtr imageBase)PatternSearcher(ref ReadOnlySpan<byte> assemblyData, IntPtr imageBase)PatternSearcher(ReadOnlySpan<byte> assemblyData, IntPtr imageBase)PatternSearcher(Memory<byte> assemblyData, IntPtr imageBase)
Search methods:
IntPtr Search(string pattern)- Returns the first transformed match, orIntPtr.Zero.IntPtr Search(string pattern, IntPtr start, int maxSearchLength)- Returns the first transformed match in a raw buffer sub-range.startis a zero-based buffer offset, not an address withImageBasealready applied.IntPtr[] SearchMany(string pattern)- Returns every transformed match for one pattern.IntPtr[] Search(string[] patterns)- Returns the first transformed match for each pattern. Results are index-aligned with the input array.IntPtr[][] SearchMany(string[] patterns)- Returns every transformed match for each pattern. Results are index-aligned with the input array.ReadOnlySpan<byte> GetSlice(int start, int length)- Returns a read-only span over a raw buffer sub-range.
PeHeaderParser
PeHeaderInfo GetPeHeaders(string filePath)- Parses the PE headers and returns the image base plus all section headers.uint RvaToFileOffset(uint rva, SimpleSectionHeader[] sections)- Converts an RVA to a file offset using the parsed section table.
PeHeaderInfo
PeHeaderInfo contains:
ulong ImageBaseSimpleSectionHeader[] SectionsSimpleSectionHeader this[int index]SimpleSectionHeader? TextSectionSimpleSectionHeader? RdataSectionSimpleSectionHeader? DataSection
SimpleSectionHeader
Represents a PE section with:
NameVirtualSizeVirtualAddressSizeOfRawDataPointerToRawData
FfxivVersionChecker
(string Version, string Date) GetVersion(FileInfo ffxivExe)- Reads the expected version marker from the data section.(string Version, string Date) GetVersionPattern(FileInfo ffxivExe)- Locates the version marker via pattern scanning, then reads and parses it.
UnmanagedMemoryManager<T>
Wraps an unmanaged pointer and length as a Memory<T> source. The caller owns the lifetime of the unmanaged memory.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.