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
The signature pattern to locate the address in the game binary.
The offset within the matched instruction to read the address from (0-indexed).
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
The signature pattern to locate the vtable in the game binary.
The offset within the matched instruction to read the vtable address.
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
- Search for references to the singleton
- Find initialization or usage patterns
- Identify the instruction that loads the address
- 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