Because game development is not anti-cheat development.
Notice: Unsigned is in heavy development currently, use with caution.
What is Unsigned?
Unsigned is a module that hides RemoteEvents and RemoteFunctions from the client. Unsigned makes all Remotes that it is given parented to nothing, and changes their name constantly so they cannot be indexed on the client after Unsigned has initialized.
Server vs. Client
As we know now, Unsigned skews the names and parents of the Remotes. The question is now though, how will this affect the existing scripts for Remotes? The answer, little to none! All that is required to adding the Remotes to the Obfuscation list. Hereās a comparison of both the serverās and clientās view of ReplicatedStorage.
Client
Server
As you can see, the remotes are nowhere to be found, and cannot be indexed.
A live example of Unsigned in action is hosted here and is uncopylocked!
Installation
You can either download it from the releases section of the GitHub repo or the Toolbox
The Future
Unsigned will have many more capabilities in the future, such as GUI detection, which will include Dex.
Plugin System
roblox-ts Support
Full-Fledged Anti-Cheat
If you would like to follow the development or contribute, Unsigned is hosted on GitHub!
Ok itās a good idea and thought out but hereās a situation where it might not work:
I have a gun system where I need to have a remote event fired? It would be a bit hard to find the remote and a exploiter could look at that code and find it should I use this module or have my own detection?
Unsigned mitigates this issue by using the UnsignedEvent object that you create with Unsigned:AddEvent().
-- Get Unsigned Module
local Unsigned = require(path.to.Unsigned)
-- Get Event
local Event_Object = game.ReplicatedStorage:WaitForChild("Event")
-- Create UnsignedEvent object
local Event = Unsigned:AddEvent(Event_Object)
-- Initialize Unsigned
Unsigned:Init()
No matter what happens to the Event, you will always have access to it. The only difference is you have to create a UnsignedEvent object in order to interact with it.
This module is waiting to be exploited, besides, this is completely unnecessary if you know how to protect your remotes in the first place. You donāt need to āhideā them (as stated here) if you just make server side checks which make it so firing the remote for exploitative purposes is rendered useless.
GUI detection is also a pointless endeavor, as you can just draw the GUI to the screen, making it impossible to detect, or it can be easily patched if anything.
The module is coded a little inefficiently, as the UnsignedLogger class literally has 3 almost copy-and-pasted functions, which is extremely redundant.
I never said I ai t going to do it, I know that learning things yourself is always better than getting a ready-to-use product
Also, Iām not an expert and I didnāt look at the code, but as you are an expert Programmer, what is wrong with this script?
(Asking this so I will learn in the future)
local logger = {}
function logger:Log(...: table)
local message = ""
if type(...) == "table" then
for _, arg in pairs(...) do
message = message..arg.." "
end
else
message = ...
end
print(
"Unsigned:",
message
)
end
function logger:Warn(...: table)
local message = ""
if type(...) == "table" then
for _, arg in pairs(...) do
message = message..arg.." "
end
else
message = ...
end
warn(
"Unsigned:",
message
)
end
function logger:Error(...)
local message = ""
if type(...) == "table" then
for _, arg in pairs(...) do
message = message..arg.." "
end
else
message = ...
end
error("Unsigned: "..message)
end
return logger
This is quite literally 3 functions copy and pasted with a single line changed on each one (to specify if it is printing, warning, or erroring)
edit: Itās also pointless, because looking through init.lua, it is used exactly the same as the print, warn and error functions, which means this is just overcomplicating a simple function call.
Okay, how is this even useful?
I donāt understand, simply send an Argument to the function and check the Argument, if itās one print, else warn, else error
(Thatās how you do it right?)
if self.Initialized then
Logger:Error("Objects cannot be added after initialization.")
end
This line uses it exactly like error(), which makes the entire point of having some ācustom loggerā class redundant. The extra work to do this makes it way slower than doing it in a normal way.
It doesnāt help that this module obfuscates remote names on RunServiceās Heartbeat function, and not to mention that an exploiter doesnāt need the exact name of a Remote, as you can just iterate through ReplicatedStorage and assign a variable to each Instance you find returns true on the :IsA call.
I programmed these with the intention of keeping it clean, but it seems that yes, you are right. Also, Unsigned is not going to single-handedly be only about hiding remotes while it may seem redundant, it restricts the environment that the attacker could use.
Your completely fine. You donāt realize this stuff unless you have seen how exploits work and understand the methods behind how itās done. It makes you think differently about networking and protection.
(Although Iāve never been in this situation)
I completely agree and understand, if youāve been a criminal and you join the police you are gonna be way better, because you know how criminals do their work, and you have a bigger view of the system
While this is a good idea, there are flaws that can come into play here.
Synapse X for example has an advanced system ā such system allows this entire module to be bypassed. One such way is this: remote event names are static; using any remote logger will allow you to receive the name of the RemoteEvent and itās arguments. All you need to do is use getnilinstances().
local instances = getnilinstances()
for i,v in pairs(instances) do
if v:IsA("RemoteEvent") then
print(v.Name)
end
end
This allows you to call the FireServer() method on the RemoteEvent with no checks whatsoever
After testing, it appears you parent the RemoteEvent to game.ReplicatedStorage; all I need to do then is have a .ChildAdded or .DescendantAdded hook to determine the correct remote event.
local r = game.ReplicatedStorage
local event
r.DescendantAdded:Connect(function(m)
if m:IsA("RemoteEvent") then
event = m
end
end)
while task.wait(2) do
event.Parent = game.ReplicatedStorage
event:FireServer()
end
Your module is a great idea, but it unfortunately canāt stop exploiters in this way.
Thereās no way to index the remote (Unless I screwed up royally somewhere). I have tested it thoroughly, even using youāre method that you mentioned. The Remote is parented to nothing, not even the game. It is only parented for when it is needed to be called even so, it doesnāt last long enough to be indexed. While I understand the better practice would be to just make the server the authority figure over the client, I found it helped overall, due to it causing a roadblock for exploits by not being indexable.