196 lines
8.3 KiB
Markdown
196 lines
8.3 KiB
Markdown
# 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](https://frida.re/) (`pip install frida-tools`)
|
|
- [imgui-bundle](https://github.com/pthom/imgui_bundle) (`pip install imgui-bundle`)
|
|
- [cryptography](https://pypi.org/project/cryptography/) (`pip install cryptography`)
|
|
- [protobuf](https://pypi.org/project/protobuf/) (`pip install protobuf`)
|
|
|
|
```
|
|
pip install frida-tools imgui-bundle cryptography protobuf
|
|
```
|
|
|
|
## Quick Start
|
|
|
|
### GUI mode (recommended)
|
|
|
|
```bat
|
|
cd frida_hooks
|
|
run_gui.bat
|
|
```
|
|
|
|
1. GUI opens with proxy listening on ports 11001 (game) and 18801 (HTTP dispatch)
|
|
2. Click **"Launch Game (Steam)"** or launch via Steam manually
|
|
3. Frida auto-attaches, hooks installed, redirect active
|
|
4. Log in — packets stream into the **Packets** tab
|
|
5. Toggle modifiers in the **Config** tab
|
|
6. Click any packet → **Send** tab → **"Replay Original"** or **"Open in Composer"**
|
|
|
|
### Headless mode (CLI)
|
|
|
|
```bat
|
|
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)
|
|
|
|
```bat
|
|
:: 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:
|
|
1. Select type from dropdown (all 191 dispatch types)
|
|
2. Edit JSON in syntax-highlighted editor
|
|
3. Choose direction (C->S / S->C)
|
|
4. 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_cards` collection (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.
|