-
Notifications
You must be signed in to change notification settings - Fork 186
Structures
Structures (or Structure Types) let you define custom data layouts and apply them to memory addresses. If you've ever reverse engineered a game struct or class and wanted to see all its fields laid out nicely with their current values, this is your bread n butter.
It's basically like creating your own mini type system on top of raw memory. You tell PINCE "at this address, there's an int at offset 0, a float at offset 4, a pointer at offset 8" and so on and the Structure view will read and display all those values for you, updating in real time as the game runs.
- You need to be attached to a process first.
- After that, open
Memory View. - Go to
Tools -> Structures.
This opens the Structures window where you can manage your structure definitions.
Click New in the Structures window to open the Structure Editor. You'll need to give your structure a name and then add members to it.
Each member represents a field in your structure. Click Add to create a new member and you'll see these options:
-
Name: What you want to call this field (likehealth,position_x,ptr_to_inventory). -
Offset (hex): The byte offset from the structure's base address. Enter in hex without the0xprefix. -
Kind: What type of member this is:-
Value: A primitive value (int, float, string, etc.) -
Pointer: A pointer to another structure (dereferences the pointer at this offset) -
Inline: Another structure embedded directly at this offset (no pointer dereference)
-
For value members, you pick the data type:
- Integer types: Int8, Int16, Int32, Int64 -> choose signed, unsigned or hex representation
- Float types: Float32, Float64
- String types: ASCII, UTF-8, UTF-16, UTF-32 -> set the length and zero termination
- Byte array: Raw bytes with a specified length
You can also set the endianness if the game uses a different byte order than your system.
These let you nest structures. Say you have a Player structure with a field that points to an Inventory structure:
- First create the
Inventorystructure with its own members. - Then in your
Playerstructure, add a member at the right offset. - Set Kind to
Pointer(if the field holds an address) orInline(if the inventory data is embedded directly). - Select
Inventoryfrom the Structure dropdown.
When you view the structure, pointer members will follow the pointer and show the nested structure's fields. Inline members will interpret the bytes at that offset as the nested structure.
Once you've defined a structure, you can view it at any address:
- Select the structure in the Structures window.
- Click
View at address…. - Enter the base address.
Right-click on any entry in the address table and hover over View as structure. You'll see a submenu listing all your defined structures. Pick one and the Structure View opens at that entry's address.
This is handy when you've already found an interesting address and want to interpret it as a known structure without having to copy the address over manually.
The Structure View window opens showing all your members with their current values. The view has four columns:
-
Offset: The byte offset from the base address. -
Name: Your member name. -
Type: The data type or nested structure name. -
Value: The current value read from memory.
The Structure View polls memory at the same interval as the address table, so values update automatically as the game runs. You can double-click any value in the Value column to edit it directly.
If you have pointer or inline members, you'll see an expand arrow next to them. Click it to drill down into the nested structure and see its fields. This works recursively up to 16 levels deep.
For pointer members, PINCE reads the pointer value at that offset and uses it as the base address for the nested structure. If the pointer is null, the expansion just shows nothing.
You can push an entire structure's members into the address table as a group:
- Select your structure in the Structures window.
- Click
Add to address table. - Enter the base address.
All the structure's members appear in the address table as a collapsible group, with their offsets shown as relative addresses. Nested structures become sub-groups. This is handy when you want to monitor or freeze multiple related values at once.
You can also do this from the Structure View window with the Add to address table button at the bottom.
When you add a structure to the address table, the parent row is a special type that only holds an address, it doesn't have an editable value or type like normal entries. The child rows underneath it are the actual members with their values.
You can change the base address of a structure row by double-clicking its address column. This updates all the child rows' addresses automatically since they're calculated as offsets from the base.
Structure rows have some limitations compared to normal entries:
- No freeze: The freeze checkbox on a structure row just unchecks itself as you can't freeze the parent row. Individual child members can still be frozen though.
-
No "View as structure": Right-clicking a structure row doesn't show the
View as structuresubmenu since it's already a structure. -
Relative addresses: Child rows show their offset as the address (like
+0x10) rather than the resolved absolute address.
-
Edit: Modify an existing structure's name or members. Double-clicking a structure in the list also opens the editor. -
Delete: Remove a structure definition. This doesn't affect any address table entries you've already created from it.
In the Structure Editor, you can reorder members with the Up and Down buttons or remove them with Remove.
Your structure definitions are saved alongside your address table when you save a session (.pct file). This means when you load a session later, all your structures come back with it.
If you share a .pct file with someone, they'll get your structures too, which is useful for distributing cheat tables that rely on specific structure layouts.
Let's say you've found a player object at 0x7f1234560000 and you know from reverse engineering that it has:
- Health (int32) at offset 0x10
- Mana (float32) at offset 0x14
- Position X (float32) at offset 0x20
- Position Y (float32) at offset 0x24
- A pointer to an Inventory struct at offset 0x30
You'd create a Player structure like this:
Player
├── health 0x10 Int32
├── mana 0x14 Float32
├── position_x 0x20 Float32
├── position_y 0x24 Float32
└── inventory 0x30 ptr → Inventory
Then create an Inventory structure with whatever fields you've found there:
Inventory
├── gold 0x08 Int32
├── item_count 0x0C Int32
└── items_ptr 0x10 ptr → ItemArray
Now when you view the Player structure at 0x7f1234560000, you see all the player's stats and you can expand inventory to see the nested Inventory fields. If you expand items_ptr, it follows that pointer too and shows the item array structure.
If you're not sure about the exact offsets, you can use the address table and Memory Viewer to figure them out. Add a variable at a known offset, then use Find out what accesses this address to see what code touches it. The disassembly will show you the offset being used (like [rax+0x14]).
The structure's size field is mostly informational, it doesn't affect how members are read. But it's useful to keep track of how big your structure is, especially if you're dealing with arrays of structures.
You can reuse the same structure definition across multiple addresses. If you have 5 enemies all using the same Enemy struct layout, just create one Enemy structure and view it at each enemy's base address.
If you're working with a Mono or IL2CPP game, the Mono/IL2CPP Dissector can automatically generate structure definitions from the game's managed classes:
-
Export as Structure: Right-click on a class to create a structure definition with all the fields already filled in. -
Dissect as Structure: Right-click on a class with a singleton pattern (likePlayer.Instance) or on a live instance in the Find Instances window to immediately open the Structure View on that object.
Games often use common patterns you can recognize:
- Object headers: Most objects start with a vtable pointer (8 bytes on 64-bit, 4 on 32-bit) followed by a sync block or reference count.
- Arrays: Managed arrays usually have a length field followed by the elements. Native arrays might just be a pointer and size stored separately.
- Strings: UTF-16 strings often have a length prefix. C-style strings are just null-terminated byte sequences.