Article: Understanding EasyAntiCheat

12 July 2015

EasyAntiCheat is anti-cheat software designed to seamlessly integrate with a variety of platforms to include Unity3D, Unreal Engine, CryEngine, and Stingray. For the purposes of this article, I'll be concentrating on the Unity3D aspect (in specific how it relates to Rust). After a bit of toying around, I was able to gather a bunch of notes and determine a bit of the behavior involved. The first and most apparent issue with this software (which prevents probably a good majority of cheaters) is how it prevents debuggers like Ollydbg, x64dbg, and Cheat Engine from attaching to the game process. On the game start-up, you may even be greeted with a message which specifically asks you to close your running Cheat Engine.

Working back from where that message originates from, you're able to get some mild success in bypassing EAC (at least on the client level). The message box which is being displayed comes from .NET executable which is in this case "Rust.exe" (default directory is Steam/steamapps/common).

MessageBox.Show(string.Format("Rust Launcher Error: {0} - {1}", (object) eventArgs.Status, (object) eventArgs.Message));

Looks familiar doesn't it? Unfortunately that's all this executable file was good for, giving you error messages. LoadCompletedEventArgs (eventArgs) is being passed from a dll (.NET assembly) from the same directory called "EasyAntiCheat.Client.dll"; this is where the fun happens. When opening up the client dll, my eyes immediately pried over to "Loader.cs" which takes care of shutting down the game on start-up.

NativeModule.Initialize(this.loadInfo.GetRealm(false), new NativeModule.MessageEventHandler(this.OnMessage), (byte[])null);

Above is the anti-cheat initialization and where this entire journey really begins and shortly ends (at least for me) because the parameters used in this method are passed to an extern void which comes from another dll (EasyAntiCheat_x64.dll) which is not .NET. Although the initialization procedure is protected, we can establish where the anti-cheat messages are coming from and where they are going to. Looking back to the code above, the second parameter takes something called a "MessageEventHandler" which acts as a callback (and it's referred to as a callback) for receiving messages. The callback takes an enumerated message type (ClientConnected, ClientDisconnected, ClientIntegrityViolation, etc) and a message string. Immediately after the anti-cheat powers up, it will send a message to our newly defined callback and the loader will check the "last message type" (there can only be one possible message sent at this point) to ensure that the loading was successful before continuing to launch the game. The easy workaround? Remove the message check and launch the game anyways.

Above is an image of my client anti-cheat bypass using modified dlls, loading Rust with Cheat Engine working. Unfortunately when you try to connect to a server, at some point you will fail with an in-game error message. There are a few more checks performed on behalf of the actual local game, server you are connecting to, local server files (EasyAntiCheat.Server.dll, eac_server.dll), and the EAC service.