Very cool!
Behavioural update
I recently discovered that the instance metamethods that are functions all interact with each other much more than I previously thought.
Hence, when one was detected to have a suspicious return value (i.e. it has been sethiddenstack()
'd), it caused some others to be incorrect and not the actual metamethod.
I have changed it to hide all the function metamethods of an instance if one is detected to be hidden or modified.
Some executors also appear to automatically sethiddenstack()
metamethods they hook, or return a similar return value to a function that has been sethiddenstack()
'd. This also means copyrawmetatable
can detect metamethod hooks for some executors.
How did you make a message less than 30 letters?
Bro just do this for isMetatableProtected
local function isMetatableProtected(target: Instance|any): boolean
return not pcall(setmetatable, target, target)
end
No, because that would set the target’s metatable to the target itself if it wasn’t protected.
Ok? Lol
local function isMetatableProtected(target: Instance|any): boolean
return not pcall(setmetatable, target, getmetatable(target))
end
- that’d return two values sometimes.
- that’d introduce the possibility of false code logic provided that the
target
isn’t a table or an instance. The future logic actually relies on the return logic ofisMetatableProtected
and an error such asinvalid argument #1 to 'setmetatable' (table expected, got string
(for example) would mess with the entire function.
I’ll admit this code isn’t as optimised as it could be, but some of that is necessary. It’s designed defensively as I was originally making it for anticheat purposes, and we don’t want exploiters passing a userdata made in c++ with __type
set. type
or typeof
could get hooked, I used them where necessary.
If you get a string from getmetatable(target) that means it’s metatable protected
setmetatable({}, {
__metatable = "awef"
})
Like what will a different error do?
Yes, I’m aware, but you could still get a table depending on what it’s set to. Please refer to my previous post.
Wdym that would return two values
pcall(...)
Yes, it returns two values, but in a tuple
If you try to do things with it it will refer to the first one in the tuple
Okay?
local function isMetatableProtected(target: Instance|any): boolean
return not pcall(setmetatable, target, getmetatable(target))
end
local metatable = setmetatable({}, {__metatable = {}})
print(isMetatableProtected(metatable))
Yes it may get disregarded should it not be stored in a second variable, but should you want the error message returned too, it could cause some issues.
Like I said before, this is all in context of my more defensive design. Please refer to the context of the code and it’s intention.
Explain
Like I said, why do you even need the error message?
This is absolutely useless for any kind of exploit prevention simply because they will anyway find a way. But what is it useful for? Optimization: directly calling __index and __newindex is a lot faster than Instance.Property. I actually made a library called RawLib which collects some of the common metamethods. It also includes some other data types like CFrames, Vector2, etc.
By your logic, that means you could just typecheck it to see if it should have a metatable, and just try to re-set that metatable, correct?
I need the error message to ensure the error was related to setting the metatable. If it wasn’t, then the call was invalid.
I will not be adjusting this, as it lies in the partial defensive design of this code. Feel free to change this yourself if you use it in your own code, but I will not be changing it on the community resource.
Changing that would mean I need to change a lot more code, which I am not going to do right now. I’ll probably take a look at a later date.
Ive taken an adaptation of this and used it in my own anticheat, with a few other hook checks. It’s proven incredibly effective so far.
Please don’t come to other resources and begin plugging your own resources. If you want to use that, use that.
local function isMetatableProtected(target: Instance|any): boolean
local success, err = pcall(function()
local old = getmetatable(target) -- Making extra variables for no reason
setmetatable(target, {}) -- Bro just set it to getmetatable(target)?
setmetatable(target, old) -- Unnecessary
end)
-- Oh, why is "not success" here?
return (not success and string.match(err:lower(), "protected metatable") ~= nil)
end
And why anything after “not success” is unnecessary:
local function isMetatableProtected(target: Instance|any): boolean
print(pcall(setmetatable, target, getmetatable(target)))
return not pcall(setmetatable, target, getmetatable(target))
end
local metatable = setmetatable({}, {__metatable = {}})
print("metatable (__metatable is table)")
print(isMetatableProtected(metatable))
local metatable1 = setmetatable({}, {__metatable = "A"})
print("metatable1 (__metatable is string)")
print(isMetatableProtected(metatable1))
local metatable2 = setmetatable({}, {})
print("metatable2 (no __metatable)")
print(isMetatableProtected(metatable2))
Result:
17:01:37.892 metatable (__metatable is table) - Edit
17:01:37.892 false cannot change a protected metatable - Edit
17:01:37.892 true - Edit
17:01:37.893 metatable1 (__metatable is string) - Edit
17:01:37.893 false invalid argument #2 to 'setmetatable' (nil or table expected, got string) - Edit
17:01:37.893 true - Edit
17:01:37.894 metatable2 (no __metatable) - Edit
17:01:37.895 true {} - Edit
17:01:37.895 false - Edit
You literally don’t even have to change anything besides the function
Just because It’s the weekend doesn’t mean you shouldn’t listen to other people’s suggestions Lol
Any kind of client-side anti-cheat is incredibly pointless and a waste of time. It’s bypassed quickly, and the only thing it does is clog your client and code and prevent you from making actual improvements. Also, if clients somehow manage to give the server a table with a metatable, then the problem is not in checking the received object but in the design of your networking.
I wrote about my own experience with using raw metamethods. You can’t stop people from speaking