Client AntiCheat Detection Compilation

Cheat detection methods collection

This post will have some of the detection methods I’ve found that I’ve decided to post publicly, if detection isn’t mine, the author will be credited. I will be updating this post whenever I add a new detection.

Extra

Define AntiCheat functions properly

If your anticheat has any functions, make sure you define them using local func = function() instead of function func(). Defining a function the first way will not reveal the function name to the exploiter, keep in mind they can still decompile and detect functions by checking constants and arguments.

How to combat hooked RemoteEvent/RemoteFunction

Method 1: Teleport Cheaters to certain coordinates incase your remote that handles bans is hooked. Make sure you use different threads for that.
Examples:

Client Code
local player = game:GetService('Players').LocalPlayer or game:GetService('Players').PlayerAdded:Wait() -- Incase you use ReplicatedFirst

local Detected = function()
	if player and player.Character and player.Character:FindFirstChild('HumanoidRootPart') then
		
		-- Fire the ban remote in a different thread
		
		local hrp = player.Character.HumanoidRootPart
		local clock = 0
		while clock < 5 do
			hrp.CFrame = CFrame.new(101337, 1337, 1337)
			hrp.Anchored = false
			clock += task.wait()
		end
		hrp.Ancohred = true
		print('Crash the client')
		-- Crash the client
	else
		print('No character, crash the client')
		-- Crash
	end
end

-- Test
task.wait(10)
Detected()
Server Code
local Players = game:GetService('Players')

local BanPos = Vector3.new(101337, 1337, 1337)

while task.wait(.5) do
	for _, player in Players:GetPlayers() do
		if player.Character and player.Character:FindFirstChild('HumanoidRootPart') then
			if (BanPos - player.Character.HumanoidRootPart.CFrame.Position).Magnitude < 25 then
				print('Ban', player.Name)
			end
		end
	end
end

Detection List

AntiAFK/Signal_Disconnect Detection (0% False Positives)

The following code can be used to protect any kind of signals, for example OnClientEvent. Be careful if you disconnect any of your signals though.

AntiCheat Code:

local player = game:GetService('Players').LocalPlayer or game:GetService('Players').PlayerAdded:Wait()

local Connection = player.Idled:Connect(function(idleTime)
	local randomVariable = idleTime
end)

while task.wait(.5) do
	if not Connection.Connected then
		print('AntiAFK script detected, ban')
	end
end

Script Example:

-- Ultra simple anti afk kick script
for _, connection in getconnections(game.Players.LocalPlayer.Idled) do
 connection:Disconnect()
end

Video Example:

__tostring trap

If you store some kind of data in ModuleScripts, you can modify __tostring method in order to detect exploiter printing out the table, keep in mind that it’s still possible to loop through it or index it without any problems. This detection is pretty rare, however it won’t have any false positives unless you print out your tables.

Module Code:

return setmetatable({
	Damage = 25,
	Cooldown = 0.15,
	ReloadTime = 2,
	OtherStat = true
}, {
	__tostring = function()
		
		print('Table was printed, ban')

		local val1 = 'table'
		local val2 = ': '
		local val3 = '0x'
		local val4 = '82fcfd59c8a044d1'


		return val1 .. val2 .. val3 .. val4
	end,
})

Script Example:

local AkStats = require(game.ReplicatedStorage['AntiCheat Scratches'].Ak47)

print(AkStats)

Other resources used

RbxStu V3 - Studio pentesting tool
Synapse X Docs - Documentation of common exploit functions

11 Likes

I don’t know if I am behind or not but synapse isn’t an executor anymore.

1 Like

Most of Synapse X functions are UNC and still being used in exploits.

They probably figured out how to do it them selfs. Also just because the names are the same etc I recommend linking each executors docs due to them potentially being a bit different.

Its also poor practice to link old documentation for something that’s not been operational for the last ~2 years.

that is a good resource to detect exploiters without false positives
while it can still be bypassed, exploiters may mess up when trying to cheat and then get banned and if they tried to cheat on another account, they would have to read all the scripts in case of another trap and they can still mess up and get banned

Most of the synapse functions follow UNC, which I don’t think has been changed in those 2 years. I don’t know any stable executors right now.

Nihon seems to be somewhat known and has its own documentation page. It’s been said to be inaccurate, so I suppose the UNC docs will do.

1 Like

Yeah this seems way better, but I’m not sure if I should add docs page that has a guide on installing an actual exploit.

1 Like

This is still an interesting tip actually, I didn’t know this actually protected against getting the function name with exploits. Thanks for that.

Fun fact: Nihon developers got lazy originally, and their documentation was written by AI, it has changed some since then; however, I’d not particularly rely on their docs or Wave’s, since things like getactors were documented to return string instead of { Actor }, which says a lot of the quality of documentation.

The Synapse X, UNC and RbxStu V3 documentation (as you used the RbxStu V3) are probably your best choice for the documentation; although the latter are still being written and probably have one or two mistakes, the first two are safest bets as they have stood for years now.

2 Likes

All positive words but RBXStu doesn’t make sure the .Connected is set to true when it disconnects, wave, synapsez, and awp all do this so you could do this

local Connection

local player = game:GetService('Players').LocalPlayer or game:GetService('Players').PlayerAdded:Wait()

local ConnectionFunction = function(idleTime)
	local randomVariable = idleTime
end)

while task.wait(.5) do
	if not Connection then
		Connection = player.Idled:Connect(ConnectionFunction)
		return
	end
	
	if not Connection.Connected then
		print('hooked')
	end
	Connection:Disconnect()
end
1 Like

It shouldn’t be set to true, that is the whole reasoning for the Disable function to exist. The disconnect function is intended to entirely kill off the connection.

I think your misunderstanding. The disconnect function does terminate the connection, but an executor would hook the metamethod of the .Connected to always return true.

I know this, but dottik believes that the behaviour he implemented is correct ( I agree ). If disconnect kept .Connected as true then there would be no need for Disable to exist realistically.

AntiAFK/Signal_Disconnect Detection (0% False Positives)

Exploits usually use connection:Disable(), which is different from connection:Disconnect(). It leaves the connection “connected” but prevents it from firing.

3 Likes