Jump to content

Anti-cheat question/advice?


FlorinSzasz

Recommended Posts

Hi everyone!

I am working on an anti-cheat resource for my server.

I wonder what method should i use ?

- make a timer and loop through all players for money,xp, x,y,z, and check with db data and so on... (i call the db once for data and i will use a formula for checks )!

- or make a timer for every player on the server and the apply the same things!

*Note: my server is not up (hosted), at first i plan to have at least 50 slots. Maybe later if i will have players i will go to 100 / 150 / 200 and so on if i need to. Which method will be better for long term if there will be more players.

Link to comment
  • Moderators
2 hours ago, FlorinSzasz said:

I wonder what method should i use ?

Preventing is better than checking for unusual values. Especially when you can't don't keep track of how they have been changed. This could have been done by an exploit instead of real cheats. I personally consider exploiting almost cheating, since the developers should be responsible to deliver decent code.

 

Back to the subject:

Value changes in databases are often done by user input. This should be your primary objective.

For example if you have a marker that opens a GUI to transfer money:

addEvent("onPlayerTransferMoney", true)
addEventHandler("onPlayerTransferMoney", resourceRoot,
function (marker, transferAccountId)
	-- Possible cheating? = hard returns
	if not isElement(marker) then return end
	
	local playerX, playerY, playerZ = getElementPosition(client)
	local markerX, markerY, markerZ = getElementPosition(marker)
	if getDistanceBetweenPoints3D ( playerX, playerY, playerZ, markerX, markerY, markerZ ) > 10 then return end
    
	-- More user friendly returns
	if not checkPassiveTimer("event:onPlayerTransferMoney", client, 1000) then return outputChatBox(client, "You can only transfer money every 1 sec.") end -- Source code: https://wiki.multitheftauto.com/wiki/CheckPassiveTimer
	if not transferAccountId then return end
    
	-- resume operations
    
	--
end)
  • Check if marker exist
  • Check if player is close enough to the marker
  • Limit execution speed (else users can create database lag by spamming the UI button)

Building an anti cheat system to check for anomalies in your database should be a secondary objective.

In this case you could start with defining limits. For example a money limit. A soft limit to detect possible cheating and a hard limit to define the impossible.

For a soft limit you only report to yourself, as it might be very active player.

And for a hard limit, you could for example freeze the money. In this case you could inform the user that you are investigating the issue. > As you might not know if the user itself is responsible for the anomaly or if another user is responsible. That user could also be helpful in finding/knowing the exploit that is responsible for the anomaly.

  • Like 1
Link to comment
27 minutes ago, IIYAMA said:

Preventing is better than checking for unusual values. Especially when you can't don't keep track of how they have been changed. This could have been done by an exploit instead of real cheats. I personally consider exploiting almost cheating, since the developers should be responsible to deliver decent code.

 

Back to the subject:

Value changes in databases are often done by user input. This should be your primary objective.

For example if you have a marker that opens a GUI to transfer money:

addEvent("onPlayerTransferMoney", true)
addEventHandler("onPlayerTransferMoney", resourceRoot,
function (marker, transferAccountId)
	-- Possible cheating? = hard returns
	if not isElement(marker) then return end
	
	local playerX, playerY, playerZ = getElementPosition(client)
	local markerX, markerY, markerZ = getElementPosition(marker)
	if getDistanceBetweenPoints3D ( playerX, playerY, playerZ, markerX, markerY, markerZ ) > 10 then return end
    
	-- More user friendly returns
	if not checkPassiveTimer("event:onPlayerTransferMoney", client, 1000) then return outputChatBox(client, "You can only transfer money every 1 sec.") end -- Source code: https://wiki.multitheftauto.com/wiki/CheckPassiveTimer
	if not transferAccountId then return end
    
	-- resume operations
    
	--
end)
  • Check if marker exist
  • Check if player is close enough to the marker
  • Limit execution speed (else users can create database lag by spamming the UI button)

Building an anti cheat system to check for anomalies in your database should be a secondary objective.

In this case you could start with defining limits. For example a money limit. A soft limit to detect possible cheating and a hard limit to define the impossible.

For a soft limit you only report to yourself, as it might be very active player.

And for a hard limit, you could for example freeze the money. In this case you could inform the user that you are investigating the issue. > As you might not know if the user itself is responsible for the anomaly or if another user is responsible. That user could also be helpful in finding/knowing the exploit that is responsible for the anomaly.

I dont allow players to spam the db or something with anything in any script i wrote!

I just want to know if i should use a timer and loop through all players when i check some things like money and xp and other or use a timer for every player, i dont want to tank the server performance. Even if this are the only timers /timer which will run.

As an example you login, i get the data for now only money from db and have it stored in a table.

I want to see your money every 20 - 30 seconds and from 1000$ you go to 999999999$ something is wrong.

  • Like 1
Link to comment
  • Moderators
20 minutes ago, FlorinSzasz said:

I want to see your money every 20 - 30 seconds and from 1000$ you go to 999999999$ something is wrong.

If you are worrying about performance, in that case you might consider using MySQL triggers. Those are as close as it gets to your db, making sure you do not cause overhead and align the value-checks with the update moments.

https://dev.mysql.com/doc/refman/8.0/en/trigger-syntax.html

  1. Create a value-check table / rollback table:
    1. Stores the value that it is being incremented (within 30 seconds)
    2. Stores the future time, when elapse the incrementing value can be ignored and resets to 0
  2. Create a trigger which triggers on update
    1. Do behaviour of value-check table.
    2. If the value jumps too much UP:
      1. Report to a separate table
      2. Freeze account
      3. Rollback manually / automatic (not recommended)

 

 

 

 

  • Thanks 1
Link to comment
21 minutes ago, IIYAMA said:

If you are worrying about performance, in that case you might consider using MySQL triggers. Those are as close as it gets to your db, making sure you do not cause overhead and align the value-checks with the update moments.

https://dev.mysql.com/doc/refman/8.0/en/trigger-syntax.html

  1. Create a value-check table / rollback table:
    1. Stores the value that it is being incremented (within 30 seconds)
    2. Stores the future time, when elapse the incrementing value can be ignored and resets to 0
  2. Create a trigger which triggers on update
    1. Do behaviour of value-check table.
    2. If the value jumps too much UP:
      1. Report to a separate table
      2. Freeze account
      3. Rollback manually / automatic (not recommended)

 

 

 

 

Thx is somehow close to what i meant.

  • Like 1
Link to comment
  • MTA Anti-Cheat Team

For every aspect of things a cheat/lua injector can result in, there is specifically tailored ways to prevent it. Scripters with a good sense of security-by-design can do so much more than follow basic "Script security" guidelines that apply mostly to triggers and such.

In case of player money, there should be a solid foundation (before anything else) that consists of not using GTA money as the truth/unit, but handle anything to do with money by yourself using your account system that operates through SQL database. At any time a player does anything that can lead to money increasing, being given or deposited, it has to happen through a trigger handling the event (context in which the player experiences a money change). Secure your triggers (also read https://wiki.mtasa.com/wiki/Script_security) and you're done.

It's important to understand that as part of it, you are redefining money: i repeat, do not use GTA money. Use a field in your SQL database that defines the unit money, and it's an entire different virtual entity than GTA money. Obviously, also avoid using elementdata to store or update player money. Of course, even when you incorporate my suggestions on how to handle money different in an integral way, extra checking like explained by @IIYAMA is a good stick behind the door, in case someone manages to find and exploit a script security flaw in your triggers.

@FlorinSzasz

  • Thanks 1
  • Sad 1
Link to comment
17 minutes ago, pixel77 said:

 And what if a cheater is flying or creating explosions, or changes other players positions, teleporting to other players with setElementPosition? @Dutchman101

This is very simple check for distances how far he should travel by foot/car/ plane in a certain time and check with timers and if something is not right bang. Well if u have teleports you have to check if the player used a command or not.

Link to comment

This is impossible.
Regarding measuring distances: this is very funny. What if you're flying fast and crash into a wall? you will get banned. Even the slightest deviation of the trajectory, which cannot be tracked in any way and is not even visible to eye, can give an advantage to cheater.

Link to comment
4 hours ago, Mtaowner said:

This is impossible.
Regarding measuring distances: this is very funny. What if you're flying fast and crash into a wall? you will get banned. Even the slightest deviation of the trajectory, which cannot be tracked in any way and is not even visible to eye, can give an advantage to cheater.

well even if you fly with hydra u wont be much faster than an infernus with max speed + nos in flat line in 30 seconds time so yeah. I tested the distances in best case the player will have on my server but they wont have that case 99.9% of time. The anti-cheat has to be 100% server side. You have to get creative when you make it, also you have to make it to work on your gamemode, for e.g my anti-cheat wont work on your gm.

2 hours ago, pixel77 said:

Yes, or jump out of a plane/helicopter, or jump off the roof.

But what about explosions? 

Well you disable them short term. For long term you have to think when most of the explosions take place so you make the code work right.

Link to comment
  • 2 weeks later...
24 minutes ago, pixel77 said:

How did you solved the distance measurement on server side? With timers?

You need a table with all players location and then you loop through all players who are in server and check were they are now and get the distance between points and thats it. There is a limit how far they can go in a certain time like 10s 20s or 30s  if they go to far in that time they are out. Also if player dies you should update the location in that moment not after timer resets.

Link to comment
3 hours ago, pixel77 said:

I see. Thanks. 

And if I am right, there is nothing against xray/wallhack?!

if i remember correctly the AC has built in things which can detect some wall hacks if not most of them. If you think someone has xray / wallhack use this for weapons

https://wiki.multitheftauto.com/wiki/GetPedWeaponMuzzlePosition    and add a 3d line to it to see where they aim.

  • Confused 1
Link to comment
  • Moderators
On 13/12/2023 at 17:50, FlorinSzasz said:

and add a 3d line to it to see where they aim.

Bullets do not start from the weapon Muzzle position.

The start position is:
https://wiki.multitheftauto.com/wiki/GetPedTargetStart

And the end position:
https://wiki.multitheftauto.com/wiki/GetPedTargetEnd

Both can be used to create a vector for the line you are referring to.

 

This is related to the reason why people can shoot around corners without showing more than their elbows. Since the bullets start more to the right of the weapon.

  • Thanks 1
Link to comment
6 hours ago, pixel77 said:

But it is not relevant, because cheaters can see all of the bones position through walls, without weapon too. 

even if they bypass the anti-cheat when it comes to wall hack somehow it wont be usefull at all  because if they dont gain any benefit from that only in a dm zone or gamemode and thats all. When they dont have any weapon in hand the wall hack from my point of view wont bring any advantage.

Link to comment

By the way the https://wiki.multitheftauto.com/wiki/Script_security#Securing_triggerServerEvent article is changed not a long time ago. 

The old triggerServerEvent validating still working or not?

The old validation method server side: 

addEvent("onRaiseTheRoof", true)
addEventHandler("onRaiseTheRoof", resourceRoot,
    function(arg1, arg2)
        -- Check data is coming from a client
        if client then

            -- The source for this event is always 'resourceRoot'
            if source ~= resourceRoot then
                reportNaughtyness( eventName, client, "source" )
                return
            end

            -- arg1 should be the player
            if arg1 ~= client then
                reportNaughtyness( eventName, client, "arg1" )
                return
            end

            -- arg2 should be between 1 and 100
            if arg2 < 1 or arg2 >100 then
                reportNaughtyness( eventName, client, "arg2" )
                return
            end
        end

        --
        -- Do usual code for 'onRaiseTheRoof'
        --
    end
)

-- Helper function to log illegal things
function reportNaughtyness( eventName, client, reason )
    -- Report
    outputConsole( "Possible rogue client!"
            .. " client:" .. tostring(getPlayerName(client))
            .. " eventName:" .. tostring(eventName)
            .. " reason:" .. tostring(reason)
            )
end

 

Link to comment
  • Moderators
6 hours ago, pixel77 said:

The old triggerServerEvent validating still working or not?

It does.

The new method is a utility function, which does everything for you. But also for some parts it forces you to be more strict. For example the: protectedKeys table part.

 

 

6 hours ago, pixel77 said:
            -- The source for this event is always 'resourceRoot'
            if source ~= resourceRoot then
                reportNaughtyness( eventName, client, "source" )
                return
            end

For resourceRoot you can also block this, instead of reporting. Saves you some lines of code.

addEvent("onRaiseTheRoof", true)
addEventHandler("onRaiseTheRoof", resourceRoot,
function(arg1, arg2)

end, false) -- propagate disable, making sure only resourceRoot can be passed as the source

 

  • Thanks 1
Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...