diff --git a/Utils.py b/Utils.py new file mode 100644 index 0000000..62a60bd --- /dev/null +++ b/Utils.py @@ -0,0 +1,22 @@ +import array + +def stringToByteArray(string): + return array.array('B', string.decode("hex")) + +def TwosCompliment(hash): + hash = array.array('B', hash.decode("hex")) + carry = True + for i in range((hash.__len__() - 1), 0, -1): + if(carry): + carry = hash[i] == 0xFF + hash[i] += 1 + return hash + +def trimStart(string, character): + for c in string: + if (c == character): + string = string[1:] + else: + break + return string + \ No newline at end of file diff --git a/networking/NetworkManager.py b/networking/NetworkManager.py index c89d6b2..44696dd 100644 --- a/networking/NetworkManager.py +++ b/networking/NetworkManager.py @@ -2,10 +2,12 @@ import socket import wx import PacketListenerManager import urllib2 +import urllib import traceback import threading import hashlib import string +import Utils from networking import PacketSenderManager from Crypto.Random import _UserFriendlyRNG from Crypto.Util import asn1 @@ -65,28 +67,34 @@ class ServerConnection(threading.Thread): packetFD = PacketListenerManager.handleFD(self.FileObject) #Import the server's public key - print "#Import the server's public key" self.pubkey = RSA.importKey(packetFD['Public Key']) #Generate a 16 byte (128 bit) shared secret - print "#Generate a 16 byte (128 bit) shared secret" self.sharedSecret = _UserFriendlyRNG.get_random_bytes(16) #Grab the server id - print "#Grab the server id" - serverid = packetFD['ServerID'] sha1 = hashlib.sha1() - sha1.update(serverid) - sha1.update(str(self.sharedSecret)) - sha1.update(str(self.pubkey)) - serverid = sha1.hexdigest() + sha1.update(packetFD['ServerID']) + sha1.update(self.sharedSecret) + sha1.update(packetFD['Public Key']) + #lovely java style hex digest by SirCmpwn + sha1 = sha1.hexdigest() + negative = (int(sha1[0], 16) & 0x80) == 0x80 + if(negative): + sha1 = Utils.TwosCompliment(sha1.digest()) + #else: + # sha1 = sha1.digest() + Utils.trimStart(str(sha1), '0') + if (negative): + sha1 = '-' + sha1 + serverid = sha1 #Authenticate the server from sessions.minecraft.net - print "#Authenticate the server from sessions.minecraft.net" if(serverid != '-'): try: #Open up the url with the appropriate get parameters url = "http://session.minecraft.net/game/joinserver.jsp?user=" + self.username + "&sessionId=" + self.sessionID + "&serverId=" + serverid + print url response = urllib2.urlopen(url).read() if(response != "OK"): @@ -99,20 +107,16 @@ class ServerConnection(threading.Thread): return False #Success \o/ We can now begin sending our stuff to the server - print "#Success \o/ We can now begin sending our stuff to the server" #Instantiate our main packet listener - print "#Instantiate our main packet listener" PacketListener(self, self.window, self.socket, self.FileObject).start() #Encrypt the verification token from earlier along with our shared secret with the server's rsa key - print "#Encrypt the verification token from earlier along with our shared secret" self.RSACipher = PKCS1_v1_5.new(self.pubkey) encryptedSanityToken = self.RSACipher.encrypt(str(packetFD['Token'])) encryptedSharedSecret = self.RSACipher.encrypt(str(self.sharedSecret)) #Send out a a packet FC to the server - print "#Send out a a packet FC to the server" PacketSenderManager.sendFC(self.socket, encryptedSharedSecret, encryptedSanityToken) #GUI handling @@ -151,7 +155,9 @@ class EncryptedFileObjectHandler(): self.cipher = cipher def read(self, length): - return self.cipher.decrypt(self.fileobject.read(length)) + rawData = self.fileobject.read(length) + unencryptedData = self.cipher.decrypt(rawData) + return unencryptedData class EncryptedSocketObjectHandler(): @@ -161,9 +167,6 @@ class EncryptedSocketObjectHandler(): def send(self, stuff): self.socket.send(self.cipher.encrypt(stuff)) - - def recv(self, length): - return self.cipher.decrypt(self.socket.recv(length)) def close(self): self.socket.close() @@ -180,14 +183,13 @@ class PacketListener(threading.Thread): def enableEncryption(self): #Create an AES cipher from the previously obtained public key - print "#Create an AES cipher from the previously generated shared secret" - self.cipher = AES.new(str(self.connection.sharedSecret), AES.MODE_CFB, IV=self.connection.sharedSecret, segment_size=8) + self.cipher = AES.new(self.connection.sharedSecret, AES.MODE_CFB, IV=self.connection.sharedSecret) + self.decipher = AES.new(self.connection.sharedSecret, AES.MODE_CFB, IV=self.connection.sharedSecret) self.rawsocket = self.socket self.socket = EncryptedSocketObjectHandler(self.rawsocket, self.cipher) self.rawFileObject = self.FileObject - self.FileObject = EncryptedFileObjectHandler(self.rawFileObject, self.cipher) + self.FileObject = EncryptedFileObjectHandler(self.rawFileObject, self.decipher) self.encryptedConnection = True - print "#Encryption enabled" def run(self): while True: @@ -202,11 +204,11 @@ class PacketListener(threading.Thread): print "Ping timeout" traceback.print_exc() break - print hex(ord(response)) if(response == "\x00"): PacketListenerManager.handle00(self.FileObject, self.socket) elif(response == "\x01"): - PacketListenerManager.handle01(self.FileObject) + packet01 = PacketListenerManager.handle01(self.FileObject) + print "Logged in \o/ Received an entity id of " + str(packet01['EntityID']) elif(response == "\x03"): message = PacketListenerManager.handle03(self.FileObject) if(self.connection.NoGUI): @@ -275,8 +277,6 @@ class PacketListener(threading.Thread): PacketListenerManager.handle2A(self.FileObject) elif(response == "\x2B"): PacketListenerManager.handle2B(self.FileObject) - elif(response == "\x32"): - PacketListenerManager.handle32(self.FileObject) elif(response == "\x33"): PacketListenerManager.handle33(self.FileObject) elif(response == "\x34"): @@ -285,10 +285,16 @@ class PacketListener(threading.Thread): PacketListenerManager.handle35(self.FileObject) elif(response == "\x36"): PacketListenerManager.handle36(self.FileObject) + elif(response == "\x37"): + PacketListenerManager.handle37(self.FileObject) + elif(response == "\x38"): + PacketListenerManager.handle38(self.FileObject) elif(response == "\x3C"): PacketListenerManager.handle3C(self.FileObject) elif(response == "\x3D"): PacketListenerManager.handle3D(self.FileObject) + elif(response == "\x3E"): + PacketListenerManager.handle3E(self.FileObject) elif(response == "\x46"): PacketListenerManager.handle46(self.FileObject) elif(response == "\x47"): @@ -300,7 +306,7 @@ class PacketListener(threading.Thread): elif(response == "\x67"): PacketListenerManager.handle67(self.FileObject) elif(response == "\x68"): - PacketListenerManager.handle68(self.FileObject) + print PacketListenerManager.handle68(self.FileObject) elif(response == "\x69"): PacketListenerManager.handle69(self.FileObject) elif(response == "\x6A"): @@ -316,9 +322,11 @@ class PacketListener(threading.Thread): elif(response == "\xC8"): PacketListenerManager.handleC8(self.FileObject) elif(response == "\xC9"): - PacketListenerManager.handleC9(self.FileObject) + print PacketListenerManager.handleC9(self.FileObject) elif(response == "\xCA"): PacketListenerManager.handleCA(self.FileObject) + elif(response == "\xCB"): + PacketListenerManager.handleCB(self.FileObject) elif(response == "\xFA"): PacketListenerManager.handleFA(self.FileObject) elif(response == "\xFC"): diff --git a/networking/PacketListenerManager.py b/networking/PacketListenerManager.py index 308e043..c9c6755 100644 --- a/networking/PacketListenerManager.py +++ b/networking/PacketListenerManager.py @@ -9,8 +9,8 @@ def handle01(FileObject): Eid = struct.unpack('!i', FileObject.read(4))[0] length = struct.unpack('!h', FileObject.read(2))[0] * 2 world = FileObject.read(length).decode('utf-16be') - mode = struct.unpack('!i', FileObject.read(4))[0] - dimension = struct.unpack('!i', FileObject.read(4))[0] + mode = struct.unpack('!b', FileObject.read(1))[0] + dimension = struct.unpack('!b', FileObject.read(1))[0] difficulty = struct.unpack('!b', FileObject.read(1))[0] FileObject.read(1) maxplayers = struct.unpack('!B', FileObject.read(1))[0] @@ -29,6 +29,12 @@ def handle02(FileObject): message = message.decode('utf-16be', 'strict') return message +def handle03(FileObject): + length = struct.unpack('!h', FileObject.read(2))[0] * 2 + message = FileObject.read(length) + message = message.decode('utf-16be','strict') + return message + def handle04(FileObject): time = struct.unpack('!q', FileObject.read(8))[0] return time @@ -140,7 +146,6 @@ def handle14(FileObject): 'curItem' : curItem, 'Metadata' : metadata } - print toReturn return toReturn def handle15(FileObject): @@ -211,6 +216,9 @@ def handle18(FileObject): Yaw = struct.unpack('!b', FileObject.read(1))[0] Pitch = struct.unpack('!b', FileObject.read(1))[0] HeadYaw = struct.unpack('!b', FileObject.read(1))[0] + VelocityX = struct.unpack('!h', FileObject.read(2))[0] + VelocityY = struct.unpack('!h', FileObject.read(2))[0] + VelocityZ = struct.unpack('!h', FileObject.read(2))[0] metadata = readEntityMetadata(FileObject) return {'EntityID' : EntityID, @@ -221,7 +229,10 @@ def handle18(FileObject): 'Yaw' : Yaw, 'Pitch' : Pitch, 'HeadYaw' : HeadYaw, - 'Metadata' : metadata + 'Metadata' : metadata, + 'VelocityX' : VelocityX, + 'VelocityY' : VelocityY, + 'VelocityZ' : VelocityZ } def handle19(FileObject): @@ -266,8 +277,11 @@ def handle1C(FileObject): } def handle1D(FileObject): - EntityID = struct.unpack('!i', FileObject.read(4))[0] - return EntityID + EntityArrayLength = struct.unpack('!b', FileObject.read(1))[0] + Entities = [] + for i in range(EntityArrayLength): + Entities.append(struct.unpack('!i', FileObject.read(4))[0]) + return Entities def handle1E(FileObject): EntityID = struct.unpack('!i', FileObject.read(4))[0] @@ -377,16 +391,6 @@ def handle2B(FileObject): 'Level' : Level, 'TotalExp' : TotalExp } - -def handle32(FileObject): - X = struct.unpack('!i', FileObject.read(4))[0] - raw = FileObject.read(4) - Z = struct.unpack('!i', raw)[0] - Mode = struct.unpack('?', FileObject.read(1))[0] - return {'x' : X, - 'z' : Z, - 'Mode' : Mode - } def handle33(FileObject): X = struct.unpack('!i', FileObject.read(4))[0] @@ -395,7 +399,6 @@ def handle33(FileObject): PrimaryBitMap = struct.unpack('!H', FileObject.read(2))[0] AddBitMap = struct.unpack('!H', FileObject.read(2))[0] CompressedSize = struct.unpack('!i', FileObject.read(4))[0] - FileObject.read(4) #unused int FileObject.read(CompressedSize) #not going to be deflating and using this data until I know how to :3 return {'x' : X, 'z' : Z @@ -416,7 +419,7 @@ def handle35(FileObject): X = struct.unpack('!i', FileObject.read(4))[0] Y = struct.unpack('!b', FileObject.read(1))[0] Z = struct.unpack('!i', FileObject.read(4))[0] - BlockType = struct.unpack('!b', FileObject.read(1))[0] + BlockType = struct.unpack('!h', FileObject.read(2))[0] BlockMetaData = struct.unpack('!b', FileObject.read(1))[0] return {'x' : X, 'y' : Y, @@ -431,18 +434,57 @@ def handle36(FileObject): Z = struct.unpack('!i', FileObject.read(4))[0] Byte1 = struct.unpack('!b', FileObject.read(1))[0] Byte2 = struct.unpack('!b', FileObject.read(1))[0] + BlockID = struct.unpack('!h', FileObject.read(2))[0] return {'x' : X, 'y' : Y, 'z' : Z, 'Byte1' : Byte1, - 'Byte2' : Byte2 + 'Byte2' : Byte2, + 'BlockID' : BlockID + } + +def handle37(FileObject): + #int - EntityID + EntityID = struct.unpack('!i', FileObject.read(4))[0] + + #int - X cord + x = struct.unpack('!i', FileObject.read(4))[0] + + #int - Y cord + y = struct.unpack('!i', FileObject.read(4))[0] + + #int - Z cord + z = struct.unpack('!i', FileObject.read(4))[0] + + #byte - Stage + DestroyedStage = struct.unpack('!b', FileObject.read(1))[0] + return {'EntityID' : EntityID, + 'x' : x, + 'y' : y, + 'z' : z, + 'DestroyedStage' : DestroyedStage + } + +def handle38(FileObject): + #short - number of chunks + ChunkCount = struct.unpack('!h', FileObject.read(2))[0] + + #int - chunk data length + ChunkDataLength = struct.unpack('!i', FileObject.read(4))[0] + FileObject.read(ChunkDataLength) #just gonna ignore this for now + + #metadata - ignoring this + for i in range(ChunkCount): + FileObject.read(12) + + return {'ChunkCount' : ChunkCount } def handle3C(FileObject): X = struct.unpack('!d', FileObject.read(8))[0] Y = struct.unpack('!d', FileObject.read(8))[0] Z = struct.unpack('!d', FileObject.read(8))[0] - FileObject.read(4) #Unknown what this float does + Radius = struct.unpack('!f', FileObject.read(4))[0] RecordCount = struct.unpack('!i', FileObject.read(4))[0] AffectedBlocks = [] for i in range((RecordCount * 3)): @@ -450,9 +492,15 @@ def handle3C(FileObject): y = struct.unpack('!b', FileObject.read(1))[0] z = struct.unpack('!b', FileObject.read(1))[0] AffectedBlocks.append({'x' : x, 'y' : y, 'z' : z}) - return {'X' : X, - 'Y' : Y, - 'Z' : Z, + #---Unknown what these floats do + FileObject.read(4) + FileObject.read(4) + FileObject.read(4) + #--- + return {'x' : X, + 'y' : Y, + 'z' : Z, + 'Raidus' : Radius, 'AffectedBlocks' : AffectedBlocks } @@ -469,6 +517,22 @@ def handle3D(FileObject): 'Data' : Data } +def handle3E(FileObject): + length = struct.unpack('!h', FileObject.read(2))[0] * 2 + Sound = FileObject.read(length).decode('utf-16be') + x = struct.unpack('!i', FileObject.read(4))[0] + y = struct.unpack('!i', FileObject.read(4))[0] + z = struct.unpack('!i', FileObject.read(4))[0] + Volume = struct.unpack('!f', FileObject.read(4))[0] + Pitch = struct.unpack('!b', FileObject.read(1))[0] + return {'Sound' : Sound, + 'x' : x, + 'y' : y, + 'z' : z, + 'Volume' : Volume, + 'Pitch' : Pitch + } + def handle46(FileObject): Reason = struct.unpack('!b', FileObject.read(1))[0] GameMode = struct.unpack('!b', FileObject.read(1))[0] @@ -519,7 +583,6 @@ def handle68(FileObject): Slots = [] for i in range(Count): SlotData = decodeSlotData(FileObject) - SlotData["index"] = i Slots.append(SlotData) return {'WindowID' : WindowID, 'Count' : Count, @@ -555,7 +618,7 @@ def handle82(FileObject): X = struct.unpack('!i', FileObject.read(4))[0] Y = struct.unpack('!h', FileObject.read(2))[0] Z = struct.unpack('!i', FileObject.read(4))[0] - length = struct.unpack('!i', FileObject.read(2))[0] * 2 + length = struct.unpack('!h', FileObject.read(2))[0] * 2 Line1 = FileObject.read(length).decode("utf-16be") length = struct.unpack('!h', FileObject.read(2))[0] * 2 Line2 = FileObject.read(length).decode("utf-16be") @@ -587,16 +650,19 @@ def handle84(FileObject): Y = struct.unpack('!h', FileObject.read(2))[0] Z = struct.unpack('!i', FileObject.read(4))[0] Action = struct.unpack('!b', FileObject.read(1))[0] - Custom1 = struct.unpack('!i', FileObject.read(4))[0] - Custom2 = struct.unpack('!i', FileObject.read(4))[0] - Custom3 = struct.unpack('!i', FileObject.read(4))[0] + DataLength = struct.unpack('!h', FileObject.read(2))[0] + if (DataLength != -1): + NBTData = struct.unpack(str(DataLength) + "s", FileObject.read(DataLength))[0] + return {'x' : X, + 'y' : Y, + 'z' : Z, + 'Action' : Action, + 'NBTData' : NBTData + } return {'x' : X, 'y' : Y, 'z' : Z, - 'Action' : Action, - 'Custom1' : Custom1, - 'Custom2' : Custom2, - 'Custom3': Custom3 + 'Action' : Action } def handleC8(FileObject): @@ -617,16 +683,24 @@ def handleC9(FileObject): } def handleCA(FileObject): - Invulnerable = struct.unpack('?', FileObject.read(1))[0] - IsFlying = struct.unpack('?', FileObject.read(1))[0] - CanFly = struct.unpack('?', FileObject.read(1))[0] - InstantDestroy = struct.unpack('?', FileObject.read(1))[0] - return {'Invulnerable' : Invulnerable, - 'IsFlying' : IsFlying, - 'CanFly' : CanFly, - 'InstantDestroy' : InstantDestroy + #byte - flags + Flags = struct.unpack('!b', FileObject.read(1))[0] + + #byte - fly speed + FlySpeed = struct.unpack('!b', FileObject.read(1))[0] + + #byte - walk speed + WalkSpeed = struct.unpack('!b', FileObject.read(1))[0] + return {'Flags' : Flags, + 'Fly Speed' : FlySpeed, + 'Walk Speed' : WalkSpeed } +def handleCB(FileObject): + length = struct.unpack('!h', FileObject.read(2))[0] * 2 + text = FileObject.read(length).decode("utf-16be") + return {'Text' : text} + def handleFA(FileObject): length = struct.unpack('!h', FileObject.read(2))[0] * 2 Channel = FileObject.read(length).decode("utf-16be") @@ -716,22 +790,21 @@ def decodeSlotData(FileObject): BlockID = struct.unpack('!h', FileObject.read(2))[0] if(BlockID != -1): ItemCount = struct.unpack('!b', FileObject.read(1))[0] - MetaData = struct.unpack('!h', FileObject.read(2))[0] - if((256 <= BlockID and BlockID <= 259) or (267 <= BlockID and BlockID <= 279) or (283 <= BlockID and BlockID <= 286) or (290 <= BlockID and BlockID <= 294) or (298 <= BlockID and BlockID <= 317) or BlockID == 261 or BlockID == 359 or BlockID == 346): - IncomingDataLength = struct.unpack('!h', FileObject.read(2))[0] - if(IncomingDataLength != -1): - raw = FileObject.read(IncomingDataLength) - ByteArray = struct.unpack(str(IncomingDataLength) + "s", raw)[0] - Data = zlib.decompress(ByteArray, 15+32) - return {'BlockID' : BlockID, - 'ItemCount' : ItemCount, - 'MetaData' : MetaData, - 'Data' : Data - } + Damage = struct.unpack('!h', FileObject.read(2))[0] + MetadataLength = struct.unpack('!h', FileObject.read(2))[0] + if(MetadataLength != -1): + raw = FileObject.read(MetadataLength) + ByteArray = struct.unpack(str(MetadataLength) + "s", raw)[0] + Data = zlib.decompress(ByteArray, 15+32) + return {'BlockID' : BlockID, + 'ItemCount' : ItemCount, + 'Damage' : Damage, + 'Data' : Data + } return {'BlockID' : BlockID, 'ItemCount' : ItemCount, - 'MetaData' : MetaData + 'Damage' : Damage } return {'BlockID' : -1, - 'ItemCount' : -1 + 'ItemCount' : 0 }