Please view the disclaimer before use!
So… I got a little bored and thought, what might be a generically useful function? So I decided to remake getrawmetatable
in pure Luau.
For those of you who don’t know, getrawmetatable
is a function commonly used in exploits which retrieves a table’s metatable regardless of whether or not the __metatable
field is set. The __metatable
field is supposed to protect the metatable, preventing it from being modified and also controlling what getmetatable
returns when called on the table.
How does it work?
This works by leveraging how Luau manages the stack.
What's the stack?
The stack is an area of memory where temporary values are stored, such as functions, their parameters, and local variables, as well as return results. In Luau, this is shared between Luau scripts and the C++ code, allowing effective data sharing between the two. It follows the LIFO (Last In, First Out) principle, where the last value to enter the stack is the first value to exit it.
When a metamethod is invoked, Luau pushes it onto the stack. It executes, and when completed, it’s popped off the top. This script uses a userdata
object, with metamethods, to “intercept” the stack and retrieve the metamethod while it’s executing. Additionally, it leverages xpcall
to inspect the stack if the metamethod errors, before Luau pops it off the top.
Inherent limitations
Roblox’s Luau is heavily sandboxed and doesn’t allow that many debugging tools - the main one used in this module is debug.info
, which allows me to retrieve functions and their information from the stack. Therefore, the functionality of this module is limited:
- Metamethods that do not interact with the proxy object, or do not error, are undetectable by this function. This includes things like
__tostring
. - This limitation means some metamethods can be “hidden” from
copyrawmetatable
- using specific code means they won’t show up.
What can I use it on?
Due to the nature of userdata
objects, here’s what you can use it on and what it will return:
- Instances: returns the
__namecall
,__index
,__type
(new) and__newindex
metamethods currently assigned - Tables: returns all detectable metamethods (see above)
Demonstration
local copyrawmetatable = require("./getrawmetatable/getrawmetatable")
local testTable = setmetatable({
someValue = 3,
someString = "hi"
}, {
__add = function(self, other)
return self + other
end,
__concat = function(self, other)
return self.someString..other
end,
__index = function(self, other)
print(other)
end,
__newindex = function(self, other)
print("This doesn't reference the proxy (would be in variable other), so won't show up")
end,
__metatable = "This is protected."
})
local tableMetatable = copyrawmetatable(testTable)
local instanceMetatable = copyrawmetatable(workspace.Baseplate)
local __concat = tableMetatable.__concat
local instance__newindex = instanceMetatable.__newindex
print(__concat({someString = "string 1 "}, "string 2"))
instance__newindex(workspace.Baseplate, "Name", "SomeOtherName")
and here’s the results:
And the metatables themselves:
Table metatable:
Instance metatable:
Disclaimer
Use this at your own risk! I will not accept any responsibility for consequences which arise as a result of using this module. Should this module break due to later Roblox updates, I will try and fix it, but there’s always the chance it may just stop working. Please note that this doesn’t retrieve all metamethods at all times because of Luau’s limitations.
Tables returned by copyrawmetatable
are readonly, due to Roblox limitations… and also the fact it’s against terms of service (for instances).
They are editable, but changes will not apply to the actual metatable.
Feel free to edit this however you want - just add a comment in the script somewhere to say you edited it
I’m open to suggestions on how to improve this, and what functionality could be added. I’d appreciate ideas!
Here’s the module, it’s in a model with a test part you can use to test the function.
If you just want the source, here’s the GitHub repository.