| name | game-ctf |
| description | Lab/CTF: game/GamePwn challenges; Unity Mono/IL2CPP, native game binaries, assets, save files, memory dumps, game network captures. |
| license | MIT |
| compatibility | Linux/Windows; Unity (Mono/IL2CPP), native ELF/PE; tools: dnSpy, Il2CppDumper, Ghidra, UABE, Cheat Engine, Python. |
| metadata | {"author":"AeonDave","version":"1.0","category":"ctf-solving"} |
Game CTF
Solve game challenges by identifying the engine and runtime first, then choosing the narrowest extraction or patching path before escalating to full binary reversing.
When this skill applies
- Artifact is a game binary:
.exe, ELF, GameAssembly.dll, Assembly-CSharp.dll, or a Unity build folder.
- Challenge requires reaching a win condition, unlocking a hidden flag, bypassing a score check, or extracting data from game assets.
- Artifact includes Unity asset files (
*.assets, *.dmp, sharedassets*, globalgamemanagers).
- Challenge involves intercepting or replaying a game network protocol to obtain the flag.
Operating model
1. Identify engine: Unity Mono | Unity IL2CPP | Native (SDL/OpenGL/custom) | Godot | other
2. Quick win: strings <binary> | grep flag{ — catches ~20% of challenges
3. Static analysis path per engine type
4. If flag not found: memory manipulation (Cheat Engine/Python) or network replay
5. Validate and submit
Technique integration
reversing-technique for obfuscated native binaries, custom crypto, or complex control flow.
pwn-ctf if the game binary has an exploitable memory corruption vulnerability.
game-technique for Cheat Engine workflow, pointer scanning, and real-world memory hacking patterns.
Engine identification
file target
ls <GameName>_Data/Managed/Assembly-CSharp.dll
ls GameAssembly.dll
ls <GameName>_Data/Native/GameAssembly.so
ls <GameName>_Data/il2cpp_data/Metadata/global-metadata.dat
strings <binary> | grep -i "SDL_\|OpenGL\|GLFW\|raylib\|allegro"
ls *.pck
strings <binary> | grep -i "godot\|GDScript"
Type 1 — Unity Mono (.NET)
Assembly-CSharp.dll is a plain .NET assembly — decompile directly with dnSpy.
dotnet tool install -g ilspycmd
ilspycmd Assembly-CSharp.dll -o decompiled/
grep -r "flag{\|flag\|win\|score\|complete\|cheat\|unlock" decompiled/ -i | head -20
Common CTF patterns in Mono games:
WinCondition.CheckScore() compares player score against hardcoded threshold → change threshold to 0 or patch ret true
FlagManager.GetFlag() returns encrypted string → patch to return plaintext or log the decrypted value
GameManager.gameOver boolean → force set via Cheat Engine or DLL patch
Type 2 — Unity IL2CPP
C# compiled to native ARM/x86 binary. GameAssembly.dll contains game logic but method names are stripped without metadata.
Step 1 — Dump class/method names with Il2CppDumper
Il2CppDumper.exe GameAssembly.dll global-metadata.dat output/
grep -i "flag{\|flag\|win\|score\|cheat\|unlock\|complete\|GetFlag\|CheckScore" output/dump.cs
python3 -c "
import json
data = json.load(open('output/stringliteral.json'))
for entry in data:
if 'flag{' in entry.get('value','') or 'flag' in entry.get('value','').lower():
print(entry)
"
Step 2 — Ghidra analysis with Il2CppDumper script
Step 3 — Memory patch via Cheat Engine
python3 - <<'EOF'
import ctypes, struct
pid = <game_pid>
target_addr = <address_from_il2cppdumper_offset>
with open(f'/proc/{pid}/mem', 'rb') as m:
m.seek(target_addr)
val = struct.unpack('<i', m.read(4))[0]
print(f'Current: {val}')
with open(f'/proc/{pid}/mem', 'r+b') as m:
m.seek(target_addr)
m.write(struct.pack('<i', 999999))
EOF
Type 3 — Unity Asset files
Asset files (*.assets, *.dmp, sharedassets*.assets, globalgamemanagers) contain game data: textures, text, audio, MonoBehaviour configs.
strings game_radar_challenge/assets.dmp | grep -i "flag{\|flag\|secret\|key" | head -20
strings sharedassets0.assets | grep -i "flag{"
git clone https://github.com/AssetRipper/AssetRipper
python3 -c "
import re
data = open('assets.dmp','rb').read()
# Generic flag pattern — swap PREFIX (e.g. HTB, CTF, pico, flag) per event
print('Flags:', re.findall(rb'(?:flag|HTB|CTF|pico)\{[^}]{1,80}\}', data, re.I))
print('Strings (16+ printable chars):')
for s in re.findall(b'[\x20-\x7e]{16,}', data):
print(s.decode())
" | head -40
Type 4 — Native game binary (SDL / OpenGL / custom)
Pure C/C++ game. Standard binary reversing + memory manipulation.
strings radar_challenge | grep -iE "flag\{|HTB\{|CTF\{|pico\{|score|win|cheat|pass|unlock|level"
checksec --file=radar_challenge
r2 -A radar_challenge
[0x00401234]> afl | grep -i "win\|flag\|check\|score\|valid"
[0x00401234]> pdf @ sym.check_win
python3 -c "
data = bytearray(open('radar_challenge','rb').read())
# Find and patch offset from Ghidra analysis
data[0x1234:0x1238] = b'\\x31\\xc0\\xff\\xc0\\x90' # xor eax,eax; inc eax; nop
open('radar_patched','wb').write(data)
"
scanmem --pid=$(pgrep radar_challenge)
Type 5 — Godot games
godotpcktool extract game.pck -o extracted/
find extracted/ -name "*.gd" | xargs grep -i "flag\|password\|flag{\|win\|cheat"
Type 6 — Game network protocol
When the game connects to a server and the server validates score/flag.
tcpdump -i lo -w game.pcap
python3 - <<'EOF'
import socket, struct
HOST, PORT = '127.0.0.1', 31337
packet = struct.pack('>I', 999999)
with socket.socket() as s:
s.connect((HOST, PORT))
s.send(packet)
print(s.recv(1024))
EOF
Quick pivots by symptom
| Symptom | Action |
|---|
Assembly-CSharp.dll present | Unity Mono → dnSpy → patch WinCondition |
GameAssembly.dll + metadata | Unity IL2CPP → Il2CppDumper → dump.cs → Ghidra/Cheat Engine |
*.assets / *.dmp files | strings + UABE/AssetRipper — flag may be text asset |
*.pck file | Godot → godotpcktool extract → grep .gd scripts |
| Native ELF/PE, SDL strings | Binary reversing → checksec → r2/Ghidra → patch win condition |
| Game connects to server | Capture traffic → replay with tampered score/flag field |
| Score comparison in decompiled code | Patch comparison (JNZ→JMP) or set score via Cheat Engine/ptrace |
| Flag printed only on legit win | Hook flag-print function with Frida or breakpoint in debugger |
Resources