Paralogue MITM Toolkit
Man-in-the-middle proxy and protocol analysis toolkit for Operation Lovecraft: Fallen Doll (Paralogue). Intercepts, decodes, modifies, and injects game network traffic in real time via a desktop GUI.
Features
- Real-time packet inspector — decoded protobuf messages with JSON syntax highlighting and hex dump
- Bidirectional wire decryption — ChaCha20 key extraction via Frida, proxy-side decrypt/re-encrypt
- Packet modification — frame-aware S->C rewriter with pluggable modifier callbacks
- Packet injection — send custom or replayed packets in both C->S and S->C directions
- Full type mapping — all 191 dispatch-table msg_ids resolved to protobuf type names via Frida GetTypeName()
- ImGui GUI with 6 tabs: Packets, Config, Session, Wire, Dispatch, Composer
Requirements
- Python 3.12+ (tested on 3.13)
- Windows 10/11
- Frida (
pip install frida-tools) - imgui-bundle (
pip install imgui-bundle) - cryptography (
pip install cryptography) - protobuf (
pip install protobuf)
pip install frida-tools imgui-bundle cryptography protobuf
Quick Start
GUI mode (recommended)
cd frida_hooks
run_gui.bat
- GUI opens with proxy listening on ports 11001 (game) and 18801 (HTTP dispatch)
- Click "Launch Game (Steam)" or launch via Steam manually
- Frida auto-attaches, hooks installed, redirect active
- Log in — packets stream into the Packets tab
- Toggle modifiers in the Config tab
- Click any packet → Send tab → "Replay Original" or "Open in Composer"
Headless mode (CLI)
cd frida_hooks
run_mitm.bat :: normal passthrough
run_mitm.bat --inject-items :: with all modifiers active
run_mitm.bat --no-decoder :: proxy only, no Frida
Re-extract type names (after game update)
:: Game must be running
cd frida_hooks
run_extract_types.bat
Updates dispatch_type_names.json, known_msg_ids.json, and factory_to_type.json.
Architecture
┌────────────┐ ┌─────────────────────┐ ┌──────────────┐
│ Game │◄──►│ Proxy (port 11001) │◄──►│ Real Server │
│ Client │ │ ┌───────────────┐ │ │ 3.33.185.193│
│ │ │ │ WireRewriter │ │ │ │
│ Frida ────┼────┤ │ S->C + C->S │ │ │ │
│ hooks │ │ └───────────────┘ │ │ │
└────────────┘ │ ┌───────────────┐ │ └──────────────┘
│ │ Modifier Chain│ │
│ └───────────────┘ │
└──────────┬──────────┘
│ SharedState
┌──────────▼──────────┐
│ ImGui GUI │
│ (main thread) │
└─────────────────────┘
Threading model
- Main thread: ImGui render loop at ~60fps
- Background daemon thread: asyncio event loop running the TCP proxy, Frida bridge, and injection consumer
- SharedState: thread-safe bridge (deque for packets, Lock for config, inject_queue for packet injection)
Encryption
The game uses ChaCha20 stream cipher with a shared key+nonce for both directions but independent byte counters:
| Offset | Field | Usage |
|---|---|---|
| +0x68 | key ptr | 32B ChaCha20 key (shared) |
| +0x78 | nonce ptr | 12B nonce (shared) |
| +0x88 | EVP ctx | encrypt (C->S) |
| +0x90 | EVP ctx | decrypt (S->C) |
| +0x98 | counter | encrypt byte offset (C->S) |
| +0xA0 | counter | decrypt byte offset (S->C) |
The proxy uses WireRewriter per direction to decrypt→modify→re-encrypt. ChaCha20 XOR involution means identity transforms produce byte-identical output.
Wire format
[2B big-endian prefix N][N-byte PacketHead protobuf][body_len bytes body]
PacketHead fields: msg_id, body_len, packet_id (request-response correlation), seq_id, flag, total_pack_count.
GUI Tabs
Packets
Real-time scrolling table of decoded messages. Filter by direction, source (Frida/wire), msg_id, or type name. Click a row to expand:
- Tree — recursive protobuf field viewer
- JSON — syntax-highlighted JSON via TextEditor
- Hex — formatted hex dump with ASCII sidebar
- Send — replay original or open in Composer
Config
Toggle modifiers individually:
- Card Bag Expand —
CardBagOpenRsp.rets→ 0..2000 (unlock experiments) - Inject Blueprints — add 11000..11999 to bag (lootbox items)
- Role Attrs Saturate — max affection, dilation, unlock all roles
- Inject Bag Items — dense fill across cloth/card/normal ranges
- Unlock Functions — populate
PlayerMainDataRsp.unlockFunctions
Parameter sliders for card range, attr values. Auto-saves to gui_config.json.
Session
Session key/nonce, game PID, Frida status (color-coded), proxy ports, dispatch summary, packet counts. Action buttons:
- Restart Frida — reattach after game relaunch
- Launch Game (Steam) — opens via
steam://rungameid/2701680
Wire
Raw encrypted traffic hex viewer with timestamps and direction arrows.
Dispatch
All 191 msg_id → protobuf type mappings. Green = pinned (resolved), red = unknown. Searchable by type name.
Composer
Compose and send custom protobuf packets:
- Select type from dropdown (all 191 dispatch types)
- Edit JSON in syntax-highlighted editor
- Choose direction (C->S / S->C)
- Click Send
Files
| File | Purpose |
|---|---|
frida_hooks/paralogue_gui.py |
ImGui GUI application |
frida_hooks/paralogue_mitm.py |
Core proxy + Frida bridge + WireRewriter |
frida_hooks/paralogue_decoder.py |
Protobuf framing, identification, parsing |
frida_hooks/protocol_decoder.js |
Frida hooks for decrypt/encrypt wrappers |
frida_hooks/redirect_connect.js |
Frida ws2_32!connect redirect hook |
frida_hooks/dispatch_dump.js |
Frida dispatch table walker |
frida_hooks/extract_type_names.js |
Frida GetTypeName() extraction |
frida_hooks/item_table_dump.js |
Frida bag item subsystem walker |
frida_hooks/paralogue_net_full.proto |
239-message protobuf schema |
frida_hooks/paralogue_net_full_pb2.py |
Compiled protobuf module |
frida_hooks/dispatch_type_names.json |
msg_id → type name (ground truth, 191 entries) |
frida_hooks/factory_to_type.json |
vtable → type name (stable across runs) |
frida_hooks/known_msg_ids.json |
Merged msg_id → type cache |
frida_hooks/gui_config.json |
Persisted GUI modifier toggles |
frida_hooks/run_gui.bat |
GUI launcher |
frida_hooks/run_mitm.bat |
CLI launcher |
frida_hooks/run_extract_types.bat |
Type extraction launcher |
RESEARCH.md |
Detailed protocol research notes |
Game Data Model
Bag items (BagItemFullS2C, msg_id 1036)
| ID range | Category | Description |
|---|---|---|
| 0-999 | Experiments/Cards | Unlocked via CardBagOpen |
| 11000-11999 | Pack/Blueprints | Lootbox items (open → grant card) |
| 20000-28999 | Card/Normal | Collectibles |
| 31000-31999 | Cloth | Outfit pieces |
| 40000-40999 | Normal | Misc items |
| 99000-99999 | Money | Currencies (with counts) |
Experiments
- Stored as client-side
learn_cardscollection (persisted on disk) - Updated incrementally via
CardBagOpenRsp.rets[].card_id - UI filters by client DataTable (invalid IDs silently dropped)
- Unlock all: enable "Card Bag Expand" modifier, open one blueprint
Roles (RoleListFullS2C, msg_id 1202)
Per-role data: name, is_unlock, favor_lv, worn_clothes[], preset_clothes[], attrs{}, appearance, clothes_colors.
License
Research and educational purposes only. Not affiliated with the game developers.