Esempio n. 1
0
	def parse(self, id):
		if id == 0x00:
			if self.state == 0:
				data = self.read("varint:version|string:address|ushort:port|varint:state")
				self.version = data["version"]
				self.packet.version = self.version
				if not self.wrapper.server.protocolVersion == self.version and data["state"] == 2:
					self.disconnect("You're not running the same Minecraft version as the server!")
					return
				if not self.wrapper.server.state == 2:
					self.disconnect("Server has not finished booting. Please try connecting again in a few seconds")
					return
				if data["state"] in (1, 2):
					self.state = data["state"]
				else:
					self.disconnect("Invalid state '%d'" % data["state"])
				return False
			elif self.state == 1:
				sample = []
				for i in self.wrapper.server.players:
					player = self.wrapper.server.players[i]
					sample.append({"name": player.username, "id": str(player.uuid)})
					if len(sample) > 5: break
				MOTD = {"description": json.loads(self.wrapper.server.processColorCodes(self.wrapper.server.motd.replace("\\", ""))), 
					"players": {"max": self.wrapper.server.maxPlayers, "online": len(self.wrapper.server.players), "sample": sample},
					"version": {"name": self.wrapper.server.version, "protocol": self.wrapper.server.protocolVersion}
				}
				if os.path.exists("server-icon.png"):
					f = open("server-icon.png", "r")
					serverIcon = "data:image/png;base64," + f.read().encode("base64")
					f.close()
					MOTD["favicon"] = serverIcon
				self.send(0x00, "string", (json.dumps(MOTD),))
				self.state = 5
				return False
			elif self.state == 2:
				data = self.read("string:username")
				self.username = data["username"]
				
				if self.config["Proxy"]["online-mode"]:
					self.state = 4
					self.verifyToken = encryption.generate_challenge_token()
					self.serverID = encryption.generate_server_id()
					if self.wrapper.server.protocolVersion < 6: # 1.7.x versions
						self.send(0x01, "string|bytearray_short|bytearray_short", (self.serverID, self.publicKey, self.verifyToken))
					else:
						self.send(0x01, "string|bytearray|bytearray", (self.serverID, self.publicKey, self.verifyToken))
				else:
					self.connect()
					self.uuid = uuid.uuid3(uuid.NAMESPACE_OID, "OfflinePlayer: %s" % self.username)
					self.serverUUID = self.UUIDFromName("OfflinePlayer:" + self.username)
					self.send(0x02, "string|string", (str(self.uuid), self.username))
					self.state = 3
					self.log.info("%s logged in (IP: %s)" % (self.username, self.addr[0]))
				return False
			elif self.state == 3:
				return False
		if id == 0x01:
			if self.state == 3: # chat packet
				if not self.isLocal == True: return True
				data = self.read("string:message")
				if data is None: return False
				try:
					if not self.wrapper.callEvent("player.rawMessage", {"player": self.getPlayerObject(), "message": data["message"]}): return False
					if data["message"][0] == "/":
						def args(i):
							try: return data["message"].split(" ")[i]
							except: return ""
						def argsAfter(i):
							try: return data["message"].split(" ")[i:]
							except: return ""
						return self.wrapper.callEvent("player.runCommand", {"player": self.getPlayerObject(), "command": args(0)[1:], "args": argsAfter(1)})
				except:
					print traceback.format_exc()
			elif self.state == 4: # Encryption Response Packet
				if self.wrapper.server.protocolVersion < 6:
					data = self.read("bytearray_short:shared_secret|bytearray_short:verify_token")
				else:
					data = self.read("bytearray:shared_secret|bytearray:verify_token")
				sharedSecret = encryption.decrypt_shared_secret(data["shared_secret"], self.privateKey)
				verifyToken = encryption.decrypt_shared_secret(data["verify_token"], self.privateKey)
				h = hashlib.sha1()
				h.update(self.serverID)
				h.update(sharedSecret)
				h.update(self.publicKey)
				serverId = self.packet.hexdigest(h)
				r = requests.get("https://sessionserver.mojang.com/session/minecraft/hasJoined?username=%s&serverId=%s" % (self.username, serverId))
#				print "SessionServer response: %s" % r.text
				
				self.packet.sendCipher = encryption.AES128CFB8(sharedSecret)
				self.packet.recvCipher = encryption.AES128CFB8(sharedSecret)
				
				if not verifyToken == self.verifyToken:
					self.disconnect("Verify tokens are not the same")
					return False
				try:
					data = r.json()
					self.uuid = data["id"]
					self.uuid = "%s-%s-%s-%s-%s" % (self.uuid[:8], self.uuid[8:12], self.uuid[12:16], self.uuid[16:20], self.uuid[20:])
					self.uuid = uuid.UUID(self.uuid)
					
					if not data["name"] == self.username:
						self.disconnect("Client's username did not match Mojang's record")
						return False 
					for property in data["properties"]:
						if property["name"] == "textures":
							self.skinBlob = property["value"]
							self.wrapper.proxy.skins[str(self.uuid)] = self.skinBlob
					self.properties = data["properties"]
				except:
					self.disconnect("Session Server Error")
					return False
				if self.proxy.lookupUUID(self.uuid):
					self.username = self.proxy.lookupUUID(self.uuid)["name"]
					
				self.serverUUID = self.UUIDFromName("OfflinePlayer:" + self.username)
				
				if self.version > 26:
					self.packet.setCompression(256)
					
				# Ban code should go here

				if not self.wrapper.callEvent("player.preLogin", {"player": self.username, "online_uuid": self.uuid, "offline_uuid": self.serverUUID, "ip": self.addr[0]}):
					self.disconnect("Login denied.")
					return False

				self.send(0x02, "string|string", (str(self.uuid), self.username))
				self.state = 3
				self.connect()
				
				self.log.info("%s logged in (UUID: %s | IP: %s)" % (self.username, self.uuid, self.addr[0]))
				self.proxy.setUUID(self.uuid, self.username)
				
				return False
			elif self.state == 5: # ping packet during status request
				keepAlive = self.read("long:keepAlive")["keepAlive"]
				self.send(0x01, "long", (keepAlive,))
		if id == 0x04:
			data = self.read("double:x|double:y|double:z|bool:on_ground")
			self.position = (data["x"], data["y"], data["z"])
		if id == 0x06:
			data = self.read("double:x|double:y|double:z|float:yaw|float:pitch|bool:on_ground")
			#objection = self.wrapper.callEvent("player.move", {"player": self.username, "xyz": (data["x"], data["y"], data["z"]), "on_ground": data["on_ground"]})
			self.position = (data["x"], data["y"], data["z"])
			if self.server.state is not 3: return False
		if id == 0x07: # Player Block Dig
			if not self.isLocal == True: return True
			if self.version < 6:
				data = self.read("byte:status|int:x|ubyte:y|int:z|byte:face")
				position = (data["x"], data["y"], data["z"])
			else:
				data = self.read("byte:status|position:position|byte:face")
				position = data["position"]
			if data is None: return False 
			if data["status"] == 2:
				if not self.wrapper.callEvent("player.dig", {"player": self.getPlayerObject(), "position": position, "action": "end_break", "face": data["face"]}): return False
			if data["status"] == 0:
				if not self.gamemode == 1:
					if not self.wrapper.callEvent("player.dig", {"player": self.getPlayerObject(), "position": position, "action": "begin_break", "face": data["face"]}): return False
				else:
					if not self.wrapper.callEvent("player.dig", {"player": self.getPlayerObject(), "position": position, "action": "end_break", "face": data["face"]}): return False
			if self.server.state is not 3: return False
		if id == 0x08: # Player Block Placement
			if not self.isLocal == True: return True
			if self.version < 6:
				data = self.read("int:x|ubyte:y|int:z|byte:face|slot:item")
				position = (data["x"], data["y"], data["z"])
			else:
				data = self.read("position:position|byte:face|slot:item")
				position = data["position"]
			position = None
			if self.version > 6: position = data["position"]
			if not position == None:
				face = data["face"]
				if not self.wrapper.callEvent("player.interact", {"player": self.getPlayerObject(), "position": position}): return False
				if face == 0: # Compensate for block placement coordinates
					position = (position[0], position[1] - 1, position[2])
				elif face == 1:
					position = (position[0], position[1] + 1, position[2])
				elif face == 2:
					position = (position[0], position[1], position[2] - 1)
				elif face == 3:
					position = (position[0], position[1], position[2] + 1)
				elif face == 4:
					position = (position[0] - 1, position[1], position[2])
				elif face == 5:
					position = (position[0] + 1, position[1], position[2])
				if not self.wrapper.callEvent("player.place", {"player": self.getPlayerObject(), "position": position, "item": data["item"]}): return False
			if self.server.state is not 3: return False
		if id == 0x09: # Held Item Change
			slot = self.read("short:short")["short"]
			if self.slot > -1 and self.slot < 9:
				self.slot = slot
			else:
				return False
		return True
Esempio n. 2
0
    def _handle_client(self, connection):
        with closing(connection[0]) as sock:
            clt_spec, srv_spec = protocol[0]
            print t.bold("\nConnected to %s:%s" % connection[1])

            print t.bold_cyan("\nExpecting Server Ping (0xfe) " +
                              "or Handshake (0x02) packet")
            packet = parse_packet(sock, clt_spec)
            if packet['msgtype'] == 0xfe:
                send_packet(sock, srv_spec, {'msgtype': 0xff,
                                             'reason': 'mc3p debugger'})
                return
            elif packet['msgtype'] != 0x02:
                raise UnexpectedPacketException(packet['msgtype'])
            if packet['proto_version'] < 38:
                print t.bold_red("Error:"),
                print "Unsupported protocol version"
                return
            username = packet['username']
            clt_spec, srv_spec = protocol[packet['proto_version']]

            print t.bold("\nGenerating RSA key pair")
            key = encryption.generate_key_pair()
            challenge = encryption.generate_challenge_token()
            server_id = encryption.generate_server_id()

            packet = {'msgtype': 0xfd,
                      'server_id': server_id,
                      'public_key': encryption.encode_public_key(key),
                      'challenge_token': challenge}
            send_packet(sock, srv_spec, packet)

            packet = parse_packet(sock, clt_spec, 0xfc)
            try:
                decrypted_token = encryption.decrypt_shared_secret(
                    packet['challenge_token'], key
                )
            except:
                decrypted_token = None
            if decrypted_token is None:
                try:
                    decrypted_token = key.decrypt(packet['challenge_token'])
                except:
                    pass
                if decrypted_token == challenge:
                    print t.bold_red("\nError:"),
                    print ("The challenge token was not padded " +
                           "correctly. See ftp://ftp.rsasecurity.com/pub/" +
                           "pkcs/pkcs-1/pkcs-1v2-1.pdf section 7.2.1 if " +
                           "your library does not support PKCS#1 padding.")
                else:
                    print t.bold_red("\nError:"),
                    print "The challenge token is not encrypted correctly.\n"
                    print PacketFormatter.bytes(decrypted_token,
                                                "Decrypted bytes: ", t.bold)
                return
            elif decrypted_token != challenge:
                print t.bold_red("\nError:"),
                print "Received challenge token does not",
                print "match the expected value.\n"
                print PacketFormatter.bytes(decrypted_token,
                                            "Received bytes: ", t.bold)
                print
                print PacketFormatter.bytes(challenge,
                                            "Expected bytes: ", t.bold)
                return
            secret = encryption.decrypt_shared_secret(packet['shared_secret'],
                                                      key)
            if secret is None:
                print t.bold_red("\nError:"),
                print ("The shared secret was not padded" +
                       "correctly. See ftp://ftp.rsasecurity.com/pub/" +
                       "pkcs/pkcs-1/pkcs-1v2-1.pdf section 7.2.1 if " +
                       "your library does not support PKCS#1 padding.")
                return
            print PacketFormatter.bytes(secret, "Shared secret: ", t.bold)
            if len(secret) != 16:
                print t.bold_red("\nError:"),
                print "The shared secret must be 16 bytes long",
                print "(received length is %s)" % len(secret)
                return

            print t.bold_cyan("\nAuthentication")
            print PacketFormatter.bytes(server_id, "Server ID:     ", t.bold)
            print PacketFormatter.bytes(secret, "Shared secret: ", t.bold)
            print PacketFormatter.bytes(encryption.encode_public_key(key),
                                        "Public key:    ", t.bold)
            print t.bold("Login hash:   "),
            print Authenticator.login_hash(server_id, secret, key)
            if Authenticator.check_player(username, server_id, secret, key):
                print t.bold_green("Success:"), "You are authenticated"
            else:
                print t.bold_yellow("Warning:"), "You are not authenticated"

            send_packet(sock, srv_spec, {'msgtype': 0xfc,
                                         'challenge_token': '',
                                         'shared_secret': ''})

            print t.bold("\nStarting AES encryption")
            clt_cipher = encryption.AES128CFB8(secret)
            srv_cipher = encryption.AES128CFB8(secret)
            backup_cipher = encryption.AES128CFB8(secret)

            parse_packet(sock, clt_spec, 0xcd, clt_cipher, backup_cipher)

            send_packet(sock, srv_spec, {'msgtype': 0x01,
                                         'eid': 1337,
                                         'level_type': 'flat',
                                         'server_mode': 0,
                                         'dimension': 0,
                                         'difficulty': 2,
                                         'unused': 0,
                                         'max_players': 20}, srv_cipher)

            if self.send_chunks:
                while True:
                    print
                    packet = parse_packet(sock, clt_spec, cipher=clt_cipher)
                    if packet['msgtype'] == 0x0d:
                        break

                x, y, z = 5, 9, 5

                send_packet(sock, srv_spec, {'msgtype': 0x06,
                                             'x': x,
                                             'y': y,
                                             'z': z}, srv_cipher)

                send_packet(sock, srv_spec, {'msgtype': 0xca,
                                             'abilities': 0b0100,
                                             'walking_speed': 25,
                                             'flying_speed': 12}, srv_cipher)

                send_packet(sock, srv_spec, {'msgtype': 0x04,
                                             'time': 0}, srv_cipher)

                send_packet(sock, srv_spec, multi_chunk_packet(), srv_cipher)

                send_packet(sock, srv_spec, {'msgtype': 0x0d,
                                             'x': x,
                                             'y': y,
                                             'stance': y + 1.5,
                                             'z': z,
                                             'yaw': 0,
                                             'pitch': 0,
                                             'on_ground': False}, srv_cipher)

                buffer = StringSocket()

                send_packet(buffer, srv_spec,
                            {'msgtype': 0x03,
                             'chat_msg': 'First message'},
                            srv_cipher)

                send_packet(buffer, srv_spec,
                            {'msgtype': 0x03,
                             'chat_msg': 'Second message'}, srv_cipher)

                sock.sendall(buffer.data)

            if self.stay_connected:
                while True:
                    packet = parse_packet(sock, clt_spec, cipher=clt_cipher,
                                          title=True)
                    if packet['msgtype'] == 0xff:
                        break
                    elif packet['msgtype'] == 0x00:
                        send_packet(buffer, srv_spec, {'msgtype': 0x00,
                                                       'id': 0}, srv_cipher)
                        break
            else:
                send_packet(sock, srv_spec,
                            {'msgtype': 0xff,
                             'reason': "Successfully logged in"}, srv_cipher)
Esempio n. 3
0
    def handle_read(self):
        """Read all available bytes, and process as many packets as possible.
        """
        t = time()
        if self.last_report + 5 < t and self.stream.tot_bytes > 0:
            self.last_report = t
            logger.debug(
                "%s: total/wasted bytes is %d/%d (%f wasted)" %
                (self.side, self.stream.tot_bytes, self.stream.wasted_bytes,
                 100 * float(self.stream.wasted_bytes) /
                 self.stream.tot_bytes))
        self.stream.append(self.recv(4092))

        if self.out_of_sync:
            data = self.stream.read(len(self.stream))
            self.stream.packet_finished()
            if self.other_side:
                self.other_side.send(data)
            return

        try:
            packet = parse_packet(self.stream, self.msg_spec, self.side)
            while packet != None:
                rebuild = False
                if packet['msgtype'] == 0x02 and self.side == 'client':
                    # Determine which protocol message definitions to use.
                    proto_version = packet['proto_version']
                    logger.info('Client requests protocol version %d' %
                                proto_version)
                    if not proto_version in messages.protocol:
                        logger.error("Unsupported protocol version %d" %
                                     proto_version)
                        self.handle_close()
                        return
                    self.username = packet['username']
                    self.msg_spec, self.other_side.msg_spec = messages.protocol[
                        proto_version]
                    self.cipher = encryption.encryption_for_version(
                        proto_version)
                    self.other_side.cipher = self.cipher
                elif packet['msgtype'] == 0xfd:
                    self.rsa_key = encryption.decode_public_key(
                        packet['public_key'])
                    self.encoded_rsa_key = packet['public_key']
                    packet['public_key'] = encryption.encode_public_key(
                        self.other_side.rsa_key)
                    if 'challenge_token' in packet:
                        self.challenge_token = packet['challenge_token']
                        self.other_side.challenge_token = self.challenge_token
                    self.other_side.server_id = packet['server_id']
                    if check_auth:
                        packet['server_id'] = encryption.generate_server_id()
                    else:
                        packet['server_id'] = "-"
                    self.server_id = packet['server_id']
                    rebuild = True
                elif packet['msgtype'] == 0xfc and self.side == 'client':
                    self.shared_secret = encryption.decrypt_shared_secret(
                        packet['shared_secret'], self.rsa_key)
                    if (len(self.shared_secret) > 16
                            and self.cipher == encryption.RC4):
                        logger.error("Unsupported protocol version")
                        self.handle_close()
                        return
                    packet['shared_secret'] = encryption.encrypt_shared_secret(
                        self.other_side.shared_secret, self.other_side.rsa_key)
                    if 'challenge_token' in packet:
                        challenge_token = encryption.decrypt_shared_secret(
                            packet['challenge_token'], self.rsa_key)
                        if challenge_token != self.challenge_token:
                            self.kick("Invalid client reply")
                            return
                        packet[
                            'challenge_token'] = encryption.encrypt_shared_secret(
                                self.other_side.challenge_token,
                                self.other_side.rsa_key)
                    if auth:
                        logger.info("Authenticating on server")
                        auth.join_server(self.server_id,
                                         self.other_side.shared_secret,
                                         self.other_side.rsa_key)
                    if check_auth:
                        logger.info("Checking authenticity")
                        if not Authenticator.check_player(
                                self.username, self.other_side.server_id,
                                self.shared_secret, self.rsa_key):
                            self.kick("Unable to verify username")
                            return
                    rebuild = True
                elif packet['msgtype'] == 0xfc and self.side == 'server':
                    logger.debug("Starting encryption")
                    self.start_cipher()
                forward = True
                if self.plugin_mgr:
                    forwarding = self.plugin_mgr.filter(packet, self.side)
                    if forwarding and packet.modified:
                        rebuild = True
                if rebuild:
                    packet['raw_bytes'] = self.msg_spec[
                        packet['msgtype']].emit(packet)
                if forwarding and self.other_side is not None:
                    self.other_side.send(packet['raw_bytes'])
                if packet['msgtype'] == 0xfc and self.side == 'server':
                    self.other_side.start_cipher()
                # Since we know we're at a message boundary, we can inject
                # any messages in the queue.
                msgbytes = self.plugin_mgr.next_injected_msg_from(self.side)
                while self.other_side and msgbytes is not None:
                    self.other_side.send(msgbytes)
                    msgbytes = self.plugin_mgr.next_injected_msg_from(
                        self.side)

                # Attempt to parse the next packet.
                packet = parse_packet(self.stream, self.msg_spec, self.side)
        except PartialPacketException:
            pass  # Not all data for the current packet is available.
        except Exception:
            logger.error(
                "MinecraftProxy for %s caught exception, out of sync" %
                self.side)
            logger.error(traceback.format_exc())
            logger.debug("Current stream buffer: %s" % repr(self.stream.buf))
            self.out_of_sync = True
            self.stream.reset()
Esempio n. 4
0
    def handle_read(self):
        """Read all available bytes, and process as many packets as possible.
        """
        t = time()
        if self.last_report + 5 < t and self.stream.tot_bytes > 0:
            self.last_report = t
            logger.debug("%s: total/wasted bytes is %d/%d (%f wasted)" % (
                 self.side, self.stream.tot_bytes, self.stream.wasted_bytes,
                 100 * float(self.stream.wasted_bytes) / self.stream.tot_bytes))
        self.stream.append(self.recv(4092))

        if self.out_of_sync:
            data = self.stream.read(len(self.stream))
            self.stream.packet_finished()
            if self.other_side:
                self.other_side.send(data)
            return

        try:
            packet = parse_packet(self.stream, self.msg_spec, self.side)
            while packet != None:
                rebuild = False
                if packet['msgtype'] == 0x02 and self.side == 'client':
                    # Determine which protocol message definitions to use.
                    proto_version = packet['proto_version']
                    logger.info('Client requests protocol version %d' % proto_version)
                    if not proto_version in messages.protocol:
                        logger.error("Unsupported protocol version %d" % proto_version)
                        self.handle_close()
                        return
                    self.username = packet['username']
                    self.msg_spec, self.other_side.msg_spec = messages.protocol[proto_version]
                    self.cipher = encryption.encryption_for_version(proto_version)
                    self.other_side.cipher = self.cipher
                elif packet['msgtype'] == 0xfd:
                    self.rsa_key = encryption.decode_public_key(
                        packet['public_key']
                    )
                    self.encoded_rsa_key = packet['public_key']
                    packet['public_key'] = encryption.encode_public_key(
                        self.other_side.rsa_key
                    )
                    if 'challenge_token' in packet:
                        self.challenge_token = packet['challenge_token']
                        self.other_side.challenge_token = self.challenge_token
                    self.other_side.server_id = packet['server_id']
                    if check_auth:
                        packet['server_id'] = encryption.generate_server_id()
                    else:
                        packet['server_id'] = "-"
                    self.server_id = packet['server_id']
                    rebuild = True
                elif packet['msgtype'] == 0xfc and self.side == 'client':
                    self.shared_secret = encryption.decrypt_shared_secret(
                        packet['shared_secret'],
                        self.rsa_key
                    )
                    if (len(self.shared_secret) > 16 and
                        self.cipher == encryption.RC4):
                        logger.error("Unsupported protocol version")
                        self.handle_close()
                        return
                    packet['shared_secret'] = encryption.encrypt_shared_secret(
                        self.other_side.shared_secret,
                        self.other_side.rsa_key
                    )
                    if 'challenge_token' in packet:
                        challenge_token = encryption.decrypt_shared_secret(
                            packet['challenge_token'], self.rsa_key
                        )
                        if challenge_token != self.challenge_token:
                            self.kick("Invalid client reply")
                            return
                        packet['challenge_token'] = encryption.encrypt_shared_secret(
                            self.other_side.challenge_token,
                            self.other_side.rsa_key
                        )
                    if auth:
                        logger.info("Authenticating on server")
                        auth.join_server(self.server_id,
                                        self.other_side.shared_secret,
                                        self.other_side.rsa_key)
                    if check_auth:
                        logger.info("Checking authenticity")
                        if not Authenticator.check_player(
                            self.username, self.other_side.server_id,
                            self.shared_secret, self.rsa_key):
                            self.kick("Unable to verify username")
                            return
                    rebuild = True
                elif packet['msgtype'] == 0xfc and self.side == 'server':
                    logger.debug("Starting encryption")
                    self.start_cipher()
                forward = True
                if self.plugin_mgr:
                    forwarding = self.plugin_mgr.filter(packet, self.side)
                    if forwarding and packet.modified:
                        rebuild = True
                if rebuild:
                    packet['raw_bytes'] = self.msg_spec[packet['msgtype']].emit(packet)
                if forwarding and self.other_side is not None:
                    self.other_side.send(packet['raw_bytes'])
                if packet['msgtype'] == 0xfc and self.side == 'server':
                    self.other_side.start_cipher()
                # Since we know we're at a message boundary, we can inject
                # any messages in the queue.
                msgbytes = self.plugin_mgr.next_injected_msg_from(self.side)
                while self.other_side and msgbytes is not None:
                    self.other_side.send(msgbytes)
                    msgbytes = self.plugin_mgr.next_injected_msg_from(self.side)

                # Attempt to parse the next packet.
                packet = parse_packet(self.stream,self.msg_spec, self.side)
        except PartialPacketException:
            pass # Not all data for the current packet is available.
        except Exception:
            logger.error("MinecraftProxy for %s caught exception, out of sync" % self.side)
            logger.error(traceback.format_exc())
            logger.debug("Current stream buffer: %s" % repr(self.stream.buf))
            self.out_of_sync = True
            self.stream.reset()