open-legacy-rpc 0.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. open_legacy_rpc-0.0.2/LICENSE +21 -0
  2. open_legacy_rpc-0.0.2/PKG-INFO +150 -0
  3. open_legacy_rpc-0.0.2/README.md +135 -0
  4. open_legacy_rpc-0.0.2/open_legacy_rpc.egg-info/PKG-INFO +150 -0
  5. open_legacy_rpc-0.0.2/open_legacy_rpc.egg-info/SOURCES.txt +39 -0
  6. open_legacy_rpc-0.0.2/open_legacy_rpc.egg-info/dependency_links.txt +1 -0
  7. open_legacy_rpc-0.0.2/open_legacy_rpc.egg-info/requires.txt +4 -0
  8. open_legacy_rpc-0.0.2/open_legacy_rpc.egg-info/top_level.txt +1 -0
  9. open_legacy_rpc-0.0.2/protocol/__init__.py +0 -0
  10. open_legacy_rpc-0.0.2/protocol/analyze_pcap.py +137 -0
  11. open_legacy_rpc-0.0.2/protocol/bitstream.py +229 -0
  12. open_legacy_rpc-0.0.2/protocol/diff_experiment.py +180 -0
  13. open_legacy_rpc-0.0.2/protocol/huffman.py +142 -0
  14. open_legacy_rpc-0.0.2/protocol/master_client.py +201 -0
  15. open_legacy_rpc-0.0.2/protocol/master_serialization.py +363 -0
  16. open_legacy_rpc-0.0.2/protocol/master_server.py +404 -0
  17. open_legacy_rpc-0.0.2/protocol/pcap_reader.py +147 -0
  18. open_legacy_rpc-0.0.2/protocol/raknet.py +260 -0
  19. open_legacy_rpc-0.0.2/protocol/raknet_connection.py +514 -0
  20. open_legacy_rpc-0.0.2/protocol/raknet_transport.py +514 -0
  21. open_legacy_rpc-0.0.2/protocol/unity_client.py +184 -0
  22. open_legacy_rpc-0.0.2/protocol/unity_protocol.py +1019 -0
  23. open_legacy_rpc-0.0.2/protocol/unity_rpc_decode.py +422 -0
  24. open_legacy_rpc-0.0.2/protocol/unity_server.py +345 -0
  25. open_legacy_rpc-0.0.2/pyproject.toml +37 -0
  26. open_legacy_rpc-0.0.2/setup.cfg +4 -0
  27. open_legacy_rpc-0.0.2/tests/test_bitstream.py +297 -0
  28. open_legacy_rpc-0.0.2/tests/test_cross_assembly_batch_index_dispatch.py +110 -0
  29. open_legacy_rpc-0.0.2/tests/test_cross_conformance.py +350 -0
  30. open_legacy_rpc-0.0.2/tests/test_decode_pcap.py +377 -0
  31. open_legacy_rpc-0.0.2/tests/test_decode_safety.py +159 -0
  32. open_legacy_rpc-0.0.2/tests/test_delta_state_sync.py +224 -0
  33. open_legacy_rpc-0.0.2/tests/test_exception_safety.py +72 -0
  34. open_legacy_rpc-0.0.2/tests/test_master_server.py +653 -0
  35. open_legacy_rpc-0.0.2/tests/test_network_groups.py +214 -0
  36. open_legacy_rpc-0.0.2/tests/test_python_integration.py +410 -0
  37. open_legacy_rpc-0.0.2/tests/test_raknet_transport.py +355 -0
  38. open_legacy_rpc-0.0.2/tests/test_unity_interop.py +84 -0
  39. open_legacy_rpc-0.0.2/tests/test_unity_interop_server.py +128 -0
  40. open_legacy_rpc-0.0.2/tests/test_unity_protocol.py +1088 -0
  41. open_legacy_rpc-0.0.2/tests/test_viewid_allocation.py +202 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2026 Rose
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,150 @@
1
+ Metadata-Version: 2.4
2
+ Name: open-legacy-rpc
3
+ Version: 0.0.2
4
+ Summary: Reverse-engineered wire format and implementation of Unity's legacy RPC networking (NetworkView / [RPC] / OnSerializeNetworkView)
5
+ License-Expression: MIT
6
+ Project-URL: Repository, https://github.com/Rose-Vibe/open-legacy-rpc
7
+ Project-URL: Documentation, https://github.com/Rose-Vibe/open-legacy-rpc/tree/main/docs
8
+ Requires-Python: >=3.8
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Provides-Extra: dev
12
+ Requires-Dist: pytest; extra == "dev"
13
+ Requires-Dist: ruff~=0.14; extra == "dev"
14
+ Dynamic: license-file
15
+
16
+ # open-legacy-rpc
17
+
18
+ Reverse-engineered wire format spec and implementations (Python + TypeScript)
19
+ of Unity's legacy RPC networking stack (`NetworkView` / `[RPC]` /
20
+ `OnSerializeNetworkView`), last supported in Unity 2018.1.6f1. The transport
21
+ layer is RakNet 3.731; this project documents and implements **Unity's
22
+ undocumented framing on top of RakNet**.
23
+
24
+ Unity is a trademark of Unity Technologies. This project is independent,
25
+ clean-room reverse-engineered, and not affiliated with or endorsed by Unity
26
+ Technologies.
27
+
28
+ ## Repository Structure
29
+
30
+ ```
31
+ protocol/ Python reference implementation (client + server)
32
+ ts/ TypeScript production implementation (client + server)
33
+ experiments/ Unity projects for capture-based protocol analysis
34
+ docs/research/ Wire format specifications
35
+ docs/notes/ Lab notes and setup guides
36
+ docs/plans/ Sprint plans (active and archived)
37
+ ```
38
+
39
+ ## Prerequisites
40
+
41
+ - **Python**: 3.10+
42
+ - **Node.js**: 18+ (TypeScript implementation)
43
+
44
+ ## Quick Start
45
+
46
+ ### Python
47
+
48
+ ```bash
49
+ cd protocol
50
+ pip install -e . # or: pip install -r ../requirements-dev.txt
51
+ python -m pytest ../tests/
52
+ ```
53
+
54
+ ### TypeScript
55
+
56
+ ```bash
57
+ cd ts
58
+ npm install
59
+ npm test
60
+ ```
61
+
62
+ ## Wire Format Specs
63
+
64
+ - [Unity Legacy Networking Wire Format](docs/research/unity-legacy-networking-wire-format.md)
65
+ - [Master Server Wire Format](docs/research/master-server-wire-format.md)
66
+ - [Spec Status](docs/research/spec-status.md)
67
+
68
+ ## Usage
69
+
70
+ ### TypeScript Server
71
+
72
+ ```typescript
73
+ import { UnityServer, createUdpSocket } from 'open-legacy-rpc';
74
+
75
+ const socket = createUdpSocket();
76
+ const server = new UnityServer(socket, { port: 7777 });
77
+
78
+ server.onConnect((session) => {
79
+ console.log(`Client connected: ${session.id}`);
80
+ });
81
+
82
+ server.onRpc('MyRpc', (session, ...params) => {
83
+ console.log('RPC received:', params);
84
+ });
85
+
86
+ await server.start();
87
+ ```
88
+
89
+ ### Python Client
90
+
91
+ ```python
92
+ from protocol.unity_client import UnityClient
93
+
94
+ client = UnityClient()
95
+ client.connect('127.0.0.1', 7777)
96
+
97
+ client.send_rpc('MyRpc', [42], view_id=1)
98
+
99
+ for msg in client.poll(timeout=1.0):
100
+ print(msg)
101
+
102
+ client.disconnect()
103
+ ```
104
+
105
+ ## PCAP Decoder
106
+
107
+ Decode pcap/pcapng captures into structured JSON through the full protocol
108
+ stack (RakNet → Unity → game-specific):
109
+
110
+ ```bash
111
+ python3 tools/decode_pcap.py capture.pcap # all defaults
112
+ python3 tools/decode_pcap.py capture.pcap --config config.json # with config
113
+ python3 tools/decode_pcap.py capture.pcap --compact # single-line JSON
114
+ ```
115
+
116
+ Config file (all fields optional):
117
+
118
+ ```json
119
+ {
120
+ "serverPort": 7777,
121
+ "methodSignatures": { "RPC_TakeDamage": ["int", "float"] },
122
+ "viewNames": { "8192": "Player" }
123
+ }
124
+ ```
125
+
126
+ Output uses discriminated unions at each layer (`type` field). Game-specific
127
+ data that can't be decoded is marked `{ "type": "not_deserialized", "rawHex": "..." }`
128
+ for downstream post-processing.
129
+
130
+ ## Network Namespace
131
+
132
+ Unity's RakNet crashes when the host has >10 IPv4 addresses. Use the
133
+ included scripts to create an isolated network namespace:
134
+
135
+ ```bash
136
+ sudo bash netns-setup.sh # create namespace
137
+ sudo ip netns exec unitynet ./Player.x86_64 ... # run Unity inside
138
+ tshark -i veth-unet-h -f 'udp' -w /tmp/unity.pcap # capture from host
139
+ sudo bash netns-teardown.sh # clean up
140
+ ```
141
+
142
+ ## Shell Scripts
143
+
144
+ | Script | Purpose |
145
+ |--------|---------|
146
+ | `run-experiment.sh` | Run a Unity experiment with traffic capture |
147
+ | `run-interop-tests.sh` | End-to-end interop tests: Python <-> Unity |
148
+ | `run-server-only.sh` | Start a Unity server, wait for external connections |
149
+ | `netns-setup.sh` | Create network namespace for Unity |
150
+ | `netns-teardown.sh` | Tear down network namespace |
@@ -0,0 +1,135 @@
1
+ # open-legacy-rpc
2
+
3
+ Reverse-engineered wire format spec and implementations (Python + TypeScript)
4
+ of Unity's legacy RPC networking stack (`NetworkView` / `[RPC]` /
5
+ `OnSerializeNetworkView`), last supported in Unity 2018.1.6f1. The transport
6
+ layer is RakNet 3.731; this project documents and implements **Unity's
7
+ undocumented framing on top of RakNet**.
8
+
9
+ Unity is a trademark of Unity Technologies. This project is independent,
10
+ clean-room reverse-engineered, and not affiliated with or endorsed by Unity
11
+ Technologies.
12
+
13
+ ## Repository Structure
14
+
15
+ ```
16
+ protocol/ Python reference implementation (client + server)
17
+ ts/ TypeScript production implementation (client + server)
18
+ experiments/ Unity projects for capture-based protocol analysis
19
+ docs/research/ Wire format specifications
20
+ docs/notes/ Lab notes and setup guides
21
+ docs/plans/ Sprint plans (active and archived)
22
+ ```
23
+
24
+ ## Prerequisites
25
+
26
+ - **Python**: 3.10+
27
+ - **Node.js**: 18+ (TypeScript implementation)
28
+
29
+ ## Quick Start
30
+
31
+ ### Python
32
+
33
+ ```bash
34
+ cd protocol
35
+ pip install -e . # or: pip install -r ../requirements-dev.txt
36
+ python -m pytest ../tests/
37
+ ```
38
+
39
+ ### TypeScript
40
+
41
+ ```bash
42
+ cd ts
43
+ npm install
44
+ npm test
45
+ ```
46
+
47
+ ## Wire Format Specs
48
+
49
+ - [Unity Legacy Networking Wire Format](docs/research/unity-legacy-networking-wire-format.md)
50
+ - [Master Server Wire Format](docs/research/master-server-wire-format.md)
51
+ - [Spec Status](docs/research/spec-status.md)
52
+
53
+ ## Usage
54
+
55
+ ### TypeScript Server
56
+
57
+ ```typescript
58
+ import { UnityServer, createUdpSocket } from 'open-legacy-rpc';
59
+
60
+ const socket = createUdpSocket();
61
+ const server = new UnityServer(socket, { port: 7777 });
62
+
63
+ server.onConnect((session) => {
64
+ console.log(`Client connected: ${session.id}`);
65
+ });
66
+
67
+ server.onRpc('MyRpc', (session, ...params) => {
68
+ console.log('RPC received:', params);
69
+ });
70
+
71
+ await server.start();
72
+ ```
73
+
74
+ ### Python Client
75
+
76
+ ```python
77
+ from protocol.unity_client import UnityClient
78
+
79
+ client = UnityClient()
80
+ client.connect('127.0.0.1', 7777)
81
+
82
+ client.send_rpc('MyRpc', [42], view_id=1)
83
+
84
+ for msg in client.poll(timeout=1.0):
85
+ print(msg)
86
+
87
+ client.disconnect()
88
+ ```
89
+
90
+ ## PCAP Decoder
91
+
92
+ Decode pcap/pcapng captures into structured JSON through the full protocol
93
+ stack (RakNet → Unity → game-specific):
94
+
95
+ ```bash
96
+ python3 tools/decode_pcap.py capture.pcap # all defaults
97
+ python3 tools/decode_pcap.py capture.pcap --config config.json # with config
98
+ python3 tools/decode_pcap.py capture.pcap --compact # single-line JSON
99
+ ```
100
+
101
+ Config file (all fields optional):
102
+
103
+ ```json
104
+ {
105
+ "serverPort": 7777,
106
+ "methodSignatures": { "RPC_TakeDamage": ["int", "float"] },
107
+ "viewNames": { "8192": "Player" }
108
+ }
109
+ ```
110
+
111
+ Output uses discriminated unions at each layer (`type` field). Game-specific
112
+ data that can't be decoded is marked `{ "type": "not_deserialized", "rawHex": "..." }`
113
+ for downstream post-processing.
114
+
115
+ ## Network Namespace
116
+
117
+ Unity's RakNet crashes when the host has >10 IPv4 addresses. Use the
118
+ included scripts to create an isolated network namespace:
119
+
120
+ ```bash
121
+ sudo bash netns-setup.sh # create namespace
122
+ sudo ip netns exec unitynet ./Player.x86_64 ... # run Unity inside
123
+ tshark -i veth-unet-h -f 'udp' -w /tmp/unity.pcap # capture from host
124
+ sudo bash netns-teardown.sh # clean up
125
+ ```
126
+
127
+ ## Shell Scripts
128
+
129
+ | Script | Purpose |
130
+ |--------|---------|
131
+ | `run-experiment.sh` | Run a Unity experiment with traffic capture |
132
+ | `run-interop-tests.sh` | End-to-end interop tests: Python <-> Unity |
133
+ | `run-server-only.sh` | Start a Unity server, wait for external connections |
134
+ | `netns-setup.sh` | Create network namespace for Unity |
135
+ | `netns-teardown.sh` | Tear down network namespace |
@@ -0,0 +1,150 @@
1
+ Metadata-Version: 2.4
2
+ Name: open-legacy-rpc
3
+ Version: 0.0.2
4
+ Summary: Reverse-engineered wire format and implementation of Unity's legacy RPC networking (NetworkView / [RPC] / OnSerializeNetworkView)
5
+ License-Expression: MIT
6
+ Project-URL: Repository, https://github.com/Rose-Vibe/open-legacy-rpc
7
+ Project-URL: Documentation, https://github.com/Rose-Vibe/open-legacy-rpc/tree/main/docs
8
+ Requires-Python: >=3.8
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Provides-Extra: dev
12
+ Requires-Dist: pytest; extra == "dev"
13
+ Requires-Dist: ruff~=0.14; extra == "dev"
14
+ Dynamic: license-file
15
+
16
+ # open-legacy-rpc
17
+
18
+ Reverse-engineered wire format spec and implementations (Python + TypeScript)
19
+ of Unity's legacy RPC networking stack (`NetworkView` / `[RPC]` /
20
+ `OnSerializeNetworkView`), last supported in Unity 2018.1.6f1. The transport
21
+ layer is RakNet 3.731; this project documents and implements **Unity's
22
+ undocumented framing on top of RakNet**.
23
+
24
+ Unity is a trademark of Unity Technologies. This project is independent,
25
+ clean-room reverse-engineered, and not affiliated with or endorsed by Unity
26
+ Technologies.
27
+
28
+ ## Repository Structure
29
+
30
+ ```
31
+ protocol/ Python reference implementation (client + server)
32
+ ts/ TypeScript production implementation (client + server)
33
+ experiments/ Unity projects for capture-based protocol analysis
34
+ docs/research/ Wire format specifications
35
+ docs/notes/ Lab notes and setup guides
36
+ docs/plans/ Sprint plans (active and archived)
37
+ ```
38
+
39
+ ## Prerequisites
40
+
41
+ - **Python**: 3.10+
42
+ - **Node.js**: 18+ (TypeScript implementation)
43
+
44
+ ## Quick Start
45
+
46
+ ### Python
47
+
48
+ ```bash
49
+ cd protocol
50
+ pip install -e . # or: pip install -r ../requirements-dev.txt
51
+ python -m pytest ../tests/
52
+ ```
53
+
54
+ ### TypeScript
55
+
56
+ ```bash
57
+ cd ts
58
+ npm install
59
+ npm test
60
+ ```
61
+
62
+ ## Wire Format Specs
63
+
64
+ - [Unity Legacy Networking Wire Format](docs/research/unity-legacy-networking-wire-format.md)
65
+ - [Master Server Wire Format](docs/research/master-server-wire-format.md)
66
+ - [Spec Status](docs/research/spec-status.md)
67
+
68
+ ## Usage
69
+
70
+ ### TypeScript Server
71
+
72
+ ```typescript
73
+ import { UnityServer, createUdpSocket } from 'open-legacy-rpc';
74
+
75
+ const socket = createUdpSocket();
76
+ const server = new UnityServer(socket, { port: 7777 });
77
+
78
+ server.onConnect((session) => {
79
+ console.log(`Client connected: ${session.id}`);
80
+ });
81
+
82
+ server.onRpc('MyRpc', (session, ...params) => {
83
+ console.log('RPC received:', params);
84
+ });
85
+
86
+ await server.start();
87
+ ```
88
+
89
+ ### Python Client
90
+
91
+ ```python
92
+ from protocol.unity_client import UnityClient
93
+
94
+ client = UnityClient()
95
+ client.connect('127.0.0.1', 7777)
96
+
97
+ client.send_rpc('MyRpc', [42], view_id=1)
98
+
99
+ for msg in client.poll(timeout=1.0):
100
+ print(msg)
101
+
102
+ client.disconnect()
103
+ ```
104
+
105
+ ## PCAP Decoder
106
+
107
+ Decode pcap/pcapng captures into structured JSON through the full protocol
108
+ stack (RakNet → Unity → game-specific):
109
+
110
+ ```bash
111
+ python3 tools/decode_pcap.py capture.pcap # all defaults
112
+ python3 tools/decode_pcap.py capture.pcap --config config.json # with config
113
+ python3 tools/decode_pcap.py capture.pcap --compact # single-line JSON
114
+ ```
115
+
116
+ Config file (all fields optional):
117
+
118
+ ```json
119
+ {
120
+ "serverPort": 7777,
121
+ "methodSignatures": { "RPC_TakeDamage": ["int", "float"] },
122
+ "viewNames": { "8192": "Player" }
123
+ }
124
+ ```
125
+
126
+ Output uses discriminated unions at each layer (`type` field). Game-specific
127
+ data that can't be decoded is marked `{ "type": "not_deserialized", "rawHex": "..." }`
128
+ for downstream post-processing.
129
+
130
+ ## Network Namespace
131
+
132
+ Unity's RakNet crashes when the host has >10 IPv4 addresses. Use the
133
+ included scripts to create an isolated network namespace:
134
+
135
+ ```bash
136
+ sudo bash netns-setup.sh # create namespace
137
+ sudo ip netns exec unitynet ./Player.x86_64 ... # run Unity inside
138
+ tshark -i veth-unet-h -f 'udp' -w /tmp/unity.pcap # capture from host
139
+ sudo bash netns-teardown.sh # clean up
140
+ ```
141
+
142
+ ## Shell Scripts
143
+
144
+ | Script | Purpose |
145
+ |--------|---------|
146
+ | `run-experiment.sh` | Run a Unity experiment with traffic capture |
147
+ | `run-interop-tests.sh` | End-to-end interop tests: Python <-> Unity |
148
+ | `run-server-only.sh` | Start a Unity server, wait for external connections |
149
+ | `netns-setup.sh` | Create network namespace for Unity |
150
+ | `netns-teardown.sh` | Tear down network namespace |
@@ -0,0 +1,39 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ open_legacy_rpc.egg-info/PKG-INFO
5
+ open_legacy_rpc.egg-info/SOURCES.txt
6
+ open_legacy_rpc.egg-info/dependency_links.txt
7
+ open_legacy_rpc.egg-info/requires.txt
8
+ open_legacy_rpc.egg-info/top_level.txt
9
+ protocol/__init__.py
10
+ protocol/analyze_pcap.py
11
+ protocol/bitstream.py
12
+ protocol/diff_experiment.py
13
+ protocol/huffman.py
14
+ protocol/master_client.py
15
+ protocol/master_serialization.py
16
+ protocol/master_server.py
17
+ protocol/pcap_reader.py
18
+ protocol/raknet.py
19
+ protocol/raknet_connection.py
20
+ protocol/raknet_transport.py
21
+ protocol/unity_client.py
22
+ protocol/unity_protocol.py
23
+ protocol/unity_rpc_decode.py
24
+ protocol/unity_server.py
25
+ tests/test_bitstream.py
26
+ tests/test_cross_assembly_batch_index_dispatch.py
27
+ tests/test_cross_conformance.py
28
+ tests/test_decode_pcap.py
29
+ tests/test_decode_safety.py
30
+ tests/test_delta_state_sync.py
31
+ tests/test_exception_safety.py
32
+ tests/test_master_server.py
33
+ tests/test_network_groups.py
34
+ tests/test_python_integration.py
35
+ tests/test_raknet_transport.py
36
+ tests/test_unity_interop.py
37
+ tests/test_unity_interop_server.py
38
+ tests/test_unity_protocol.py
39
+ tests/test_viewid_allocation.py
@@ -0,0 +1,4 @@
1
+
2
+ [dev]
3
+ pytest
4
+ ruff~=0.14
File without changes
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Analyze pcap files from Unity legacy networking experiments.
4
+ Dumps UDP payloads with hex + ASCII, identifies RakNet framing,
5
+ and highlights potential Unity-layer messages.
6
+ """
7
+ import struct
8
+ import sys
9
+
10
+ from .pcap_reader import read_pcap
11
+
12
+
13
+ def hexdump(data, prefix=' '):
14
+ """Hex + ASCII dump."""
15
+ lines = []
16
+ for i in range(0, len(data), 16):
17
+ chunk = data[i:i+16]
18
+ hex_part = ' '.join(f'{b:02x}' for b in chunk)
19
+ ascii_part = ''.join(chr(b) if 32 <= b < 127 else '.' for b in chunk)
20
+ lines.append(f'{prefix}{i:04x} {hex_part:<48s} {ascii_part}')
21
+ return '\n'.join(lines)
22
+
23
+
24
+ # RakNet offline message magic
25
+ OFFLINE_MAGIC = bytes([
26
+ 0x00, 0xFF, 0xFF, 0x00, 0xFE, 0xFE, 0xFE, 0xFE,
27
+ 0xFD, 0xFD, 0xFD, 0xFD, 0x12, 0x34, 0x56, 0x78
28
+ ])
29
+
30
+
31
+ def classify_packet(payload):
32
+ """Classify a UDP payload into RakNet packet types."""
33
+ if not payload:
34
+ return 'empty', {}
35
+
36
+ first = payload[0]
37
+
38
+ # Offline messages
39
+ if first == 0x05 and len(payload) > 17 and payload[1:17] == OFFLINE_MAGIC:
40
+ return 'open_connection_request', {'protocol_version': payload[17] if len(payload) > 17 else None}
41
+ if first == 0x06 and len(payload) > 17 and payload[1:17] == OFFLINE_MAGIC:
42
+ return 'open_connection_reply', {}
43
+ if first == 0x09 and len(payload) > 17:
44
+ return 'open_connection_request_2', {}
45
+
46
+ # Connected datagrams: check bitflags
47
+ # Bit 7 (MSB): is_ack
48
+ is_ack = (first >> 7) & 1
49
+ is_nak = (first >> 6) & 1 if not is_ack else 0
50
+
51
+ if is_ack:
52
+ return 'ack', {}
53
+ if is_nak:
54
+ return 'nak', {}
55
+
56
+ # Data datagram - try to parse encapsulated messages
57
+ return 'data', {'raw': payload}
58
+
59
+
60
+ def find_known_patterns(payload, context=''):
61
+ """Look for known byte patterns in the payload."""
62
+ findings = []
63
+
64
+ # Look for float values we sent
65
+ for i in range(len(payload) - 3):
66
+ chunk = payload[i:i+4]
67
+
68
+ # Check for known int values (little-endian)
69
+ try:
70
+ val_le = struct.unpack('<i', chunk)[0]
71
+ val_be = struct.unpack('>i', chunk)[0]
72
+ if val_le in (0, 1, 42, -1, 2147483647, 10, 20, 30, 40, 99):
73
+ findings.append((i, f'int32_le={val_le}'))
74
+ if val_be in (0, 1, 42, -1, 2147483647, 10, 20, 30, 40, 99) and val_be != val_le:
75
+ findings.append((i, f'int32_be={val_be}'))
76
+ except struct.error:
77
+ pass
78
+
79
+ # Check for known float values (little-endian)
80
+ try:
81
+ fval_le = struct.unpack('<f', chunk)[0]
82
+ fval_be = struct.unpack('>f', chunk)[0]
83
+ for known in [1.0, 3.14159, -1.0, 2.0, 3.0, -100.5, 0.001, 999.999, 3.14, 45.0, 90.0, 180.0]:
84
+ if abs(fval_le - known) < 0.001:
85
+ findings.append((i, f'float_le={fval_le:.6f}'))
86
+ if abs(fval_be - known) < 0.001 and abs(fval_be - fval_le) > 0.001:
87
+ findings.append((i, f'float_be={fval_be:.6f}'))
88
+ except struct.error:
89
+ pass
90
+
91
+ # Look for known strings
92
+ for s in [b'Hello', b'Hello World', b'hello', b'A']:
93
+ idx = payload.find(s)
94
+ if idx >= 0:
95
+ findings.append((idx, f'string="{s.decode()}"'))
96
+
97
+ # Look for known byte patterns
98
+ idx = payload.find(b'\xDE\xAD\xBE\xEF')
99
+ if idx >= 0:
100
+ findings.append((idx, 'bytes=DEADBEEF'))
101
+
102
+ return findings
103
+
104
+
105
+ def analyze_pcap(path):
106
+ """Full analysis of a pcap file."""
107
+ print(f'\n{"="*70}')
108
+ print(f'Analyzing: {path}')
109
+ print(f'{"="*70}\n')
110
+
111
+ server_port = 7777
112
+ packets = list(read_pcap(path))
113
+
114
+ for i, (ts, src_port, dst_port, payload) in enumerate(packets):
115
+ direction = 'C→S' if dst_port == server_port else 'S→C'
116
+ ptype, info = classify_packet(payload)
117
+
118
+ print(f'Packet {i+1}: {direction} ({src_port}→{dst_port}) {len(payload)}B [{ptype}]')
119
+
120
+ # Show hex dump
121
+ print(hexdump(payload))
122
+
123
+ # Show any found patterns
124
+ findings = find_known_patterns(payload)
125
+ if findings:
126
+ print(f' ** Found: {", ".join(f"@{off}: {desc}" for off, desc in findings)}')
127
+
128
+ print()
129
+
130
+
131
+ if __name__ == '__main__':
132
+ if len(sys.argv) < 2:
133
+ print(f'Usage: {sys.argv[0]} <pcap-file> [pcap-file ...]')
134
+ sys.exit(1)
135
+
136
+ for path in sys.argv[1:]:
137
+ analyze_pcap(path)