Anticheat Methods
Hey fellow developers!
I decided to release these detections because of this topic and the fact that exploiting is quickly dying, meaning nobody will buy Roblox anti exploits anymore.
Protect MetaMethod Calls
This is made to protect namecall calls such as MyPart:GetChildren()
from being hooked like this:
local oldNC = hookmetamethod(game, "__namecall", newcclosure(function(...)
if getnamecallmethod() == "GetChildren" and ... --[[blah blah]] then
...
end
return oldNC(...);
end))
Because getnamecallmethod()
will instead return something along the lines of GetChildren\000\000\0004154565.1561216
(\000 being null bytes, which are string terminators in C++, meaning the rest will be ignored) which is NOT equal to GetChildren
.
--// Init
local GetCallerFromXpcall = function(BaseFunction)
return select(2, xpcall(BaseFunction, function()
return debug.info(2, "f")
end))
end
local ProtectMethodString = function(SObject: string)
return string.format("%s%s%e", SObject, string.rep("\000", math.random(1, 6)), os.clock() ^ 5 + 0.1)
end
local SafeCFakeNamecall, SafeCNewIndex, SafeCIndex do --> Methods for Roblox-Made classes like Enum and Instance
local __newindex = GetCallerFromXpcall(function() game.____ = nil end)
local __index = GetCallerFromXpcall(function() return game.____ end)
SafeCFakeNamecall = function(self, Method, ...) --> Does not invoke __namecall, but will invoke __index instead.
return self[ProtectMethodString(Method)](self, ...)
end
--> These two closures will invoke the C __index and C __newindex of the Roblox-Made classes
SafeCNewIndex = function(self, Index, New)
return __newindex(self, type(Index) == "string" and ProtectMethodString(Index) or Index, New)
end
SafeCIndex = function(self, Index, New)
return __index(self, type(Index) == "string" and ProtectMethodString(Index) or Index)
end
end
--// Example
local GameName = SafeCFakeNamecall(workspace:WaitForChild("Baseplate"), "GetFullName") --> Returns: Workspace.Baseplate
SafeCNewIndex(workspace, "Gravity", 0) --> Setting gravity to 0.
SafeCIndex(workspace, "Gravity") --> Returns: 0
Detecting GUIs
These methods detect a large number of GUIs, including Rayfield and Infinite Yield, and EVEN SirHurt!
Here we abuse how the garbage collector works in Lua by checking if CoreGui was referenced or not. This is already a known method by some, but I just want to make sure everyone knows about it.
- Keep in mind that you CANNOT declare a variable with the value of the CoreGui
service, else the check will false positive!
--// Init
local IsReferenced = function(Object)
local Table = {}
local WeakMT = setmetatable({ --> This could be shuffled
Table, 1, "String", Object,
}, {
__mode = "kv"
})
--> Clean up
Table = nil
Object = nil
repeat task.wait(); until (not WeakMT[1]) --> Wait until garbage collector gc the values
task.wait(.2)
if #WeakMT ~= 3 or rawlen(WeakMT) ~= 3 then --> Table tampering (eg. table.clear) / 'Object' is still referenced
print(WeakMT)
return true
end
return false
end
--// Example
if IsReferenced(game.CoreGui) then --> This should be in a loop
print("Detected - CoreGui referenced without cloneref")
end
--> KRNL Gethui detection connection
--> Fun fact: this method used to work on scriptware for a short time!
SafeCFakeNamecall(SafeCIndex(game, "DescendantAdded"), "Connect", function(Object)
local Success, Out = pcall(function()
return SafeCFakeNamecall(Object, "GetFullName")
end)
if Success and string.find(Out, "^CoreGui") then --> Make sure it is the Actual CoreGui Instance, and not just the name
local Success, IsFromCoreGui = pcall(function()
return Object:IsDescendantOf(game.CoreGui)
end)
if (not Success) or IsFromCoreGui then --> Double check
print("Detected - KRNL gethui")
end
end
end)
Raw Game Metamethod Hooks (Includes CMD-X Detection)
This will detect changes in the game’s metatable object like this:
local mt = getrawmetatable(game)
local oldNC = mt.__namecall
setreadonly(mt, false)
mt.__namecall = newcclosure(function(...)
if ... then
... --> Random code blah blah...
end
return oldNC(...)
end)
setreadonly(mt, true)
Only bad exploit scripts detours like this, such as the old synapse init script or CMD-X.
--// Init
local __namecall = GetCallerFromXpcall(function() return game:____() end)
local __newindex = GetCallerFromXpcall(function() return game.____ = nil end)
local __index = GetCallerFromXpcall(function() return game.____ end)
--// Example
while true do
--> Check if the metamethods have changed (getrawmetatable hooks)
if
__namecall ~= GetCallerFromXpcall(function() return game:____() end)
or __newindex ~= GetCallerFromXpcall(function() return game.____ = nil end)
or __index ~= GetCallerFromXpcall(function() return game.____ end) --> This specifically detects CMD-X's __index hook.
then
print("Detected - Raw metamethod hooks")
end
task.wait()
end
Basic Hook Detection (hooks using the closure returned by the detour function)
- This code snippet (the detection) is unoptimized and should be throttled in order to prevent performance drops.
coroutine.wrap
returns a wrapper function that resumes a coroutine from a given function. When calling it over and over (in this case 198
times), it will not error. But 1 more call will result in a C Stack overflow
. A good detour function will normally NOT replace the function address, meaning you cannot do if currFunc ~= Func
, so calling a function itself again and again will eventually result in a C stack overflow
. If it errors, it means they obviously hooked the function, as “old” was used.
An example of a detectable hook with that method would be:
local old = hookfunction(gcinfo, newcclosure(function(...)
return old(...)
end))
--// Init
local IsHooked = function(Closure)
for _ = 1, 198 do
Closure = coroutine.wrap(Closure)
end
local Success, Out = pcall(Closure)
if (not Success) and string.find(Out, "C stack overflow") then
return true
end
return false
end
--// Example
while true do
if IsHooked(YourFunction_ThisCanBeAnyFunction) then
print("Detected - Some function was hooked")
end
if IsHooked(AnotherFunction) then
print("Detected - Another function was hooked")
end
task.wait()
end
Electron Detection
A few months ago, me and my friends dumped the Electron Init Script with Process Hacker and realized they referenced a Service (InsertService
) that could detect electron (since it references it).
--// Example
while true do
if IsReferenced(game.InsertService) then
print("Detected - Electron Detected.")
end
task.wait()
end
“tostring(Argument)
” detection
This will detect many bad hooks by exploiters (especially skids), as __tostring shouldn’t be triggered when calling Roblox Functions, meaning they did, for example, tostring(self)
to check the name of the passed object.
--// Init
local IsTostringDetected do
local __namecall = GetCallerFromXpcall(function() return game:____() end)
IsTostringDetected = function(Func)
local Called = false
local Bait = setmetatable({}, {
__tostring = function(self)
Called = true
return ""
end
})
if Func == __namecall then --> If the given function is game's __namecall...
game:GetFullName() --> Set the namecall method in the current thread to a valid one
end
pcall(Func, Bait, Bait, Bait)
return Called
end
end
--// Example
while true do
if IsTostringDetected(__whatevergamemetamethodOrAnyRobloxFunction) then
print("Detected - tostring has been called on self or the function arguments.")
end
task.wait()
end
Please use your brain when replying to this post!
Replying with something stupid like “Yes but the exploiter can just disable the script” is just stupid and unproductive! Those are methods! It is up to you to protect them.
The end
Let me know if you want another method or detection on this topic by private messaging me and I’ll add it if possible!
- I will not detect physics exploits such as flying or noclipping.
Also please tell me if any of these methods false positives so I can fix them.
Edits:
- 10/26/2023 - Added explanation of detections | @Doomcolp
- 10/27/2023 - Added Electron Detection | @TheLikerYT