Backend Reversing
Reconstruct a server from a client. The client is a deterministic state machine that must parse, validate, and serialize everything the server understands — so the compiled client already contains the complete shape of the protocol. This skill walks that shape out of the binary and turns it into a running emulator, and it pairs each step with the hardening that would have stopped it.
Before anything: scope and footing
This pipeline only makes sense — and is only appropriate — against a target you are allowed to analyze. Confirm one of these holds before you start, and say which in your report:
- Preservation — a defunct or offline game whose official servers are gone, or a game with an offline/single-player mode you are studying locally.
- Ownership — software you wrote or own.
- Authorization — a sanctioned engagement, bug-bounty scope, or written permission to test.
- CTF / research — a challenge target or a lab environment built for this.
Two things this skill deliberately does not do, because they move the work out of that scope: circumventing live-service anti-cheat or DRM to force a protected client into an analyzable state, and defeating certificate pinning or instrumentation-detection on a production service. Analyze a target that is already in a state you may study — an offline build, a local instance, a defunct client — rather than breaking an active protection to get there. If the user needs the protection gone first, that is where this skill stops.
The pipeline
Seven phases. Most projects run them roughly in order, but they feed back into each other — a serialization finding sends you back to the decompiler, a handshake finding changes how you capture. Do not treat this as a waterfall.
- Client audit — locate the game objects and the netcode in memory.
- Identify the engine — the engine dictates every tool after this.
- Decompile — recover class names, field offsets, and method signatures.
- Reach the plaintext — capture buffers before encryption, and understand the handshake well enough to know you can.
- Model the protocol — framing, compression, serialization, endpoints.
- Emulate — a concurrent authoritative server that speaks the protocol.
- Harden — for each finding, the server-side fix that closes it.
Phase 1 — Client audit
Two complementary reads of the running (or on-disk) client.
Memory and class discovery. Runtime state — health, resources,
coordinates, status — lives contiguously in the process. A scanner (Cheat
Engine, or scripted with a memory-access library) isolates a value by delta
scanning: search a data type, change the value in-game, search again for the
new value, repeat until one address remains. Then a hardware breakpoint via the
debug registers ("find what writes to this address") halts on the instruction
that mutates it and exposes the offset form — MOV [RCX + 0x18], EAX says the
value sits at +0x18 in the object RCX points at. Neighboring offsets are
usually the sibling fields the compiler grouped into the same object, so one
breakpoint often maps a whole class. Those offsets are the same fields the
server must track.
Netcode hooking. To read payloads before symmetric encryption or
compression, hook the send/receive path rather than the wire. On Windows the
targets are the Winsock calls send, recv, WSASend, WSARecv; a small
Detours-based DLL logs the buffer and passes the call through. That buffer is
plaintext — the client has to hand the socket cleartext, whatever it did
before. This is almost always faster than attacking the crypto (Phase 4).
Do this against a client already in an analyzable state (see scope). If the
target is a native (non-managed) binary, x64dbg / Ghidra / IDA on the on-disk
executable get you the same offsets statically.
Phase 2 — Identify the engine
Everything downstream depends on it. Quick tells:
- Unity —
UnityPlayer.dll, a*_Data/folder,GameAssembly.dll+global-metadata.dat(IL2CPP) orManaged/*.dll(Mono). - Unreal —
*/Binaries/,*.pakfiles,*-Shipping.exe, engine strings. - Godot — a
.pcknext to the executable, or data appended to the exe;project.godotinside the pack.
Then read references/engines.md for the exact per-engine recovery route. Do
not proceed on memory of one engine's toolchain — the metadata formats, the
dumpers, and the failure modes differ sharply.
Phase 3 — Decompile
Recover the type system: class names, field offsets, method signatures, and the
serialization functions. The output you want is a map from raw offsets and
native subroutines back to meaningful names, so that a captured buffer becomes
readable structs. The engine reference covers IL2CPP (Il2CppDumper / Cpp2IL,
dummy DLLs, Ghidra scripts, encrypted-metadata recovery), Unreal (GObjects /
GNames SDK generation), and Godot (.pck extraction, GDScript decompilation).
If the local save or config is encrypted, the same decompiled code holds the
key-derivation routine — trace the save/prefs initializer. A recurring pattern
is Rfc2898DeriveBytes(folder_name + static_constant, static_salt) feeding
AES-128-CBC. Recovering that lets you read local state, which often documents
field meanings the network protocol leaves implicit.
Phase 4 — Reach the plaintext
You rarely break the crypto; you capture around it (Phase 1 hook) or you find the key in memory (Phase 3). But you must understand the handshake to know which is possible and to replicate it in the emulator, because the server side has to perform the other half.
references/crypto-handshakes.md covers Diffie-Hellman (and ephemeral
DHE/ECDHE), RSA key transport, and the KDF-to-AES step, with the reversing
implications of each — most importantly that a static-RSA session can be
decrypted after the fact if the server key leaks, while an ephemeral exchange
cannot, which changes whether captured traffic alone is enough.
Phase 5 — Model the protocol
Turn captured bytes into a schema. Order of operations on a raw buffer:
- Deframe. Most custom protocols are Type-Length-Value. Split the stream
into frames before anything else —
scripts/tlv_parse.py. - Decompress. Large payloads are often compressed; the magic bytes tell
you which —
scripts/identify_payload.pydetects and unwraps common formats and then guesses the serialization inside. - Deserialize. Protobuf, FlatBuffers, gRPC, or MessagePack. For a
schemaless Protobuf capture,
scripts/decode_protobuf.pywalks the wire format with no.proto. The rest — FlatBuffers vtable tracing, gRPC vtable deconstruction in stripped C++ binaries, MessagePack reconstruction — is inreferences/serialization.md.
UDP protocols usually add a reliability layer (sequence numbers, ack flags) on top of the framing; account for it before assuming a frame boundary.
Phase 6 — Emulate
Build an authoritative server that speaks the recovered protocol and holds the
real game state. references/emulator.md covers the two dominant concurrency
models (Rust/Tokio async, C++/actor), persistence with a write-behind cache so
per-action DB writes don't bottleneck, loading client template data so the
server's math matches the client's, and replication managers for scaling world
state. The central rule: the server is authoritative and validates every
incoming payload — never trust the client's numbers.
Phase 7 — Harden
The reason to keep the defensive pairing in view: a backend that survived this process would have made each phase harder. Emit these alongside the findings — they are the deliverable for anyone doing this to protect a service.
- Server authority. Treat the client as a compromised UI. Validate every payload against physics and game-state bounds before it touches the database. This defeats the Phase 1 memory edits and injection outright.
- Centralize rules. Keep drop tables, economy, and security checks on the server, never the client. A memory scanner can only reach what the client computes.
- Modern, ephemeral crypto. TLS or ECDHE gives forward secrecy — a leaked server key does not retroactively decrypt captured sessions.
- Protect metadata. Encrypting
global-metadata.datand stripping symbols does not stop a determined analyst, but it raises the cost of Phase 3. - Verify the client. mTLS or a real handshake makes an unauthorized third-party emulator unable to connect to production.
Bundled scripts
All four are pure-Python (standard library only), operate on data you have already captured, and are safe to run repeatedly. Point them at a hex string or a file.
| Script | Use it when |
|---|---|
scripts/identify_payload.py |
You have a raw buffer and do not know what it is — detects compression (gzip/zlib/bzip2/xz) and unwraps it, then guesses the serialization inside. Run this first on any unknown payload. |
scripts/tlv_parse.py |
The stream is Type-Length-Value framed and you need it split into frames. Configurable type/length width and endianness. |
scripts/decode_protobuf.py |
You captured a Protobuf message but do not have the .proto. Walks the wire format and prints a field tree, recursing into nested messages. |
scripts/il2cpp_metadata_probe.py |
A Unity IL2CPP global-metadata.dat fails to load — checks the header magic and version, and detects/recovers a single-byte XOR obfuscation of the header. |
Run any of them with --help for arguments. They compose: tlv_parse.py
splits frames, identify_payload.py triages each value, decode_protobuf.py
decodes the ones that are Protobuf.
References
Read the one the current phase needs; do not preload all of them.
| File | Contents |
|---|---|
references/engines.md |
Per-engine decompilation: Unity IL2CPP (dumpers, dummy DLLs, Ghidra integration, encrypted metadata), Unreal (GObjects/GNames SDK generation), Godot (.pck, GDScript). |
references/serialization.md |
Protobuf vs FlatBuffers, --decode_raw, FlatBuffers vtable tracing, gRPC stripped-C++ vtable deconstruction, MessagePack. |
references/crypto-handshakes.md |
Diffie-Hellman, DHE/ECDHE, RSA transport, the KDF-to-AES step, and what each means for capture and emulation. |
references/emulator.md |
Concurrency models, persistence and write-behind, template data, replication managers, and the server-authority rules. |
Output format
When you run this skill for a user, produce a running findings document, not a lecture. Structure it as:
- Scope — which authorized footing applies, and the target's state.
- What was recovered — engine, key classes and offsets, protocol framing, serialization format, endpoint list, handshake type. Concrete: names, offsets, field numbers, not "the protocol was analyzed".
- Open unknowns — what is not yet mapped and what would map it.
- Emulator plan — concurrency model, persistence, the endpoints to implement first.
- Hardening — the Phase 7 pairing for each finding.
Lead with what you actually recovered. A precise Login = TLV type 0x02, body is Protobuf, field 1 = username (string), field 2 = client_version (varint) is
worth more than a page of methodology the user already has.