mirror of
https://port.numenaute.org/aleajactaest/clientbot.git
synced 2024-11-25 00:26:33 +00:00
725 lines
44 KiB
Python
Executable file
725 lines
44 KiB
Python
Executable file
#!/usr/bin/python3
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# script to read pcap file (communcation with khanat)
|
|
#
|
|
# Copyright (C) 2019 AleaJactaEst
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
# Ex.:
|
|
# sudo tcpdump -i docker0 -w capture2.pcap
|
|
# ./spykhanat.py --pcap-file=../capture2.pcap --msg-xml=../khanat-opennel-code/code/ryzom/common/data_common/msg.xml
|
|
|
|
# install pcapfile
|
|
# pip install pypcapfile
|
|
|
|
import argparse
|
|
import logging
|
|
from pcapfile import savefile
|
|
from pcapfile.protocols.linklayer import ethernet
|
|
from pcapfile.protocols.network import ip
|
|
from pcapfile.protocols.transport import udp
|
|
from pcapfile.protocols.transport import tcp
|
|
import binascii
|
|
import re
|
|
from tools import BitStream
|
|
from tools import Enum
|
|
from tools import CActionFactory
|
|
from tools import CBitSet
|
|
from tools import DecodeImpulse
|
|
from tools import World
|
|
from tools import CGenericMultiPartTemp
|
|
from tools import CImpulseDecoder
|
|
#from tools import CStringManager
|
|
from tools import CAction
|
|
from tools import Impulse
|
|
import xml.etree.ElementTree as ET
|
|
from datetime import datetime
|
|
|
|
LOGGER = 'SpyKhanat'
|
|
|
|
#file = open('capture2.pcap' , 'rb')
|
|
#pcapfile = savefile.load_savefile(file,verbose=True)
|
|
#pkt = pcapfile.packets[0]
|
|
#print(pkt.raw())
|
|
#print(pkt.timestamp)
|
|
#eth_frame = ethernet.Ethernet(pkt.raw())
|
|
#print(eth_frame)
|
|
#ip_packet = ip.IP(binascii.unhexlify(eth_frame.payload))
|
|
#print(ip_packet)
|
|
|
|
def write_yaml_str_or_array(outyaml, nbspace, value):
|
|
if type(value) == str:
|
|
outyaml.write(" " * nbspace + "- %s\n" % (value))
|
|
else:
|
|
for key in value:
|
|
outyaml.write(" " * nbspace + "- %s\n" % (key))
|
|
outyaml.write(" " * nbspace + str(type(value)) + "\n")
|
|
|
|
class SpyPcap():
|
|
def __init__(self, khanat_host_service, pcap_file, msg_xml, filter_host_service, show_raw_packet, show_message_decoded, outyaml=None):
|
|
if khanat_host_service:
|
|
self.khanat_host_service = re.compile(khanat_host_service)
|
|
else:
|
|
self.khanat_host_service = None
|
|
self.pcap_file = pcap_file
|
|
self.msg_xml = msg_xml
|
|
if filter_host_service:
|
|
self.filter_host_service = re.compile(filter_host_service)
|
|
else:
|
|
self.filter_host_service = None
|
|
self.show_raw_packet = show_raw_packet
|
|
self.show_message_decoded = show_message_decoded
|
|
self.actionFactory = CActionFactory.CActionFactory(None)
|
|
self.client_state = {}
|
|
self.decodeImpulse = DecodeImpulse.DecodeImpulse()
|
|
self.decodeImpulseSimple = Impulse.DecodeImpulseSimple()
|
|
|
|
fp = open(msg_xml , 'rt')
|
|
msgRawXml = fp.read()
|
|
self.msgXml = ET.fromstring(msgRawXml)
|
|
self.decodeImpulse.loadMsg(self.msgXml)
|
|
self.decodeImpulseSimple.loadMsg(self.msgXml)
|
|
self.outyaml = outyaml
|
|
|
|
def readRaw(self):
|
|
file = open( self.pcap_file , 'rb')
|
|
pcapfile = savefile.load_savefile(file,verbose=False)
|
|
for pkt in pcapfile.packets:
|
|
print("pkt:", dir(pkt))
|
|
print("pkt.header:", dir(pkt.header))
|
|
print("pkt.packet:", dir(pkt.packet))
|
|
logging.getLogger(LOGGER).debug("raw: %s" % pkt.raw())
|
|
logging.getLogger(LOGGER).debug("timestamp: %s (%s)" % (pkt.timestamp,datetime.fromtimestamp(pkt.timestamp).strftime("%Y/%m/%d %H:%M:%S")))
|
|
eth_frame = ethernet.Ethernet(pkt.raw())
|
|
logging.getLogger(LOGGER).debug("eth_frame: %s" % eth_frame)
|
|
if eth_frame.type == 2048:
|
|
ip_packet = ip.IP(binascii.unhexlify(eth_frame.payload))
|
|
logging.getLogger(LOGGER).debug("ip packet: %s" % ip_packet)
|
|
print("ip_packet:", dir(ip_packet))
|
|
logging.getLogger(LOGGER).debug("ip packet: %s ->%s" % (ip_packet.src.decode(), ip_packet.dst.decode()) )
|
|
if ip_packet.p == 17:
|
|
# UDP
|
|
logging.getLogger(LOGGER).debug("ip packet: protocol UDP (%s)" % ip_packet.p)
|
|
udp_packet = udp.UDP(binascii.unhexlify(ip_packet.payload))
|
|
print("udp_packet:", dir(udp_packet))
|
|
logging.getLogger(LOGGER).debug("UDP packet: %s" % udp_packet)
|
|
data = udp_packet.payload
|
|
print("data:", dir(data))
|
|
logging.getLogger(LOGGER).debug("data packet: %s" % data)
|
|
data = udp_packet.payload
|
|
logging.getLogger(LOGGER).info("data packet: timestamp:%s src:%s:%d dst:%s:%d data:%s" % (pkt.timestamp,
|
|
ip_packet.src.decode(), udp_packet.src_port,
|
|
ip_packet.dst.decode(), udp_packet.dst_port,
|
|
data.decode()))
|
|
|
|
elif ip_packet.p == 6:
|
|
# TCP
|
|
logging.getLogger(LOGGER).debug("ip packet: protocol TCP (%s)" % ip_packet.p)
|
|
tcp_packet = tcp.TCP(binascii.unhexlify(ip_packet.payload))
|
|
data = tcp_packet.payload
|
|
logging.getLogger(LOGGER).info("data packet: timestamp:%s src:%s:%d dst:%s:%d data:%s" % (pkt.timestamp,
|
|
ip_packet.src.decode(), tcp_packet.src_port,
|
|
ip_packet.dst.decode(), tcp_packet.dst_port,
|
|
data.decode()))
|
|
else:
|
|
logging.getLogger(LOGGER).debug("ip packet: protocol (%s)" % ip_packet.p)
|
|
|
|
def detect_khanat_server(self, packets):
|
|
hostdetected = {}
|
|
for pkt in packets:
|
|
eth_frame = ethernet.Ethernet(pkt.raw())
|
|
if eth_frame.type == 2048:
|
|
ip_packet = ip.IP(binascii.unhexlify(eth_frame.payload))
|
|
if ip_packet.p == 17: # UDP
|
|
udp_packet = udp.UDP(binascii.unhexlify(ip_packet.payload))
|
|
data = udp_packet.payload
|
|
host = "%s:%d" % (ip_packet.src.decode(), udp_packet.src_port)
|
|
hostdetected.setdefault(host, 0)
|
|
if len(data) == 20:
|
|
hostdetected[host] += 1
|
|
id = None
|
|
max = 0
|
|
for host in hostdetected:
|
|
if id:
|
|
if max < hostdetected[host]:
|
|
max = hostdetected[host]
|
|
id = host
|
|
else:
|
|
max = hostdetected[host]
|
|
id = host
|
|
logging.getLogger(LOGGER).info(" host :%s" % id)
|
|
return id
|
|
|
|
def initialize_client(self, clientid):
|
|
self.client_state.setdefault(clientid, {'CurrentReceivedNumber': 0,
|
|
'CurrentSendNumber': 0,
|
|
'LastReceivedAck': 0,
|
|
'AckBitMask': 0,
|
|
'RegisteredAction': {},
|
|
'world': World.World(),
|
|
'GenericMultiPartTempServer': CGenericMultiPartTemp.GenericMultiPartTemp(),
|
|
'GenericMultiPartTempClient': CGenericMultiPartTemp.GenericMultiPartTemp(),
|
|
'CImpulseDecoder': None,
|
|
'CActionFactory': None,
|
|
'LastAck0': [-1],
|
|
'LastAck1': [-1, -1],
|
|
'LastAck2': [-1, -1, -1, -1]})
|
|
self.client_state[clientid]['CImpulseDecoder'] = CImpulseDecoder.CImpulseDecoder(self.client_state[clientid]['world'])
|
|
self.client_state[clientid]['CActionFactory'] = CActionFactory.CActionFactory(self.client_state[clientid]['world'])
|
|
|
|
def add_registered_action(self, clientid, action):
|
|
self.client_state[clientid]['RegisteredAction'].setdefault(action.Code, [])
|
|
self.client_state[clientid]['RegisteredAction'][action.Code].append(action)
|
|
|
|
def decode_server(self, clientid, msgin, receivedPacket, receivedAck, nextSentPacket=0):
|
|
actions = []
|
|
for level in range(0, 3):
|
|
if level == 0:
|
|
lAck = self.client_state[clientid]['LastAck0']
|
|
channel = 0
|
|
elif level == 1:
|
|
lAck = self.client_state[clientid]['LastAck1']
|
|
channel = receivedPacket & 1
|
|
elif level == 2:
|
|
lAck = self.client_state[clientid]['LastAck2']
|
|
channel = receivedPacket & 3
|
|
keep = True
|
|
checkOnce = False
|
|
num = 0
|
|
#logging.getLogger(LOGGER).debug("level:%d channel:%d lAck:%s" %(level, channel, ':'.join([str(x) for x in lAck])))
|
|
# lastAck = lAck[channel]
|
|
while True:
|
|
next = msgin.readBool('next:' + str(level) + ':' + str(channel))
|
|
if not next:
|
|
break
|
|
if not checkOnce:
|
|
checkOnce = True
|
|
keep = receivedAck >= lAck[channel]
|
|
logging.getLogger(LOGGER).debug("keep:%s [%d => %d]" % (str(keep), receivedAck, lAck[channel]))
|
|
if keep:
|
|
lAck[channel] = nextSentPacket
|
|
logging.getLogger(LOGGER).debug("lAck:%s" % ':'.join([str(x) for x in lAck]))
|
|
pass
|
|
num += 1
|
|
#actionFactory = CAction.CActionFactory(None)
|
|
action = self.actionFactory.unpack(msgin)
|
|
logging.getLogger(LOGGER).debug("action:%s" % action)
|
|
#action = self._CActionFactory.unpack(msgin)
|
|
if keep:
|
|
logging.getLogger(LOGGER).debug("keep Code:%s" % str(action.Code))
|
|
actions.append(action)
|
|
elif action:
|
|
logging.getLogger(LOGGER).debug("append Code:%s" % str(action.Code))
|
|
self.add_registered_action(clientid, action)
|
|
return actions
|
|
|
|
def decode_client_send_normal_message(self, msgin, clientid, dst, sequenceid, name, Reference):
|
|
'''
|
|
khanat-opennel-code/code/ryzom/client/src/network_connection.cpp:2029 void CNetworkConnection::sendNormalMessage()
|
|
khanat-opennel-code/code/ryzom/common/src/game_share/action_block.cpp:36 void CActionBlock::serial(CBitMemStream &msg)
|
|
'''
|
|
#actions = self.client_state[clientid]['CImpulseDecoder'].decode(msgin, self.client_state[clientid]['CurrentReceivedNumber'], self.client_state[clientid]['LastReceivedAck'], self.client_state[clientid]['CurrentSendNumber'] )
|
|
actions = []
|
|
impulses = []
|
|
noerror = True
|
|
msgin.disable_LogErrorOnStreamOverflow()
|
|
while noerror:
|
|
try:
|
|
cycle = msgin.readUint32("Cycle")
|
|
num = msgin.readUint8("num")
|
|
logging.getLogger(LOGGER).debug("[Client -> Server] Cycle:%u num:%u" % (cycle, num))
|
|
for i in range(0, num):
|
|
actions.append(self.client_state[clientid]['CActionFactory'].unpack(msgin, Reference, name))
|
|
pass
|
|
except BitStream.OverflowError:
|
|
noerror = False
|
|
except Exception as e:
|
|
logging.getLogger(LOGGER).debug("[Client -> Server] end %s (not read:%u)" % (e.__class__, msgin.needRead()))
|
|
raise e
|
|
msgin.enable_LogErrorOnStreamOverflow()
|
|
|
|
ids = 0
|
|
for action in actions:
|
|
try:
|
|
impulse = action.decodeImpulseSimple(
|
|
self.decodeImpulseSimple,
|
|
self.client_state[clientid]['world'],
|
|
self.client_state[clientid]['GenericMultiPartTempClient'],
|
|
Reference = name
|
|
) #, Reference = Parent, Name = "%s_%d" % (target, 0))
|
|
if impulse:
|
|
impulses.append(impulse)
|
|
except Impulse.ImpulseNoElement:
|
|
pass
|
|
ids += 1
|
|
logging.getLogger(LOGGER).debug("[Client -> Server] detail actions [%d] => %s" % ( len(actions), ', '.join([str(x) for x in actions])))
|
|
# decodeVisualProperties( msgin );
|
|
return actions, impulses
|
|
|
|
def decode_client_message(self, msgin, clientid, dst, sequenceid, name, Parent, Source):
|
|
target = "%s_%s" % (Source, Parent[7:])
|
|
CurrentReceivedNumber = msgin.readSint32('CurrentReceivedNumber')
|
|
SystemMode = msgin.readBool('SystemMode')
|
|
logging.getLogger(LOGGER).debug("[Client -> Server] {CurrentReceivedNumber:%d, SystemMode:%d, src:%s, dst:%s}" % (CurrentReceivedNumber, SystemMode, clientid, dst))
|
|
self.initialize_client(clientid)
|
|
self.client_state[clientid]['CurrentReceivedNumber'] = CurrentReceivedNumber
|
|
actions = []
|
|
impulses = []
|
|
if not SystemMode:
|
|
'''
|
|
khanat-opennel-code/code/ryzom/client/src/network_connection.cpp:2029 void CNetworkConnection::sendNormalMessage()
|
|
'''
|
|
LastReceivedAck = msgin.readSint32('LastReceivedAck')
|
|
self.client_state[clientid]['LastReceivedAck'] = LastReceivedAck
|
|
AckBitMask = msgin.readUint32('AckBitMask')
|
|
self.client_state[clientid]['AckBitMask'] = AckBitMask
|
|
logging.getLogger(LOGGER).info("[Client -> Server] Normal Mode {CurrentReceivedNumber:%d, src:%s, dst:%s, LastReceivedAck:%d}" % (CurrentReceivedNumber, clientid, dst, LastReceivedAck))
|
|
# self.decode_server(msgin, _CurrentReceivedNumber, _CurrentReceivedNumber-1)
|
|
actions, _ = self.decode_client_send_normal_message(msgin, clientid, dst, sequenceid, "%s_%d" % (target, 0), Parent)
|
|
else:
|
|
message = msgin.readUint8('message')
|
|
try:
|
|
typeMessage = Enum.CLFECOMMON(message).name
|
|
except ValueError:
|
|
typeMessage = "Unknown"
|
|
if message == Enum.CLFECOMMON.SYSTEM_LOGIN_CODE:
|
|
UserAddr = msgin.readUint32('UserAddr')
|
|
UserKey = msgin.readUint32('UserKey')
|
|
UserId = msgin.readUint32('UserId')
|
|
LanguageCode = msgin.readString('LanguageCode')
|
|
logging.getLogger(LOGGER).info("[Client -> Server] System Mode:%s {CurrentReceivedNumber:%d, src:%s, dst:%s, UserAddr:%d, UserId:%d, UserAddr:%d LanguageCode:%s}" %
|
|
(typeMessage, CurrentReceivedNumber, clientid, dst, UserAddr, UserKey, UserId, LanguageCode))
|
|
action = CAction.CActionFake('SYSTEM_LOGIN_CODE', msgin, Reference=Parent, Name = "%s_%d" % (target, 0))
|
|
actions.append(action)
|
|
elif message == Enum.CLFECOMMON.SYSTEM_PROBE_CODE:
|
|
LatestProbe = msgin.readSint32('LatestProbe')
|
|
logging.getLogger(LOGGER).info("[Client -> Server] System Mode:%s probe:%d {CurrentReceivedNumber:%d, src:%s, dst:%s}" % (typeMessage,
|
|
LatestProbe, CurrentReceivedNumber, clientid, dst))
|
|
action = CAction.CActionFake('SYSTEM_PROBE_CODE', msgin, Reference=Parent, Name = "%s_%d" % (target, 0))
|
|
actions.append(action)
|
|
elif message == Enum.CLFECOMMON.SYSTEM_SYNC_CODE:
|
|
Synchronize = msgin.readUint32('Synchronize')
|
|
stime = msgin.readSint64('stime')
|
|
LatestSync = msgin.readUint32('LatestSync')
|
|
MsgData = msgin.readArrayUint8(16, 'MsgData')
|
|
md5Msg = bytes(MsgData)
|
|
DatabaseData = msgin.readArrayUint8(16, 'DatabaseData')
|
|
md5Database = bytes(DatabaseData)
|
|
logging.getLogger(LOGGER).info("[Client -> Server] System Mode:%s {CurrentReceivedNumber:%d, src:%s, dst:%s, Synchronize:%d, stime:%d, LatestSync:%d, MsgData:%s, DatabaseData:%s}" % (
|
|
typeMessage, CurrentReceivedNumber, clientid, dst, Synchronize, stime, LatestSync, binascii.hexlify(md5Msg).decode(), binascii.hexlify(md5Database).decode()))
|
|
action = CAction.CActionFake('SYSTEM_SYNC_CODE', msgin, Reference=Parent, Name = "%s_%d" % (target, 0))
|
|
actions.append(action)
|
|
elif message == Enum.CLFECOMMON.SYSTEM_ACK_SYNC_CODE:
|
|
LastReceivedNumber = msgin.readSint32('LastReceivedNumber')
|
|
LastAckInLongAck = msgin.readSint32('LastAckInLongAck')
|
|
LongAckBitField = CBitSet.CBitSet()
|
|
LongAckBitField.readSerial(msgin, 'LongAckBitField')
|
|
LatestSync = msgin.readSint32('LatestSync')
|
|
logging.getLogger(LOGGER).info("[Client -> Server] System Mode:%s {CurrentReceivedNumber:%d, src:%s, dst:%s, LastReceivedNumber:%d, LastAckInLongAck:%d, LatestSync:%d, LongAckBitField:%s}" % (
|
|
typeMessage, CurrentReceivedNumber, clientid, dst, LastReceivedNumber, LastAckInLongAck, LatestSync, LongAckBitField))
|
|
action = CAction.CActionFake('SYSTEM_ACK_SYNC_CODE', msgin, Reference=Parent, Name = "%s_%d" % (target, 0))
|
|
actions.append(action)
|
|
elif message == Enum.CLFECOMMON.SYSTEM_ACK_PROBE_CODE:
|
|
SizeLatestProbes = msgin.readSint32('SizeLatestProbes')
|
|
LatestProbes = []
|
|
for data in range(0, SizeLatestProbes):
|
|
LatestProbes.append(msgin.readSint32('LatestProbes'))
|
|
logging.getLogger(LOGGER).info("[Client -> Server] System Mode:%s (%d) {CurrentReceivedNumber:%d, src:%s, dst:%s, SizeLatestProbes:%d, LatestProbes:%s}" % (typeMessage, message, CurrentReceivedNumber, clientid, dst, SizeLatestProbes, str(LatestProbes)))
|
|
action = CAction.CActionFake('SYSTEM_ACK_PROBE_CODE', msgin, Reference=Parent, Name = "%s_%d" % (target, 0))
|
|
actions.append(action)
|
|
else:
|
|
logging.getLogger(LOGGER).info("[Client -> Server] System Mode:%s (%d) {CurrentReceivedNumber:%d, src:%s, dst:%s}" % (typeMessage, message, CurrentReceivedNumber, clientid, dst))
|
|
logging.getLogger(LOGGER).debug("[Client -> Server] msg:%s" % msgin.showAllData())
|
|
return actions, impulses
|
|
|
|
def decode_khanat_message(self, msgin, src, dst, sequenceid, clientname, Parent, Source):
|
|
target = "%s_%s" % (Source, Parent[7:])
|
|
actions = []
|
|
impulses = []
|
|
CurrentSendNumber = msgin.readSint32('CurrentSendNumber')
|
|
logging.getLogger(LOGGER).debug("[Server -> Client] {CurrentSendNumber:%d, src:%s, dst:%s}" % (CurrentSendNumber, src, dst))
|
|
SystemMode = msgin.readBool('SystemMode')
|
|
self.initialize_client(dst)
|
|
self.client_state[dst]['CurrentSendNumber'] = CurrentSendNumber
|
|
id = 0
|
|
if not SystemMode:
|
|
_LastReceivedAck = msgin.readSint32('LastReceivedAck');
|
|
logging.getLogger(LOGGER).debug("[Server -> Client] Normal Mode {CurrentSendNumber:%d, src:%s, dst:%s, _LastReceivedAck:%d}" % (CurrentSendNumber, src, dst, _LastReceivedAck))
|
|
actions = self.decode_server(dst, msgin, CurrentSendNumber, CurrentSendNumber-1)
|
|
if actions:
|
|
logging.getLogger(LOGGER).debug('list actions: [' + str(len(actions)) + '] ' +','.join( [ str(x) for x in actions] ) )
|
|
else:
|
|
logging.getLogger(LOGGER).debug('list actions: None')
|
|
|
|
# Decode the actions received in the impulsions
|
|
logging.getLogger(LOGGER).debug('=' * 80)
|
|
actionsbis = []
|
|
for action in actions:
|
|
referenceBis = "%s_%d" % (target, id)
|
|
action.add_reference(Parent)
|
|
action.set_name(referenceBis)
|
|
logging.getLogger(LOGGER).debug('-' * 80)
|
|
logging.getLogger(LOGGER).debug('Analyse actions:%s', action)
|
|
if action.Code == Enum.TActionCode.ACTION_DISCONNECTION_CODE:
|
|
#action.add_reference(Parent)
|
|
logging.getLogger(LOGGER).info("Action : ACTION_DISCONNECTION_CODE")
|
|
actionsbis.append(CAction.CActionFake('ACTION_DISCONNECTION_CODE', self.client_state[dst]['GenericMultiPartTempServer'].data[id].read())) # , Reference = Parent, Name = "%s_%d" % (target, 0)
|
|
elif action.Code == Enum.TActionCode.ACTION_GENERIC_CODE:
|
|
#action.add_reference(Parent)
|
|
#action.genericAction(self.decodeImpulse, self.client_state[dst]['world'], self.client_state[dst]['GenericMultiPartTempServer']) #, Reference = Parent, Name = "%s_%d" % (target, 0))
|
|
try:
|
|
impulse = action.decodeImpulseSimple(
|
|
self.decodeImpulseSimple,
|
|
self.client_state[dst]['world'],
|
|
self.client_state[dst]['GenericMultiPartTempServer'],
|
|
Reference = [Parent, ],
|
|
Name = "%s_%d" % (target, 0)
|
|
) #, Reference = Parent, Name = "%s_%d" % (target, 0))
|
|
logging.getLogger(LOGGER).info("impulse:%s" % str(impulse))
|
|
if impulse:
|
|
impulses.append(impulse)
|
|
except Impulse.ImpulseNoElement:
|
|
pass
|
|
logging.getLogger(LOGGER).info("[Server -> Client] ACTION_GENERIC_CODE : {CurrentSendNumber:%d, src:%s, dst:%s, _LastReceivedAck:%d, action:%s}" % (CurrentSendNumber, src, dst, _LastReceivedAck, action))
|
|
elif action.Code == Enum.TActionCode.ACTION_GENERIC_MULTI_PART_CODE:
|
|
#action.genericAction(self.decodeImpulse, self.client_state[dst]['world'], self.client_state[dst]['GenericMultiPartTempServer'], Reference = referenceBis) #, Reference = Parent, Name = "%s_%d" % (target, 0))
|
|
try:
|
|
impulse = action.decodeImpulseSimple(
|
|
self.decodeImpulseSimple,
|
|
self.client_state[dst]['world'],
|
|
self.client_state[dst]['GenericMultiPartTempServer'],
|
|
Reference = Parent,
|
|
Name = "%s_%d" % (target, 0)
|
|
) #, Reference = Parent, Name = "%s_%d" % (target, 0))
|
|
logging.getLogger(LOGGER).info("impulse:%s" % str(impulse))
|
|
if impulse:
|
|
impulses.append(impulse)
|
|
except Impulse.ImpulseNoElement:
|
|
pass
|
|
logging.getLogger(LOGGER).debug("[Server -> Client] ACTION_GENERIC_MULTI_PART_CODE : %s" % action)
|
|
for id in self.client_state[dst]['GenericMultiPartTempServer'].data:
|
|
if self.client_state[dst]['GenericMultiPartTempServer'].data[id].isAvailable():
|
|
logging.getLogger(LOGGER).info("[Server -> Client] ACTION_GENERIC_MULTI_PART_CODE {CurrentSendNumber:%d, src:%s, dst:%s, _LastReceivedAck:%d, id:%d, msg:%s}" % (
|
|
CurrentSendNumber, src, dst, _LastReceivedAck, id,
|
|
self.client_state[dst]['GenericMultiPartTempServer'].data[id].read().showAllData()))
|
|
temp = CAction.CActionFake('ACTION_GENERIC_MULTI_PART_CODE',
|
|
self.client_state[dst]['GenericMultiPartTempServer'].data[id].read(),
|
|
# {'coucou': ', '.join(self.client_state[dst]['GenericMultiPartTemp'].data[id].Reference)},
|
|
# Reference = self.client_state[dst]['GenericMultiPartTemp'].data[id].Reference,
|
|
Name = "inpulse_%s_%s" % (Parent[7:], 0)
|
|
)
|
|
temp.Reference = self.client_state[dst]['GenericMultiPartTempServer'].data[id].Reference
|
|
actionsbis.append(temp)
|
|
elif action.Code == Enum.TActionCode.ACTION_DUMMY_CODE:
|
|
#action.add_reference(Parent)
|
|
logging.getLogger(LOGGER).info("Action : ACTION_DUMMY_CODE")
|
|
actionsbis.append(CAction.CActionFake('ACTION_DUMMY_CODE', self.client_state[dst]['GenericMultiPartTempServer'].data[id].read(), Reference=Parent, Name = "%s_%d" % (target, 0)))
|
|
self.add_registered_action(dst, action)
|
|
id += 1
|
|
# # remove all old actions that are acked
|
|
# while self._Actions and self._Actions[0].FirstPacket != 0 and self._Actions[0].FirstPacket < self._LastReceivedAck:
|
|
# logging.getLogger(LOGGER).debug("remove old action [%d/%d] : %s" % (self._Actions[0].FirstPacket, self._LastReceivedAck, self._Actions[0]))
|
|
# self._Actions.pop(0)
|
|
for action in actionsbis:
|
|
actions.append(action)
|
|
|
|
logging.getLogger(LOGGER).info("impulses:%s" % str(impulses))
|
|
else:
|
|
message = msgin.readUint8('message')
|
|
#referenceBis = "%s_%d" % (Parent, id)
|
|
logging.getLogger(LOGGER).debug("[Server -> Client] System Mode {CurrentSendNumber:%d, src:%s, dst:%s, message:%d" % (CurrentSendNumber, src, dst, message))
|
|
if message == Enum.CLFECOMMON.SYSTEM_SYNC_CODE:
|
|
logging.getLogger(LOGGER).debug("[Server -> Client] Synchronize")
|
|
Synchronize = msgin.readUint32('Synchronize')
|
|
stime = msgin.readSint64('stime')
|
|
LatestSync = msgin.readUint32('LatestSync')
|
|
MsgData = msgin.readArrayUint8(16, 'MsgData')
|
|
DatabaseData = msgin.readArrayUint8(16, 'DatabaseData')
|
|
logging.getLogger(LOGGER).info("[Server -> Client] System Mode / Synchronize {CurrentSendNumber:%d, src:%s, dst:%s, message:%d, Synchronize:%d, stime:%d, LatestSync:%d, MsgData:%s, DatabaseData:%s}" % (CurrentSendNumber, src, dst, message, Synchronize, stime, LatestSync, str(MsgData), str(DatabaseData)))
|
|
action = CAction.CActionFake('SYSTEM_SYNC_CODE', msgin, Reference=Parent, Name = "%s_%d" % (target, 0))
|
|
actions.append(action)
|
|
elif message == Enum.CLFECOMMON.SYSTEM_STALLED_CODE:
|
|
logging.getLogger(LOGGER).debug("[Server -> Client] Stalled")
|
|
logging.getLogger(LOGGER).info("[Server -> Client] System Mode / Stalled {CurrentSendNumber:%d, src:%s, dst:%s, message:%d}" % (CurrentSendNumber, src, dst, message))
|
|
action = CAction.CActionFake('SYSTEM_STALLED_CODE', msgin, Reference=Parent, Name = "%s_%d" % (target, 0))
|
|
actions.append(action)
|
|
elif message == Enum.CLFECOMMON.SYSTEM_PROBE_CODE:
|
|
logging.getLogger(LOGGER).debug("[Server -> Client] Probe")
|
|
LatestProbe = msgin.readSint32('LatestProbe')
|
|
logging.getLogger(LOGGER).info("[Server -> Client] System Mode / Probe {CurrentSendNumber:%d, src:%s, dst:%s, message:%d, LatestProbe:%d}" % (CurrentSendNumber, src, dst, message, LatestProbe))
|
|
action = CAction.CActionFake('SYSTEM_PROBE_CODE', msgin, Reference=Parent, Name = "%s_%d" % (target, 0))
|
|
actions.append(action)
|
|
elif message == Enum.CLFECOMMON.SYSTEM_SERVER_DOWN_CODE:
|
|
logging.getLogger(LOGGER).info("[Server -> Client] System Mode / BACK-END DOWN {CurrentSendNumber:%d, src:%s, dst:%s, message:%d}" % (CurrentSendNumber, src, dst, message))
|
|
action = CAction.CActionFake('SYSTEM_SERVER_DOWN_CODE', msgin, Reference=Parent, Name = "%s_%d" % (target, 0))
|
|
actions.append(action)
|
|
else:
|
|
logging.getLogger(LOGGER).warning("CNET: received system %d in state Login" % message)
|
|
#cActionFactory = CAction.CActionFactory(None)
|
|
#cActionFactory.unpack(msgin)
|
|
logging.getLogger(LOGGER).debug("[Server -> Client] msg:%s" % msgin.showAllData())
|
|
#logging.getLogger(LOGGER).info("impulses:%s" % str(impulses))
|
|
return actions, impulses
|
|
|
|
def read(self):
|
|
file = open( self.pcap_file , 'rb')
|
|
pcapfile = savefile.load_savefile(file,verbose=False)
|
|
khanat_host = self.detect_khanat_server(pcapfile.packets)
|
|
clientid = 1
|
|
serverid = 1
|
|
list_host = {}
|
|
sequenceid = 1
|
|
sequencenum = 1
|
|
if self.outyaml:
|
|
self.outyaml.write("# Generated : %s\n\n" % (datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
|
|
|
|
for pkt in pcapfile.packets:
|
|
eth_frame = ethernet.Ethernet(pkt.raw())
|
|
if eth_frame.type == 2048:
|
|
ip_packet = ip.IP(binascii.unhexlify(eth_frame.payload))
|
|
if ip_packet.p == 17:
|
|
# UDP
|
|
udp_packet = udp.UDP(binascii.unhexlify(ip_packet.payload))
|
|
data = udp_packet.payload
|
|
if udp_packet.src_port == 53 or udp_packet.dst_port == 53:
|
|
continue
|
|
if udp_packet.src_port == 5353 and ip_packet.src.decode() == "224.0.0.251":
|
|
continue
|
|
if udp_packet.dst_port == 5353 and ip_packet.dst.decode() == "224.0.0.251":
|
|
continue
|
|
if not self.filter_host_service or self.filter_host_service.match("%s:%d" % (ip_packet.src.decode(), udp_packet.src_port)) or self.filter_host_service.match("%s:%d" % (ip_packet.dst.decode(), udp_packet.dst_port)):
|
|
Reference = "packet_%d" % sequenceid
|
|
logging.getLogger(LOGGER).debug("-" * 80)
|
|
actions_clients = []
|
|
actions_servers = []
|
|
inpulses_servers = []
|
|
inpulses_clients = []
|
|
if self.show_raw_packet:
|
|
logging.getLogger(LOGGER).debug("[raw packet] timestamp:%s [%s] src:%s:%d dst:%s:%d data:%s" % (pkt.timestamp,
|
|
datetime.fromtimestamp(pkt.timestamp).strftime("%Y/%m/%d %H:%M:%S"),
|
|
ip_packet.src.decode(), udp_packet.src_port,
|
|
ip_packet.dst.decode(), udp_packet.dst_port,
|
|
data.decode()))
|
|
msgin = BitStream.BitStream()
|
|
msgin.fromBytes(binascii.unhexlify(data))
|
|
src = "%s:%d" % (ip_packet.src.decode(), udp_packet.src_port)
|
|
dst = "%s:%d" % (ip_packet.dst.decode(), udp_packet.dst_port)
|
|
|
|
if (self.khanat_host_service and self.khanat_host_service.match(src)) or ( not self.khanat_host_service and khanat_host == src):
|
|
if src not in list_host:
|
|
name = "Server%d" % serverid
|
|
list_host.setdefault(src, name)
|
|
serverid += 1
|
|
else:
|
|
if src not in list_host:
|
|
name = "Client%d" % clientid
|
|
list_host.setdefault(src, name)
|
|
clientid += 1
|
|
|
|
if (self.khanat_host_service and self.khanat_host_service.match(dst)) or ( not self.khanat_host_service and khanat_host == dst):
|
|
if dst not in list_host:
|
|
name = "Server%d" % serverid
|
|
list_host.setdefault(dst, name)
|
|
serverid += 1
|
|
else:
|
|
if dst not in list_host:
|
|
name = "Client%d" % clientid
|
|
list_host.setdefault(dst, name)
|
|
clientid += 1
|
|
|
|
if (self.khanat_host_service and self.khanat_host_service.match(src)) or ( not self.khanat_host_service and khanat_host == src):
|
|
_provenance = 'Server -> Client'
|
|
logging.getLogger(LOGGER).debug("[%s] (message received) [%s] %s" % (_provenance, datetime.fromtimestamp(pkt.timestamp).strftime("%Y/%m/%d %H:%M:%S"), msgin.showAllData()))
|
|
actions_servers, inpulses_servers = self.decode_khanat_message(msgin, src, dst, sequenceid, list_host[dst], Reference, list_host[src])
|
|
else:
|
|
_provenance = 'Client -> Server'
|
|
logging.getLogger(LOGGER).debug("[%s] (message received) [%s] %s" % (_provenance, datetime.fromtimestamp(pkt.timestamp).strftime("%Y/%m/%d %H:%M:%S"), msgin.showAllData()))
|
|
actions_clients, inpulses_clients = self.decode_client_message(msgin, src, dst, sequenceid, list_host[dst], Reference, list_host[src])
|
|
if not msgin.checkOnlyZeroAtEnd(): # msgin.needRead() > 7:
|
|
moredata = "message partially decoded"
|
|
else:
|
|
moredata = 'message decoded'
|
|
if self.show_message_decoded:
|
|
logging.getLogger(LOGGER).debug("[%s] (%s) %s" % (_provenance, moredata, msgin.showAllData()))
|
|
if self.outyaml:
|
|
self.outyaml.write("\n%s:\n sequence: %d\n time: %s\n source: %s\n destination: %s\n function: %s\n adress_source: %s\n adress_destination: %s\n state: %s\n message:\n" % (
|
|
Reference,
|
|
sequencenum,
|
|
datetime.fromtimestamp(pkt.timestamp).strftime("%Y/%m/%d %H:%M:%S"),
|
|
list_host[src],
|
|
list_host[dst],
|
|
_provenance,
|
|
src,
|
|
dst,
|
|
moredata
|
|
))
|
|
for key in msgin.extractAllData():
|
|
self.outyaml.write(" - %s\n" % key)
|
|
if actions_servers:
|
|
self.outyaml.write("\nblock_%s_%d:\n" %(list_host[src], sequenceid))
|
|
id = 0
|
|
for action in actions_servers:
|
|
params = action.get_parameter()
|
|
self.outyaml.write(" %s:\n" % (action.get_name()))
|
|
# if "parent" in params:
|
|
# self.outyaml.write(" parent: %s\n" % (params['parent']))
|
|
# elif "Reference" in params:
|
|
# #self.outyaml.write(" parents: %s\n" % (', '.join(params['Reference'])))
|
|
# self.outyaml.write(" parents:\n")
|
|
# for key in params['Reference']:
|
|
# self.outyaml.write(" - %s\n" % (key))
|
|
# else:
|
|
# self.outyaml.write(" parent: udp_%s\n" % (sequenceid))
|
|
self.outyaml.write(" receivedby: client\n")
|
|
self.outyaml.write(" sequence: %s\n" % (sequencenum))
|
|
self.outyaml.write(" source: %s\n" % (list_host[src]))
|
|
self.outyaml.write(" destination: %s\n" % (list_host[dst]))
|
|
for key in params:
|
|
if key == 'Message':
|
|
self.outyaml.write(" %s:\n" % (key))
|
|
for key2 in params[key]:
|
|
self.outyaml.write(" - %s\n" % key2)
|
|
elif key == "Reference":
|
|
self.outyaml.write(" parents:\n")
|
|
for key in params['Reference']:
|
|
self.outyaml.write(" - %s\n" % (key))
|
|
#elif key != 'parent':
|
|
else:
|
|
self.outyaml.write(" %s: %s\n" % (key, params[key]))
|
|
#self.outyaml.write(" %s: %s\n" % (key, params[key]))
|
|
id += 1
|
|
|
|
if inpulses_servers:
|
|
self.outyaml.write("\ninpulseserver_%s_%d:\n" %(list_host[src], sequenceid))
|
|
id = 0
|
|
#print("-"*30)
|
|
#print(inpulses_servers)
|
|
for inpulse in inpulses_servers:
|
|
#print("-"*80)
|
|
#print(inpulse)
|
|
params = inpulse.get_parameter()
|
|
self.outyaml.write(" %s:\n" % (inpulse.get_name()))
|
|
for key in params:
|
|
if key == 'Message':
|
|
self.outyaml.write(" %s:\n" % (key))
|
|
for key2 in params[key]:
|
|
self.outyaml.write(" - %s\n" % key2)
|
|
elif key == "Reference":
|
|
self.outyaml.write(" parents:\n")
|
|
for key in params['Reference']:
|
|
self.outyaml.write(" - %s\n" % (key))
|
|
#elif key != 'parent':
|
|
else:
|
|
self.outyaml.write(" %s: %s\n" % (key, params[key]))
|
|
#self.outyaml.write(" %s: %s\n" % (key, params[key]))
|
|
id += 1
|
|
#print("-"*30)
|
|
|
|
if actions_clients:
|
|
self.outyaml.write("\nblock_%s_%d:\n" %(list_host[src], sequenceid))
|
|
id = 0
|
|
for action in actions_clients:
|
|
self.outyaml.write(" %s:\n" % (action.get_name()))
|
|
self.outyaml.write(" receivedby: server\n")
|
|
self.outyaml.write(" sequence: %s\n" % (sequencenum))
|
|
self.outyaml.write(" source: %s\n" % (list_host[src]))
|
|
self.outyaml.write(" destination: %s\n" % (list_host[dst]))
|
|
params = action.get_parameter()
|
|
for key in params:
|
|
if key == 'Message':
|
|
self.outyaml.write(" %s:\n" % (key))
|
|
for key2 in params[key]:
|
|
self.outyaml.write(" - %s\n" % key2)
|
|
elif key == "Reference":
|
|
self.outyaml.write(" parents:\n")
|
|
for key in params['Reference']:
|
|
self.outyaml.write(" - %s\n" % (key))
|
|
else:
|
|
self.outyaml.write(" %s: %s\n" % (key, params[key]))
|
|
#self.outyaml.write(" %s: %s\n" % (key, params[key]))
|
|
id += 1
|
|
|
|
# if inpulses_clients:
|
|
# self.outyaml.write("\ninpulseclient_%s_%d:\n" %(list_host[src], sequenceid))
|
|
# id = 0
|
|
# for inpulse in inpulses_clients:
|
|
# params = inpulse.get_parameter()
|
|
# self.outyaml.write(" %s:\n" % (inpulse.get_name()))
|
|
# id += 1
|
|
sequenceid += 1
|
|
sequencenum += 1
|
|
for client in self.client_state:
|
|
logging.getLogger(LOGGER).debug("%s [server tick:%d, client tick:%d]" %(client, self.client_state[client]['CurrentSendNumber'], self.client_state[client]['CurrentReceivedNumber']))
|
|
|
|
|
|
def main():
|
|
FORMAT = '%(asctime)-15s %(levelname)s %(filename)s:%(lineno)d %(message)s'
|
|
logging.basicConfig(format=FORMAT)
|
|
|
|
logger = []
|
|
logger.append(logging.getLogger(LOGGER))
|
|
# logger.append(logging.getLogger(CImpulseDecoder.LOGGER))
|
|
# #logger.append(logging.getLogger(DecodeImpuls.LOGGER))
|
|
# #logger.append(logging.getLogger(BitStream.LOGGER))
|
|
# logger.append(logging.getLogger(CStringManager.LOGGER))
|
|
# logger.append(logging.getLogger(CAction.LOGGER))
|
|
# logger.append(logging.getLogger(CActionFactory.LOGGER))
|
|
# logger.append(logging.getLogger(BitStream.LOGGER))
|
|
logger.append(logging.getLogger(Impulse.LOGGER))
|
|
CImpulseDecoder
|
|
# logger.append(logging.getLogger('CGenericMultiPartTemp'))
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--khanat-host-service", help="filter to detect khanat host:service (FES)")
|
|
parser.add_argument("--filter-host-service", help="filter host:service")
|
|
parser.add_argument("-d", "--debug", help="show debug message", action='store_true')
|
|
parser.add_argument("-v", "--verbose", help="show verbose message", action='store_true')
|
|
parser.add_argument("-p", "--pcap-file", help="file pcap to read", required=True)
|
|
parser.add_argument("-m", "--msg-xml", help="file msg.xml (from server khanat)", required=True)
|
|
parser.add_argument("-r", "--raw", help="show message raw", action='store_true')
|
|
parser.add_argument("--yaml", help="generate YAML file (decode all message)", type=argparse.FileType('w'), default=None)
|
|
parser.add_argument("--show-raw-packet", help="show packet (raw data)", action='store_true')
|
|
parser.add_argument("--show-message-decoded", help="show packet (raw data)", action='store_true')
|
|
|
|
args = parser.parse_args()
|
|
|
|
if args.debug:
|
|
level = logging.getLevelName('DEBUG')
|
|
elif args.verbose:
|
|
level = logging.getLevelName('INFO')
|
|
else:
|
|
level = logging.getLevelName('WARNING')
|
|
|
|
|
|
for logid in logger:
|
|
logid.setLevel(level)
|
|
logging.getLogger(LOGGER).info("Begin")
|
|
|
|
spy = SpyPcap(khanat_host_service=args.khanat_host_service,
|
|
pcap_file=args.pcap_file,
|
|
msg_xml=args.msg_xml,
|
|
filter_host_service=args.filter_host_service,
|
|
show_raw_packet=args.show_raw_packet,
|
|
show_message_decoded=args.show_message_decoded,
|
|
outyaml=args.yaml)
|
|
if args.raw:
|
|
spy.readRaw()
|
|
else:
|
|
spy.read()
|
|
logging.getLogger(LOGGER).info("End")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|