Xylee999

Untitled

Jun 30th, 2023
32
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 31.46 KB | None | 0 0
  1. import asyncio
  2. import pathlib
  3. import ssl
  4. import websockets
  5. import sys
  6. import os
  7. import base64
  8. import requests
  9. import zipfile
  10. import hashlib
  11.  
  12. #To parse the mappings
  13. import json
  14. import pandas as pd
  15.  
  16. #Fancy loading bar for the download
  17. from tqdm import tqdm
  18.  
  19. #Self signed SSL cert
  20. from OpenSSL import crypto, SSL
  21.  
  22. SERVER_IP = "localhost"
  23. SERVER_PORT = 8765
  24.  
  25. ##Mapping stuff
  26. version_map={}
  27.  
  28. mapping_urls={
  29. "1.7.10": {
  30. "map": "http://export.mcpbot.golde.org/mcp_stable/12-1.7.10/mcp_stable-12-1.7.10.zip",
  31. "srg": "http://export.mcpbot.golde.org/mcp/1.7.10/mcp-1.7.10-srg.zip"
  32. },
  33. "1.8.9": {
  34. "map": "http://export.mcpbot.golde.org/mcp_stable/22-1.8.9/mcp_stable-22-1.8.9.zip",
  35. "srg": "http://export.mcpbot.golde.org/mcp/1.8.9/mcp-1.8.9-srg.zip"
  36. },
  37. "1.12": {
  38. "map": "http://export.mcpbot.golde.org/mcp_stable/39-1.12/mcp_stable-39-1.12.zip",
  39. "srg": "http://export.mcpbot.golde.org/mcp/1.12/mcp-1.12-srg.zip"
  40. }
  41. }
  42.  
  43. def makeDir(path):
  44. if not os.path.exists(path):
  45. os.makedirs(path)
  46.  
  47. def downloadFile(url, file_name):
  48. print(f"Downloading {file_name} from {url}")
  49. response = requests.get(url, stream=True)
  50. with open(file_name, "wb") as handle:
  51. for data in tqdm(response.iter_content()):
  52. handle.write(data)
  53.  
  54. def loadAllMappings(path = "mappings"):
  55. global mapping_urls, version_map
  56. downloadMappings(path = path)
  57. for version in mapping_urls:
  58. current_path = path + "/" + version + "/"
  59. srg_method, srg_field = loadMappings(path = current_path, version = version)
  60.  
  61. version_map[version] = {}
  62. version_map[version]["method"] = srg_method
  63. version_map[version]["field"] = srg_field
  64. print(f"Loaded {version} mapping!")
  65.  
  66. def downloadMappings(path = "mappings"):
  67. global mapping_urls
  68. makeDir(path)
  69. for version in mapping_urls:
  70. current_path = path + "/" + version + "/"
  71. makeDir(current_path)
  72.  
  73. map_file = current_path + "map.zip"
  74. srg_file = current_path + "srg.zip"
  75.  
  76. if os.path.exists(current_path + "fields.csv") and os.path.exists(current_path + "methods.csv") and os.path.exists(current_path + "joined.srg"):
  77. continue
  78.  
  79. downloadFile(mapping_urls[version]["map"], map_file)
  80. downloadFile(mapping_urls[version]["srg"], srg_file)
  81.  
  82. with zipfile.ZipFile(map_file, 'r') as zip_ref:
  83. zip_ref.extract("fields.csv", current_path)
  84. print(f"Extracted {version} fields.csv")
  85. zip_ref.extract("methods.csv", current_path)
  86. print(f"Extracted {version} methods.csv")
  87. with zipfile.ZipFile(srg_file, 'r') as zip_ref:
  88. zip_ref.extract("joined.srg", current_path)
  89. print(f"Extracted {version} joined.srg")
  90.  
  91. os.remove(map_file)
  92. os.remove(srg_file)
  93.  
  94. def saveMappingCache(srg_method, srg_field, fields_cache = "fields_cache.json", methods_cache = "methods_cache.json", path = ""):
  95. print("Saving fields cache...")
  96. with open(path + fields_cache, "w") as f:
  97. f.write(json.dumps(srg_field))
  98. print("Saving methods cache...")
  99. with open(path + methods_cache, "w") as f:
  100. f.write(json.dumps(srg_method))
  101.  
  102. def loadMappingCache(fields_cache = "fields_cache.json", methods_cache = "methods_cache.json", path = ""):
  103. srg_field = None
  104. srg_method = None
  105.  
  106. if os.path.exists(path + fields_cache):
  107. print("Loading cached fields...")
  108. with open(path + fields_cache, "r") as f:
  109. srg_field = json.loads(f.read())
  110. if os.path.exists(path + methods_cache):
  111. print("Loading cached methods...")
  112. with open(path + methods_cache, "r") as f:
  113. srg_method = json.loads(f.read())
  114.  
  115. return (srg_method, srg_field)
  116.  
  117. #This is TOO SLOW!
  118. def parseSRG(file, fields_map, methods_map, parse_fields = True, parse_methods = True, version = ""):
  119. srg_method = {}
  120. srg_field = {}
  121.  
  122. skipped_field = 0
  123. skipped_method = 0
  124. with open(file, "r") as f:
  125. for line in f:
  126. if parse_fields and line.startswith("FD: "):
  127. splited = line.split(" ")
  128. vanilla_obf = splited[1].rsplit("/", 1)
  129. forge_map = splited[2].rsplit("/", 1)
  130.  
  131. #Just for better readability
  132. vanilla_class = vanilla_obf[0].rstrip()
  133. vanilla_field_name = vanilla_obf[1].rstrip()
  134.  
  135. forge_class = forge_map[0].rstrip()
  136. forge_field_name = forge_map[1].rstrip()
  137.  
  138. if forge_field_name.startswith("field_"):
  139. csv_result = fields_map[fields_map["searge"] == forge_field_name]
  140. if csv_result.empty:
  141. skipped_field += 1
  142. continue
  143.  
  144. mcp_field_name = csv_result.values[0][1]#"name"
  145. else:
  146. mcp_field_name = forge_field_name
  147.  
  148. if version == "1.7.10":
  149. NAME_CORRECTION = {
  150. "yOffset2": "ySize",
  151. "fontRendererObj": "fontRenderer"
  152. }
  153.  
  154. if mcp_field_name in NAME_CORRECTION:
  155. mcp_field_name = NAME_CORRECTION[mcp_field_name]
  156. elif version == "1.12":
  157. NAME_CORRECTION = {
  158. "field_71466_p": "fontRendererObj",
  159. "field_70123_F": "isCollidedHorizontally",
  160. "field_70124_G": "isCollidedVertically",
  161. "field_70132_H": "isCollided",
  162. "field_72450_a": "xCoord",
  163. "field_72448_b": "yCoord",
  164. "field_72449_c": "zCoord",
  165. "field_147707_d": "theShaderGroup",
  166. "field_178183_a": "worldRenderer",
  167. "field_76636_d": "isChunkLoaded",
  168. "field_77864_a": "efficiencyOnProperMaterial",
  169. "field_77865_bY": "damageVsEntity"
  170. }
  171.  
  172. if forge_field_name in NAME_CORRECTION:
  173. mcp_field_name = NAME_CORRECTION[forge_field_name]
  174.  
  175. forge_full_path = forge_class + "/" + mcp_field_name
  176.  
  177. srg_field[forge_full_path] = {
  178. "name": mcp_field_name,
  179. "forge_name": forge_field_name,
  180. "vanilla_name": vanilla_field_name,
  181. "vanilla_class": vanilla_class
  182. }
  183. elif parse_methods and line.startswith("MD: "):
  184. splited = line.split(" ")
  185. vanilla_obf = splited[1].rsplit("/", 1)
  186. vanilla_params = splited[2].rstrip()
  187.  
  188. forge_map = splited[3].rsplit("/", 1)
  189. forge_params = splited[4].rstrip()
  190.  
  191. #Just for better readability 2
  192. vanilla_class = vanilla_obf[0].rstrip()
  193. vanilla_method_name = vanilla_obf[1].rstrip()
  194.  
  195. forge_class = forge_map[0].rstrip()
  196. forge_method_name = forge_map[1].rstrip()
  197.  
  198. csv_result = methods_map[methods_map["searge"] == forge_method_name]
  199. if csv_result.empty:
  200. skipped_method += 1
  201. continue
  202.  
  203. mcp_method_name = csv_result.values[0][1]#"name"
  204. forge_full_path = forge_class + "/" + mcp_method_name
  205.  
  206. #Manual correction
  207. FORCE_METHOD_MATCH = {
  208. "net/minecraft/util/BlockPos/down": "func_177977_b",
  209. "net/minecraft/client/renderer/WorldRenderer/color": "func_181666_a",
  210. "net/minecraft/entity/EntityLivingBase/isPotionActive": "func_70644_a",
  211. "net/minecraft/world/World/markBlockRangeForRenderUpdate": "func_147458_c"
  212. }
  213. if version == "1.12":
  214. FORCE_METHOD_MATCH["net/minecraft/world/World/getCollisionBoxes"] = "func_184144_a"
  215. FORCE_METHOD_MATCH["net/minecraft/util/math/BlockPos"] = "func_177977_b"
  216. FORCE_METHOD_MATCH["net/minecraft/client/renderer/BufferBuilder/color"] = "func_181666_a"
  217.  
  218. if forge_full_path in FORCE_METHOD_MATCH and FORCE_METHOD_MATCH[forge_full_path] != forge_method_name:
  219. skipped_method += 1
  220. continue
  221.  
  222. if version == "1.7.10":
  223. FORCE_SIGNATURE_MATCH = {
  224. "net/minecraft/client/renderer/entity/RenderPlayer/doRender": "(Lnet/minecraft/client/entity/AbstractClientPlayer;DDDFF)V"
  225. }
  226. NAME_CORRECTION = {
  227. "isCustomInventoryName": "hasCustomInventoryName",
  228. "canStopRayTrace": "canCollideCheck",
  229. "getMetadata": "getItemDamage",
  230. "setMetadata": "setItemDamage",
  231. "mouseReleased": "mouseMovedOrUp"
  232. }
  233.  
  234. if forge_full_path in FORCE_SIGNATURE_MATCH and FORCE_SIGNATURE_MATCH[forge_full_path] != forge_params:
  235. skipped_method += 1
  236. continue
  237.  
  238. if mcp_method_name in NAME_CORRECTION:
  239. mcp_method_name = NAME_CORRECTION[mcp_method_name]
  240. forge_full_path = forge_class + "/" + mcp_method_name
  241. elif version == "1.12":
  242. NAME_CORRECTION = {
  243. "func_70032_d": "getDistanceToEntity",
  244. "func_70082_c": "setAngles",
  245. "func_92059_d": "getEntityItem",
  246. "func_72964_e": "getChunkFromChunkCoords",
  247. "func_175726_f": "getChunkFromBlockCoords",
  248. "func_72441_c": "addVector",
  249. "func_72314_b": "addCoord",
  250. "func_72318_a": "isVecInside",
  251. "func_72326_a": "intersectsWith",
  252. "func_77977_a": "getUnlocalizedName",
  253. "func_180664_k": "getBlockLayer",
  254. "func_180634_a": "onEntityCollidedWithBlock",
  255. "func_110623_a": "getResourcePath",
  256. "func_150931_i": "getDamageVsEntity"
  257. }
  258.  
  259. if forge_method_name in NAME_CORRECTION:
  260. mcp_field_name = NAME_CORRECTION[forge_method_name]
  261. forge_full_path = forge_class + "/" + mcp_method_name
  262. #
  263.  
  264. srg_method[forge_full_path] = {
  265. "name": mcp_method_name,
  266. "forge_name": forge_method_name,
  267. "forge_params": forge_params,
  268. "vanilla_name": vanilla_method_name,
  269. "vanilla_params": vanilla_params,
  270. "vanilla_class": vanilla_class
  271. }
  272. print(f"Skipped {skipped_field} fields and {skipped_method} methods")
  273. return (srg_method, srg_field)
  274.  
  275. def loadMappings(fields_csv = "fields.csv", methods_csv = "methods.csv", joined_srg = "joined.srg", path = "", version = ""):
  276. fields_map = None
  277. methods_map = None
  278. print("Loading mappings...")
  279. srg_method, srg_field = loadMappingCache(path = path)
  280.  
  281. load_methods=False
  282. load_fields=False
  283. if srg_method == None or len(srg_method) == 0:
  284. print("Unable to load cached methods")
  285. load_methods = True
  286.  
  287. if srg_field == None or len(srg_field) == 0:
  288. print("Unable to load cached fields")
  289. load_fields = True
  290.  
  291. if load_fields:
  292. fields_map = pd.read_csv(path + fields_csv)
  293. print(f"Loaded {path + fields_csv}")
  294. if load_methods:
  295. methods_map = pd.read_csv(path + methods_csv)
  296. print(f"Loaded {path + methods_csv}")
  297.  
  298. if load_fields or load_methods:
  299. print("Parsing SRG ... (This can take some time. But don\'t worry this is done only once per version!)")
  300. srg_method, srg_field = parseSRG(path + joined_srg, fields_map, methods_map, load_fields, load_methods, version)
  301. print(f"Loaded {path + joined_srg}")
  302. saveMappingCache(srg_method, srg_field, path = path)
  303.  
  304. return srg_method, srg_field
  305. ##
  306.  
  307. ASSET_LIST = {
  308. "circle2": "bcf707a0e4cfb8f7bf2d674231e3f75ec1e3639ebe963f679eb134ff25887cd5",
  309. "di_target": "c3686a196e62f5760695bcff6943d53895e6644e52a53acd1f58fab5d465adc5",
  310. "duel info": "31d5f4778af17e0d75ba7322ce4173dc45fc99c4d0f9b5fad9d89b67a8461936",
  311. "settings": "b7bb7fd763e2725c43aef027abb91dd757444265599eab33bd2ceac72c4858ca",
  312. "dots": "d0a4e01a291f327c77688376cf56147ab1de864bcfcbf3cb2de813d2a00895e4",
  313. "import": "6938de58f81af17bab49e9da5b410a7f8de2d8dad03191d315e967398bc9d2a6",
  314. "peace": "d0c010b5028a550079f9958d7a12eed8074155dcf4cfddc942a2545b3f1e261a",
  315. "favorite": "108d5a643d7ed7827224d71144761df77cc66f6e6875cc8e685e492e895012db",
  316. "utility": "2bb27052316e8db9a6b50c12a1990a3466670261d7856e9d6cf92cadaf1acb0f",
  317. "toggleback": "675ec7214adb0db0bb783fd875e3320284a5b3e7e82865d01313e9236b841ace",
  318. "toggleback2": "e1726408b195b1f7158a1dfadcaed415bc4a18ec60e96ce1d47460c1f5952293",
  319. "ex": "ed598122f2fceefeed5dbe2357ddf49c325ac4cb8e044edb2cf17accb4ffa665",
  320. "di_pot": "8b23b5f61f79c15fb379be0b202b486f0c59a2cfebe2eb9468f7fa3618aa7a2e",
  321. "togglefront": "f1fc2b10fbd117275240697e8847e0bda8fb089dab66cb971ac782bef553494e",
  322. "text gui": "59e14f49d31550145cd758e36cdd1154c217f0a4badb62515229210c9160ba40",
  323. "creeper": "99f1518b6d74b3c191a6fa3282585e220d5022c608cac4512b840fcfd694a5ba",
  324. "delete": "da3957946ea980de5d7aeebed7e1649ecd77ad229a54b230595c1f99d85e10d6",
  325. "check": "25b197a324870c001261ef2b12ef52b4014e01b23e7c500277a43dee8f00555b",
  326. "render": "e22933b47ee4b1c4b3609f384e5f154534eb684fce430421cfa03f927002f120",
  327. "download": "d35544d765b61d4585b1f1b19232a2df4a6c30ba0c87ccef93dc6c62a77d98e2",
  328. "world": "f97d9cfe66e06a47fb7432fe148b01cd230b5bd255e062ecf5ab8ec12b5ee311",
  329. "other": "2c82463597d503c87cede3b7061ebc29589a85767848047e6c63dc2a95481761",
  330. "sync": "293fde987eb63f4c39d70efa0740ca8087302356c60119a6055d41971e440ec0",
  331. "circle": "113acf8dfe24448284d74da9c731632af084cb12ed15b2b74d98aecbcd8bf57e",
  332. "di_hit": "1582f9bcdd9b43e40c6ae9f13f39bf48f752deca684e3dd72a7164f70dd9aa49",
  333. "info": "e36b3edb0bba8492280646adc1e17bcdf10b91c0645eb87fbd9cbaf59246af24",
  334. "blatant": "f72ab642ede4ef6a7f1152197beb1ad9842929bb1f18882b7673f5c691a2758c",
  335. "v4": "4744da398abde37aa83a9cde867ba38113a7679595c7f880814703f3fcf30ab1",
  336. "copy": "88422534bd97919412e4e0e09f54e181cec378bbda004c6847e25c5d5bf2bdd6",
  337. "friends": "8242e7ee68a7f7e5d34386b5dc979b5d8e1d88ff8bf25840f6a2ffea8e829006",
  338. "cross": "3c4eb0e2797645333838c55d7fc8ead47cfb5ffacd579bf5a59e4d441315e323",
  339. "vapelogo": "e59b3fcb5e6d4a069d06f2a6c9c5b58cd00e106ceb0f1ea5ce2b89ee70f6c497",
  340. "macros": "e396e2bd3ef8ff13b2ba9431b7957549a3453241ecd8e3ad97e44f5e28c71ed2",
  341. "target info": "052866360adb6b90970eed7ce8a4551d98a3538ff3b176980e5f8efd3cbc107a",
  342. "icon4": "46812a97eade3bd8fd5de6840d175ff954e2e08956de525e9d02f9adf2381fcd",
  343. "exo": "5a62d706b159c1f3cd29425f6280c26937e8e38f1cc6094d791aa8c06911ce71",
  344. "gui": "32e40a636dd76c7b745375985430e9e089e623c567ce5f6b57e1f7b3916472e3",
  345. "user": "e68b1e29e6e0670cced111b07afb253ae14e297b291e5e2aa9068f9c563feacf",
  346. "export": "c20f514c52d97aa5b60c54fb18ec30fb6456ee2f2a653f7d964ec33c5e7676f0",
  347. "upload": "7dc7ff0ff8b68aa4f550e5a0b7f9183d2d439b0ecbb8cdebc47abf6607d6d2d1",
  348. "radar": "dab5efdc30a28d129def7c1a1d869d7871a5c1df6a84612a3003c2b7f35e693d",
  349. "vapelogosmall": "0ea2da5d53629e15d7c0a6e372b8ba4482e82d5220a86fe5338c1a5cdd6e2506",
  350. "profiles": "4d019acc7a5c31c4bb3b23ea78ff5838beabc3ffd8635347a32f51f665f31d6d",
  351. "search": "86a4ec5dfb45efea48006d621e87a38e1f8e8d239f08221b67eb811dcef19a2a",
  352. "magnify": "b68aa467047e7936f7459cf8a955fea5acbc2d9e3dfa7c35aa8a0de6fe0ed412",
  353. "pin": "872ae634a5712ca7ccc479c45333738a836735e22e4252c17571b38b4912a6b0",
  354. "rearview": "396322a81e8c15555adf3f709c353ddec0d77de6a485093ccdd28bf987fa9382",
  355. "fire": "3c2cf0c986787ab10d9f5259cbdd345bba07a07360a039b494fae54e3be52883",
  356. "combat": "8a5aa93b6575a7e221873149f4a252888ea411c3e4f7a2f6d4d6d763b7164951"
  357. }
  358.  
  359. DUMP0_HASH = "610e11480fa719e99349c2f1cc1c341d7536410126ff98ba1d818afa710c5354"
  360. DUMP1_HASH = "5ec35d530ddd0270a4f5724f37b49af671ae72f960ff62dc4e60f555657aa07b"
  361. DUMP2_HASH = "59a95e9499f9c6cd54ec4cdc283d1638a11c3f68d23c689407b0c2f04c66ec7f"
  362. STRINGS_HASH = "0d9131680c83e1602759d234b9ac86e4dd15707b6332a5232d08642302836e6f"
  363.  
  364. def HashString(string):
  365. return hashlib.sha256(string.encode("ascii")).hexdigest()
  366.  
  367.  
  368. def FileHash(file_path):
  369. sha256 = hashlib.sha256()
  370. with open(file_path, 'rb') as f:
  371. data = f.read()
  372. sha256.update(data)
  373. return sha256.hexdigest()
  374.  
  375. def SingleFileIntegrity(file_path, expected_hash):
  376. if not os.path.exists(file_path):
  377. print(f"{file_path} not found! Please redownload or redump it.")
  378. exit(1)
  379.  
  380. file_hash = FileHash(file_path)
  381. if file_hash != expected_hash:
  382. print(f"Invalid file hash {file_path} !\nGot : {file_hash}\nExpected : {expected_hash}")
  383. exit(1)
  384.  
  385. def FileIntegrityCheck(assets_folder = "assets"):
  386. global ASSET_LIST
  387. for file in ASSET_LIST:
  388. asset_path = assets_folder + "/" + file + ".png"
  389. SingleFileIntegrity(asset_path, ASSET_LIST[file])
  390.  
  391. SingleFileIntegrity("Dump0", DUMP0_HASH)
  392. SingleFileIntegrity("Dump1", DUMP1_HASH)
  393. SingleFileIntegrity("Dump2", DUMP2_HASH)
  394. SingleFileIntegrity("strings.txt", STRINGS_HASH)
  395. print("Passed integrity check!")
  396.  
  397. #https://stackoverflow.com/questions/27164354/create-a-self-signed-x509-certificate-in-python
  398. def GenerateSSL(cert_file = "cert.pem", key_file = "key.pem"):
  399. k = crypto.PKey()
  400. k.generate_key(crypto.TYPE_RSA, 2048)
  401.  
  402. cert = crypto.X509()
  403. cert.get_subject().C = "FR"
  404. cert.get_subject().O = "Vape Offline Server by Andro24"
  405. cert.get_subject().CN = "localhost:8765"
  406. cert.set_serial_number(1337)
  407. cert.gmtime_adj_notBefore(0)
  408. cert.gmtime_adj_notAfter(10*365*24*60*60)#~10 years
  409. cert.set_issuer(cert.get_subject())
  410. cert.set_pubkey(k)
  411. cert.sign(k, "sha1")
  412. with open(cert_file, "wt") as f:
  413. f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert).decode("utf-8"))
  414. with open(key_file, "wt") as f:
  415. f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, k).decode("utf-8"))
  416. print("Generated self signed SSL cert and key!")
  417.  
  418. ##The actual server starts here:
  419. async def xor_string(text: bytes, key: int) -> bytes:
  420. return bytes([b ^ key for b in text])
  421.  
  422. async def saveSettings(ip, settings, path):
  423. makeDir(path)
  424. with open(path + "/" + HashString(ip) + ".json", "w") as f:
  425. f.write(settings)
  426.  
  427. async def loadSettings(ip, path):
  428. settings_file_path = path + "/" + HashString(ip) + ".json"
  429. if not os.path.exists(settings_file_path):
  430. print(f"{ip} : No settings found for this ip.")
  431. return "{}"
  432. with open(settings_file_path, "r") as f:
  433. return f.read()
  434.  
  435. FILE_BUFFER_SIZE=1000
  436. async def send_file(websocket, file, xor_key):
  437. global FILE_BUFFER_SIZE
  438. ip = websocket.remote_address[0];
  439. print(f"{ip} : Sending {file}...")
  440. with open(file, "rb") as f:
  441. f.seek(0, os.SEEK_END)
  442. fileSize = f.tell()
  443. f.seek(0, os.SEEK_SET)
  444.  
  445. await websocket.send(str(fileSize))
  446.  
  447. buffer = f.read(FILE_BUFFER_SIZE)
  448. while buffer:
  449. await websocket.send(await xor_string(buffer, xor_key))
  450. buffer = f.read(FILE_BUFFER_SIZE)
  451. print(f"{ip} : Sent {file} ({fileSize}) !")
  452.  
  453. async def send_assets(websocket, xor_key, assets_folder = "assets"):
  454. global ASSET_LIST
  455. ip = websocket.remote_address[0];
  456.  
  457. await websocket.send(str(len(ASSET_LIST)))
  458. for file in ASSET_LIST:
  459. await websocket.send(await xor_string(file.encode("ascii"), xor_key))
  460. with open(assets_folder + "/" + file + ".png", "rb") as f:
  461. await websocket.send(await xor_string(f.read(), xor_key))
  462. print(f"{ip} : Sent {file} !")
  463. print(f"{ip} : Sent {len(ASSET_LIST)} assets !")
  464.  
  465. async def send_strings(websocket, xor_key, strings_file = "strings.txt"):
  466. ip = websocket.remote_address[0];
  467.  
  468. strings = []
  469. with open(strings_file, "rb") as f:
  470. strings = f.read().split(b"\000")
  471. strings = strings[:-1]#Remove the last empty string due to the split
  472.  
  473. await websocket.send(str(len(strings)))
  474.  
  475. for string in strings:
  476. await websocket.send(await xor_string(base64.b64encode(string), xor_key))
  477. print(f"{ip} : Sent {len(strings)} strings !")
  478.  
  479. async def handle_client(websocket, path):
  480. global version_map
  481.  
  482. MAGIC = ""
  483. MAGIC_V4="ab33cdea3e72c957eb44677e44b98909"
  484. MAGIC_LITE="7f14cd4b5e3e73833e333635c7f17a1b"
  485.  
  486. ip = websocket.remote_address[0];
  487. print(f"New client from {ip} !")
  488.  
  489. xor_key = 0
  490. client_mc_version = ""
  491. vape_version = ""
  492. settings_path = "settings/"
  493.  
  494. srg_method = None
  495. srg_field = None
  496.  
  497. while True:
  498. try:
  499. recvdata = await websocket.recv()
  500. recvdata = recvdata.split("\n")
  501.  
  502. packet_id = int(recvdata[0])
  503.  
  504. ##Handle Packets
  505.  
  506. #Hello
  507. if packet_id == 3 and len(recvdata) >= 3:
  508. if recvdata[2] != "V4" and recvdata[2] != "lite":
  509. print(f"{ip} : [Hello] Invalid version : {recvdata[2]}")
  510. return
  511. print(f"{ip} : [Hello] Version : {recvdata[2]}")
  512. vape_version = recvdata[2]
  513. if vape_version == "V4":
  514. MAGIC = MAGIC_V4
  515. settings_path += "v4"
  516. elif vape_version == "lite":
  517. MAGIC = MAGIC_LITE
  518. settings_path += "lite"
  519.  
  520. await websocket.send("ok")
  521. #XorKey
  522. elif packet_id == 12 and len(recvdata) >= 2:
  523. print(f"{ip} : [XorKey] Key : {recvdata[1]}")
  524. xor_key = int(recvdata[1])
  525. #HashReq
  526. elif packet_id == 2:
  527. print(f"{ip} : [HashReq]")
  528. await websocket.send(await xor_string(MAGIC.encode("ascii"), xor_key))
  529. #UNK1
  530. elif packet_id == 27 and len(recvdata) >= 4:
  531. print(f"{ip} : [UNK1] {recvdata[1]} {recvdata[2]} {recvdata[3]}")
  532. await websocket.send("0")
  533. #FileReq1
  534. elif packet_id == 47 and len(recvdata) >= 2:
  535. if recvdata[1] != "2" and recvdata[1] != "5":
  536. print(f"{ip} : [FileReq1] Invalid file number : {recvdata[1]}")
  537. return
  538. print(f"{ip} : [FileReq1] {recvdata[1]}")
  539.  
  540. if recvdata[1] == "2":
  541. await send_file(websocket, "Dump0", xor_key)
  542. elif recvdata[1] == "5":
  543. await send_file(websocket, "Dump2", xor_key)
  544. #MCVersion
  545. elif packet_id == 10 and len(recvdata) >= 2:
  546. if not recvdata[1] in version_map:
  547. print(f"{ip} : [MCVersion] Invalid version : {recvdata[1]}")
  548. return
  549.  
  550. print(f"{ip} : [MCVersion] Version : {recvdata[1]}")
  551. client_mc_version = recvdata[1]
  552. await websocket.send("0")
  553.  
  554. srg_method = version_map[client_mc_version]["method"]
  555. srg_field = version_map[client_mc_version]["field"]
  556. #FileReq2
  557. elif packet_id == 53:
  558. print(f"{ip} : [FileReq2]")
  559. await send_file(websocket, "Dump1", xor_key)
  560. #FieldMap
  561. elif packet_id == 8 and len(recvdata) >= 3:
  562. if srg_field == None:
  563. print(f"{ip} : [FieldMap] Unknown version !")
  564. return
  565.  
  566. class_name = (await xor_string(recvdata[1].encode("ascii"), xor_key)).decode("ascii")
  567. field_name = (await xor_string(recvdata[2].encode("ascii"), xor_key)).decode("ascii")
  568. full_path = class_name + "/" + field_name
  569.  
  570. if full_path in srg_field:
  571. print(f"{ip} : [FieldMap] Field : {full_path}")
  572. await websocket.send(await xor_string(srg_field[full_path]["forge_name"].encode("ascii"), xor_key))
  573. else:
  574. print(f"{ip} : [FieldMap] Unknown field : {full_path}")
  575. await websocket.send(await xor_string(field_name.encode("ascii"), xor_key))
  576. #MethodMap
  577. elif packet_id == 9 and len(recvdata) >= 3:
  578. if srg_method == None:
  579. print(f"{ip} : [MethodMap] Unknown version !")
  580. return
  581.  
  582. class_name = (await xor_string(recvdata[1].encode("ascii"), xor_key)).decode("ascii")
  583. method_name = (await xor_string(recvdata[2].encode("ascii"), xor_key)).decode("ascii")
  584. full_path = class_name + "/" + method_name
  585. if full_path in srg_method:
  586. print(f"{ip} : [MethodMap] Method : {full_path}")
  587. await websocket.send(await xor_string((srg_method[full_path]["forge_name"] + ":" + srg_method[full_path]["forge_params"]).encode("ascii"), xor_key))
  588. else:
  589. print(f"{ip} : [MethodMap] Unknown method : {full_path}")
  590. await websocket.send(await xor_string(method_name.encode("ascii"), xor_key))
  591. #LoadSettings
  592. elif packet_id == 51:
  593. print(f"{ip} : [LoadSettings] Sending settings ({settings_path})")
  594. settings = base64.b64encode((await loadSettings(ip, settings_path)).encode("ascii"))
  595. await websocket.send(settings)
  596. #AssetsReq
  597. elif packet_id == 55:
  598. print(f"{ip} : [AssetsReq]")
  599. await send_assets(websocket, xor_key)
  600. #StringsReq
  601. elif packet_id == 54:
  602. print(f"{ip} : [StringsReq]")
  603. await send_strings(websocket, xor_key)
  604. #ClientInfo (Vape V4)
  605. elif packet_id == 56 and len(recvdata) >= 6:
  606. IGN = (await xor_string(recvdata[1].encode("ascii"), xor_key)).decode("ascii")
  607. PCUsername = (await xor_string(recvdata[2].encode("ascii"), xor_key)).decode("ascii")
  608. MACAddress = (await xor_string(recvdata[3].encode("ascii"), xor_key)).decode("ascii")
  609. UNK1 = (await xor_string(recvdata[4].encode("ascii"), xor_key)).decode("ascii")
  610. UNK2 = (await xor_string(recvdata[5].encode("ascii"), xor_key)).decode("ascii")
  611. print(f"{ip} : [ClientInfo V4] IGN : {IGN} PC Username : {PCUsername} MAC Address : {MACAddress} UNK1 : {UNK1} UNK2 : {UNK2}")
  612. #Hello2 (V4)
  613. elif packet_id == 4 and len(recvdata) >= 4:
  614. if recvdata[1] != MAGIC_V4 or recvdata[3] != "V4":
  615. print(f"{ip} : [Hello2] Invalid. MAGIC : {recvdata[1]} Version : {recvdata[3]}")
  616. return
  617. print(f"{ip} : [Hello2] Version : {recvdata[3]}")
  618. settings_path += "v4"
  619. await websocket.send("ok")
  620. #ProfileReq
  621. elif packet_id == 52 and len(recvdata) >= 2:
  622. request = (await xor_string(recvdata[1].encode("ascii"), xor_key)).decode("ascii")
  623. print(f"{ip} : [ProfileReq] Request : {request}")
  624. if vape_version == "V4":
  625. await websocket.send(await xor_string(MAGIC.encode("ascii"), xor_key))
  626. elif vape_version == "lite":
  627. await websocket.send("e30=")# "{}" in b64
  628. #SyncSettings
  629. elif packet_id == 50 and len(recvdata) >= 2:
  630. settings = base64.b64decode(recvdata[1].encode("ascii")).decode("ascii")
  631. await saveSettings(ip, settings, settings_path)
  632. print(f"{ip} : [SyncSettings] Downloaded settings ({settings_path})")
  633. #UNK3 Only Vape Lite
  634. elif packet_id == 48 and len(recvdata) >= 2:
  635. print(f"{ip} : [UNK3] {recvdata[1]}")
  636. await websocket.send(await xor_string("44".encode("ascii"), xor_key))
  637. #IGN Only Vape Lite
  638. elif packet_id == 21 and len(recvdata) >= 2:
  639. IGN = (await xor_string(recvdata[1].encode("ascii"), xor_key)).decode("ascii")
  640. print(f"{ip} : [IGN] {IGN}")
  641. #ClientInfo (Vape Lite)
  642. elif packet_id == 49 and len(recvdata) >= 5:
  643. PCUsername = (await xor_string(recvdata[1].encode("ascii"), xor_key)).decode("ascii")
  644. MACAddress = (await xor_string(recvdata[2].encode("ascii"), xor_key)).decode("ascii")
  645. UNK1 = (await xor_string(recvdata[3].encode("ascii"), xor_key)).decode("ascii")
  646. UNK2 = (await xor_string(recvdata[4].encode("ascii"), xor_key)).decode("ascii")
  647.  
  648. print(f"{ip} : [ClientInfo Lite] PC Username : {PCUsername} MAC Address : {MACAddress} UNK1 : {UNK1} UNK2 : {UNK2}")
  649. #Hello3 (Lite)
  650. elif packet_id == 1 and len(recvdata) >= 4:
  651. if recvdata[1] != MAGIC_LITE:
  652. print(f"{ip} : [Hello3] Invalid. MAGIC : {recvdata[1]}")
  653. return
  654. print(f"{ip} : [Hello3]")
  655. settings_path += "lite"
  656. await websocket.send("ok")
  657. else:
  658. print(f"{ip} : Unknown packet. ID : {packet_id}")
  659.  
  660. except websockets.exceptions.ConnectionClosedError as e:
  661. if e.code == 1005:
  662. print(f"{ip} : Connection closed.")
  663. else:
  664. print(e)
  665. break
  666.  
  667. def main():
  668. loadAllMappings()
  669. FileIntegrityCheck()
  670.  
  671. SSL_CERT_FILE = "cert.pem"
  672. SSL_KEY_FILE = "key.pem"
  673.  
  674. if not os.path.exists(SSL_CERT_FILE) or not os.path.exists(SSL_KEY_FILE):
  675. GenerateSSL(SSL_CERT_FILE, SSL_KEY_FILE)
  676.  
  677. ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
  678. cert_file = pathlib.Path(__file__).with_name(SSL_CERT_FILE)
  679. key_file = pathlib.Path(__file__).with_name(SSL_KEY_FILE)
  680. ssl_context.load_cert_chain(certfile=cert_file, keyfile=key_file)
  681.  
  682. start_server = websockets.serve(
  683. handle_client, SERVER_IP, SERVER_PORT, ssl=ssl_context
  684. )
  685.  
  686. print(f"Listening on {SERVER_IP}:{SERVER_PORT}")
  687.  
  688. asyncio.get_event_loop().run_until_complete(start_server)
  689. asyncio.get_event_loop().run_forever()
  690.  
  691. if __name__ == "__main__":
  692. main()
Add Comment
Please, Sign In to add comment