- summary: The client telling the server it found a mod loader. Empty on purpose.
- area: Game.Client.Verification
- confidence: 0.85
The client telling the server it found a mod loader. Empty on purpose.
- Kind request
- Area Game.Client.Verification
- Named by gamemapping
THE CLIENT'S ANTI-TAMPER TELLING THE SERVER IT FOUND A MOD LOADER. It is sent ONLY on a positive detection and it carries no payload at all: the fact that it was sent IS the whole report.
========================================================================= WHAT THE CLIENT ACTUALLY LOOKS FOR =========================================================================
Everything below is decompiled from 3.6.10.10, not inferred. The scan runs against the GAME INSTALL ROOT, computed as
Application.streamingAssetsPath + "/../../"
CHECK 1 - LOADER DIRECTORIES. It tests whether any of these exists directly under that root, and stops at the first hit:
MelonLoader BepInEx dotnet
MelonLoader and BepInEx are the two mainstream Unity mod loaders; dotnet is
the runtime folder they install beside themselves.
CHECK 2 - MANAGED PROXY ASSEMBLIES. If no directory matched, it recursively enumerates *.dll under the same root (SearchOption.AllDirectories) and flags any file whose name without extension is one of:
Core Animator2D Il2Cpp__Generated UnityEngine
THIS IS THE CLEVER HALF, AND IT IS WORTH UNDERSTANDING. Dofus is an IL2CPP build: it ships ONE native GameAssembly.dll and no managed assemblies with those names. Those four files are what an IL2CPP interop layer GENERATES when someone attaches a loader to the game - the managed proxies a modder needs in order to write code against Core, UnityEngine and the generated glue. Their presence on disk is therefore evidence of tooling rather than of a modified game, and the check finds a prepared modding environment even when nothing is injected at run time.
The scan is deliberately gentle: it awaits UniTask.NextFrame() between files, so it walks the tree one entry per frame rather than stalling the game.
========================================================================= WHEN IT IS SENT, AND WHEN IT IS NOT =========================================================================
Either check firing calls the same reporter, which sends this message and stops. Neither check firing sends nothing at all. A clean install is therefore SILENT - which is why this message will not appear in an ordinary capture, and why its absence carries no information.
The whole routine is also gated on a configuration value read from the client's configuration service: if that value is 4 or greater the scan returns immediately without checking anything.
========================================================================= IT DELIBERATELY TELLS THE SERVER NOTHING ELSE =========================================================================
The client computes a reason internally - "detected dir" or "detected DLLs" - and then DISCARDS it. The reporter takes that string as a parameter and the async method that actually sends this message has no string field at all, so the reason never reaches the wire.
So the server learns exactly one bit: this client tripped the check. It does not learn which loader, which file, or which of the two checks fired. That is consistent with the message having no members in the first place - it is not an empty message that happens to mean something, it is a deliberate one-bit signal.
========================================================================= WHERE IT SITS IN THE PROTOCOL =========================================================================
It belongs to Game.Client.Verification, and it is the ODD ONE OUT. Every other member of that area is a REPLY or a server-driven prompt:
ServerVerificationEvent server obliges the client to identify itself ClientIdRequest client answers with a fresh commitment ClientChallengeInitRequest client publishes its long-lived public key ServerChallengeEvent server's challenge ClientChallengeProofRequest client's proof, closing the handshake FileCheckEvent server names a game file to measure FileCheckRequest client returns the measurement CheckType which measurement: byte length, or a checksum
Those are a three-step proof-of-identity handshake plus an on-demand file-integrity probe, and every one of them is started by the server. THIS message is the only one the client raises on its own initiative, unprompted, and the only one that reports an observation rather than answering a question.
ServerSessionReadyEvent shares the same generated .proto file but is documented under Game.Connection, because it belongs to the connection handshake rather than to verification.
========================================================================= IF YOU ARE WRITING A CLIENT =========================================================================
There is nothing to fill in and nothing to wait for - the send is fire-and-forget from an async void, and no client code awaits a reply.
A well-behaved client never sends it. Sending it volunteers a report about yourself, so the correct behaviour for a reimplementation is simply to never emit it. Note also what it does NOT do: it does not scan process memory, does not enumerate loaded modules, and does not hash anything - it is a filesystem check and nothing more. File integrity is a separate channel, and the server drives that one through FileCheckEvent.
When you may send it
- Requires
transport_establishedinferred
Related messages
- Nothing answers it - send it and move on nothing in the client waits on a reply; the send is fire-and-forget from an async void
Recovered with score 140, confidence 0.85, margin 140 over the runner-up. A margin close to the score means nothing else came near; a thin margin means a decoy nearly won. Members recovered: 0 of 0.
Aucun avis à afficher.