Skip to main content

Overview

Static address attributes are used to locate static objects and virtual tables in the game binary. These are essential for accessing singletons and enabling static function hooking.

StaticAddress Attribute

The [StaticAddress] attribute resolves the location of static objects like singletons.

Attribute Definition

signature
string
required
The signature pattern to locate the address in the game binary.
offset
int
required
The offset within the matched instruction to read the address from (0-indexed).
isPointer
bool
default:"false"
Whether the resolved address is a pointer to the instance (true) or the instance itself (false).

Basic Usage

[StaticAddress("48 8B 1D ?? ?? ?? ?? 8B 7C 24", 3, isPointer: true)]
public static partial Framework* Instance();

Generated Code

For pointer-type static addresses:
public unsafe static class StaticAddressPointers
{
    public static Framework** ppInstance 
        => (Framework**)Framework.Addresses.Instance.Value;
}

public static partial Framework* Instance()
{
    if (StaticAddressPointers.ppInstance is null)
    {
        ThrowHelper.ThrowNullAddress(
            "Framework.Instance", 
            "48 8B 1D ?? ?? ?? ?? 8B 7C 24");
    }
    return *StaticAddressPointers.ppInstance;
}

Pointer vs Instance

The isPointer parameter determines how the address is interpreted:

Static Pointer (isPointer: true)

The address points to a pointer that points to the actual instance (heap-allocated):
// Common pattern: static ClassName* instance;
[StaticAddress("48 8B 1D ?? ?? ?? ?? 8B 7C 24", 3, isPointer: true)]
public static partial Framework* Instance();

// Resolves: Binary -> Pointer Location -> Framework* -> Framework instance

Static Instance (isPointer: false)

The address points directly to the instance (binary-allocated):
// Rare pattern: static ClassName instance;
[StaticAddress("48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 84 C0 75 06", 3)]
public static partial UIState* Instance();

// Resolves: Binary -> UIState instance directly

Understanding Offset Parameter

The offset specifies where to read the RIP-relative address within the matched instruction:
; Example instruction: 48 8B 0D [XX XX XX XX]
; Bytes:                0  1  2  3  4  5  6
;
; Signature: "48 8B 0D ?? ?? ?? ??"
; Offset: 3 (points to the first ?? which contains the RIP-relative offset)

mov rcx, [rip + offset]  ; 48 8B 0D [offset]
                         ;  0  1  2   3-6
The offset is usually:
  • 3 for LEA/MOV instructions with RIP-relative addressing
  • 2 for some shorter instructions
  • 7 or higher for instructions with prefixes

Examples from the Codebase

Framework Singleton

From Framework.cs:23:
[StaticAddress("48 8B 1D ?? ?? ?? ?? 8B 7C 24", 3, isPointer: true)]
public static partial Framework* Instance();

// Usage
var framework = Framework.Instance();
if (framework != null)
{
    var deltaTime = framework->FrameDeltaTime;
}

UI State

From UIState.cs:13:
[StaticAddress("48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B 8B ?? ?? ?? ?? 48 8B 01", 3)]
public static partial UIState* Instance();

Client Object Manager

From ClientObjectManager.cs:9:
[StaticAddress("48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? C7 43 60 FF FF FF FF", 3)]
public static partial ClientObjectManager* Instance();

Game Object Manager

From GameObjectManager.cs:7:
[StaticAddress("48 8D 35 ?? ?? ?? ?? 81 FA", 3)]
public static partial GameObjectManager* Instance();

AtkStage

From AtkStage.cs:15:
[StaticAddress("48 8B 05 ?? ?? ?? ?? 4C 8B 40 18 45 8B 40 18", 3, isPointer: true)]
public static partial AtkStage* Instance();

VTableAddress Attribute

The [VTableAddress] attribute locates the static virtual table for a class.

Attribute Definition

signature
string
required
The signature pattern to locate the vtable in the game binary.
offset
int
required
The offset within the matched instruction to read the vtable address.
isPointer
bool
default:"false"
Whether the address is a pointer to the vtable (rare).

Basic Usage

[VTableAddress("48 8d 05 ?? ?? ?? ?? 48 89 03 48 8d 83 50 02 00 00", 3)]
public unsafe partial struct AddonRetainerTaskAsk
{
    [VirtualFunction(48)]
    public partial void OnSetup(uint a2, AtkValue* atkValues);
}

Generated Code

public static class Addresses
{
    public static readonly Address StaticVirtualTable = 
        new Address(
            "FFXIVClientStructs.FFXIV.Client.UI.AddonRetainerTaskAsk.StaticVirtualTable",
            "48 8D 05 ?? ?? ?? ?? 48 89 03 33 C0",
            new ushort[] {3},
            ...);
}

[StructLayout(LayoutKind.Explicit)]
public unsafe partial struct AddonRetainerTaskAskVirtualTable
{
    [FieldOffset(384)] 
    public delegate* unmanaged<AddonRetainerTaskAsk*, uint, AtkValue*, void> OnSetup;
}

public static AddonRetainerTaskAskVirtualTable* StaticVirtualTablePointer 
    => (AddonRetainerTaskAskVirtualTable*)Addresses.StaticVirtualTable.Value;

Use Case: Static Hooking

// Hook a virtual function before any instance exists
var hook = Hook<OnSetupDelegate>.FromAddress(
    (nint)AddonRetainerTaskAsk.StaticVirtualTablePointer->OnSetup,
    OnSetupDetour);

Finding Static Addresses

Using IDA/Ghidra

  1. Search for references to the singleton
  2. Find initialization or usage patterns
  3. Identify the instruction that loads the address
  4. Create a signature around that instruction

Common Patterns

; Pattern 1: LEA (Load Effective Address)
lea rcx, [rip + offset]  ; 48 8D 0D ?? ?? ?? ??

; Pattern 2: MOV from RIP-relative
mov rax, [rip + offset]  ; 48 8B 05 ?? ?? ?? ??

; Pattern 3: MOV pointer from RIP-relative
mov rbx, [rip + offset]  ; 48 8B 1D ?? ?? ?? ??

Signature Best Practices

Make signatures specific enough to be unique, but flexible enough to survive game updates.
// Good - includes surrounding context
[StaticAddress("48 8B 1D ?? ?? ?? ?? 8B 7C 24", 3, isPointer: true)]

// Risky - too short, might match multiple locations
[StaticAddress("48 8B 1D ?? ?? ?? ??", 3, isPointer: true)]

// Too specific - likely to break on updates
[StaticAddress("48 8B 1D ?? ?? ?? ?? 8B 7C 24 20 41 8B F0 8B EA", 3, isPointer: true)]

Accessing Singletons

Once defined, singletons are easy to access:
// Get Framework instance
var framework = Framework.Instance();
if (framework != null)
{
    // Access framework members
    var uiModule = framework->GetUIModule();
}

// Get UI state
var uiState = UIState.Instance();
if (uiState != null)
{
    var directChat = uiState->DirectChat;
}

// Get game object manager
var objectManager = GameObjectManager.Instance();
for (var i = 0; i < objectManager->ObjectListLength; i++)
{
    var obj = objectManager->ObjectList[i];
    // Process object
}

Null Checking

Always check for null when accessing static instances:
// Good - null check
var framework = Framework.Instance();
if (framework != null)
{
    // Safe to use
    var dt = framework->FrameDeltaTime;
}

// Bad - potential null reference
var dt = Framework.Instance()->FrameDeltaTime; // May crash!

Multiple Static Values

Some classes have multiple static addresses: From OrchestrionManager.cs:11-56:
[StaticAddress("66 89 1D ?? ?? ?? ?? B9", 3)]
public static partial ushort* CurrentSongIndex();

[StaticAddress("F3 0F 11 05 ?? ?? ?? ?? F3 0F 10 4A", 4)]
public static partial float* SongPosition();

See Also

Build docs developers (and LLMs) love