#!/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 . # 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: logging.getLogger(LOGGER).info("[Server -> Client] ACTION_GENERIC_MULTI_PART_CODE {id:%d, available:%s NbBlock:%d/%d, isDecoded:%s, FirstRead:%s}" % ( id, self.client_state[dst]['GenericMultiPartTempServer'].data[id].isAvailable(), self.client_state[dst]['GenericMultiPartTempServer'].data[id].getNbCurrentBlock(), self.client_state[dst]['GenericMultiPartTempServer'].data[id].NbBlock, self.client_state[dst]['GenericMultiPartTempServer'].data[id].isDecoded(), self.client_state[dst]['GenericMultiPartTempServer'].data[id].FirstRead )) 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) else: logging.getLogger(LOGGER).info("[Server -> Client] ACTION_GENERIC_MULTI_PART_CODE {CurrentSendNumber:%d, src:%s, dst:%s, _LastReceivedAck:%d, id:%d}" % ( CurrentSendNumber, src, dst, _LastReceivedAck, id)) 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()