Brazil's biggest MU Online portal — since 2003
Tutorial Advanced Client

How to set up anti-tamper (protection) for the MU Online client

Harden your MU Online server's Main.exe against editing, injection and memory hacks by combining integrity checksums, anti-debug, module verification and a signed handshake with the server.

GA Gabriel · Updated on Jul 12, 2025 · ⏱ 24 min read
Quick answer

Protecting the MU Online client is a permanent arms race. On the other side are hex editors, DLL injectors, memory trainers and speed, wallhack and auto-combo cheats that take advantage of an unprotected Main.exe. Anti-tamper is the set of defenses that makes it harder to edit the binary, attach a d

Protecting the MU Online client is a permanent arms race. On the other side are hex editors, DLL injectors, memory trainers and speed, wallhack and auto-combo cheats that take advantage of an unprotected Main.exe. Anti-tamper is the set of defenses that makes it harder to edit the binary, attach a debugger, inject code and alter values in memory — and, just as important, lets the server detect when the client is not trustworthy. In this tutorial you will build these layers in a practical way: checksum integrity, anti-debug, verification of loaded modules, memory protection and a signed handshake with the server. All the concrete values (offsets, ports, section names) appear as examples and vary by season/client.

Prerequisites

Before hardening anything, you need a working base. If you are still building the server, start with how to create a MU Online server and come back here once the client already connects normally. Hardening a client that can't even log in yet only multiplies the variables when something breaks.

RequirementDetail (example — varies by season/client)
Working clientMain.exe that connects and logs in with no protection yet
Binary backupUntouched copy Main_original.exe outside the client folder
Analysis toolsHxD, CFF Explorer, x64dbg, Process Hacker
Your own launcherExecutable that validates and starts the client (ideal)
Access to the ConnectServerTo add server-side validation
Isolated test environmentClean machine, separate from the development one

The least obvious item is your own launcher. A lot of protection is better placed in a component you control completely and can recompile at will, rather than trying to patch the Main.exe that came in the package. The launcher verifies integrity, talks to the server and only then launches the client.

Threat model: what you are protecting against

Before choosing techniques, define what the attacker wants to do. Generic protection wastes CPU and generates false positives; protection aimed at the real attack is worth the effort.

AttackTypical vectorMain defense
Edit IP/version in the binaryHxD, patcherIntegrity checksum
Attach a debuggerx64dbg, Cheat EngineAnti-debug
Inject a cheat DLLInjector, LoadLibraryModule verification
Alter values in memoryCheat Engine, trainerVariable encryption/obfuscation + server-side validation
Speed hackClock acceleratorsTime checking + server-side validation
Forge packetsProxy, botSigned handshake + server-side validation

Notice that server-side validation appears on almost every row. That is the central lesson: the client runs on the enemy's machine and will never be fully trustworthy. Anti-tamper raises the cost of the attack and feeds the server with signals; the final verdict belongs to the server.

Layer 1 — Checksum integrity

The cheapest defense against binary editing is to check whether it is bit-for-bit identical to what you distributed. Generate a strong hash of the original Main.exe and verify it in two places: in the launcher, before starting, and on the server, during the handshake.

Step 1 — Generate the reference hash

On Windows, with the final binary ready (already patched with the correct IP and version):

certutil -hashfile Main.exe SHA256

Keep that hash. It is the "truth" against which every client will be compared. Every time you re-edit Main.exe, the hash changes — automate the regeneration so you don't distribute a client with an outdated hash.

Step 2 — Verify in the launcher

The launcher computes the SHA-256 of the on-disk Main.exe and compares it with the expected value before executing. Example in C++ (sketch):

// Pseudocode — varies by season/client and by how you embed the hash
bool VerifyIntegrity(const wchar_t* path, const std::string& expectedHash) {
    std::string currentHash = ComputeSHA256(path); // via CryptoAPI / bcrypt
    if (currentHash != expectedHash) {
        ShowError(L"Tampered client. Download again through the launcher.");
        return false;
    }
    return true;
}

> Don't embed the expected hash as a plain-text string that is easy to find. Obfuscate it, split it into parts or derive it from something else. An attacker who locates the string simply swaps it for the hash of their edited binary.

Step 3 — Section checksum at runtime (self-check)

Beyond the on-disk check, the client itself can verify the checksum of its own code section (.text) in memory while running. This catches patches applied after loading (code injection). The idea: at build time, compute the CRC/hash of the .text section and, at runtime, recompute it periodically, comparing it with the stored value. If it diverges, report to the server.

Because .text is mapped with relocations and the expected value has to match, this check usually requires control of the build process or a protector that already does it for you (see Layer 6).

Layer 2 — Anti-debug

Cheat Engine and x64dbg rely on attaching a debugger to the process. Detecting that presence makes dynamic analysis more expensive.

Step 4 — Basic checks

// Classic examples — combine several, never rely on just one
bool DebuggerPresent() {
    if (IsDebuggerPresent()) return true;

    BOOL remote = FALSE;
    CheckRemoteDebuggerPresent(GetCurrentProcess(), &remote);
    if (remote) return true;

    // PEB->BeingDebugged and NtGlobalFlag are also checked by deeper protections
    return false;
}

Step 5 — Detect tools by window/process

Scan windows and processes for known signatures (class names and titles of Cheat Engine, x64dbg, OllyDbg, Process Hacker). Keep the list external and updatable, because names change.

> Don't kill the client on detection. Recording overlays, accessibility tools and even some antiviruses trigger these checks. The safe pattern is to report to the server with the signal and leave the ban to human analysis or accumulated heuristics. Killing the process on the spot is the recipe for a forum full of complaints from legitimate players.

Layer 3 — Verification of loaded modules

Cheats frequently enter as an injected DLL. Enumerating the process's modules and comparing them against an allowlist detects obvious injections.

Step 6 — Enumerate and classify modules

// Sketch — the allowlist varies A LOT by season/client and by the user's drivers
void ScanModules() {
    // EnumProcessModules -> for each module, GetModuleFileName
    // Classify: system (system32), the client's own, and "unknown"
    // Unknown, unsigned modules -> signal to the server
}

Watch out for false positives: overlays (Discord, Steam, GPU), IMEs and audio software inject legitimate DLLs. That is why the ideal output is a report to the server with the suspicious modules, not a blind local block. Digitally signing and verifying the signature (Authenticode) greatly reduces the noise.

Layer 4 — Protecting values in memory

Trainers edit HP, zen, coordinates and speed directly in RAM with Cheat Engine. You can't stop the reading, but you can make the useful writing harder.

  • Don't store sensitive values in plain text. Keep HP, zen and speed encrypted/obfuscated and decrypt them only at the point of use. Cheat Engine finds the value 1000 easily; finding the encrypted value that changes representation, not so much.
  • Keep a shadow copy. Maintain a second derived value (a checksum) and, periodically, verify that they match. A divergence = someone wrote directly.
  • Validate on the server. HP, zen, position and speed that exist only on the client are useless: the server must be the authority. If the client says it walked 50 cells in 100 ms, the server rejects it, regardless of any local protection.

Layer 5 — Signed handshake with the server

This is the layer the attacker does not control, and therefore the most valuable. Instead of trusting that the client is intact, the server demands proof each session.

Step 7 — Challenge-response on connection

  1. On connecting, the ConnectServer/GameServer sends a random nonce (single-use value).
  2. The client combines that nonce with the hash of its own binary and an embedded secret, and responds with an HMAC.
  3. The server recomputes the HMAC with the expected hash and the same secret. If they don't match, it refuses the session.
Server -> Client : nonce = 0x9F3A...   (random per session)
Client -> Server : resp = HMAC_SHA256(secret, nonce || binaryHash)
Server           : recomputes and compares. Different => drops the connection.

Because the nonce changes every session, an attacker can't record a valid response and replay it later (anti-replay). And because the server knows the expected binaryHash, an edited Main.exe produces a different HMAC and is blocked — even if the attacker has neutralized the local check.

> The secret embedded in the client is the weak link: whoever reverse-engineers the binary can extract it. Rotate the secret with every client update, combine it with build data and treat it as something that will eventually leak. The real strength comes from adding this to behavior validation on the server.

Layer 6 — Commercial packers and protectors

If you don't have the client's source code, the protectors applied to the finished binary deliver much of layers 1, 2 and part of 4 at once.

ToolFocusTrade-off (example)
ThemidaAnti-debug, virtualization, integrityHeavy; AV false positives; slow startup
VMProtectVirtualization of critical stretchesFPS loss if you virtualize hot code
Enigma ProtectorIntegrity, licensing, anti-dumpExtensive configuration

Golden rules when using a protector: virtualize only the critical routines (handshake, checks), never the render loop, or the FPS collapses on weak machines; keep an internal build without protection so you can debug; and test antiviruses before launch, because packers trigger heuristics and you will need whitelisting/reputation.

Common errors and fixes

SymptomLikely causeFix
Client closes on launch for many playersAggressive anti-debug catching an overlay/AVSwitch the block to a report; refine the allowlist
Antivirus deletes Main.exePacker with no reputationSign the binary; submit it for whitelisting; build reputation on VirusTotal
Hash always invalid in the launcherReference hash outdated after re-editingAutomate hash regeneration in the build
FPS dropped a lot after protectingVirtualization of the render loopVirtualize only critical routines
Handshake refuses legitimate clientsClock/nonce out of sync or wrong versionCheck the time tolerance and AcceptVersion
False positive with Discord/SteamBlind module verificationSign/allowlist known signed modules
Handshake secret leakedPlain-text string in the binaryObfuscate/rotate the secret on every update

Distribution and rotation

Protection is not "set it and forget it". Every new client version should generate a new hash, a new handshake secret and, if possible, small variations in the check routines to prevent one public bypass solution from working forever. Always distribute through the launcher (which validates before running), publish the hash alongside the download and scan the final binary before uploading it. Treat every bypass reported on your Discord as a high-priority bug.

Launch checklist

  • Backup Main_original.exe kept outside the client folder
  • Reference SHA-256 hash generated from the final binary
  • Integrity verification active in the launcher (obfuscated hash)
  • Anti-debug in report mode, not kill mode
  • Module verification with an allowlist of known signed modules
  • Sensitive values (HP/zen/speed) encrypted in the client
  • Server-side validation of HP, position, speed and packets
  • Handshake nonce + HMAC active in the ConnectServer/GameServer
  • Handshake secret rotatable and obfuscated
  • Protector applied only to critical routines (FPS tested on a weak PC)
  • Binary signed and submitted for antivirus reputation
  • Tested on a clean machine, separate from the development one
  • Internal debug build without protection kept for your own debugging
  • Hash/secret rotation plan documented for every update

Frequently asked questions

Does anti-tamper replace the server's anti-cheat?

No. Anti-tamper protects the client binary and local memory, while server-side validation checks whether the actions and values it receives make sense. They are complementary layers: a hardened client with a naive server still falls to forged packets, and vice versa. Always validate on the server in addition to protecting the client.

Can I use Themida or VMProtect on Main.exe?

Yes, commercial packers like Themida, VMProtect and Enigma are widely used to make reverse engineering and debugging harder. The cost is higher CPU/RAM usage, slower startup and false positives in some antiviruses. Test performance on weak machines before launch and keep a debug build without the packer so you can debug it yourself.

Can the client checksum be bypassed?

A determined attacker can always neutralize a local check, because the code that verifies it runs on their machine. The goal of anti-tamper isn't to be unbreakable, but to raise the cost of the attack and let the server detect inconsistencies. That is why the signed handshake with the server, which the attacker doesn't control, is the most valuable part.

Will anti-debug get in the way of legitimate players?

It can, if it is too aggressive. Techniques like IsDebuggerPresent and checking for tool windows rarely affect players, but killing the process on detecting anything suspicious produces false positives with overlays, screen recorders and accessibility software. Prefer to report to the server and leave the ban decision to analysis, rather than killing the client on the spot.

Do I need to recompile Main.exe to add protection?

It depends. If you have the client's source code (rare), you compile the protection in. Without the source, the path is to apply an external packer/protector to the binary and, if there is support via a loader or a DLL injected by your launcher, add checks to that component. Integrity verification and the handshake can live in the launcher and the ConnectServer without touching Main.exe.

GA
Guides & builds editor

Gabriel covers gameplay, class builds, PvP and progression. He tests every strategy on a live server before publishing.

Keep reading

Related articles