Handshake (Anti Cheat related) [Dynamic Server - Client Communication]

(This is purely to make sure roblox’s search engine makes this thread pop up on search)
Anti Cheat RemoteEvent RemoteFunction Dynamic Communication Anti Exploit [hookmetamethod]

Here’s a script I wrote together for my future portfolios and to help people who doesn’t know what a handshake is or how to write one.

Before you start throwing shade on me for using Base64 encoding / decoding, IT SHOULD NOT BE USED IN PRODUCTION. Base64 is a well known Encoding function which itself makes it really unsafe to use it as a main Encoding function. I Recommend changing the Encoding function + Decoding function to something more complex.

Usage :

Handshakes main usage is to make sure the client behaves a certain way through server confirmation.

Can be used to detect whenever a certain localscript has been disabled, removed or destroyed.
Can be used to detect whenever a exploiter is trying to yield a RemoteEvent.

You need :

3 Module Scripts,1 ServerScript,1 LocalScript and 1 RemoteEvent (ReplicatedStorage)

(See picture at the end of the thread if struggling to understand)


Server Script Code (Parent of ConfigurationModule and DecodingModule)

ServerScript
-- // Service variables
local HttpService = game.HttpService
local ReplicatedStorage = game.ReplicatedStorage

-- // Module variables
local Configuration = require(script.Configuration)
local Decoding = require(script.Decoding)

-- // Remote
local RemoteEvent = ReplicatedStorage.RemoteEvent

-- // Player storage variables
local PlayerLastCommunications = {}
local PlayerThreads = {}

game.Players.PlayerAdded:Connect(function(Player)
	
	-- // Create a Player specific thread for each player in the upon join
	PlayerThreads[Player] = task.spawn(function()
		
		-- // Set values of Player specific communications(sent / recieved)
		PlayerLastCommunications[Player] = {
			
			Numbers = {
				Sent = 0,
				Misses = 0,
			},
			
			Strings = {
				GeneratedKey = "",
				CurrentKey = "",
			},
			
		}
		
		while task.wait(Configuration.Numbers.CommunicationSpeed) do

			-- // Timeout detection trigger
			if PlayerLastCommunications[Player].Numbers.Sent and PlayerLastCommunications[Player].Numbers.Misses >= 1 then
				if tick() - PlayerLastCommunications[Player].Numbers.Sent > Configuration.Numbers.TimeoutLimit then
					PlayerLastCommunications[Player].Numbers.Misses += Configuration.Numbers.TimeoutLimit * 2
				end
			end
				
			-- // Timeout punishment
			if PlayerLastCommunications[Player].Numbers.Misses >= Configuration.Numbers.TimeoutLimit then
				Player:Kick("Handshake timeout")
			else
				PlayerLastCommunications[Player].Numbers.Misses = 0
			end
			
			-- // Generate a new key for every player every 1 second
			PlayerLastCommunications[Player].Strings.GeneratedKey = HttpService:GenerateGUID(false)	
			
			-- // Pass a Client the new key
			RemoteEvent:FireClient(Player, PlayerLastCommunications[Player].Strings.GeneratedKey)
			
			-- // Reset 
			PlayerLastCommunications[Player].Numbers.Misses += 1
		end
	end)
end)

RemoteEvent.OnServerEvent:Connect(function(Player, PassedKey)
	
	-- // Type check
	if type(PassedKey) ~= "string" then
		Player:Kick("Key must be string")
	end
	
	-- // Decode key and store it as Player specific
	PlayerLastCommunications[Player].Strings.CurrentKey = Decoding.Decode(PassedKey)
	
	-- // Check if key has changed or not
	if PlayerLastCommunications[Player].Strings.GeneratedKey ~= PlayerLastCommunications[Player].Strings.CurrentKey then
		Player:Kick("Key missmatch")
	end
	
	-- // Timeout reset
	PlayerLastCommunications[Player].Numbers.Sent = tick()
end)

game.Players.PlayerRemoving:Connect(function(Player)
    -- // Clears all Player specific values upon leaving
	PlayerLastCommunications[Player] = nil
	
	task.defer(PlayerThreads[Player])
	PlayerThreads[Player] = nil
end)

ConfigurationModule (Child of ServerScript)

ConfigurationModule
local ConfigurationModule = {

	Numbers = {
		TimeoutLimit = 3,
		CommunicationSpeed = 1,
	},

}
return ConfigurationModule

DecodingModule (Child of ServerScript)

DecodingModule
local DecodingModule = {
	Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
}

-- // Base64 decoding [I didn't write this]
DecodingModule.Decode = function(Data)
	Data = string.gsub(Data, '[^'..DecodingModule.Alphabet..'=]', '')
	return (Data:gsub('.', function(x)
		if (x == '=') then return '' end
		local r,f='',(DecodingModule.Alphabet:find(x)-1)
		for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
		return r;
	end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
		if (#x ~= 8) then return '' end
		local c=0
		for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
		return string.char(c)
	end))
end

return DecodingModule

LocalScript (Parent of EncodingModule)

LocalScript
if not game:IsLoaded() then
	game.Loaded:Wait()
end

-- // Variables
local Encoding = require(script.Encoding)
local RemoteEvent = game:GetService("ReplicatedStorage").RemoteEvent

RemoteEvent.OnClientEvent:Connect(function(RecievedKey)
	-- // Upon client receiving key we Encode the Key and pass the newly encoded key to the server
	RecievedKey = Encoding.Encode(RecievedKey)
	RemoteEvent:FireServer(RecievedKey)
end)

EncodingModule (Child of LocalScript)

EncodingModule
local EncodingModule = {
	Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
}

-- // Base64 encoding [I didn't write this]
EncodingModule.Encode = function(Data)
	return ((Data:gsub('.', function(x) 
		local r,b='',x:byte()
		for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
		return r;
	end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
		if (#x < 6) then return '' end
		local c=0
		for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
		return EncodingModule.Alphabet:sub(c+1,c+1)
	end)..({ '', '==', '=' })[#Data%3+1])
end

return EncodingModule

If you struggled to understand how it was set up then here’s a picture.

12 Likes

That’s cool and all, but won’t exploiters just be able to read your code in the localscript and encoding script to figure out the type of encryption/cipher you are using and make their own script to act in the place of the remote?

There is multiple ways a dev could hide this, I’m not sending them here because its a private method.

Nothing on the client can truly be hidden.

1 Like

it’s way harder said than done.

one word for you: getnilinstances

Or even better game:GetDescendants() and check if the object found in a loop is a localscript

I’ve tried setting my localscript to nil and by using the getfenv().script method, and game:GetDescendants() can still locate it.

nah that wouldnt work against parenting to nil

Oh all it takes is one experienced exploiter figuring it out, and the next is a post on popular exploit forums such as v3rmillion about how they bypassed your anti-cheat.
Then, skids just grab that script and use it on their silly little exploit that claims to have bypassed hyperion when in reality they’re just going to get banned a couple days later.

All you are relying on here is hiding scripts, you might as well just hide all your scripts and forgo this entirely.

This can be bypassed with a basic understanding of exploiting and the ability to spy remotes/view script environments. If the exploiter has the capability to decompile your code this is trivial to bypass. There are other ways you could set this up that at least require exploiters to decompile your scripts.

Avoidable

Keystrokess

  1. This is a seemingly condescending post
  2. Who cares as long as you have some form of moderations & a way to report bad actors
  3. Who uses v3rm anymore? I haven’t thought of that site since sw/synapse/krnl closed down

Moderations can only work so far, there are still a lot of kids who just see and take scripts from popular websites such as v3rm, and v3rm doesn’t need to be the only one. There are forums, discord servers and many other sources to grab scripts from. Like I said, only one person is required to find how to bypass it and release it to the internet.

It’s a game of cat and mouse, and it always will be, so to make it easier for yourself, instead of using a Handshake for your first line of defense, you use it for the last, and focus server-sided anti-cheats first.

Most of the time they’ll be open source so ez patch and if they’re obfuscated you can pay someone to deobfuscate it. Moonsec, Brew, Luraph all can be reversed and decompiled even without source. You just need skill :wink:

That’s the point buddy, this is a game of cat and mouse, and you’re just making things harder for yourself. “ez patch” may require you to work sometimes hours on end trying to patch something that’s going to eventually be bypassed anyway.

Client is the exploiter’s territory, if you think you can win in there, good luck.

If it takes you hours to patch an open source script then I don’t think you should be trying to patch bypasses also who cares if they bypass it. Add more measures, patch current bypass or just detect who is bypassing and ban them lol

If you’re worried about people making ‘bypasses’ don’t have any client side input and have it all run server sided which is what games like Doors do.

Don’t hate for no reason. Your arguments can easily be resolved.


That’s delusional. Criminality has a client side anti cheat and its decent. Its not hard to prevent a lot of cheaters from cheating client side but then again why do you think people do things on the server now instead of the client?

1 Like