Got inspired to return to this after seeing Geo use the VMBuffer to get the offsets (pretty cool that you managed to make sense of them BTW). My method is hopefully the correct/best way and similar to UE4’s FNames where an index is used to retrieve the variable name. It’s probably not new as I think as there may have been people who figured this or at least pieces of this out way before me but I didn’t find any real tutorial on it so I’m claiming “First”.
This first method is for GameMaker .exe’s with virtual functions.
Find the Name list by doing a string search for ‘prototype’ or ‘@@array@@’ or ‘GameObjects’. Make sure it is aligned by 4 byte for x32 or 8 byte for x64, otherwise you might get too many results (should be no more than 3-5). Check each result by searching for the address ( 4byte hex for x32 and 8byte hex for x64) and then searching for something within 3-4 x pointer size and that address (see picture).
It should be located within the .exe. Then you can do an assembly scan for that address like this:
That will get you an instruction that you can do a search for to reliably grab the base.
After that you just read through the whole list (the number of names is typically -0xC from the base) and create a lookup table for each index (the index is just it's position in the array). I’ll go over how to use it later.
Code: Select all
function collectNames()
local base = getAddressSafe('NameList')
local num = readInteger(base-0xC)
base = readPointer(base)
if base == nil then return end
nameList = {}
for i = 0,num do
local nAddr= readPointer(base+i*8)
local name = readString(nAddr,50)
if name then nameList[i] = name end
end
print("NameList Table Parsed")
end
Same principle as before but we now have the index stored next to the pointer to the string, so you will need to go two points back when searching for the address (search for address of string, then search for address of pointer to string).
The base you find should be a pointer to the .exe’s directory (as a string) and is accessed by something when you either alt-tab/pause or exit to menu so try one of those before doing an assembly scan (it takes longer with a precompiled .exe). This base will have the NameList at either +54 or +94 (haven’t checked for x64 games but you should be able to tell which one it is). Once you find the base and register it, just loop through the name/index pairs generating a lookup table that uses the index you read as the key --> nameList[index] = name.
Notice how there is a pointer to a small struct with the pointer to the string ("___struct___0") and the index (100000) at each entry.
Code: Select all
function collectNames()
local base = getAddressSafe('[NameList]')
if base == nil then return end
nameList = {}
local num = readInteger(base+0x8C)
base = readPointer(base+0x94)
for i = 0,num do
local cAddr = readPointer(base+i*4)
local index = readInteger(cAddr+4)
local nAddr = readPointer(cAddr)
local name = readString(nAddr,50)
if name then nameList[index] = name end
end
print("NameList Table Parsed")
end
The variable array is typically at +2C-->+10 from the object’s instance base (see my previous post on Objects and their instances to understand how to get to it). The number of variables in the array is at +2C->+8 from the object’s instance base.
Each variable’s pointer is separated by +C (the game does a lea ecx[eax+eax*2] and then multiplies by 4) for x32 or +10 (game does a shift left by 4) for x64. The name index is at +4 or +8 for x32 and x64 respectively so just read out the name from your dictionary using the index.
*Note that for some games, the index is the full value read out and others you will need to add or subtract 100000 to it (if the names are blank when populated, just add or subtract 100000 to the index you read from the object).
And there you have it, the entire list of variables for this object (there are way more than what is shown in the picture).
I'll eventually try to get around to making a template table like Cake-san's UE4 table but I think anyone who is interested enough can make sense of this to get what they want (despite my poor explanation of it).
Happy Hacking,
aSwedishMagyar