Visual_Studio

Minecraft Client

Jan 24th, 2018
111
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 14.39 KB | None | 0 0
  1. #!/usr/bin/python3
  2.  
  3. import socket
  4. from os import urandom
  5. from io import BytesIO
  6. from uuid import uuid4
  7. from hashlib import sha1
  8. from os.path import isfile
  9. from json import load, dump
  10. from enum import Enum, IntEnum
  11. from struct import pack, unpack
  12. from binascii import hexlify as _hexlify
  13.  
  14. from Crypto.PublicKey import RSA
  15. from Crypto.Cipher import AES, PKCS1_v1_5
  16.  
  17. import requests
  18.  
  19. AUTH_URL = "https://authserver.mojang.com"
  20. SESSION_URL = "https://sessionserver.mojang.com/session/minecraft"
  21.  
  22. def rshift(val, n): return val >> n if val >= 0 else (val + 0x100000000) >> n
  23.  
  24. def hexlify(b: (bytes, bytearray)) -> str:
  25.     return str(_hexlify(b), "utf8")
  26.  
  27. #doubt this is used more than once
  28. def bytes_to_num(b: (bytes, bytearray)) -> int:
  29.     return int.from_bytes(b, byteorder='big', signed=True)
  30.  
  31. def minecraft_sha1_hash_digest(b: (bytes, bytearray)) -> str:
  32.     return format(bytes_to_num(sha1(b).digest()), 'x')
  33.  
  34. class ClientPacket(IntEnum):
  35.     HANDSHAKE = 0x00
  36.     LOGIN = 0x00
  37.     ENCRYPTION_RESPONSE = 0x01
  38.  
  39. class ServerPacket(IntEnum):
  40.     ENCRYPTION_REQUEST = 0x01
  41.  
  42. class Endian(Enum):
  43.     def __str__(self) -> str:
  44.         return str(self.value)
  45.  
  46.     LITTLE = "<"
  47.     BIG = ">"
  48.     NETWORK = "!"
  49.  
  50. class MinecraftIO(BytesIO):
  51.     """
  52.    If only Python supported do-while loops, this is cancer to write
  53.    """
  54.     endian = Endian.LITTLE
  55.  
  56.     def __init__(self, *args, **kwargs):
  57.         if "endian" in kwargs and isinstance(kwargs["endian"], Endian):
  58.             self.set_endian(kwargs.pop("endian", Endian.LITTLE))
  59.         else:
  60.             self.set_endian(Endian.LITTLE)
  61.         super(MinecraftIO, self).__init__(*args, **kwargs)
  62.  
  63.     def set_endian(self, endian: Endian) -> None:
  64.         self.endian = str(endian)
  65.  
  66.     def get_endian(self) -> Endian:
  67.         return Endian(self.endian)
  68.  
  69.     #read
  70.     def read_bytes(self, count: int) -> (bytes, bytearray):
  71.         return self.read(count)
  72.  
  73.     def read_byte(self) -> int:
  74.         return self.read_uint8()
  75.  
  76.     def read_uint8(self) -> int:
  77.         return self.read(1)[0]
  78.  
  79.     def read_string(self, encoding: str = "ascii") -> str:
  80.         return str(self.read(self.read_varint()), encoding)
  81.  
  82.     def read_ushort(self) -> int:
  83.         return self.read_uint16()
  84.  
  85.     def read_uint16(self) -> int:
  86.         return unpack(self.endian + "H", self.read(2))[0]
  87.  
  88.     def read_ulong(self) -> int:
  89.         return self.read_uint32()
  90.  
  91.     def read_uint32(self) -> int:
  92.         return unpack(self.endian + "L", self.read(4))[0]
  93.  
  94.     def read_ulonglong(self) -> int:
  95.         return self.read_uint64()
  96.  
  97.     def read_uint64(self) -> int:
  98.         return unpack(self.endian + "Q", self.read(8))[0]
  99.  
  100.     def read_position(self) -> (tuple, list):
  101.         val = self.read_ulonglong()
  102.         x = val >> 38
  103.         y = (val >> 26) & 0xFFF
  104.         z = val & 0x3FFFFFF
  105.         #fixes for negative numbers on non-java implementations
  106.         if x >= pow(2, 25):
  107.             x -= pow(2, 26)
  108.         if y >= pow(2, 11):
  109.             y -= pow(2, 12)
  110.         if z >= pow(2, 25):
  111.             z -= pow(2, 26)
  112.         return (x, y, z)
  113.  
  114.     def read_varint(self) -> int:
  115.         number = 0
  116.         bytes_encountered = 0
  117.         while True:
  118.             byte = self.read(1)[0]
  119.             number |= (byte & 0x7F) << 7 * bytes_encountered
  120.             if not byte & 0x80:
  121.                 break
  122.             bytes_encountered += 1
  123.             if bytes_encountered > 5:
  124.                 raise ValueError("Tried to read too long of a VarInt")
  125.         return number
  126.  
  127.     #write
  128.     def write_bytes(self, values: (bytes, bytearray)) -> int:
  129.         return self.write(values)
  130.  
  131.     def write_byte(self, value: int) -> int:
  132.         return self.write_byte(value)
  133.  
  134.     def write_uint8(self, value: int) -> int:
  135.         return self.write(bytes([value]))
  136.  
  137.     def write_string(self, s: str, encoding: str = "ascii") -> int:
  138.         c = self.write_varint(len(s))
  139.         c += self.write(bytes(s, encoding))
  140.         return c
  141.  
  142.     def write_ushort(self, value: int) -> int:
  143.         return self.write_uint16(value)
  144.  
  145.     def write_uint16(self, value: int) -> int:
  146.         return self.write(pack(self.endian + "H", value))
  147.  
  148.     def write_ulong(self, value: int) -> int:
  149.         return self.write_uint32(value)
  150.  
  151.     def write_uint32(self, value: int) -> int:
  152.         return self.write(pack(self.endian + "L", value))
  153.  
  154.     def write_ulonglong(self, value: int) -> int:
  155.         return self.write_uint64(value)
  156.  
  157.     def write_uint64(self, value: int) -> int:
  158.         return self.write(pack(self.endian + "Q", value))
  159.  
  160.     def write_position(self, x: int, y: int, z: int) -> int:
  161.         return self.write_ulonglong(((x & 0x3FFFFFF) << 38) | ((y & 0xFFF) << 26) | (z & 0x3FFFFFF))
  162.  
  163.     def write_varint(self, value: int) -> int:
  164.         out = bytes()
  165.         while True:
  166.             byte = value & 0x7F
  167.             value >>= 7
  168.             out += pack(self.endian + "B", byte | (0x80 if value > 0 else 0))
  169.             if value == 0:
  170.                 break
  171.         return self.write(out)
  172.  
  173.     #don't know if these will be used
  174.     def read_varlong(self) -> int:
  175.         num_read = 0
  176.         result = 0
  177.  
  178.         #implementation one
  179.         read = self.read(1)[0]
  180.         value = read & 0b01111111
  181.         result |= value << (7 * num_read)
  182.         num_read += 1
  183.         if num_read > 10:
  184.             raise Exception("VarLong is too big")
  185.         while read & 0b10000000 != 0:
  186.             #implementation two
  187.             read = self.read(1)[0]
  188.             value = read & 0b01111111
  189.             result |= value << (7 * num_read)
  190.             num_read += 1
  191.             if num_read > 10:
  192.                 raise Exception("VarLong is too big")
  193.         return result
  194.  
  195.     def write_varlong(self, value: int) -> None:
  196.         #)implementation one
  197.         temp = value & 0b01111111
  198.         value = rshift(value, 7)
  199.         if value != 0:
  200.             temp |= 0b10000000
  201.         self.write(bytes([temp]))
  202.         while value != 0:
  203.             #implementation two
  204.             temp = value & 0b01111111
  205.             value = rshift(value, 7)
  206.             if value != 0:
  207.                 temp |= 0b10000000
  208.             self.write(bytes([temp]))
  209.  
  210. #documentation: http://wiki.vg/Authentication
  211. class Yggdrasil(object):
  212.     username = None
  213.     password = None
  214.  
  215.     def __init__(self, username: str, password: str) -> None:
  216.         self.username = username
  217.         self.password = password
  218.  
  219.     def authenticate(self, client_token: str = None) -> dict:
  220.         """
  221.        Authenticate with the server using a given username and password
  222.        :param client_token: Use a specified client token otherwise it will be a random uuid4
  223.        :return: client token, access token, and selected profile
  224.        """
  225.         json_data = {
  226.             "agent": {
  227.                 "name": "Minecraft",
  228.                 "version": 1
  229.             },
  230.             "username": self.username,
  231.             "password": self.password,
  232.             "clientToken": client_token if client_token is not None else str(uuid4())
  233.         }
  234.         headers = {"Content-Type": "application/json"}
  235.         response = requests.post(AUTH_URL + "/authenticate", json=json_data, headers=headers).json()
  236.         if any(key in response for key in ["error", "errorMessage", "cause"]):
  237.             if response["error"] == "ForbiddenOperationException":
  238.                 if "cause" in response and response["cause"] == "UserMigratedException":
  239.                     raise Exception("Account is migrated use your email instead")
  240.             raise Exception("Login failed: \"" + response["errorMessage"] + "\"")
  241.         return {
  242.             "client_token": response["clientToken"],
  243.             "access_token": response["accessToken"],
  244.             "selected_profile": response["selectedProfile"]
  245.         }
  246.  
  247.     def validate(self, access_token: str, client_token: str = None) -> bool:
  248.         """
  249.        Validates a given access token and client token pair
  250.        :param access_token: The access token to verify
  251.        :param client_token: The client token tied to the access token
  252.        :return: true if valid otherwise false
  253.        """
  254.         json_data = {
  255.             "accessToken": access_token
  256.         }
  257.         if isinstance(client_token, str):
  258.             json_data["clientToken"] = client_token
  259.         headers = {"Content-Type": "application/json"}
  260.         response = requests.post(AUTH_URL + "/validate", json=json_data, headers=headers)
  261.         return response.status_code == 204
  262.  
  263.     def refresh(self, access_token: str, client_token: str = None) -> dict:
  264.         """
  265.        Refreshes an access token and client token pair
  266.        :param access_token: The access token to refresh
  267.        :param client_token: The client token tied to the access token
  268.        :return: client token, access token, and selected profile
  269.        """
  270.         json_data = {
  271.             "accessToken": access_token
  272.         }
  273.         if isinstance(client_token, str):
  274.             json_data["clientToken"] = client_token
  275.         headers = {"Content-Type": "application/json"}
  276.         response = requests.post(AUTH_URL + "/refresh", json=json_data, headers=headers).json()
  277.         return {
  278.             "client_token": response["clientToken"],
  279.             "access_token": response["accessToken"],
  280.             "selected_profile": response["selectedProfile"]
  281.         }
  282.  
  283.     def signout(self) -> bool:
  284.         """
  285.        Signs out and invalidates any current sessions
  286.        :return: true if successful otherwise false
  287.        """
  288.         json_data = {
  289.             "username": self.username,
  290.             "password": self.password
  291.         }
  292.         headers = {"Content-Type": "application/json"}
  293.         response = requests.post(AUTH_URL + "/signout", json=json_data, headers=headers)
  294.         return response.status_code == 200 and response.text == ""
  295.  
  296.     def invalidate(self, access_token: str, client_token: str = None) -> bool:
  297.         """
  298.        Invalidates a specific access token and client token pair
  299.        :param access_token: The access token to invalidate
  300.        :param client_token: The client token tied to the access token
  301.        :return: true if successful otherwise false
  302.        """
  303.         json_data = {
  304.             "accessToken": access_token
  305.         }
  306.         if isinstance(client_token, str):
  307.             json_data["clientToken"] = client_token
  308.         headers = {"Content-Type": "application/json"}
  309.         response = requests.post(AUTH_URL + "/invalidate", json=json_data, headers=headers)
  310.         return response.status_code == 200 and response.text == ""
  311.  
  312. if __name__ == "__main__":
  313.     username = ""  #email if migrated
  314.     password = ""
  315.     session_file = "session.json"
  316.  
  317.     """
  318.    ygg = Yggdrasil(username, password)
  319.    if not isfile(session_file):
  320.        login_data = ygg.authenticate()
  321.        dump(login_data, open(session_file, "w"))
  322.        print("Authenticated with Mojang!")
  323.    else:
  324.        login_data = load(open(session_file, "r"))
  325.        if ygg.validate(login_data["access_token"], login_data["client_token"]):
  326.            print("Session is still valid!")
  327.        else:
  328.            print("Session is invalid, refreshing...")
  329.            login_data = ygg.refresh(login_data["access_token"], login_data["client_token"])
  330.            dump(login_data, open(session_file, "w"))
  331.            print("Session refreshed!")
  332.    print("%s is logged in!" % (login_data["selected_profile"]["name"]))
  333.    """
  334.  
  335.     #socket
  336.     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  337.     sock.connect(("mineheroes.net", 25565))
  338.  
  339.     #handshake
  340.     mc_io = MinecraftIO(endian=Endian.NETWORK)
  341.     mc_io.write_varint(340)  #protocol version
  342.     mc_io.write_string("mineheroes.net")  #server hostname
  343.     mc_io.write_ushort(25565)  #server port
  344.     mc_io.write_varint(2)  #next state
  345.     payload = mc_io.getvalue()
  346.  
  347.     #header
  348.     mc_io = MinecraftIO(endian=Endian.NETWORK)
  349.     mc_io.write_varint(int(ClientPacket.HANDSHAKE))  #packet ID
  350.     mc_io.write(payload)  #data
  351.     payload = mc_io.getvalue()
  352.  
  353.     #packet length
  354.     mc_io = MinecraftIO(endian=Endian.NETWORK)
  355.     mc_io.write_varint(len(payload))  #length
  356.     packet_len = mc_io.getvalue()
  357.  
  358.     #complete header
  359.     #length + packet ID + data
  360.     packet = packet_len + payload
  361.  
  362.     sock.send(packet)
  363.  
  364.     #login start
  365.     mc_io = MinecraftIO(endian=Endian.NETWORK)
  366.     mc_io.write_string("Visual_Studio")
  367.     payload = mc_io.getvalue()
  368.  
  369.     #header
  370.     mc_io = MinecraftIO(endian=Endian.NETWORK)
  371.     mc_io.write_varint(int(ClientPacket.LOGIN))  # packet ID
  372.     mc_io.write(payload)  # data
  373.     payload = mc_io.getvalue()
  374.  
  375.     #packet length
  376.     mc_io = MinecraftIO(endian=Endian.NETWORK)
  377.     mc_io.write_varint(len(payload))  # length
  378.     packet_len = mc_io.getvalue()
  379.  
  380.     #complete header
  381.     #length + packet ID + data
  382.     packet = packet_len + payload
  383.  
  384.     sock.send(packet)
  385.  
  386.     response = sock.recv(4096)
  387.     mc_io = MinecraftIO(response)
  388.     response = mc_io.read(mc_io.read_varint())
  389.     mc_io = MinecraftIO(response)
  390.     packet_id = mc_io.read_varint()
  391.     server_id = mc_io.read_string()
  392.     public_key = mc_io.read(mc_io.read_varint())
  393.     print("Public Key:   " + hexlify(public_key))
  394.     verify_token = mc_io.read(mc_io.read_varint())
  395.     print("Verify Token: " + hexlify(verify_token))
  396.     shared_key = urandom(16)
  397.     print("Shared Key:   " + hexlify(shared_key))
  398.     client_hash = minecraft_sha1_hash_digest(bytes(server_id, "ascii") + shared_key + public_key)
  399.     print("Client Hash:  " + client_hash)
  400.  
  401.     rsa = PKCS1_v1_5.new(RSA.import_key(public_key))
  402.     enc_shared_key = rsa.encrypt(shared_key)
  403.     enc_verify_token = rsa.encrypt(verify_token)
  404.  
  405.     #encryption response
  406.     mc_io = MinecraftIO(endian=Endian.NETWORK)
  407.     mc_io.write_varint(len(enc_shared_key))
  408.     mc_io.write(enc_shared_key)
  409.     mc_io.write_varint(len(enc_verify_token))
  410.     mc_io.write(enc_verify_token)
  411.     payload = mc_io.getvalue()
  412.  
  413.     #header
  414.     mc_io = MinecraftIO(endian=Endian.NETWORK)
  415.     mc_io.write_varint(int(ClientPacket.ENCRYPTION_RESPONSE))  #packet ID
  416.     mc_io.write(payload)  #data
  417.     payload = mc_io.getvalue()
  418.  
  419.     # packet length
  420.     mc_io = MinecraftIO(endian=Endian.NETWORK)
  421.     mc_io.write_varint(len(payload))  #length
  422.     packet_len = mc_io.getvalue()
  423.  
  424.     #complete header
  425.     #length + packet ID + data
  426.     packet = packet_len + payload
  427.  
  428.     sock.send(packet)
  429.  
  430.     print(sock.recv(4096))
Advertisement
Add Comment
Please, Sign In to add comment