Intercept and read the network packets to find useful information. Also, inject some data.
Project description
SniParInject
It is the abbreviation of these three word: Sniffer, Parser and Inject. Intercept and read the network packets to find information about the game, but it could be useful for any network sniffer purpose. The injection is coming soon.
This project is work in progress. Reefer to the To-Do's list to check what is done and what is coming.
Run it
Locally
For more information go to the package's page in PyPi.
Install the package.
python3 -m pip install sniparinject
As a developer
The only two special Python's packages that you need are scapy
and
PyYAML
, the suggested version of Python
is >= 3.9
. Follow the commands to
set up and install this package.
git clone https://github.com/airvzxf/sniparinject.git
cd sniparinject || exit 123
./script/setup-dev.bash
After this setup, it could be easy to activate the vEnv and start to work.
# Inside of the GitHub cloned directory.
source ./venv/bin/activate
cd src/sniparinject || exit 123
# Make modification to the source code files.
# Run your changes.
# Finally deactivate the vEnv.
deactivate
Usage
The most basic setup is creating one Python file, and the settings file.
main.py
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
The most basic example of SniParInject.
"""
from sniparinject.network_sniffer import NetworkSniffer
if __name__ == '__main__':
SETTINGS_PATH = 'settings.yml'
NetworkSniffer(SETTINGS_PATH).start()
settings.yml
%YAML 1.1
---
Network:
interface: enp4s0
Server:
ip: 52.174.196.146
port: 5122
Game:
node:
actions:
0x7d:
title: Scenario change
0x85:
title: Player move to
display_message: Yes
structs:
- type: chars
size: 3
output:
type: hex
host:
actions:
0x78:
title: NPC Info
structs:
- name: ID
type: unsigned int
output:
type: hex
auto_zero_fill: Yes
- type: chars
size: 1
output:
type: hex
- name: HP
type: unsigned short
- name: Max HP
type: unsigned short
The settings, needs to set the network interface which could get by the
command ip addr
or ls -1 /sys/class/net
. The next two are the host IP and
its port, in our case we will use the game IP and port. You could use this
command lsof -i
to show all the process which has connection to the network,
check the column NAME
to review the connection
manaplus 5563 wolf ... MSIGT73EVR7RF:55238->52.174.196.146:5122 (ESTABLISHED)
. Also, another command is
sudo netstat -nap | grep -i NAME_OF_GAME_PROCESS
.
Execute the application:
# Execute with root permissions the main script.
sudo python3 main.py
Example
This example is for the game Mana Plus
.
Settings
This software does not need to modify any file in the source code.
Create a settings.yml
file which is able to add, modify or remove any parse
rules.
Examples
The required fields for the Server
information are ip
, port
or both.
Network:
interface: enp4s0
Server:
ip: 52.174.196.146
port: 5122
The default protocol is TCP
but you can assign the UDP
.
Network:
interface: enp4s0
Server:
protocol: UDP
port: 6666
This is the basic structure without any rule.
Game:
node:
host:
Example for node, which is the raw data send from your computer to the server.
Here will capture all the packets which start with the id 0x7d
equal to the
raw data \x00\x7d
. It will print in your console:
--> Scenario change |
every time your computer send to the server this
packet.
The action 0x85
equal to the raw data \x00\x85
. It will print in your
console: --> Player move to | a07f18 |
. Means the player communicate to the
server that it is moving to this position.
Game:
node:
actions:
0x7d:
title: Scenario change
0x85:
title: Player move to
structs:
- type: chars
size: 3
output:
type: hex
Same example as above, but we do not want to print the action Player move to
.
Add display_message: No
in the action which want to avoid the print.
Game:
node:
actions:
0x7d:
title: Scenario change
0x85:
title: Player move to
display_message: No
structs:
- type: chars
size: 3
output:
type: hex
Same example as above, but we do not want to print any node action, it means
the actions sent by you computer to the server. It will print only the host's
actions. The default value for the node/host or actions is always
display_message: Yes
, it does need to set explicitly.
Game:
node:
display_message: No
actions:
0x7d:
title: Scenario change
host:
actions:
0xad:
title: Scenario change
What are the structs? It is the way that it will parse the data. Basically,
split the raw data based on the Python Structs which are C Types. They are
well-known as an integer, char, long, float, etc. You will find information in
the official web page Python Structs. In the game the function which
contains this logic is
def _get_struct(self, struct_type: str, repeat_count: int = 1)
in ./core/game.py
.
# 'ID': ('Python struct symbol', Size in bytes)
structs = {
'char': ('c', 1),
'signed char': ('b', 1),
'unsigned char': ('B', 1),
'bool': ('?', 1),
'short': ('h', 2),
'unsigned short': ('H', 2),
'int': ('i', 4),
'unsigned int': ('I', 4),
'long': ('q', 8),
'unsigned long': ('Q', 8),
'half precision': ('e', 2),
'float': ('f', 4),
'double': ('d', 6),
'chars': ('s', 0),
}
The option size
returns the number of repeats of this struct. One example is
if the setting's action has type: int
and size: 8
, it will returns
4i
and it is exactly the same to write iiii
. The example below will
return 5s
.
structs:
- type: chars
size: 5
In this example it will take the raw data and split in two. The first is an
unsigned int
with 4 bytes
of size and the char
with 1 byte
of size. The
raw data could be \x00\x80 \x0f\x00\x00\x00 \xf8
, I split intentional with
spaces to be more clear. The rule in the example take the first
unsigned short
value as an action ID (\x00\x80
equal to 0x80
), then
extract as a unsigned int
the monster ID, and the last byte as an unknown.
The output will be ---> NPC Monster Check | ID 0x0000000f | 0xf8 |
. Note in
the monster ID that the raw data, and the output is different:
\x0f\x00\x00\x00
vs 0x0000000f
. It is because the network data is coming
in big-endian
and the human / machine-readable for numbers is
in little-endian
.
Game:
node:
actions:
0x80:
title: NPC Monster Check
structs:
- name: ID
type: unsigned int
output:
type: hex
auto_zero_fill: Yes
- type: chars
size: 1
output:
type: hex
In every struct there is an option to add the name which will display before
the value. In the follow example the first struct does not have the name, and
the second has the name id. The output will be
---> NPC Monster Check | 56 | ID 78 |
.
Game:
node:
actions:
0x80:
title: NPC Monster Check
structs:
- type: unsigned int
- name: ID
type: unsigned int
What happen if I do not use the output
as an hex
? It will show the raw
data: ---> NPC Monster Check | ID b'\x00\x00\x00\x0f' |
Game:
node:
actions:
0x80:
title: NPC Monster Check
structs:
- name: ID
type: chars
size: 4
Take a little break for output and check the reference
option. In the middle
time that you are parsing the data, some findings will be discovered for
example the ID types of the shop options. It is possible to map this
information and display as a text in the output. Adding reference: *node_shop_options
to your settings, it will take the section referring with
the ID shop_options: &node_shop_options
. If the value is not in the map, it
will print as usual.
Given the raw data \x00\xc5 \x00
, the output will be
---> Shop store | ID Buy |
.
Given the raw data \x00\xc5 \x01
, the output will be
---> Shop store | ID Sell |
.
Given the raw data \x00\xc5 \x5a
, the output will be
---> Shop store | ID 0x5a |
.
Game:
node:
references:
shop_options: &node_shop_options
0x0: 'Buy'
0x1: 'Sell'
actions:
0xc5:
title: Shop store
structs:
- name: ID
type: unsigned char
reference: *node_shop_options
output:
type: hex
auto_zero_fill: Yes
Formatting the output
is possible with these options zero_fill
,
auto_zero_fill
, fill
and fill_left
.
zero_fill
- Add zeros to the left for example if you have for digits as an output0x12
and the property set to8
, it will display:00000x12
.auto_zero_fill
- Add zeros to the left taking the type of the structure. For example if you have aninteger
withbyte size of 4
, but the value is5
. The output without this option will be0x05
, then if your next value is123456789
the output will be0x75bcd15
. Keep aligned the output and using the propertyauto_zero_fill: Yes
it will display both numbers as0000000x05
and00x75bcd15
because theinteger
is4 bytes
equal to00 00 00 00
plus the pre-fix0x
, it means 10 digits.fill
- Same aszero_fill
but add blank spaces to the right of the value.fill_left
- Same as the previous option but add blank spaces to the left of the value.
Game:
node:
actions:
0x146:
title: NPC Dialog close
structs:
- type: unsigned int
output:
type: hex
zero_fill: 20
- type: unsigned int
output:
type: hex
auto_zero_fill: Yes
- type: unsigned int
output:
type: hex
fill: 9
- type: unsigned int
output:
type: hex
fill_left: 17
Endian
All the data coming from the network or server usually is big-endian, to read
this as the game interpretation needs to convert to little-endian. In this
example if you receive a raw data ff 00 ab 14 36 f0 1f
. The parse should be:
- ID Action package:
ff 00
=0x00ff
=255
. - ID Monster:
ab 14 36 f0
=0xf03614ab
. - Unknown:
1f
=0x1f
=31
.
Enjoy!
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for sniparinject-0.0.0.dev7-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 06cb2ac08249a8aa00d131b445090f9d8399e66a9d14b6746437642e6330b0f8 |
|
MD5 | 88e88e16faf8f5c00daa5661fc59ec5a |
|
BLAKE2b-256 | 558094bb1adc6616089c947b6f904b6e1d8dc28300e6b61e7383ee27fda95233 |