Overview
ISigScanner facilitates searching for memory signatures in the game’s process. It provides methods to scan different sections of memory (.text, .data, .rdata) for byte patterns.
Namespace
Properties
IsCopy
public bool IsCopy { get; }
Gets a value indicating whether the search on this module is performed on a copy.
True if searching on a memory copy
Is32BitProcess
public bool Is32BitProcess { get; }
Gets a value indicating whether the ProcessModule is 32-bit.
True if the process is 32-bit
SearchBase
public IntPtr SearchBase { get; }
Gets the base address of the search area. When copied, this will be the address of the copy.
TextSectionBase
public IntPtr TextSectionBase { get; }
Gets the base address of the .text section search area.
The .text section base address
TextSectionOffset
public long TextSectionOffset { get; }
Gets the offset of the .text section from the base of the module.
TextSectionSize
public int TextSectionSize { get; }
Gets the size of the .text section.
The .text section size in bytes
DataSectionBase
public IntPtr DataSectionBase { get; }
Gets the base address of the .data section search area.
The .data section base address
DataSectionOffset
public long DataSectionOffset { get; }
Gets the offset of the .data section from the base of the module.
DataSectionSize
public int DataSectionSize { get; }
Gets the size of the .data section.
The .data section size in bytes
RDataSectionBase
public IntPtr RDataSectionBase { get; }
Gets the base address of the .rdata section search area.
The .rdata section base address
RDataSectionOffset
public long RDataSectionOffset { get; }
Gets the offset of the .rdata section from the base of the module.
The .rdata section offset
RDataSectionSize
public int RDataSectionSize { get; }
Gets the size of the .rdata section.
The .rdata section size in bytes
Module
public ProcessModule Module { get; }
Gets the ProcessModule on which the search is performed.
Methods
GetStaticAddressFromSig
public nint GetStaticAddressFromSig(string signature, int offset = 0);
Scans for a .data address using a .text function. This is intended to be used with IDA sigs.
The signature of the function using the data
The offset from function start of the instruction using the data (default: 0)
An IntPtr to the static memory location
TryGetStaticAddressFromSig
public bool TryGetStaticAddressFromSig(string signature, out nint result, int offset = 0);
Tries to scan for a .data address using a .text function.
The signature of the function using the data
An IntPtr to the static memory location, if found (out parameter)
The offset from function start (default: 0)
True if the signature was found
ScanData
public nint ScanData(string signature);
Scans for a byte signature in the .data section.
The signature to search for
The real offset of the found signature
TryScanData
public bool TryScanData(string signature, out nint result);
Tries to scan for a byte signature in the .data section.
The signature to search for
The real offset of the signature, if found (out parameter)
True if the signature was found
ScanModule
public nint ScanModule(string signature);
Scans for a byte signature in the whole module search area.
The signature to search for
The real offset of the found signature
TryScanModule
public bool TryScanModule(string signature, out nint result);
Tries to scan for a byte signature in the whole module search area.
The signature to search for
The real offset of the signature, if found (out parameter)
True if the signature was found
ScanText
public nint ScanText(string signature);
Scans for a byte signature in the .text section.
The signature to search for
The real offset of the found signature
TryScanText
public bool TryScanText(string signature, out nint result);
Tries to scan for a byte signature in the .text section.
The signature to search for
The real offset of the signature, if found (out parameter)
True if the signature was found
ScanAllText
public nint[] ScanAllText(string signature);
public IEnumerable<nint> ScanAllText(string signature, CancellationToken cancellationToken);
Scans for all matching byte signatures in the .text section.
The signature to search for
return
nint[] | IEnumerable<nint>
Array or enumerable of real offsets of found signatures
ResolveRelativeAddress
public nint ResolveRelativeAddress(nint nextInstAddr, int relOffset);
Resolves a RVA address.
The address of the next instruction
Example Usage
public class MyPlugin : IDalamudPlugin
{
private readonly ISigScanner sigScanner;
private nint importantAddress;
public MyPlugin(ISigScanner sigScanner)
{
this.sigScanner = sigScanner;
// Scan for a signature
if (this.sigScanner.TryScanText(
"E8 ?? ?? ?? ?? 48 8B D8 48 85 C0",
out var address))
{
this.importantAddress = address;
Log.Information($"Found signature at: {address:X}");
}
else
{
Log.Error("Failed to find signature!");
}
}
public void ScanForStaticAddress()
{
// Get a static address from a function signature
if (this.sigScanner.TryGetStaticAddressFromSig(
"48 8D 0D ?? ?? ?? ??",
out var staticAddr,
3)) // Offset to the RVA
{
Log.Information($"Static address: {staticAddr:X}");
}
}
}
Advanced Usage
// Exact bytes
"E8 12 34 56 78"
// Wildcards with ??
"E8 ?? ?? ?? ?? 48 8B"
// IDA-style signature
"48 8D 0D ?? ?? ?? ?? E8"
Finding Multiple Matches
public void FindAllMatches()
{
var signature = "E8 ?? ?? ?? ?? 48";
var matches = this.sigScanner.ScanAllText(signature);
Log.Information($"Found {matches.Length} matches");
foreach (var match in matches)
{
Log.Information($" Match at: {match:X}");
}
}
Resolving Relative Addresses
public nint GetFunctionAddress()
{
// Find call instruction
var callAddr = this.sigScanner.ScanText("E8 ?? ?? ?? ??");
// Read the relative offset (4 bytes after E8)
var relOffset = Marshal.ReadInt32(callAddr + 1);
// Resolve to absolute address
// nextInstAddr = callAddr + 5 (instruction length)
var funcAddr = this.sigScanner.ResolveRelativeAddress(
callAddr + 5,
relOffset);
return funcAddr;
}
Section-Specific Scanning
public void ScanSpecificSections()
{
// Scan only in .text section (code)
if (this.sigScanner.TryScanText("E8 ?? ?? ??", out var textAddr))
{
Log.Information($"Found in .text: {textAddr:X}");
}
// Scan only in .data section (initialized data)
if (this.sigScanner.TryScanData("12 34 56 78", out var dataAddr))
{
Log.Information($"Found in .data: {dataAddr:X}");
}
// Scan entire module
if (this.sigScanner.TryScanModule("AB CD EF", out var moduleAddr))
{
Log.Information($"Found in module: {moduleAddr:X}");
}
}
Signature Best Practices
- Use wildcards for values that may change between versions
- Keep signatures unique to avoid false positives
- Include enough context (usually 10-15 bytes minimum)
- Test across game versions when possible
- Use Try methods* for graceful failure handling
- Signatures use hexadecimal byte patterns
- Use
?? as a wildcard for bytes that may vary
- The .text section contains executable code
- The .data section contains initialized global/static variables
- The .rdata section contains read-only data
- Always use Try* methods and handle failures gracefully
- Signatures may break between game updates
- Resolved addresses are absolute memory addresses, not RVAs